diff --git a/e2e/angular-core/src/ng-add.test.ts b/e2e/angular-core/src/ng-add.test.ts index 543126704af35..0c1420fb7ae20 100644 --- a/e2e/angular-core/src/ng-add.test.ts +++ b/e2e/angular-core/src/ng-add.test.ts @@ -387,14 +387,54 @@ describe('convert Angular CLI workspace to an Nx workspace', () => { }); }); - it('should support preserveAngularCliLayout', () => { + it('should support --preserve-angular-cli-layout', () => { + // add another app and a library + runCommand(`ng g @schematics/angular:application app2`); + runCommand(`ng g @schematics/angular:library lib1`); + runNgAdd('@nrwl/angular', '--preserve-angular-cli-layout'); - const updatedAngularCLIJson = readJson('angular.json'); - expect(updatedAngularCLIJson.projects[project].root).toEqual(''); - expect(updatedAngularCLIJson.projects[project].sourceRoot).toEqual('src'); + // check config still uses Angular CLI layout + const updatedAngularJson = readJson('angular.json'); + expect(updatedAngularJson.projects[project].root).toEqual(''); + expect(updatedAngularJson.projects[project].sourceRoot).toEqual('src'); + expect(updatedAngularJson.projects.app2.root).toEqual('projects/app2'); + expect(updatedAngularJson.projects.app2.sourceRoot).toEqual( + 'projects/app2/src' + ); + expect(updatedAngularJson.projects.lib1.root).toEqual('projects/lib1'); + expect(updatedAngularJson.projects.lib1.sourceRoot).toEqual( + 'projects/lib1/src' + ); + + // check building an app + let output = runCLI(`build ${project} --outputHashing none`); + expect(output).toContain( + `> nx run ${project}:build:production --outputHashing=none` + ); + expect(output).toContain( + `Successfully ran target build for project ${project}` + ); + checkFilesExist(`dist/${project}/main.js`); - const output = runCLI('build'); - expect(output).toContain(`> nx run ${project}:build:production`); + output = runCLI(`build ${project} --outputHashing none`); + expect(output).toContain( + `> nx run ${project}:build:production --outputHashing=none [existing outputs match the cache, left as is]` + ); + expect(output).toContain( + `Successfully ran target build for project ${project}` + ); + + // check building lib1 + output = runCLI('build lib1'); + expect(output).toContain('> nx run lib1:build:production'); + expect(output).toContain('Successfully ran target build for project lib1'); + checkFilesExist('dist/lib1/package.json'); + + output = runCLI('build lib1'); + expect(output).toContain( + '> nx run lib1:build:production [existing outputs match the cache, left as is]' + ); + expect(output).toContain('Successfully ran target build for project lib1'); }); }); diff --git a/packages/angular/src/generators/ng-add/__snapshots__/migrate-from-angular-cli.spec.ts.snap b/packages/angular/src/generators/ng-add/__snapshots__/migrate-from-angular-cli.spec.ts.snap index 6668958923bae..7b5f554eafb57 100644 --- a/packages/angular/src/generators/ng-add/__snapshots__/migrate-from-angular-cli.spec.ts.snap +++ b/packages/angular/src/generators/ng-add/__snapshots__/migrate-from-angular-cli.spec.ts.snap @@ -1,5 +1,87 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`workspace --preserve-angular-cli-layout should create nx.json 1`] = ` +Object { + "affected": Object { + "defaultBase": "main", + }, + "implicitDependencies": Object { + ".eslintrc.json": "*", + "package.json": Object { + "dependencies": "*", + "devDependencies": "*", + }, + }, + "npmScope": "my-scope", + "targetDependencies": Object { + "build": Array [ + Object { + "projects": "dependencies", + "target": "build", + }, + ], + }, + "tasksRunnerOptions": Object { + "default": Object { + "options": Object { + "cacheableOperations": Array [ + "build", + "lint", + "test", + "e2e", + ], + }, + "runner": "nx/tasks-runners/default", + }, + }, + "workspaceLayout": Object { + "appsDir": "", + "libsDir": "", + }, +} +`; + +exports[`workspace --preserve-angular-cli-layout should support multiple projects 1`] = ` +Object { + "affected": Object { + "defaultBase": "main", + }, + "implicitDependencies": Object { + ".eslintrc.json": "*", + "package.json": Object { + "dependencies": "*", + "devDependencies": "*", + }, + }, + "npmScope": "my-scope", + "targetDependencies": Object { + "build": Array [ + Object { + "projects": "dependencies", + "target": "build", + }, + ], + }, + "tasksRunnerOptions": Object { + "default": Object { + "options": Object { + "cacheableOperations": Array [ + "build", + "lint", + "test", + "e2e", + ], + }, + "runner": "nx/tasks-runners/default", + }, + }, + "workspaceLayout": Object { + "appsDir": "projects", + "libsDir": "projects", + }, +} +`; + exports[`workspace move to nx layout cypress should handle project configuration without cypress-run or cypress-open 1`] = ` Object { "implicitDependencies": Array [ diff --git a/packages/angular/src/generators/ng-add/migrate-from-angular-cli.spec.ts b/packages/angular/src/generators/ng-add/migrate-from-angular-cli.spec.ts index 69fe1642abfc8..26d14de0684b6 100644 --- a/packages/angular/src/generators/ng-add/migrate-from-angular-cli.spec.ts +++ b/packages/angular/src/generators/ng-add/migrate-from-angular-cli.spec.ts @@ -632,41 +632,102 @@ describe('workspace', () => { }); }); - describe('preserve angular cli layout', () => { + describe('--preserve-angular-cli-layout', () => { beforeEach(() => { - tree.write('/package.json', JSON.stringify({ devDependencies: {} })); - tree.write('/angular.json', JSON.stringify({ projects: { myproj: {} } })); - tree.write('/tsconfig.json', '{"compilerOptions": {}}'); + tree.write( + 'package.json', + JSON.stringify({ name: 'my-scope', devDependencies: {} }) + ); + tree.write('angular.json', JSON.stringify({ projects: { myproj: {} } })); + tree.write('tsconfig.json', '{"compilerOptions": {}}'); }); it('should update package.json', async () => { - await migrateFromAngularCli(tree, { - preserveAngularCliLayout: true, - }); + await migrateFromAngularCli(tree, { preserveAngularCliLayout: true }); - const d = readJson(tree, '/package.json').devDependencies; - expect(d['@nrwl/workspace']).toBeDefined(); - expect(d['@nrwl/angular']).not.toBeDefined(); + const { devDependencies } = readJson(tree, 'package.json'); + expect(devDependencies['@nrwl/workspace']).toBeDefined(); + expect(devDependencies['nx']).toBeDefined(); }); it('should create nx.json', async () => { - await migrateFromAngularCli(tree, { - preserveAngularCliLayout: true, - }); + await migrateFromAngularCli(tree, { preserveAngularCliLayout: true }); - const nxJson = readJson(tree, '/nx.json'); - expect(nxJson.npmScope).toEqual('myproj'); - expect(nxJson.workspaceLayout).toBeTruthy(); + expect(readJson(tree, 'nx.json')).toMatchSnapshot(); }); it('should create decorate-angular-cli.js', async () => { - await migrateFromAngularCli(tree, { - preserveAngularCliLayout: true, - }); - const s = readJson(tree, '/package.json').scripts; + await migrateFromAngularCli(tree, { preserveAngularCliLayout: true }); + + expect(tree.exists('/decorate-angular-cli.js')).toBe(true); + const { scripts } = readJson(tree, 'package.json'); + expect(scripts.postinstall).toBe('node ./decorate-angular-cli.js'); + }); + + it('should support multiple projects', async () => { + const angularJson = { + $schema: './node_modules/@angular/cli/lib/config/schema.json', + version: 1, + defaultProject: 'app1', + newProjectRoot: 'projects', + projects: { + app1: { + root: '', + sourceRoot: 'src', + architect: { + build: { + options: { tsConfig: 'tsconfig.app.json' }, + }, + test: { + options: { tsConfig: 'tsconfig.spec.json' }, + }, + e2e: { + builder: '@angular-devkit/build-angular:protractor', + options: { protractorConfig: 'e2e/protractor.conf.js' }, + }, + }, + }, + app2: { + root: 'projects/app2', + sourceRoot: 'projects/app2/src', + architect: { + build: { + options: { tsConfig: 'projects/app2/tsconfig.app.json' }, + }, + test: { + options: { tsConfig: 'projects/app2/tsconfig.spec.json' }, + }, + e2e: { + builder: '@angular-devkit/build-angular:protractor', + options: { + protractorConfig: 'projects/app2/e2e/protractor.conf.js', + }, + }, + }, + }, + lib1: { + root: 'projects/lib1', + sourceRoot: 'projects/lib1/src', + architect: { + build: { + options: { tsConfig: 'projects/lib1/tsconfig.lib.json' }, + }, + test: { + options: { tsConfig: 'projects/lib1/tsconfig.spec.json' }, + }, + }, + }, + }, + }; + tree.write('/angular.json', JSON.stringify(angularJson)); + + await migrateFromAngularCli(tree, { preserveAngularCliLayout: true }); - expect(tree.read('/decorate-angular-cli.js')).not.toBe(null); - expect(s.postinstall).toEqual('node ./decorate-angular-cli.js'); + expect(readJson(tree, 'angular.json')).toStrictEqual(angularJson); + expect(tree.exists('/decorate-angular-cli.js')).toBe(true); + const { scripts } = readJson(tree, 'package.json'); + expect(scripts.postinstall).toBe('node ./decorate-angular-cli.js'); + expect(readJson(tree, 'nx.json')).toMatchSnapshot(); }); }); }); diff --git a/packages/angular/src/generators/ng-add/migrate-from-angular-cli.ts b/packages/angular/src/generators/ng-add/migrate-from-angular-cli.ts index 324282abdc4a3..f70bbadd107ab 100755 --- a/packages/angular/src/generators/ng-add/migrate-from-angular-cli.ts +++ b/packages/angular/src/generators/ng-add/migrate-from-angular-cli.ts @@ -28,7 +28,7 @@ export async function migrateFromAngularCli( rawOptions: GeneratorOptions ) { const projects = getAllProjects(tree); - const options = normalizeOptions(rawOptions, projects); + const options = normalizeOptions(tree, rawOptions, projects); if (options.preserveAngularCliLayout) { addDependenciesToPackageJson( diff --git a/packages/angular/src/generators/ng-add/utilities/normalize-options.ts b/packages/angular/src/generators/ng-add/utilities/normalize-options.ts index 94966d8a11510..b4c5c5c0c9c8d 100644 --- a/packages/angular/src/generators/ng-add/utilities/normalize-options.ts +++ b/packages/angular/src/generators/ng-add/utilities/normalize-options.ts @@ -1,24 +1,33 @@ -import { names } from '@nrwl/devkit'; +import { joinPathFragments, names, readJson, Tree } from '@nrwl/devkit'; import { GeneratorOptions } from '../schema'; import { WorkspaceProjects } from './types'; export function normalizeOptions( + tree: Tree, options: GeneratorOptions, projects: WorkspaceProjects ): GeneratorOptions { - // TODO: this restrictions will be removed, it's here temporarily to - // execute for both a full migration and a minimal one to maintain - // the current behavior - const hasLibraries = projects.libs.length > 0; - if (projects.apps.length > 2 || hasLibraries) { - throw new Error('Can only convert projects with one app'); - } - let npmScope = options.npmScope ?? options.name; if (npmScope) { npmScope = names(npmScope).fileName; - } else { - npmScope = projects.apps[0].name; + } else if (projects.libs.length > 0) { + // try get the scope from any library that have one + for (const lib of projects.libs) { + const { name } = readJson( + tree, + joinPathFragments(lib.config.root, 'package.json') + ); + if (name.startsWith('@')) { + npmScope = name.split('/')[0].substring(1); + break; + } + } + } + + if (!npmScope) { + // use the name (scope if exists) in the root package.json + const { name } = readJson(tree, 'package.json'); + npmScope = name.startsWith('@') ? name.split('/')[0].substring(1) : name; } return { ...options, npmScope }; diff --git a/packages/angular/src/generators/ng-add/utilities/workspace.ts b/packages/angular/src/generators/ng-add/utilities/workspace.ts index a8903a40051e3..9b8e0ec9d0e7a 100644 --- a/packages/angular/src/generators/ng-add/utilities/workspace.ts +++ b/packages/angular/src/generators/ng-add/utilities/workspace.ts @@ -43,6 +43,12 @@ export function validateWorkspace( throw new Error('Cannot find angular.json'); } + // TODO: this restrictions will be removed when support for multiple + // projects is added + if (projects.apps.length > 2 || projects.libs.length > 0) { + throw new Error('Can only convert projects with one app'); + } + const e2eKey = getE2eKey(projects); const e2eApp = getE2eProject(projects); @@ -97,19 +103,7 @@ export function createNxJson( options: GeneratorOptions, setWorkspaceLayoutAsNewProjectRoot: boolean = false ): void { - const { projects = {}, newProjectRoot = '' } = readJson(tree, 'angular.json'); - // TODO: temporarily leaving this here because it's the old behavior for a - // minimal migration, will be removed in a later PR - const hasLibraries = Object.keys(projects).find( - (project) => - projects[project].projectType && - projects[project].projectType !== 'application' - ); - if (Object.keys(projects).length !== 1 || hasLibraries) { - throw new Error( - `The schematic can only be used with Angular CLI workspaces with a single application.` - ); - } + const { newProjectRoot = '' } = readJson(tree, 'angular.json'); writeJson(tree, 'nx.json', { npmScope: options.npmScope,