docs(angular): update AngularJS migration to Nx guide

ISSUES CLOSED: #4695
This commit is contained in:
Leosvel Pérez Espinosa 2021-03-24 09:24:22 +00:00 committed by Victor Savkin
parent 578455f032
commit 4b669f2d52

View File

@ -21,16 +21,16 @@ There is also a [repo](https://github.com/nrwl/nx-migrate-angularjs-example) tha
To start migrating the Real World app, create an Nx workspace:
```bash
npx create-nx-workspace@latest nx-migrate-angularjs
npx create-nx-workspace@latest nx-migrate-angularjs --cli=angular
```
When prompted choose the `empty` preset. The other presets use certain recommended defaults for the workspace configuration. Because you have existing code with specific requirements for configuration, starting with a blank workspace avoids resetting these defaults. This will give you the ability to customize the workspace for the incoming code.
At the next prompt, choose `Angular CLI` for your workspace CLI. While you may not be using Angular now, this gives you the best option to upgrade to Angular later. The Angular CLI is also the best CLI option for using Karma and Protractor, the two testing suites most commonly used for AngularJS.
At the next prompt, you can choose whether to use [Nx Cloud](https://nx.app) or not. By using Nx Cloud, youll be able to share the computation cache of operations like build, test or even your own commands with everyone working on the same project. Whether you choose to use it or not, the outcome of the migration wont be affected and you can always change your choice later.
```bash
? What to create in the new workspace empty [an empty workspace]
? CLI to power the Nx workspace Angular CLI [Extensible CLI for Angular applications. Recommended for Angular projects.]
? What to create in the new workspace empty [an empty workspace with a layout that works best for building apps]
? Use Nx Cloud? (It's free and doesn't require registration.) Yes [Faster builds, run details, Github integration. Learn more at https://nx.app]
```
## Creating your app
@ -38,19 +38,12 @@ At the next prompt, choose `Angular CLI` for your workspace CLI. While you may n
Your new workspace wont have much in it because of the `empty` preset. Youll need to generate an application to have some structure created. Add the Angular capability to your workspace:
```bash
ng add @nrwl/angular
```
When prompted, make a choice of unit test runner and e2e test runner:
```bash
? Which Unit Test Runner would you like to use for the application? Karma [ https://karma-runner.github.io ]
? Which E2E Test Runner would you like to use? Protractor [ https://www.protractortest.org ]
ng add @nrwl/angular --unitTestRunner=karma --e2eTestRunner=protractor
```
For this example, we will use Karma and Protractor, the most common unit test runner and e2e test runner for AngularJS.
> Codebases with existing unit and e2e tests should continue to use whatever runner they need. Weve chosen Karma and Protractor here because its the most common. If youre going to be adding unit testing or e2e as part of this transition and are starting fresh, we recommend starting with Jest and Cypress.
> Codebases with existing unit and e2e tests should continue to use whatever runner they need. Weve chosen Karma and Protractor here because its the most common. If youre going to be adding unit testing or e2e as part of this transition and are starting fresh, we recommend starting with Jest and Cypress (the default if no arguments are passed to the above command).
With the Angular capability added, generate your application:
@ -79,7 +72,8 @@ Your `package.json` should now look like this:
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"ng": "nx",
"postinstall": "node ./decorate-angular-cli.js && ngcc --properties es2015 browser module main",
"nx": "nx",
"start": "ng serve",
"build": "ng build",
@ -97,66 +91,74 @@ Your `package.json` should now look like this:
"format": "nx format:write",
"format:write": "nx format:write",
"format:check": "nx format:check",
"update": "ng update @nrwl/workspace",
"update": "nx migrate latest",
"workspace-generator": "nx workspace-generator",
"dep-graph": "nx dep-graph",
"help": "nx help",
"postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points"
"help": "nx help"
},
"private": true,
"dependencies": {
"@nrwl/angular": "^9.0.4",
"@angular/animations": "9.0.0",
"@angular/common": "9.0.0",
"@angular/compiler": "9.0.0",
"@angular/core": "9.0.0",
"@angular/forms": "9.0.0",
"@angular/platform-browser": "9.0.0",
"@angular/platform-browser-dynamic": "9.0.0",
"@angular/router": "9.0.0",
"@angular/animations": "^11.2.0",
"@angular/common": "^11.2.0",
"@angular/compiler": "^11.2.0",
"@angular/core": "^11.2.0",
"@angular/forms": "^11.2.0",
"@angular/platform-browser": "^11.2.0",
"@angular/platform-browser-dynamic": "^11.2.0",
"@angular/router": "^11.2.0",
"@nrwl/angular": "^11.5.1",
"angular": "^1.5.0-rc.2",
"angular-ui-router": "^0.4.2",
"core-js": "^2.5.4",
"rxjs": "~6.5.0",
"marked": "^0.3.5",
"rxjs": "~6.6.3",
"tslib": "^2.0.0",
"zone.js": "^0.10.2"
},
"devDependencies": {
"@angular/cli": "9.0.1",
"@nrwl/workspace": "9.0.4",
"@types/node": "~8.9.4",
"dotenv": "6.2.0",
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"eslint": "6.1.0",
"typescript": "~3.7.4",
"prettier": "1.18.2",
"@angular/compiler-cli": "9.0.0",
"@angular/language-service": "9.0.0",
"@angular-devkit/build-angular": "0.900.1",
"codelyzer": "~5.0.1",
"karma": "~4.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"@types/jasmine": "~2.8.8",
"protractor": "~5.4.0",
"@angular-devkit/build-angular": "~0.1102.0",
"@angular-eslint/eslint-plugin": "~1.0.0",
"@angular-eslint/eslint-plugin-template": "~1.0.0",
"@angular-eslint/template-parser": "~1.0.0",
"@angular/cli": "~11.0.0",
"@angular/compiler-cli": "^11.2.0",
"@angular/language-service": "^11.2.0",
"@nrwl/cli": "11.5.1",
"@nrwl/eslint-plugin-nx": "11.5.1",
"@nrwl/linter": "11.5.1",
"@nrwl/nx-cloud": "latest",
"@nrwl/tao": "11.5.1",
"@nrwl/workspace": "11.5.1",
"@types/jasmine": "~3.5.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "12.12.38",
"@typescript-eslint/eslint-plugin": "4.3.0",
"@typescript-eslint/parser": "4.3.0",
"babel-preset-es2015": "^6.3.13",
"babelify": "^7.2.0",
"browser-sync": "^2.11.1",
"browserify": "^13.0.0",
"browserify-ngannotate": "^2.0.0",
"dotenv": "6.2.0",
"eslint": "7.10.0",
"eslint-config-prettier": "8.1.0",
"gulp": "^3.9.1",
"gulp-angular-templatecache": "^1.8.0",
"gulp-notify": "^2.2.0",
"gulp-rename": "^1.2.2",
"gulp-uglify": "^1.5.3",
"gulp-util": "^3.0.7",
"marked": "^0.3.5",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.0.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"merge-stream": "^1.0.0",
"prettier": "2.2.1",
"protractor": "~7.0.0",
"ts-node": "~9.1.1",
"typescript": "~4.0.3",
"vinyl-source-stream": "^1.1.0"
}
}
@ -177,7 +179,6 @@ apps
|____realworld-e2e
|____realworld
| |____src
| | |____index.html
| | |____app
| | | |____settings
| | | |____home
@ -189,12 +190,15 @@ apps
| | | |____article
| | | |____services
| | | |____editor
| | | |____app.js\
| | |____styles.css
| | |____environments
| | |____main.ts
| | |____test.ts
| | | |____app.js
| | |____assets
| | |____environments
| | |____favicon.ico
| | |____index.html
| | |____main.ts
| | |____polyfills.ts
| | |____styles.css
| | |____test.ts
```
> You most likely have your own AngularJS project written in JavaScript as well. While youll continue to use JavaScript through the rest of this example, we strongly recommend switching AngularJS projects to TypeScript, especially if youre planning an upgrade to Angular.
@ -206,14 +210,16 @@ Your generated application will also have an `index.html` provided. However, it
Your application also has a `main.ts` file which is responsible for bootstrapping your app. Again, you dont need much from this file any more. Replace its contents with:
```typescript
import ./app/app.js;
import './app/app.js';
```
And re-name it to `main.js`. This will import the existing app.js file from the RealWorld app which will bootstrap the app.
## Adding existing build and serve processes
If youre looking at the example repo, the code for this section is available on branch `initial-migration`. This section is an interim step that continues to use gulp to build and serve the app locally. Youll replace gulp in the next section. The RealWorld app uses gulp 3.9.1 to build. This version is not supported anymore and doesnt run on any version of Node greater than 10.\*. To build this using gulp, you need to install an appropriate version of Node and make sure you re-install your dependencies. If this isnt possible (or you just dont want to), feel free to skip to the next section. The webpack build process should run in any modern Node version.
If youre looking at the example repo, the code for this section is available on branch `initial-migration`. This section is an interim step that continues to use gulp to build and serve the app locally, so we can validate everything works before continuing with the migration. Youll replace gulp in the next section.
> The RealWorld app uses gulp 3.9.1 to build. This version is not supported anymore and doesnt run on any version of Node greater than 10.\*. To build this app using gulp, you need to install an appropriate version of Node and make sure you re-install your dependencies. If this isnt possible (or you just dont want to), feel free to skip to the next section. The webpack build process should run in any modern Node version.
The RealWorld app uses gulp to build the application, as well as provide a development server. To verify that the migration has worked, stay with that build process for now.
@ -323,7 +329,7 @@ You need to point your `build` and `serve` tasks at this gulp build process. Typ
```json
...
"build": {
"builder": "@nrwl/workspace:run-commands",
"executor": "@nrwl/workspace:run-commands",
"options": {
"commands": [
{
@ -333,7 +339,7 @@ You need to point your `build` and `serve` tasks at this gulp build process. Typ
}
},
"serve": {
"builder": "@nrwl/workspace:run-commands",
"executor": "@nrwl/workspace:run-commands",
"options": {
"commands": [
{
@ -345,7 +351,7 @@ You need to point your `build` and `serve` tasks at this gulp build process. Typ
...
```
This sets up the `build` and `serve` commands to use the locally installed version of gulp to run `build` and `serve`. To see the RealWorld app working, run
This sets up the `build` and `serve` commands to use the locally installed version of gulp to run `build` and `serve`. To see the RealWorld app working, run:
```bash
ng serve realworld
@ -353,19 +359,19 @@ ng serve realworld
Navigate around the application and see that things work.
> Your own project might not be using gulp. If youre using webpack, you can follow the next section and substitute your own webpack configuration. If youre using something else like grunt or a home-grown solution, you can follow the same steps here to use it. Youll use the `run-commands` builder and substitute in the commands for your project.
> Your own project might not be using gulp. If youre using webpack, you can follow the next section and substitute your own webpack configuration. If youre using something else like grunt or a home-grown solution, you can follow the same steps here to use it. Youll use the `run-commands` executor and substitute in the commands for your project.
## Switching to webpack
So far, youve mostly gotten already existing code and processes to work. This is the best way to get started with any migration: get existing code to work before you start making changes. This gives you a good, stable base to build on. It also means you having working code now rather than hoping youll have working code in the future!
But migrating AngularJS code means we need to switch some of our tools to a more modern tool stack. Specifically, using webpack and babel is going to allow us to take advantage of Nx more easily. Becoming an expert in these build tools is outside the scope of this article, but Ill address some AngularJS specific concerns. To get started, install a new dependency:
But migrating AngularJS code means we need to switch some of our tools to a more modern tool stack. Specifically, using webpack and babel is going to allow us to take advantage of Nx more easily. Becoming an expert in these build tools is outside the scope of this article, but Ill address some AngularJS specific concerns. To get started, install these new dependencies:
```bash
npm install babel-plugin-angularjs-annotate
npm install -D @nrwl/web babel-plugin-angularjs-annotate
```
Nx already has most of what you need for webpack added as a dependency. `babel-plugin-angularjs-annotate` is going to accomplish the same thing that `browserify-ngannotate` previously did in gulp: add dependency injection annotations.
Nx already has most of what you need for webpack added as a dependency. `@nrwl/web` contains the [executors](https://nx.dev/angular/core-concepts/nx-devkit#executors) we need to use to build and serve the application with webpack and `babel-plugin-angularjs-annotate` is going to accomplish the same thing that `browserify-ngannotate` previously did in gulp: add dependency injection annotations.
Start with a `webpack.config.js` file in your applications root directory:
@ -404,11 +410,11 @@ To use webpack instead of gulp, go back to your `angular.json` file and modify t
```json
...
"build": {
"builder": "@nrwl/web:build",
"executor": "@nrwl/web:build",
"options": {
"outputPath": "dist/apps/realworld",
"index": "apps/realworld/src/index.html",
"main": "apps/realworld/src/main.ts",
"main": "apps/realworld/src/main.js",
"polyfills": "apps/realworld/src/polyfills.ts",
"tsConfig": "apps/realworld/tsconfig.app.json",
"assets": [
@ -446,7 +452,7 @@ To use webpack instead of gulp, go back to your `angular.json` file and modify t
}
},
"serve": {
"builder": "@nrwl/web:dev-server",
"executor": "@nrwl/web:dev-server",
"options": {
"buildTarget": "realworld:build"
}
@ -502,6 +508,55 @@ Now, go through each component of the application and make this change. To make
> In an example like this, its easy enough to make this kind of change by hand. In a larger codebase, doing this manually could be very time-intensive. Youll want to look into an automated tool to do this for you, such as js-codemod or generators.
We also need to modify the `app.js` and remove the import of `config/app.templates.js`. Modify it like this:
```javascript
import angular from 'angular';
// Import our app config files
import constants from './config/app.constants';
import appConfig from './config/app.config';
import appRun from './config/app.run';
import 'angular-ui-router';
// Import our app functionaity
import './layout';
import './components';
import './home';
import './profile';
import './article';
import './services';
import './auth';
import './settings';
import './editor';
// Create and bootstrap application
const requires = [
'ui.router',
'app.layout',
'app.components',
'app.home',
'app.profile',
'app.article',
'app.services',
'app.auth',
'app.settings',
'app.editor',
];
// Mount on window for testing
window.app = angular.module('app', requires);
angular.module('app').constant('AppConstants', constants);
angular.module('app').config(appConfig);
angular.module('app').run(appRun);
angular.bootstrap(document, ['app'], {
strictDi: true,
});
```
Run the application the same way as before:
```bash
@ -515,7 +570,7 @@ Unit testing can be an important part of any code migration. If you migrate your
You need a few dependencies for AngularJS unit testing that Nx doesnt provide by default:
```bash
npm install angular-mocks@1.5.11 karma-webpack
npm install -D angular-mocks@1.5.11 karma-webpack
```
Earlier, you configured this app to use Karma as its unit test runner. Nx has provided a Karma config file for you, but youll need to modify it to work with AngularJS:
@ -571,6 +626,20 @@ module.exports = function (config) {
};
```
Next, rename the existing `apps/realworld/src/test.ts` to `apps/realworld/src/test.js` and modify its content to match the following:
```javascript
import 'angular';
import 'angular-mocks';
import 'angular-ui-router';
// require all test files using special Webpack feature
// https://webpack.github.io/docs/context.html#require-context
const testsContext = require.context('./', true, /\.spec$/);
testsContext.keys().forEach(testsContext);
```
Now add a unit test for the comment component:
```javascript
@ -636,9 +705,10 @@ describe('workspace-project App', () => {
page = new AppPage();
});
it('should display app title', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('conduit');
it('should display app title', async () => {
await page.navigateTo();
expect(await page.getTitleText()).toEqual('conduit');
});
afterEach(async () => {
@ -658,8 +728,8 @@ describe('workspace-project App', () => {
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo(): Promise<unknown> {
return browser.get(browser.baseUrl) as Promise<unknown>;
navigateTo(): Promise<string> {
return browser.get(browser.baseUrl) as Promise<string>;
}
getTitleText(): Promise<string> {