From fe6ffa20d942c7d6b6d75f35120a17a6926e57fc Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Wed, 1 Feb 2023 17:09:29 +0000 Subject: [PATCH] feat(angular): set angular devkit packages as peer deps (#14723) --- e2e/angular-core/src/angular-linting.test.ts | 4 +- e2e/angular-core/src/config.test.ts | 12 +- .../src/module-federation.test.ts | 4 +- e2e/angular-core/src/projects.test.ts | 4 +- .../src/cypress-component-tests.test.ts | 4 +- e2e/angular-extensions/src/misc.test.ts | 8 +- e2e/angular-extensions/src/ngrx.test.ts | 7 +- e2e/angular-extensions/src/tailwind.test.ts | 7 +- e2e/jest/src/jest-root.test.ts | 10 +- e2e/linter/src/linter.test.ts | 5 +- e2e/utils/index.ts | 16 +- packages/angular/migrations.json | 6 + packages/angular/package.json | 9 +- .../install-required-packages.spec.ts | 213 ++++++++++++++++++ .../install-required-packages.ts | 32 +++ .../new/__snapshots__/new.spec.ts.snap | 3 + .../src/generators/new/generate-preset.ts | 11 +- scripts/check-imports.js | 1 + 18 files changed, 314 insertions(+), 42 deletions(-) create mode 100644 packages/angular/src/migrations/update-15-7-0/install-required-packages.spec.ts create mode 100644 packages/angular/src/migrations/update-15-7-0/install-required-packages.ts diff --git a/e2e/angular-core/src/angular-linting.test.ts b/e2e/angular-core/src/angular-linting.test.ts index 8d4f1034d7..9aacfb87b1 100644 --- a/e2e/angular-core/src/angular-linting.test.ts +++ b/e2e/angular-core/src/angular-linting.test.ts @@ -1,6 +1,6 @@ import { cleanupProject, - newProject, + newAngularProject, runCLI, uniq, updateFile, @@ -9,7 +9,7 @@ import * as path from 'path'; describe('Angular Package', () => { describe('linting', () => { - beforeAll(() => newProject()); + beforeAll(() => newAngularProject()); afterAll(() => cleanupProject()); it('should support eslint and pass linting on the standard generated code', async () => { diff --git a/e2e/angular-core/src/config.test.ts b/e2e/angular-core/src/config.test.ts index b91bc07613..9fd9e843c6 100644 --- a/e2e/angular-core/src/config.test.ts +++ b/e2e/angular-core/src/config.test.ts @@ -1,20 +1,16 @@ import { - expectTestsPass, - newProject, cleanupProject, + expectTestsPass, + newAngularProject, + readJson, runCLI, runCLIAsync, uniq, updateFile, - removeFile, - checkFilesDoNotExist, - isNotWindows, - readJson, - createFile, } from '@nrwl/e2e/utils'; describe('Angular Config', () => { - beforeAll(() => newProject()); + beforeAll(() => newAngularProject()); afterAll(() => cleanupProject()); it('should upgrade the config correctly', async () => { diff --git a/e2e/angular-core/src/module-federation.test.ts b/e2e/angular-core/src/module-federation.test.ts index e77adeeb02..cab1ece92f 100644 --- a/e2e/angular-core/src/module-federation.test.ts +++ b/e2e/angular-core/src/module-federation.test.ts @@ -1,6 +1,6 @@ import { cleanupProject, - newProject, + newAngularProject, promisifiedTreeKill, readProjectConfig, runCLI, @@ -16,7 +16,7 @@ import { names } from '@nrwl/devkit'; describe('Angular Projects', () => { let proj: string; - beforeAll(() => (proj = newProject())); + beforeAll(() => (proj = newAngularProject())); afterAll(() => cleanupProject()); it('should serve the host and remote apps successfully, even with a shared library with a secondary entry point between them', async () => { diff --git a/e2e/angular-core/src/projects.test.ts b/e2e/angular-core/src/projects.test.ts index 1ded053b62..6bc48af17d 100644 --- a/e2e/angular-core/src/projects.test.ts +++ b/e2e/angular-core/src/projects.test.ts @@ -3,7 +3,7 @@ import { cleanupProject, getSize, killPorts, - newProject, + newAngularProject, promisifiedTreeKill, readFile, runCLI, @@ -20,7 +20,7 @@ import { names } from '@nrwl/devkit'; describe('Angular Projects', () => { let proj: string; - beforeAll(() => (proj = newProject())); + beforeAll(() => (proj = newAngularProject())); afterAll(() => cleanupProject()); it('should generate an app, a lib, link them, build, serve and test both correctly', async () => { diff --git a/e2e/angular-extensions/src/cypress-component-tests.test.ts b/e2e/angular-extensions/src/cypress-component-tests.test.ts index 974027141c..fa492f3301 100644 --- a/e2e/angular-extensions/src/cypress-component-tests.test.ts +++ b/e2e/angular-extensions/src/cypress-component-tests.test.ts @@ -2,7 +2,7 @@ import { checkFilesDoNotExist, cleanupProject, createFile, - newProject, + newAngularProject, runCLI, uniq, updateFile, @@ -17,7 +17,7 @@ describe('Angular Cypress Component Tests', () => { const buildableLibName = uniq('cy-angular-buildable-lib'); beforeAll(async () => { - projectName = newProject({ name: uniq('cy-ng') }); + projectName = newAngularProject({ name: uniq('cy-ng') }); runCLI(`generate @nrwl/angular:app ${appName} --no-interactive`); runCLI( `generate @nrwl/angular:component fancy-component --project=${appName} --no-interactive` diff --git a/e2e/angular-extensions/src/misc.test.ts b/e2e/angular-extensions/src/misc.test.ts index a8c7ae0d22..c7e4348960 100644 --- a/e2e/angular-extensions/src/misc.test.ts +++ b/e2e/angular-extensions/src/misc.test.ts @@ -1,13 +1,9 @@ import { - checkFilesExist, cleanupProject, - newProject, - readFile, + newAngularProject, runCLI, uniq, - updateFile, } from '@nrwl/e2e/utils'; -import { classify } from '@nrwl/workspace/src/utils/strings'; describe('Move Angular Project', () => { let proj: string; @@ -16,7 +12,7 @@ describe('Move Angular Project', () => { let newPath: string; beforeAll(() => { - proj = newProject(); + proj = newAngularProject(); app1 = uniq('app1'); app2 = uniq('app2'); newPath = `subfolder/${app2}`; diff --git a/e2e/angular-extensions/src/ngrx.test.ts b/e2e/angular-extensions/src/ngrx.test.ts index aa5116e728..0730ad0f67 100644 --- a/e2e/angular-extensions/src/ngrx.test.ts +++ b/e2e/angular-extensions/src/ngrx.test.ts @@ -2,7 +2,7 @@ import { cleanupProject, expectTestsPass, getSelectedPackageManager, - newProject, + newAngularProject, readJson, runCLI, runCLIAsync, @@ -10,15 +10,12 @@ import { } from '@nrwl/e2e/utils'; describe('Angular Package', () => { - let previousPM = process.env.SELECTED_PM; describe('ngrx', () => { beforeAll(() => { - process.env.SELECTED_PM = 'npm'; - newProject(); + newAngularProject(); }); afterAll(() => { cleanupProject(); - process.env.SELECTED_PM = previousPM; }); it('should work', async () => { diff --git a/e2e/angular-extensions/src/tailwind.test.ts b/e2e/angular-extensions/src/tailwind.test.ts index ec9d3aa809..dd08811234 100644 --- a/e2e/angular-extensions/src/tailwind.test.ts +++ b/e2e/angular-extensions/src/tailwind.test.ts @@ -3,7 +3,7 @@ process.env.SELECTED_CLI = 'angular'; import { cleanupProject, listFiles, - newProject, + newAngularProject, readFile, removeFile, runCLI, @@ -118,10 +118,8 @@ describe('Tailwind support', () => { updateFile(tailwindConfigPath, tailwindConfigUpdated); }; - let previousPM = process.env.SELECTED_PM; beforeAll(() => { - process.env.SELECTED_PM = 'npm'; - project = newProject(); + project = newAngularProject(); // Create tailwind config in the workspace root createWorkspaceTailwindConfigFile(); @@ -129,7 +127,6 @@ describe('Tailwind support', () => { afterAll(() => { cleanupProject(); - process.env.SELECTED_PM = previousPM; }); describe('Libraries', () => { diff --git a/e2e/jest/src/jest-root.test.ts b/e2e/jest/src/jest-root.test.ts index cc97acba43..0e5bc0e346 100644 --- a/e2e/jest/src/jest-root.test.ts +++ b/e2e/jest/src/jest-root.test.ts @@ -1,4 +1,10 @@ -import { newProject, runCLI, uniq, runCLIAsync } from '@nrwl/e2e/utils'; +import { + newAngularProject, + newProject, + runCLI, + runCLIAsync, + uniq, +} from '@nrwl/e2e/utils'; describe('Jest root projects', () => { const myapp = uniq('myapp'); @@ -6,7 +12,7 @@ describe('Jest root projects', () => { describe('angular', () => { beforeAll(() => { - newProject(); + newAngularProject(); }); it('should test root level app projects', async () => { diff --git a/e2e/linter/src/linter.test.ts b/e2e/linter/src/linter.test.ts index ae1559b767..453bff5633 100644 --- a/e2e/linter/src/linter.test.ts +++ b/e2e/linter/src/linter.test.ts @@ -3,6 +3,7 @@ import { checkFilesExist, cleanupProject, createFile, + newAngularProject, newProject, readFile, readJson, @@ -185,7 +186,7 @@ describe('Linter', () => { }, 1000000); it('lint plugin should ensure module boundaries', () => { - const proj = newProject(); + const proj = newAngularProject(); const myapp = uniq('myapp'); const myapp2 = uniq('myapp2'); const mylib = uniq('mylib'); @@ -515,7 +516,7 @@ export function tslibC(): string { const myapp = uniq('myapp'); const mylib = uniq('mylib'); - newProject(); + newAngularProject(); runCLI(`generate @nrwl/angular:app ${myapp} --rootProject=true`); let rootEslint = readJson('.eslintrc.json'); diff --git a/e2e/utils/index.ts b/e2e/utils/index.ts index b39e8e168d..65b2d1ad1e 100644 --- a/e2e/utils/index.ts +++ b/e2e/utils/index.ts @@ -2,7 +2,6 @@ import { joinPathFragments, parseJson, ProjectConfiguration, - ProjectsConfigurations, readJsonFile, workspaceRoot, } from '@nrwl/devkit'; @@ -365,6 +364,21 @@ export function newProject({ } } +export function newAngularProject({ + name = uniq('proj'), + packageManager = getSelectedPackageManager(), +} = {}): string { + const projScope = newProject({ name, packageManager }); + + const angularPackages = [ + '@angular-devkit/core', + '@angular-devkit/schematics', + '@schematics/angular', + ]; + packageInstall(angularPackages.join(` `), projScope, 'latest'); + return projScope; +} + export function newLernaWorkspace({ name = uniq('lerna-proj'), packageManager = getSelectedPackageManager(), diff --git a/packages/angular/migrations.json b/packages/angular/migrations.json index c44bdc4038..67d8cded93 100644 --- a/packages/angular/migrations.json +++ b/packages/angular/migrations.json @@ -197,6 +197,12 @@ "version": "15.5.0-beta.0", "description": "Update the @angular/cli package version to ~15.1.0.", "factory": "./src/migrations/update-15-5-0/update-angular-cli" + }, + "install-required-packages": { + "cli": "nx", + "version": "15.7.0-beta.1", + "description": "Install the required angular-devkit packages as we do not directly depend on them anymore", + "factory": "./src/migrations/update-15-7-0/install-required-packages" } }, "packageJsonUpdates": { diff --git a/packages/angular/package.json b/packages/angular/package.json index cbc755fa31..decba35c34 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -36,7 +36,6 @@ "migrations": "./migrations.json" }, "dependencies": { - "@angular-devkit/schematics": "~15.1.0", "@nrwl/cypress": "file:../cypress", "@nrwl/devkit": "file:../devkit", "@nrwl/jest": "file:../jest", @@ -44,7 +43,6 @@ "@nrwl/webpack": "file:../webpack", "@nrwl/workspace": "file:../workspace", "@phenomnomnominal/tsquery": "4.1.1", - "@schematics/angular": "~15.1.0", "chalk": "^4.1.0", "chokidar": "^3.5.1", "http-server": "^14.1.0", @@ -58,7 +56,12 @@ "webpack-merge": "5.7.3" }, "peerDependencies": { - "@nguniversal/builders": "~15.1.0", + "@angular-devkit/architect": ">= 0.1400.0 < 0.1600.0", + "@angular-devkit/build-angular": ">= 14.0.0 < 16.0.0", + "@angular-devkit/schematics": ">= 14.0.0 < 16.0.0", + "@schematics/angular": ">= 14.0.0 < 16.0.0", + "@angular-devkit/core": ">= 14.0.0 < 16.0.0", + "@nguniversal/builders": ">= 14.0.0 < 16.0.0", "rxjs": "^6.5.3 || ^7.5.0" }, "peerDependenciesMeta": { diff --git a/packages/angular/src/migrations/update-15-7-0/install-required-packages.spec.ts b/packages/angular/src/migrations/update-15-7-0/install-required-packages.spec.ts new file mode 100644 index 0000000000..5057744ac1 --- /dev/null +++ b/packages/angular/src/migrations/update-15-7-0/install-required-packages.spec.ts @@ -0,0 +1,213 @@ +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import { readJson, updateJson } from '@nrwl/devkit'; +import installRequiredPackages from './install-required-packages'; + +describe('installed-required-packages', () => { + it('should install the dependencies if they do not exist for v15', async () => { + // ARRANGE + const tree = createTreeWithEmptyWorkspace(); + updateJson(tree, 'package.json', (pkg) => ({ + ...pkg, + dependencies: { + '@angular/core': '~15.0.0', + }, + devDependencies: { + '@angular/cli': '~15.0.0', + }, + })); + + // ACT + await installRequiredPackages(tree); + + // ASSERT + const pkgJson = readJson(tree, 'package.json'); + expect(pkgJson.dependencies).toMatchInlineSnapshot(` + Object { + "@angular/core": "~15.0.0", + } + `); + expect(pkgJson.devDependencies).toMatchInlineSnapshot(` + Object { + "@angular-devkit/core": "~15.0.0", + "@angular-devkit/schematics": "~15.0.0", + "@angular/cli": "~15.0.0", + "@schematics/angular": "~15.0.0", + } + `); + }); + + it('should install the dependencies if they do not exist for v14', async () => { + // ARRANGE + const tree = createTreeWithEmptyWorkspace(); + updateJson(tree, 'package.json', (pkg) => ({ + ...pkg, + dependencies: { + '@angular/core': '~14.0.0', + }, + devDependencies: { + '@angular/cli': '~14.0.0', + }, + })); + + // ACT + await installRequiredPackages(tree); + + // ASSERT + const pkgJson = readJson(tree, 'package.json'); + expect(pkgJson.dependencies).toMatchInlineSnapshot(` + Object { + "@angular/core": "~14.0.0", + } + `); + expect(pkgJson.devDependencies).toMatchInlineSnapshot(` + Object { + "@angular-devkit/core": "~14.0.0", + "@angular-devkit/schematics": "~14.0.0", + "@angular/cli": "~14.0.0", + "@schematics/angular": "~14.0.0", + } + `); + }); + + it('should not install the dependencies if they exist for v15', async () => { + // ARRANGE + const tree = createTreeWithEmptyWorkspace(); + updateJson(tree, 'package.json', (pkg) => ({ + ...pkg, + dependencies: { + '@angular/core': '~15.0.0', + }, + devDependencies: { + '@angular/cli': '~15.0.0', + '@angular-devkit/core': '~15.0.0', + '@angular-devkit/schematics': '~15.0.0', + '@schematics/angular': '~15.0.0', + }, + })); + + // ACT + await installRequiredPackages(tree); + + // ASSERT + const pkgJson = readJson(tree, 'package.json'); + expect(pkgJson.dependencies).toMatchInlineSnapshot(` + Object { + "@angular/core": "~15.0.0", + } + `); + expect(pkgJson.devDependencies).toMatchInlineSnapshot(` + Object { + "@angular-devkit/core": "~15.0.0", + "@angular-devkit/schematics": "~15.0.0", + "@angular/cli": "~15.0.0", + "@schematics/angular": "~15.0.0", + } + `); + }); + + it('should not install the dependencies if they exist for v14', async () => { + // ARRANGE + const tree = createTreeWithEmptyWorkspace(); + updateJson(tree, 'package.json', (pkg) => ({ + ...pkg, + dependencies: { + '@angular/core': '~14.0.0', + }, + devDependencies: { + '@angular/cli': '~14.0.0', + '@angular-devkit/core': '~14.0.0', + '@angular-devkit/schematics': '~14.0.0', + '@schematics/angular': '~14.0.0', + }, + })); + + // ACT + await installRequiredPackages(tree); + + // ASSERT + const pkgJson = readJson(tree, 'package.json'); + expect(pkgJson.dependencies).toMatchInlineSnapshot(` + Object { + "@angular/core": "~14.0.0", + } + `); + expect(pkgJson.devDependencies).toMatchInlineSnapshot(` + Object { + "@angular-devkit/core": "~14.0.0", + "@angular-devkit/schematics": "~14.0.0", + "@angular/cli": "~14.0.0", + "@schematics/angular": "~14.0.0", + } + `); + }); + + it('should install the missing dependencies for v15', async () => { + // ARRANGE + const tree = createTreeWithEmptyWorkspace(); + updateJson(tree, 'package.json', (pkg) => ({ + ...pkg, + dependencies: { + '@angular/core': '~15.0.0', + }, + devDependencies: { + '@angular/cli': '~15.0.0', + '@angular-devkit/core': '~15.0.0', + '@schematics/angular': '~15.0.0', + }, + })); + + // ACT + await installRequiredPackages(tree); + + // ASSERT + const pkgJson = readJson(tree, 'package.json'); + expect(pkgJson.dependencies).toMatchInlineSnapshot(` + Object { + "@angular/core": "~15.0.0", + } + `); + expect(pkgJson.devDependencies).toMatchInlineSnapshot(` + Object { + "@angular-devkit/core": "~15.0.0", + "@angular-devkit/schematics": "~15.0.0", + "@angular/cli": "~15.0.0", + "@schematics/angular": "~15.0.0", + } + `); + }); + + it('should not install the missing dependencies for v14', async () => { + // ARRANGE + const tree = createTreeWithEmptyWorkspace(); + updateJson(tree, 'package.json', (pkg) => ({ + ...pkg, + dependencies: { + '@angular/core': '~14.0.0', + }, + devDependencies: { + '@angular/cli': '~14.0.0', + '@angular-devkit/core': '~14.0.0', + '@schematics/angular': '~14.0.0', + }, + })); + + // ACT + await installRequiredPackages(tree); + + // ASSERT + const pkgJson = readJson(tree, 'package.json'); + expect(pkgJson.dependencies).toMatchInlineSnapshot(` + Object { + "@angular/core": "~14.0.0", + } + `); + expect(pkgJson.devDependencies).toMatchInlineSnapshot(` + Object { + "@angular-devkit/core": "~14.0.0", + "@angular-devkit/schematics": "~14.0.0", + "@angular/cli": "~14.0.0", + "@schematics/angular": "~14.0.0", + } + `); + }); +}); diff --git a/packages/angular/src/migrations/update-15-7-0/install-required-packages.ts b/packages/angular/src/migrations/update-15-7-0/install-required-packages.ts new file mode 100644 index 0000000000..450872981c --- /dev/null +++ b/packages/angular/src/migrations/update-15-7-0/install-required-packages.ts @@ -0,0 +1,32 @@ +import type { Tree } from '@nrwl/devkit'; +import { addDependenciesToPackageJson, readJson } from '@nrwl/devkit'; +import { getInstalledAngularMajorVersion } from '../../generators/utils/version-utils'; +import { getPkgVersionForAngularMajorVersion } from '../../utils/version-utils'; + +export default async function (tree: Tree) { + const packagesToInstall = [ + '@angular-devkit/core', + '@angular-devkit/schematics', + '@schematics/angular', + ]; + const pkgJson = readJson(tree, 'package.json'); + + const angularMajorVersion = getInstalledAngularMajorVersion(tree); + const angularDevkitVersion = getPkgVersionForAngularMajorVersion( + 'angularDevkitVersion', + angularMajorVersion + ); + + const angularCliVersion = + pkgJson.devDependencies['@angular/cli'] ?? + pkgJson.dependencies['@angular/cli'] ?? + angularDevkitVersion; + + const filteredPackages: Record = packagesToInstall + .filter( + (pkg) => !pkgJson.devDependencies[pkg] && !pkgJson.dependencies[pkg] + ) + .reduce((allPkgs, pkg) => ({ ...allPkgs, [pkg]: angularCliVersion }), {}); + + addDependenciesToPackageJson(tree, {}, { ...filteredPackages }); +} diff --git a/packages/workspace/src/generators/new/__snapshots__/new.spec.ts.snap b/packages/workspace/src/generators/new/__snapshots__/new.spec.ts.snap index 72bea6d209..79e32e2534 100644 --- a/packages/workspace/src/generators/new/__snapshots__/new.spec.ts.snap +++ b/packages/workspace/src/generators/new/__snapshots__/new.spec.ts.snap @@ -6,7 +6,10 @@ Object { "@nrwl/angular": "0.0.1", }, "devDependencies": Object { + "@angular-devkit/core": "~15.1.0", + "@angular-devkit/schematics": "~15.1.0", "@nrwl/workspace": "0.0.1", + "@schematics/angular": "~15.1.0", "nx": "0.0.1", "prettier": "^2.6.2", "typescript": "~4.8.2", diff --git a/packages/workspace/src/generators/new/generate-preset.ts b/packages/workspace/src/generators/new/generate-preset.ts index 631fd76133..48a4f8de38 100644 --- a/packages/workspace/src/generators/new/generate-preset.ts +++ b/packages/workspace/src/generators/new/generate-preset.ts @@ -4,7 +4,7 @@ import { Tree, } from '@nrwl/devkit'; import { Preset } from '../utils/presets'; -import { nxVersion } from '../../utils/versions'; +import { angularCliVersion, nxVersion } from '../../utils/versions'; import { getNpmPackageVersion } from '../utils/get-npm-package-version'; import { NormalizedSchema } from './new'; import { join } from 'path'; @@ -89,7 +89,14 @@ function getPresetDependencies(preset: string, version?: string) { case Preset.AngularMonorepo: case Preset.AngularStandalone: - return { dependencies: { '@nrwl/angular': nxVersion }, dev: {} }; + return { + dependencies: { '@nrwl/angular': nxVersion }, + dev: { + '@angular-devkit/core': angularCliVersion, + '@angular-devkit/schematics': angularCliVersion, + '@schematics/angular': angularCliVersion, + }, + }; case Preset.Express: return { dependencies: {}, dev: { '@nrwl/express': nxVersion } }; diff --git a/scripts/check-imports.js b/scripts/check-imports.js index 4b309ab80d..bc9c6ad076 100644 --- a/scripts/check-imports.js +++ b/scripts/check-imports.js @@ -33,6 +33,7 @@ function check() { 'packages/workspace/src/command-line/report.spec.ts', 'packages/workspace/src/core/file-command-line-utils.ts', 'packages/workspace/src/generators/preset/preset.ts', + 'packages/workspace/src/generators/new/generate-preset.ts', 'packages/workspace/src/generators/init/init.ts', 'packages/workspace/src/utils/update-task.ts', 'packages/workspace/src/migrations/update-8-3-0/update-8-3-0.spec.ts',