From f10ecc1c8e53134e6e627c5a095a25d17c0138f9 Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Tue, 21 Mar 2023 10:39:07 +0000 Subject: [PATCH 01/76] chore(angular): disable flaky ssr test (#15788) --- e2e/angular-core/src/module-federation.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/e2e/angular-core/src/module-federation.test.ts b/e2e/angular-core/src/module-federation.test.ts index b9765f8ba1ac6..ad95725d5c94b 100644 --- a/e2e/angular-core/src/module-federation.test.ts +++ b/e2e/angular-core/src/module-federation.test.ts @@ -205,7 +205,8 @@ describe('Angular Projects', () => { } }, 10_000_000); - it('should scaffold a ssr MF setup successfully', async () => { + // TODO(colum): enable when this issue is resolved https://github.com/module-federation/universe/issues/604 + xit('should scaffold a ssr MF setup successfully', async () => { // ARRANGE const remoteApp1 = uniq('remote1'); const remoteApp2 = uniq('remote2'); From 7ebca5107e384e944dfdfb85b3a370e83c3114c6 Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Tue, 21 Mar 2023 08:48:16 -0400 Subject: [PATCH 02/76] feat(bundling): add for esbuild to enable/disable package.json generation (#15777) --- .../packages/esbuild/executors/esbuild.json | 5 + e2e/esbuild/src/esbuild.test.ts | 14 ++ packages/esbuild/migrations.json | 9 +- .../src/executors/esbuild/esbuild.impl.ts | 35 ++--- .../esbuild/lib/build-esbuild-options.spec.ts | 47 +++++-- .../executors/esbuild/lib/normalize.spec.ts | 127 +++++++++++++----- .../src/executors/esbuild/lib/normalize.ts | 17 ++- .../esbuild/src/executors/esbuild/schema.d.ts | 3 +- .../esbuild/src/executors/esbuild/schema.json | 5 + .../esbuild-project/esbuild-project.spec.ts | 1 - .../esbuild-project/esbuild-project.ts | 1 - .../set-generate-package-json.spec.ts | 73 ++++++++++ .../set-generate-package-json.ts | 32 +++++ packages/js/src/generators/library/library.ts | 4 + .../src/generators/application/application.ts | 1 + 15 files changed, 305 insertions(+), 69 deletions(-) create mode 100644 packages/esbuild/src/migrations/update-15-8-7/set-generate-package-json.spec.ts create mode 100644 packages/esbuild/src/migrations/update-15-8-7/set-generate-package-json.ts diff --git a/docs/generated/packages/esbuild/executors/esbuild.json b/docs/generated/packages/esbuild/executors/esbuild.json index 09a7622172461..fefe398783996 100644 --- a/docs/generated/packages/esbuild/executors/esbuild.json +++ b/docs/generated/packages/esbuild/executors/esbuild.json @@ -147,6 +147,11 @@ "default": false, "x-priority": "internal" }, + "generatePackageJson": { + "type": "boolean", + "description": "Generates a `package.json` and pruned lock file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated.", + "default": false + }, "thirdParty": { "type": "boolean", "description": "Includes third-party packages in the bundle (i.e. npm packages).", diff --git a/e2e/esbuild/src/esbuild.test.ts b/e2e/esbuild/src/esbuild.test.ts index 0acbafb0292ec..e33a792fabf4d 100644 --- a/e2e/esbuild/src/esbuild.test.ts +++ b/e2e/esbuild/src/esbuild.test.ts @@ -34,6 +34,17 @@ describe('EsBuild Plugin', () => { updateFile(`libs/${myPkg}/assets/a.md`, 'file a'); updateFile(`libs/${myPkg}/assets/b.md`, 'file b'); + // Copy package.json as asset rather than generate with Nx-detected fields. + runCLI(`build ${myPkg} --generatePackageJson=false`); + const packageJson = readJson(`libs/${myPkg}/package.json`); + // This is the file that is generated by lib generator (no deps, no main, etc.). + expect(packageJson).toEqual({ + name: `@proj/${myPkg}`, + version: '0.0.1', + type: 'commonjs', + }); + + // Build normally with package.json generation. runCLI(`build ${myPkg}`); expect(runCommand(`node dist/libs/${myPkg}/index.js`)).toMatch(/Hello/); @@ -41,6 +52,9 @@ describe('EsBuild Plugin', () => { checkFilesExist(`dist/libs/${myPkg}/package.json`); expect(runCommand(`node dist/libs/${myPkg}`)).toMatch(/Hello/); + expect(runCommand(`node dist/libs/${myPkg}/index.js`)).toMatch(/Hello/); + // main field should be set correctly in package.json + expect(readFile(`dist/libs/${myPkg}/assets/a.md`)).toMatch(/file a/); expect(readFile(`dist/libs/${myPkg}/assets/b.md`)).toMatch(/file b/); diff --git a/packages/esbuild/migrations.json b/packages/esbuild/migrations.json index 74bebae89a3ce..85b363d1938f6 100644 --- a/packages/esbuild/migrations.json +++ b/packages/esbuild/migrations.json @@ -1,5 +1,12 @@ { - "generators": {}, + "generators": { + "set-generate-package-json": { + "cli": "nx", + "version": "15.8.7-beta.0", + "description": "Set generatePackageJson to true to maintain existing behavior of generating package.json in output path.", + "factory": "./src/migrations/update-15-8-7/set-generate-package-json" + } + }, "packageJsonUpdates": { "15.7.0": { "version": "15.7.0-beta.0", diff --git a/packages/esbuild/src/executors/esbuild/esbuild.impl.ts b/packages/esbuild/src/executors/esbuild/esbuild.impl.ts index 717e881561bf7..26b07e0c68826 100644 --- a/packages/esbuild/src/executors/esbuild/esbuild.impl.ts +++ b/packages/esbuild/src/executors/esbuild/esbuild.impl.ts @@ -41,7 +41,7 @@ export async function* esbuildExecutor( _options: EsBuildExecutorOptions, context: ExecutorContext ) { - const options = normalizeOptions(_options); + const options = normalizeOptions(_options, context); if (options.deleteOutputPath) removeSync(options.outputPath); const assetsResult = await copyAssets(options, context); @@ -70,24 +70,27 @@ export async function* esbuildExecutor( } } - const cpjOptions: CopyPackageJsonOptions = { - ...options, - // TODO(jack): make types generate with esbuild - skipTypings: true, - outputFileExtensionForCjs: getOutExtension('cjs', options), - excludeLibsInPackageJson: !options.thirdParty, - updateBuildableProjectDepsInPackageJson: externalDependencies.length > 0, - }; + let packageJsonResult; + if (options.generatePackageJson) { + const cpjOptions: CopyPackageJsonOptions = { + ...options, + // TODO(jack): make types generate with esbuild + skipTypings: true, + outputFileExtensionForCjs: getOutExtension('cjs', options), + excludeLibsInPackageJson: !options.thirdParty, + updateBuildableProjectDepsInPackageJson: externalDependencies.length > 0, + }; + + // If we're bundling third-party packages, then any extra deps from external should be the only deps in package.json + if (options.thirdParty && externalDependencies.length > 0) { + cpjOptions.overrideDependencies = externalDependencies; + } else { + cpjOptions.extraDependencies = externalDependencies; + } - // If we're bundling third-party packages, then any extra deps from external should be the only deps in package.json - if (options.thirdParty && externalDependencies.length > 0) { - cpjOptions.overrideDependencies = externalDependencies; - } else { - cpjOptions.extraDependencies = externalDependencies; + packageJsonResult = await copyPackageJson(cpjOptions, context); } - const packageJsonResult = await copyPackageJson(cpjOptions, context); - if ('context' in esbuild) { // 0.17.0+ adds esbuild.context and context.watch() if (options.watch) { diff --git a/packages/esbuild/src/executors/esbuild/lib/build-esbuild-options.spec.ts b/packages/esbuild/src/executors/esbuild/lib/build-esbuild-options.spec.ts index 27af26c0c2134..b7fe2105a3deb 100644 --- a/packages/esbuild/src/executors/esbuild/lib/build-esbuild-options.spec.ts +++ b/packages/esbuild/src/executors/esbuild/lib/build-esbuild-options.spec.ts @@ -45,7 +45,6 @@ describe('buildEsbuildOptions', () => { main: 'apps/myapp/src/index.ts', outputPath: 'dist/apps/myapp', tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', assets: [], outputFileName: 'index.js', singleEntry: true, @@ -82,7 +81,6 @@ describe('buildEsbuildOptions', () => { additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'], outputPath: 'dist/apps/myapp', tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', assets: [], outputFileName: 'index.js', singleEntry: false, @@ -118,7 +116,6 @@ describe('buildEsbuildOptions', () => { main: 'apps/myapp/src/index.ts', outputPath: 'dist/apps/myapp', tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', assets: [], outputFileName: 'index.js', singleEntry: true, @@ -154,7 +151,6 @@ describe('buildEsbuildOptions', () => { main: 'apps/myapp/src/index.ts', outputPath: 'dist/apps/myapp', tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', assets: [], outputFileName: 'index.js', singleEntry: true, @@ -187,7 +183,6 @@ describe('buildEsbuildOptions', () => { main: 'apps/myapp/src/index.ts', outputPath: 'dist/apps/myapp', tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', outputFileName: 'index.js', assets: [], singleEntry: true, @@ -223,7 +218,6 @@ describe('buildEsbuildOptions', () => { main: 'apps/myapp/src/index.ts', outputPath: 'dist/apps/myapp', tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', outputFileName: 'index.js', assets: [], singleEntry: true, @@ -260,7 +254,6 @@ describe('buildEsbuildOptions', () => { main: 'apps/myapp/src/index.ts', outputPath: 'dist/apps/myapp', tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', outputFileName: 'index.js', assets: [], singleEntry: true, @@ -298,10 +291,9 @@ describe('buildEsbuildOptions', () => { main: 'apps/myapp/src/index.ts', outputPath: 'dist/apps/myapp', tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', - outputFileName: 'index.js', assets: [], singleEntry: true, + outputFileName: 'index.js', external: ['foo'], esbuildOptions: { external: ['bar'], @@ -334,8 +326,6 @@ describe('buildEsbuildOptions', () => { main: 'apps/myapp/src/index.ts', outputPath: 'dist/apps/myapp', tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', - outputFileName: 'index.js', assets: [], singleEntry: true, external: ['foo'], @@ -367,7 +357,40 @@ describe('buildEsbuildOptions', () => { main: 'apps/myapp/src/index.ts', outputPath: 'dist/apps/myapp', tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', + outputFileName: 'index.js', + assets: [], + singleEntry: true, + sourcemap: true, + external: [], + }, + context + ) + ).toEqual({ + bundle: false, + entryNames: '[dir]/[name]', + entryPoints: ['apps/myapp/src/index.ts'], + format: 'esm', + platform: 'node', + outdir: 'dist/apps/myapp', + tsconfig: 'apps/myapp/tsconfig.app.json', + external: undefined, + sourcemap: true, + outExtension: { + '.js': '.js', + }, + }); + }); + + it('should set sourcemap', () => { + expect( + buildEsbuildOptions( + 'esm', + { + bundle: false, + platform: 'node', + main: 'apps/myapp/src/index.ts', + outputPath: 'dist/apps/myapp', + tsConfig: 'apps/myapp/tsconfig.app.json', outputFileName: 'index.js', assets: [], singleEntry: true, diff --git a/packages/esbuild/src/executors/esbuild/lib/normalize.spec.ts b/packages/esbuild/src/executors/esbuild/lib/normalize.spec.ts index 5b1451bfd23b2..4f9fed0b307fa 100644 --- a/packages/esbuild/src/executors/esbuild/lib/normalize.spec.ts +++ b/packages/esbuild/src/executors/esbuild/lib/normalize.spec.ts @@ -1,21 +1,45 @@ import { normalizeOptions } from './normalize'; +import { ExecutorContext } from '@nrwl/devkit'; describe('normalizeOptions', () => { + const context: ExecutorContext = { + root: '/', + cwd: '/', + isVerbose: false, + projectName: 'myapp', + projectGraph: { + nodes: { + myapp: { + type: 'app', + name: 'myapp', + data: { + root: 'apps/myapp', + files: [], + }, + }, + }, + dependencies: {}, + }, + }; + it('should handle single entry point options', () => { expect( - normalizeOptions({ - main: 'apps/myapp/src/index.ts', - outputPath: 'dist/apps/myapp', - tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', - assets: [], - }) + normalizeOptions( + { + main: 'apps/myapp/src/index.ts', + outputPath: 'dist/apps/myapp', + tsConfig: 'apps/myapp/tsconfig.app.json', + generatePackageJson: true, + assets: [], + }, + context + ) ).toEqual({ main: 'apps/myapp/src/index.ts', outputPath: 'dist/apps/myapp', tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', assets: [], + generatePackageJson: true, outputFileName: 'index.js', singleEntry: true, external: [], @@ -24,20 +48,23 @@ describe('normalizeOptions', () => { it('should handle multiple entry point options', () => { expect( - normalizeOptions({ - main: 'apps/myapp/src/index.ts', - outputPath: 'dist/apps/myapp', - tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', - assets: [], - additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'], - }) + normalizeOptions( + { + main: 'apps/myapp/src/index.ts', + outputPath: 'dist/apps/myapp', + tsConfig: 'apps/myapp/tsconfig.app.json', + assets: [], + generatePackageJson: true, + additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'], + }, + context + ) ).toEqual({ main: 'apps/myapp/src/index.ts', outputPath: 'dist/apps/myapp', tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', assets: [], + generatePackageJson: true, outputFileName: 'index.js', additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'], singleEntry: false, @@ -47,20 +74,23 @@ describe('normalizeOptions', () => { it('should support custom output file name', () => { expect( - normalizeOptions({ - main: 'apps/myapp/src/index.ts', - outputPath: 'dist/apps/myapp', - tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', - assets: [], - outputFileName: 'test.js', - }) + normalizeOptions( + { + main: 'apps/myapp/src/index.ts', + outputPath: 'dist/apps/myapp', + tsConfig: 'apps/myapp/tsconfig.app.json', + assets: [], + generatePackageJson: true, + outputFileName: 'test.js', + }, + context + ) ).toEqual({ main: 'apps/myapp/src/index.ts', outputPath: 'dist/apps/myapp', tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', assets: [], + generatePackageJson: true, outputFileName: 'test.js', singleEntry: true, external: [], @@ -69,15 +99,42 @@ describe('normalizeOptions', () => { it('should validate against multiple entry points + outputFileName', () => { expect(() => - normalizeOptions({ - main: 'apps/myapp/src/index.ts', - outputPath: 'dist/apps/myapp', - tsConfig: 'apps/myapp/tsconfig.app.json', - project: 'apps/myapp/package.json', - assets: [], - additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'], - outputFileName: 'test.js', - }) + normalizeOptions( + { + main: 'apps/myapp/src/index.ts', + outputPath: 'dist/apps/myapp', + tsConfig: 'apps/myapp/tsconfig.app.json', + assets: [], + generatePackageJson: true, + additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'], + outputFileName: 'test.js', + }, + context + ) ).toThrow(/Cannot use/); }); + + it('should add package.json to assets array if generatePackageJson is false', () => { + expect( + normalizeOptions( + { + main: 'apps/myapp/src/index.ts', + outputPath: 'dist/apps/myapp', + tsConfig: 'apps/myapp/tsconfig.app.json', + generatePackageJson: false, + assets: [], + }, + context + ) + ).toEqual({ + main: 'apps/myapp/src/index.ts', + outputPath: 'dist/apps/myapp', + tsConfig: 'apps/myapp/tsconfig.app.json', + assets: ['apps/myapp/package.json'], + generatePackageJson: false, + outputFileName: 'index.js', + singleEntry: true, + external: [], + }); + }); }); diff --git a/packages/esbuild/src/executors/esbuild/lib/normalize.ts b/packages/esbuild/src/executors/esbuild/lib/normalize.ts index a52a408cd3992..e1114e9ef7b43 100644 --- a/packages/esbuild/src/executors/esbuild/lib/normalize.ts +++ b/packages/esbuild/src/executors/esbuild/lib/normalize.ts @@ -3,10 +3,23 @@ import { EsBuildExecutorOptions, NormalizedEsBuildExecutorOptions, } from '../schema'; +import { ExecutorContext, joinPathFragments } from '@nrwl/devkit'; export function normalizeOptions( - options: EsBuildExecutorOptions + options: EsBuildExecutorOptions, + context: ExecutorContext ): NormalizedEsBuildExecutorOptions { + // If we're not generating package.json file, then copy it as-is as an asset. + const assets = options.generatePackageJson + ? options.assets + : [ + ...options.assets, + joinPathFragments( + context.projectGraph.nodes[context.projectName].data.root, + 'package.json' + ), + ]; + if (options.additionalEntryPoints?.length > 0) { const { outputFileName, ...rest } = options; if (outputFileName) { @@ -16,6 +29,7 @@ export function normalizeOptions( } return { ...rest, + assets, external: options.external ?? [], singleEntry: false, // Use the `main` file name as the output file name. @@ -26,6 +40,7 @@ export function normalizeOptions( } else { return { ...options, + assets, external: options.external ?? [], singleEntry: true, outputFileName: diff --git a/packages/esbuild/src/executors/esbuild/schema.d.ts b/packages/esbuild/src/executors/esbuild/schema.d.ts index 8ff607c23cbbb..ee23cfba2747c 100644 --- a/packages/esbuild/src/executors/esbuild/schema.d.ts +++ b/packages/esbuild/src/executors/esbuild/schema.d.ts @@ -4,7 +4,7 @@ type Compiler = 'babel' | 'swc'; export interface EsBuildExecutorOptions { additionalEntryPoints?: string[]; - assets: AssetGlob[]; + assets: (AssetGlob | string)[]; buildableProjectDepsInPackageJsonType?: 'dependencies' | 'peerDependencies'; bundle?: boolean; deleteOutputPath?: boolean; @@ -20,7 +20,6 @@ export interface EsBuildExecutorOptions { outputHashing?: 'none' | 'all'; outputPath: string; platform?: 'node' | 'browser' | 'neutral'; - project: string; sourcemap?: boolean | 'linked' | 'inline' | 'external' | 'both'; skipTypeCheck?: boolean; target?: string; diff --git a/packages/esbuild/src/executors/esbuild/schema.json b/packages/esbuild/src/executors/esbuild/schema.json index 371c012cc7f99..5675e1764d943 100644 --- a/packages/esbuild/src/executors/esbuild/schema.json +++ b/packages/esbuild/src/executors/esbuild/schema.json @@ -122,6 +122,11 @@ "default": false, "x-priority": "internal" }, + "generatePackageJson": { + "type": "boolean", + "description": "Generates a `package.json` and pruned lock file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated.", + "default": false + }, "thirdParty": { "type": "boolean", "description": "Includes third-party packages in the bundle (i.e. npm packages).", diff --git a/packages/esbuild/src/generators/esbuild-project/esbuild-project.spec.ts b/packages/esbuild/src/generators/esbuild-project/esbuild-project.spec.ts index 583c97d8e4dca..808c51bf3ce27 100644 --- a/packages/esbuild/src/generators/esbuild-project/esbuild-project.spec.ts +++ b/packages/esbuild/src/generators/esbuild-project/esbuild-project.spec.ts @@ -36,7 +36,6 @@ describe('esbuildProjectGenerator', () => { main: `libs/mypkg/src/${main}`, outputFileName: 'main.js', outputPath: 'dist/libs/mypkg', - project: 'libs/mypkg/package.json', tsConfig: `libs/mypkg/${tsConfig}`, }); }); diff --git a/packages/esbuild/src/generators/esbuild-project/esbuild-project.ts b/packages/esbuild/src/generators/esbuild-project/esbuild-project.ts index d5fbf0a3814eb..eafafb804926d 100644 --- a/packages/esbuild/src/generators/esbuild-project/esbuild-project.ts +++ b/packages/esbuild/src/generators/esbuild-project/esbuild-project.ts @@ -55,7 +55,6 @@ function addBuildTarget(tree: Tree, options: EsBuildProjectSchema) { outputPath: joinPathFragments('dist', project.root), outputFileName: 'main.js', tsConfig, - project: `${project.root}/package.json`, assets: [], platform: options.platform, }; diff --git a/packages/esbuild/src/migrations/update-15-8-7/set-generate-package-json.spec.ts b/packages/esbuild/src/migrations/update-15-8-7/set-generate-package-json.spec.ts new file mode 100644 index 0000000000000..c23eef22b457c --- /dev/null +++ b/packages/esbuild/src/migrations/update-15-8-7/set-generate-package-json.spec.ts @@ -0,0 +1,73 @@ +import { + addProjectConfiguration, + readProjectConfiguration, + Tree, +} from '@nrwl/devkit'; +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import update from './set-generate-package-json'; + +describe('Migration: Set generatePackageJson', () => { + let tree: Tree; + + beforeEach(() => { + tree = createTreeWithEmptyWorkspace(); + }); + + it('should keep existing generatePackageJson option if it exists', async () => { + addProjectConfiguration(tree, 'myapp', { + root: 'myapp', + targets: { + build: { + executor: '@nrwl/esbuild:esbuild', + options: { + generatePackageJson: false, + }, + }, + }, + }); + + await update(tree); + + const config = readProjectConfiguration(tree, 'myapp'); + + expect(config.targets.build.options).toEqual({ + generatePackageJson: false, + }); + }); + + it('should set generatePackageJson to true for esbuild targets', async () => { + addProjectConfiguration(tree, 'myapp', { + root: 'myapp', + targets: { + build: { + executor: '@nrwl/esbuild:esbuild', + }, + }, + }); + + await update(tree); + + const config = readProjectConfiguration(tree, 'myapp'); + + expect(config.targets.build.options).toEqual({ + generatePackageJson: true, + }); + }); + + it('should ignore targets not using esbuild', async () => { + addProjectConfiguration(tree, 'myapp', { + root: 'myapp', + targets: { + build: { + executor: '@nrwl/webpack:webpack', + }, + }, + }); + + await update(tree); + + const config = readProjectConfiguration(tree, 'myapp'); + + expect(config.targets.build.options).toBeUndefined(); + }); +}); diff --git a/packages/esbuild/src/migrations/update-15-8-7/set-generate-package-json.ts b/packages/esbuild/src/migrations/update-15-8-7/set-generate-package-json.ts new file mode 100644 index 0000000000000..d2d8535167935 --- /dev/null +++ b/packages/esbuild/src/migrations/update-15-8-7/set-generate-package-json.ts @@ -0,0 +1,32 @@ +import type { Tree } from '@nrwl/devkit'; +import { + formatFiles, + getProjects, + updateProjectConfiguration, +} from '@nrwl/devkit'; + +export default async function update(tree: Tree): Promise { + const projects = getProjects(tree); + + projects.forEach((projectConfig, projectName) => { + let shouldUpdate = false; + + Object.entries(projectConfig.targets).forEach( + ([targetName, targetConfig]) => { + if (targetConfig.executor === '@nrwl/esbuild:esbuild') { + shouldUpdate = true; + + projectConfig.targets[targetName].options ??= {}; + projectConfig.targets[targetName].options.generatePackageJson ??= + true; + } + } + ); + + if (shouldUpdate) { + updateProjectConfiguration(tree, projectName, projectConfig); + } + }); + + await formatFiles(tree); +} diff --git a/packages/js/src/generators/library/library.ts b/packages/js/src/generators/library/library.ts index ef5a750449db4..6fe60afcb6e63 100644 --- a/packages/js/src/generators/library/library.ts +++ b/packages/js/src/generators/library/library.ts @@ -163,6 +163,10 @@ function addProject( }, }; + if (options.bundler === 'esbuild') { + projectConfiguration.targets.build.options.generatePackageJson = true; + } + if (options.bundler === 'rollup') { projectConfiguration.targets.build.options.project = `${options.projectRoot}/package.json`; projectConfiguration.targets.build.options.compiler = 'swc'; diff --git a/packages/node/src/generators/application/application.ts b/packages/node/src/generators/application/application.ts index 1e22ed224b59c..26eac2dbf7d59 100644 --- a/packages/node/src/generators/application/application.ts +++ b/packages/node/src/generators/application/application.ts @@ -110,6 +110,7 @@ function getEsBuildConfig( ), tsConfig: joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'), assets: [joinPathFragments(project.sourceRoot, 'assets')], + generatePackageJson: true, esbuildOptions: { sourcemap: true, // Generate CJS files as .js so imports can be './foo' rather than './foo.cjs'. From 63cdddfa1130336f24bc203c74e2cfc49e65aafa Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Tue, 21 Mar 2023 11:21:02 -0400 Subject: [PATCH 03/76] fix(nextjs): handle buildable libs correctly (#15795) --- e2e/next/src/next.test.ts | 33 ++++++++++++++++------- packages/next/plugins/with-nx.ts | 2 +- packages/next/src/utils/buildable-libs.ts | 30 --------------------- 3 files changed, 25 insertions(+), 40 deletions(-) delete mode 100644 packages/next/src/utils/buildable-libs.ts diff --git a/e2e/next/src/next.test.ts b/e2e/next/src/next.test.ts index 663d85c00e1cf..932dd116cca40 100644 --- a/e2e/next/src/next.test.ts +++ b/e2e/next/src/next.test.ts @@ -45,10 +45,14 @@ describe('Next.js Applications', () => { const appName = uniq('app'); const nextLib = uniq('nextlib'); const jsLib = uniq('tslib'); + const buildableLib = uniq('buildablelib'); runCLI(`generate @nrwl/next:app ${appName} --no-interactive --style=css`); runCLI(`generate @nrwl/next:lib ${nextLib} --no-interactive`); runCLI(`generate @nrwl/js:lib ${jsLib} --no-interactive`); + runCLI( + `generate @nrwl/js:lib ${buildableLib} --no-interactive --bundler=vite` + ); // Create file in public that should be copied to dist updateFile(`apps/${appName}/public/a/b.txt`, `Hello World!`); @@ -77,27 +81,36 @@ describe('Next.js Applications', () => { updateFile( `libs/${jsLib}/src/lib/${jsLib}.ts`, ` - export function testFn(): string { + export function jsLib(): string { return 'Hello Nx'; }; // testing whether async-await code in Node / Next.js api routes works as expected - export async function testAsyncFn() { + export async function jsLibAsync() { return await Promise.resolve('hell0'); } ` ); + updateFile( + `libs/${buildableLib}/src/lib/${buildableLib}.ts`, + ` + export function buildableLib(): string { + return 'Hello Buildable'; + }; + ` + ); + const mainPath = `apps/${appName}/pages/index.tsx`; const content = readFile(mainPath); updateFile( `apps/${appName}/pages/api/hello.ts`, ` - import { testAsyncFn } from '@${proj}/${jsLib}'; + import { jsLibAsync } from '@${proj}/${jsLib}'; export default async function handler(_, res) { - const value = await testAsyncFn(); + const value = await jsLibAsync(); res.send(value); } ` @@ -106,7 +119,8 @@ describe('Next.js Applications', () => { updateFile( mainPath, ` - import { testFn } from '@${proj}/${jsLib}'; + import { jsLib } from '@${proj}/${jsLib}'; + import { buildableLib } from '@${proj}/${buildableLib}'; /* eslint-disable */ import dynamic from 'next/dynamic'; @@ -119,7 +133,8 @@ describe('Next.js Applications', () => { ``, `
- {testFn()} + {jsLib()} + {buildableLib()}
` @@ -194,7 +209,7 @@ describe('Next.js Applications', () => { updateFile( `libs/${jsLib}/src/lib/${jsLib}.ts`, ` - export function testFn(): string { + export function jsLib(): string { return process.env.NX_CUSTOM_VAR; }; ` @@ -204,11 +219,11 @@ describe('Next.js Applications', () => { `apps/${appName}/pages/index.tsx`, ` import React from 'react'; - import { testFn } from '@${proj}/${jsLib}'; + import { jsLib } from '@${proj}/${jsLib}'; export const Index = ({ greeting }: any) => { return ( -

{testFn()}

+

{jsLib()}

); }; export default Index; diff --git a/packages/next/plugins/with-nx.ts b/packages/next/plugins/with-nx.ts index f59699cf7c014..211850bbe94ad 100644 --- a/packages/next/plugins/with-nx.ts +++ b/packages/next/plugins/with-nx.ts @@ -134,7 +134,7 @@ export function withNx( } = getNxContext(graph, originalTarget); const projectDirectory = projectNode.data.root; - if (!options.buildLibsFromSource && targetName) { + if (options.buildLibsFromSource === false && targetName) { const result = calculateProjectDependencies( graph, workspaceRoot, diff --git a/packages/next/src/utils/buildable-libs.ts b/packages/next/src/utils/buildable-libs.ts deleted file mode 100644 index 38fdcbf004d85..0000000000000 --- a/packages/next/src/utils/buildable-libs.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - DependentBuildableProjectNode, - findMissingBuildDependencies, -} from '@nrwl/js/src/utils/buildable-libs-utils'; -import * as chalk from 'chalk'; -import { ExecutorContext, stripIndents } from '@nrwl/devkit'; - -export function assertDependentProjectsHaveBeenBuilt( - dependencies: DependentBuildableProjectNode[], - context: ExecutorContext -) { - const missing = findMissingBuildDependencies( - context.root, - context.projectName, - context.targetName, - dependencies - ); - if (missing.length > 0) { - throw new Error( - chalk.red(stripIndents` - Some of the project ${ - context.projectName - }'s dependencies have not been built yet. - - Please build these libraries first: - ${missing.map((x) => ` - ${x.node.name}`).join('\n')} - `) - ); - } -} From e772ee22bd4a5bdf26a0ac671b0485a5c9a335c0 Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Tue, 21 Mar 2023 11:46:12 -0400 Subject: [PATCH 04/76] feat(web): remove hard dependencies on cypress, rollup, jest, and linter (#15797) --- .../packages/web/generators/application.json | 2 +- packages/web/package.json | 4 -- .../src/generators/application/application.ts | 44 ++++++++++++------- .../src/generators/application/schema.d.ts | 2 +- .../src/generators/application/schema.json | 2 +- packages/web/src/generators/init/init.ts | 8 +++- 6 files changed, 37 insertions(+), 25 deletions(-) diff --git a/docs/generated/packages/web/generators/application.json b/docs/generated/packages/web/generators/application.json index 51a1a4735786a..5b2bc487bbd4e 100644 --- a/docs/generated/packages/web/generators/application.json +++ b/docs/generated/packages/web/generators/application.json @@ -62,7 +62,7 @@ "linter": { "description": "The tool to use for running lint checks.", "type": "string", - "enum": ["eslint"], + "enum": ["eslint", "none"], "default": "eslint" }, "skipFormat": { diff --git a/packages/web/package.json b/packages/web/package.json index 00d7db2171a7e..6bc4547ed6a9e 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -30,12 +30,8 @@ "migrations": "./migrations.json" }, "dependencies": { - "@nrwl/cypress": "file:../cypress", "@nrwl/devkit": "file:../devkit", - "@nrwl/jest": "file:../jest", "@nrwl/js": "file:../js", - "@nrwl/linter": "file:../linter", - "@nrwl/rollup": "file:../rollup", "@nrwl/workspace": "file:../workspace", "chalk": "^4.1.0", "chokidar": "^3.5.1", diff --git a/packages/web/src/generators/application/application.ts b/packages/web/src/generators/application/application.ts index 176f31fbea5a7..5190365f962cf 100644 --- a/packages/web/src/generators/application/application.ts +++ b/packages/web/src/generators/application/application.ts @@ -1,5 +1,4 @@ import { join } from 'path'; -import { cypressProjectGenerator } from '@nrwl/cypress'; import { addDependenciesToPackageJson, addProjectConfiguration, @@ -21,9 +20,8 @@ import { updateNxJson, updateProjectConfiguration, } from '@nrwl/devkit'; -import { jestProjectGenerator } from '@nrwl/jest'; import { swcCoreVersion } from '@nrwl/js/src/utils/versions'; -import { Linter, lintProjectGenerator } from '@nrwl/linter'; +import type { Linter } from '@nrwl/linter'; import { getRelativePathToRootTsConfig } from '@nrwl/js'; @@ -247,20 +245,30 @@ export async function applicationGenerator(host: Tree, schema: Schema) { ); } - const lintTask = await lintProjectGenerator(host, { - linter: options.linter, - project: options.projectName, - tsConfigPaths: [ - joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'), - ], - unitTestRunner: options.unitTestRunner, - eslintFilePatterns: [`${options.appProjectRoot}/**/*.ts`], - skipFormat: true, - setParserOptionsProject: options.setParserOptionsProject, - }); - tasks.push(lintTask); + if (options.linter === 'eslint') { + const { lintProjectGenerator } = await ensurePackage( + '@nrwl/linter', + nxVersion + ); + const lintTask = await lintProjectGenerator(host, { + linter: options.linter, + project: options.projectName, + tsConfigPaths: [ + joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'), + ], + unitTestRunner: options.unitTestRunner, + eslintFilePatterns: [`${options.appProjectRoot}/**/*.ts`], + skipFormat: true, + setParserOptionsProject: options.setParserOptionsProject, + }); + tasks.push(lintTask); + } if (options.e2eTestRunner === 'cypress') { + const { cypressProjectGenerator } = await ensurePackage( + '@nrwl/cypress', + nxVersion + ); const cypressTask = await cypressProjectGenerator(host, { ...options, name: `${options.name}-e2e`, @@ -270,6 +278,10 @@ export async function applicationGenerator(host: Tree, schema: Schema) { tasks.push(cypressTask); } if (options.unitTestRunner === 'jest') { + const { jestProjectGenerator } = await ensurePackage( + '@nrwl/jest', + nxVersion + ); const jestTask = await jestProjectGenerator(host, { project: options.projectName, skipSerializers: true, @@ -323,7 +335,7 @@ function normalizeOptions(host: Tree, options: Schema): NormalizedSchema { } options.style = options.style || 'css'; - options.linter = options.linter || Linter.EsLint; + options.linter = options.linter || ('eslint' as Linter.EsLint); options.unitTestRunner = options.unitTestRunner || 'jest'; options.e2eTestRunner = options.e2eTestRunner || 'cypress'; diff --git a/packages/web/src/generators/application/schema.d.ts b/packages/web/src/generators/application/schema.d.ts index 8c66c906e0a48..e6af676c23b9a 100644 --- a/packages/web/src/generators/application/schema.d.ts +++ b/packages/web/src/generators/application/schema.d.ts @@ -1,4 +1,4 @@ -import { Linter } from '@nrwl/linter'; +import type { Linter } from '@nrwl/linter'; export interface Schema { name: string; diff --git a/packages/web/src/generators/application/schema.json b/packages/web/src/generators/application/schema.json index ef15dbe98ad04..56bd2ac79336c 100644 --- a/packages/web/src/generators/application/schema.json +++ b/packages/web/src/generators/application/schema.json @@ -65,7 +65,7 @@ "linter": { "description": "The tool to use for running lint checks.", "type": "string", - "enum": ["eslint"], + "enum": ["eslint", "none"], "default": "eslint" }, "skipFormat": { diff --git a/packages/web/src/generators/init/init.ts b/packages/web/src/generators/init/init.ts index 104cb5561c1f2..a55564bad6a88 100644 --- a/packages/web/src/generators/init/init.ts +++ b/packages/web/src/generators/init/init.ts @@ -1,14 +1,13 @@ -import { cypressInitGenerator } from '@nrwl/cypress'; import { addDependenciesToPackageJson, convertNxGenerator, + ensurePackage, formatFiles, GeneratorCallback, removeDependenciesFromPackageJson, runTasksInSerial, Tree, } from '@nrwl/devkit'; -import { jestInitGenerator } from '@nrwl/jest'; import { addBabelInputs } from '@nrwl/js/src/utils/add-babel-inputs'; import { initGenerator as jsInitGenerator } from '@nrwl/js'; @@ -48,12 +47,17 @@ export async function webInitGenerator(tree: Tree, schema: Schema) { tasks.push(jsInitTask); if (!schema.unitTestRunner || schema.unitTestRunner === 'jest') { + const { jestInitGenerator } = await ensurePackage('@nrwl/jest', nxVersion); const jestTask = await jestInitGenerator(tree, { skipPackageJson: schema.skipPackageJson, }); tasks.push(jestTask); } if (!schema.e2eTestRunner || schema.e2eTestRunner === 'cypress') { + const { cypressInitGenerator } = await ensurePackage( + '@nrwl/cypress', + nxVersion + ); const cypressTask = await cypressInitGenerator(tree, { skipPackageJson: schema.skipPackageJson, }); From 0cb817b37f864df78d1326ca1c3408cc8b2793c4 Mon Sep 17 00:00:00 2001 From: Emily Xiong Date: Tue, 21 Mar 2023 13:37:38 -0400 Subject: [PATCH 05/76] feat(react-native): add IntelliJ to react native and expo welcome page (#15744) --- .../files/src/app/App.tsx.template | 46 +++++++++++++++---- .../files/app/src/app/App.tsx.template | 46 +++++++++++++++---- 2 files changed, 76 insertions(+), 16 deletions(-) diff --git a/packages/expo/src/generators/application/files/src/app/App.tsx.template b/packages/expo/src/generators/application/files/src/app/App.tsx.template index 3b18e0a8de36d..21b55d2ad8b09 100644 --- a/packages/expo/src/generators/application/files/src/app/App.tsx.template +++ b/packages/expo/src/generators/application/files/src/app/App.tsx.template @@ -280,7 +280,7 @@ export const App = () => { - + Linking.openURL( @@ -288,7 +288,7 @@ export const App = () => { ) } > - + { styles.marginBottomSm, ]} > - Install Nx Console + Install Nx Console for VSCode - Plugin for VSCode + The official VSCode plugin for Nx. + + + + + + Linking.openURL( + 'https://plugins.jetbrains.com/plugin/21060-nx-console' + ) + } + > + + + + + + + Install Nx Console for JetBrains + + + Available for WebStorm, Intellij IDEA Ultimate and more! @@ -461,9 +493,7 @@ export const App = () => { - - nx graph - + nx graph { styles.marginBottomMd, ]} > - nx affected:dep-graph + nx affected:graph # run tests for current changes diff --git a/packages/react-native/src/generators/application/files/app/src/app/App.tsx.template b/packages/react-native/src/generators/application/files/app/src/app/App.tsx.template index 219c52eb30ae2..89e1c012f358b 100644 --- a/packages/react-native/src/generators/application/files/app/src/app/App.tsx.template +++ b/packages/react-native/src/generators/application/files/app/src/app/App.tsx.template @@ -280,7 +280,7 @@ export const App = () => { - + Linking.openURL( @@ -288,7 +288,7 @@ export const App = () => { ) } > - + { styles.marginBottomSm, ]} > - Install Nx Console + Install Nx Console for VSCode - Plugin for VSCode + The official VSCode plugin for Nx. + + + + + + Linking.openURL( + 'https://plugins.jetbrains.com/plugin/21060-nx-console' + ) + } + > + + + + + + + Install Nx Console for JetBrains + + + Available for WebStorm, Intellij IDEA Ultimate and more! @@ -440,7 +472,7 @@ export const App = () => { - { - - nx graph - + nx graph Date: Tue, 21 Mar 2023 15:04:55 -0400 Subject: [PATCH 06/76] fix(core): move the daemon stop log out of client (#15783) --- packages/nx/src/command-line/daemon.ts | 3 ++- packages/nx/src/command-line/reset.ts | 1 + packages/nx/src/daemon/client/client.ts | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/nx/src/command-line/daemon.ts b/packages/nx/src/command-line/daemon.ts index 74d7dda8e24ee..144a035de4cb1 100644 --- a/packages/nx/src/command-line/daemon.ts +++ b/packages/nx/src/command-line/daemon.ts @@ -17,7 +17,8 @@ export async function daemonHandler(args: Arguments) { }); } else if (args.stop) { const { daemonClient } = await import('../daemon/client/client'); - daemonClient.stop(); + await daemonClient.stop(); + output.log({ title: 'Daemon Server - Stopped' }); } else { console.log(generateDaemonHelpOutput()); } diff --git a/packages/nx/src/command-line/reset.ts b/packages/nx/src/command-line/reset.ts index a266e5c603592..3e65432c2b919 100644 --- a/packages/nx/src/command-line/reset.ts +++ b/packages/nx/src/command-line/reset.ts @@ -9,6 +9,7 @@ export async function resetHandler() { bodyLines: [`This might take a few minutes.`], }); await daemonClient.stop(); + output.log({ title: 'Daemon Server - Stopped' }); removeSync(cacheDir); if (projectGraphCacheDirectory !== cacheDir) { removeSync(projectGraphCacheDirectory); diff --git a/packages/nx/src/daemon/client/client.ts b/packages/nx/src/daemon/client/client.ts index f42fb18badb23..bf81546e0ab5d 100644 --- a/packages/nx/src/daemon/client/client.ts +++ b/packages/nx/src/daemon/client/client.ts @@ -376,7 +376,6 @@ export class DaemonClient { } removeSocketDir(); - output.log({ title: 'Daemon Server - Stopped' }); } } From a5d12584a1cc85e79eb37965665d7ecfdc34b7ec Mon Sep 17 00:00:00 2001 From: Emily Xiong Date: Tue, 21 Mar 2023 15:12:47 -0400 Subject: [PATCH 07/76] bugfix(expo): update expo 48.0.6 to use react-native 0.71.4 (#15804) --- packages/expo/migrations.json | 13 +++++++++++++ packages/expo/src/utils/versions.ts | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/expo/migrations.json b/packages/expo/migrations.json index 4abb038258cca..2225a01a79294 100644 --- a/packages/expo/migrations.json +++ b/packages/expo/migrations.json @@ -704,6 +704,19 @@ "alwaysAddToPackageJson": false } } + }, + "15.9.0": { + "version": "15.9.0-beta.0", + "packages": { + "react-native": { + "version": "0.71.4", + "alwaysAddToPackageJson": false + }, + "@types/react-native": { + "version": "0.71.4", + "alwaysAddToPackageJson": false + } + } } } } diff --git a/packages/expo/src/utils/versions.ts b/packages/expo/src/utils/versions.ts index 0fcc170ae5997..dc2fb4d896e76 100644 --- a/packages/expo/src/utils/versions.ts +++ b/packages/expo/src/utils/versions.ts @@ -14,8 +14,8 @@ export const reactDomVersion = '18.2.0'; export const reactTestRendererVersion = '18.2.0'; export const typesReactVersion = '18.0.28'; -export const reactNativeVersion = '0.71.3'; -export const typesReactNativeVersion = '0.71.3'; +export const reactNativeVersion = '0.71.4'; +export const typesReactNativeVersion = '0.71.4'; export const reactNativeWebVersion = '~0.18.12'; export const reactNativeSvgTransformerVersion = '1.0.0'; From 247e8a2b8ab2cd0fcf394c8a1c8bd6c3b374dde2 Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Tue, 21 Mar 2023 16:24:24 -0400 Subject: [PATCH 08/76] feat(devkit): provide better error messaging when attempting to use a FsTree instance after changes are committed to disk. (#15780) --- packages/nx/src/command-line/generate.ts | 2 ++ packages/nx/src/command-line/migrate.ts | 1 + packages/nx/src/command-line/new.ts | 1 + packages/nx/src/generators/tree.ts | 38 ++++++++++++++++++++++++ 4 files changed, 42 insertions(+) diff --git a/packages/nx/src/command-line/generate.ts b/packages/nx/src/command-line/generate.ts index 36f3fef3bb91b..eeb84f935656b 100644 --- a/packages/nx/src/command-line/generate.ts +++ b/packages/nx/src/command-line/generate.ts @@ -372,6 +372,8 @@ export async function generate(cwd: string, args: { [k: string]: any }) { } const task = await implementation(host, combinedOpts); + host.lock(); + const changes = host.listChanges(); printChanges(changes); diff --git a/packages/nx/src/command-line/migrate.ts b/packages/nx/src/command-line/migrate.ts index decd9c14b77fa..b94fd8d669247 100644 --- a/packages/nx/src/command-line/migrate.ts +++ b/packages/nx/src/command-line/migrate.ts @@ -1540,6 +1540,7 @@ async function runNxMigration(root: string, packageName: string, name: string) { const fn = require(implPath).default; const host = new FsTree(root, false); await fn(host, {}); + host.lock(); const changes = host.listChanges(); flushChanges(root, changes); return changes; diff --git a/packages/nx/src/command-line/new.ts b/packages/nx/src/command-line/new.ts index 035f54c30ec71..1311916f4aa2a 100644 --- a/packages/nx/src/command-line/new.ts +++ b/packages/nx/src/command-line/new.ts @@ -35,6 +35,7 @@ export async function newWorkspace(cwd: string, args: { [k: string]: any }) { const implementation = implementationFactory(); const task = await implementation(host, combinedOpts); flushChanges(cwd, host.listChanges()); + host.lock(); if (task) { await task(); } diff --git a/packages/nx/src/generators/tree.ts b/packages/nx/src/generators/tree.ts index 788438eac0d67..6e79fcce28f5e 100644 --- a/packages/nx/src/generators/tree.ts +++ b/packages/nx/src/generators/tree.ts @@ -9,8 +9,10 @@ import { } from 'fs-extra'; import type { Mode } from 'fs'; import { logger } from '../utils/logger'; +import { output } from '../utils/output'; import { dirname, join, relative, sep } from 'path'; import * as chalk from 'chalk'; +import { gt } from 'semver'; /** * Options to set when writing a file in the Virtual file system tree. @@ -127,6 +129,14 @@ export class FsTree implements Tree { }; } = {}; + /** + * Signifies if operations on the tree instance + * are allowed. Set to false after changes are written + * to disk, to prevent someone trying to use the tree to update + * files when the tree is no longer effective. + */ + private locked = false; + constructor(readonly root: string, private readonly isVerbose: boolean) {} read(filePath: string): Buffer | null; @@ -155,6 +165,7 @@ export class FsTree implements Tree { content: Buffer | string, options?: TreeWriteOptions ): void { + this.assertUnlocked(); filePath = this.normalize(filePath); if ( this.fsExists(this.rp(filePath)) && @@ -187,6 +198,7 @@ export class FsTree implements Tree { } delete(filePath: string): void { + this.assertUnlocked(); filePath = this.normalize(filePath); if (this.filesForDir(this.rp(filePath)).length > 0) { this.filesForDir(this.rp(filePath)).forEach( @@ -220,6 +232,7 @@ export class FsTree implements Tree { } rename(from: string, to: string): void { + this.assertUnlocked(); from = this.normalize(from); to = this.normalize(to); if (from === to) { @@ -292,6 +305,7 @@ export class FsTree implements Tree { } changePermissions(filePath: string, mode: Mode): void { + this.assertUnlocked(); filePath = this.normalize(filePath); const filePathChangeKey = this.rp(filePath); if (this.recordedChanges[filePathChangeKey]) { @@ -320,6 +334,30 @@ export class FsTree implements Tree { } } + // Marks FsTree as final. + lock() { + this.locked = true; + } + + private assertUnlocked() { + if (this.locked) { + // TODO (v17): Remove condition + if (gt(require('../../package.json').version, '17.0.0')) { + throw new Error( + 'The tree has already been committed to disk. It can no longer be modified. Do not modify the tree during a GeneratorCallback and ensure that Promises have resolved before the generator returns or resolves.' + ); + } else { + output.warn({ + title: 'Tree modified after commit to disk.', + bodyLines: [ + 'The tree has already been committed to disk. It can no longer be modified. Do not modify the tree during a GeneratorCallback and ensure that Promises have resolved before the generator returns or resolves.', + `This will be an error in version 16. Please open an issue on the Nx repo if experiencing this with a first-party plugin, or the plugin's repo if using a community plugin.`, + ], + }); + } + } + } + private normalize(path: string) { return relative(this.root, join(this.root, path)).split(sep).join('/'); } From 1e48f6aa22c61f98374bdb61a5ccf1f96f4f2692 Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Tue, 21 Mar 2023 16:39:23 -0400 Subject: [PATCH 09/76] fix(core): use a main function inside nx.ts to avoid future hoisting issues (#15806) --- packages/nx/bin/nx.ts | 124 ++++++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 60 deletions(-) diff --git a/packages/nx/bin/nx.ts b/packages/nx/bin/nx.ts index db8c8fc9d537e..c4f7ffec46b02 100644 --- a/packages/nx/bin/nx.ts +++ b/packages/nx/bin/nx.ts @@ -14,71 +14,73 @@ import { major } from 'semver'; import { readJsonFile } from '../src/utils/fileutils'; import { execSync } from 'child_process'; -const workspace = findWorkspaceRoot(process.cwd()); -// new is a special case because there is no local workspace to load -if ( - process.argv[2] === 'new' || - process.argv[2] === '_migrate' || - process.argv[2] === 'init' || - (process.argv[2] === 'graph' && !workspace) -) { - process.env.NX_DAEMON = 'false'; - require('nx/src/command-line/nx-commands').commandsObject.argv; -} else { - if (workspace && workspace.type === 'nx') { - require('v8-compile-cache'); - } - // polyfill rxjs observable to avoid issues with multiple version of Observable installed in node_modules - // https://twitter.com/BenLesh/status/1192478226385428483?s=20 - if (!(Symbol as any).observable) - (Symbol as any).observable = Symbol('observable polyfill'); +function main() { + const workspace = findWorkspaceRoot(process.cwd()); + // new is a special case because there is no local workspace to load + if ( + process.argv[2] === 'new' || + process.argv[2] === '_migrate' || + process.argv[2] === 'init' || + (process.argv[2] === 'graph' && !workspace) + ) { + process.env.NX_DAEMON = 'false'; + require('nx/src/command-line/nx-commands').commandsObject.argv; + } else { + if (workspace && workspace.type === 'nx') { + require('v8-compile-cache'); + } + // polyfill rxjs observable to avoid issues with multiple version of Observable installed in node_modules + // https://twitter.com/BenLesh/status/1192478226385428483?s=20 + if (!(Symbol as any).observable) + (Symbol as any).observable = Symbol('observable polyfill'); - if (!workspace) { - output.log({ - title: `The current directory isn't part of an Nx workspace.`, - bodyLines: [ - `To create a workspace run:`, - chalk.bold.white(`npx create-nx-workspace@latest `), - '', - `To add Nx to existing workspace run with a workspace-specific nx.json:`, - chalk.bold.white(`npx add-nx-to-monorepo@latest`), - '', - `To add the default nx.json file run:`, - chalk.bold.white(`nx init`), - ], - }); + if (!workspace) { + output.log({ + title: `The current directory isn't part of an Nx workspace.`, + bodyLines: [ + `To create a workspace run:`, + chalk.bold.white(`npx create-nx-workspace@latest `), + '', + `To add Nx to existing workspace run with a workspace-specific nx.json:`, + chalk.bold.white(`npx add-nx-to-monorepo@latest`), + '', + `To add the default nx.json file run:`, + chalk.bold.white(`nx init`), + ], + }); - output.note({ - title: `For more information please visit https://nx.dev/`, - }); - process.exit(1); - } + output.note({ + title: `For more information please visit https://nx.dev/`, + }); + process.exit(1); + } - // Make sure that a local copy of Nx exists in workspace - let localNx: string; - try { - localNx = resolveNx(workspace); - } catch { - // If we can't resolve a local copy of Nx, we must be global. - warnIfUsingOutdatedGlobalInstall(); - output.error({ - title: `Could not find Nx modules in this workspace.`, - bodyLines: [`Have you run ${chalk.bold.white(`npm/yarn install`)}?`], - }); - process.exit(1); - } + // Make sure that a local copy of Nx exists in workspace + let localNx: string; + try { + localNx = resolveNx(workspace); + } catch { + // If we can't resolve a local copy of Nx, we must be global. + warnIfUsingOutdatedGlobalInstall(); + output.error({ + title: `Could not find Nx modules in this workspace.`, + bodyLines: [`Have you run ${chalk.bold.white(`npm/yarn install`)}?`], + }); + process.exit(1); + } - // this file is already in the local workspace - if (localNx === resolveNx(null)) { - initLocal(workspace); - } else { - // Nx is being run from globally installed CLI - hand off to the local - warnIfUsingOutdatedGlobalInstall(getLocalNxVersion(workspace)); - if (localNx.includes('.nx')) { - const nxWrapperPath = localNx.replace(/\.nx.*/, '.nx/') + 'nxw.js'; - require(nxWrapperPath); + // this file is already in the local workspace + if (localNx === resolveNx(null)) { + initLocal(workspace); } else { - require(localNx); + // Nx is being run from globally installed CLI - hand off to the local + warnIfUsingOutdatedGlobalInstall(getLocalNxVersion(workspace)); + if (localNx.includes('.nx')) { + const nxWrapperPath = localNx.replace(/\.nx.*/, '.nx/') + 'nxw.js'; + require(nxWrapperPath); + } else { + require(localNx); + } } } } @@ -159,3 +161,5 @@ const getLatestVersionOfNx = ((fn: () => string) => { let cache: string = null; return () => cache || (cache = fn()); })(_getLatestVersionOfNx); + +main(); From 2cc949e2f1865898988f3cabbca232934b9cf163 Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Tue, 21 Mar 2023 18:08:31 -0400 Subject: [PATCH 10/76] feat(core): show task graph of commands (#15440) Co-authored-by: Philip Fulcher --- docs/generated/cli/affected-graph.md | 16 +++ docs/generated/cli/affected.md | 8 ++ docs/generated/cli/exec.md | 8 ++ docs/generated/cli/graph.md | 16 +++ docs/generated/cli/run-many.md | 8 ++ .../nx/documents/affected-dep-graph.md | 16 +++ .../packages/nx/documents/affected.md | 8 ++ .../packages/nx/documents/dep-graph.md | 16 +++ docs/generated/packages/nx/documents/exec.md | 8 ++ .../packages/nx/documents/run-many.md | 8 ++ .../src/app/feature-tasks/task-list.tsx | 2 +- .../src/app/feature-tasks/tasks-sidebar.tsx | 134 ++++++++++-------- graph/client/src/app/util.ts | 6 +- .../client/src/assets/project-graphs/e2e.json | 6 +- graph/ui-graph/src/lib/graph.ts | 5 + graph/ui-graph/src/lib/interfaces.ts | 3 +- .../util-cytoscape/task-traversal.graph.ts | 37 +++-- packages/nx/src/command-line/affected.ts | 35 +++-- packages/nx/src/command-line/dep-graph.ts | 97 +++++++++---- packages/nx/src/command-line/nx-commands.ts | 16 +++ packages/nx/src/command-line/run-many.ts | 36 +++-- packages/nx/src/command-line/run-one.ts | 36 +++-- packages/nx/src/utils/command-line-utils.ts | 1 + 23 files changed, 392 insertions(+), 134 deletions(-) diff --git a/docs/generated/cli/affected-graph.md b/docs/generated/cli/affected-graph.md index 6fdbdcd9e34c6..d80a58705c007 100644 --- a/docs/generated/cli/affected-graph.md +++ b/docs/generated/cli/affected-graph.md @@ -129,6 +129,12 @@ Type: `number` Bind the project graph server to a specific port. +### targets + +Type: `string` + +The target to show tasks for in the task graph + ### uncommitted Type: `boolean` @@ -147,6 +153,16 @@ Type: `boolean` Show version number +### view + +Type: `string` + +Choices: [projects, tasks] + +Default: `projects` + +Choose whether to view the projects or task graph + ### watch Type: `boolean` diff --git a/docs/generated/cli/affected.md b/docs/generated/cli/affected.md index 832a7441c05ea..454172bd5f4cc 100644 --- a/docs/generated/cli/affected.md +++ b/docs/generated/cli/affected.md @@ -97,6 +97,14 @@ Type: `string` Change the way Nx is calculating the affected command by providing directly changed files, list of files delimited by commas or spaces +### graph + +Type: `boolean` + +Default: `false` + +Show the task graph of the command + ### head Type: `string` diff --git a/docs/generated/cli/exec.md b/docs/generated/cli/exec.md index 31360eccf5a84..57c9bdf13aa59 100644 --- a/docs/generated/cli/exec.md +++ b/docs/generated/cli/exec.md @@ -29,6 +29,14 @@ Type: `string` Exclude certain projects from being processed +### graph + +Type: `boolean` + +Default: `false` + +Show the task graph of the command + ### nx-bail Type: `boolean` diff --git a/docs/generated/cli/graph.md b/docs/generated/cli/graph.md index df1441fd25bcf..3f98092857c24 100644 --- a/docs/generated/cli/graph.md +++ b/docs/generated/cli/graph.md @@ -117,12 +117,28 @@ Type: `number` Bind the project graph server to a specific port. +### targets + +Type: `string` + +The target to show tasks for in the task graph + ### version Type: `boolean` Show version number +### view + +Type: `string` + +Choices: [projects, tasks] + +Default: `projects` + +Choose whether to view the projects or task graph + ### watch Type: `boolean` diff --git a/docs/generated/cli/run-many.md b/docs/generated/cli/run-many.md index eba051448d042..9a8198b836764 100644 --- a/docs/generated/cli/run-many.md +++ b/docs/generated/cli/run-many.md @@ -75,6 +75,14 @@ Type: `string` Exclude certain projects from being processed +### graph + +Type: `boolean` + +Default: `false` + +Show the task graph of the command + ### help Type: `boolean` diff --git a/docs/generated/packages/nx/documents/affected-dep-graph.md b/docs/generated/packages/nx/documents/affected-dep-graph.md index 6fdbdcd9e34c6..d80a58705c007 100644 --- a/docs/generated/packages/nx/documents/affected-dep-graph.md +++ b/docs/generated/packages/nx/documents/affected-dep-graph.md @@ -129,6 +129,12 @@ Type: `number` Bind the project graph server to a specific port. +### targets + +Type: `string` + +The target to show tasks for in the task graph + ### uncommitted Type: `boolean` @@ -147,6 +153,16 @@ Type: `boolean` Show version number +### view + +Type: `string` + +Choices: [projects, tasks] + +Default: `projects` + +Choose whether to view the projects or task graph + ### watch Type: `boolean` diff --git a/docs/generated/packages/nx/documents/affected.md b/docs/generated/packages/nx/documents/affected.md index 832a7441c05ea..454172bd5f4cc 100644 --- a/docs/generated/packages/nx/documents/affected.md +++ b/docs/generated/packages/nx/documents/affected.md @@ -97,6 +97,14 @@ Type: `string` Change the way Nx is calculating the affected command by providing directly changed files, list of files delimited by commas or spaces +### graph + +Type: `boolean` + +Default: `false` + +Show the task graph of the command + ### head Type: `string` diff --git a/docs/generated/packages/nx/documents/dep-graph.md b/docs/generated/packages/nx/documents/dep-graph.md index df1441fd25bcf..3f98092857c24 100644 --- a/docs/generated/packages/nx/documents/dep-graph.md +++ b/docs/generated/packages/nx/documents/dep-graph.md @@ -117,12 +117,28 @@ Type: `number` Bind the project graph server to a specific port. +### targets + +Type: `string` + +The target to show tasks for in the task graph + ### version Type: `boolean` Show version number +### view + +Type: `string` + +Choices: [projects, tasks] + +Default: `projects` + +Choose whether to view the projects or task graph + ### watch Type: `boolean` diff --git a/docs/generated/packages/nx/documents/exec.md b/docs/generated/packages/nx/documents/exec.md index 31360eccf5a84..57c9bdf13aa59 100644 --- a/docs/generated/packages/nx/documents/exec.md +++ b/docs/generated/packages/nx/documents/exec.md @@ -29,6 +29,14 @@ Type: `string` Exclude certain projects from being processed +### graph + +Type: `boolean` + +Default: `false` + +Show the task graph of the command + ### nx-bail Type: `boolean` diff --git a/docs/generated/packages/nx/documents/run-many.md b/docs/generated/packages/nx/documents/run-many.md index eba051448d042..9a8198b836764 100644 --- a/docs/generated/packages/nx/documents/run-many.md +++ b/docs/generated/packages/nx/documents/run-many.md @@ -75,6 +75,14 @@ Type: `string` Exclude certain projects from being processed +### graph + +Type: `boolean` + +Default: `false` + +Show the task graph of the command + ### help Type: `boolean` diff --git a/graph/client/src/app/feature-tasks/task-list.tsx b/graph/client/src/app/feature-tasks/task-list.tsx index d406474c42b69..3da187a964cec 100644 --- a/graph/client/src/app/feature-tasks/task-list.tsx +++ b/graph/client/src/app/feature-tasks/task-list.tsx @@ -141,7 +141,7 @@ export function TaskList({ }: TaskListProps) { const filteredProjects = projects .filter((project) => - (project.data as any).targets.hasOwnProperty(selectedTarget) + (project.data as any).targets?.hasOwnProperty(selectedTarget) ) .sort((a, b) => a.name.localeCompare(b.name)); const appProjects = getProjectsByType('app', filteredProjects); diff --git a/graph/client/src/app/feature-tasks/tasks-sidebar.tsx b/graph/client/src/app/feature-tasks/tasks-sidebar.tsx index 1b47fcd0825de..f5a826dc2eb50 100644 --- a/graph/client/src/app/feature-tasks/tasks-sidebar.tsx +++ b/graph/client/src/app/feature-tasks/tasks-sidebar.tsx @@ -11,7 +11,7 @@ import type { TaskGraphClientResponse, } from 'nx/src/command-line/dep-graph'; import { getGraphService } from '../machines/graph.service'; -import { useEffect, useState } from 'react'; +import { useEffect, useMemo } from 'react'; import { CheckboxPanel } from '../ui-components/checkbox-panel'; import { Dropdown } from '@nrwl/graph/ui-components'; @@ -23,6 +23,7 @@ export function TasksSidebar() { const graphService = getGraphService(); const navigate = useNavigate(); const params = useParams(); + const createRoute = useRouteConstructor(); const [searchParams, setSearchParams] = useSearchParams(); const groupByProject = searchParams.get('groupByProject') === 'true'; @@ -40,10 +41,23 @@ export function TasksSidebar() { const selectedTarget = params['selectedTarget'] ?? targets[0]; - const [selectedProjects, setSelectedProjects] = useState([]); - const currentRoute = useCurrentPath(); - const routeContructor = useRouteConstructor(); + const isAllRoute = + currentRoute.currentPath === `/tasks/${selectedTarget}/all`; + + const allProjectsWithTargetAndNoErrors = projects.filter( + (project) => + project.data.targets?.hasOwnProperty(selectedTarget) && + !errors?.hasOwnProperty(createTaskName(project.name, selectedTarget)) + ); + + const selectedProjects = useMemo( + () => + isAllRoute + ? allProjectsWithTargetAndNoErrors.map(({ name }) => name) + : searchParams.get('projects')?.split(' ') ?? [], + [allProjectsWithTargetAndNoErrors, searchParams, isAllRoute] + ); function selectTarget(target: string) { if (target === selectedTarget) return; @@ -72,59 +86,75 @@ export function TasksSidebar() { } function selectProject(project: string) { - setSelectedProjects([...selectedProjects, project]); - - const taskId = createTaskName(project, selectedTarget); - - graphService.handleTaskEvent({ - type: 'notifyTaskGraphTasksSelected', - taskIds: [taskId], - }); - } + const newSelectedProjects = [...selectedProjects, project]; + const allProjectsSelected = + newSelectedProjects.length === allProjectsWithTargetAndNoErrors.length; + if (allProjectsSelected) { + searchParams.delete('projects'); + } else { + searchParams.set('projects', newSelectedProjects.join(' ')); + } - function selectAllProjects() { navigate( - routeContructor(`/tasks/${encodeURIComponent(selectedTarget)}/all`, true) + createRoute( + { + pathname: allProjectsSelected + ? `/tasks/${encodeURIComponent(selectedTarget)}/all` + : `/tasks/${encodeURIComponent(selectedTarget)}`, + search: searchParams.toString(), + }, + false + ) ); } - function hideAllProjects() { - setSelectedProjects([]); - - const allProjects = projects.map( - (project) => `${project.name}:${selectedTarget}` + function deselectProject(project: string) { + const newSelectedProjects = selectedProjects.filter( + (selectedProject) => selectedProject !== project ); - - graphService.handleTaskEvent({ - type: 'notifyTaskGraphTasksDeselected', - taskIds: allProjects, - }); - + if (newSelectedProjects.length === 0) { + searchParams.delete('projects'); + } else { + searchParams.set('projects', newSelectedProjects.join(' ')); + } navigate( - routeContructor(`/tasks/${encodeURIComponent(selectedTarget)}`, true) + createRoute( + { + pathname: `/tasks/${encodeURIComponent(selectedTarget)}`, + search: searchParams.toString(), + }, + false + ) ); } - function deselectProject(project: string) { - const newSelectedProjects = selectedProjects.filter( - (selectedProject) => selectedProject !== project + function selectAllProjects() { + searchParams.delete('projects'); + navigate( + createRoute( + { + pathname: `/tasks/${encodeURIComponent(selectedTarget)}/all`, + search: searchParams.toString(), + }, + false + ) ); - setSelectedProjects(newSelectedProjects); - - const taskId = `${project}:${selectedTarget}`; - - graphService.handleTaskEvent({ - type: 'notifyTaskGraphTasksDeselected', - taskIds: [taskId], - }); + } + function hideAllProjects() { + searchParams.delete('projects'); navigate( - routeContructor(`/tasks/${encodeURIComponent(selectedTarget)}`, true) + createRoute( + { + pathname: `/tasks/${encodeURIComponent(selectedTarget)}`, + search: searchParams.toString(), + }, + false + ) ); } useEffect(() => { - setSelectedProjects([]); graphService.handleTaskEvent({ type: 'notifyTaskGraphSetProjects', projects: selectedWorkspaceRouteData.projects, @@ -147,25 +177,11 @@ export function TasksSidebar() { }, [searchParams]); useEffect(() => { - switch (currentRoute.currentPath) { - case `/tasks/${selectedTarget}/all`: - const allProjectsWithSelectedTarget = projects.filter((project) => - project.data.targets.hasOwnProperty(selectedTarget) - ); - - setSelectedProjects( - allProjectsWithSelectedTarget.map((project) => project.name) - ); - - graphService.handleTaskEvent({ - type: 'notifyTaskGraphTasksSelected', - taskIds: allProjectsWithSelectedTarget.map( - (project) => `${project.name}:${selectedTarget}` - ), - }); - break; - } - }, [currentRoute]); + graphService.handleTaskEvent({ + type: 'notifyTaskGraphSetTasks', + taskIds: selectedProjects.map((p) => createTaskName(p, selectedTarget)), + }); + }, [graphService, selectedProjects, selectedTarget]); function groupByProjectChanged(checked) { setSearchParams( diff --git a/graph/client/src/app/util.ts b/graph/client/src/app/util.ts index 15080bcf30bd9..f91786e507f67 100644 --- a/graph/client/src/app/util.ts +++ b/graph/client/src/app/util.ts @@ -23,7 +23,11 @@ export const useRouteConstructor = (): (( return { ...to, pathname, - search: retainSearchParams ? searchParams.toString() : '', + search: to.search + ? to.search.toString() + : retainSearchParams + ? searchParams.toString() + : '', }; } else if (typeof to === 'string') { if (environment === 'dev') { diff --git a/graph/client/src/assets/project-graphs/e2e.json b/graph/client/src/assets/project-graphs/e2e.json index 7915163997254..21a2f0f0b34a4 100644 --- a/graph/client/src/assets/project-graphs/e2e.json +++ b/graph/client/src/assets/project-graphs/e2e.json @@ -7,7 +7,8 @@ "data": { "tags": [], "root": "libs/project-a", - "files": [] + "files": [], + "targets": {} } }, { @@ -16,7 +17,8 @@ "data": { "tags": [], "root": "libs/project-a", - "files": [] + "files": [], + "targets": {} } }, { diff --git a/graph/ui-graph/src/lib/graph.ts b/graph/ui-graph/src/lib/graph.ts index 4e6cf23fb2430..dd276ded50480 100644 --- a/graph/ui-graph/src/lib/graph.ts +++ b/graph/ui-graph/src/lib/graph.ts @@ -230,6 +230,11 @@ export class GraphService { case 'notifyTaskGraphSetProjects': this.taskTraversalGraph.setProjects(event.projects, event.taskGraphs); break; + case 'notifyTaskGraphSetTasks': + elementsToSendToRender = this.taskTraversalGraph.setTasks( + event.taskIds + ); + break; case 'notifyTaskGraphTasksSelected': elementsToSendToRender = this.taskTraversalGraph.selectTask( event.taskIds diff --git a/graph/ui-graph/src/lib/interfaces.ts b/graph/ui-graph/src/lib/interfaces.ts index 30571aae6db95..b414c407633ef 100644 --- a/graph/ui-graph/src/lib/interfaces.ts +++ b/graph/ui-graph/src/lib/interfaces.ts @@ -100,7 +100,8 @@ export type TaskGraphRenderEvents = | { type: 'setGroupByProject'; groupByProject: boolean; - }; + } + | { type: 'notifyTaskGraphSetTasks'; taskIds: string[] }; export type TooltipEvent = | { diff --git a/graph/ui-graph/src/lib/util-cytoscape/task-traversal.graph.ts b/graph/ui-graph/src/lib/util-cytoscape/task-traversal.graph.ts index d050df11de9c7..3d20fd12eb28d 100644 --- a/graph/ui-graph/src/lib/util-cytoscape/task-traversal.graph.ts +++ b/graph/ui-graph/src/lib/util-cytoscape/task-traversal.graph.ts @@ -22,15 +22,6 @@ export class TaskTraversalGraph { this.taskGraphs = taskGraphs; } - selectTask(taskIds: string[]) { - taskIds.forEach((taskId) => { - this.selectedTasks.add(taskId); - }); - this.createElements(Array.from(this.selectedTasks), this.groupByProject); - - return this.cy.elements(); - } - setGroupByProject(groupByProject: boolean) { this.groupByProject = groupByProject; @@ -46,6 +37,34 @@ export class TaskTraversalGraph { return this.cy.elements(); } + setTasks(taskIds: string[]) { + let changed = false; + this.selectedTasks.forEach((selectedTask) => { + if (!taskIds.includes(selectedTask)) { + this.selectedTasks.delete(selectedTask); + changed = true; + } + }); + for (const taskId of taskIds) { + if (!this.selectedTasks.has(taskId)) { + this.selectedTasks.add(taskId); + changed = true; + } + } + if (changed) { + this.createElements(Array.from(this.selectedTasks), this.groupByProject); + } + return this.cy.elements(); + } + + selectTask(taskIds: string[]) { + taskIds.forEach((taskId) => { + this.selectedTasks.add(taskId); + }); + + return this.cy.elements(); + } + deselectTask(taskIds: string[]) { taskIds.forEach((taskId) => { this.selectedTasks.delete(taskId); diff --git a/packages/nx/src/command-line/affected.ts b/packages/nx/src/command-line/affected.ts index ef400b9ff8692..c493d7f97a3b7 100644 --- a/packages/nx/src/command-line/affected.ts +++ b/packages/nx/src/command-line/affected.ts @@ -117,16 +117,31 @@ export async function affected( case 'affected': { const projectsWithTarget = allProjectsWithTarget(projects, nxArgs); - await runCommand( - projectsWithTarget, - projectGraph, - { nxJson }, - nxArgs, - overrides, - null, - extraTargetDependencies, - { excludeTaskDependencies: false, loadDotEnvFiles: true } - ); + if (nxArgs.graph) { + const projectNames = projectsWithTarget.map((t) => t.name); + + return await generateGraph( + { + watch: false, + open: true, + view: 'tasks', + targets: nxArgs.targets, + projects: projectNames, + }, + projectNames + ); + } else { + await runCommand( + projectsWithTarget, + projectGraph, + { nxJson }, + nxArgs, + overrides, + null, + extraTargetDependencies, + { excludeTaskDependencies: false, loadDotEnvFiles: true } + ); + } break; } } diff --git a/packages/nx/src/command-line/dep-graph.ts b/packages/nx/src/command-line/dep-graph.ts index fe04e162203ac..975d6abcd3000 100644 --- a/packages/nx/src/command-line/dep-graph.ts +++ b/packages/nx/src/command-line/dep-graph.ts @@ -3,11 +3,10 @@ import { createHash } from 'crypto'; import { existsSync, readFileSync, statSync, writeFileSync } from 'fs'; import { copySync, ensureDirSync } from 'fs-extra'; import * as http from 'http'; -import ignore from 'ignore'; import * as open from 'open'; import { basename, dirname, extname, isAbsolute, join, parse } from 'path'; import { performance } from 'perf_hooks'; -import { URL, URLSearchParams } from 'url'; +import { URL } from 'url'; import { readNxJson, workspaceLayout } from '../config/configuration'; import { defaultFileHasher } from '../hasher/file-hasher'; import { output } from '../utils/output'; @@ -23,6 +22,7 @@ import { createTaskGraph } from '../tasks-runner/create-task-graph'; import { TargetDefaults, TargetDependencies } from '../config/nx-json'; import { TaskGraph } from '../config/task-graph'; import { daemonClient } from '../daemon/client/client'; +import { Server } from 'net'; export interface ProjectGraphClientResponse { hash: string; @@ -175,18 +175,35 @@ export async function generateGraph( file?: string; host?: string; port?: number; - focus?: string; - exclude?: string[]; groupByFolder?: boolean; watch?: boolean; open?: boolean; + view: 'projects' | 'tasks'; + projects?: string[]; + all?: boolean; + targets?: string[]; + focus?: string; + exclude?: string[]; }, affectedProjects: string[] ): Promise { + if (Array.isArray(args.targets) && args.targets.length > 1) { + output.warn({ + title: 'Showing Multiple Targets is not supported yet', + bodyLines: [ + `Only the task graph for "${args.targets[0]}" tasks will be shown`, + ], + }); + } + + // TODO: Graph Client should support multiple targets + const target = Array.isArray(args.targets && args.targets.length >= 1) + ? args.targets[0] + : args.targets; + let graph = pruneExternalNodes( await createProjectGraphAsync({ exitOnError: true }) ); - const layout = workspaceLayout(); const projects = Object.values(graph.nodes) as ProjectGraphProjectNode[]; projects.sort((a, b) => { @@ -299,7 +316,7 @@ export async function generateGraph( !!args.file && args.file.endsWith('html') ? 'build' : 'serve' ); - await startServer( + const { app, url } = await startServer( html, environmentJs, args.host || '127.0.0.1', @@ -308,9 +325,43 @@ export async function generateGraph( affectedProjects, args.focus, args.groupByFolder, - args.exclude, - args.open + args.exclude ); + + url.pathname = args.view; + + if (args.focus) { + url.pathname += '/' + args.focus; + } + + if (target) { + url.pathname += '/' + target; + } + if (args.all) { + url.pathname += '/all'; + } else if (args.projects) { + url.searchParams.append( + 'projects', + args.projects + .map((projectName) => encodeURIComponent(projectName)) + .join(' ') + ); + } + if (args.groupByFolder) { + url.searchParams.append('groupByFolder', 'true'); + } + + output.success({ + title: `Project graph started at ${url.toString()}`, + }); + + if (args.open) { + open(url.toString()); + } + + return new Promise((res) => { + app.once('close', res); + }); } } @@ -323,8 +374,7 @@ async function startServer( affected: string[] = [], focus: string = null, groupByFolder: boolean = false, - exclude: string[] = [], - openBrowser: boolean = true + exclude: string[] = [] ) { let unregisterFileWatcher: (() => void) | undefined; if (watchForchanges) { @@ -391,27 +441,6 @@ async function startServer( } }); - app.listen(port, host); - - output.note({ - title: `Project graph started at http://${host}:${port}`, - }); - - if (openBrowser) { - let url = `http://${host}:${port}/projects`; - let params = new URLSearchParams(); - - if (focus) { - url += `/${focus}`; - } - - if (groupByFolder) { - params.append('groupByFolder', 'true'); - } - - open(`${url}?${params.toString()}`); - } - const handleTermination = async (exitCode: number) => { if (unregisterFileWatcher) { unregisterFileWatcher(); @@ -420,6 +449,12 @@ async function startServer( }; process.on('SIGINT', () => handleTermination(128 + 2)); process.on('SIGTERM', () => handleTermination(128 + 15)); + + return new Promise<{ app: Server; url: URL }>((res) => { + app.listen(port, host, () => { + res({ app, url: new URL(`http://${host}:${port}`) }); + }); + }); } let currentDepGraphClientResponse: ProjectGraphClientResponse = { diff --git a/packages/nx/src/command-line/nx-commands.ts b/packages/nx/src/command-line/nx-commands.ts index 9e928fc1ce7a9..1dc36d32c44a3 100644 --- a/packages/nx/src/command-line/nx-commands.ts +++ b/packages/nx/src/command-line/nx-commands.ts @@ -501,6 +501,11 @@ function withRunOptions(yargs: yargs.Argv): yargs.Argv { default: false, hidden: true, }) + .option('graph', { + type: 'boolean', + describe: 'Show the task graph of the command', + default: false, + }) .option('verbose', { type: 'boolean', describe: @@ -618,6 +623,17 @@ function withDepGraphOptions(yargs: yargs.Argv): yargs.Argv { 'Output file (e.g. --file=output.json or --file=dep-graph.html)', type: 'string', }) + .option('view', { + describe: 'Choose whether to view the projects or task graph', + type: 'string', + default: 'projects', + choices: ['projects', 'tasks'], + }) + .option('targets', { + describe: 'The target to show tasks for in the task graph', + type: 'string', + coerce: parseCSV, + }) .option('focus', { describe: 'Use to show the project graph for a particular project and every node that is either an ancestor or a descendant.', diff --git a/packages/nx/src/command-line/run-many.ts b/packages/nx/src/command-line/run-many.ts index 66d66ccdd6f64..c0a3379ff0ebd 100644 --- a/packages/nx/src/command-line/run-many.ts +++ b/packages/nx/src/command-line/run-many.ts @@ -11,6 +11,7 @@ import { readNxJson } from '../config/configuration'; import { output } from '../utils/output'; import { findMatchingProjects } from '../utils/find-matching-projects'; import { workspaceConfigurationCheck } from '../utils/workspace-configuration-check'; +import { generateGraph } from './dep-graph'; export async function runMany( args: { [k: string]: any }, @@ -41,16 +42,31 @@ export async function runMany( const projectGraph = await createProjectGraphAsync({ exitOnError: true }); const projects = projectsToRun(nxArgs, projectGraph); - await runCommand( - projects, - projectGraph, - { nxJson }, - nxArgs, - overrides, - null, - extraTargetDependencies, - extraOptions - ); + if (nxArgs.graph) { + const projectNames = projects.map((t) => t.name); + return await generateGraph( + { + watch: false, + open: true, + view: 'tasks', + all: nxArgs.all, + targets: nxArgs.targets, + projects: projectNames, + }, + projectNames + ); + } else { + await runCommand( + projects, + projectGraph, + { nxJson }, + nxArgs, + overrides, + null, + extraTargetDependencies, + extraOptions + ); + } } export function projectsToRun( diff --git a/packages/nx/src/command-line/run-one.ts b/packages/nx/src/command-line/run-one.ts index 3752324c0ef51..94f036ef799f4 100644 --- a/packages/nx/src/command-line/run-one.ts +++ b/packages/nx/src/command-line/run-one.ts @@ -17,6 +17,7 @@ import { } from '../config/workspace-json-project-json'; import { readNxJson } from '../config/configuration'; import { workspaceConfigurationCheck } from '../utils/workspace-configuration-check'; +import { generateGraph } from './dep-graph'; export async function runOne( cwd: string, @@ -63,16 +64,31 @@ export async function runOne( const { projects } = getProjects(projectGraph, opts.project); - await runCommand( - projects, - projectGraph, - { nxJson }, - nxArgs, - overrides, - opts.project, - extraTargetDependencies, - extraOptions - ); + if (nxArgs.graph) { + const projectNames = projects.map((t) => t.name); + + return await generateGraph( + { + watch: false, + open: true, + view: 'tasks', + targets: nxArgs.targets, + projects: projectNames, + }, + projectNames + ); + } else { + await runCommand( + projects, + projectGraph, + { nxJson }, + nxArgs, + overrides, + opts.project, + extraTargetDependencies, + extraOptions + ); + } } function getProjects(projectGraph: ProjectGraph, project: string): any { diff --git a/packages/nx/src/utils/command-line-utils.ts b/packages/nx/src/utils/command-line-utils.ts index 3dbc7deee8657..02526231b2572 100644 --- a/packages/nx/src/utils/command-line-utils.ts +++ b/packages/nx/src/utils/command-line-utils.ts @@ -29,6 +29,7 @@ export interface NxArgs { plain?: boolean; projects?: string[]; select?: string; + graph?: boolean; skipNxCache?: boolean; outputStyle?: string; nxBail?: boolean; From 6834b658f71d5415f3d53fe3cb4a2c90efb17232 Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Tue, 21 Mar 2023 18:35:55 -0400 Subject: [PATCH 11/76] fix(nx-plugin): allow some customization when running nx-plugins (#15438) --- packages/cypress/package.json | 2 +- packages/detox/package.json | 2 +- packages/devkit/package.json | 2 +- packages/esbuild/package.json | 2 +- packages/expo/package.json | 2 +- packages/express/package.json | 2 +- packages/jest/package.json | 2 +- packages/linter/package.json | 2 +- packages/nest/package.json | 2 +- packages/next/package.json | 2 +- packages/node/package.json | 2 +- packages/nx-plugin/package.json | 2 +- packages/nx/.eslintrc.json | 18 ++++++++++++++++-- packages/nx/src/utils/nx-plugin.ts | 17 ++++++++++++++--- packages/nx/src/utils/register.ts | 21 ++++++++++++++++----- packages/react-native/package.json | 2 +- packages/react/package.json | 2 +- packages/rollup/package.json | 2 +- packages/storybook/package.json | 2 +- packages/tao/package.json | 4 ++-- packages/vite/package.json | 2 +- packages/web/package.json | 2 +- packages/webpack/package.json | 2 +- packages/workspace/package.json | 2 +- 24 files changed, 68 insertions(+), 32 deletions(-) diff --git a/packages/cypress/package.json b/packages/cypress/package.json index 6b741d688286c..12dbd48290af0 100644 --- a/packages/cypress/package.json +++ b/packages/cypress/package.json @@ -19,7 +19,7 @@ "Cypress", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", diff --git a/packages/detox/package.json b/packages/detox/package.json index c81fe76c19f62..6ad96ed1d7974 100644 --- a/packages/detox/package.json +++ b/packages/detox/package.json @@ -22,7 +22,7 @@ }, "license": "MIT", "author": "Victor Savkin", - "main": "index.js", + "main": "./index", "types": "index.d.ts", "dependencies": { "@nrwl/devkit": "file:../devkit", diff --git a/packages/devkit/package.json b/packages/devkit/package.json index 2059ea2bffaa0..1582771901d89 100644 --- a/packages/devkit/package.json +++ b/packages/devkit/package.json @@ -19,7 +19,7 @@ "Cypress", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", diff --git a/packages/esbuild/package.json b/packages/esbuild/package.json index d42066efe56ee..13649571cbb7d 100644 --- a/packages/esbuild/package.json +++ b/packages/esbuild/package.json @@ -14,7 +14,7 @@ "Web", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", diff --git a/packages/expo/package.json b/packages/expo/package.json index 1a1e59e07a725..62fab978e4eea 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -23,7 +23,7 @@ }, "license": "MIT", "author": "Victor Savkin", - "main": "index.js", + "main": "./index", "types": "index.d.ts", "dependencies": { "@nrwl/detox": "file:../detox", diff --git a/packages/express/package.json b/packages/express/package.json index 6b10225d8c46b..7b4bc15b456f0 100644 --- a/packages/express/package.json +++ b/packages/express/package.json @@ -16,7 +16,7 @@ "Cypress", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", diff --git a/packages/jest/package.json b/packages/jest/package.json index 1a907a464337a..fe17321aa70ae 100644 --- a/packages/jest/package.json +++ b/packages/jest/package.json @@ -19,7 +19,7 @@ "Unit Testing", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", diff --git a/packages/linter/package.json b/packages/linter/package.json index 6ee91db1058ec..531c044354a9c 100644 --- a/packages/linter/package.json +++ b/packages/linter/package.json @@ -15,7 +15,7 @@ "ESLint", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", diff --git a/packages/nest/package.json b/packages/nest/package.json index d3fbfdb2d98e9..b8648052c5643 100644 --- a/packages/nest/package.json +++ b/packages/nest/package.json @@ -16,7 +16,7 @@ "Cypress", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", diff --git a/packages/next/package.json b/packages/next/package.json index af5cb5c0fde2a..6e37247e68c9d 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -16,7 +16,7 @@ "Cypress", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", diff --git a/packages/node/package.json b/packages/node/package.json index 445db110b3648..65cd661b4fde0 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -16,7 +16,7 @@ "Cypress", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", diff --git a/packages/nx-plugin/package.json b/packages/nx-plugin/package.json index 1a08a70d93fcd..2a1e202b59132 100644 --- a/packages/nx-plugin/package.json +++ b/packages/nx-plugin/package.json @@ -13,7 +13,7 @@ "Nx", "CLI" ], - "main": "./index.js", + "main": "./index", "types": "./index.d.ts", "author": "Nrwl", "license": "MIT", diff --git a/packages/nx/.eslintrc.json b/packages/nx/.eslintrc.json index 634134c1f3b32..2160aa7bb5821 100644 --- a/packages/nx/.eslintrc.json +++ b/packages/nx/.eslintrc.json @@ -8,8 +8,22 @@ "rules": {} }, { - "files": ["*.ts", "*.tsx"], - "rules": {} + "files": ["*.ts"], + "excludedFiles": ["*.spec.ts"], + "rules": { + "@typescript-eslint/no-restricted-imports": [ + "error", + { + "paths": [ + { + "name": "typescript", + "message": "TypeScript is an optional dependency for Nx. If you need to use it, make sure its installed first with ensureTypescript.", + "allowTypeImports": true + } + ] + } + ] + } }, { "files": ["*.js", "*.jsx"], diff --git a/packages/nx/src/utils/nx-plugin.ts b/packages/nx/src/utils/nx-plugin.ts index 44c0c3334d4cf..bb933097aa320 100644 --- a/packages/nx/src/utils/nx-plugin.ts +++ b/packages/nx/src/utils/nx-plugin.ts @@ -24,6 +24,9 @@ import { import { normalizePath } from './path'; import { join } from 'path'; import { getNxRequirePaths } from './installation-directory'; +import { readTsConfig } from './typescript'; + +import type * as ts from 'typescript'; export type ProjectTargetConfigurator = ( file: string @@ -253,17 +256,25 @@ export function registerPluginTSTranspiler() { if (!tsNodeAndPathsRegistered) { // nx-ignore-next-line const ts: typeof import('typescript') = require('typescript'); + const tsConfigName = existsSync('tsconfig.base.json') + ? 'tsconfig.base.json' + : existsSync('tsconfig.json') + ? 'tsconfig.json' + : null; + const tsConfig: Partial = tsConfigName + ? readTsConfig(tsConfigName) + : {}; registerTsConfigPaths(join(workspaceRoot, 'tsconfig.base.json')); registerTranspiler({ + experimentalDecorators: true, + emitDecoratorMetadata: true, + ...tsConfig.options, lib: ['es2021'], module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2021, inlineSourceMap: true, - esModuleInterop: true, skipLibCheck: true, - experimentalDecorators: true, - emitDecoratorMetadata: true, }); } tsNodeAndPathsRegistered = true; diff --git a/packages/nx/src/utils/register.ts b/packages/nx/src/utils/register.ts index 5739d3a9dd6c8..de87ed3f8ce3b 100644 --- a/packages/nx/src/utils/register.ts +++ b/packages/nx/src/utils/register.ts @@ -1,5 +1,5 @@ import { dirname, join } from 'path'; -import type { CompilerOptions } from 'typescript'; +import type { CompilerOptions, ModuleResolutionKind } from 'typescript'; import { logger, NX_PREFIX, stripIndent } from './logger'; const swcNodeInstalled = packageIsInstalled('@swc-node/register'); @@ -178,9 +178,9 @@ export function getTsNodeCompilerOptions(compilerOptions: CompilerOptions) { ts = require('typescript'); } - const flagMap: Partial< - Record, keyof typeof ts> - > = { + const flagMap: Partial<{ + [key in keyof RemoveIndex]: keyof typeof ts; + }> = { module: 'ModuleKind', target: 'ScriptTarget', moduleDetection: 'ModuleDetectionKind', @@ -189,7 +189,9 @@ export function getTsNodeCompilerOptions(compilerOptions: CompilerOptions) { importsNotUsedAsValues: 'ImportsNotUsedAsValues', }; - const result = { ...compilerOptions }; + const result: { [key in keyof CompilerOptions]: any } = { + ...compilerOptions, + }; for (const flag in flagMap) { if (compilerOptions[flag]) { @@ -197,6 +199,15 @@ export function getTsNodeCompilerOptions(compilerOptions: CompilerOptions) { } } + delete result.pathsBasePath; + delete result.configFilePath; + if (result.moduleResolution) { + result.moduleResolution = + result.moduleResolution === 'NodeJs' + ? 'node' + : result.moduleResolution.toLowerCase(); + } + return result; } diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 915988c3088e3..8ca6b41ad6238 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -22,7 +22,7 @@ }, "license": "MIT", "author": "Victor Savkin", - "main": "index.js", + "main": "./index", "types": "index.d.ts", "dependencies": { "@nrwl/detox": "file:../detox", diff --git a/packages/react/package.json b/packages/react/package.json index ce4e0237ccffa..617bd64a410c5 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -16,7 +16,7 @@ "Cypress", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", diff --git a/packages/rollup/package.json b/packages/rollup/package.json index eea7331434da0..1271e2d369951 100644 --- a/packages/rollup/package.json +++ b/packages/rollup/package.json @@ -14,7 +14,7 @@ "Web", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", diff --git a/packages/storybook/package.json b/packages/storybook/package.json index d3d93510415a2..48a44cdb6f893 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -15,7 +15,7 @@ "Storybook", "Cypress" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Isaac Mann", "license": "MIT", diff --git a/packages/tao/package.json b/packages/tao/package.json index 195e17c5b22e8..f8a9bb31acd69 100644 --- a/packages/tao/package.json +++ b/packages/tao/package.json @@ -19,7 +19,7 @@ "Cypress", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", @@ -27,7 +27,7 @@ "url": "https://github.com/nrwl/nx/issues" }, "bin": { - "tao": "./index.js" + "tao": "./index" }, "homepage": "https://nx.dev", "dependencies": { diff --git a/packages/vite/package.json b/packages/vite/package.json index a0d1649f2a1d2..47819969d03a0 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -14,7 +14,7 @@ "Web", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", diff --git a/packages/web/package.json b/packages/web/package.json index 6bc4547ed6a9e..685eb14ff613d 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -15,7 +15,7 @@ "Cypress", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", diff --git a/packages/webpack/package.json b/packages/webpack/package.json index d5cc148e4970f..942a505f142ba 100644 --- a/packages/webpack/package.json +++ b/packages/webpack/package.json @@ -14,7 +14,7 @@ "Web", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", diff --git a/packages/workspace/package.json b/packages/workspace/package.json index a6944ba69caae..218c5e324a1c3 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -19,7 +19,7 @@ "Cypress", "CLI" ], - "main": "./index.js", + "main": "./index", "typings": "./index.d.ts", "author": "Victor Savkin", "license": "MIT", From 0370794e6311e5333109f64d37964c05e19ad11d Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Tue, 21 Mar 2023 19:53:21 -0400 Subject: [PATCH 12/76] feat(core): add --quiet option to suppress generate output (#15802) --- docs/shared/reference/environment-variables.md | 1 + e2e/nx-misc/src/extras.test.ts | 8 ++++++++ packages/create-nx-workspace/src/create-preset.ts | 2 +- packages/devkit/src/tasks/install-packages-task.ts | 2 +- packages/devkit/src/utils/package-json.ts | 4 +++- packages/nx/src/adapter/ngcli-adapter.ts | 1 + packages/nx/src/command-line/generate.ts | 7 ++++++- packages/nx/src/command-line/nx-commands.ts | 10 ++++++++++ 8 files changed, 31 insertions(+), 4 deletions(-) diff --git a/docs/shared/reference/environment-variables.md b/docs/shared/reference/environment-variables.md index 719ab8abb7c86..91f050cf42bdd 100644 --- a/docs/shared/reference/environment-variables.md +++ b/docs/shared/reference/environment-variables.md @@ -20,6 +20,7 @@ The following environment variables are ones that you can set to change the beha | NX_VERBOSE_LOGGING | boolean | If set to `true`, will print debug information useful for troubleshooting | | NX_DRY_RUN | boolean | If set to `true`, will perform a dry run of the generator. No files will be created and no packages will be installed. | | NX_INTERACTIVE | boolean | If set to `true`, will allow Nx to prompt you in the terminal to answer some further questions when running generators. | +| NX_GENERATE_QUIET | boolean | If set to `true`, will prevent Nx logging file operations during generate | Nx will set the following environment variables so they can be accessible within the process even outside of executors and generators diff --git a/e2e/nx-misc/src/extras.test.ts b/e2e/nx-misc/src/extras.test.ts index 654e93d6cb5a0..f2b29e1de938e 100644 --- a/e2e/nx-misc/src/extras.test.ts +++ b/e2e/nx-misc/src/extras.test.ts @@ -260,4 +260,12 @@ describe('Extra Nx Misc Tests', () => { checkFilesExist(`${folder}/dummy.txt`); }, 120000); }); + + describe('generate --quiet', () => { + it('should not log tree operations or install tasks', () => { + const output = runCLI('generate @nrwl/react:app --quiet test-project'); + expect(output).not.toContain('CREATE'); + expect(output).not.toContain('Installed'); + }); + }); }); diff --git a/packages/create-nx-workspace/src/create-preset.ts b/packages/create-nx-workspace/src/create-preset.ts index 17982faa5500a..a83df25c375e5 100644 --- a/packages/create-nx-workspace/src/create-preset.ts +++ b/packages/create-nx-workspace/src/create-preset.ts @@ -38,7 +38,7 @@ export async function createPreset( } } - const command = `g ${preset}:preset ${args}`; + const command = `g ${preset}:preset --quiet ${args}`; try { const [exec, ...args] = pmc.exec.split(' '); diff --git a/packages/devkit/src/tasks/install-packages-task.ts b/packages/devkit/src/tasks/install-packages-task.ts index 0a886183143fc..d52797f5590f5 100644 --- a/packages/devkit/src/tasks/install-packages-task.ts +++ b/packages/devkit/src/tasks/install-packages-task.ts @@ -40,7 +40,7 @@ export function installPackagesTask( const pmc = getPackageManagerCommand(packageManager); execSync(pmc.install, { cwd: join(tree.root, cwd), - stdio: [0, 1, 2], + stdio: process.env.NX_GENERATE_QUIET === 'true' ? 'ignore' : 'inherit', }); } } diff --git a/packages/devkit/src/utils/package-json.ts b/packages/devkit/src/utils/package-json.ts index e914598ad6c1e..d78cba3e7b7a3 100644 --- a/packages/devkit/src/utils/package-json.ts +++ b/packages/devkit/src/utils/package-json.ts @@ -446,9 +446,11 @@ export function ensurePackage( } const tempDir = dirSync().name; + + console.log(`Fetching ${pkg}...`); execSync(`${getPackageManagerCommand().addDev} ${pkg}@${requiredVersion}`, { cwd: tempDir, - stdio: [0, 1, 2], + stdio: 'ignore', }); addToNodePath(join(workspaceRoot, 'node_modules')); diff --git a/packages/nx/src/adapter/ngcli-adapter.ts b/packages/nx/src/adapter/ngcli-adapter.ts index ea1a0cd29b56d..a0445472d7336 100644 --- a/packages/nx/src/adapter/ngcli-adapter.ts +++ b/packages/nx/src/adapter/ngcli-adapter.ts @@ -792,6 +792,7 @@ export function wrapAngularDevkitSchematic( generatorName, force: false, defaults: false, + quiet: false, }; const workflow = createWorkflow(fsHost, host.root, options); diff --git a/packages/nx/src/command-line/generate.ts b/packages/nx/src/command-line/generate.ts index eeb84f935656b..b36af34ba9bcf 100644 --- a/packages/nx/src/command-line/generate.ts +++ b/packages/nx/src/command-line/generate.ts @@ -31,6 +31,7 @@ export interface GenerateOptions { dryRun: boolean; interactive: boolean; defaults: boolean; + quiet: boolean; } export function printChanges(fileChanges: FileChange[]) { @@ -245,6 +246,7 @@ async function convertToGenerateOptions( dryRun: generatorOptions.dryRun as boolean, interactive, defaults: generatorOptions.defaults as boolean, + quiet: generatorOptions.quiet, }; delete generatorOptions.d; @@ -257,6 +259,7 @@ async function convertToGenerateOptions( delete generatorOptions.generator; delete generatorOptions['--']; delete generatorOptions['$0']; + delete generatorOptions.quiet; return res; } @@ -376,7 +379,9 @@ export async function generate(cwd: string, args: { [k: string]: any }) { const changes = host.listChanges(); - printChanges(changes); + if (!opts.quiet) { + printChanges(changes); + } if (!opts.dryRun) { flushChanges(workspaceRoot, changes); if (task) { diff --git a/packages/nx/src/command-line/nx-commands.ts b/packages/nx/src/command-line/nx-commands.ts index 1dc36d32c44a3..b79016171cfcd 100644 --- a/packages/nx/src/command-line/nx-commands.ts +++ b/packages/nx/src/command-line/nx-commands.ts @@ -754,6 +754,11 @@ function withGenerateOptions(yargs: yargs.Argv) { type: 'boolean', default: false, }) + .option('quiet', { + describe: 'Hides logs from tree operations (e.g. `CREATE package.json`)', + type: 'boolean', + default: false, + }) .middleware((args) => { if (process.env.NX_INTERACTIVE === 'false') { args.interactive = false; @@ -765,6 +770,11 @@ function withGenerateOptions(yargs: yargs.Argv) { } else { process.env.NX_DRY_RUN = `${args.dryRun}`; } + if (process.env.NX_GENERATE_QUIET === 'true') { + args.quiet = true; + } else { + process.env.NX_GENERATE_QUIET = `${args.quiet}`; + } }); if (generatorWillShowHelp) { From 7cb3a3f33324e3d21bf54f6cee1c7c3355eeb5ac Mon Sep 17 00:00:00 2001 From: FrozenPandaz Date: Tue, 21 Mar 2023 20:36:44 -0400 Subject: [PATCH 13/76] chore(misc): publish 15.9.0-beta.7 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 9f32e08e7c670..07516dfbc3eeb 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["build/packages/*", "build/packages/nx/native-packages/*"], - "version": "15.9.0-beta.6", + "version": "15.9.0-beta.7", "granularPathspec": false, "command": { "publish": { From 4dbe6a5d5bb624159c5e7387639dbffbd59b8665 Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Wed, 22 Mar 2023 10:22:19 +0000 Subject: [PATCH 14/76] cleanup(angular): replace wrapAngularDevkitSchematics for component (#15791) --- .../angular/generators/component.json | 5 + .../__snapshots__/component.spec.ts.snap | 60 ++++------ .../src/generators/component/component.ts | 110 ++++++++++++++++-- .../__fileName__.__type__.__style__ | 6 + .../__fileName__.__type__.html__tpl__ | 1 + .../__fileName__.__type__.spec.ts__tpl__ | 22 ++++ .../__fileName__.__type__.ts__tpl__ | 21 ++++ .../src/generators/component/lib/component.ts | 2 +- .../component/lib/normalize-options.ts | 13 ++- .../src/generators/component/schema.d.ts | 2 + .../src/generators/component/schema.json | 5 + .../__snapshots__/library.spec.ts.snap | 66 +++-------- .../src/generators/utils/find-module.ts | 6 +- 13 files changed, 215 insertions(+), 104 deletions(-) create mode 100644 packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.__style__ create mode 100644 packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.html__tpl__ create mode 100644 packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.spec.ts__tpl__ create mode 100644 packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.ts__tpl__ diff --git a/docs/generated/packages/angular/generators/component.json b/docs/generated/packages/angular/generators/component.json index 31cae0d3126b5..148174c4e7090 100644 --- a/docs/generated/packages/angular/generators/component.json +++ b/docs/generated/packages/angular/generators/component.json @@ -28,6 +28,11 @@ "$default": { "$source": "argv", "index": 0 }, "x-prompt": "What name would you like to use for the component?" }, + "prefix": { + "type": "string", + "description": "The prefix to apply to the generated component selector.", + "alias": "p" + }, "displayBlock": { "description": "Specifies if the style will contain `:host { display: block; }`.", "type": "boolean", diff --git a/packages/angular/src/generators/component/__snapshots__/component.spec.ts.snap b/packages/angular/src/generators/component/__snapshots__/component.spec.ts.snap index a8bb0263a0ac6..ddc3b5f0a990b 100644 --- a/packages/angular/src/generators/component/__snapshots__/component.spec.ts.snap +++ b/packages/angular/src/generators/component/__snapshots__/component.spec.ts.snap @@ -4,13 +4,11 @@ exports[`component Generator --flat should create the component correctly and ex "import { Component } from '@angular/core'; @Component({ - selector: 'example', + selector: 'proj-example', templateUrl: './example.component.html', styleUrls: ['./example.component.css'] }) -export class ExampleComponent { - -} +export class ExampleComponent {} " `; @@ -18,13 +16,11 @@ exports[`component Generator --flat should create the component correctly and no "import { Component } from '@angular/core'; @Component({ - selector: 'example', + selector: 'proj-example', templateUrl: './example.component.html', styleUrls: ['./example.component.css'] }) -export class ExampleComponent { - -} +export class ExampleComponent {} " `; @@ -32,13 +28,11 @@ exports[`component Generator --path should create the component correctly and ex "import { Component } from '@angular/core'; @Component({ - selector: 'example', + selector: 'proj-example', templateUrl: './example.component.html', styleUrls: ['./example.component.css'] }) -export class ExampleComponent { - -} +export class ExampleComponent {} " `; @@ -46,13 +40,11 @@ exports[`component Generator secondary entry points should create the component "import { Component } from '@angular/core'; @Component({ - selector: 'example', + selector: 'proj-example', templateUrl: './example.component.html', styleUrls: ['./example.component.css'] }) -export class ExampleComponent { - -} +export class ExampleComponent {} " `; @@ -65,13 +57,11 @@ exports[`component Generator should create the component correctly and export it "import { Component } from '@angular/core'; @Component({ - selector: 'example', + selector: 'proj-example', templateUrl: './example.component.html', styleUrls: ['./example.component.css'] }) -export class ExampleComponent { - -} +export class ExampleComponent {} " `; @@ -85,15 +75,13 @@ exports[`component Generator should create the component correctly and export it import { CommonModule } from '@angular/common'; @Component({ - selector: 'example', + selector: 'proj-example', standalone: true, imports: [CommonModule], templateUrl: './example.component.html', styleUrls: ['./example.component.css'] }) -export class ExampleComponent { - -} +export class ExampleComponent {} " `; @@ -101,13 +89,11 @@ exports[`component Generator should create the component correctly and not expor "import { Component } from '@angular/core'; @Component({ - selector: 'example', + selector: 'proj-example', templateUrl: './example.component.html', styleUrls: ['./example.component.css'] }) -export class ExampleComponent { - -} +export class ExampleComponent {} " `; @@ -116,15 +102,13 @@ exports[`component Generator should create the component correctly and not expor import { CommonModule } from '@angular/common'; @Component({ - selector: 'example', + selector: 'proj-example', standalone: true, imports: [CommonModule], templateUrl: './example.component.html', styleUrls: ['./example.component.css'] }) -export class ExampleComponent { - -} +export class ExampleComponent {} " `; @@ -132,13 +116,11 @@ exports[`component Generator should create the component correctly and not expor "import { Component } from '@angular/core'; @Component({ - selector: 'example', + selector: 'proj-example', templateUrl: './example.component.html', styleUrls: ['./example.component.css'] }) -export class ExampleComponent { - -} +export class ExampleComponent {} " `; @@ -146,12 +128,10 @@ exports[`component Generator should create the component correctly but not expor "import { Component } from '@angular/core'; @Component({ - selector: 'example', + selector: 'proj-example', templateUrl: './example.component.html', styleUrls: ['./example.component.css'] }) -export class ExampleComponent { - -} +export class ExampleComponent {} " `; diff --git a/packages/angular/src/generators/component/component.ts b/packages/angular/src/generators/component/component.ts index 47bd7a68abcf7..f7e55173bd368 100644 --- a/packages/angular/src/generators/component/component.ts +++ b/packages/angular/src/generators/component/component.ts @@ -1,11 +1,20 @@ import type { Tree } from '@nrwl/devkit'; -import { formatFiles, stripIndents } from '@nrwl/devkit'; +import { + formatFiles, + generateFiles, + joinPathFragments, + names, + readNxJson, + stripIndents, +} from '@nrwl/devkit'; import { lt } from 'semver'; import { checkPathUnderProjectRoot } from '../utils/path'; import { getInstalledAngularVersionInfo } from '../utils/version-utils'; import { exportComponentInEntryPoint } from './lib/component'; import { normalizeOptions } from './lib/normalize-options'; import type { Schema } from './schema'; +import { addToNgModule } from '../utils'; +import { findModuleFromOptions } from './lib/module'; export async function componentGenerator(tree: Tree, rawOptions: Schema) { const installedAngularVersionInfo = getInstalledAngularVersionInfo(tree); @@ -19,20 +28,105 @@ export async function componentGenerator(tree: Tree, rawOptions: Schema) { } const options = await normalizeOptions(tree, rawOptions); - const { projectSourceRoot, ...schematicOptions } = options; checkPathUnderProjectRoot(tree, options.project, options.path); - const { wrapAngularDevkitSchematic } = require('@nrwl/devkit/ngcli-adapter'); - const angularComponentSchematic = wrapAngularDevkitSchematic( - '@schematics/angular', - 'component' - ); - await angularComponentSchematic(tree, schematicOptions); + const pathToGenerate = options.flat + ? joinPathFragments(__dirname, './files/__fileName__') + : joinPathFragments(__dirname, './files'); + + const componentNames = names(options.name); + const typeNames = names(options.type); + + const selector = + options.selector || + buildSelector(tree, componentNames.fileName, options.prefix); + + generateFiles(tree, pathToGenerate, options.path, { + fileName: componentNames.fileName, + className: componentNames.className, + type: typeNames.fileName, + typeClassName: typeNames.className, + style: options.style, + inlineStyle: options.inlineStyle, + inlineTemplate: options.inlineTemplate, + standalone: options.standalone, + skipSelector: options.skipSelector, + changeDetection: options.changeDetection, + viewEncapsulation: options.viewEncapsulation, + displayBlock: options.displayBlock, + selector, + tpl: '', + }); + + if (options.skipTests) { + const pathToSpecFile = joinPathFragments( + options.path, + `${!options.flat ? `${componentNames.fileName}/` : ``}${ + componentNames.fileName + }.${typeNames.fileName}.spec.ts` + ); + + tree.delete(pathToSpecFile); + } + + if (options.inlineTemplate) { + const pathToTemplateFile = joinPathFragments( + options.path, + `${!options.flat ? `${componentNames.fileName}/` : ``}${ + componentNames.fileName + }.${typeNames.fileName}.html` + ); + + tree.delete(pathToTemplateFile); + } + + if (options.inlineStyle) { + const pathToStyleFile = joinPathFragments( + options.path, + `${!options.flat ? `${componentNames.fileName}/` : ``}${ + componentNames.fileName + }.${typeNames.fileName}.${options.style}` + ); + + tree.delete(pathToStyleFile); + } + + if (!options.skipImport && !options.standalone) { + const modulePath = findModuleFromOptions( + tree, + options, + options.projectRoot + ); + addToNgModule( + tree, + options.path, + modulePath, + componentNames.fileName, + `${componentNames.className}${typeNames.className}`, + options.flat + ? `${componentNames.fileName}.${typeNames.fileName}` + : joinPathFragments( + componentNames.fileName, + `${componentNames.fileName}.${typeNames.fileName}` + ), + 'declarations', + options.flat, + options.export + ); + } exportComponentInEntryPoint(tree, options); await formatFiles(tree); } +function buildSelector(tree: Tree, name: string, prefix: string) { + const selectorPrefix = names( + prefix ?? readNxJson(tree).npmScope ?? 'app' + ).fileName; + + return names(`${selectorPrefix}-${name}`).fileName; +} + export default componentGenerator; diff --git a/packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.__style__ b/packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.__style__ new file mode 100644 index 0000000000000..421d8143c9bb2 --- /dev/null +++ b/packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.__style__ @@ -0,0 +1,6 @@ +<% if(displayBlock){ if(style != 'sass') { %>:host { + display: block; +} +<% } else { %>\:host + display: block; +<% }} %> diff --git a/packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.html__tpl__ b/packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.html__tpl__ new file mode 100644 index 0000000000000..2fd10df414b4e --- /dev/null +++ b/packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.html__tpl__ @@ -0,0 +1 @@ +

<%= fileName %> works!

diff --git a/packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.spec.ts__tpl__ b/packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.spec.ts__tpl__ new file mode 100644 index 0000000000000..3a6f444f216e9 --- /dev/null +++ b/packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.spec.ts__tpl__ @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { <%= className %><%= typeClassName %> } from './<%= fileName %><%= type ? '.' + type: '' %>'; + +describe('<%= className %><%= typeClassName %>', () => { + let component: <%= className %><%= typeClassName %>; + let fixture: ComponentFixture<<%= className %><%= typeClassName %>>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + <%= standalone ? 'imports' : 'declarations' %>: [ <%= className %><%= typeClassName %> ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(<%= className %><%= typeClassName %>); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.ts__tpl__ b/packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.ts__tpl__ new file mode 100644 index 0000000000000..6cb79a450b3d3 --- /dev/null +++ b/packages/angular/src/generators/component/files/__fileName__/__fileName__.__type__.ts__tpl__ @@ -0,0 +1,21 @@ +import { <% if(changeDetection !== 'Default') { %>ChangeDetectionStrategy, <% }%>Component<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%> } from '@angular/core';<% if(standalone) {%> +import { CommonModule } from '@angular/common';<% } %> + +@Component({<% if(!skipSelector) {%> + selector: '<%= selector %>',<%}%><% if(standalone) {%> + standalone: true, + imports: [CommonModule],<%}%><% if(inlineTemplate) { %> + template: `

<%= fileName %> works!

`<% } else { %> + templateUrl: './<%= fileName %><%= type ? '.' + type : '' %>.html'<% } if(inlineStyle) { %>, + styles: [<% if(displayBlock){ %> + ` + :host { + display: block; + } + `<% } %> + ]<% } else if (style !== 'none') { %>, + styleUrls: ['./<%= fileName %><%= type ? '.' + type : '' %>.<%= style %>']<% } %><% if(!!viewEncapsulation) { %>, + encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>, + changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %> +}) +export class <%= className %><%= typeClassName %> {} diff --git a/packages/angular/src/generators/component/lib/component.ts b/packages/angular/src/generators/component/lib/component.ts index 4f1295ce53fe4..036309bc28ec0 100644 --- a/packages/angular/src/generators/component/lib/component.ts +++ b/packages/angular/src/generators/component/lib/component.ts @@ -11,7 +11,7 @@ export function exportComponentInEntryPoint( tree: Tree, schema: NormalizedSchema ): void { - if (!schema.export || (schema.skipImport && !schema.standalone)) { + if (!schema.export || schema.skipImport) { return; } diff --git a/packages/angular/src/generators/component/lib/normalize-options.ts b/packages/angular/src/generators/component/lib/normalize-options.ts index 999ee958db8f4..e30a844324ea6 100644 --- a/packages/angular/src/generators/component/lib/normalize-options.ts +++ b/packages/angular/src/generators/component/lib/normalize-options.ts @@ -11,16 +11,27 @@ export async function normalizeOptions( options.project ); const projectSourceRoot = sourceRoot ?? joinPathFragments(root, 'src'); + + const parsedName = options.name.split('/'); + const name = parsedName.pop(); + const namedPath = parsedName.join('/'); + const path = options.path ?? joinPathFragments( projectSourceRoot, - projectType === 'application' ? 'app' : 'lib' + projectType === 'application' ? 'app' : 'lib', + namedPath ); return { ...options, + name, + type: options.type ?? 'component', + changeDetection: options.changeDetection ?? 'Default', + style: options.style ?? 'css', path, projectSourceRoot, + projectRoot: root, }; } diff --git a/packages/angular/src/generators/component/schema.d.ts b/packages/angular/src/generators/component/schema.d.ts index 826da21f71a45..2d51cc61b0feb 100644 --- a/packages/angular/src/generators/component/schema.d.ts +++ b/packages/angular/src/generators/component/schema.d.ts @@ -17,9 +17,11 @@ export interface Schema { module?: string; skipSelector?: boolean; export?: boolean; + prefix?: string; } export interface NormalizedSchema extends Schema { path: string; projectSourceRoot: string; + projectRoot: string; } diff --git a/packages/angular/src/generators/component/schema.json b/packages/angular/src/generators/component/schema.json index 8ddb7c5d9a52b..5174b5967657f 100644 --- a/packages/angular/src/generators/component/schema.json +++ b/packages/angular/src/generators/component/schema.json @@ -30,6 +30,11 @@ }, "x-prompt": "What name would you like to use for the component?" }, + "prefix": { + "type": "string", + "description": "The prefix to apply to the generated component selector.", + "alias": "p" + }, "displayBlock": { "description": "Specifies if the style will contain `:host { display: block; }`.", "type": "boolean", diff --git a/packages/angular/src/generators/library/__snapshots__/library.spec.ts.snap b/packages/angular/src/generators/library/__snapshots__/library.spec.ts.snap index 0bd6a91737682..ab699cd741fbf 100644 --- a/packages/angular/src/generators/library/__snapshots__/library.spec.ts.snap +++ b/packages/angular/src/generators/library/__snapshots__/library.spec.ts.snap @@ -13,15 +13,12 @@ import { CommonModule } from '@angular/common'; templateUrl: './my-lib.component.html', styleUrls: ['./my-lib.component.css'] }) -export class MyLibComponent { - -} +export class MyLibComponent {} " `; exports[`lib --angular-14 should generate a library with a standalone component as entry point with angular 14.1.0 3`] = ` "import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { MyLibComponent } from './my-lib.component'; describe('MyLibComponent', () => { @@ -59,15 +56,12 @@ import { CommonModule } from '@angular/common'; templateUrl: './my-lib.component.html', styleUrls: ['./my-lib.component.css'] }) -export class MyLibComponent { - -} +export class MyLibComponent {} " `; exports[`lib --standalone should generate a library with a standalone component and have it flat 3`] = ` "import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { MyLibComponent } from './my-lib.component'; describe('MyLibComponent', () => { @@ -109,15 +103,12 @@ import { CommonModule } from '@angular/common'; templateUrl: './my-lib.component.html', styleUrls: ['./my-lib.component.css'] }) -export class MyLibComponent { - -} +export class MyLibComponent {} " `; exports[`lib --standalone should generate a library with a standalone component and have it flat with routing setup 3`] = ` "import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { MyLibComponent } from './my-lib.component'; describe('MyLibComponent', () => { @@ -165,15 +156,12 @@ import { CommonModule } from '@angular/common'; templateUrl: './my-lib.component.html', styleUrls: ['./my-lib.component.css'] }) -export class MyLibComponent { - -} +export class MyLibComponent {} " `; exports[`lib --standalone should generate a library with a standalone component as entry point 3`] = ` "import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { MyLibComponent } from './my-lib.component'; describe('MyLibComponent', () => { @@ -208,19 +196,13 @@ import { CommonModule } from '@angular/common'; selector: 'proj-my-lib', standalone: true, imports: [CommonModule], - template: \` -

- my-lib works! -

- \`, + template: \`

my-lib works!

\`, styles: [ ], encapsulation: ViewEncapsulation.ShadowDom, changeDetection: ChangeDetectionStrategy.OnPush }) -export class MyLibComponent { - -} +export class MyLibComponent {} " `; @@ -234,17 +216,11 @@ import { CommonModule } from '@angular/common'; selector: 'proj-my-lib', standalone: true, imports: [CommonModule], - template: \` -

- my-lib works! -

- \`, + template: \`

my-lib works!

\`, styles: [ ] }) -export class MyLibComponent { - -} +export class MyLibComponent {} " `; @@ -258,23 +234,16 @@ import { CommonModule } from '@angular/common'; selector: 'proj-my-lib', standalone: true, imports: [CommonModule], - template: \` -

- my-lib works! -

- \`, + template: \`

my-lib works!

\`, styles: [ ] }) -export class MyLibComponent { - -} +export class MyLibComponent {} " `; exports[`lib --standalone should generate a library with a standalone component as entry point following SFC pattern 3`] = ` "import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { MyLibComponent } from './my-lib.component'; describe('MyLibComponent', () => { @@ -326,15 +295,12 @@ import { CommonModule } from '@angular/common'; templateUrl: './my-lib.component.html', styleUrls: ['./my-lib.component.css'] }) -export class MyLibComponent { - -} +export class MyLibComponent {} " `; exports[`lib --standalone should generate a library with a standalone component as entry point with routing setup 4`] = ` "import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { MyLibComponent } from './my-lib.component'; describe('MyLibComponent', () => { @@ -444,15 +410,12 @@ import { CommonModule } from '@angular/common'; templateUrl: './my-dir-my-lib.component.html', styleUrls: ['./my-dir-my-lib.component.css'] }) -export class MyDirMyLibComponent { - -} +export class MyDirMyLibComponent {} " `; exports[`lib --standalone should generate a library with a standalone component in a directory 3`] = ` "import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { MyDirMyLibComponent } from './my-dir-my-lib.component'; describe('MyDirMyLibComponent', () => { @@ -490,15 +453,12 @@ import { CommonModule } from '@angular/common'; templateUrl: './my-lib.component.html', styleUrls: ['./my-lib.component.css'] }) -export class MyLibComponent { - -} +export class MyLibComponent {} " `; exports[`lib --standalone should generate a library with a standalone component in a directory with a simple name 3`] = ` "import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { MyLibComponent } from './my-lib.component'; describe('MyLibComponent', () => { diff --git a/packages/angular/src/generators/utils/find-module.ts b/packages/angular/src/generators/utils/find-module.ts index 88e8fc3740276..854cd26c45b61 100644 --- a/packages/angular/src/generators/utils/find-module.ts +++ b/packages/angular/src/generators/utils/find-module.ts @@ -13,7 +13,7 @@ let tsModule: typeof import('typescript'); export function findModule(tree: Tree, path: string, module?: string) { let modulePath = ''; let pathToSearch = path; - while (pathToSearch !== '/') { + while (pathToSearch !== '.' && pathToSearch !== '/') { if (module) { const pathToModule = joinPathFragments(pathToSearch, module); if (tree.exists(pathToModule)) { @@ -36,6 +36,10 @@ export function findModule(tree: Tree, path: string, module?: string) { pathToSearch = dirname(pathToSearch); } + if (modulePath === '') { + throw new Error('Could not find a declaring module file.'); + } + const moduleContents = tree.read(modulePath, 'utf-8'); if (!moduleContents.includes('@NgModule')) { throw new Error( From 44478fb57ec14b98f4f1a2e92144f8072a715d58 Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Wed, 22 Mar 2023 11:34:44 +0000 Subject: [PATCH 15/76] cleanup(angular): add warning to schematic usage of generators (#15815) --- packages/angular/src/generators/add-linting/compat.ts | 3 ++- .../src/generators/application/application.compat.ts | 5 ++++- .../src/generators/component-cypress-spec/compat.ts | 5 ++++- packages/angular/src/generators/component-story/compat.ts | 5 ++++- packages/angular/src/generators/component-test/compat.ts | 5 ++++- .../angular/src/generators/component/component.compat.ts | 3 ++- .../convert-to-with-mf/convert-to-with-mf.compat.ts | 3 ++- .../convert-tslint-to-eslint/convert-tslint-to-eslint.ts | 5 ++++- .../generators/cypress-component-configuration/compat.ts | 5 ++++- packages/angular/src/generators/host/host.compat.ts | 3 ++- packages/angular/src/generators/init/init.compat.ts | 5 ++++- .../src/generators/library-secondary-entry-point/compat.ts | 5 ++++- packages/angular/src/generators/library/library.compat.ts | 5 ++++- packages/angular/src/generators/move/move.ts | 5 ++++- packages/angular/src/generators/ngrx/compat.ts | 3 ++- packages/angular/src/generators/remote/remote.compat.ts | 3 ++- .../src/generators/scam-directive/scam-directive.compat.ts | 3 ++- .../angular/src/generators/scam-pipe/scam-pipe.compat.ts | 3 ++- .../angular/src/generators/scam-to-standalone/compat.ts | 3 ++- packages/angular/src/generators/scam/scam.compat.ts | 3 ++- .../angular/src/generators/setup-mf/setup-mf.compat.ts | 3 ++- .../angular/src/generators/setup-ssr/setup-ssr.compat.ts | 3 ++- .../src/generators/setup-tailwind/setup-tailwind.compat.ts | 5 ++++- packages/angular/src/generators/stories/compat.ts | 5 ++++- .../src/generators/storybook-configuration/compat.ts | 5 ++++- .../src/generators/utils/warn-for-schematic-usage.ts | 7 +++++++ packages/angular/src/generators/web-worker/compat.ts | 3 ++- 27 files changed, 85 insertions(+), 26 deletions(-) create mode 100644 packages/angular/src/generators/utils/warn-for-schematic-usage.ts diff --git a/packages/angular/src/generators/add-linting/compat.ts b/packages/angular/src/generators/add-linting/compat.ts index de50b010701e0..ec253ec541140 100644 --- a/packages/angular/src/generators/add-linting/compat.ts +++ b/packages/angular/src/generators/add-linting/compat.ts @@ -1,4 +1,5 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { addLintingGenerator } from './add-linting'; -export default convertNxGenerator(addLintingGenerator); +export default warnForSchematicUsage(convertNxGenerator(addLintingGenerator)); diff --git a/packages/angular/src/generators/application/application.compat.ts b/packages/angular/src/generators/application/application.compat.ts index 991fe623994f8..9c7c57629f70e 100644 --- a/packages/angular/src/generators/application/application.compat.ts +++ b/packages/angular/src/generators/application/application.compat.ts @@ -1,4 +1,7 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import application from './application'; -export const applicationSchematic = convertNxGenerator(application); +export const applicationSchematic = warnForSchematicUsage( + convertNxGenerator(application) +); diff --git a/packages/angular/src/generators/component-cypress-spec/compat.ts b/packages/angular/src/generators/component-cypress-spec/compat.ts index 3047bda8bb9ad..8600d16d5b59a 100644 --- a/packages/angular/src/generators/component-cypress-spec/compat.ts +++ b/packages/angular/src/generators/component-cypress-spec/compat.ts @@ -1,4 +1,7 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { componentCypressSpecGenerator } from './component-cypress-spec'; -export default convertNxGenerator(componentCypressSpecGenerator); +export default warnForSchematicUsage( + convertNxGenerator(componentCypressSpecGenerator) +); diff --git a/packages/angular/src/generators/component-story/compat.ts b/packages/angular/src/generators/component-story/compat.ts index 64866f0a57582..ec8b2ef9cd8aa 100644 --- a/packages/angular/src/generators/component-story/compat.ts +++ b/packages/angular/src/generators/component-story/compat.ts @@ -1,4 +1,7 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { componentStoryGenerator } from './component-story'; -export default convertNxGenerator(componentStoryGenerator); +export default warnForSchematicUsage( + convertNxGenerator(componentStoryGenerator) +); diff --git a/packages/angular/src/generators/component-test/compat.ts b/packages/angular/src/generators/component-test/compat.ts index 8245c03054ec4..fb99057e38340 100644 --- a/packages/angular/src/generators/component-test/compat.ts +++ b/packages/angular/src/generators/component-test/compat.ts @@ -1,4 +1,7 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { componentTestGenerator } from './component-test'; -export default convertNxGenerator(componentTestGenerator); +export default warnForSchematicUsage( + convertNxGenerator(componentTestGenerator) +); diff --git a/packages/angular/src/generators/component/component.compat.ts b/packages/angular/src/generators/component/component.compat.ts index f73c31c33528b..85d23ed319946 100644 --- a/packages/angular/src/generators/component/component.compat.ts +++ b/packages/angular/src/generators/component/component.compat.ts @@ -1,4 +1,5 @@ import componentGenerator from './component'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { convertNxGenerator } from '@nrwl/devkit'; -export default convertNxGenerator(componentGenerator); +export default warnForSchematicUsage(convertNxGenerator(componentGenerator)); diff --git a/packages/angular/src/generators/convert-to-with-mf/convert-to-with-mf.compat.ts b/packages/angular/src/generators/convert-to-with-mf/convert-to-with-mf.compat.ts index 1389226105582..e544ee4920a5d 100644 --- a/packages/angular/src/generators/convert-to-with-mf/convert-to-with-mf.compat.ts +++ b/packages/angular/src/generators/convert-to-with-mf/convert-to-with-mf.compat.ts @@ -1,4 +1,5 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import convertToWithMF from './convert-to-with-mf'; -export default convertNxGenerator(convertToWithMF); +export default warnForSchematicUsage(convertNxGenerator(convertToWithMF)); diff --git a/packages/angular/src/generators/convert-tslint-to-eslint/convert-tslint-to-eslint.ts b/packages/angular/src/generators/convert-tslint-to-eslint/convert-tslint-to-eslint.ts index cbbde4fcdf398..1601dcd1fe06b 100755 --- a/packages/angular/src/generators/convert-tslint-to-eslint/convert-tslint-to-eslint.ts +++ b/packages/angular/src/generators/convert-tslint-to-eslint/convert-tslint-to-eslint.ts @@ -6,6 +6,7 @@ import { logger, Tree, } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { ConvertTSLintToESLintSchema, ProjectConverter } from '@nrwl/linter'; import type { Linter } from 'eslint'; import { addLintingGenerator } from '../add-linting/add-linting'; @@ -137,7 +138,9 @@ export async function conversionGenerator( }; } -export const conversionSchematic = convertNxGenerator(conversionGenerator); +export const conversionSchematic = warnForSchematicUsage( + convertNxGenerator(conversionGenerator) +); /** * In the case of Angular lint rules, we need to apply them to correct override depending upon whether diff --git a/packages/angular/src/generators/cypress-component-configuration/compat.ts b/packages/angular/src/generators/cypress-component-configuration/compat.ts index c6857c6aa1749..7e76a1967b7ce 100644 --- a/packages/angular/src/generators/cypress-component-configuration/compat.ts +++ b/packages/angular/src/generators/cypress-component-configuration/compat.ts @@ -1,4 +1,7 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { cypressComponentConfiguration } from './cypress-component-configuration'; -export default convertNxGenerator(cypressComponentConfiguration); +export default warnForSchematicUsage( + convertNxGenerator(cypressComponentConfiguration) +); diff --git a/packages/angular/src/generators/host/host.compat.ts b/packages/angular/src/generators/host/host.compat.ts index e9d6e3f165b03..b976df1330fb2 100644 --- a/packages/angular/src/generators/host/host.compat.ts +++ b/packages/angular/src/generators/host/host.compat.ts @@ -1,4 +1,5 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import host from './host'; -export default convertNxGenerator(host); +export default warnForSchematicUsage(convertNxGenerator(host)); diff --git a/packages/angular/src/generators/init/init.compat.ts b/packages/angular/src/generators/init/init.compat.ts index 377fa17212dc0..2796b24e1f31b 100644 --- a/packages/angular/src/generators/init/init.compat.ts +++ b/packages/angular/src/generators/init/init.compat.ts @@ -1,4 +1,7 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { angularInitGenerator } from './init'; -export const initSchematic = convertNxGenerator(angularInitGenerator); +export const initSchematic = warnForSchematicUsage( + convertNxGenerator(angularInitGenerator) +); diff --git a/packages/angular/src/generators/library-secondary-entry-point/compat.ts b/packages/angular/src/generators/library-secondary-entry-point/compat.ts index e7395c5aca8fc..7b645dccab51b 100644 --- a/packages/angular/src/generators/library-secondary-entry-point/compat.ts +++ b/packages/angular/src/generators/library-secondary-entry-point/compat.ts @@ -1,4 +1,7 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { librarySecondaryEntryPointGenerator } from './library-secondary-entry-point'; -export default convertNxGenerator(librarySecondaryEntryPointGenerator); +export default warnForSchematicUsage( + convertNxGenerator(librarySecondaryEntryPointGenerator) +); diff --git a/packages/angular/src/generators/library/library.compat.ts b/packages/angular/src/generators/library/library.compat.ts index f6dc564067e15..be5e842a54815 100644 --- a/packages/angular/src/generators/library/library.compat.ts +++ b/packages/angular/src/generators/library/library.compat.ts @@ -1,4 +1,7 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import library from './library'; -export const librarySchematic = convertNxGenerator(library); +export const librarySchematic = warnForSchematicUsage( + convertNxGenerator(library) +); diff --git a/packages/angular/src/generators/move/move.ts b/packages/angular/src/generators/move/move.ts index ed3a50c8b70f4..6f84ee20eb04d 100644 --- a/packages/angular/src/generators/move/move.ts +++ b/packages/angular/src/generators/move/move.ts @@ -1,4 +1,5 @@ import { convertNxGenerator, formatFiles, Tree } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { moveGenerator } from '@nrwl/workspace/generators'; import { updateModuleName } from './lib/update-module-name'; import { updateNgPackage } from './lib/update-ng-package'; @@ -24,4 +25,6 @@ export async function angularMoveGenerator( } } -export const angularMoveSchematic = convertNxGenerator(angularMoveGenerator); +export const angularMoveSchematic = warnForSchematicUsage( + convertNxGenerator(angularMoveGenerator) +); diff --git a/packages/angular/src/generators/ngrx/compat.ts b/packages/angular/src/generators/ngrx/compat.ts index 7e7ba07137204..9b7d2baed2585 100644 --- a/packages/angular/src/generators/ngrx/compat.ts +++ b/packages/angular/src/generators/ngrx/compat.ts @@ -1,4 +1,5 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { ngrxGenerator } from './ngrx'; -export default convertNxGenerator(ngrxGenerator); +export default warnForSchematicUsage(convertNxGenerator(ngrxGenerator)); diff --git a/packages/angular/src/generators/remote/remote.compat.ts b/packages/angular/src/generators/remote/remote.compat.ts index 4bc9f1fc8c0c1..586cb509857e2 100644 --- a/packages/angular/src/generators/remote/remote.compat.ts +++ b/packages/angular/src/generators/remote/remote.compat.ts @@ -1,4 +1,5 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import remote from './remote'; -export default convertNxGenerator(remote); +export default warnForSchematicUsage(convertNxGenerator(remote)); diff --git a/packages/angular/src/generators/scam-directive/scam-directive.compat.ts b/packages/angular/src/generators/scam-directive/scam-directive.compat.ts index 94e4962f95c43..b64e34a2b7906 100644 --- a/packages/angular/src/generators/scam-directive/scam-directive.compat.ts +++ b/packages/angular/src/generators/scam-directive/scam-directive.compat.ts @@ -1,4 +1,5 @@ import scamGenerator from './scam-directive'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { convertNxGenerator } from '@nrwl/devkit'; -export default convertNxGenerator(scamGenerator); +export default warnForSchematicUsage(convertNxGenerator(scamGenerator)); diff --git a/packages/angular/src/generators/scam-pipe/scam-pipe.compat.ts b/packages/angular/src/generators/scam-pipe/scam-pipe.compat.ts index e166791d619cf..e11c6a66c0336 100644 --- a/packages/angular/src/generators/scam-pipe/scam-pipe.compat.ts +++ b/packages/angular/src/generators/scam-pipe/scam-pipe.compat.ts @@ -1,4 +1,5 @@ import scamPipeGenerator from './scam-pipe'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { convertNxGenerator } from '@nrwl/devkit'; -export default convertNxGenerator(scamPipeGenerator); +export default warnForSchematicUsage(convertNxGenerator(scamPipeGenerator)); diff --git a/packages/angular/src/generators/scam-to-standalone/compat.ts b/packages/angular/src/generators/scam-to-standalone/compat.ts index 8189a3206f120..c1293ae5189e5 100644 --- a/packages/angular/src/generators/scam-to-standalone/compat.ts +++ b/packages/angular/src/generators/scam-to-standalone/compat.ts @@ -1,4 +1,5 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { scamToStandalone } from './scam-to-standalone'; -export default convertNxGenerator(scamToStandalone); +export default warnForSchematicUsage(convertNxGenerator(scamToStandalone)); diff --git a/packages/angular/src/generators/scam/scam.compat.ts b/packages/angular/src/generators/scam/scam.compat.ts index 0eb7743a7425f..4e92fad1225b9 100644 --- a/packages/angular/src/generators/scam/scam.compat.ts +++ b/packages/angular/src/generators/scam/scam.compat.ts @@ -1,4 +1,5 @@ import scamGenerator from './scam'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { convertNxGenerator } from '@nrwl/devkit'; -export default convertNxGenerator(scamGenerator); +export default warnForSchematicUsage(convertNxGenerator(scamGenerator)); diff --git a/packages/angular/src/generators/setup-mf/setup-mf.compat.ts b/packages/angular/src/generators/setup-mf/setup-mf.compat.ts index 1daf80fb4e9a5..d70619038adfd 100644 --- a/packages/angular/src/generators/setup-mf/setup-mf.compat.ts +++ b/packages/angular/src/generators/setup-mf/setup-mf.compat.ts @@ -1,4 +1,5 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { setupMf } from './setup-mf'; -export default convertNxGenerator(setupMf); +export default warnForSchematicUsage(convertNxGenerator(setupMf)); diff --git a/packages/angular/src/generators/setup-ssr/setup-ssr.compat.ts b/packages/angular/src/generators/setup-ssr/setup-ssr.compat.ts index 40911dfdc13ff..be052ea0f8c59 100644 --- a/packages/angular/src/generators/setup-ssr/setup-ssr.compat.ts +++ b/packages/angular/src/generators/setup-ssr/setup-ssr.compat.ts @@ -1,4 +1,5 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { setupSsr } from './setup-ssr'; -export default convertNxGenerator(setupSsr); +export default warnForSchematicUsage(convertNxGenerator(setupSsr)); diff --git a/packages/angular/src/generators/setup-tailwind/setup-tailwind.compat.ts b/packages/angular/src/generators/setup-tailwind/setup-tailwind.compat.ts index 03de034928f8c..fe4cd06277445 100644 --- a/packages/angular/src/generators/setup-tailwind/setup-tailwind.compat.ts +++ b/packages/angular/src/generators/setup-tailwind/setup-tailwind.compat.ts @@ -1,4 +1,7 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { setupTailwindGenerator } from './setup-tailwind'; -export default convertNxGenerator(setupTailwindGenerator); +export default warnForSchematicUsage( + convertNxGenerator(setupTailwindGenerator) +); diff --git a/packages/angular/src/generators/stories/compat.ts b/packages/angular/src/generators/stories/compat.ts index 800b2b57dfeac..effdd2f3928b1 100644 --- a/packages/angular/src/generators/stories/compat.ts +++ b/packages/angular/src/generators/stories/compat.ts @@ -1,4 +1,7 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { angularStoriesGenerator } from './stories'; -export default convertNxGenerator(angularStoriesGenerator); +export default warnForSchematicUsage( + convertNxGenerator(angularStoriesGenerator) +); diff --git a/packages/angular/src/generators/storybook-configuration/compat.ts b/packages/angular/src/generators/storybook-configuration/compat.ts index 10d4a018f85d6..d0e08eab2cc56 100644 --- a/packages/angular/src/generators/storybook-configuration/compat.ts +++ b/packages/angular/src/generators/storybook-configuration/compat.ts @@ -1,4 +1,7 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { storybookConfigurationGenerator } from './storybook-configuration'; -export default convertNxGenerator(storybookConfigurationGenerator); +export default warnForSchematicUsage( + convertNxGenerator(storybookConfigurationGenerator) +); diff --git a/packages/angular/src/generators/utils/warn-for-schematic-usage.ts b/packages/angular/src/generators/utils/warn-for-schematic-usage.ts new file mode 100644 index 0000000000000..2d10ec100f0de --- /dev/null +++ b/packages/angular/src/generators/utils/warn-for-schematic-usage.ts @@ -0,0 +1,7 @@ +export function warnForSchematicUsage(convertedGenerator: T): T { + console.warn( + 'Running generators as schematics is deprecated and will be removed in v17.' + ); + + return convertedGenerator; +} diff --git a/packages/angular/src/generators/web-worker/compat.ts b/packages/angular/src/generators/web-worker/compat.ts index 37ae7d4113016..6e74e6361f49f 100644 --- a/packages/angular/src/generators/web-worker/compat.ts +++ b/packages/angular/src/generators/web-worker/compat.ts @@ -1,4 +1,5 @@ import { convertNxGenerator } from '@nrwl/devkit'; +import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage'; import { webWorkerGenerator } from './web-worker'; -export default convertNxGenerator(webWorkerGenerator); +export default warnForSchematicUsage(convertNxGenerator(webWorkerGenerator)); From ec1d8e1bf924a28f67ca2b462457bd0854f3493f Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Wed, 22 Mar 2023 14:36:22 +0000 Subject: [PATCH 16/76] cleanup(angular): remove v14 folder for init (#15816) --- .../src/generators/init/angular-v14/init.ts | 209 ------------------ .../generators/init/angular-v14/schema.d.ts | 14 -- packages/angular/src/generators/init/init.ts | 65 ++++-- 3 files changed, 41 insertions(+), 247 deletions(-) delete mode 100755 packages/angular/src/generators/init/angular-v14/init.ts delete mode 100644 packages/angular/src/generators/init/angular-v14/schema.d.ts diff --git a/packages/angular/src/generators/init/angular-v14/init.ts b/packages/angular/src/generators/init/angular-v14/init.ts deleted file mode 100755 index 4101f85b33cac..0000000000000 --- a/packages/angular/src/generators/init/angular-v14/init.ts +++ /dev/null @@ -1,209 +0,0 @@ -import { cypressInitGenerator } from '@nrwl/cypress'; -import { - addDependenciesToPackageJson, - ensurePackage, - formatFiles, - GeneratorCallback, - logger, - readNxJson, - runTasksInSerial, - Tree, - updateNxJson, -} from '@nrwl/devkit'; -import { jestInitGenerator } from '@nrwl/jest'; -import { Linter } from '@nrwl/linter'; -import { initGenerator as jsInitGenerator } from '@nrwl/js'; - -import { backwardCompatibleVersions } from '../../../utils/backward-compatible-versions'; -import { E2eTestRunner, UnitTestRunner } from '../../../utils/test-runners'; -import { - addDependenciesToPackageJsonIfDontExist, - getInstalledPackageVersion, -} from '../../utils/version-utils'; -import { Schema } from './schema'; - -export async function angularInitGenerator( - host: Tree, - rawOptions: Schema -): Promise { - const options = normalizeOptions(rawOptions); - setDefaults(host, options); - const tasks: GeneratorCallback[] = []; - - const peerDepsToInstall = [ - '@angular-devkit/core', - '@angular-devkit/schematics', - '@schematics/angular', - ]; - let devkitVersion: string; - peerDepsToInstall.forEach((pkg) => { - const packageVersion = getInstalledPackageVersion(host, pkg); - - if (!packageVersion) { - devkitVersion ??= - getInstalledPackageVersion(host, '@angular-devkit/build-angular') ?? - backwardCompatibleVersions.angularV14.angularDevkitVersion; - - try { - ensurePackage(pkg, devkitVersion); - } catch { - // @schematics/angular cannot be required so this fails but this will still allow wrapping the schematic later on - } - - if (!options.skipPackageJson) { - tasks.push( - addDependenciesToPackageJson(host, {}, { [pkg]: devkitVersion }) - ); - } - } - }); - - const jsTask = await jsInitGenerator(host, { - ...options, - js: false, - tsConfigName: options.rootProject ? 'tsconfig.json' : 'tsconfig.base.json', - skipFormat: true, - }); - tasks.push(jsTask); - - if (!options.skipPackageJson) { - tasks.push(updateDependencies(host)); - } - - const unitTestTask = await addUnitTestRunner(host, options); - tasks.push(unitTestTask); - const e2eTask = await addE2ETestRunner(host, options); - tasks.push(e2eTask); - - addGitIgnoreEntry(host, '.angular'); - - if (!options.skipFormat) { - await formatFiles(host); - } - - return runTasksInSerial(...tasks); -} - -function normalizeOptions(options: Schema): Required { - return { - e2eTestRunner: options.e2eTestRunner ?? E2eTestRunner.Cypress, - linter: options.linter ?? Linter.EsLint, - skipFormat: options.skipFormat ?? false, - skipInstall: options.skipInstall ?? false, - skipPackageJson: options.skipPackageJson ?? false, - style: options.style ?? 'css', - unitTestRunner: options.unitTestRunner ?? UnitTestRunner.Jest, - rootProject: options.rootProject, - }; -} - -function setDefaults(host: Tree, options: Schema) { - const nxJson = readNxJson(host); - - nxJson.generators = nxJson.generators || {}; - nxJson.generators['@nrwl/angular:application'] = { - style: options.style, - linter: options.linter, - unitTestRunner: options.unitTestRunner, - e2eTestRunner: options.e2eTestRunner, - ...(nxJson.generators['@nrwl/angular:application'] || {}), - }; - nxJson.generators['@nrwl/angular:library'] = { - linter: options.linter, - unitTestRunner: options.unitTestRunner, - ...(nxJson.generators['@nrwl/angular:library'] || {}), - }; - nxJson.generators['@nrwl/angular:component'] = { - style: options.style, - ...(nxJson.generators['@nrwl/angular:component'] || {}), - }; - - updateNxJson(host, nxJson); -} - -function updateDependencies(tree: Tree): GeneratorCallback { - const angularVersion = - getInstalledPackageVersion(tree, '@angular/core') ?? - backwardCompatibleVersions.angularV14.angularVersion; - const angularDevkitVersion = - getInstalledPackageVersion(tree, '@angular-devkit/build-angular') ?? - backwardCompatibleVersions.angularV14.angularDevkitVersion; - - return addDependenciesToPackageJsonIfDontExist( - tree, - { - '@angular/animations': angularVersion, - '@angular/common': angularVersion, - '@angular/compiler': angularVersion, - '@angular/core': angularVersion, - '@angular/forms': angularVersion, - '@angular/platform-browser': angularVersion, - '@angular/platform-browser-dynamic': angularVersion, - '@angular/router': angularVersion, - rxjs: backwardCompatibleVersions.angularV14.rxjsVersion, - tslib: backwardCompatibleVersions.angularV14.tsLibVersion, - 'zone.js': backwardCompatibleVersions.angularV14.zoneJsVersion, - }, - { - '@angular/cli': angularDevkitVersion, - '@angular/compiler-cli': angularVersion, - '@angular/language-service': angularVersion, - '@angular-devkit/build-angular': angularDevkitVersion, - } - ); -} - -async function addUnitTestRunner( - tree: Tree, - options: Schema -): Promise { - switch (options.unitTestRunner) { - case UnitTestRunner.Jest: - if (!options.skipPackageJson) { - addDependenciesToPackageJsonIfDontExist( - tree, - {}, - { - 'jest-preset-angular': - backwardCompatibleVersions.angularV14.jestPresetAngularVersion, - } - ); - } - - return jestInitGenerator(tree, { - skipPackageJson: options.skipPackageJson, - }); - default: - return () => {}; - } -} - -async function addE2ETestRunner( - tree: Tree, - options: Schema -): Promise { - switch (options.e2eTestRunner) { - case E2eTestRunner.Cypress: - return await cypressInitGenerator(tree, { - skipPackageJson: options.skipPackageJson, - }); - default: - return () => {}; - } -} - -function addGitIgnoreEntry(host: Tree, entry: string) { - if (host.exists('.gitignore')) { - let content = host.read('.gitignore', 'utf-8'); - if (/^\.angular$/gm.test(content)) { - return; - } - - content = `${content}\n${entry}\n`; - host.write('.gitignore', content); - } else { - logger.warn(`Couldn't find .gitignore file to update`); - } -} - -export default angularInitGenerator; diff --git a/packages/angular/src/generators/init/angular-v14/schema.d.ts b/packages/angular/src/generators/init/angular-v14/schema.d.ts deleted file mode 100644 index 171b5d834e187..0000000000000 --- a/packages/angular/src/generators/init/angular-v14/schema.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Linter } from '@nrwl/linter'; -import { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners'; -import type { Styles } from '../utils/types'; - -export interface Schema { - unitTestRunner?: UnitTestRunner; - e2eTestRunner?: E2eTestRunner; - skipFormat?: boolean; - skipInstall?: boolean; - style?: Styles; - linter?: Linter; - skipPackageJson?: boolean; - rootProject?: boolean; -} diff --git a/packages/angular/src/generators/init/init.ts b/packages/angular/src/generators/init/init.ts index 60d4ed05c5635..7c005c1c71958 100755 --- a/packages/angular/src/generators/init/init.ts +++ b/packages/angular/src/generators/init/init.ts @@ -13,8 +13,6 @@ import { import { jestInitGenerator } from '@nrwl/jest'; import { Linter } from '@nrwl/linter'; import { initGenerator as jsInitGenerator } from '@nrwl/js'; - -import { join } from 'path'; import { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners'; import { angularDevkitVersion, @@ -26,25 +24,17 @@ import { } from '../../utils/versions'; import { addDependenciesToPackageJsonIfDontExist, - getGeneratorDirectoryForInstalledAngularVersion, + getInstalledAngularVersionInfo, getInstalledPackageVersion, } from '../utils/version-utils'; +import { backwardCompatibleVersions } from '../../utils/backward-compatible-versions'; import { Schema } from './schema'; export async function angularInitGenerator( tree: Tree, rawOptions: Schema ): Promise { - const generatorDirectory = - getGeneratorDirectoryForInstalledAngularVersion(tree); - if (generatorDirectory) { - let previousGenerator = await import( - join(__dirname, generatorDirectory, 'init') - ); - await previousGenerator.default(tree, rawOptions); - return; - } - + const installedAngularVersionInfo = getInstalledAngularVersionInfo(tree); const tasks: GeneratorCallback[] = []; const options = normalizeOptions(rawOptions); @@ -60,7 +50,9 @@ export async function angularInitGenerator( if (!packageVersion) { devkitVersion ??= getInstalledPackageVersion(tree, '@angular-devkit/build-angular') ?? - angularDevkitVersion; + (installedAngularVersionInfo.major === 14 + ? backwardCompatibleVersions.angularV14.angularDevkitVersion + : angularDevkitVersion); try { ensurePackage(pkg, devkitVersion); @@ -86,9 +78,13 @@ export async function angularInitGenerator( tasks.push(jsTask); if (!options.skipPackageJson) { - tasks.push(updateDependencies(tree)); + tasks.push(updateDependencies(tree, installedAngularVersionInfo.major)); } - const unitTestTask = await addUnitTestRunner(tree, options); + const unitTestTask = await addUnitTestRunner( + tree, + options, + installedAngularVersionInfo.major + ); tasks.push(unitTestTask); const e2eTask = await addE2ETestRunner(tree, options); tasks.push(e2eTask); @@ -139,12 +135,20 @@ function setDefaults(host: Tree, options: Schema) { updateNxJson(host, nxJson); } -function updateDependencies(tree: Tree): GeneratorCallback { +function updateDependencies( + tree: Tree, + angularMajorVersion: number +): GeneratorCallback { const angularVersionToInstall = - getInstalledPackageVersion(tree, '@angular/core') ?? angularVersion; + getInstalledPackageVersion(tree, '@angular/core') ?? + (angularMajorVersion === 14 + ? backwardCompatibleVersions.angularV14.angularVersion + : angularVersion); const angularDevkitVersionToInstall = getInstalledPackageVersion(tree, '@angular-devkit/build-angular') ?? - angularDevkitVersion; + (angularMajorVersion === 14 + ? backwardCompatibleVersions.angularV14.angularDevkitVersion + : angularDevkitVersion); return addDependenciesToPackageJsonIfDontExist( tree, @@ -157,9 +161,18 @@ function updateDependencies(tree: Tree): GeneratorCallback { '@angular/platform-browser': angularVersionToInstall, '@angular/platform-browser-dynamic': angularVersionToInstall, '@angular/router': angularVersionToInstall, - rxjs: rxjsVersion, - tslib: tsLibVersion, - 'zone.js': zoneJsVersion, + rxjs: + angularMajorVersion === 14 + ? backwardCompatibleVersions.angularV14.rxjsVersion + : rxjsVersion, + tslib: + angularMajorVersion === 14 + ? backwardCompatibleVersions.angularV14.tsLibVersion + : tsLibVersion, + 'zone.js': + angularMajorVersion === 14 + ? backwardCompatibleVersions.angularV14.zoneJsVersion + : zoneJsVersion, }, { '@angular/cli': angularDevkitVersionToInstall, @@ -172,7 +185,8 @@ function updateDependencies(tree: Tree): GeneratorCallback { async function addUnitTestRunner( tree: Tree, - options: Schema + options: Schema, + angularMajorVersion: number ): Promise { switch (options.unitTestRunner) { case UnitTestRunner.Jest: @@ -181,7 +195,10 @@ async function addUnitTestRunner( tree, {}, { - 'jest-preset-angular': jestPresetAngularVersion, + 'jest-preset-angular': + angularMajorVersion === 14 + ? backwardCompatibleVersions.angularV14.jestPresetAngularVersion + : jestPresetAngularVersion, } ); } From 1db5fb82b922441a9f082ffa661a335813e95976 Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Wed, 22 Mar 2023 14:36:40 +0000 Subject: [PATCH 17/76] cleanup(angular): remove duplicated code for add-linting (#15818) --- .../src/generators/add-linting/add-linting.ts | 12 -- .../add-linting/angular-v14/add-linting.ts | 52 ------- .../lib/add-angular-eslint-dependencies.ts | 18 --- .../lib/add-project-lint-target.ts | 25 ---- .../lib/create-eslint-configuration.ts | 135 ------------------ .../add-linting/angular-v14/schema.d.ts | 9 -- .../lib/add-angular-eslint-dependencies.ts | 13 +- .../lib/add-project-lint-target.ts | 25 ---- 8 files changed, 10 insertions(+), 279 deletions(-) delete mode 100755 packages/angular/src/generators/add-linting/angular-v14/add-linting.ts delete mode 100644 packages/angular/src/generators/add-linting/angular-v14/lib/add-angular-eslint-dependencies.ts delete mode 100644 packages/angular/src/generators/add-linting/angular-v14/lib/add-project-lint-target.ts delete mode 100644 packages/angular/src/generators/add-linting/angular-v14/lib/create-eslint-configuration.ts delete mode 100644 packages/angular/src/generators/add-linting/angular-v14/schema.d.ts delete mode 100644 packages/angular/src/generators/add-linting/lib/add-project-lint-target.ts diff --git a/packages/angular/src/generators/add-linting/add-linting.ts b/packages/angular/src/generators/add-linting/add-linting.ts index 62d89c626d53c..97e9a48f31da4 100755 --- a/packages/angular/src/generators/add-linting/add-linting.ts +++ b/packages/angular/src/generators/add-linting/add-linting.ts @@ -7,8 +7,6 @@ import { } from '@nrwl/devkit'; import { Linter, lintProjectGenerator } from '@nrwl/linter'; import { mapLintPattern } from '@nrwl/linter/src/generators/lint-project/lint-project'; -import { join } from 'path'; -import { getGeneratorDirectoryForInstalledAngularVersion } from '../utils/version-utils'; import { addAngularEsLintDependencies } from './lib/add-angular-eslint-dependencies'; import { extendAngularEslintJson } from './lib/create-eslint-configuration'; import type { AddLintingGeneratorSchema } from './schema'; @@ -17,16 +15,6 @@ export async function addLintingGenerator( tree: Tree, options: AddLintingGeneratorSchema ): Promise { - const generatorDirectory = - getGeneratorDirectoryForInstalledAngularVersion(tree); - if (generatorDirectory) { - let previousGenerator = await import( - join(__dirname, generatorDirectory, 'add-linting') - ); - await previousGenerator.default(tree, options); - return; - } - const tasks: GeneratorCallback[] = []; const rootProject = options.projectRoot === '.' || options.projectRoot === ''; const lintTask = await lintProjectGenerator(tree, { diff --git a/packages/angular/src/generators/add-linting/angular-v14/add-linting.ts b/packages/angular/src/generators/add-linting/angular-v14/add-linting.ts deleted file mode 100755 index e8817f59ad3c9..0000000000000 --- a/packages/angular/src/generators/add-linting/angular-v14/add-linting.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { - GeneratorCallback, - joinPathFragments, - runTasksInSerial, - Tree, - updateJson, -} from '@nrwl/devkit'; -import { Linter, lintProjectGenerator } from '@nrwl/linter'; -import { mapLintPattern } from '@nrwl/linter/src/generators/lint-project/lint-project'; - -import { addAngularEsLintDependencies } from './lib/add-angular-eslint-dependencies'; -import { extendAngularEslintJson } from './lib/create-eslint-configuration'; -import type { AddLintingGeneratorSchema } from './schema'; - -export async function addLintingGenerator( - tree: Tree, - options: AddLintingGeneratorSchema -): Promise { - const tasks: GeneratorCallback[] = []; - const rootProject = options.projectRoot === '.' || options.projectRoot === ''; - const lintTask = await lintProjectGenerator(tree, { - linter: Linter.EsLint, - project: options.projectName, - tsConfigPaths: [ - joinPathFragments(options.projectRoot, 'tsconfig.app.json'), - ], - unitTestRunner: options.unitTestRunner, - eslintFilePatterns: [ - mapLintPattern(options.projectRoot, 'ts', rootProject), - mapLintPattern(options.projectRoot, 'html', rootProject), - ], - setParserOptionsProject: options.setParserOptionsProject, - skipFormat: true, - rootProject: rootProject, - }); - tasks.push(lintTask); - - updateJson( - tree, - joinPathFragments(options.projectRoot, '.eslintrc.json'), - (json) => extendAngularEslintJson(json, options) - ); - - if (!options.skipPackageJson) { - const installTask = await addAngularEsLintDependencies(tree); - tasks.push(installTask); - } - - return runTasksInSerial(...tasks); -} - -export default addLintingGenerator; diff --git a/packages/angular/src/generators/add-linting/angular-v14/lib/add-angular-eslint-dependencies.ts b/packages/angular/src/generators/add-linting/angular-v14/lib/add-angular-eslint-dependencies.ts deleted file mode 100644 index c08f4f4eeec46..0000000000000 --- a/packages/angular/src/generators/add-linting/angular-v14/lib/add-angular-eslint-dependencies.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { GeneratorCallback, Tree } from '@nrwl/devkit'; -import { addDependenciesToPackageJson } from '@nrwl/devkit'; -import { backwardCompatibleVersions } from '../../../../utils/backward-compatible-versions'; - -export function addAngularEsLintDependencies(tree: Tree): GeneratorCallback { - return addDependenciesToPackageJson( - tree, - {}, - { - '@angular-eslint/eslint-plugin': - backwardCompatibleVersions.angularV14.angularEslintVersion, - '@angular-eslint/eslint-plugin-template': - backwardCompatibleVersions.angularV14.angularEslintVersion, - '@angular-eslint/template-parser': - backwardCompatibleVersions.angularV14.angularEslintVersion, - } - ); -} diff --git a/packages/angular/src/generators/add-linting/angular-v14/lib/add-project-lint-target.ts b/packages/angular/src/generators/add-linting/angular-v14/lib/add-project-lint-target.ts deleted file mode 100644 index d8efac04b5dbd..0000000000000 --- a/packages/angular/src/generators/add-linting/angular-v14/lib/add-project-lint-target.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { Tree } from '@nrwl/devkit'; -import { - readProjectConfiguration, - updateProjectConfiguration, -} from '@nrwl/devkit'; -import { mapLintPattern } from '@nrwl/linter/src/generators/lint-project/lint-project'; -import type { AddLintingGeneratorSchema } from '../schema'; - -export function addProjectLintTarget( - tree: Tree, - options: AddLintingGeneratorSchema -): void { - const project = readProjectConfiguration(tree, options.projectName); - const rootProject = options.projectRoot === '.' || options.projectRoot === ''; - project.targets.lint = { - executor: '@nrwl/linter:eslint', - options: { - lintFilePatterns: [ - mapLintPattern(options.projectRoot, 'ts', rootProject), - mapLintPattern(options.projectRoot, 'html', rootProject), - ], - }, - }; - updateProjectConfiguration(tree, options.projectName, project); -} diff --git a/packages/angular/src/generators/add-linting/angular-v14/lib/create-eslint-configuration.ts b/packages/angular/src/generators/add-linting/angular-v14/lib/create-eslint-configuration.ts deleted file mode 100644 index 786a2f3efe73d..0000000000000 --- a/packages/angular/src/generators/add-linting/angular-v14/lib/create-eslint-configuration.ts +++ /dev/null @@ -1,135 +0,0 @@ -import type { Tree } from '@nrwl/devkit'; -import { joinPathFragments, offsetFromRoot, writeJson } from '@nrwl/devkit'; -import type { Linter } from 'eslint'; -import type { AddLintingGeneratorSchema } from '../schema'; -import { camelize, dasherize } from '@nrwl/devkit/src/utils/string-utils'; - -type EslintExtensionSchema = { - prefix: string; -}; - -export const extendAngularEslintJson = ( - json: Linter.Config, - options: EslintExtensionSchema -) => { - const overrides = [ - { - ...json.overrides[0], - files: ['*.ts'], - extends: [ - ...(json.overrides[0].extends || []), - 'plugin:@nrwl/nx/angular', - 'plugin:@angular-eslint/template/process-inline-templates', - ], - rules: { - '@angular-eslint/directive-selector': [ - 'error', - { - type: 'attribute', - prefix: camelize(options.prefix), - style: 'camelCase', - }, - ], - '@angular-eslint/component-selector': [ - 'error', - { - type: 'element', - prefix: dasherize(options.prefix), - style: 'kebab-case', - }, - ], - }, - }, - { - files: ['*.html'], - extends: ['plugin:@nrwl/nx/angular-template'], - /** - * Having an empty rules object present makes it more obvious to the user where they would - * extend things from if they needed to - */ - rules: {}, - }, - ]; - - return { - ...json, - overrides, - }; -}; - -/** - * @deprecated Use {@link extendAngularEslintJson} instead - */ -export function createEsLintConfiguration( - tree: Tree, - options: AddLintingGeneratorSchema -): void { - const rootConfig = `${offsetFromRoot(options.projectRoot)}.eslintrc.json`; - // Include all project files to be linted (since they are turned off in the root eslintrc file). - const ignorePatterns = ['!**/*']; - - const configJson = { - extends: [rootConfig], - ignorePatterns, - overrides: [ - { - files: ['*.ts'], - extends: [ - 'plugin:@nrwl/nx/angular', - 'plugin:@angular-eslint/template/process-inline-templates', - ], - /** - * NOTE: We no longer set parserOptions.project by default when creating new projects. - * - * We have observed that users rarely add rules requiring type-checking to their Nx workspaces, and therefore - * do not actually need the capabilites which parserOptions.project provides. When specifying parserOptions.project, - * typescript-eslint needs to create full TypeScript Programs for you. When omitting it, it can perform a simple - * parse (and AST tranformation) of the source files it encounters during a lint run, which is much faster and much - * less memory intensive. - * - * In the rare case that users attempt to add rules requiring type-checking to their setup later on (and haven't set - * parserOptions.project), the executor will attempt to look for the particular error typescript-eslint gives you - * and provide feedback to the user. - */ - parserOptions: !options.setParserOptionsProject - ? undefined - : { - project: [`${options.projectRoot}/tsconfig.*?.json`], - }, - rules: { - '@angular-eslint/directive-selector': [ - 'error', - { - type: 'attribute', - prefix: camelize(options.prefix), - style: 'camelCase', - }, - ], - '@angular-eslint/component-selector': [ - 'error', - { - type: 'element', - prefix: dasherize(options.prefix), - style: 'kebab-case', - }, - ], - }, - }, - { - files: ['*.html'], - extends: ['plugin:@nrwl/nx/angular-template'], - /** - * Having an empty rules object present makes it more obvious to the user where they would - * extend things from if they needed to - */ - rules: {}, - }, - ], - }; - - writeJson( - tree, - joinPathFragments(options.projectRoot, '.eslintrc.json'), - configJson - ); -} diff --git a/packages/angular/src/generators/add-linting/angular-v14/schema.d.ts b/packages/angular/src/generators/add-linting/angular-v14/schema.d.ts deleted file mode 100644 index c471cf65b15c6..0000000000000 --- a/packages/angular/src/generators/add-linting/angular-v14/schema.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface AddLintingGeneratorSchema { - projectName: string; - projectRoot: string; - prefix: string; - setParserOptionsProject?: boolean; - skipFormat?: boolean; - skipPackageJson?: boolean; - unitTestRunner?: string; -} diff --git a/packages/angular/src/generators/add-linting/lib/add-angular-eslint-dependencies.ts b/packages/angular/src/generators/add-linting/lib/add-angular-eslint-dependencies.ts index b2393bec98a6d..7aa32c50fe2df 100644 --- a/packages/angular/src/generators/add-linting/lib/add-angular-eslint-dependencies.ts +++ b/packages/angular/src/generators/add-linting/lib/add-angular-eslint-dependencies.ts @@ -1,15 +1,22 @@ import type { GeneratorCallback, Tree } from '@nrwl/devkit'; import { addDependenciesToPackageJson } from '@nrwl/devkit'; import { angularEslintVersion } from '../../../utils/versions'; +import { getInstalledAngularVersionInfo } from '../../utils/version-utils'; +import { backwardCompatibleVersions } from '../../../utils/backward-compatible-versions'; export function addAngularEsLintDependencies(tree: Tree): GeneratorCallback { + const installedAngularVersionInfo = getInstalledAngularVersionInfo(tree); + const angularEslintVersionToInstall = + installedAngularVersionInfo.major === 14 + ? backwardCompatibleVersions.angularV14.angularEslintVersion + : angularEslintVersion; return addDependenciesToPackageJson( tree, {}, { - '@angular-eslint/eslint-plugin': angularEslintVersion, - '@angular-eslint/eslint-plugin-template': angularEslintVersion, - '@angular-eslint/template-parser': angularEslintVersion, + '@angular-eslint/eslint-plugin': angularEslintVersionToInstall, + '@angular-eslint/eslint-plugin-template': angularEslintVersionToInstall, + '@angular-eslint/template-parser': angularEslintVersionToInstall, } ); } diff --git a/packages/angular/src/generators/add-linting/lib/add-project-lint-target.ts b/packages/angular/src/generators/add-linting/lib/add-project-lint-target.ts deleted file mode 100644 index d8efac04b5dbd..0000000000000 --- a/packages/angular/src/generators/add-linting/lib/add-project-lint-target.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { Tree } from '@nrwl/devkit'; -import { - readProjectConfiguration, - updateProjectConfiguration, -} from '@nrwl/devkit'; -import { mapLintPattern } from '@nrwl/linter/src/generators/lint-project/lint-project'; -import type { AddLintingGeneratorSchema } from '../schema'; - -export function addProjectLintTarget( - tree: Tree, - options: AddLintingGeneratorSchema -): void { - const project = readProjectConfiguration(tree, options.projectName); - const rootProject = options.projectRoot === '.' || options.projectRoot === ''; - project.targets.lint = { - executor: '@nrwl/linter:eslint', - options: { - lintFilePatterns: [ - mapLintPattern(options.projectRoot, 'ts', rootProject), - mapLintPattern(options.projectRoot, 'html', rootProject), - ], - }, - }; - updateProjectConfiguration(tree, options.projectName, project); -} From 939f2be750f242c5a79cab07f3a21bed7c3d84a6 Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Wed, 22 Mar 2023 14:37:00 +0000 Subject: [PATCH 18/76] cleanup(angular): remove duplicated v14 code for application (#15821) --- .../application/angular-v14/application.ts | 135 --- .../files/src/app/app.routes.ts__tpl__ | 3 - .../angular-v14/files/src/favicon.ico | Bin 15086 -> 0 bytes .../files/tsconfig.editor.json__tpl__ | 5 - .../application/angular-v14/lib/add-e2e.ts | 22 - .../angular-v14/lib/add-linting.ts | 19 - .../angular-v14/lib/add-proxy-config.ts | 33 - .../angular-v14/lib/add-unit-test-runner.ts | 19 - .../lib/convert-to-standalone-app.ts | 133 --- .../angular-v14/lib/create-files.ts | 21 - .../lib/enable-strict-type-checking.ts | 46 - .../application/angular-v14/lib/index.ts | 19 - .../angular-v14/lib/normalize-options.ts | 77 -- .../angular-v14/lib/normalized-schema.ts | 16 - .../angular-v14/lib/nrwl-home-tpl.ts | 819 ------------------ .../angular-v14/lib/remove-scaffolded-e2e.ts | 31 - .../angular-v14/lib/root-router-config.ts | 46 - .../angular-v14/lib/set-app-strict-default.ts | 15 - .../lib/update-app-component-template.ts | 51 -- .../angular-v14/lib/update-component-spec.ts | 72 -- .../angular-v14/lib/update-config-files.ts | 135 --- .../angular-v14/lib/update-e2e-project.ts | 66 -- .../angular-v14/lib/update-editor-tsconfig.ts | 53 -- .../lib/update-nx-component-template.ts | 63 -- .../application/angular-v14/schema.d.ts | 30 - .../src/generators/application/application.ts | 15 +- .../files/base/tsconfig.app.json__tpl__ | 2 +- .../files/base/tsconfig.json__tpl__ | 4 +- .../files/ng-module/src/main.ts__tpl__ | 10 +- .../files/v14/.browserlistrc__tpl__ | 16 + .../environments/environment.prod.ts__tpl__ | 3 + .../src/environments/environment.ts__tpl__ | 16 + .../files/v14/src/polyfills.ts__tpl__ | 53 ++ .../application/lib/create-files.ts | 89 +- .../application/lib/create-project.ts | 16 +- 35 files changed, 148 insertions(+), 2005 deletions(-) delete mode 100644 packages/angular/src/generators/application/angular-v14/application.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/files/src/app/app.routes.ts__tpl__ delete mode 100644 packages/angular/src/generators/application/angular-v14/files/src/favicon.ico delete mode 100644 packages/angular/src/generators/application/angular-v14/files/tsconfig.editor.json__tpl__ delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/add-e2e.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/add-linting.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/add-proxy-config.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/add-unit-test-runner.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/convert-to-standalone-app.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/create-files.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/enable-strict-type-checking.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/index.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/normalize-options.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/normalized-schema.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/nrwl-home-tpl.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/remove-scaffolded-e2e.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/root-router-config.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/set-app-strict-default.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/update-app-component-template.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/update-component-spec.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/update-config-files.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/update-e2e-project.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/update-editor-tsconfig.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/lib/update-nx-component-template.ts delete mode 100644 packages/angular/src/generators/application/angular-v14/schema.d.ts create mode 100644 packages/angular/src/generators/application/files/v14/.browserlistrc__tpl__ create mode 100644 packages/angular/src/generators/application/files/v14/src/environments/environment.prod.ts__tpl__ create mode 100644 packages/angular/src/generators/application/files/v14/src/environments/environment.ts__tpl__ create mode 100644 packages/angular/src/generators/application/files/v14/src/polyfills.ts__tpl__ diff --git a/packages/angular/src/generators/application/angular-v14/application.ts b/packages/angular/src/generators/application/angular-v14/application.ts deleted file mode 100644 index fc6074df4c8dd..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/application.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { - formatFiles, - installPackagesTask, - moveFilesToNewDirectory, - Tree, -} from '@nrwl/devkit'; -import { UnitTestRunner } from '../../../utils/test-runners'; -import { angularInitGenerator } from '../../init/init'; -import { setupTailwindGenerator } from '../../setup-tailwind/setup-tailwind'; -import { - addE2e, - addLinting, - addProxyConfig, - addRouterRootConfiguration, - addUnitTestRunner, - convertToStandaloneApp, - createFiles, - enableStrictTypeChecking, - normalizeOptions, - setApplicationStrictDefault, - updateAppComponentTemplate, - updateComponentSpec, - updateConfigFiles, - updateEditorTsConfig, - updateNxComponentTemplate, -} from './lib'; -import type { Schema } from './schema'; - -export async function applicationGenerator( - host: Tree, - schema: Partial -) { - const options = normalizeOptions(host, schema); - - await angularInitGenerator(host, { - ...options, - skipFormat: true, - }); - - const { wrapAngularDevkitSchematic } = require('@nrwl/devkit/ngcli-adapter'); - const angularAppSchematic = wrapAngularDevkitSchematic( - '@schematics/angular', - 'application' - ); - await angularAppSchematic(host, { - name: options.name, - inlineStyle: options.inlineStyle, - inlineTemplate: options.inlineTemplate, - prefix: options.prefix, - skipTests: options.skipTests, - style: options.style, - viewEncapsulation: options.viewEncapsulation, - routing: false, - skipInstall: true, - skipPackageJson: options.skipPackageJson, - }); - - if (options.ngCliSchematicAppRoot !== options.appProjectRoot) { - moveFilesToNewDirectory( - host, - options.ngCliSchematicAppRoot, - options.appProjectRoot - ); - } - - createFiles(host, options); - updateConfigFiles(host, options); - updateAppComponentTemplate(host, options); - - if (!options.minimal) { - // Create the NxWelcomeComponent - const angularComponentSchematic = wrapAngularDevkitSchematic( - '@schematics/angular', - 'component' - ); - await angularComponentSchematic(host, { - name: 'NxWelcome', - inlineTemplate: true, - inlineStyle: true, - prefix: options.prefix, - skipTests: true, - style: options.style, - flat: true, - viewEncapsulation: 'None', - project: options.name, - standalone: options.standalone, - }); - updateNxComponentTemplate(host, options); - } - - if (options.addTailwind) { - await setupTailwindGenerator(host, { - project: options.name, - skipFormat: true, - skipPackageJson: options.skipPackageJson, - }); - } - - if (options.unitTestRunner !== UnitTestRunner.None) { - updateComponentSpec(host, options); - } - - if (options.routing) { - addRouterRootConfiguration(host, options); - } - - await addLinting(host, options); - await addUnitTestRunner(host, options); - await addE2e(host, options); - updateEditorTsConfig(host, options); - - if (options.backendProject) { - addProxyConfig(host, options); - } - - if (options.strict) { - enableStrictTypeChecking(host, options); - } else { - setApplicationStrictDefault(host, false); - } - - if (options.standalone) { - convertToStandaloneApp(host, options); - } - - if (!options.skipFormat) { - await formatFiles(host); - } - - return () => { - installPackagesTask(host); - }; -} - -export default applicationGenerator; diff --git a/packages/angular/src/generators/application/angular-v14/files/src/app/app.routes.ts__tpl__ b/packages/angular/src/generators/application/angular-v14/files/src/app/app.routes.ts__tpl__ deleted file mode 100644 index 5ef938d9c4b81..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/files/src/app/app.routes.ts__tpl__ +++ /dev/null @@ -1,3 +0,0 @@ -import { Route } from '@angular/router'; - -export const appRoutes: Route[] = []; \ No newline at end of file diff --git a/packages/angular/src/generators/application/angular-v14/files/src/favicon.ico b/packages/angular/src/generators/application/angular-v14/files/src/favicon.ico deleted file mode 100644 index 317ebcb2336e0833a22dddf0ab287849f26fda57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 zcmeI332;U^%p|z7g|#(P)qFEA@4f!_@qOK2 z_lJl}!lhL!VT_U|uN7%8B2iKH??xhDa;*`g{yjTFWHvXn;2s{4R7kH|pKGdy(7z!K zgftM+Ku7~24TLlh(!g)gz|foI94G^t2^IO$uvX$3(OR0<_5L2sB)lMAMy|+`xodJ{ z_Uh_1m)~h?a;2W{dmhM;u!YGo=)OdmId_B<%^V^{ovI@y`7^g1_V9G}*f# zNzAtvou}I!W1#{M^@ROc(BZ! z+F!!_aR&Px3_reO(EW+TwlW~tv*2zr?iP7(d~a~yA|@*a89IUke+c472NXM0wiX{- zl`UrZC^1XYyf%1u)-Y)jj9;MZ!SLfd2Hl?o|80Su%Z?To_=^g_Jt0oa#CT*tjx>BI z16wec&AOWNK<#i0Qd=1O$fymLRoUR*%;h@*@v7}wApDl^w*h}!sYq%kw+DKDY)@&A z@9$ULEB3qkR#85`lb8#WZw=@})#kQig9oqy^I$dj&k4jU&^2(M3q{n1AKeGUKPFbr z1^<)aH;VsG@J|B&l>UtU#Ejv3GIqERzYgL@UOAWtW<{p#zy`WyJgpCy8$c_e%wYJL zyGHRRx38)HyjU3y{-4z6)pzb>&Q1pR)B&u01F-|&Gx4EZWK$nkUkOI|(D4UHOXg_- zw{OBf!oWQUn)Pe(=f=nt=zkmdjpO^o8ZZ9o_|4tW1ni+Un9iCW47*-ut$KQOww!;u z`0q)$s6IZO!~9$e_P9X!hqLxu`fpcL|2f^I5d4*a@Dq28;@2271v_N+5HqYZ>x;&O z05*7JT)mUe&%S0@UD)@&8SmQrMtsDfZT;fkdA!r(S=}Oz>iP)w=W508=Rc#nNn7ym z1;42c|8($ALY8#a({%1#IXbWn9-Y|0eDY$_L&j{63?{?AH{);EzcqfydD$@-B`Y3<%IIj7S7rK_N}je^=dEk%JQ4c z!tBdTPE3Tse;oYF>cnrapWq*o)m47X1`~6@(!Y29#>-#8zm&LXrXa(3=7Z)ElaQqj z-#0JJy3Fi(C#Rx(`=VXtJ63E2_bZGCz+QRa{W0e2(m3sI?LOcUBx)~^YCqZ{XEPX)C>G>U4tfqeH8L(3|pQR*zbL1 zT9e~4Tb5p9_G}$y4t`i*4t_Mr9QYvL9C&Ah*}t`q*}S+VYh0M6GxTTSXI)hMpMpIq zD1ImYqJLzbj0}~EpE-aH#VCH_udYEW#`P2zYmi&xSPs_{n6tBj=MY|-XrA;SGA_>y zGtU$?HXm$gYj*!N)_nQ59%lQdXtQZS3*#PC-{iB_sm+ytD*7j`D*k(P&IH2GHT}Eh z5697eQECVIGQAUe#eU2I!yI&%0CP#>%6MWV z@zS!p@+Y1i1b^QuuEF*13CuB zu69dve5k7&Wgb+^s|UB08Dr3u`h@yM0NTj4h7MnHo-4@xmyr7(*4$rpPwsCDZ@2be zRz9V^GnV;;?^Lk%ynzq&K(Aix`mWmW`^152Hoy$CTYVehpD-S1-W^#k#{0^L`V6CN+E z!w+xte;2vu4AmVNEFUOBmrBL>6MK@!O2*N|2=d|Y;oN&A&qv=qKn73lDD zI(+oJAdgv>Yr}8(&@ZuAZE%XUXmX(U!N+Z_sjL<1vjy1R+1IeHt`79fnYdOL{$ci7 z%3f0A*;Zt@ED&Gjm|OFTYBDe%bbo*xXAQsFz+Q`fVBH!N2)kaxN8P$c>sp~QXnv>b zwq=W3&Mtmih7xkR$YA)1Yi?avHNR6C99!u6fh=cL|KQ&PwF!n@ud^n(HNIImHD!h87!i*t?G|p0o+eelJ?B@A64_9%SBhNaJ64EvKgD&%LjLCYnNfc; znj?%*p@*?dq#NqcQFmmX($wms@CSAr9#>hUR^=I+=0B)vvGX%T&#h$kmX*s=^M2E!@N9#m?LhMvz}YB+kd zG~mbP|D(;{s_#;hsKK9lbVK&Lo734x7SIFJ9V_}2$@q?zm^7?*XH94w5Qae{7zOMUF z^?%F%)c1Y)Q?Iy?I>knw*8gYW#ok|2gdS=YYZLiD=CW|Nj;n^x!=S#iJ#`~Ld79+xXpVmUK^B(xO_vO!btA9y7w3L3-0j-y4 z?M-V{%z;JI`bk7yFDcP}OcCd*{Q9S5$iGA7*E1@tfkyjAi!;wP^O71cZ^Ep)qrQ)N z#wqw0_HS;T7x3y|`P==i3hEwK%|>fZ)c&@kgKO1~5<5xBSk?iZV?KI6&i72H6S9A* z=U(*e)EqEs?Oc04)V-~K5AUmh|62H4*`UAtItO$O(q5?6jj+K^oD!04r=6#dsxp?~}{`?&sXn#q2 zGuY~7>O2=!u@@Kfu7q=W*4egu@qPMRM>(eyYyaIE<|j%d=iWNdGsx%c!902v#ngNg z@#U-O_4xN$s_9?(`{>{>7~-6FgWpBpqXb`Ydc3OFL#&I}Irse9F_8R@4zSS*Y*o*B zXL?6*Aw!AfkNCgcr#*yj&p3ZDe2y>v$>FUdKIy_2N~}6AbHc7gA3`6$g@1o|dE>vz z4pl(j9;kyMsjaw}lO?(?Xg%4k!5%^t#@5n=WVc&JRa+XT$~#@rldvN3S1rEpU$;XgxVny7mki3 z-Hh|jUCHrUXuLr!)`w>wgO0N%KTB-1di>cj(x3Bav`7v z3G7EIbU$z>`Nad7Rk_&OT-W{;qg)-GXV-aJT#(ozdmnA~Rq3GQ_3mby(>q6Ocb-RgTUhTN)))x>m&eD;$J5Bg zo&DhY36Yg=J=$Z>t}RJ>o|@hAcwWzN#r(WJ52^g$lh^!63@hh+dR$&_dEGu&^CR*< z!oFqSqO@>xZ*nC2oiOd0eS*F^IL~W-rsrO`J`ej{=ou_q^_(<$&-3f^J z&L^MSYWIe{&pYq&9eGaArA~*kA ({ - [`/${options.backendProject}`]: { - target: 'http://localhost:3333', - secure: false, - }, - })); - - projectConfig.targets.serve.options = { - ...projectConfig.targets.serve.options, - proxyConfig: pathToProxyFile, - }; - updateProjectConfiguration(host, options.name, projectConfig); - } -} diff --git a/packages/angular/src/generators/application/angular-v14/lib/add-unit-test-runner.ts b/packages/angular/src/generators/application/angular-v14/lib/add-unit-test-runner.ts deleted file mode 100644 index 95a8ab50b4cef..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/add-unit-test-runner.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { Tree } from '@nrwl/devkit'; -import type { NormalizedSchema } from './normalized-schema'; - -import { jestProjectGenerator } from '@nrwl/jest'; - -import { UnitTestRunner } from '../../../../utils/test-runners'; - -export async function addUnitTestRunner(host: Tree, options: NormalizedSchema) { - if (options.unitTestRunner === UnitTestRunner.Jest) { - await jestProjectGenerator(host, { - project: options.name, - setupFile: 'angular', - supportTsx: false, - skipSerializers: false, - skipPackageJson: options.skipPackageJson, - rootProject: options.rootProject, - }); - } -} diff --git a/packages/angular/src/generators/application/angular-v14/lib/convert-to-standalone-app.ts b/packages/angular/src/generators/application/angular-v14/lib/convert-to-standalone-app.ts deleted file mode 100644 index 286428808910b..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/convert-to-standalone-app.ts +++ /dev/null @@ -1,133 +0,0 @@ -import type { Tree } from '@nrwl/devkit'; -import { joinPathFragments } from '@nrwl/devkit'; -import type { NormalizedSchema } from './normalized-schema'; -import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript'; - -export function convertToStandaloneApp(tree: Tree, options: NormalizedSchema) { - const pathToAppModule = joinPathFragments( - options.appProjectRoot, - 'src/app/app.module.ts' - ); - updateMainEntrypoint(options, tree, pathToAppModule); - updateAppComponent(tree, options); - if (!options.skipTests) { - updateAppComponentSpec(tree, options); - } - - tree.delete(pathToAppModule); -} - -function updateMainEntrypoint( - options: NormalizedSchema, - tree: Tree, - pathToAppModule: string -) { - let routerModuleSetup: string; - if (options.routing) { - ensureTypescript(); - const { tsquery } = require('@phenomnomnominal/tsquery'); - const appModuleContents = tree.read(pathToAppModule, 'utf-8'); - const ast = tsquery.ast(appModuleContents); - - const ROUTER_MODULE_SELECTOR = - 'PropertyAssignment:has(Identifier[name=imports]) CallExpression:has(PropertyAccessExpression > Identifier[name=RouterModule])'; - const nodes = tsquery(ast, ROUTER_MODULE_SELECTOR, { - visitAllChildren: true, - }); - if (nodes.length > 0) { - routerModuleSetup = nodes[0].getText(); - } - } - - tree.write( - joinPathFragments(options.appProjectRoot, 'src/main.ts'), - standaloneComponentMainContents(routerModuleSetup) - ); -} - -const standaloneComponentMainContents = ( - routerModuleSetup -) => `import { enableProdMode } from '@angular/core'; -import { bootstrapApplication } from '@angular/platform-browser';${ - routerModuleSetup - ? ` -import { provideRouter, withEnabledBlockingInitialNavigation } from '@angular/router'` - : `` -}; -import { AppComponent } from './app/app.component'; -import { environment } from './environments/environment'; -${routerModuleSetup ? `import { appRoutes } from './app/app.routes';` : ''} -if (environment.production) { - enableProdMode(); -} -bootstrapApplication(AppComponent${ - routerModuleSetup - ? `, { - providers: [provideRouter(appRoutes, withEnabledBlockingInitialNavigation())], -}` - : '' -}).catch((err) => console.error(err));`; - -function updateAppComponent(tree: Tree, options: NormalizedSchema) { - const pathToAppComponent = joinPathFragments( - options.appProjectRoot, - 'src/app/app.component.ts' - ); - const appComponentContents = tree.read(pathToAppComponent, 'utf-8'); - - ensureTypescript(); - const { tsquery } = require('@phenomnomnominal/tsquery'); - const ast = tsquery.ast(appComponentContents); - const COMPONENT_DECORATOR_SELECTOR = - 'Decorator > CallExpression:has(Identifier[name=Component]) ObjectLiteralExpression'; - const nodes = tsquery(ast, COMPONENT_DECORATOR_SELECTOR, { - visitAllChildren: true, - }); - - if (nodes.length === 0) { - throw new Error( - 'Could not find Component decorator within app.component.ts for standalone app generation.' - ); - } - - const startPos = nodes[0].getStart() + 1; - - const newAppComponentContents = `import { NxWelcomeComponent } from './nx-welcome.component';${ - options.routing - ? ` -import { RouterModule } from '@angular/router';` - : '' - } -${appComponentContents.slice(0, startPos)} - standalone: true, - imports: [NxWelcomeComponent${ - options.routing ? ', RouterModule' : '' - }],${appComponentContents.slice(startPos, -1)}`; - - tree.write(pathToAppComponent, newAppComponentContents); -} - -function updateAppComponentSpec(tree: Tree, options: NormalizedSchema) { - const pathToAppComponentSpec = joinPathFragments( - options.appProjectRoot, - 'src/app/app.component.spec.ts' - ); - const appComponentSpecContents = tree.read(pathToAppComponentSpec, 'utf-8'); - - let newAppComponentSpecContents: string; - if (!options.routing) { - newAppComponentSpecContents = appComponentSpecContents.replace( - 'declarations', - 'imports' - ); - } else { - newAppComponentSpecContents = appComponentSpecContents - .replace( - 'imports: [RouterTestingModule],', - 'imports: [AppComponent, NxWelcomeComponent, RouterTestingModule]' - ) - .replace('declarations: [AppComponent, NxWelcomeComponent]', ''); - } - - tree.write(pathToAppComponentSpec, newAppComponentSpecContents); -} diff --git a/packages/angular/src/generators/application/angular-v14/lib/create-files.ts b/packages/angular/src/generators/application/angular-v14/lib/create-files.ts deleted file mode 100644 index 459e1b80df97d..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/create-files.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { Tree } from '@nrwl/devkit'; -import { generateFiles, joinPathFragments } from '@nrwl/devkit'; -import type { NormalizedSchema } from './normalized-schema'; - -export function createFiles(tree: Tree, options: NormalizedSchema) { - generateFiles( - tree, - joinPathFragments(__dirname, '../files'), - options.appProjectRoot, - { - ...options, - tpl: '', - } - ); - - if (!options.routing) { - tree.delete( - joinPathFragments(options.appProjectRoot, 'src/app/app.routes.ts') - ); - } -} diff --git a/packages/angular/src/generators/application/angular-v14/lib/enable-strict-type-checking.ts b/packages/angular/src/generators/application/angular-v14/lib/enable-strict-type-checking.ts deleted file mode 100644 index 9b6b64c245e67..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/enable-strict-type-checking.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { Tree } from '@nrwl/devkit'; -import type { NormalizedSchema } from './normalized-schema'; - -import { updateJson } from '@nrwl/devkit'; - -export function enableStrictTypeChecking( - host: Tree, - options: NormalizedSchema -) { - const configFiles = [ - `${options.appProjectRoot}/tsconfig.json`, - `${options.e2eProjectRoot}/tsconfig.json`, - ]; - - for (const configFile of configFiles) { - if (!host.exists(configFile)) { - continue; - } - - // Update the settings in the tsconfig.app.json to enable strict type checking. - // This matches the settings defined by the Angular CLI https://angular.io/guide/strict-mode - updateJson(host, configFile, (json) => { - // update the TypeScript settings - json.compilerOptions = { - ...(json.compilerOptions ?? {}), - forceConsistentCasingInFileNames: true, - strict: true, - noImplicitOverride: true, - noPropertyAccessFromIndexSignature: true, - noImplicitReturns: true, - noFallthroughCasesInSwitch: true, - }; - - // update Angular Template Settings - json.angularCompilerOptions = { - ...(json.angularCompilerOptions ?? {}), - enableI18nLegacyMessageIdFormat: false, - strictInjectionParameters: true, - strictInputAccessModifiers: true, - strictTemplates: true, - }; - - return json; - }); - } -} diff --git a/packages/angular/src/generators/application/angular-v14/lib/index.ts b/packages/angular/src/generators/application/angular-v14/lib/index.ts deleted file mode 100644 index 241220c850bfb..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -export * from './add-e2e'; -export * from './add-linting'; -export * from './add-proxy-config'; -export * from './add-unit-test-runner'; -export * from './create-files'; -export * from './enable-strict-type-checking'; -export * from './normalize-options'; -export * from './normalized-schema'; -export * from './nrwl-home-tpl'; -export * from './remove-scaffolded-e2e'; -export * from './root-router-config'; -export * from './set-app-strict-default'; -export * from './update-component-spec'; -export * from './update-app-component-template'; -export * from './update-nx-component-template'; -export * from './update-config-files'; -export * from './update-e2e-project'; -export * from './update-editor-tsconfig'; -export * from './convert-to-standalone-app'; diff --git a/packages/angular/src/generators/application/angular-v14/lib/normalize-options.ts b/packages/angular/src/generators/application/angular-v14/lib/normalize-options.ts deleted file mode 100644 index 8ff239908cbe5..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/normalize-options.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { - extractLayoutDirectory, - getWorkspaceLayout, - joinPathFragments, - names, - Tree, -} from '@nrwl/devkit'; -import type { Schema } from '../schema'; -import type { NormalizedSchema } from './normalized-schema'; -import { E2eTestRunner, UnitTestRunner } from '../../../../utils/test-runners'; -import { Linter } from '@nrwl/linter'; -import { - normalizeDirectory, - normalizePrefix, - normalizeProjectName, -} from '../../../utils/project'; - -export function normalizeOptions( - host: Tree, - options: Partial -): NormalizedSchema { - const { layoutDirectory, projectDirectory } = extractLayoutDirectory( - options.directory - ); - const appDirectory = normalizeDirectory(options.name, projectDirectory); - const appProjectName = normalizeProjectName(options.name, projectDirectory); - const e2eProjectName = options.rootProject - ? 'e2e' - : `${names(options.name).fileName}-e2e`; - - const { - appsDir: defaultAppsDir, - npmScope, - standaloneAsDefault, - } = getWorkspaceLayout(host); - const appsDir = layoutDirectory ?? defaultAppsDir; - const appProjectRoot = options.rootProject - ? '.' - : joinPathFragments(appsDir, appDirectory); - const e2eProjectRoot = options.rootProject - ? 'e2e' - : joinPathFragments(appsDir, `${appDirectory}-e2e`); - - const parsedTags = options.tags - ? options.tags.split(',').map((s) => s.trim()) - : []; - - const prefix = normalizePrefix(options.prefix, npmScope); - - options.standaloneConfig = options.standaloneConfig ?? standaloneAsDefault; - - const ngCliSchematicAppRoot = appProjectName; - const ngCliSchematicE2ERoot = `${appProjectName}/e2e`; - - // Set defaults and then overwrite with user options - return { - style: 'css', - routing: false, - inlineStyle: false, - inlineTemplate: false, - skipTests: options.unitTestRunner === UnitTestRunner.None, - skipFormat: false, - unitTestRunner: UnitTestRunner.Jest, - e2eTestRunner: E2eTestRunner.Cypress, - linter: Linter.EsLint, - strict: true, - ...options, - prefix, - name: appProjectName, - appProjectRoot, - e2eProjectRoot, - e2eProjectName, - parsedTags, - ngCliSchematicAppRoot, - ngCliSchematicE2ERoot, - }; -} diff --git a/packages/angular/src/generators/application/angular-v14/lib/normalized-schema.ts b/packages/angular/src/generators/application/angular-v14/lib/normalized-schema.ts deleted file mode 100644 index a13f600975a56..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/normalized-schema.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { E2eTestRunner, UnitTestRunner } from '../../../../utils/test-runners'; -import type { Schema } from '../schema'; -import { Linter } from '@nrwl/linter'; - -export interface NormalizedSchema extends Schema { - linter: Linter; - unitTestRunner: UnitTestRunner; - e2eTestRunner: E2eTestRunner; - prefix: string; - appProjectRoot: string; - e2eProjectName: string; - e2eProjectRoot: string; - parsedTags: string[]; - ngCliSchematicAppRoot: string; - ngCliSchematicE2ERoot: string; -} diff --git a/packages/angular/src/generators/application/angular-v14/lib/nrwl-home-tpl.ts b/packages/angular/src/generators/application/angular-v14/lib/nrwl-home-tpl.ts deleted file mode 100644 index fcc14b704fbb4..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/nrwl-home-tpl.ts +++ /dev/null @@ -1,819 +0,0 @@ -export const nrwlHomeTemplate = { - getSelector: (prefix) => `<${prefix}-nx-welcome>`, - template: (title) => ` - - -
-
- -
-

- Hello there, - Welcome ${title} 👋 -

-
- - -
-
-

- - - - You're up and running -

- What's next? -
-
- - - -
-
- - - - - -
-

Next steps

-

Here are some things you can do with Nx:

-
- - - - - Add UI library - -
# Generate UI lib
-nx g @nrwl/angular:lib ui
-
-# Add a component
-nx g @nrwl/angular:component button --project ui
-
-
- - - - - View interactive project graph - -
nx graph
-
-
- - - - - Run affected commands - -
# see what's been affected by changes
-nx affected:graph
-
-# run tests for current changes
-nx affected:test
-
-# run e2e tests for current changes
-nx affected:e2e
-
-
- -

- Carefully crafted with - - - -

-
-
- `, -}; diff --git a/packages/angular/src/generators/application/angular-v14/lib/remove-scaffolded-e2e.ts b/packages/angular/src/generators/application/angular-v14/lib/remove-scaffolded-e2e.ts deleted file mode 100644 index 0dacc06817b6c..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/remove-scaffolded-e2e.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { Tree } from '@nrwl/devkit'; -import type { NormalizedSchema } from './normalized-schema'; - -import { - updateProjectConfiguration, - readProjectConfiguration, -} from '@nrwl/devkit'; - -export function removeScaffoldedE2e( - host: Tree, - { name }: NormalizedSchema, - e2eProjectRoot: string -) { - if (host.exists(`${e2eProjectRoot}/src/app.e2e-spec.ts`)) { - host.delete(`${e2eProjectRoot}/src/app.e2e-spec.ts`); - } - if (host.exists(`${e2eProjectRoot}/src/app.po.ts`)) { - host.delete(`${e2eProjectRoot}/src/app.po.ts`); - } - if (host.exists(`${e2eProjectRoot}/protractor.conf.js`)) { - host.delete(`${e2eProjectRoot}/protractor.conf.js`); - } - if (host.exists(`${e2eProjectRoot}/tsconfig.json`)) { - host.delete(`${e2eProjectRoot}/tsconfig.json`); - } - - const project = readProjectConfiguration(host, name); - delete project.targets['e2e']; - - updateProjectConfiguration(host, name, project); -} diff --git a/packages/angular/src/generators/application/angular-v14/lib/root-router-config.ts b/packages/angular/src/generators/application/angular-v14/lib/root-router-config.ts deleted file mode 100644 index 522acca3f3c9b..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/root-router-config.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { Tree } from '@nrwl/devkit'; -import { insertImport } from '@nrwl/js'; -import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript'; -import { addImportToModule } from '../../../../utils/nx-devkit/ast-utils'; -import type { NormalizedSchema } from './normalized-schema'; - -let tsModule: typeof import('typescript'); - -export function addRouterRootConfiguration( - host: Tree, - options: NormalizedSchema -) { - if (!tsModule) { - tsModule = ensureTypescript(); - } - const modulePath = `${options.appProjectRoot}/src/app/app.module.ts`; - const moduleSource = host.read(modulePath, 'utf-8'); - - let sourceFile = tsModule.createSourceFile( - modulePath, - moduleSource, - tsModule.ScriptTarget.Latest, - true - ); - - sourceFile = insertImport( - host, - sourceFile, - modulePath, - 'RouterModule', - '@angular/router' - ); - sourceFile = insertImport( - host, - sourceFile, - modulePath, - 'appRoutes', - './app.routes' - ); - sourceFile = addImportToModule( - host, - sourceFile, - modulePath, - `RouterModule.forRoot(appRoutes, {initialNavigation: 'enabledBlocking'})` - ); -} diff --git a/packages/angular/src/generators/application/angular-v14/lib/set-app-strict-default.ts b/packages/angular/src/generators/application/angular-v14/lib/set-app-strict-default.ts deleted file mode 100644 index cceed3b4bad58..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/set-app-strict-default.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Tree } from '@nrwl/devkit'; - -import { readNxJson, updateNxJson } from '@nrwl/devkit'; - -export function setApplicationStrictDefault(host: Tree, strict: boolean) { - const nxJson = readNxJson(host); - - nxJson.generators = nxJson.generators || {}; - nxJson.generators['@nrwl/angular:application'] = - nxJson.generators['@nrwl/angular:application'] || {}; - nxJson.generators['@nrwl/angular:application'].strict = - nxJson.generators['@nrwl/angular:application'].strict ?? strict; - - updateNxJson(host, nxJson); -} diff --git a/packages/angular/src/generators/application/angular-v14/lib/update-app-component-template.ts b/packages/angular/src/generators/application/angular-v14/lib/update-app-component-template.ts deleted file mode 100644 index 47f2b689a4e6c..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/update-app-component-template.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { Tree } from '@nrwl/devkit'; -import { replaceNodeValue } from '@nrwl/js'; -import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript'; -import { getDecoratorPropertyValueNode } from '../../../../utils/nx-devkit/ast-utils'; -import { nrwlHomeTemplate } from './nrwl-home-tpl'; -import type { NormalizedSchema } from './normalized-schema'; - -let tsModule: typeof import('typescript'); - -export function updateAppComponentTemplate( - host: Tree, - options: NormalizedSchema -) { - if (!tsModule) { - tsModule = ensureTypescript(); - } - const content = options.routing - ? `${nrwlHomeTemplate.getSelector( - options.prefix - )}\n` - : nrwlHomeTemplate.getSelector(options.prefix); - - if (!options.inlineTemplate) { - host.write(`${options.appProjectRoot}/src/app/app.component.html`, content); - - return; - } - - // Inline component update - const componentPath = `${options.appProjectRoot}/src/app/app.component.ts`; - const templateNodeValue = getDecoratorPropertyValueNode( - host, - componentPath, - 'Component', - 'template', - '@angular/core' - ); - - replaceNodeValue( - host, - tsModule.createSourceFile( - componentPath, - host.read(componentPath, 'utf-8'), - tsModule.ScriptTarget.Latest, - true - ), - componentPath, - templateNodeValue, - `\`\n${nrwlHomeTemplate.getSelector(options.prefix)}\n\`` - ); -} diff --git a/packages/angular/src/generators/application/angular-v14/lib/update-component-spec.ts b/packages/angular/src/generators/application/angular-v14/lib/update-component-spec.ts deleted file mode 100644 index a678fcce82c31..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/update-component-spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type { Tree } from '@nrwl/devkit'; -import { insertImport } from '@nrwl/js'; -import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript'; -import { - addImportToTestBed, - replaceIntoToTestBed, -} from '../../../../utils/nx-devkit/ast-utils'; -import type { NormalizedSchema } from './normalized-schema'; - -let tsModule: typeof import('typescript'); - -export function updateComponentSpec(host: Tree, options: NormalizedSchema) { - if (!tsModule) { - tsModule = ensureTypescript(); - } - if (options.skipTests !== true) { - const componentSpecPath = `${options.appProjectRoot}/src/app/app.component.spec.ts`; - const componentSpecSource = host.read(componentSpecPath, 'utf-8'); - - let componentSpecSourceFile = tsModule.createSourceFile( - componentSpecPath, - componentSpecSource, - tsModule.ScriptTarget.Latest, - true - ); - - host.write( - componentSpecPath, - componentSpecSource - .replace('.content span', 'h1') - .replace(`${options.name} app is running!`, `Welcome ${options.name}`) - ); - - // Adding NxWelcome component to app.component.spec - componentSpecSourceFile = insertImport( - host, - componentSpecSourceFile, - componentSpecPath, - 'NxWelcomeComponent', - './nx-welcome.component' - ); - - componentSpecSourceFile = replaceIntoToTestBed( - host, - componentSpecSourceFile, - componentSpecPath, - `declarations: [AppComponent, NxWelcomeComponent]`, - ` - declarations: [ - AppComponent - ], - ` - ); - - if (options.routing) { - componentSpecSourceFile = insertImport( - host, - componentSpecSourceFile, - componentSpecPath, - 'RouterTestingModule', - '@angular/router/testing' - ); - - componentSpecSourceFile = addImportToTestBed( - host, - componentSpecSourceFile, - componentSpecPath, - `RouterTestingModule` - ); - } - } -} diff --git a/packages/angular/src/generators/application/angular-v14/lib/update-config-files.ts b/packages/angular/src/generators/application/angular-v14/lib/update-config-files.ts deleted file mode 100644 index 2d1d8fe783c0e..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/update-config-files.ts +++ /dev/null @@ -1,135 +0,0 @@ -import type { Tree } from '@nrwl/devkit'; -import { - addProjectConfiguration, - getProjects, - joinPathFragments, - offsetFromRoot, - readProjectConfiguration, - removeProjectConfiguration, - updateJson, -} from '@nrwl/devkit'; -import { getRelativePathToRootTsConfig } from '@nrwl/js'; -import { E2eTestRunner, UnitTestRunner } from '../../../../utils/test-runners'; -import type { NormalizedSchema } from './normalized-schema'; -import { - createTsConfig, - extractTsConfigBase, -} from '../../../utils/create-ts-config'; -import { replaceAppNameWithPath } from '../../../../utils/cli-config-utils'; - -export function updateConfigFiles(host: Tree, options: NormalizedSchema) { - if (!options.rootProject) { - extractTsConfigBase(host); - } - updateTsConfigOptions(host, options); - updateAppAndE2EProjectConfigurations(host, options); -} - -function updateTsConfigOptions(host: Tree, options: NormalizedSchema) { - // tsconfig.app.json - updateJson(host, `${options.appProjectRoot}/tsconfig.app.json`, (json) => ({ - ...json, - extends: './tsconfig.json', - compilerOptions: { - ...json.compilerOptions, - outDir: `${offsetFromRoot(options.appProjectRoot)}dist/out-tsc`, - }, - exclude: [ - ...new Set([ - ...(json.exclude || []), - 'jest.config.ts', - '**/*.test.ts', - '**/*.spec.ts', - ]), - ], - })); - - createTsConfig( - host, - options.appProjectRoot, - 'app', - options, - getRelativePathToRootTsConfig(host, options.appProjectRoot) - ); -} - -function updateAppAndE2EProjectConfigurations( - host: Tree, - options: NormalizedSchema -) { - let project = readProjectConfiguration(host, options.name); - - if (options.ngCliSchematicAppRoot !== options.appProjectRoot) { - project = replaceAppNameWithPath( - project, - options.ngCliSchematicAppRoot, - options.appProjectRoot - ); - // project already has the right root, but the above function, makes it incorrect. - // This corrects it. - project.root = options.appProjectRoot; - } - - delete project.targets.test; - - // Ensure the outputs property comes after the executor for - // better readability. - const { executor, ...rest } = project.targets.build; - project.targets.build = { - executor, - outputs: ['{options.outputPath}'], - ...rest, - options: { - ...rest.options, - outputPath: joinPathFragments( - 'dist', - !options.rootProject ? options.appProjectRoot : options.name - ), - }, - }; - - if (project.generators) { - delete project.generators; - } - - if (options.port) { - project.targets.serve = { - ...project.targets.serve, - options: { - ...project.targets.serve.options, - port: options.port, - }, - }; - } - - project.tags = options.parsedTags; - - /** - * The "$schema" property on our configuration files is only added when the - * project configuration is added and not when updating it. It's done this - * way to avoid re-adding "$schema" when updating a project configuration - * and that property was intentionally removed by the devs. - * - * Since the project gets created by the Angular application schematic, - * the "$schema" property is not added, so we remove the project and add - * it back to workaround that. - */ - removeProjectConfiguration(host, options.name); - addProjectConfiguration(host, options.name, project); - - if (options.unitTestRunner === UnitTestRunner.None) { - host.delete(`${options.appProjectRoot}/src/app/app.component.spec.ts`); - host.delete(`${options.appProjectRoot}/tsconfig.spec.json`); - } - - if (options.e2eTestRunner === E2eTestRunner.None) { - const projects = getProjects(host); - if (projects.has(options.e2eProjectName)) { - removeProjectConfiguration(host, options.e2eProjectName); - } - } - - // delete some default test configs - host.delete(`${options.appProjectRoot}/karma.conf.js`); - host.delete(`${options.appProjectRoot}/src/test.ts`); -} diff --git a/packages/angular/src/generators/application/angular-v14/lib/update-e2e-project.ts b/packages/angular/src/generators/application/angular-v14/lib/update-e2e-project.ts deleted file mode 100644 index 5586c81ac1bc4..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/update-e2e-project.ts +++ /dev/null @@ -1,66 +0,0 @@ -import type { ProjectConfiguration, Tree } from '@nrwl/devkit'; -import { - addProjectConfiguration, - offsetFromRoot, - readProjectConfiguration, - updateJson, - updateProjectConfiguration, -} from '@nrwl/devkit'; -import { getRelativePathToRootTsConfig } from '@nrwl/js'; -import type { NormalizedSchema } from './normalized-schema'; - -export function updateE2eProject(tree: Tree, options: NormalizedSchema) { - const spec = `${options.e2eProjectRoot}/src/app.e2e-spec.ts`; - const content = tree.read(spec, 'utf-8'); - tree.write( - spec, - content.replace( - `${options.name} app is running!`, - `Welcome ${options.name}` - ) - ); - - const page = `${options.e2eProjectRoot}/src/app.po.ts`; - const pageContent = tree.read(page, 'utf-8'); - tree.write(page, pageContent.replace(`.content span`, `header h1`)); - - const proj = readProjectConfiguration(tree, options.name); - const project: ProjectConfiguration = { - root: options.e2eProjectRoot, - projectType: 'application', - targets: { - e2e: proj.targets.e2e, - }, - implicitDependencies: [options.name], - tags: [], - }; - project.targets.e2e.options.protractorConfig = `${options.e2eProjectRoot}/protractor.conf.js`; - addProjectConfiguration(tree, options.e2eProjectName, project); - - delete proj.targets.e2e; - updateProjectConfiguration(tree, options.name, proj); - - // update tsconfig e2e - if (!tree.exists(`${options.e2eProjectRoot}/tsconfig.e2e.json`)) { - tree.write(`${options.e2eProjectRoot}/tsconfig.e2e.json`, '{}'); - } - - updateJson(tree, `${options.e2eProjectRoot}/tsconfig.e2e.json`, (json) => { - return { - ...json, - extends: `./tsconfig.json`, - compilerOptions: { - ...json.compilerOptions, - outDir: `${offsetFromRoot(options.e2eProjectRoot)}dist/out-tsc`, - }, - }; - }); - - // update tsconfig - updateJson(tree, `${options.e2eProjectRoot}/tsconfig.json`, (json) => { - return { - ...json, - extends: getRelativePathToRootTsConfig(tree, options.e2eProjectRoot), - }; - }); -} diff --git a/packages/angular/src/generators/application/angular-v14/lib/update-editor-tsconfig.ts b/packages/angular/src/generators/application/angular-v14/lib/update-editor-tsconfig.ts deleted file mode 100644 index a7e94d33df29c..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/update-editor-tsconfig.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { Tree } from '@nrwl/devkit'; -import type { NormalizedSchema } from './normalized-schema'; - -import { joinPathFragments, readJson, updateJson } from '@nrwl/devkit'; - -interface TsConfig { - compilerOptions: { types: string[] }; -} - -function getCompilerOptionsTypes(tsConfig: TsConfig): string[] { - return tsConfig?.compilerOptions?.types ?? []; -} - -export function updateEditorTsConfig(tree: Tree, options: NormalizedSchema) { - const types = getCompilerOptionsTypes( - readJson( - tree, - joinPathFragments(options.appProjectRoot, 'tsconfig.app.json') - ) - ); - - if (options.unitTestRunner !== 'none') { - types.push( - ...getCompilerOptionsTypes( - readJson( - tree, - joinPathFragments(options.appProjectRoot, 'tsconfig.spec.json') - ) - ) - ); - } - - updateJson( - tree, - joinPathFragments(options.appProjectRoot, 'tsconfig.editor.json'), - (json) => { - json.compilerOptions.types = types; - return json; - } - ); - - // This should be the last tsconfig references so it's not in the template - updateJson( - tree, - joinPathFragments(options.appProjectRoot, 'tsconfig.json'), - (json) => { - json.references.push({ - path: './tsconfig.editor.json', - }); - return json; - } - ); -} diff --git a/packages/angular/src/generators/application/angular-v14/lib/update-nx-component-template.ts b/packages/angular/src/generators/application/angular-v14/lib/update-nx-component-template.ts deleted file mode 100644 index 2157eea73f471..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/lib/update-nx-component-template.ts +++ /dev/null @@ -1,63 +0,0 @@ -import type { Tree } from '@nrwl/devkit'; -import type { NormalizedSchema } from './normalized-schema'; - -import * as ts from 'typescript'; -import { addGlobal, replaceNodeValue } from '@nrwl/js'; -import { getDecoratorPropertyValueNode } from '../../../../utils/nx-devkit/ast-utils'; -import { nrwlHomeTemplate } from './nrwl-home-tpl'; - -export function updateNxComponentTemplate( - host: Tree, - options: NormalizedSchema -) { - const componentPath = `${options.appProjectRoot}/src/app/nx-welcome.component.ts`; - const templateNodeValue = getDecoratorPropertyValueNode( - host, - componentPath, - 'Component', - 'template', - '@angular/core' - ); - - replaceNodeValue( - host, - ts.createSourceFile( - componentPath, - host.read(componentPath, 'utf-8'), - ts.ScriptTarget.Latest, - true - ), - componentPath, - templateNodeValue, - `\`\n${nrwlHomeTemplate.template(options.name)}\n\`` - ); - - // Fixing extra comma issue `,,` - let sourceFile = ts.createSourceFile( - componentPath, - host.read(componentPath, 'utf-8'), - ts.ScriptTarget.Latest, - true - ); - const componentFile = host - .read(componentPath, 'utf-8') - .toString() - .replace(/,,/gi, ','); - host.write(componentPath, componentFile); - sourceFile.update(componentFile, { - newLength: componentFile.length, - span: { - length: sourceFile.text.length, - start: 0, - }, - }); - - // Add ESLint ignore to pass the lint step - sourceFile = ts.createSourceFile( - componentPath, - host.read(componentPath, 'utf-8'), - ts.ScriptTarget.Latest, - true - ); - addGlobal(host, sourceFile, componentPath, '/* eslint-disable */'); -} diff --git a/packages/angular/src/generators/application/angular-v14/schema.d.ts b/packages/angular/src/generators/application/angular-v14/schema.d.ts deleted file mode 100644 index c60a3c085f892..0000000000000 --- a/packages/angular/src/generators/application/angular-v14/schema.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Linter } from '@nrwl/linter'; -import { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners'; -import type { Styles } from '../utils/types'; - -export interface Schema { - name: string; - addTailwind?: boolean; - skipFormat?: boolean; - inlineStyle?: boolean; - inlineTemplate?: boolean; - viewEncapsulation?: 'Emulated' | 'Native' | 'None'; - routing?: boolean; - prefix?: string; - style?: Styles; - skipTests?: boolean; - directory?: string; - tags?: string; - linter?: Linter; - unitTestRunner?: UnitTestRunner; - e2eTestRunner?: E2eTestRunner; - backendProject?: string; - strict?: boolean; - standaloneConfig?: boolean; - port?: number; - setParserOptionsProject?: boolean; - skipPackageJson?: boolean; - standalone?: boolean; - rootProject?: boolean; - minimal?: boolean; -} diff --git a/packages/angular/src/generators/application/application.ts b/packages/angular/src/generators/application/application.ts index 26a8811af43bc..3c6d50e139f83 100644 --- a/packages/angular/src/generators/application/application.ts +++ b/packages/angular/src/generators/application/application.ts @@ -8,13 +8,9 @@ import { Tree, updateNxJson, } from '@nrwl/devkit'; -import { join } from 'path'; import { angularInitGenerator } from '../init/init'; import { setupTailwindGenerator } from '../setup-tailwind/setup-tailwind'; -import { - getGeneratorDirectoryForInstalledAngularVersion, - getInstalledAngularVersionInfo, -} from '../utils/version-utils'; +import { getInstalledAngularVersionInfo } from '../utils/version-utils'; import { addE2e, addLinting, @@ -54,15 +50,6 @@ export async function applicationGenerator( }).then((a) => a['standalone-components']); } - const generatorDirectory = - getGeneratorDirectoryForInstalledAngularVersion(tree); - if (generatorDirectory) { - let previousGenerator = await import( - join(__dirname, generatorDirectory, 'application') - ); - return await previousGenerator.default(tree, schema); - } - const options = normalizeOptions(tree, schema); const rootOffset = offsetFromRoot(options.appProjectRoot); diff --git a/packages/angular/src/generators/application/files/base/tsconfig.app.json__tpl__ b/packages/angular/src/generators/application/files/base/tsconfig.app.json__tpl__ index 61dcf0ec390a8..2d91132ac88c2 100644 --- a/packages/angular/src/generators/application/files/base/tsconfig.app.json__tpl__ +++ b/packages/angular/src/generators/application/files/base/tsconfig.app.json__tpl__ @@ -4,7 +4,7 @@ "outDir": "<%= rootOffset %>dist/out-tsc", "types": [] }, - "files": ["src/main.ts"], + "files": ["src/main.ts"<% if(installedAngularInfo.major === 14) { %>, "src/polyfills.ts"<% } %>], "include": ["src/**/*.d.ts"], "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] } diff --git a/packages/angular/src/generators/application/files/base/tsconfig.json__tpl__ b/packages/angular/src/generators/application/files/base/tsconfig.json__tpl__ index 9da091e641394..574499b52ee13 100644 --- a/packages/angular/src/generators/application/files/base/tsconfig.json__tpl__ +++ b/packages/angular/src/generators/application/files/base/tsconfig.json__tpl__ @@ -1,7 +1,7 @@ { "compilerOptions": { - "target": "es2022", - "useDefineForClassFields": false, + "target": <% if(installedAngularInfo.major === 14) { %>"es2020"<% } else { %>"es2022"<% } %><% if(installedAngularInfo.major === 15) { %>, + "useDefineForClassFields": false<% } %> }, "files": [], "include": [], diff --git a/packages/angular/src/generators/application/files/ng-module/src/main.ts__tpl__ b/packages/angular/src/generators/application/files/ng-module/src/main.ts__tpl__ index 16de2365d275e..8640123932894 100644 --- a/packages/angular/src/generators/application/files/ng-module/src/main.ts__tpl__ +++ b/packages/angular/src/generators/application/files/ng-module/src/main.ts__tpl__ @@ -1,5 +1,11 @@ -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { AppModule } from './app/app.module'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';<% if(installedAngularInfo.major === 14) { %> +import { enableProdMode } from '@angular/core';<% } %> +import { AppModule } from './app/app.module';<% if(installedAngularInfo.major === 14) { %> +import { environment } from './environments/environment'; + +if(environment.production) { + enableProdMode(); +}<% } %> platformBrowserDynamic() .bootstrapModule(AppModule) diff --git a/packages/angular/src/generators/application/files/v14/.browserlistrc__tpl__ b/packages/angular/src/generators/application/files/v14/.browserlistrc__tpl__ new file mode 100644 index 0000000000000..4f9ac26980c15 --- /dev/null +++ b/packages/angular/src/generators/application/files/v14/.browserlistrc__tpl__ @@ -0,0 +1,16 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# For the full list of supported browsers by the Angular framework, please see: +# https://angular.io/guide/browser-support + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +last 1 Chrome version +last 1 Firefox version +last 2 Edge major versions +last 2 Safari major versions +last 2 iOS major versions +Firefox ESR diff --git a/packages/angular/src/generators/application/files/v14/src/environments/environment.prod.ts__tpl__ b/packages/angular/src/generators/application/files/v14/src/environments/environment.prod.ts__tpl__ new file mode 100644 index 0000000000000..3612073bc31cd --- /dev/null +++ b/packages/angular/src/generators/application/files/v14/src/environments/environment.prod.ts__tpl__ @@ -0,0 +1,3 @@ +export const environment = { + production: true +}; diff --git a/packages/angular/src/generators/application/files/v14/src/environments/environment.ts__tpl__ b/packages/angular/src/generators/application/files/v14/src/environments/environment.ts__tpl__ new file mode 100644 index 0000000000000..8c29253accfd8 --- /dev/null +++ b/packages/angular/src/generators/application/files/v14/src/environments/environment.ts__tpl__ @@ -0,0 +1,16 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build` replaces `environment.ts` with `environment.prod.ts`. +// The list of file replacements can be found in `angular.json`. + +export const environment = { + production: false +}; + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +// import 'zone.js/plugins/zone-error'; diff --git a/packages/angular/src/generators/application/files/v14/src/polyfills.ts__tpl__ b/packages/angular/src/generators/application/files/v14/src/polyfills.ts__tpl__ new file mode 100644 index 0000000000000..429bb9ef2d340 --- /dev/null +++ b/packages/angular/src/generators/application/files/v14/src/polyfills.ts__tpl__ @@ -0,0 +1,53 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes recent versions of Safari, Chrome (including + * Opera), Edge on the desktop, and iOS and Chrome on mobile. + * + * Learn more in https://angular.io/guide/browser-support + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + * because those flags need to be set before `zone.js` being loaded, and webpack + * will put import in the top of bundle, so user need to create a separate file + * in this directory (for example: zone-flags.ts), and put the following flags + * into that file, and then add the following code before importing zone.js. + * import './zone-flags'; + * + * The flags allowed in zone-flags.ts are listed here. + * + * The following flags will work for all browsers. + * + * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + * + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + * + * (window as any).__Zone_enable_cross_context_check = true; + * + */ + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js'; // Included with Angular CLI. + + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ diff --git a/packages/angular/src/generators/application/lib/create-files.ts b/packages/angular/src/generators/application/lib/create-files.ts index 67de6a5ff4406..375f60f8db326 100644 --- a/packages/angular/src/generators/application/lib/create-files.ts +++ b/packages/angular/src/generators/application/lib/create-files.ts @@ -7,83 +7,60 @@ import { } from '@nrwl/js'; import { createTsConfig } from '../../utils/create-ts-config'; import { UnitTestRunner } from '../../../utils/test-runners'; +import { getInstalledAngularVersionInfo } from '../../utils/version-utils'; export async function createFiles( tree: Tree, options: NormalizedSchema, rootOffset: string ) { - await generateFiles( + const installedAngularInfo = getInstalledAngularVersionInfo(tree); + const substitutions = { + rootSelector: `${options.prefix}-root`, + appName: options.name, + inlineStyle: options.inlineStyle, + inlineTemplate: options.inlineTemplate, + style: options.style, + viewEncapsulation: options.viewEncapsulation, + unitTesting: options.unitTestRunner !== UnitTestRunner.None, + routing: options.routing, + minimal: options.minimal, + nxWelcomeSelector: `${options.prefix}-nx-welcome`, + rootTsConfig: joinPathFragments(rootOffset, getRootTsConfigFileName(tree)), + installedAngularInfo, + rootOffset, + tpl: '', + }; + + generateFiles( tree, joinPathFragments(__dirname, '../files/base'), options.appProjectRoot, - { - rootSelector: `${options.prefix}-root`, - appName: options.name, - inlineStyle: options.inlineStyle, - inlineTemplate: options.inlineTemplate, - style: options.style, - viewEncapsulation: options.viewEncapsulation, - unitTesting: options.unitTestRunner !== UnitTestRunner.None, - routing: options.routing, - minimal: options.minimal, - nxWelcomeSelector: `${options.prefix}-nx-welcome`, - rootTsConfig: joinPathFragments( - rootOffset, - getRootTsConfigFileName(tree) - ), - rootOffset, - tpl: '', - } + substitutions ); + if (installedAngularInfo.major === 14) { + generateFiles( + tree, + joinPathFragments(__dirname, '../files/v14'), + options.appProjectRoot, + substitutions + ); + } + if (options.standalone) { - await generateFiles( + generateFiles( tree, joinPathFragments(__dirname, '../files/standalone-components'), options.appProjectRoot, - { - rootSelector: `${options.prefix}-root`, - appName: options.name, - inlineStyle: options.inlineStyle, - inlineTemplate: options.inlineTemplate, - style: options.style, - viewEncapsulation: options.viewEncapsulation, - unitTesting: options.unitTestRunner !== UnitTestRunner.None, - routing: options.routing, - minimal: options.minimal, - nxWelcomeSelector: `${options.prefix}-nx-welcome`, - rootTsConfig: joinPathFragments( - rootOffset, - getRootTsConfigFileName(tree) - ), - rootOffset, - tpl: '', - } + substitutions ); } else { await generateFiles( tree, joinPathFragments(__dirname, '../files/ng-module'), options.appProjectRoot, - { - rootSelector: `${options.prefix}-root`, - appName: options.name, - inlineStyle: options.inlineStyle, - inlineTemplate: options.inlineTemplate, - style: options.style, - viewEncapsulation: options.viewEncapsulation, - unitTesting: options.unitTestRunner !== UnitTestRunner.None, - routing: options.routing, - minimal: options.minimal, - nxWelcomeSelector: `${options.prefix}-nx-welcome`, - rootTsConfig: joinPathFragments( - rootOffset, - getRootTsConfigFileName(tree) - ), - rootOffset, - tpl: '', - } + substitutions ); } diff --git a/packages/angular/src/generators/application/lib/create-project.ts b/packages/angular/src/generators/application/lib/create-project.ts index b526115d5c578..4916905b861fe 100644 --- a/packages/angular/src/generators/application/lib/create-project.ts +++ b/packages/angular/src/generators/application/lib/create-project.ts @@ -1,8 +1,10 @@ import { NormalizedSchema } from './normalized-schema'; import type { ProjectConfiguration, Tree } from '@nrwl/devkit'; import { addProjectConfiguration } from '@nrwl/devkit'; +import { getInstalledAngularVersionInfo } from '../../utils/version-utils'; export function createProject(tree: Tree, options: NormalizedSchema) { + const installedAngularInfo = getInstalledAngularVersionInfo(tree); const project: ProjectConfiguration & { prefix: string } = { name: options.name, projectType: 'application', @@ -20,7 +22,10 @@ export function createProject(tree: Tree, options: NormalizedSchema) { }`, index: `${options.appProjectRoot}/src/index.html`, main: `${options.appProjectRoot}/src/main.ts`, - polyfills: ['zone.js'], + polyfills: + installedAngularInfo.major === 14 + ? `${options.appProjectRoot}/src/polyfills.ts` + : ['zone.js'], tsConfig: `${options.appProjectRoot}/tsconfig.app.json`, assets: [ `${options.appProjectRoot}/src/favicon.ico`, @@ -43,6 +48,15 @@ export function createProject(tree: Tree, options: NormalizedSchema) { maximumError: '4kb', }, ], + fileReplacements: + installedAngularInfo.major === 14 + ? [ + { + replace: `${options.appProjectRoot}/src/environments/environment.ts`, + with: `${options.appProjectRoot}/src/environments/environment.prod.ts`, + }, + ] + : undefined, outputHashing: 'all', }, development: { From d3a85e5450634bc66c75482c0cd24238ed99be21 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Wed, 22 Mar 2023 17:02:39 +0200 Subject: [PATCH 19/76] feat(vite): ts-node register paths in vite (#15629) --- packages/vite/src/executors/build/build.impl.ts | 7 ++++++- packages/vite/src/executors/dev-server/dev-server.impl.ts | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/executors/build/build.impl.ts b/packages/vite/src/executors/build/build.impl.ts index cd082b668821e..593d632e4b44d 100644 --- a/packages/vite/src/executors/build/build.impl.ts +++ b/packages/vite/src/executors/build/build.impl.ts @@ -11,14 +11,19 @@ import { existsSync } from 'fs'; import { resolve } from 'path'; import { createAsyncIterable } from '@nrwl/devkit/src/utils/async-iterable'; +import { registerTsConfigPaths } from 'nx/src/utils/register'; + export async function* viteBuildExecutor( options: ViteBuildExecutorOptions, context: ExecutorContext ) { - const normalizedOptions = normalizeOptions(options); const projectRoot = context.projectsConfigurations.projects[context.projectName].root; + registerTsConfigPaths(resolve(projectRoot, 'tsconfig.json')); + + const normalizedOptions = normalizeOptions(options); + const buildConfig = mergeConfig( getViteSharedConfig(normalizedOptions, false, context), { diff --git a/packages/vite/src/executors/dev-server/dev-server.impl.ts b/packages/vite/src/executors/dev-server/dev-server.impl.ts index ce5bc2826e288..971cb6d05ba79 100644 --- a/packages/vite/src/executors/dev-server/dev-server.impl.ts +++ b/packages/vite/src/executors/dev-server/dev-server.impl.ts @@ -11,11 +11,18 @@ import { import { ViteDevServerExecutorOptions } from './schema'; import { ViteBuildExecutorOptions } from '../build/schema'; +import { registerTsConfigPaths } from 'nx/src/utils/register'; +import { resolve } from 'path'; export async function* viteDevServerExecutor( options: ViteDevServerExecutorOptions, context: ExecutorContext ): AsyncGenerator<{ success: boolean; baseUrl: string }> { + const projectRoot = + context.projectsConfigurations.projects[context.projectName].root; + + registerTsConfigPaths(resolve(projectRoot, 'tsconfig.json')); + // Retrieve the option for the configured buildTarget. const buildTargetOptions: ViteBuildExecutorOptions = getNxTargetOptions( options.buildTarget, From b230af225dd23e8255d8916a036d5b88ab910bbf Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Wed, 22 Mar 2023 11:23:46 -0400 Subject: [PATCH 20/76] chore(repo): add ora as a dependency (#15810) --- package.json | 1 + yarn.lock | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/package.json b/package.json index 8ec6e8d111255..a03fbb1c5a299 100644 --- a/package.json +++ b/package.json @@ -202,6 +202,7 @@ "nx": "15.9.0-beta.4", "octokit": "^2.0.14", "open": "^8.4.0", + "ora": "5.3.0", "parse-markdown-links": "^1.0.4", "parse5": "4.0.0", "postcss": "8.4.19", diff --git a/yarn.lock b/yarn.lock index fabd12b9b3a65..59543fcee84b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19970,6 +19970,20 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +ora@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.3.0.tgz#fb832899d3a1372fe71c8b2c534bbfe74961bb6f" + integrity sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g== + dependencies: + bl "^4.0.3" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + log-symbols "^4.0.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + ora@5.4.1, ora@^5.1.0, ora@^5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" From ac209831ce64044c846c294b7dc27ebc69118036 Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Wed, 22 Mar 2023 11:25:00 -0400 Subject: [PATCH 21/76] chore(repo): add github action to prevent merging PRs that are not ready yet (#15822) --- .github/workflows/do-not-merge.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/do-not-merge.yml diff --git a/.github/workflows/do-not-merge.yml b/.github/workflows/do-not-merge.yml new file mode 100644 index 0000000000000..07aed4e3b5769 --- /dev/null +++ b/.github/workflows/do-not-merge.yml @@ -0,0 +1,20 @@ +name: Unmergable Labels Check + +on: + pull_request: + types: [synchronize, opened, reopened, labeled, unlabeled] + +jobs: + do-not-merge: + name: Prevent Merging + runs-on: ubuntu-latest + steps: + - name: Check for label + run: | + echo "${{ toJSON(github.event.*.labels.*.name) }}" + node -e 'const forbidden = ["target: next major version", "PR Status: needs tests", "PR Status: in-progress", "blocked: needs rebase"]; + const match = ${{ toJSON(github.event.*.labels.*.name) }}.find(l => forbidden.includes(l)); + if (match) { + console.log("Cannot merge PRs that are labeled with " + match); + process.exit(1) + }' From 5e5a3f748714c0fb9f07143a6328686cb1abc23b Mon Sep 17 00:00:00 2001 From: Victor Savkin Date: Wed, 22 Mar 2023 11:45:25 -0400 Subject: [PATCH 22/76] chore(repo): update to nx-cloud 15.3.1 --- nx.json | 2 +- package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nx.json b/nx.json index 16a8972b0f46c..8a37132293aa7 100644 --- a/nx.json +++ b/nx.json @@ -7,7 +7,7 @@ "default": { "runner": "@nrwl/nx-cloud", "options": { - "accessToken": "MjEyM2IwYWQtMTEyMS00ZGFmLThkY2UtYjg4YWRiZTNjZmI5fHJlYWQtd3JpdGU=", + "accessToken": "NDg1NTA3MTAtOGFmZC00YmIwLTk2Y2MtOTkzNzc4ZTczYTlkfHJlYWQtb25seQ==", "cacheableOperations": [ "build", "lint-base", diff --git a/package.json b/package.json index a03fbb1c5a299..cc88391dc09bf 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@nrwl/js": "15.9.0-beta.4", "@nrwl/linter": "15.9.0-beta.4", "@nrwl/next": "15.9.0-beta.4", - "@nrwl/nx-cloud": "15.2.3", + "@nrwl/nx-cloud": "15.3.1", "@nrwl/react": "15.9.0-beta.4", "@nrwl/storybook": "15.9.0-beta.4", "@nrwl/web": "15.9.0-beta.4", diff --git a/yarn.lock b/yarn.lock index 59543fcee84b0..21451d8b7c7d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5096,10 +5096,10 @@ url-loader "^4.1.1" webpack-merge "^5.8.0" -"@nrwl/nx-cloud@15.2.3": - version "15.2.3" - resolved "https://registry.yarnpkg.com/@nrwl/nx-cloud/-/nx-cloud-15.2.3.tgz#5c33f845f6aa9d0c9b60e9dc10476a2c54eade01" - integrity sha512-odbLJVhANnjXhUvHOeKGfMeK6hrC8poa/y1p6Wj1E5K8xbECMlz/kUWYjcbkEFcSCGyjCednDZCirhT9oy3RVQ== +"@nrwl/nx-cloud@15.3.1": + version "15.3.1" + resolved "https://registry.yarnpkg.com/@nrwl/nx-cloud/-/nx-cloud-15.3.1.tgz#f31acb39879a879cd4e547071196f0269618aed2" + integrity sha512-6NMXtmRQLDv8MiXfVLnqi0+qkY7IUdo0FN7lKXZ0Inlzxoky1gvYRzcI1gXyGuw9Ma/t+gjCLXUQAnUGdovYog== dependencies: axios "^0.21.2" chalk "4.1.0" From c7e49c564aec257499c526c0b6a5a6a1931f373f Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Wed, 22 Mar 2023 14:03:09 -0400 Subject: [PATCH 23/76] fix(nextjs): produce correct next.config.js file for production server (#15824) --- e2e/next/src/next.test.ts | 52 +++++--- packages/next/package.json | 5 +- packages/next/plugins/with-nx.ts | 118 ++++++++++-------- .../build/lib/create-next-config-file.ts | 62 +-------- .../generators/application/lib/add-cypress.ts | 10 +- .../generators/application/lib/add-jest.ts | 15 ++- packages/next/src/generators/init/init.ts | 10 +- packages/next/src/utils/config.ts | 11 +- packages/next/src/utils/create-copy-plugin.ts | 83 ++++++++++++ 9 files changed, 216 insertions(+), 150 deletions(-) create mode 100644 packages/next/src/utils/create-copy-plugin.ts diff --git a/e2e/next/src/next.test.ts b/e2e/next/src/next.test.ts index 932dd116cca40..05b0d546fca6b 100644 --- a/e2e/next/src/next.test.ts +++ b/e2e/next/src/next.test.ts @@ -5,6 +5,7 @@ import { cleanupProject, getPackageManagerCommand, isNotWindows, + killPort, killPorts, newProject, packageManagerLockFile, @@ -20,25 +21,22 @@ import { } from '@nrwl/e2e/utils'; import * as http from 'http'; import { checkApp } from './utils'; +import { removeSync } from 'fs-extra'; describe('Next.js Applications', () => { let proj: string; let originalEnv: string; let packageManager; - beforeAll(() => { + beforeEach(() => { proj = newProject(); packageManager = detectPackageManager(tmpProjPath()); - }); - - afterAll(() => cleanupProject()); - - beforeEach(() => { originalEnv = process.env.NODE_ENV; }); afterEach(() => { process.env.NODE_ENV = originalEnv; + cleanupProject(); }); it('should generate app + libs', async () => { @@ -169,6 +167,22 @@ describe('Next.js Applications', () => { `dist/apps/${appName}/public/a/b.txt`, `dist/apps/${appName}/public/shared/ui/hello.txt` ); + + // Check that the output is self-contained (i.e. can run with its own package.json + node_modules) + const distPath = joinPathFragments(tmpProjPath(), 'dist/apps', appName); + const port = 3000; + const pmc = getPackageManagerCommand(); + runCommand(`${pmc.install}`, { + cwd: distPath, + }); + runCLI( + `generate @nrwl/workspace:run-commands serve-prod --project ${appName} --cwd=dist/apps/${appName} --command="npx next start --port=${port}"` + ); + await runCommandUntil(`run ${appName}:serve-prod`, (output) => { + return output.includes(`localhost:${port}`); + }); + + await killPort(port); }, 300_000); it('should build and install pruned lock file', () => { @@ -399,17 +413,19 @@ describe('Next.js Applications', () => { }, 300_000); }); -function getData(port: number, path = ''): Promise { - return new Promise((resolve) => { - http.get(`http://localhost:${port}${path}`, (res) => { - expect(res.statusCode).toEqual(200); - let data = ''; - res.on('data', (chunk) => { - data += chunk; - }); - res.once('end', () => { - resolve(data); - }); - }); +function getData(port, path = ''): Promise { + return new Promise((resolve, reject) => { + http + .get(`http://localhost:${port}${path}`, (res) => { + expect(res.statusCode).toEqual(200); + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + res.once('end', () => { + resolve(data); + }); + }) + .on('error', (err) => reject(err)); }); } diff --git a/packages/next/package.json b/packages/next/package.json index 6e37247e68c9d..782c8b10517ed 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -35,22 +35,21 @@ }, "dependencies": { "@babel/plugin-proposal-decorators": "^7.14.5", - "@nrwl/cypress": "file:../cypress", "@nrwl/devkit": "file:../devkit", - "@nrwl/jest": "file:../jest", "@nrwl/js": "file:../js", "@nrwl/linter": "file:../linter", "@nrwl/react": "file:../react", - "@nrwl/webpack": "file:../webpack", "@nrwl/workspace": "file:../workspace", "@svgr/webpack": "^6.1.2", "chalk": "^4.1.0", + "copy-webpack-plugin": "^10.2.4", "dotenv": "~10.0.0", "fs-extra": "^11.1.0", "ignore": "^5.0.4", "semver": "7.3.4", "ts-node": "10.9.1", "tsconfig-paths": "^4.1.2", + "tsconfig-paths-webpack-plugin": "4.0.0", "url-loader": "^4.1.1", "webpack-merge": "^5.8.0" }, diff --git a/packages/next/plugins/with-nx.ts b/packages/next/plugins/with-nx.ts index 211850bbe94ad..51b7e5d6c6d19 100644 --- a/packages/next/plugins/with-nx.ts +++ b/packages/next/plugins/with-nx.ts @@ -13,8 +13,9 @@ import { DependentBuildableProjectNode, } from '@nrwl/js/src/utils/buildable-libs-utils'; import type { NextConfig } from 'next'; +import { PHASE_PRODUCTION_SERVER } from 'next/constants'; -import path = require('path'); +import * as path from 'path'; import { createWebpackConfig } from '../src/utils/config'; import { NextBuildBuilderOptions } from '../src/utils/types'; export interface WithNxOptions extends NextConfig { @@ -113,62 +114,69 @@ function getNxContext( export function withNx( _nextConfig = {} as WithNxOptions, context: WithNxContext = getWithNxContext() -): () => Promise { - return async () => { - let dependencies: DependentBuildableProjectNode[] = []; - - const graph = await createProjectGraphAsync(); - - const originalTarget = { - project: process.env.NX_TASK_TARGET_PROJECT, - target: process.env.NX_TASK_TARGET_TARGET, - configuration: process.env.NX_TASK_TARGET_CONFIGURATION, - }; - - const { - node: projectNode, - options, - projectName: project, - targetName, - configurationName, - } = getNxContext(graph, originalTarget); - const projectDirectory = projectNode.data.root; - - if (options.buildLibsFromSource === false && targetName) { - const result = calculateProjectDependencies( - graph, - workspaceRoot, - project, +): (phase: string) => Promise { + return async (phase: string) => { + if (phase === PHASE_PRODUCTION_SERVER) { + // If we are running an already built production server, just return the configuration. + const { nx, ...validNextConfig } = _nextConfig; + return { distDir: '.next', ...validNextConfig }; + } else { + // Otherwise, add in webpack and eslint configuration for build or test. + let dependencies: DependentBuildableProjectNode[] = []; + + const graph = await createProjectGraphAsync(); + + const originalTarget = { + project: process.env.NX_TASK_TARGET_PROJECT, + target: process.env.NX_TASK_TARGET_TARGET, + configuration: process.env.NX_TASK_TARGET_CONFIGURATION, + }; + + const { + node: projectNode, + options, + projectName: project, targetName, - configurationName - ); - dependencies = result.dependencies; - } + configurationName, + } = getNxContext(graph, originalTarget); + const projectDirectory = projectNode.data.root; + + if (options.buildLibsFromSource === false && targetName) { + const result = calculateProjectDependencies( + graph, + workspaceRoot, + project, + targetName, + configurationName + ); + dependencies = result.dependencies; + } - // Get next config - const nextConfig = getNextConfig(_nextConfig, context); - - const outputDir = `${offsetFromRoot(projectDirectory)}${ - options.outputPath - }`; - nextConfig.distDir = - nextConfig.distDir && nextConfig.distDir !== '.next' - ? joinPathFragments(outputDir, nextConfig.distDir) - : joinPathFragments(outputDir, '.next'); - - const userWebpackConfig = nextConfig.webpack; - - nextConfig.webpack = (a, b) => - createWebpackConfig( - workspaceRoot, - options.root, - options.fileReplacements, - options.assets, - dependencies, - path.join(workspaceRoot, context.libsDir) - )(userWebpackConfig ? userWebpackConfig(a, b) : a, b); - - return nextConfig; + // Get next config + const nextConfig = getNextConfig(_nextConfig, context); + + const outputDir = `${offsetFromRoot(projectDirectory)}${ + options.outputPath + }`; + nextConfig.distDir = + nextConfig.distDir && nextConfig.distDir !== '.next' + ? joinPathFragments(outputDir, nextConfig.distDir) + : joinPathFragments(outputDir, '.next'); + + const userWebpackConfig = nextConfig.webpack; + + nextConfig.webpack = (a, b) => + createWebpackConfig( + workspaceRoot, + options.root, + options.fileReplacements, + options.assets, + dependencies, + path.join(workspaceRoot, context.libsDir) + )(userWebpackConfig ? userWebpackConfig(a, b) : a, b); + + return nextConfig; + } }; } diff --git a/packages/next/src/executors/build/lib/create-next-config-file.ts b/packages/next/src/executors/build/lib/create-next-config-file.ts index 39d13be27882f..535aa218543e7 100644 --- a/packages/next/src/executors/build/lib/create-next-config-file.ts +++ b/packages/next/src/executors/build/lib/create-next-config-file.ts @@ -1,17 +1,9 @@ -import type { ExecutorContext } from '@nrwl/devkit'; -import { - applyChangesToString, - ChangeType, - workspaceLayout, - workspaceRoot, - stripIndents, -} from '@nrwl/devkit'; -import * as ts from 'typescript'; -import { existsSync, readFileSync, writeFileSync } from 'fs'; +import { ExecutorContext } from '@nrwl/devkit'; + +import { copyFileSync, existsSync } from 'fs'; import { join } from 'path'; import type { NextBuildBuilderOptions } from '../../../utils/types'; -import { findNodes } from 'nx/src/utils/typescript'; export function createNextConfigFile( options: NextBuildBuilderOptions, @@ -21,53 +13,7 @@ export function createNextConfigFile( ? join(context.root, options.nextConfig) : join(context.root, options.root, 'next.config.js'); - // Copy config file and our `with-nx.js` file to remove dependency on @nrwl/next for production build. if (existsSync(nextConfigPath)) { - writeFileSync(join(options.outputPath, 'with-nx.js'), getWithNxContent()); - writeFileSync( - join(options.outputPath, 'next.config.js'), - readFileSync(nextConfigPath) - .toString() - .replace('@nrwl/next/plugins/with-nx', './with-nx.js') - ); - } -} - -function getWithNxContent() { - const withNxFile = join(__dirname, '../../../../plugins/with-nx.js'); - let withNxContent = readFileSync(withNxFile).toString(); - const withNxSource = ts.createSourceFile( - withNxFile, - withNxContent, - ts.ScriptTarget.Latest, - true - ); - const getWithNxContextDeclaration = findNodes( - withNxSource, - ts.SyntaxKind.FunctionDeclaration - )?.find( - (node: ts.FunctionDeclaration) => node.name?.text === 'getWithNxContext' - ); - - if (getWithNxContextDeclaration) { - withNxContent = applyChangesToString(withNxContent, [ - { - type: ChangeType.Delete, - start: getWithNxContextDeclaration.getStart(withNxSource), - length: getWithNxContextDeclaration.getWidth(withNxSource), - }, - { - type: ChangeType.Insert, - index: getWithNxContextDeclaration.getStart(withNxSource), - text: stripIndents`function getWithNxContext() { - return { - workspaceRoot: '${workspaceRoot}', - libsDir: '${workspaceLayout().libsDir}' - } - }`, - }, - ]); + copyFileSync(nextConfigPath, join(options.outputPath, 'next.config.js')); } - - return withNxContent; } diff --git a/packages/next/src/generators/application/lib/add-cypress.ts b/packages/next/src/generators/application/lib/add-cypress.ts index 33ac31ffa759b..fb3be4b8aeae3 100644 --- a/packages/next/src/generators/application/lib/add-cypress.ts +++ b/packages/next/src/generators/application/lib/add-cypress.ts @@ -1,13 +1,17 @@ -import { cypressProjectGenerator } from '@nrwl/cypress'; -import { Tree } from '@nrwl/devkit'; -import { NormalizedSchema } from './normalize-options'; +import { ensurePackage, Tree } from '@nrwl/devkit'; import { Linter } from '@nrwl/linter'; +import { nxVersion } from '../../../utils/versions'; +import { NormalizedSchema } from './normalize-options'; + export async function addCypress(host: Tree, options: NormalizedSchema) { if (options.e2eTestRunner !== 'cypress') { return () => {}; } + const { cypressProjectGenerator } = ensurePackage< + typeof import('@nrwl/cypress') + >('@nrwl/cypress', nxVersion); return cypressProjectGenerator(host, { ...options, linter: Linter.EsLint, diff --git a/packages/next/src/generators/application/lib/add-jest.ts b/packages/next/src/generators/application/lib/add-jest.ts index 3237a4ebb664f..5e6e52e5de28d 100644 --- a/packages/next/src/generators/application/lib/add-jest.ts +++ b/packages/next/src/generators/application/lib/add-jest.ts @@ -1,5 +1,12 @@ -import { joinPathFragments, readJson, Tree, updateJson } from '@nrwl/devkit'; -import { jestProjectGenerator } from '@nrwl/jest'; +import { + ensurePackage, + joinPathFragments, + readJson, + Tree, + updateJson, +} from '@nrwl/devkit'; + +import { nxVersion } from '../../../utils/versions'; import { NormalizedSchema } from './normalize-options'; export async function addJest(host: Tree, options: NormalizedSchema) { @@ -7,6 +14,10 @@ export async function addJest(host: Tree, options: NormalizedSchema) { return () => {}; } + const { jestProjectGenerator } = ensurePackage( + '@nrwl/jest', + nxVersion + ); const jestTask = await jestProjectGenerator(host, { ...options, project: options.projectName, diff --git a/packages/next/src/generators/init/init.ts b/packages/next/src/generators/init/init.ts index acdeb2034390d..dd7c4e225c6f9 100644 --- a/packages/next/src/generators/init/init.ts +++ b/packages/next/src/generators/init/init.ts @@ -1,13 +1,12 @@ import { addDependenciesToPackageJson, convertNxGenerator, + ensurePackage, GeneratorCallback, runTasksInSerial, Tree, } from '@nrwl/devkit'; -import { jestInitGenerator } from '@nrwl/jest'; -import { cypressInitGenerator } from '@nrwl/cypress'; import { reactDomVersion, reactVersion } from '@nrwl/react/src/utils/versions'; import reactInitGenerator from '@nrwl/react/src/generators/init/init'; import { initGenerator as jsInitGenerator } from '@nrwl/js'; @@ -48,10 +47,17 @@ export async function nextInitGenerator(host: Tree, schema: InitSchema) { ); if (!schema.unitTestRunner || schema.unitTestRunner === 'jest') { + const { jestInitGenerator } = ensurePackage( + '@nrwl/jest', + nxVersion + ); const jestTask = await jestInitGenerator(host, schema); tasks.push(jestTask); } if (!schema.e2eTestRunner || schema.e2eTestRunner === 'cypress') { + const { cypressInitGenerator } = ensurePackage< + typeof import('@nrwl/cypress') + >('@nrwl/cypress', nxVersion); const cypressTask = await cypressInitGenerator(host, {}); tasks.push(cypressTask); } diff --git a/packages/next/src/utils/config.ts b/packages/next/src/utils/config.ts index 9b5cb25b72280..3e6a0cc885bf1 100644 --- a/packages/next/src/utils/config.ts +++ b/packages/next/src/utils/config.ts @@ -2,7 +2,7 @@ import { join, resolve } from 'path'; import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin'; import { Configuration, RuleSetRule } from 'webpack'; import { FileReplacement } from './types'; -import { createCopyPlugin, normalizeAssets } from '@nrwl/webpack'; +import { createCopyPlugin } from './create-copy-plugin'; import { createTmpTsConfig, DependentBuildableProjectNode, @@ -77,14 +77,7 @@ export function createWebpackConfig( // Copy (shared) assets to `public` folder during client-side compilation if (!isServer && Array.isArray(assets) && assets.length > 0) { - config.plugins.push( - createCopyPlugin( - normalizeAssets(assets, workspaceRoot, projectRoot).map((asset) => ({ - ...asset, - output: join('../public', asset.output), - })) - ) - ); + config.plugins.push(createCopyPlugin(assets, workspaceRoot, projectRoot)); } return config; diff --git a/packages/next/src/utils/create-copy-plugin.ts b/packages/next/src/utils/create-copy-plugin.ts new file mode 100644 index 0000000000000..01294f2f0be17 --- /dev/null +++ b/packages/next/src/utils/create-copy-plugin.ts @@ -0,0 +1,83 @@ +import * as CopyWebpackPlugin from 'copy-webpack-plugin'; +import { normalizePath } from 'nx/src/utils/path'; +import { basename, dirname, join, relative, resolve } from 'path'; +import { statSync } from 'fs'; + +interface AssetGlobPattern { + glob: string; + input: string; + output: string; + ignore?: string[]; +} + +function normalizeAssets( + assets: any[], + root: string, + sourceRoot: string +): AssetGlobPattern[] { + return assets.map((asset) => { + if (typeof asset === 'string') { + const assetPath = normalizePath(asset); + const resolvedAssetPath = resolve(root, assetPath); + const resolvedSourceRoot = resolve(root, sourceRoot); + + if (!resolvedAssetPath.startsWith(resolvedSourceRoot)) { + throw new Error( + `The ${resolvedAssetPath} asset path must start with the project source root: ${sourceRoot}` + ); + } + + const isDirectory = statSync(resolvedAssetPath).isDirectory(); + const input = isDirectory + ? resolvedAssetPath + : dirname(resolvedAssetPath); + const output = relative(resolvedSourceRoot, resolve(root, input)); + const glob = isDirectory ? '**/*' : basename(resolvedAssetPath); + return { + input, + output, + glob, + }; + } else { + if (asset.output.startsWith('..')) { + throw new Error( + 'An asset cannot be written to a location outside of the output path.' + ); + } + + const assetPath = normalizePath(asset.input); + const resolvedAssetPath = resolve(root, assetPath); + return { + ...asset, + input: resolvedAssetPath, + // Now we remove starting slash to make Webpack place it from the output root. + output: asset.output.replace(/^\//, ''), + }; + } + }); +} + +export function createCopyPlugin( + assets: any[], + root: string, + sourceRoot: string +) { + return new CopyWebpackPlugin({ + patterns: normalizeAssets(assets, root, sourceRoot).map((asset) => { + return { + context: asset.input, + to: join('../public', asset.output), + from: asset.glob, + globOptions: { + ignore: [ + '.gitkeep', + '**/.DS_Store', + '**/Thumbs.db', + ...(asset.ignore ?? []), + ], + dot: true, + }, + }; + }), + }); +} From 8cd410a361e443eac5574aa4ee24a05641bc1980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Jona=C5=A1?= Date: Wed, 22 Mar 2023 19:06:10 +0100 Subject: [PATCH 24/76] fix(core): fix broken path elevation in npm lockfile pruning (#15833) --- packages/nx/src/lock-file/npm-parser.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/nx/src/lock-file/npm-parser.ts b/packages/nx/src/lock-file/npm-parser.ts index 7d2cb67f079ea..7000c79c07ddf 100644 --- a/packages/nx/src/lock-file/npm-parser.ts +++ b/packages/nx/src/lock-file/npm-parser.ts @@ -580,10 +580,20 @@ function elevateNestedPaths( `${segs.join('/node_modules/')}/node_modules/${packageName}`; // check if grandparent has the same package - while ( - segments.length > 1 && - !result.has(getNewPath(segments.slice(0, -1))) - ) { + const shouldElevate = (segs: string[]) => { + const newPath = getNewPath(segs.slice(0, -1)); + if (result.has(newPath)) { + const match = result.get(newPath); + const source = remappedPackages.get(path); + return ( + match.valueV1?.version === source.valueV1?.version && + match.valueV3?.version === source.valueV3?.version + ); + } + return true; + }; + + while (segments.length > 1 && shouldElevate(segments)) { segments.pop(); } const newPath = getNewPath(segments); From e744f0429fab28464bd20f9733132aea664e0957 Mon Sep 17 00:00:00 2001 From: FrozenPandaz Date: Wed, 22 Mar 2023 14:14:45 -0400 Subject: [PATCH 25/76] chore(misc): publish 15.9.0-beta.8 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 07516dfbc3eeb..14750d8d4d4d8 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["build/packages/*", "build/packages/nx/native-packages/*"], - "version": "15.9.0-beta.7", + "version": "15.9.0-beta.8", "granularPathspec": false, "command": { "publish": { From 4bf652d2e7badb64dcb83141c3b5a651890090e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Jona=C5=A1?= Date: Wed, 22 Mar 2023 19:17:40 +0100 Subject: [PATCH 26/76] fix(node): fix standalone linter setup (#15681) --- e2e/linter/src/linter.test.ts | 188 +++++++++--------- .../src/generators/application/application.ts | 14 +- .../src/generators/e2e-project/e2e-project.ts | 35 +++- 3 files changed, 139 insertions(+), 98 deletions(-) diff --git a/e2e/linter/src/linter.test.ts b/e2e/linter/src/linter.test.ts index 3feb4006a0e2d..93c9b8ab3bfbc 100644 --- a/e2e/linter/src/linter.test.ts +++ b/e2e/linter/src/linter.test.ts @@ -443,68 +443,81 @@ describe('Linter', () => { beforeEach(() => newProject()); afterEach(() => cleanupProject()); - it('(React standalone) should set root project config to app and e2e app and migrate when another lib is added', () => { - const myapp = uniq('myapp'); - const mylib = uniq('mylib'); - - runCLI(`generate @nrwl/react:app ${myapp} --rootProject=true`); - - let rootEslint = readJson('.eslintrc.json'); - let e2eEslint = readJson('e2e/.eslintrc.json'); + function verifySuccessfulStandaloneSetup(myapp: string) { + expect(runCLI(`lint ${myapp}`, { silenceError: true })).toContain( + 'All files pass linting' + ); + expect(runCLI(`lint e2e`, { silenceError: true })).toContain( + 'All files pass linting' + ); expect(() => checkFilesExist(`.eslintrc.base.json`)).toThrow(); + const rootEslint = readJson('.eslintrc.json'); + const e2eEslint = readJson('e2e/.eslintrc.json'); + // should directly refer to nx plugin expect(rootEslint.plugins).toEqual(['@nrwl/nx']); expect(e2eEslint.plugins).toEqual(['@nrwl/nx']); - // should only extend framework plugin - expect(rootEslint.extends).toEqual(['plugin:@nrwl/nx/react']); - expect(e2eEslint.extends).toEqual(['plugin:cypress/recommended']); - // should have plugin extends - expect(rootEslint.overrides[0].extends).toEqual([ - 'plugin:@nrwl/nx/typescript', - ]); - expect(rootEslint.overrides[1].extends).toEqual([ - 'plugin:@nrwl/nx/javascript', - ]); - expect(e2eEslint.overrides[0].extends).toEqual([ - 'plugin:@nrwl/nx/typescript', - ]); - expect(e2eEslint.overrides[1].extends).toEqual([ - 'plugin:@nrwl/nx/javascript', - ]); + } - runCLI(`generate @nrwl/react:lib ${mylib} --unitTestRunner=jest`); - // should add new tslint + function verifySuccessfulMigratedSetup(myapp: string, mylib: string) { + expect(runCLI(`lint ${myapp}`, { silenceError: true })).toContain( + 'All files pass linting' + ); + expect(runCLI(`lint e2e`, { silenceError: true })).toContain( + 'All files pass linting' + ); + expect(runCLI(`lint ${mylib}`, { silenceError: true })).toContain( + 'All files pass linting' + ); expect(() => checkFilesExist(`.eslintrc.base.json`)).not.toThrow(); - const appEslint = readJson(`.eslintrc.json`); - rootEslint = readJson('.eslintrc.base.json'); - e2eEslint = readJson('e2e/.eslintrc.json'); + + const rootEslint = readJson('.eslintrc.base.json'); + const appEslint = readJson('.eslintrc.json'); + const e2eEslint = readJson('e2e/.eslintrc.json'); const libEslint = readJson(`libs/${mylib}/.eslintrc.json`); - // should directly refer to nx plugin only in the root + // should directly refer to nx plugin expect(rootEslint.plugins).toEqual(['@nrwl/nx']); expect(appEslint.plugins).toBeUndefined(); expect(e2eEslint.plugins).toBeUndefined(); - // should extend framework plugin and root config - expect(appEslint.extends).toEqual([ - 'plugin:@nrwl/nx/react', - './.eslintrc.base.json', - ]); - expect(e2eEslint.extends).toEqual([ - 'plugin:cypress/recommended', - '../.eslintrc.base.json', - ]); - expect(libEslint.extends).toEqual([ - 'plugin:@nrwl/nx/react', + expect(libEslint.plugins).toBeUndefined(); + + // should extend base + expect(appEslint.extends.slice(-1)).toEqual(['./.eslintrc.base.json']); + expect(e2eEslint.extends.slice(-1)).toEqual(['../.eslintrc.base.json']); + expect(libEslint.extends.slice(-1)).toEqual([ '../../.eslintrc.base.json', ]); + } + + it('(React standalone) should set root project config to app and e2e app and migrate when another lib is added', () => { + const myapp = uniq('myapp'); + const mylib = uniq('mylib'); + + runCLI(`generate @nrwl/react:app ${myapp} --rootProject=true`); + verifySuccessfulStandaloneSetup(myapp); + + let appEslint = readJson('.eslintrc.json'); + let e2eEslint = readJson('e2e/.eslintrc.json'); + + // should have plugin extends + expect(appEslint.overrides[0].extends).toBeDefined(); + expect(appEslint.overrides[1].extends).toBeDefined(); + expect(e2eEslint.overrides[0].extends).toBeDefined(); + expect(e2eEslint.overrides[1].extends).toBeDefined(); + + runCLI(`generate @nrwl/workspace:lib ${mylib} --unitTestRunner=jest`); + verifySuccessfulMigratedSetup(myapp, mylib); + + appEslint = readJson(`.eslintrc.json`); + e2eEslint = readJson('e2e/.eslintrc.json'); + // should have no plugin extends expect(appEslint.overrides[0].extends).toBeUndefined(); expect(appEslint.overrides[1].extends).toBeUndefined(); expect(e2eEslint.overrides[0].extends).toBeUndefined(); expect(e2eEslint.overrides[1].extends).toBeUndefined(); - expect(libEslint.overrides[1].extends).toBeUndefined(); - expect(libEslint.overrides[1].extends).toBeUndefined(); }); it('(Angular standalone) should set root project config to app and e2e app and migrate when another lib is added', () => { @@ -514,57 +527,23 @@ describe('Linter', () => { runCLI( `generate @nrwl/angular:app ${myapp} --rootProject=true --no-interactive` ); + verifySuccessfulStandaloneSetup(myapp); - let rootEslint = readJson('.eslintrc.json'); + let appEslint = readJson('.eslintrc.json'); let e2eEslint = readJson('e2e/.eslintrc.json'); - expect(() => checkFilesExist(`.eslintrc.base.json`)).toThrow(); - // should directly refer to nx plugin - expect(rootEslint.plugins).toEqual(['@nrwl/nx']); - expect(e2eEslint.plugins).toEqual(['@nrwl/nx']); - // should only extend framework plugin - expect(e2eEslint.extends).toEqual(['plugin:cypress/recommended']); // should have plugin extends - expect(rootEslint.overrides[0].files).toEqual(['*.ts']); - expect(rootEslint.overrides[0].extends).toEqual([ - 'plugin:@nrwl/nx/typescript', - 'plugin:@nrwl/nx/angular', - 'plugin:@angular-eslint/template/process-inline-templates', - ]); - expect(Object.keys(rootEslint.overrides[0].rules)).toEqual([ - '@angular-eslint/directive-selector', - '@angular-eslint/component-selector', - ]); - expect(rootEslint.overrides[1].files).toEqual(['*.html']); - expect(rootEslint.overrides[1].extends).toEqual([ - 'plugin:@nrwl/nx/angular-template', - ]); - expect(e2eEslint.overrides[0].extends).toEqual([ - 'plugin:@nrwl/nx/typescript', - ]); - expect(e2eEslint.overrides[1].extends).toEqual([ - 'plugin:@nrwl/nx/javascript', - ]); + expect(appEslint.overrides[0].extends).toBeDefined(); + expect(appEslint.overrides[1].extends).toBeDefined(); + expect(e2eEslint.overrides[0].extends).toBeDefined(); + expect(e2eEslint.overrides[1].extends).toBeDefined(); - runCLI(`generate @nrwl/angular:lib ${mylib} --no-interactive`); - // should add new tslint - expect(() => checkFilesExist(`.eslintrc.base.json`)).not.toThrow(); - const appEslint = readJson(`.eslintrc.json`); - rootEslint = readJson('.eslintrc.base.json'); + runCLI(`generate @nrwl/workspace:lib ${mylib} --no-interactive`); + verifySuccessfulMigratedSetup(myapp, mylib); + + appEslint = readJson(`.eslintrc.json`); e2eEslint = readJson('e2e/.eslintrc.json'); - const libEslint = readJson(`libs/${mylib}/.eslintrc.json`); - // should directly refer to nx plugin only in the root - expect(rootEslint.plugins).toEqual(['@nrwl/nx']); - expect(appEslint.plugins).toBeUndefined(); - expect(e2eEslint.plugins).toBeUndefined(); - // should extend framework plugin and root config - expect(appEslint.extends).toEqual(['./.eslintrc.base.json']); - expect(e2eEslint.extends).toEqual([ - 'plugin:cypress/recommended', - '../.eslintrc.base.json', - ]); - expect(libEslint.extends).toEqual(['../../.eslintrc.base.json']); // should have no plugin extends expect(appEslint.overrides[0].extends).toEqual([ 'plugin:@nrwl/nx/angular', @@ -572,10 +551,37 @@ describe('Linter', () => { ]); expect(e2eEslint.overrides[0].extends).toBeUndefined(); expect(e2eEslint.overrides[1].extends).toBeUndefined(); - expect(libEslint.overrides[0].extends).toEqual([ - 'plugin:@nrwl/nx/angular', - 'plugin:@angular-eslint/template/process-inline-templates', - ]); + }); + + it('(Node standalone) should set root project config to app and e2e app and migrate when another lib is added', () => { + const myapp = uniq('myapp'); + const mylib = uniq('mylib'); + + runCLI( + `generate @nrwl/node:app ${myapp} --rootProject=true --no-interactive` + ); + verifySuccessfulStandaloneSetup(myapp); + + let appEslint = readJson('.eslintrc.json'); + let e2eEslint = readJson('e2e/.eslintrc.json'); + + // should have plugin extends + expect(appEslint.overrides[0].extends).toBeDefined(); + expect(appEslint.overrides[1].extends).toBeDefined(); + expect(e2eEslint.overrides[0].extends).toBeDefined(); + expect(e2eEslint.overrides[1].extends).toBeDefined(); + + runCLI(`generate @nrwl/workspace:lib ${mylib} --no-interactive`); + verifySuccessfulMigratedSetup(myapp, mylib); + + appEslint = readJson(`.eslintrc.json`); + e2eEslint = readJson('e2e/.eslintrc.json'); + + // should have no plugin extends + expect(appEslint.overrides[0].extends).toBeUndefined(); + expect(appEslint.overrides[1].extends).toBeUndefined(); + expect(e2eEslint.overrides[0].extends).toBeUndefined(); + expect(e2eEslint.overrides[1].extends).toBeUndefined(); }); }); }); diff --git a/packages/node/src/generators/application/application.ts b/packages/node/src/generators/application/application.ts index 26eac2dbf7d59..6027a4bf55d9e 100644 --- a/packages/node/src/generators/application/application.ts +++ b/packages/node/src/generators/application/application.ts @@ -45,6 +45,7 @@ import { e2eProjectGenerator } from '../e2e-project/e2e-project'; import { setupDockerGenerator } from '../setup-docker/setup-docker'; import { Schema } from './schema'; +import { mapLintPattern } from '@nrwl/linter/src/generators/lint-project/lint-project'; export interface NormalizedSchema extends Schema { appProjectRoot: string; @@ -273,7 +274,11 @@ export async function addLintingToApplication( joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'), ], eslintFilePatterns: [ - `${options.appProjectRoot}/**/*.${options.js ? 'js' : 'ts'}`, + mapLintPattern( + options.appProjectRoot, + options.js ? 'js' : 'ts', + options.rootProject + ), ], unitTestRunner: options.unitTestRunner, skipFormat: true, @@ -382,11 +387,8 @@ export async function applicationGenerator(tree: Tree, schema: Schema) { updateTsConfigOptions(tree, options); - if (options.linter !== Linter.None) { - const lintTask = await addLintingToApplication(tree, { - ...options, - skipFormat: true, - }); + if (options.linter === Linter.EsLint) { + const lintTask = await addLintingToApplication(tree, options); tasks.push(lintTask); } diff --git a/packages/node/src/generators/e2e-project/e2e-project.ts b/packages/node/src/generators/e2e-project/e2e-project.ts index cc0c5d8d13190..9389e88038a66 100644 --- a/packages/node/src/generators/e2e-project/e2e-project.ts +++ b/packages/node/src/generators/e2e-project/e2e-project.ts @@ -14,11 +14,17 @@ import { readProjectConfiguration, runTasksInSerial, Tree, + updateJson, } from '@nrwl/devkit'; import { Linter, lintProjectGenerator } from '@nrwl/linter'; import { Schema } from './schema'; import { axiosVersion } from '../../utils/versions'; +import { join } from 'path'; +import { + globalJavaScriptOverrides, + globalTypeScriptOverrides, +} from '@nrwl/linter/src/generators/init/global-eslint-config'; export async function e2eProjectGenerator(host: Tree, _options: Schema) { const tasks: GeneratorCallback[] = []; @@ -90,7 +96,7 @@ export async function e2eProjectGenerator(host: Tree, _options: Schema) { ); tasks.push(installTask); - if (options.linter === 'eslint') { + if (options.linter === Linter.EsLint) { const linterTask = await lintProjectGenerator(host, { project: options.e2eProjectName, linter: Linter.EsLint, @@ -104,6 +110,33 @@ export async function e2eProjectGenerator(host: Tree, _options: Schema) { rootProject: options.rootProject, }); tasks.push(linterTask); + + updateJson(host, join(options.e2eProjectRoot, '.eslintrc.json'), (json) => { + if (options.rootProject) { + json.plugins = ['@nrwl/nx']; + json.extends = []; + } + json.overrides = [ + ...(options.rootProject + ? [globalTypeScriptOverrides, globalJavaScriptOverrides] + : []), + /** + * In order to ensure maximum efficiency when typescript-eslint generates TypeScript Programs + * behind the scenes during lint runs, we need to make sure the project is configured to use its + * own specific tsconfigs, and not fall back to the ones in the root of the workspace. + */ + { + files: ['*.ts', '*.tsx', '*.js', '*.jsx'], + /** + * Having an empty rules object present makes it more obvious to the user where they would + * extend things from if they needed to + */ + rules: {}, + }, + ]; + + return json; + }); } if (options.formatFile) { From 6100064db967a1a95b4a88986453ec03182c7804 Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Wed, 22 Mar 2023 18:39:08 +0000 Subject: [PATCH 27/76] cleanup(angular): add versions util (#15829) --- .../lib/add-angular-eslint-dependencies.ts | 10 +-- packages/angular/src/generators/init/init.ts | 87 ++++++++----------- .../angular/src/generators/library/library.ts | 13 +-- .../ngrx/lib/add-ngrx-to-package-json.ts | 11 +-- .../src/generators/setup-ssr/setup-ssr.ts | 13 ++- .../lib/add-tailwind-required-packages.ts | 13 ++- .../src/generators/utils/dependencies.ts | 16 ++-- .../src/generators/utils/version-utils.ts | 12 +++ 8 files changed, 75 insertions(+), 100 deletions(-) diff --git a/packages/angular/src/generators/add-linting/lib/add-angular-eslint-dependencies.ts b/packages/angular/src/generators/add-linting/lib/add-angular-eslint-dependencies.ts index 7aa32c50fe2df..a2cc132d3ca3d 100644 --- a/packages/angular/src/generators/add-linting/lib/add-angular-eslint-dependencies.ts +++ b/packages/angular/src/generators/add-linting/lib/add-angular-eslint-dependencies.ts @@ -1,15 +1,9 @@ import type { GeneratorCallback, Tree } from '@nrwl/devkit'; import { addDependenciesToPackageJson } from '@nrwl/devkit'; -import { angularEslintVersion } from '../../../utils/versions'; -import { getInstalledAngularVersionInfo } from '../../utils/version-utils'; -import { backwardCompatibleVersions } from '../../../utils/backward-compatible-versions'; +import { versions } from '../../utils/version-utils'; export function addAngularEsLintDependencies(tree: Tree): GeneratorCallback { - const installedAngularVersionInfo = getInstalledAngularVersionInfo(tree); - const angularEslintVersionToInstall = - installedAngularVersionInfo.major === 14 - ? backwardCompatibleVersions.angularV14.angularEslintVersion - : angularEslintVersion; + const angularEslintVersionToInstall = versions(tree).angularEslintVersion; return addDependenciesToPackageJson( tree, {}, diff --git a/packages/angular/src/generators/init/init.ts b/packages/angular/src/generators/init/init.ts index 7c005c1c71958..ce2fc8f161fdf 100755 --- a/packages/angular/src/generators/init/init.ts +++ b/packages/angular/src/generators/init/init.ts @@ -14,20 +14,13 @@ import { jestInitGenerator } from '@nrwl/jest'; import { Linter } from '@nrwl/linter'; import { initGenerator as jsInitGenerator } from '@nrwl/js'; import { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners'; -import { - angularDevkitVersion, - angularVersion, - jestPresetAngularVersion, - rxjsVersion, - tsLibVersion, - zoneJsVersion, -} from '../../utils/versions'; import { addDependenciesToPackageJsonIfDontExist, getInstalledAngularVersionInfo, getInstalledPackageVersion, + versions, } from '../utils/version-utils'; -import { backwardCompatibleVersions } from '../../utils/backward-compatible-versions'; +import type { PackageVersions } from '../../utils/backward-compatible-versions'; import { Schema } from './schema'; export async function angularInitGenerator( @@ -38,6 +31,8 @@ export async function angularInitGenerator( const tasks: GeneratorCallback[] = []; const options = normalizeOptions(rawOptions); + const pkgVersions = versions(tree); + const peerDepsToInstall = [ '@angular-devkit/core', '@angular-devkit/schematics', @@ -50,9 +45,7 @@ export async function angularInitGenerator( if (!packageVersion) { devkitVersion ??= getInstalledPackageVersion(tree, '@angular-devkit/build-angular') ?? - (installedAngularVersionInfo.major === 14 - ? backwardCompatibleVersions.angularV14.angularDevkitVersion - : angularDevkitVersion); + pkgVersions.angularDevkitVersion; try { ensurePackage(pkg, devkitVersion); @@ -78,12 +71,12 @@ export async function angularInitGenerator( tasks.push(jsTask); if (!options.skipPackageJson) { - tasks.push(updateDependencies(tree, installedAngularVersionInfo.major)); + tasks.push(updateDependencies(tree, pkgVersions)); } const unitTestTask = await addUnitTestRunner( tree, options, - installedAngularVersionInfo.major + pkgVersions.jestPresetAngularVersion ); tasks.push(unitTestTask); const e2eTask = await addE2ETestRunner(tree, options); @@ -137,48 +130,41 @@ function setDefaults(host: Tree, options: Schema) { function updateDependencies( tree: Tree, - angularMajorVersion: number + versions: PackageVersions ): GeneratorCallback { - const angularVersionToInstall = + const angularVersion = getInstalledPackageVersion(tree, '@angular/core') ?? - (angularMajorVersion === 14 - ? backwardCompatibleVersions.angularV14.angularVersion - : angularVersion); - const angularDevkitVersionToInstall = + versions.angularVersion; + const angularDevkitVersion = getInstalledPackageVersion(tree, '@angular-devkit/build-angular') ?? - (angularMajorVersion === 14 - ? backwardCompatibleVersions.angularV14.angularDevkitVersion - : angularDevkitVersion); + versions.angularDevkitVersion; + const rxjsVersion = + getInstalledPackageVersion(tree, 'rxjs') ?? versions.rxjsVersion; + const tsLibVersion = + getInstalledPackageVersion(tree, 'tslib') ?? versions.tsLibVersion; + const zoneJsVersion = + getInstalledPackageVersion(tree, 'zone.js') ?? versions.zoneJsVersion; return addDependenciesToPackageJsonIfDontExist( tree, { - '@angular/animations': angularVersionToInstall, - '@angular/common': angularVersionToInstall, - '@angular/compiler': angularVersionToInstall, - '@angular/core': angularVersionToInstall, - '@angular/forms': angularVersionToInstall, - '@angular/platform-browser': angularVersionToInstall, - '@angular/platform-browser-dynamic': angularVersionToInstall, - '@angular/router': angularVersionToInstall, - rxjs: - angularMajorVersion === 14 - ? backwardCompatibleVersions.angularV14.rxjsVersion - : rxjsVersion, - tslib: - angularMajorVersion === 14 - ? backwardCompatibleVersions.angularV14.tsLibVersion - : tsLibVersion, - 'zone.js': - angularMajorVersion === 14 - ? backwardCompatibleVersions.angularV14.zoneJsVersion - : zoneJsVersion, + '@angular/animations': angularVersion, + '@angular/common': angularVersion, + '@angular/compiler': angularVersion, + '@angular/core': angularVersion, + '@angular/forms': angularVersion, + '@angular/platform-browser': angularVersion, + '@angular/platform-browser-dynamic': angularVersion, + '@angular/router': angularVersion, + rxjs: rxjsVersion, + tslib: tsLibVersion, + 'zone.js': zoneJsVersion, }, { - '@angular/cli': angularDevkitVersionToInstall, - '@angular/compiler-cli': angularVersionToInstall, - '@angular/language-service': angularVersionToInstall, - '@angular-devkit/build-angular': angularDevkitVersionToInstall, + '@angular/cli': angularDevkitVersion, + '@angular/compiler-cli': angularVersion, + '@angular/language-service': angularVersion, + '@angular-devkit/build-angular': angularDevkitVersion, } ); } @@ -186,7 +172,7 @@ function updateDependencies( async function addUnitTestRunner( tree: Tree, options: Schema, - angularMajorVersion: number + jestPresetAngularVersion: string ): Promise { switch (options.unitTestRunner) { case UnitTestRunner.Jest: @@ -195,10 +181,7 @@ async function addUnitTestRunner( tree, {}, { - 'jest-preset-angular': - angularMajorVersion === 14 - ? backwardCompatibleVersions.angularV14.jestPresetAngularVersion - : jestPresetAngularVersion, + 'jest-preset-angular': jestPresetAngularVersion, } ); } diff --git a/packages/angular/src/generators/library/library.ts b/packages/angular/src/generators/library/library.ts index d30c4abb4153c..805ff94640752 100644 --- a/packages/angular/src/generators/library/library.ts +++ b/packages/angular/src/generators/library/library.ts @@ -12,10 +12,12 @@ import { updateRootTsConfig } from '@nrwl/js'; import { lt } from 'semver'; import init from '../../generators/init/init'; import { E2eTestRunner } from '../../utils/test-runners'; -import { getPkgVersionForAngularMajorVersion } from '../../utils/version-utils'; import addLintingGenerator from '../add-linting/add-linting'; import setupTailwindGenerator from '../setup-tailwind/setup-tailwind'; -import { getInstalledAngularVersionInfo } from '../utils/version-utils'; +import { + getInstalledAngularVersionInfo, + versions, +} from '../utils/version-utils'; import { addBuildableLibrariesPostCssDependencies } from '../utils/dependencies'; import { addModule } from './lib/add-module'; import { addStandaloneComponent } from './lib/add-standalone-component'; @@ -62,6 +64,8 @@ export async function libraryGenerator( const options = normalizeOptions(tree, schema); const { libraryOptions } = options; + const pkgVersions = versions(tree); + await init(tree, { ...libraryOptions, skipFormat: true, @@ -97,10 +101,7 @@ export async function libraryGenerator( tree, {}, { - 'ng-packagr': getPkgVersionForAngularMajorVersion( - 'ngPackagrVersion', - userInstalledAngularVersion.major - ), + 'ng-packagr': pkgVersions.ngPackagrVersion, } ); addBuildableLibrariesPostCssDependencies(tree); diff --git a/packages/angular/src/generators/ngrx/lib/add-ngrx-to-package-json.ts b/packages/angular/src/generators/ngrx/lib/add-ngrx-to-package-json.ts index 8bf4dbbc0f570..7e9b273493206 100644 --- a/packages/angular/src/generators/ngrx/lib/add-ngrx-to-package-json.ts +++ b/packages/angular/src/generators/ngrx/lib/add-ngrx-to-package-json.ts @@ -2,9 +2,8 @@ import type { GeneratorCallback, Tree } from '@nrwl/devkit'; import { addDependenciesToPackageJson, readJson } from '@nrwl/devkit'; import { checkAndCleanWithSemver } from '@nrwl/devkit/src/utils/semver'; import { gte } from 'semver'; -import { getPkgVersionForAngularMajorVersion } from '../../../utils/version-utils'; import { rxjsVersion as defaultRxjsVersion } from '../../../utils/versions'; -import { getInstalledAngularMajorVersion } from '../../utils/version-utils'; +import { versions } from '../../utils/version-utils'; export function addNgRxToPackageJson(tree: Tree): GeneratorCallback { let rxjsVersion: string; @@ -16,13 +15,9 @@ export function addNgRxToPackageJson(tree: Tree): GeneratorCallback { } catch { rxjsVersion = checkAndCleanWithSemver('rxjs', defaultRxjsVersion); } - const jasmineMarblesVersion = gte(rxjsVersion, '7.0.0') ? '~0.9.1' : '~0.8.3'; - const angularMajorVersion = getInstalledAngularMajorVersion(tree); - const ngrxVersion = getPkgVersionForAngularMajorVersion( - 'ngrxVersion', - angularMajorVersion - ); + const jasmineMarblesVersion = gte(rxjsVersion, '7.0.0') ? '~0.9.1' : '~0.8.3'; + const ngrxVersion = versions(tree).ngrxVersion; return addDependenciesToPackageJson( tree, diff --git a/packages/angular/src/generators/setup-ssr/setup-ssr.ts b/packages/angular/src/generators/setup-ssr/setup-ssr.ts index 27a820acb7130..509742cfe66ef 100644 --- a/packages/angular/src/generators/setup-ssr/setup-ssr.ts +++ b/packages/angular/src/generators/setup-ssr/setup-ssr.ts @@ -4,8 +4,7 @@ import { formatFiles, installPackagesTask, } from '@nrwl/devkit'; -import { getPkgVersionsForAngularMajorVersion } from '../../utils/version-utils'; -import { getInstalledAngularVersionInfo } from '../utils/version-utils'; +import { versions } from '../utils/version-utils'; import { generateSSRFiles, normalizeOptions, @@ -21,18 +20,16 @@ export async function setupSsr(tree: Tree, schema: Schema) { updateAppModule(tree, options); updateProjectConfig(tree, options); - const installedAngularVersion = getInstalledAngularVersionInfo(tree); - const { angularVersion: ngPlatformServerVersion, ngUniversalVersion } = - getPkgVersionsForAngularMajorVersion(installedAngularVersion.major); + const pkgVersions = versions(tree); addDependenciesToPackageJson( tree, { - '@nguniversal/express-engine': ngUniversalVersion, - '@angular/platform-server': ngPlatformServerVersion, + '@nguniversal/express-engine': pkgVersions.ngUniversalVersion, + '@angular/platform-server': pkgVersions.angularVersion, }, { - '@nguniversal/builders': ngUniversalVersion, + '@nguniversal/builders': pkgVersions.ngUniversalVersion, } ); diff --git a/packages/angular/src/generators/setup-tailwind/lib/add-tailwind-required-packages.ts b/packages/angular/src/generators/setup-tailwind/lib/add-tailwind-required-packages.ts index d832458c27938..9c3d881d0d797 100644 --- a/packages/angular/src/generators/setup-tailwind/lib/add-tailwind-required-packages.ts +++ b/packages/angular/src/generators/setup-tailwind/lib/add-tailwind-required-packages.ts @@ -3,20 +3,17 @@ import { GeneratorCallback, Tree, } from '@nrwl/devkit'; -import { - autoprefixerVersion, - postcssVersion, - tailwindVersion, -} from '../../../utils/versions'; +import { versions } from '../../utils/version-utils'; export function addTailwindRequiredPackages(tree: Tree): GeneratorCallback { + const pkgVersions = versions(tree); return addDependenciesToPackageJson( tree, {}, { - autoprefixer: autoprefixerVersion, - postcss: postcssVersion, - tailwindcss: tailwindVersion, + autoprefixer: pkgVersions.autoprefixerVersion, + postcss: pkgVersions.postcssVersion, + tailwindcss: pkgVersions.tailwindVersion, } ); } diff --git a/packages/angular/src/generators/utils/dependencies.ts b/packages/angular/src/generators/utils/dependencies.ts index b919bac7a068f..9eeb6dc7d44b2 100644 --- a/packages/angular/src/generators/utils/dependencies.ts +++ b/packages/angular/src/generators/utils/dependencies.ts @@ -1,20 +1,16 @@ import { addDependenciesToPackageJson, Tree } from '@nrwl/devkit'; -import { - postcssImportVersion, - postcssPresetEnvVersion, - postcssUrlVersion, - postcssVersion, -} from '../../utils/versions'; +import { versions } from './version-utils'; export function addBuildableLibrariesPostCssDependencies(tree: Tree): void { + const pkgVersions = versions(tree); addDependenciesToPackageJson( tree, {}, { - postcss: postcssVersion, - 'postcss-import': postcssImportVersion, - 'postcss-preset-env': postcssPresetEnvVersion, - 'postcss-url': postcssUrlVersion, + postcss: pkgVersions.postcssVersion, + 'postcss-import': pkgVersions.postcssImportVersion, + 'postcss-preset-env': pkgVersions.postcssPresetEnvVersion, + 'postcss-url': pkgVersions.postcssUrlVersion, } ); } diff --git a/packages/angular/src/generators/utils/version-utils.ts b/packages/angular/src/generators/utils/version-utils.ts index 069d1da359aa4..b571acb3c3414 100644 --- a/packages/angular/src/generators/utils/version-utils.ts +++ b/packages/angular/src/generators/utils/version-utils.ts @@ -1,7 +1,9 @@ import type { GeneratorCallback, Tree } from '@nrwl/devkit'; import { addDependenciesToPackageJson, readJson } from '@nrwl/devkit'; import { clean, coerce, major } from 'semver'; +import * as latestVersions from '../../utils/versions'; import { angularVersion } from '../../utils/versions'; +import { backwardCompatibleVersions } from '../../utils/backward-compatible-versions'; export function getGeneratorDirectoryForInstalledAngularVersion( tree: Tree @@ -91,3 +93,13 @@ export function addDependenciesToPackageJsonIfDontExist( packageJsonPath ); } + +export function versions(tree: Tree) { + const majorAngularVersion = getInstalledAngularMajorVersion(tree); + switch (majorAngularVersion) { + case 14: + return backwardCompatibleVersions.angularV14; + default: + return latestVersions; + } +} From b124b97c36b62470ef240c57dbc4d5f9f8d8c19d Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Wed, 22 Mar 2023 14:53:02 -0400 Subject: [PATCH 28/76] feat(testing): add static serve target for e2e tests in CI (#15808) --- e2e/web/src/file-server.test.ts | 8 +++- .../__snapshots__/application.spec.ts.snap | 18 ++++++++ .../src/generators/application/lib/add-e2e.ts | 24 +++++++++++ .../cypress-project/cypress-project.spec.ts | 42 +++++++++++++++++++ .../cypress-project/cypress-project.ts | 5 +++ .../generators/application/lib/add-cypress.ts | 8 +++- packages/web/index.ts | 1 + 7 files changed, 103 insertions(+), 3 deletions(-) diff --git a/e2e/web/src/file-server.test.ts b/e2e/web/src/file-server.test.ts index 29c428bc114ee..927a602009f8a 100644 --- a/e2e/web/src/file-server.test.ts +++ b/e2e/web/src/file-server.test.ts @@ -44,8 +44,12 @@ describe('file-server', () => { const ngAppName = uniq('ng-app'); const reactAppName = uniq('react-app'); - runCLI(`generate @nrwl/angular:app ${ngAppName} --no-interactive`); - runCLI(`generate @nrwl/react:app ${reactAppName} --no-interactive`); + runCLI( + `generate @nrwl/angular:app ${ngAppName} --no-interactive --e2eTestRunner=none` + ); + runCLI( + `generate @nrwl/react:app ${reactAppName} --no-interactive --e2eTestRunner=none` + ); runCLI( `generate @nrwl/web:static-config --buildTarget=${ngAppName}:build --no-interactive` ); diff --git a/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap b/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap index a620a9525a31a..b94b0842d739d 100644 --- a/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap +++ b/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap @@ -474,6 +474,12 @@ Object { "defaultConfiguration": "development", "executor": "@angular-devkit/build-angular:dev-server", }, + "serve-static": Object { + "executor": "@nrwl/angular:file-server", + "options": Object { + "buildTarget": "my-dir-my-app:build", + }, + }, "test": Object { "configurations": Object { "ci": Object { @@ -508,6 +514,9 @@ Object { "targets": Object { "e2e": Object { "configurations": Object { + "ci": Object { + "devServerTarget": "my-dir-my-app:serve-static", + }, "production": Object { "devServerTarget": "my-dir-my-app:serve:production", }, @@ -623,6 +632,12 @@ Object { "defaultConfiguration": "development", "executor": "@angular-devkit/build-angular:dev-server", }, + "serve-static": Object { + "executor": "@nrwl/angular:file-server", + "options": Object { + "buildTarget": "my-app:build", + }, + }, "test": Object { "configurations": Object { "ci": Object { @@ -657,6 +672,9 @@ Object { "targets": Object { "e2e": Object { "configurations": Object { + "ci": Object { + "devServerTarget": "my-app:serve-static", + }, "production": Object { "devServerTarget": "my-app:serve:production", }, diff --git a/packages/angular/src/generators/application/lib/add-e2e.ts b/packages/angular/src/generators/application/lib/add-e2e.ts index d5503fdfa9128..bf3b1104cbdb4 100644 --- a/packages/angular/src/generators/application/lib/add-e2e.ts +++ b/packages/angular/src/generators/application/lib/add-e2e.ts @@ -1,12 +1,20 @@ import type { Tree } from '@nrwl/devkit'; import type { NormalizedSchema } from './normalized-schema'; +import { + readProjectConfiguration, + updateProjectConfiguration, +} from '@nrwl/devkit'; import { cypressProjectGenerator } from '@nrwl/cypress'; import { removeScaffoldedE2e } from './remove-scaffolded-e2e'; export async function addE2e(tree: Tree, options: NormalizedSchema) { removeScaffoldedE2e(tree, options, options.ngCliSchematicE2ERoot); + if (options.e2eTestRunner === 'cypress') { + // TODO: This can call `@nrwl/web:static-config` generator once we merge `@nrwl/angular:file-server` into `@nrwl/web:file-server`. + addFileServerTarget(tree, options, 'serve-static'); + await cypressProjectGenerator(tree, { name: options.e2eProjectName, directory: options.directory, @@ -19,3 +27,19 @@ export async function addE2e(tree: Tree, options: NormalizedSchema) { }); } } + +function addFileServerTarget( + tree: Tree, + options: NormalizedSchema, + targetName: string +) { + const projectConfig = readProjectConfiguration(tree, options.name); + projectConfig.targets[targetName] = { + executor: '@nrwl/angular:file-server', + options: { + buildTarget: `${options.name}:build`, + port: options.port, + }, + }; + updateProjectConfiguration(tree, options.name, projectConfig); +} diff --git a/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts b/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts index 13a9952ae7805..cd52cc3dba4f2 100644 --- a/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts +++ b/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts @@ -354,6 +354,48 @@ describe('Cypress Project', () => { 'apps/one/two/other-e2e/src/e2e/app.cy.ts', ].forEach((path) => expect(tree.exists(path)).toBeTruthy()); }); + + describe('serve-static', () => { + it('should configure Cypress with ci configuration if serve-static is found', async () => { + const appConfig = readProjectConfiguration(tree, 'my-app'); + appConfig.targets['serve-static'] = { + executor: 'serve-static-executor', + options: {}, + configurations: { + production: {}, + }, + }; + updateProjectConfiguration(tree, 'my-app', appConfig); + + await cypressProjectGenerator(tree, { + ...defaultOptions, + name: 'my-app-e2e', + project: 'my-app', + }); + + const e2eConfig = readProjectConfiguration(tree, 'my-app-e2e'); + expect(e2eConfig.targets.e2e).toMatchObject({ + options: { + devServerTarget: 'my-app:serve', + }, + configurations: { + production: { devServerTarget: 'my-app:serve:production' }, + ci: { devServerTarget: 'my-app:serve-static' }, + }, + }); + }); + + it('should not configure Cypress with ci configuration if serve-static is not found', async () => { + await cypressProjectGenerator(tree, { + ...defaultOptions, + name: 'my-app-e2e', + project: 'my-app', + }); + + const e2eConfig = readProjectConfiguration(tree, 'my-app-e2e'); + expect(e2eConfig.targets.e2e.configurations.ci).toBeUndefined(); + }); + }); }); describe('v9 - v7', () => { diff --git a/packages/cypress/src/generators/cypress-project/cypress-project.ts b/packages/cypress/src/generators/cypress-project/cypress-project.ts index a317ea5a94862..643ef7546e674 100644 --- a/packages/cypress/src/generators/cypress-project/cypress-project.ts +++ b/packages/cypress/src/generators/cypress-project/cypress-project.ts @@ -154,6 +154,11 @@ function addProject(tree: Tree, options: CypressProjectSchema) { tags: [], implicitDependencies: options.project ? [options.project] : undefined, }; + if (project.targets?.['serve-static']) { + e2eProjectConfig.targets.e2e.configurations.ci = { + devServerTarget: `${options.project}:serve-static`, + }; + } } else { throw new Error(`Either project or baseUrl should be specified.`); } diff --git a/packages/react/src/generators/application/lib/add-cypress.ts b/packages/react/src/generators/application/lib/add-cypress.ts index 45ed092e0ce76..18ada80ec827d 100644 --- a/packages/react/src/generators/application/lib/add-cypress.ts +++ b/packages/react/src/generators/application/lib/add-cypress.ts @@ -1,4 +1,4 @@ -import { ensurePackage, Tree } from '@nrwl/devkit'; +import { ensurePackage, joinPathFragments, Tree } from '@nrwl/devkit'; import { nxVersion } from '../../../utils/versions'; import { NormalizedSchema } from '../schema'; @@ -7,6 +7,12 @@ export async function addCypress(host: Tree, options: NormalizedSchema) { return () => {}; } + const { webStaticServeGenerator } = ensurePackage('@nrwl/web', nxVersion); + await webStaticServeGenerator(host, { + buildTarget: `${options.projectName}:build`, + targetName: 'serve-static', + }); + const { cypressProjectGenerator } = ensurePackage('@nrwl/cypress', nxVersion); return await cypressProjectGenerator(host, { diff --git a/packages/web/index.ts b/packages/web/index.ts index 5bdcea824fe08..6a66b3065b7b6 100644 --- a/packages/web/index.ts +++ b/packages/web/index.ts @@ -1,2 +1,3 @@ export { webInitGenerator } from './src/generators/init/init'; export { applicationGenerator } from './src/generators/application/application'; +export { webStaticServeGenerator } from './src/generators/static-serve/static-serve-configuration'; From b3dd65adbc8ebd386ed0eddbd67fcdc1d8b3fa48 Mon Sep 17 00:00:00 2001 From: Caleb Ukle Date: Wed, 22 Mar 2023 14:38:32 -0700 Subject: [PATCH 29/76] feat(storybook): add configureStaticServe option (#15688) --- .../generators/storybook-configuration.json | 7 ++ .../generators/storybook-configuration.json | 7 ++ .../storybook/generators/configuration.json | 5 ++ .../storybook/generators/cypress-project.json | 5 ++ .../lib/generate-storybook-configuration.ts | 1 + .../storybook-configuration/schema.d.ts | 1 + .../storybook-configuration/schema.json | 7 ++ .../storybook-configuration/configuration.ts | 1 + .../storybook-configuration/schema.d.ts | 1 + .../storybook-configuration/schema.json | 7 ++ .../configuration/configuration.spec.ts | 78 +++++++++++++++++++ .../generators/configuration/configuration.ts | 61 ++++++--------- .../src/generators/configuration/schema.d.ts | 1 + .../src/generators/configuration/schema.json | 5 ++ .../configuration/util-functions.ts | 24 ++++++ .../cypress-project/cypress-project.ts | 24 ++++-- .../generators/cypress-project/schema.json | 5 ++ .../migrate-stories-to-6-2.spec.ts | 1 + .../storybook/src/utils/utilities.spec.ts | 1 + scripts/depcheck/missing.ts | 2 + 20 files changed, 198 insertions(+), 46 deletions(-) diff --git a/docs/generated/packages/angular/generators/storybook-configuration.json b/docs/generated/packages/angular/generators/storybook-configuration.json index 3fbd6fbc09f58..0cd789743b444 100644 --- a/docs/generated/packages/angular/generators/storybook-configuration.json +++ b/docs/generated/packages/angular/generators/storybook-configuration.json @@ -39,6 +39,13 @@ "default": true, "x-priority": "important" }, + "configureStaticServe": { + "type": "boolean", + "description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.", + "x-prompt": "Configure a static file server for the storybook instance?", + "default": true, + "x-priority": "important" + }, "cypressDirectory": { "type": "string", "description": "A directory where the Cypress project will be placed. Placed at the root by default." diff --git a/docs/generated/packages/react/generators/storybook-configuration.json b/docs/generated/packages/react/generators/storybook-configuration.json index 5d50ee9113a5a..7b5b14257ce2d 100644 --- a/docs/generated/packages/react/generators/storybook-configuration.json +++ b/docs/generated/packages/react/generators/storybook-configuration.json @@ -39,6 +39,13 @@ "default": true, "x-priority": "important" }, + "configureStaticServe": { + "type": "boolean", + "description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.", + "x-prompt": "Configure a static file server for the storybook instance?", + "default": true, + "x-priority": "important" + }, "cypressDirectory": { "type": "string", "description": "A directory where the Cypress project will be placed. Placed at the root by default." diff --git a/docs/generated/packages/storybook/generators/configuration.json b/docs/generated/packages/storybook/generators/configuration.json index 1fcdb800b2933..62e1dd54992bf 100644 --- a/docs/generated/packages/storybook/generators/configuration.json +++ b/docs/generated/packages/storybook/generators/configuration.json @@ -67,6 +67,11 @@ "default": true, "x-deprecated": "Nx only supports standaloneConfig" }, + "configureStaticServe": { + "type": "boolean", + "description": "Add a static-storybook to serve the static storybook built files.", + "default": false + }, "configureTestRunner": { "type": "boolean", "description": "Add a Storybook Test-Runner target." diff --git a/docs/generated/packages/storybook/generators/cypress-project.json b/docs/generated/packages/storybook/generators/cypress-project.json index 1ea695c20001a..f60f2b8ef4ea2 100644 --- a/docs/generated/packages/storybook/generators/cypress-project.json +++ b/docs/generated/packages/storybook/generators/cypress-project.json @@ -38,6 +38,11 @@ "type": "boolean", "default": true, "x-deprecated": "Nx only supports standaloneConfig" + }, + "ciTargetName": { + "type": "string", + "description": "The name of the devServerTarget to use for the Cypress CI configuration. Used to control if using :static-storybook:ci or :storybook:ci", + "x-priority": "internal" } }, "required": ["name"], diff --git a/packages/angular/src/generators/storybook-configuration/lib/generate-storybook-configuration.ts b/packages/angular/src/generators/storybook-configuration/lib/generate-storybook-configuration.ts index bdd65155cc7ce..12ac0c87cf801 100644 --- a/packages/angular/src/generators/storybook-configuration/lib/generate-storybook-configuration.ts +++ b/packages/angular/src/generators/storybook-configuration/lib/generate-storybook-configuration.ts @@ -19,5 +19,6 @@ export async function generateStorybookConfiguration( tsConfiguration: options.tsConfiguration, configureTestRunner: options.configureTestRunner, storybook7Configuration: options.storybook7Configuration, + configureStaticServe: options.configureStaticServe, }); } diff --git a/packages/angular/src/generators/storybook-configuration/schema.d.ts b/packages/angular/src/generators/storybook-configuration/schema.d.ts index d033b36bd0b9d..02666e6a5196a 100644 --- a/packages/angular/src/generators/storybook-configuration/schema.d.ts +++ b/packages/angular/src/generators/storybook-configuration/schema.d.ts @@ -2,6 +2,7 @@ import type { Linter } from '@nrwl/linter'; export interface StorybookConfigurationOptions { configureCypress: boolean; + configureStaticServe?: boolean; generateCypressSpecs: boolean; generateStories: boolean; linter: Linter; diff --git a/packages/angular/src/generators/storybook-configuration/schema.json b/packages/angular/src/generators/storybook-configuration/schema.json index 25da6be6cf3ea..3c521d94cc167 100644 --- a/packages/angular/src/generators/storybook-configuration/schema.json +++ b/packages/angular/src/generators/storybook-configuration/schema.json @@ -39,6 +39,13 @@ "default": true, "x-priority": "important" }, + "configureStaticServe": { + "type": "boolean", + "description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.", + "x-prompt": "Configure a static file server for the storybook instance?", + "default": true, + "x-priority": "important" + }, "cypressDirectory": { "type": "string", "description": "A directory where the Cypress project will be placed. Placed at the root by default." diff --git a/packages/react/src/generators/storybook-configuration/configuration.ts b/packages/react/src/generators/storybook-configuration/configuration.ts index 8f8cca9dfb619..1ed9e918a5378 100644 --- a/packages/react/src/generators/storybook-configuration/configuration.ts +++ b/packages/react/src/generators/storybook-configuration/configuration.ts @@ -65,6 +65,7 @@ export async function storybookConfigurationGenerator( standaloneConfig: schema.standaloneConfig, tsConfiguration: schema.tsConfiguration, configureTestRunner: schema.configureTestRunner, + configureStaticServe: schema.configureStaticServe, bundler, storybook7Configuration: schema.storybook7Configuration, storybook7UiFramework: diff --git a/packages/react/src/generators/storybook-configuration/schema.d.ts b/packages/react/src/generators/storybook-configuration/schema.d.ts index 9d5875305ebd7..ebb38ada921b4 100644 --- a/packages/react/src/generators/storybook-configuration/schema.d.ts +++ b/packages/react/src/generators/storybook-configuration/schema.d.ts @@ -13,5 +13,6 @@ export interface StorybookConfigureSchema { ignorePaths?: string[]; bundler?: 'webpack' | 'vite'; configureTestRunner?: boolean; + configureStaticServe?: boolean; storybook7Configuration?: boolean; } diff --git a/packages/react/src/generators/storybook-configuration/schema.json b/packages/react/src/generators/storybook-configuration/schema.json index 44d5cfe9393f8..f113909008db8 100644 --- a/packages/react/src/generators/storybook-configuration/schema.json +++ b/packages/react/src/generators/storybook-configuration/schema.json @@ -39,6 +39,13 @@ "default": true, "x-priority": "important" }, + "configureStaticServe": { + "type": "boolean", + "description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.", + "x-prompt": "Configure a static file server for the storybook instance?", + "default": true, + "x-priority": "important" + }, "cypressDirectory": { "type": "string", "description": "A directory where the Cypress project will be placed. Placed at the root by default." diff --git a/packages/storybook/src/generators/configuration/configuration.spec.ts b/packages/storybook/src/generators/configuration/configuration.spec.ts index 178b1ca50f1f5..cb5ad298af525 100644 --- a/packages/storybook/src/generators/configuration/configuration.spec.ts +++ b/packages/storybook/src/generators/configuration/configuration.spec.ts @@ -11,6 +11,7 @@ import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; import { Linter } from '@nrwl/linter'; import { libraryGenerator } from '@nrwl/workspace/generators'; +import { nxVersion } from '../../utils/versions'; import { TsConfig } from '../../utils/utilities'; import configurationGenerator from './configuration'; import * as workspaceConfiguration from './test-configs/workspace-conifiguration.json'; @@ -42,6 +43,7 @@ describe('@nrwl/storybook:configuration', () => { devDependencies: { '@storybook/addon-essentials': '~6.2.9', '@storybook/react': '~6.2.9', + '@nrwl/web': nxVersion, }, }); }); @@ -328,6 +330,82 @@ describe('@nrwl/storybook:configuration', () => { }, }); }); + + it('should add static-storybook target', async () => { + await configurationGenerator(tree, { + name: 'test-ui-lib', + uiFramework: '@storybook/react', + configureStaticServe: true, + }); + + expect( + readProjectConfiguration(tree, 'test-ui-lib').targets[ + 'static-storybook' + ] + ).toMatchInlineSnapshot(` + Object { + "configurations": Object { + "ci": Object { + "buildTarget": "test-ui-lib:build-storybook:ci", + }, + }, + "executor": "@nrwl/web:file-server", + "options": Object { + "buildTarget": "test-ui-lib:build-storybook", + "staticFilePath": "dist/storybook/test-ui-lib", + }, + } + `); + expect( + readJson(tree, 'package.json').devDependencies['@nrwl/web'] + ).toBeTruthy(); + }); + it('should use static-storybook:ci in cypress project', async () => { + await configurationGenerator(tree, { + name: 'test-ui-lib', + uiFramework: '@storybook/react', + configureStaticServe: true, + configureCypress: true, + }); + + expect( + readProjectConfiguration(tree, 'test-ui-lib').targets[ + 'static-storybook' + ] + ).toMatchInlineSnapshot(` + Object { + "configurations": Object { + "ci": Object { + "buildTarget": "test-ui-lib:build-storybook:ci", + }, + }, + "executor": "@nrwl/web:file-server", + "options": Object { + "buildTarget": "test-ui-lib:build-storybook", + "staticFilePath": "dist/storybook/test-ui-lib", + }, + } + `); + expect(readProjectConfiguration(tree, 'test-ui-lib-e2e').targets.e2e) + .toMatchInlineSnapshot(` + Object { + "configurations": Object { + "ci": Object { + "devServerTarget": "test-ui-lib:static-storybook:ci", + }, + }, + "executor": "@nrwl/cypress:cypress", + "options": Object { + "cypressConfig": "apps/test-ui-lib-e2e/cypress.config.ts", + "devServerTarget": "test-ui-lib:storybook", + "testingType": "e2e", + }, + } + `); + expect( + readJson(tree, 'package.json').devDependencies['@nrwl/web'] + ).toBeTruthy(); + }); }); describe('for other types of projects - Next.js and the swc compiler', () => { diff --git a/packages/storybook/src/generators/configuration/configuration.ts b/packages/storybook/src/generators/configuration/configuration.ts index a7eb8631d001b..1342e78804552 100644 --- a/packages/storybook/src/generators/configuration/configuration.ts +++ b/packages/storybook/src/generators/configuration/configuration.ts @@ -16,6 +16,7 @@ import { initGenerator } from '../init/init'; import { addAngularStorybookTask, addBuildStorybookToCacheableOperations, + addStaticTarget, addStorybookTask, addStorybookToNamedInputs, configureTsProjectConfig, @@ -32,6 +33,7 @@ import { isStorybookV7, } from '../../utils/utilities'; import { + nxVersion, storybookNextAddonVersion, storybookSwcAddonVersion, storybookTestRunnerVersion, @@ -180,6 +182,10 @@ export async function configurationGenerator( ); } + if (schema.configureStaticServe) { + addStaticTarget(tree, schema); + } + const e2eProject = await getE2EProjectName(tree, schema.name); if (schema.configureCypress && !e2eProject) { const cypressTask = await cypressProjectGenerator(tree, { @@ -188,6 +194,9 @@ export async function configurationGenerator( linter: schema.linter, directory: schema.cypressDirectory, standaloneConfig: schema.standaloneConfig, + ciTargetName: schema.configureStaticServe + ? 'static-storybook' + : undefined, }); tasks.push(cypressTask); } else { @@ -196,57 +205,31 @@ export async function configurationGenerator( ); } + const devDeps = {}; + if (schema.tsConfiguration) { - tasks.push( - addDependenciesToPackageJson( - tree, - {}, - { - ['@storybook/core-common']: storybookVersion, - ['ts-node']: tsNodeVersion, - } - ) - ); + devDeps['@storybook/core-common'] = storybookVersion; + devDeps['ts-node'] = tsNodeVersion; } - if ( nextBuildTarget && projectType === 'application' && !schema.storybook7Configuration ) { - tasks.push( - addDependenciesToPackageJson( - tree, - {}, - { - ['storybook-addon-next']: storybookNextAddonVersion, - ['storybook-addon-swc']: storybookSwcAddonVersion, - } - ) - ); + devDeps['storybook-addon-next'] = storybookNextAddonVersion; + devDeps['storybook-addon-swc'] = storybookSwcAddonVersion; } else if (compiler === 'swc') { - tasks.push( - addDependenciesToPackageJson( - tree, - {}, - { - ['storybook-addon-swc']: storybookSwcAddonVersion, - } - ) - ); + devDeps['storybook-addon-swc'] = storybookSwcAddonVersion; } if (schema.configureTestRunner === true) { - tasks.push( - addDependenciesToPackageJson( - tree, - {}, - { - '@storybook/test-runner': storybookTestRunnerVersion, - } - ) - ); + devDeps['@storybook/test-runner'] = storybookTestRunnerVersion; } + if (schema.configureStaticServe) { + devDeps['@nrwl/web'] = nxVersion; + } + + tasks.push(addDependenciesToPackageJson(tree, {}, devDeps)); await formatFiles(tree); diff --git a/packages/storybook/src/generators/configuration/schema.d.ts b/packages/storybook/src/generators/configuration/schema.d.ts index 45700b29fe796..c5d6cc60c4238 100644 --- a/packages/storybook/src/generators/configuration/schema.d.ts +++ b/packages/storybook/src/generators/configuration/schema.d.ts @@ -12,6 +12,7 @@ export interface StorybookConfigureSchema { cypressDirectory?: string; standaloneConfig?: boolean; configureTestRunner?: boolean; + configureStaticServe?: boolean; storybook7Configuration?: boolean; // TODO(katerina): Change when Storybook 7 storybook7UiFramework?: UiFramework7; // TODO(katerina): Change when Storybook 7 } diff --git a/packages/storybook/src/generators/configuration/schema.json b/packages/storybook/src/generators/configuration/schema.json index 7a2302f12d1e6..e6c5c53c6b667 100644 --- a/packages/storybook/src/generators/configuration/schema.json +++ b/packages/storybook/src/generators/configuration/schema.json @@ -67,6 +67,11 @@ "default": true, "x-deprecated": "Nx only supports standaloneConfig" }, + "configureStaticServe": { + "type": "boolean", + "description": "Add a static-storybook to serve the static storybook built files.", + "default": false + }, "configureTestRunner": { "type": "boolean", "description": "Add a Storybook Test-Runner target." diff --git a/packages/storybook/src/generators/configuration/util-functions.ts b/packages/storybook/src/generators/configuration/util-functions.ts index 64f17a2ac6525..c8d7b681d1c93 100644 --- a/packages/storybook/src/generators/configuration/util-functions.ts +++ b/packages/storybook/src/generators/configuration/util-functions.ts @@ -1,5 +1,6 @@ import { createProjectGraphAsync, + ensurePackage, generateFiles, joinPathFragments, logger, @@ -26,6 +27,7 @@ import { } from '../../utils/utilities'; import { StorybookConfigureSchema } from './schema'; import { UiFramework, UiFramework7 } from '../../utils/models'; +import { nxVersion } from '../../utils/versions'; const DEFAULT_PORT = 4400; @@ -140,6 +142,28 @@ export function addAngularStorybookTask( updateProjectConfiguration(tree, projectName, projectConfig); } +export function addStaticTarget(tree: Tree, opts: StorybookConfigureSchema) { + const nrwlWeb = ensurePackage( + '@nrwl/web', + nxVersion + ); + nrwlWeb.webStaticServeGenerator(tree, { + buildTarget: `${opts.name}:build-storybook`, + outputPath: joinPathFragments('dist/storybook', opts.name), + targetName: 'static-storybook', + }); + + const projectConfig = readProjectConfiguration(tree, opts.name); + + projectConfig.targets['static-storybook'].configurations = { + ci: { + buildTarget: `${opts.name}:build-storybook:ci`, + }, + }; + + updateProjectConfiguration(tree, opts.name, projectConfig); +} + export function configureTsProjectConfig( tree: Tree, schema: StorybookConfigureSchema diff --git a/packages/storybook/src/generators/cypress-project/cypress-project.ts b/packages/storybook/src/generators/cypress-project/cypress-project.ts index f55ca7497012e..443c7aa3e142b 100644 --- a/packages/storybook/src/generators/cypress-project/cypress-project.ts +++ b/packages/storybook/src/generators/cypress-project/cypress-project.ts @@ -30,6 +30,7 @@ export interface CypressConfigureSchema { directory?: string; linter: Linter; standaloneConfig?: boolean; + ciTargetName?: string; } export async function cypressProjectGenerator( @@ -65,7 +66,11 @@ export async function cypressProjectGenerator( ); removeUnneededFiles(tree, generatedCypressProjectName, schema.js); addBaseUrlToCypressConfig(tree, generatedCypressProjectName); - updateAngularJsonBuilder(tree, generatedCypressProjectName, schema.name); + updateAngularJsonBuilder(tree, { + e2eProjectName: generatedCypressProjectName, + targetProjectName: schema.name, + ciTargetName: schema.ciTargetName, + }); await formatFiles(tree); @@ -107,24 +112,29 @@ function addBaseUrlToCypressConfig(tree: Tree, projectName: string) { function updateAngularJsonBuilder( tree: Tree, - e2eProjectName: string, - targetProjectName: string + opts: { + e2eProjectName: string; + targetProjectName: string; + ciTargetName?: string; + } ) { - const project = readProjectConfiguration(tree, e2eProjectName); + const project = readProjectConfiguration(tree, opts.e2eProjectName); const e2eTarget = project.targets.e2e; project.targets.e2e = { ...e2eTarget, options: { ...e2eTarget.options, - devServerTarget: `${targetProjectName}:storybook`, + devServerTarget: `${opts.targetProjectName}:storybook`, }, configurations: { ci: { - devServerTarget: `${targetProjectName}:storybook:ci`, + devServerTarget: opts.ciTargetName + ? `${opts.targetProjectName}:${opts.ciTargetName}:ci` + : `${opts.targetProjectName}:storybook:ci`, }, }, }; - updateProjectConfiguration(tree, e2eProjectName, project); + updateProjectConfiguration(tree, opts.e2eProjectName, project); } function projectAlreadyHasCypress(tree: Tree): boolean { diff --git a/packages/storybook/src/generators/cypress-project/schema.json b/packages/storybook/src/generators/cypress-project/schema.json index 98f25d2b15b73..9f95c85d25c0d 100644 --- a/packages/storybook/src/generators/cypress-project/schema.json +++ b/packages/storybook/src/generators/cypress-project/schema.json @@ -38,6 +38,11 @@ "type": "boolean", "default": true, "x-deprecated": "Nx only supports standaloneConfig" + }, + "ciTargetName": { + "type": "string", + "description": "The name of the devServerTarget to use for the Cypress CI configuration. Used to control if using :static-storybook:ci or :storybook:ci", + "x-priority": "internal" } }, "required": ["name"] diff --git a/packages/storybook/src/migrations/update-14-0-0/migrate-stories-to-6-2/migrate-stories-to-6-2.spec.ts b/packages/storybook/src/migrations/update-14-0-0/migrate-stories-to-6-2/migrate-stories-to-6-2.spec.ts index 73f8f7f508f70..83369028e6a2b 100644 --- a/packages/storybook/src/migrations/update-14-0-0/migrate-stories-to-6-2/migrate-stories-to-6-2.spec.ts +++ b/packages/storybook/src/migrations/update-14-0-0/migrate-stories-to-6-2/migrate-stories-to-6-2.spec.ts @@ -74,6 +74,7 @@ describe('migrate-stories-to-6-2 schematic', () => { await runAngularStorybookSchematic(appTree, { name: 'test-ui-lib', configureCypress: true, + configureStaticServe: false, }); appTree.write( diff --git a/packages/storybook/src/utils/utilities.spec.ts b/packages/storybook/src/utils/utilities.spec.ts index 8387ce65d3c8c..8bf151e09a1a7 100644 --- a/packages/storybook/src/utils/utilities.spec.ts +++ b/packages/storybook/src/utils/utilities.spec.ts @@ -67,6 +67,7 @@ describe('testing utilities', () => { await runAngularStorybookSchematic(appTree, { name: 'test-ui-lib', configureCypress: true, + configureStaticServe: false, }); appTree.write( diff --git a/scripts/depcheck/missing.ts b/scripts/depcheck/missing.ts index a606f20badcb9..174c6adbbf417 100644 --- a/scripts/depcheck/missing.ts +++ b/scripts/depcheck/missing.ts @@ -127,6 +127,8 @@ const IGNORE_MATCHES_IN_PACKAGE = { '@storybook/core', '@storybook/core-server', '@storybook/types', + // lazy installed with ensurePackage + '@nrwl/web', 'rxjs', ], nx: [ From a10628057a2cebfbbac720b7bef06f53ffcc1e6d Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Wed, 22 Mar 2023 17:49:06 -0400 Subject: [PATCH 30/76] chore(repo): add to scripts/depcheck codeowners (#15837) --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index e849ab4be3a16..e2c7af1d0a7cf 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -152,7 +152,7 @@ yarn.lock @FrozenPandaz @vsavkin @AgentEnder @jaysoo @JamesHenry /CODEOWNERS @FrozenPandaz @AgentEnder # Scripts -/scripts/depcheck @FrozenPandaz @vsavkin +/scripts/depcheck @FrozenPandaz @vsavkin @jaysoo /scripts/documentation @bcabanes @isaacplmann /scripts/local-registry @FrozenPandaz @vsavkin /scripts/angular-support-upgrades @Coly010 @leosvelperez From e904e0461192e9fc962d40a705ba6c05f1b778a1 Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Wed, 22 Mar 2023 18:05:18 -0400 Subject: [PATCH 31/76] fix(nextjs): add missing style deps for less and stylus (#15839) --- packages/next/migrations.json | 6 + packages/next/plugins/with-less.ts | 155 +++++++++--------- packages/next/plugins/with-nx.ts | 6 +- packages/next/plugins/with-stylus.ts | 153 ++++++++--------- .../update-15-8-8/add-style-packages.spec.ts | 117 +++++++++++++ .../update-15-8-8/add-style-packages.ts | 35 ++++ packages/next/src/utils/styles.ts | 3 + 7 files changed, 327 insertions(+), 148 deletions(-) create mode 100644 packages/next/src/migrations/update-15-8-8/add-style-packages.spec.ts create mode 100644 packages/next/src/migrations/update-15-8-8/add-style-packages.ts diff --git a/packages/next/migrations.json b/packages/next/migrations.json index 80fe21e8d074b..a1d46660824cf 100644 --- a/packages/next/migrations.json +++ b/packages/next/migrations.json @@ -53,6 +53,12 @@ "version": "14.5.3-beta.0", "description": "Update development outputPath to the project root.", "factory": "./src/migrations/update-14-5-3/update-dev-output-path" + }, + "add-style-packages": { + "cli": "nx", + "version": "15.8.8-beta.0", + "description": "Add less and stylus packages if used.", + "factory": "./src/migrations/update-15-8-8/add-style-packages" } }, "packageJsonUpdates": { diff --git a/packages/next/plugins/with-less.ts b/packages/next/plugins/with-less.ts index 7651e320e88bf..f7dc5336fdd7a 100644 --- a/packages/next/plugins/with-less.ts +++ b/packages/next/plugins/with-less.ts @@ -1,5 +1,6 @@ // Adapted from https://raw.githubusercontent.com/elado/next-with-less/main/src/index.js import { merge } from 'webpack-merge'; +import { NextConfigFn } from './with-nx'; const addLessToRegExp = (rx) => new RegExp(rx.source.replace('|sass', '|sass|less'), rx.flags); @@ -12,86 +13,92 @@ function patchNextCSSWithLess( patchNextCSSWithLess(); -export function withLess({ lessLoaderOptions = {}, ...nextConfig }) { - return Object.assign({}, nextConfig, { - webpack(config, opts) { - // there are 2 relevant sass rules in next.js - css modules and global css - let sassModuleRule; - // global sass rule (does not exist in server builds) - let sassGlobalRule; - - const cssRule = config.module.rules.find((rule) => - rule.oneOf?.find((r) => r?.[Symbol.for('__next_css_remove')]) - ); - - const addLessToRuleTest = (test) => { - if (Array.isArray(test)) { - return test.map((rx) => addLessToRegExp(rx)); - } else { - return addLessToRegExp(test); - } - }; - - cssRule.oneOf.forEach((rule) => { - if (rule.options?.__next_css_remove) return; - - if (rule.use?.loader === 'error-loader') { - rule.test = addLessToRuleTest(rule.test); - } else if (rule.use?.loader?.includes('file-loader')) { - rule.issuer = addLessToRuleTest(rule.issuer); - } else if (rule.use?.includes?.('ignore-loader')) { - rule.test = addLessToRuleTest(rule.test); - } else if (rule.test?.source === '\\.module\\.(scss|sass)$') { - sassModuleRule = rule; - } else if (rule.test?.source === '(? { + const { lessLoaderOptions = {}, ...nextConfig } = await configFn(phase); + + return Object.assign({}, nextConfig, { + webpack(config, opts) { + // there are 2 relevant sass rules in next.js - css modules and global css + let sassModuleRule; + // global sass rule (does not exist in server builds) + let sassGlobalRule; + + const cssRule = config.module.rules.find((rule) => + rule.oneOf?.find((r) => r?.[Symbol.for('__next_css_remove')]) + ); + + const addLessToRuleTest = (test) => { + if (Array.isArray(test)) { + return test.map((rx) => addLessToRegExp(rx)); + } else { + return addLessToRegExp(test); + } + }; + + cssRule.oneOf.forEach((rule) => { + if (rule.options?.__next_css_remove) return; + + if (rule.use?.loader === 'error-loader') { + rule.test = addLessToRuleTest(rule.test); + } else if (rule.use?.loader?.includes('file-loader')) { + rule.issuer = addLessToRuleTest(rule.issuer); + } else if (rule.use?.includes?.('ignore-loader')) { + rule.test = addLessToRuleTest(rule.test); + } else if (rule.test?.source === '\\.module\\.(scss|sass)$') { + sassModuleRule = rule; + } else if (rule.test?.source === '(? { - rule.test = new RegExp(rule.test.source.replace('(scss|sass)', 'less')); - // replace sass-loader (last entry) with less-loader - rule.use.splice(-1, 1, lessLoader); - }; - - configureLessRule(lessModuleRule); - cssRule.oneOf.splice( - cssRule.oneOf.indexOf(sassModuleRule) + 1, - 0, - lessModuleRule - ); - - if (sassGlobalRule) { - let lessGlobalRule = merge(sassGlobalRule); - configureLessRule(lessGlobalRule); + }; + + let lessModuleRule = merge(sassModuleRule); + + const configureLessRule = (rule) => { + rule.test = new RegExp( + rule.test.source.replace('(scss|sass)', 'less') + ); + // replace sass-loader (last entry) with less-loader + rule.use.splice(-1, 1, lessLoader); + }; + + configureLessRule(lessModuleRule); cssRule.oneOf.splice( - cssRule.oneOf.indexOf(sassGlobalRule) + 1, + cssRule.oneOf.indexOf(sassModuleRule) + 1, 0, - lessGlobalRule + lessModuleRule ); - } - if (typeof nextConfig.webpack === 'function') { - return nextConfig.webpack(config, opts); - } + if (sassGlobalRule) { + let lessGlobalRule = merge(sassGlobalRule); + configureLessRule(lessGlobalRule); + cssRule.oneOf.splice( + cssRule.oneOf.indexOf(sassGlobalRule) + 1, + 0, + lessGlobalRule + ); + } + + if (typeof nextConfig.webpack === 'function') { + return nextConfig.webpack(config, opts); + } - return config; - }, - }); + return config; + }, + }); + }; } module.exports = withLess; diff --git a/packages/next/plugins/with-nx.ts b/packages/next/plugins/with-nx.ts index 51b7e5d6c6d19..8c35e025fab51 100644 --- a/packages/next/plugins/with-nx.ts +++ b/packages/next/plugins/with-nx.ts @@ -24,6 +24,10 @@ export interface WithNxOptions extends NextConfig { }; } +export interface NextConfigFn { + (phase: string): Promise; +} + export interface WithNxContext { workspaceRoot: string; libsDir: string; @@ -114,7 +118,7 @@ function getNxContext( export function withNx( _nextConfig = {} as WithNxOptions, context: WithNxContext = getWithNxContext() -): (phase: string) => Promise { +): NextConfigFn { return async (phase: string) => { if (phase === PHASE_PRODUCTION_SERVER) { // If we are running an already built production server, just return the configuration. diff --git a/packages/next/plugins/with-stylus.ts b/packages/next/plugins/with-stylus.ts index f3a6bbd478d02..1f4f1ce7d7287 100644 --- a/packages/next/plugins/with-stylus.ts +++ b/packages/next/plugins/with-stylus.ts @@ -1,5 +1,6 @@ // Adapted from https://raw.githubusercontent.com/elado/next-with-less/main/src/index.js import { merge } from 'webpack-merge'; +import { NextConfigFn } from './with-nx'; const addStylusToRegExp = (rx) => new RegExp(rx.source.replace('|sass', '|sass|styl'), rx.flags); @@ -12,85 +13,91 @@ function patchNextCSSWithStylus( patchNextCSSWithStylus(); -export function withStylus({ stylusLoaderOptions = {}, ...nextConfig }: any) { - return Object.assign({}, nextConfig, { - webpack(config, opts) { - // there are 2 relevant sass rules in next.js - css modules and global css - let sassModuleRule; - // global sass rule (does not exist in server builds) - let sassGlobalRule; - - const cssRule = config.module.rules.find((rule) => - rule.oneOf?.find((r) => r?.[Symbol.for('__next_css_remove')]) - ); - - const addStylusRuleToTest = (test) => { - if (Array.isArray(test)) { - return test.map((rx) => addStylusToRegExp(rx)); - } else { - return addStylusToRegExp(test); - } - }; - - cssRule.oneOf.forEach((rule) => { - if (rule.options?.__next_css_remove) return; - - if (rule.use?.loader === 'error-loader') { - rule.test = addStylusRuleToTest(rule.test); - } else if (rule.use?.loader?.includes('file-loader')) { - rule.issuer = addStylusRuleToTest(rule.issuer); - } else if (rule.use?.includes?.('ignore-loader')) { - rule.test = addStylusRuleToTest(rule.test); - } else if (rule.test?.source === '\\.module\\.(scss|sass)$') { - sassModuleRule = rule; - } else if (rule.test?.source === '(? { + const { stylusLoaderOptions = {}, ...nextConfig } = await configFn(phase); + + return Object.assign({}, nextConfig, { + webpack(config, opts) { + // there are 2 relevant sass rules in next.js - css modules and global css + let sassModuleRule; + // global sass rule (does not exist in server builds) + let sassGlobalRule; + + const cssRule = config.module.rules.find((rule) => + rule.oneOf?.find((r) => r?.[Symbol.for('__next_css_remove')]) + ); + + const addStylusRuleToTest = (test) => { + if (Array.isArray(test)) { + return test.map((rx) => addStylusToRegExp(rx)); + } else { + return addStylusToRegExp(test); + } + }; + + cssRule.oneOf.forEach((rule) => { + if (rule.options?.__next_css_remove) return; + + if (rule.use?.loader === 'error-loader') { + rule.test = addStylusRuleToTest(rule.test); + } else if (rule.use?.loader?.includes('file-loader')) { + rule.issuer = addStylusRuleToTest(rule.issuer); + } else if (rule.use?.includes?.('ignore-loader')) { + rule.test = addStylusRuleToTest(rule.test); + } else if (rule.test?.source === '\\.module\\.(scss|sass)$') { + sassModuleRule = rule; + } else if (rule.test?.source === '(? { - rule.test = new RegExp(rule.test.source.replace('(scss|sass)', 'styl')); - // replace sass-loader (last entry) with stylus-loader - rule.use.splice(-1, 1, stylusLoader); - }; - - configureStylusRule(stylusModuleRule); - cssRule.oneOf.splice( - cssRule.oneOf.indexOf(sassModuleRule) + 1, - 0, - stylusModuleRule - ); - - if (sassGlobalRule) { - let stylusGlobalRule = merge({}, sassGlobalRule); - configureStylusRule(stylusGlobalRule); + }; + + let stylusModuleRule = merge({}, sassModuleRule); + + const configureStylusRule = (rule) => { + rule.test = new RegExp( + rule.test.source.replace('(scss|sass)', 'styl') + ); + // replace sass-loader (last entry) with stylus-loader + rule.use.splice(-1, 1, stylusLoader); + }; + + configureStylusRule(stylusModuleRule); cssRule.oneOf.splice( - cssRule.oneOf.indexOf(sassGlobalRule) + 1, + cssRule.oneOf.indexOf(sassModuleRule) + 1, 0, - stylusGlobalRule + stylusModuleRule ); - } - if (typeof nextConfig.webpack === 'function') { - return nextConfig.webpack(config, opts); - } + if (sassGlobalRule) { + let stylusGlobalRule = merge({}, sassGlobalRule); + configureStylusRule(stylusGlobalRule); + cssRule.oneOf.splice( + cssRule.oneOf.indexOf(sassGlobalRule) + 1, + 0, + stylusGlobalRule + ); + } + + if (typeof nextConfig.webpack === 'function') { + return nextConfig.webpack(config, opts); + } - return config; - }, - }); + return config; + }, + }); + }; } module.exports = withStylus; diff --git a/packages/next/src/migrations/update-15-8-8/add-style-packages.spec.ts b/packages/next/src/migrations/update-15-8-8/add-style-packages.spec.ts new file mode 100644 index 0000000000000..6573e0a4ede0f --- /dev/null +++ b/packages/next/src/migrations/update-15-8-8/add-style-packages.spec.ts @@ -0,0 +1,117 @@ +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import { + writeJson, + readJson, + Tree, + addProjectConfiguration, +} from '@nrwl/devkit'; +import update from './add-style-packages'; + +describe('Add less and stylus if needed', () => { + let tree: Tree; + + beforeEach(() => { + tree = createTreeWithEmptyWorkspace(); + }); + + it('should add less if used', async () => { + writeJson(tree, 'package.json', { + dependencies: {}, + devDependencies: {}, + }); + addProjectConfiguration(tree, 'myapp', { + root: 'myapp', + targets: { + build: { + executor: '@nrwl/next:build', + }, + }, + }); + tree.write( + `myapp/next.config.js`, + `require('@nrwl/next/plugins/with-less')` + ); + + await update(tree); + + const packageJson = readJson(tree, 'package.json'); + expect(packageJson).toEqual({ + dependencies: {}, + devDependencies: { less: '3.12.2' }, + }); + }); + + it('should add stylus if used', async () => { + writeJson(tree, 'package.json', { + dependencies: {}, + devDependencies: {}, + }); + addProjectConfiguration(tree, 'myapp', { + root: 'myapp', + targets: { + build: { + executor: '@nrwl/next:build', + }, + }, + }); + tree.write( + `myapp/next.config.js`, + `require('@nrwl/next/plugins/with-stylus')` + ); + + await update(tree); + + const packageJson = readJson(tree, 'package.json'); + expect(packageJson).toEqual({ + dependencies: {}, + devDependencies: { stylus: '^0.55.0' }, + }); + }); + + it('should not add anything if less/stylus not used by Next.js app', async () => { + writeJson(tree, 'package.json', { + dependencies: {}, + devDependencies: {}, + }); + addProjectConfiguration(tree, 'myapp', { + root: 'myapp', + targets: { + build: { + executor: '@nrwl/next:build', + }, + }, + }); + tree.write(`myapp/next.config.js`, `require('@nrwl/next/plugins/with-nx')`); + + await update(tree); + + const packageJson = readJson(tree, 'package.json'); + expect(packageJson).toEqual({ + dependencies: {}, + devDependencies: {}, + }); + }); + + it('should not add anything if no Next.js apps are in workspace', async () => { + writeJson(tree, 'package.json', { + dependencies: {}, + devDependencies: {}, + }); + addProjectConfiguration(tree, 'myapp', { + root: 'myapp', + targets: { + build: { + executor: '@nrwl/webpack:webpack', + }, + }, + }); + + await update(tree); + + const packageJson = readJson(tree, 'package.json'); + expect(packageJson).toEqual({ + dependencies: {}, + devDependencies: {}, + }); + }); +}); diff --git a/packages/next/src/migrations/update-15-8-8/add-style-packages.ts b/packages/next/src/migrations/update-15-8-8/add-style-packages.ts new file mode 100644 index 0000000000000..012969a9cafcf --- /dev/null +++ b/packages/next/src/migrations/update-15-8-8/add-style-packages.ts @@ -0,0 +1,35 @@ +import { + addDependenciesToPackageJson, + getProjects, + joinPathFragments, + Tree, +} from '@nrwl/devkit'; + +export async function update(tree: Tree) { + const projects = getProjects(tree); + const missingDeps = {}; + + for (const [, config] of projects) { + if ( + config.targets?.build?.executor === '@nrwl/next:build' && + tree.exists(joinPathFragments(config.root, 'next.config.js')) + ) { + const nextConfigContent = tree.read( + joinPathFragments(config.root, 'next.config.js'), + 'utf-8' + ); + + if (nextConfigContent.includes('@nrwl/next/plugins/with-less')) { + missingDeps['less'] = '3.12.2'; + } + + if (nextConfigContent.includes('@nrwl/next/plugins/with-stylus')) { + missingDeps['stylus'] = '^0.55.0'; + } + } + } + + return addDependenciesToPackageJson(tree, {}, missingDeps); +} + +export default update; diff --git a/packages/next/src/utils/styles.ts b/packages/next/src/utils/styles.ts index 051e39d7378d9..50782f7a5c650 100644 --- a/packages/next/src/utils/styles.ts +++ b/packages/next/src/utils/styles.ts @@ -5,6 +5,7 @@ import { updateJson, } from '@nrwl/devkit'; +import { lessVersion, stylusVersion } from '@nrwl/react/src/utils/versions'; import { CSS_IN_JS_DEPENDENCIES } from '@nrwl/react/src/utils/styled'; import { babelPluginStyledComponentsVersion, @@ -40,11 +41,13 @@ export const NEXT_SPECIFIC_STYLE_DEPENDENCIES = { less: { dependencies: {}, devDependencies: { + less: lessVersion, 'less-loader': lessLoader, }, }, styl: { dependencies: { + stylus: stylusVersion, 'stylus-loader': stylusLoader, }, devDependencies: {}, From 750a978b274803c1c6223436ff2ba8ad1a5881ac Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Thu, 23 Mar 2023 13:11:46 +0200 Subject: [PATCH 32/76] fix(storybook): show e2e error only if configureCypress is true (#15832) --- .../generators/configuration/configuration.ts | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/packages/storybook/src/generators/configuration/configuration.ts b/packages/storybook/src/generators/configuration/configuration.ts index 1342e78804552..ba6e0364b4f2b 100644 --- a/packages/storybook/src/generators/configuration/configuration.ts +++ b/packages/storybook/src/generators/configuration/configuration.ts @@ -186,23 +186,25 @@ export async function configurationGenerator( addStaticTarget(tree, schema); } - const e2eProject = await getE2EProjectName(tree, schema.name); - if (schema.configureCypress && !e2eProject) { - const cypressTask = await cypressProjectGenerator(tree, { - name: schema.name, - js: schema.js, - linter: schema.linter, - directory: schema.cypressDirectory, - standaloneConfig: schema.standaloneConfig, - ciTargetName: schema.configureStaticServe - ? 'static-storybook' - : undefined, - }); - tasks.push(cypressTask); - } else { - logger.warn( - `There is already an e2e project setup for ${schema.name}, called ${e2eProject}.` - ); + if (schema.configureCypress) { + const e2eProject = await getE2EProjectName(tree, schema.name); + if (!e2eProject) { + const cypressTask = await cypressProjectGenerator(tree, { + name: schema.name, + js: schema.js, + linter: schema.linter, + directory: schema.cypressDirectory, + standaloneConfig: schema.standaloneConfig, + ciTargetName: schema.configureStaticServe + ? 'static-storybook' + : undefined, + }); + tasks.push(cypressTask); + } else { + logger.warn( + `There is already an e2e project setup for ${schema.name}, called ${e2eProject}.` + ); + } } const devDeps = {}; From 64017a27bafcf4b13d576dbae1317f673d95c610 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Thu, 23 Mar 2023 14:13:25 +0200 Subject: [PATCH 33/76] feat(vite): add outfile for build executor (#15002) (#15845) Co-authored-by: Leng, Errong --- packages/vite/src/executors/build/build.impl.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/executors/build/build.impl.ts b/packages/vite/src/executors/build/build.impl.ts index 593d632e4b44d..65439586f746b 100644 --- a/packages/vite/src/executors/build/build.impl.ts +++ b/packages/vite/src/executors/build/build.impl.ts @@ -76,7 +76,10 @@ export async function* viteBuildExecutor( }); yield* iterable; } else { - yield { success: true }; + const output = watcherOrOutput?.['output'] || watcherOrOutput?.[0]?.output; + const fileName = output?.[0]?.fileName || 'main.cjs'; + const outfile = resolve(normalizedOptions.outputPath, fileName); + yield { success: true, outfile }; } } From 7a48214438935547a36dacd9be6e410e2b510252 Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Thu, 23 Mar 2023 10:15:14 -0400 Subject: [PATCH 34/76] fix(nextjs): add missing express dependencies to next plugin (#15854) --- packages/next/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/next/package.json b/packages/next/package.json index 782c8b10517ed..03653f5d20b99 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -44,7 +44,9 @@ "chalk": "^4.1.0", "copy-webpack-plugin": "^10.2.4", "dotenv": "~10.0.0", + "express": "^4.18.1", "fs-extra": "^11.1.0", + "http-proxy-middleware": "^2.0.6", "ignore": "^5.0.4", "semver": "7.3.4", "ts-node": "10.9.1", From 7d61ae238b7893eb0a46c69dd07c0203b4389a61 Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Thu, 23 Mar 2023 10:47:43 -0400 Subject: [PATCH 35/76] fix(misc): prevent --quiet and --verbose from both being true (#15836) --- docs/generated/cli/affected.md | 2 -- docs/generated/cli/exec.md | 2 -- docs/generated/cli/run-many.md | 2 -- docs/generated/packages/nx/documents/affected.md | 2 -- docs/generated/packages/nx/documents/exec.md | 2 -- docs/generated/packages/nx/documents/run-many.md | 2 -- e2e/nx-misc/src/extras.test.ts | 4 +++- e2e/utils/command-utils.ts | 12 ++++++++---- packages/create-nx-workspace/src/create-preset.ts | 7 +++++-- packages/nx/src/command-line/nx-commands.ts | 4 +--- 10 files changed, 17 insertions(+), 22 deletions(-) diff --git a/docs/generated/cli/affected.md b/docs/generated/cli/affected.md index 454172bd5f4cc..490a417fc9641 100644 --- a/docs/generated/cli/affected.md +++ b/docs/generated/cli/affected.md @@ -183,8 +183,6 @@ Untracked changes Type: `boolean` -Default: `false` - Prints additional information about the commands (e.g., stack traces) ### version diff --git a/docs/generated/cli/exec.md b/docs/generated/cli/exec.md index 57c9bdf13aa59..f30730dd77684 100644 --- a/docs/generated/cli/exec.md +++ b/docs/generated/cli/exec.md @@ -91,8 +91,6 @@ Rerun the tasks even when the results are available in the cache Type: `boolean` -Default: `false` - Prints additional information about the commands (e.g., stack traces) ### version diff --git a/docs/generated/cli/run-many.md b/docs/generated/cli/run-many.md index 9a8198b836764..623e65370a8d9 100644 --- a/docs/generated/cli/run-many.md +++ b/docs/generated/cli/run-many.md @@ -149,8 +149,6 @@ Tasks to run for affected projects Type: `boolean` -Default: `false` - Prints additional information about the commands (e.g., stack traces) ### version diff --git a/docs/generated/packages/nx/documents/affected.md b/docs/generated/packages/nx/documents/affected.md index 454172bd5f4cc..490a417fc9641 100644 --- a/docs/generated/packages/nx/documents/affected.md +++ b/docs/generated/packages/nx/documents/affected.md @@ -183,8 +183,6 @@ Untracked changes Type: `boolean` -Default: `false` - Prints additional information about the commands (e.g., stack traces) ### version diff --git a/docs/generated/packages/nx/documents/exec.md b/docs/generated/packages/nx/documents/exec.md index 57c9bdf13aa59..f30730dd77684 100644 --- a/docs/generated/packages/nx/documents/exec.md +++ b/docs/generated/packages/nx/documents/exec.md @@ -91,8 +91,6 @@ Rerun the tasks even when the results are available in the cache Type: `boolean` -Default: `false` - Prints additional information about the commands (e.g., stack traces) ### version diff --git a/docs/generated/packages/nx/documents/run-many.md b/docs/generated/packages/nx/documents/run-many.md index 9a8198b836764..623e65370a8d9 100644 --- a/docs/generated/packages/nx/documents/run-many.md +++ b/docs/generated/packages/nx/documents/run-many.md @@ -149,8 +149,6 @@ Tasks to run for affected projects Type: `boolean` -Default: `false` - Prints additional information about the commands (e.g., stack traces) ### version diff --git a/e2e/nx-misc/src/extras.test.ts b/e2e/nx-misc/src/extras.test.ts index f2b29e1de938e..513810de6e176 100644 --- a/e2e/nx-misc/src/extras.test.ts +++ b/e2e/nx-misc/src/extras.test.ts @@ -263,7 +263,9 @@ describe('Extra Nx Misc Tests', () => { describe('generate --quiet', () => { it('should not log tree operations or install tasks', () => { - const output = runCLI('generate @nrwl/react:app --quiet test-project'); + const output = runCLI('generate @nrwl/react:app --quiet test-project', { + verbose: false, + }); expect(output).not.toContain('CREATE'); expect(output).not.toContain('Installed'); }); diff --git a/e2e/utils/command-utils.ts b/e2e/utils/command-utils.ts index 74908678076d2..e38bde3e83680 100644 --- a/e2e/utils/command-utils.ts +++ b/e2e/utils/command-utils.ts @@ -21,6 +21,7 @@ export interface RunCmdOpts { env?: Record; cwd?: string; silent?: boolean; + verbose?: boolean; } /** @@ -295,7 +296,7 @@ export function runNgAdd( const r = stripConsoleColors(result); - if (isVerboseE2ERun()) { + if (opts.verbose ?? isVerboseE2ERun()) { output.log({ title: `Original command: ${fullCommand}`, bodyLines: [result as string], @@ -319,12 +320,15 @@ export function runCLI( opts: RunCmdOpts = { silenceError: false, env: undefined, + verbose: undefined, } ): string { try { const pm = getPackageManagerCommand(); const logs = execSync( - `${pm.runNx} ${command} ${isVerboseE2ERun() ? ' --verbose' : ''}`, + `${pm.runNx} ${command} ${ + opts.verbose ?? isVerboseE2ERun() ? ' --verbose' : '' + }`, { cwd: opts.cwd || tmpProjPath(), env: { @@ -338,7 +342,7 @@ export function runCLI( } ); - if (isVerboseE2ERun()) { + if (opts.verbose ?? isVerboseE2ERun()) { output.log({ title: `Original command: ${command}`, bodyLines: [logs as string], @@ -384,7 +388,7 @@ export function runLernaCLI( maxBuffer: 50 * 1024 * 1024, }); - if (isVerboseE2ERun()) { + if (opts.verbose ?? isVerboseE2ERun()) { output.log({ title: `Original command: ${fullCommand}`, bodyLines: [logs as string], diff --git a/packages/create-nx-workspace/src/create-preset.ts b/packages/create-nx-workspace/src/create-preset.ts index a83df25c375e5..34655e6c24ed9 100644 --- a/packages/create-nx-workspace/src/create-preset.ts +++ b/packages/create-nx-workspace/src/create-preset.ts @@ -16,7 +16,7 @@ export async function createPreset( ): Promise { const { skipGit, ci, commit, nxCloud, ...restArgs } = parsedArgs; - const args = unparse({ + let args = unparse({ ...restArgs, }).join(' '); @@ -38,7 +38,10 @@ export async function createPreset( } } - const command = `g ${preset}:preset --quiet ${args}`; + if (process.env.NX_VERBOSE_LOGGING !== 'true') { + args = '--quiet ' + args; + } + const command = `g ${preset}:preset ${args}`; try { const [exec, ...args] = pmc.exec.split(' '); diff --git a/packages/nx/src/command-line/nx-commands.ts b/packages/nx/src/command-line/nx-commands.ts index b79016171cfcd..521a9c000c5b6 100644 --- a/packages/nx/src/command-line/nx-commands.ts +++ b/packages/nx/src/command-line/nx-commands.ts @@ -510,7 +510,6 @@ function withRunOptions(yargs: yargs.Argv): yargs.Argv { type: 'boolean', describe: 'Prints additional information about the commands (e.g., stack traces)', - default: false, }) .option('nx-bail', { describe: 'Stop command execution after the first failed task', @@ -752,12 +751,11 @@ function withGenerateOptions(yargs: yargs.Argv) { describe: 'Prints additional information about the commands (e.g., stack traces)', type: 'boolean', - default: false, }) .option('quiet', { describe: 'Hides logs from tree operations (e.g. `CREATE package.json`)', type: 'boolean', - default: false, + conflicts: ['verbose'], }) .middleware((args) => { if (process.env.NX_INTERACTIVE === 'false') { From e611a0bab7db5230ad1ada1d3d2347b63e57ac2e Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Thu, 23 Mar 2023 11:14:17 -0400 Subject: [PATCH 36/76] feat(nextjs): deprecate proxy config option for server executor (#15855) --- docs/generated/packages/next/executors/server.json | 3 ++- packages/next/src/executors/server/schema.json | 3 ++- packages/next/src/executors/server/server.impl.ts | 7 +++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/generated/packages/next/executors/server.json b/docs/generated/packages/next/executors/server.json index b67b84ccb95f5..5bfa1f6eff6e5 100644 --- a/docs/generated/packages/next/executors/server.json +++ b/docs/generated/packages/next/executors/server.json @@ -51,7 +51,8 @@ "proxyConfig": { "type": "string", "description": "Path to the proxy configuration file.", - "x-completion-type": "file" + "x-completion-type": "file", + "x-deprecated": "Use the built-in rewrite feature from Next.js. See: https://nextjs.org/docs/api-reference/next.config.js/rewrites" }, "buildLibsFromSource": { "type": "boolean", diff --git a/packages/next/src/executors/server/schema.json b/packages/next/src/executors/server/schema.json index 2f19772acabec..3a5296d7d28f7 100644 --- a/packages/next/src/executors/server/schema.json +++ b/packages/next/src/executors/server/schema.json @@ -48,7 +48,8 @@ "proxyConfig": { "type": "string", "description": "Path to the proxy configuration file.", - "x-completion-type": "file" + "x-completion-type": "file", + "x-deprecated": "Use the built-in rewrite feature from Next.js. See: https://nextjs.org/docs/api-reference/next.config.js/rewrites" }, "buildLibsFromSource": { "type": "boolean", diff --git a/packages/next/src/executors/server/server.impl.ts b/packages/next/src/executors/server/server.impl.ts index e38a5175b922d..2021a30137513 100644 --- a/packages/next/src/executors/server/server.impl.ts +++ b/packages/next/src/executors/server/server.impl.ts @@ -20,8 +20,6 @@ import { import { customServer } from './lib/custom-server'; import { defaultServer } from './lib/default-server'; -const infoPrefix = `[ ${chalk.dim(chalk.cyan('info'))} ] `; - export default async function* serveExecutor( options: NextServeBuilderOptions, context: ExecutorContext @@ -79,9 +77,10 @@ async function* runNextDevServer( ? join(context.root, options.proxyConfig) : join(root, 'proxy.conf.json'); + // TODO(v16): Remove proxy support. if (existsSync(proxyConfigPath)) { - logger.info( - `${infoPrefix} found proxy configuration at ${proxyConfigPath}` + logger.warn( + `The "proxyConfig" option will be removed in Nx 16. Use the "rewrites" feature from Next.js instead. See: https://nextjs.org/docs/api-reference/next.config.js/rewrites` ); proxyConfig = require(proxyConfigPath); } From 5712abe670f0ddf52a7e511e4f39ab4d054b93fd Mon Sep 17 00:00:00 2001 From: Jonathan Cammisuli Date: Thu, 23 Mar 2023 11:18:49 -0400 Subject: [PATCH 37/76] Revert "fix(core): use relative paths for socket path (#10896)" (#15857) --- packages/nx/src/daemon/socket-utils.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/nx/src/daemon/socket-utils.ts b/packages/nx/src/daemon/socket-utils.ts index 43a4a239c6c82..70cbbc2be7953 100644 --- a/packages/nx/src/daemon/socket-utils.ts +++ b/packages/nx/src/daemon/socket-utils.ts @@ -1,9 +1,7 @@ import { unlinkSync } from 'fs'; import { platform } from 'os'; -import { relative, resolve } from 'path'; +import { resolve } from 'path'; import { DAEMON_SOCKET_PATH } from './tmp-dir'; -import { ProjectGraph } from '../config/project-graph'; -import { workspaceRoot } from '../utils/workspace-root'; export const isWindows = platform() === 'win32'; @@ -12,12 +10,10 @@ export const isWindows = platform() === 'win32'; * * See https://nodejs.org/dist/latest-v14.x/docs/api/net.html#net_identifying_paths_for_ipc_connections for a full breakdown * of OS differences between Unix domain sockets and named pipes. - * - * Updated non-windows platforms to use relative paths allowing for workspaces that exist in long paths. */ export const FULL_OS_SOCKET_PATH = isWindows ? '\\\\.\\pipe\\nx\\' + resolve(DAEMON_SOCKET_PATH) - : relative(workspaceRoot, resolve(DAEMON_SOCKET_PATH)); + : resolve(DAEMON_SOCKET_PATH); export function killSocketOrPath(): void { try { From 8af80de3e760e724d49fa565519b683eb3598bd8 Mon Sep 17 00:00:00 2001 From: Emily Marigold Klassen <760204+forivall@users.noreply.github.com> Date: Thu, 23 Mar 2023 08:55:54 -0700 Subject: [PATCH 38/76] fix(core): resolve some strip-source-code bugs (#15840) --- .../nx/src/utils/strip-source-code.spec.ts | 47 ++++++++++ packages/nx/src/utils/strip-source-code.ts | 91 ++++++++++++++----- 2 files changed, 117 insertions(+), 21 deletions(-) diff --git a/packages/nx/src/utils/strip-source-code.spec.ts b/packages/nx/src/utils/strip-source-code.spec.ts index 082783b45a41b..2d287e061089d 100644 --- a/packages/nx/src/utils/strip-source-code.spec.ts +++ b/packages/nx/src/utils/strip-source-code.spec.ts @@ -258,6 +258,20 @@ require('./d')`; expect(stripSourceCode(scanner, input)).toEqual(expected); }); + it('should find an import after a template literal with a 2 variables in it', () => { + const input = ` + const a = 1; + const b = 2; + const c = \`a: $\{a}, b: $\{b}\` + const d = await import('./d') + const e = require('./e') + `; + const expected = `import('./d') +require('./e')`; + + expect(stripSourceCode(scanner, input)).toEqual(expected); + }); + it('finds imports after an escaped character', () => { const input = ` const b = unquotedLiteral.replace(/"/g, '\\\\"') @@ -282,4 +296,37 @@ require('./d')`; expect(stripSourceCode(scanner, input)).toEqual(expected); }); + + it('finds imports in the same line as template literals with division inside', () => { + const input = ` + const a = 1; + const b = \`"$\{1 / 2} $\{await import('./b')} $\{await require('./c')}"\`; + `; + const expected = `import('./b') +require('./c')`; + + expect(stripSourceCode(scanner, input)).toEqual(expected); + }); + + it('finds imports in the same line after a regex', () => { + const input = ` + const a = 1; + const b = /"/g; const c = await import('./c'); const d = require('./d') + `; + const expected = `import('./c') +require('./d')`; + + expect(stripSourceCode(scanner, input)).toEqual(expected); + }); + + it('finds imports inside template literals', () => { + const input = ` + const a = \`"$\{require('./a')}"\` + const b = \`"$\{await import('./b')}"\` + `; + const expected = `require('./a') +import('./b')`; + + expect(stripSourceCode(scanner, input)).toEqual(expected); + }); }); diff --git a/packages/nx/src/utils/strip-source-code.ts b/packages/nx/src/utils/strip-source-code.ts index 0e85d38c3d224..22cccb516c157 100644 --- a/packages/nx/src/utils/strip-source-code.ts +++ b/packages/nx/src/utils/strip-source-code.ts @@ -1,6 +1,29 @@ import type { Scanner } from 'typescript'; let SyntaxKind: typeof import('typescript').SyntaxKind; +function shouldRescanSlashToken( + lastNonTriviaToken: import('typescript').SyntaxKind +) { + switch (lastNonTriviaToken) { + case SyntaxKind.Identifier: + case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: + case SyntaxKind.BigIntLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.ThisKeyword: + case SyntaxKind.PlusPlusToken: + case SyntaxKind.MinusMinusToken: + case SyntaxKind.CloseParenToken: + case SyntaxKind.CloseBracketToken: + case SyntaxKind.CloseBraceToken: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + return false; + default: + return true; + } +} + export function stripSourceCode(scanner: Scanner, contents: string): string { if (!SyntaxKind) { SyntaxKind = require('typescript').SyntaxKind; @@ -12,9 +35,14 @@ export function stripSourceCode(scanner: Scanner, contents: string): string { scanner.setText(contents); let token = scanner.scan(); + let lastNonTriviaToken = SyntaxKind.Unknown; const statements = []; + const templateStack = []; + let ignoringLine = false; + let braceDepth = 0; let start = null; while (token !== SyntaxKind.EndOfFileToken) { + const currentToken = token; const potentialStart = scanner.getStartPos(); switch (token) { case SyntaxKind.MultiLineCommentTrivia: @@ -33,21 +61,23 @@ export function stripSourceCode(scanner: Scanner, contents: string): string { ) { token = scanner.scan(); } - - // ignore next line - while ( - token !== SyntaxKind.NewLineTrivia && - token !== SyntaxKind.EndOfFileToken - ) { - token = scanner.scan(); - } + ignoringLine = true; } break; } + case SyntaxKind.NewLineTrivia: { + ignoringLine = false; + token = scanner.scan(); + break; + } + case SyntaxKind.RequireKeyword: case SyntaxKind.ImportKeyword: { token = scanner.scan(); + if (ignoringLine) { + break; + } while ( token === SyntaxKind.WhitespaceTrivia || token === SyntaxKind.NewLineTrivia @@ -59,29 +89,44 @@ export function stripSourceCode(scanner: Scanner, contents: string): string { } case SyntaxKind.TemplateHead: { - while (true) { - token = scanner.scan(); + templateStack.push(braceDepth); + braceDepth = 0; + token = scanner.scan(); + break; + } - if (token === SyntaxKind.SlashToken) { - token = scanner.reScanSlashToken(); - } + case SyntaxKind.SlashToken: { + if (shouldRescanSlashToken(lastNonTriviaToken)) { + token = scanner.reScanSlashToken(); + } + token = scanner.scan(); + break; + } - if (token === SyntaxKind.EndOfFileToken) { - // either the template is unterminated, or there - // is some other edge case we haven't compensated for - break; - } + case SyntaxKind.OpenBraceToken: { + ++braceDepth; + token = scanner.scan(); + break; + } - if (token === SyntaxKind.CloseBraceToken) { - token = scanner.reScanTemplateToken(false); - break; + case SyntaxKind.CloseBraceToken: { + if (braceDepth) { + --braceDepth; + } else if (templateStack.length) { + token = scanner.reScanTemplateToken(false); + if (token === SyntaxKind.LastTemplateToken) { + braceDepth = templateStack.pop(); } } + token = scanner.scan(); break; } case SyntaxKind.ExportKeyword: { token = scanner.scan(); + if (ignoringLine) { + break; + } while ( token === SyntaxKind.WhitespaceTrivia || token === SyntaxKind.NewLineTrivia @@ -117,6 +162,10 @@ export function stripSourceCode(scanner: Scanner, contents: string): string { token = scanner.scan(); } } + + if (currentToken > SyntaxKind.LastTriviaToken) { + lastNonTriviaToken = currentToken; + } } return statements.join('\n'); From b3200fb7c49276dc584680b1718da24c7ee7410f Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Thu, 23 Mar 2023 12:57:10 -0400 Subject: [PATCH 39/76] fix(nextjs): set correct distDir when running production server through Nx (#15861) --- e2e/next/src/next.test.ts | 27 +++++++++++++++++++++------ packages/next/plugins/with-nx.ts | 31 ++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/e2e/next/src/next.test.ts b/e2e/next/src/next.test.ts index 05b0d546fca6b..9e0242ed93352 100644 --- a/e2e/next/src/next.test.ts +++ b/e2e/next/src/next.test.ts @@ -168,21 +168,36 @@ describe('Next.js Applications', () => { `dist/apps/${appName}/public/shared/ui/hello.txt` ); + // Check that `nx serve --prod` works with previous production build (e.g. `nx build `). + const prodServePort = 4000; + const prodServeProcess = await runCommandUntil( + `run ${appName}:serve --prod --port=${prodServePort}`, + (output) => { + return output.includes(`localhost:${prodServePort}`); + } + ); + // Check that the output is self-contained (i.e. can run with its own package.json + node_modules) const distPath = joinPathFragments(tmpProjPath(), 'dist/apps', appName); - const port = 3000; + const selfContainedPort = 3000; const pmc = getPackageManagerCommand(); runCommand(`${pmc.install}`, { cwd: distPath, }); runCLI( - `generate @nrwl/workspace:run-commands serve-prod --project ${appName} --cwd=dist/apps/${appName} --command="npx next start --port=${port}"` + `generate @nrwl/workspace:run-commands serve-prod --project ${appName} --cwd=dist/apps/${appName} --command="npx next start --port=${selfContainedPort}"` + ); + const selfContainedProcess = await runCommandUntil( + `run ${appName}:serve-prod`, + (output) => { + return output.includes(`localhost:${selfContainedPort}`); + } ); - await runCommandUntil(`run ${appName}:serve-prod`, (output) => { - return output.includes(`localhost:${port}`); - }); - await killPort(port); + prodServeProcess.kill(); + selfContainedProcess.kill(); + await killPort(prodServePort); + await killPort(selfContainedPort); }, 300_000); it('should build and install pruned lock file', () => { diff --git a/packages/next/plugins/with-nx.ts b/packages/next/plugins/with-nx.ts index 8c35e025fab51..b0ecd6c36b6fd 100644 --- a/packages/next/plugins/with-nx.ts +++ b/packages/next/plugins/with-nx.ts @@ -18,6 +18,7 @@ import { PHASE_PRODUCTION_SERVER } from 'next/constants'; import * as path from 'path'; import { createWebpackConfig } from '../src/utils/config'; import { NextBuildBuilderOptions } from '../src/utils/types'; + export interface WithNxOptions extends NextConfig { nx?: { svgr?: boolean; @@ -115,6 +116,31 @@ function getNxContext( } } +/** + * Try to read output dir from project, and default to '.next' if executing outside of Nx (e.g. dist is added to a docker image). + */ +async function determineDistDirForProdServer( + nextConfig: NextConfig +): Promise { + const project = process.env.NX_TASK_TARGET_PROJECT; + const target = process.env.NX_TASK_TARGET_TARGET; + const configuration = process.env.NX_TASK_TARGET_CONFIGURATION; + + if (project && target) { + const originalTarget = { project, target, configuration }; + const graph = await createProjectGraphAsync(); + + const { options, node: projectNode } = getNxContext(graph, originalTarget); + const outputDir = `${offsetFromRoot(projectNode.data.root)}${ + options.outputPath + }`; + return nextConfig.distDir && nextConfig.distDir !== '.next' + ? joinPathFragments(outputDir, nextConfig.distDir) + : joinPathFragments(outputDir, '.next'); + } else { + return '.next'; + } +} export function withNx( _nextConfig = {} as WithNxOptions, context: WithNxContext = getWithNxContext() @@ -123,7 +149,10 @@ export function withNx( if (phase === PHASE_PRODUCTION_SERVER) { // If we are running an already built production server, just return the configuration. const { nx, ...validNextConfig } = _nextConfig; - return { distDir: '.next', ...validNextConfig }; + return { + ...validNextConfig, + distDir: await determineDistDirForProdServer(validNextConfig), + }; } else { // Otherwise, add in webpack and eslint configuration for build or test. let dependencies: DependentBuildableProjectNode[] = []; From f1dc1520243ccb061993186c5793653cacd31ed6 Mon Sep 17 00:00:00 2001 From: FrozenPandaz Date: Thu, 23 Mar 2023 13:32:51 -0400 Subject: [PATCH 40/76] chore(misc): publish 15.9.0-beta.9 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 14750d8d4d4d8..b449d65da2d7a 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["build/packages/*", "build/packages/nx/native-packages/*"], - "version": "15.9.0-beta.8", + "version": "15.9.0-beta.9", "granularPathspec": false, "command": { "publish": { From 7c9b66e24bfc1fc1cfbd6b91dc7ab343fae1efa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leosvel=20P=C3=A9rez=20Espinosa?= Date: Thu, 23 Mar 2023 17:41:07 +0000 Subject: [PATCH 41/76] fix(misc): cleanup deps to support pnpm v7 in create-nx-workspace (#15859) --- packages/devkit/migrations.json | 10 -- packages/devkit/migrations.spec.ts | 12 -- packages/devkit/package.json | 4 - .../split-create-empty-tree.spec.ts | 109 ------------------ .../update-14-2-0/split-create-empty-tree.ts | 87 -------------- packages/js/src/generators/library/library.ts | 2 +- packages/workspace/project.json | 6 - 7 files changed, 1 insertion(+), 229 deletions(-) delete mode 100644 packages/devkit/migrations.json delete mode 100644 packages/devkit/migrations.spec.ts delete mode 100644 packages/devkit/src/migrations/update-14-2-0/split-create-empty-tree.spec.ts delete mode 100644 packages/devkit/src/migrations/update-14-2-0/split-create-empty-tree.ts diff --git a/packages/devkit/migrations.json b/packages/devkit/migrations.json deleted file mode 100644 index 23c4e23f1259e..0000000000000 --- a/packages/devkit/migrations.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "generators": { - "split-create-tree": { - "cli": "nx", - "version": "14.2.0-beta.0", - "description": "Adjusts calls to createTreeWithEmptyWorkspace to reflect new API", - "factory": "./src/migrations/update-14-2-0/split-create-empty-tree" - } - } -} diff --git a/packages/devkit/migrations.spec.ts b/packages/devkit/migrations.spec.ts deleted file mode 100644 index cde4966665f8f..0000000000000 --- a/packages/devkit/migrations.spec.ts +++ /dev/null @@ -1,12 +0,0 @@ -import path = require('path'); -import json = require('./migrations.json'); - -const migrations = Object.entries(json.generators); - -describe('devkit migrations', () => { - it.each(migrations)('should have valid path: %s', (key, m) => { - expect(() => - require.resolve(path.join(__dirname, `${m.factory}.ts`)) - ).not.toThrow(); - }); -}); diff --git a/packages/devkit/package.json b/packages/devkit/package.json index 1582771901d89..c9d04bd071b2c 100644 --- a/packages/devkit/package.json +++ b/packages/devkit/package.json @@ -32,15 +32,11 @@ "ignore": "^5.0.4", "tmp": "~0.2.1", "tslib": "^2.3.0", - "@phenomnomnominal/tsquery": "4.1.1", "semver": "7.3.4" }, "peerDependencies": { "nx": ">= 14.1 <= 16" }, - "nx-migrations": { - "migrations": "./migrations.json" - }, "publishConfig": { "access": "public" } diff --git a/packages/devkit/src/migrations/update-14-2-0/split-create-empty-tree.spec.ts b/packages/devkit/src/migrations/update-14-2-0/split-create-empty-tree.spec.ts deleted file mode 100644 index f21b351413638..0000000000000 --- a/packages/devkit/src/migrations/update-14-2-0/split-create-empty-tree.spec.ts +++ /dev/null @@ -1,109 +0,0 @@ -import update from './split-create-empty-tree'; -import { createTreeWithEmptyWorkspace } from '../../../testing'; -import { addProjectConfiguration, Tree } from '@nrwl/devkit'; - -const TS_FILE_THAT_DOESNT_USE_EITHER = `import { other } from '@nrwl/devkit/testing'; -other();`; - -const TS_FILE_THAT_USES_DEFAULT = `import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; - -createTreeWithEmptyWorkspace(); -`; - -const TS_FILE_THAT_USES_V1 = `import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; - -createTreeWithEmptyV1Workspace(); -`; - -const TS_FILE_THAT_SPECIFIED_V2 = `import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; - -createTreeWithEmptyWorkspace(2); -`; - -const TS_FILE_THAT_SPECIFIED_V1_AND_IMPORTED_ALL = `import * as devkit from '@nrwl/devkit/testing'; - -devkit.createTreeWithEmptyWorkspace(1); -`; - -const TS_FILE_THAT_SPECIFIED_V1_AND_IMPORTED_ALL_UPDATED = `import * as devkit from '@nrwl/devkit/testing'; - -devkit.createTreeWithEmptyV1Workspace(); -`; - -const TS_FILE_THAT_SPECIFIED_V1 = `import { createTreeWithEmptyWorkspace, other } from '@nrwl/devkit/testing'; - -devkit.createTreeWithEmptyWorkspace(1); -`; - -const TS_FILE_THAT_SPECIFIED_V1_UPDATED = `import { createTreeWithEmptyV1Workspace, other } from '@nrwl/devkit/testing'; - -devkit.createTreeWithEmptyV1Workspace(); -`; - -const TS_FILE_THAT_SPECIFIED_BOTH = `import { createTreeWithEmptyWorkspace, other } from '@nrwl/devkit/testing'; - -devkit.createTreeWithEmptyWorkspace(1); -devkit.createTreeWithEmptyWorkspace(2); -`; - -const TS_FILE_THAT_SPECIFIED_BOTH_UPDATED = `import { createTreeWithEmptyV1Workspace, createTreeWithEmptyWorkspace, other } from '@nrwl/devkit/testing'; - -devkit.createTreeWithEmptyV1Workspace(); -devkit.createTreeWithEmptyWorkspace(); -`; - -const testCases = [ - { - initial: TS_FILE_THAT_DOESNT_USE_EITHER, - expected: TS_FILE_THAT_DOESNT_USE_EITHER, - message: `doesn't use util`, - }, - { - initial: TS_FILE_THAT_USES_DEFAULT, - expected: TS_FILE_THAT_USES_V1, - message: `uses default value`, - }, - { - initial: TS_FILE_THAT_SPECIFIED_V2, - expected: TS_FILE_THAT_USES_DEFAULT, - message: 'only specified default', - }, - { - initial: TS_FILE_THAT_SPECIFIED_V1, - expected: TS_FILE_THAT_SPECIFIED_V1_UPDATED, - message: 'only specified v1', - }, - { - initial: TS_FILE_THAT_SPECIFIED_V1_AND_IMPORTED_ALL, - expected: TS_FILE_THAT_SPECIFIED_V1_AND_IMPORTED_ALL_UPDATED, - message: 'only specified v1, and imported all', - }, - { - initial: TS_FILE_THAT_SPECIFIED_BOTH, - expected: TS_FILE_THAT_SPECIFIED_BOTH_UPDATED, - message: 'specified both', - }, -]; - -describe('tttupdate-14-2-0-split-create-empty-tree', () => { - it.each(testCases)( - 'should match expected if file $message', - async ({ initial, expected }) => { - const { tree, tsFilePath } = createTreeWithBoilerplate(); - tree.write(tsFilePath, initial); - - await update(tree); - const contents = tree.read(tsFilePath).toString(); - expect(contents).toEqual(expected); - } - ); -}); - -function createTreeWithBoilerplate(): { tree: Tree; tsFilePath: string } { - const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); - const project = 'proj'; - addProjectConfiguration(tree, project, { - root: `libs/${project}`, - }); - return { tree, tsFilePath: `libs/${project}/some-file.ts` }; -} diff --git a/packages/devkit/src/migrations/update-14-2-0/split-create-empty-tree.ts b/packages/devkit/src/migrations/update-14-2-0/split-create-empty-tree.ts deleted file mode 100644 index aed802547cda6..0000000000000 --- a/packages/devkit/src/migrations/update-14-2-0/split-create-empty-tree.ts +++ /dev/null @@ -1,87 +0,0 @@ -import type { Tree } from 'nx/src/generators/tree'; -import { - StringChange, - ChangeType, - applyChangesToString, -} from '../../utils/string-change'; -import { extname } from 'path'; -import { tsquery } from '@phenomnomnominal/tsquery'; - -import { visitNotIgnoredFiles } from '../../generators/visit-not-ignored-files'; -import { formatFiles } from '../../generators/format-files'; -// eslint-disable-next-line @typescript-eslint/no-restricted-imports -import { requireNx } from '../../../nx'; - -const { getProjects } = requireNx(); - -export default async function update(tree: Tree): Promise { - for (const [project, { root }] of getProjects(tree)) { - visitNotIgnoredFiles(tree, root, (filePath) => { - if (extname(filePath) === '.ts') { - let changed = false; - let file = tree.read(filePath).toString(); - - // Need to import createTreeWithEmptyV1Workspace, and use it instead - if ( - file.includes('createTreeWithEmptyWorkspace(1)') || - file.includes('createTreeWithEmptyWorkspace()') - ) { - changed = true; - file = file.replace( - /createTreeWithEmptyWorkspace\(1\)/g, - 'createTreeWithEmptyV1Workspace()' - ); - file = file.replace( - /createTreeWithEmptyWorkspace\(\)/g, - 'createTreeWithEmptyV1Workspace()' - ); - - // Was only using v1, simple string replace updates imports. - if (!file.includes('createTreeWithEmptyWorkspace(2)')) { - file = file.replace( - 'createTreeWithEmptyWorkspace', - 'createTreeWithEmptyV1Workspace' - ); - } else { - const changes: StringChange[] = []; - const ast = tsquery.ast(file); - const importDeclarations = tsquery( - ast, - 'ImportDeclaration[moduleSpecifier.text="@nrwl/devkit/testing"]' - ); - for (const declaration of importDeclarations) { - const namedImports = tsquery( - declaration, - 'NamedImports ImportSpecifier' - ); - if (namedImports.length) { - const firstImport = namedImports[0].pos; - changes.push({ - type: ChangeType.Insert, - index: firstImport, - text: ' createTreeWithEmptyV1Workspace,', - }); - break; - } - } - file = applyChangesToString(file, changes); - } - } - - // Replace all instances with param set to 2, to version w/o param - if (file.includes('createTreeWithEmptyWorkspace(2)')) { - file = file.replace( - /createTreeWithEmptyWorkspace\(2\)/g, - 'createTreeWithEmptyWorkspace()' - ); - changed = true; - } - - if (changed) { - tree.write(filePath, file); - } - } - }); - } - await formatFiles(tree); -} diff --git a/packages/js/src/generators/library/library.ts b/packages/js/src/generators/library/library.ts index 6fe60afcb6e63..b526c12af7fd7 100644 --- a/packages/js/src/generators/library/library.ts +++ b/packages/js/src/generators/library/library.ts @@ -418,7 +418,7 @@ function normalizeOptions( options.bundler = 'none'; } - const { Linter } = require('@nrwl/linter'); + const { Linter } = ensurePackage('@nrwl/linter', nxVersion); if (options.config === 'npm-scripts') { options.unitTestRunner = 'none'; options.linter = Linter.None; diff --git a/packages/workspace/project.json b/packages/workspace/project.json index 3e50ac2ceabd5..bd83c03304189 100644 --- a/packages/workspace/project.json +++ b/packages/workspace/project.json @@ -87,12 +87,6 @@ }, { "command": "node ./scripts/add-dependency-to-build.js workspace nx" - }, - { - "command": "node ./scripts/add-dependency-to-build.js workspace @nrwl/devkit" - }, - { - "command": "node ./scripts/add-dependency-to-build.js workspace @nrwl/linter" } ], "parallel": false From 884c20009f90ce64be4d9ec508a1b15eecd4f9cd Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Thu, 23 Mar 2023 14:09:47 -0400 Subject: [PATCH 42/76] fix(nextjs): deference symlinks when copying public folder to dist (#15862) --- packages/next/src/executors/build/build.impl.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/next/src/executors/build/build.impl.ts b/packages/next/src/executors/build/build.impl.ts index 7f170fecc0c59..1bfd84efabbea 100644 --- a/packages/next/src/executors/build/build.impl.ts +++ b/packages/next/src/executors/build/build.impl.ts @@ -74,7 +74,9 @@ export default async function buildExecutor( createNextConfigFile(options, context); - copySync(join(root, 'public'), join(options.outputPath, 'public')); + copySync(join(root, 'public'), join(options.outputPath, 'public'), { + dereference: true, + }); return { success: true }; } From f384c738a1a154e1e61af9ea8f5a0f1ead57d835 Mon Sep 17 00:00:00 2001 From: Victor Savkin Date: Thu, 23 Mar 2023 15:17:06 -0400 Subject: [PATCH 43/76] chore(repo): update nx-cloud to 15.3.2 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index cc88391dc09bf..0a471d6bf8cd9 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@nrwl/js": "15.9.0-beta.4", "@nrwl/linter": "15.9.0-beta.4", "@nrwl/next": "15.9.0-beta.4", - "@nrwl/nx-cloud": "15.3.1", + "@nrwl/nx-cloud": "15.3.2", "@nrwl/react": "15.9.0-beta.4", "@nrwl/storybook": "15.9.0-beta.4", "@nrwl/web": "15.9.0-beta.4", diff --git a/yarn.lock b/yarn.lock index 21451d8b7c7d3..b198a54b1f27e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5096,10 +5096,10 @@ url-loader "^4.1.1" webpack-merge "^5.8.0" -"@nrwl/nx-cloud@15.3.1": - version "15.3.1" - resolved "https://registry.yarnpkg.com/@nrwl/nx-cloud/-/nx-cloud-15.3.1.tgz#f31acb39879a879cd4e547071196f0269618aed2" - integrity sha512-6NMXtmRQLDv8MiXfVLnqi0+qkY7IUdo0FN7lKXZ0Inlzxoky1gvYRzcI1gXyGuw9Ma/t+gjCLXUQAnUGdovYog== +"@nrwl/nx-cloud@15.3.2": + version "15.3.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-cloud/-/nx-cloud-15.3.2.tgz#524e7af8b19b2eb7c6720dbe7aaf55a7067e9a60" + integrity sha512-fga4duqcrkyT3djQpyRp8god3trb18hb+Tv0CYRrCcdNDHhzb6LOrxnenrFsjPjt38hyCdyu/ZYmHI/lQXyhAw== dependencies: axios "^0.21.2" chalk "4.1.0" From 24cb81d9bafc357f768aea1c61fa3a77b2f32157 Mon Sep 17 00:00:00 2001 From: Emily Xiong Date: Thu, 23 Mar 2023 16:38:39 -0400 Subject: [PATCH 44/76] feat(core): set interactive true for create-nx-workspace (#15863) --- docs/generated/cli/create-nx-workspace.md | 2 ++ docs/generated/packages/nx/documents/create-nx-workspace.md | 2 ++ packages/create-nx-workspace/bin/create-nx-workspace.ts | 1 + packages/create-nx-workspace/src/create-preset.ts | 1 + packages/create-nx-workspace/src/create-workspace-options.d.ts | 2 +- 5 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/generated/cli/create-nx-workspace.md b/docs/generated/cli/create-nx-workspace.md index 3e8d89e972d4d..961add286fabc 100644 --- a/docs/generated/cli/create-nx-workspace.md +++ b/docs/generated/cli/create-nx-workspace.md @@ -99,6 +99,8 @@ Show help Type: `boolean` +Default: `true` + Enable interactive mode with presets ### name diff --git a/docs/generated/packages/nx/documents/create-nx-workspace.md b/docs/generated/packages/nx/documents/create-nx-workspace.md index 3e8d89e972d4d..961add286fabc 100644 --- a/docs/generated/packages/nx/documents/create-nx-workspace.md +++ b/docs/generated/packages/nx/documents/create-nx-workspace.md @@ -99,6 +99,8 @@ Show help Type: `boolean` +Default: `true` + Enable interactive mode with presets ### name diff --git a/packages/create-nx-workspace/bin/create-nx-workspace.ts b/packages/create-nx-workspace/bin/create-nx-workspace.ts index 68561f0b116dc..8266a4c6c85ee 100644 --- a/packages/create-nx-workspace/bin/create-nx-workspace.ts +++ b/packages/create-nx-workspace/bin/create-nx-workspace.ts @@ -68,6 +68,7 @@ export const commandsObject: yargs.Argv = yargs .option('interactive', { describe: chalk.dim`Enable interactive mode with presets`, type: 'boolean', + default: true, }) .option('style', { describe: chalk.dim`Style option to be used when a preset with pregenerated app is selected`, diff --git a/packages/create-nx-workspace/src/create-preset.ts b/packages/create-nx-workspace/src/create-preset.ts index 34655e6c24ed9..b6fc87ffd1694 100644 --- a/packages/create-nx-workspace/src/create-preset.ts +++ b/packages/create-nx-workspace/src/create-preset.ts @@ -17,6 +17,7 @@ export async function createPreset( const { skipGit, ci, commit, nxCloud, ...restArgs } = parsedArgs; let args = unparse({ + interactive: true, ...restArgs, }).join(' '); diff --git a/packages/create-nx-workspace/src/create-workspace-options.d.ts b/packages/create-nx-workspace/src/create-workspace-options.d.ts index b8c7bb4d15501..c0ecfe190c974 100644 --- a/packages/create-nx-workspace/src/create-workspace-options.d.ts +++ b/packages/create-nx-workspace/src/create-workspace-options.d.ts @@ -6,7 +6,7 @@ export interface CreateWorkspaceOptions { nxCloud: boolean; // Enable Nx Cloud /** * @description Enable interactive mode with presets - * @default false + * @default true */ interactive?: boolean; // Enable interactive mode with presets /** From 65be9fcb3067f9a42976e76bd580a4d07934702a Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Thu, 23 Mar 2023 17:58:48 -0400 Subject: [PATCH 45/76] chore(repo): update nx to 15.9.0-beta.9 (#15865) --- package.json | 26 +- .../src/generators/library/library.ts | 4 +- .../src/generators/library/schema.d.ts | 2 +- yarn.lock | 336 ++++++++---------- 4 files changed, 170 insertions(+), 198 deletions(-) diff --git a/package.json b/package.json index 0a471d6bf8cd9..f7c433d07bc0b 100644 --- a/package.json +++ b/package.json @@ -59,18 +59,18 @@ "@ngrx/router-store": "~15.3.0", "@ngrx/store": "~15.3.0", "@nguniversal/builders": "~15.2.0", - "@nrwl/cypress": "15.9.0-beta.4", - "@nrwl/devkit": "15.9.0-beta.4", - "@nrwl/eslint-plugin-nx": "15.9.0-beta.4", - "@nrwl/jest": "15.9.0-beta.4", - "@nrwl/js": "15.9.0-beta.4", - "@nrwl/linter": "15.9.0-beta.4", - "@nrwl/next": "15.9.0-beta.4", + "@nrwl/cypress": "15.9.0-beta.9", + "@nrwl/devkit": "15.9.0-beta.9", + "@nrwl/eslint-plugin-nx": "15.9.0-beta.9", + "@nrwl/jest": "15.9.0-beta.9", + "@nrwl/js": "15.9.0-beta.9", + "@nrwl/linter": "15.9.0-beta.9", + "@nrwl/next": "15.9.0-beta.9", "@nrwl/nx-cloud": "15.3.2", - "@nrwl/react": "15.9.0-beta.4", - "@nrwl/storybook": "15.9.0-beta.4", - "@nrwl/web": "15.9.0-beta.4", - "@nrwl/webpack": "15.9.0-beta.4", + "@nrwl/react": "15.9.0-beta.9", + "@nrwl/storybook": "15.9.0-beta.9", + "@nrwl/web": "15.9.0-beta.9", + "@nrwl/webpack": "15.9.0-beta.9", "@parcel/watcher": "2.0.4", "@phenomnomnominal/tsquery": "4.1.1", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", @@ -131,6 +131,7 @@ "autoprefixer": "10.4.13", "babel-jest": "29.4.3", "babel-loader": "^9.1.2", + "babel-plugin-transform-async-to-promises": "^0.8.15", "browserslist": "^4.21.4", "chalk": "^4.1.0", "chokidar": "^3.5.1", @@ -199,7 +200,7 @@ "next-sitemap": "^3.1.10", "ng-packagr": "~15.2.2", "node-fetch": "^2.6.7", - "nx": "15.9.0-beta.4", + "nx": "15.9.0-beta.9", "octokit": "^2.0.14", "open": "^8.4.0", "ora": "5.3.0", @@ -336,4 +337,3 @@ ] } } - diff --git a/packages/workspace/src/generators/library/library.ts b/packages/workspace/src/generators/library/library.ts index c8f364d567738..ad1afb7b431d0 100644 --- a/packages/workspace/src/generators/library/library.ts +++ b/packages/workspace/src/generators/library/library.ts @@ -26,7 +26,7 @@ import { getRootTsConfigPathInTree, } from '../../utilities/ts-config'; import { nxVersion, typescriptVersion } from '../../utils/versions'; -import { Schema } from './schema'; +import type { Schema } from './schema'; export interface NormalizedSchema extends Schema { name: string; @@ -237,7 +237,7 @@ function normalizeOptions(tree: Tree, options: Schema): NormalizedSchema { } if (!options.linter) { - const { Linter } = require('@nrwl/linter'); + const { Linter } = ensurePackage('@nrwl/linter', nxVersion); options.linter = Linter.EsLint; } diff --git a/packages/workspace/src/generators/library/schema.d.ts b/packages/workspace/src/generators/library/schema.d.ts index b3c3529149bfa..c9dae74e97155 100644 --- a/packages/workspace/src/generators/library/schema.d.ts +++ b/packages/workspace/src/generators/library/schema.d.ts @@ -1,5 +1,5 @@ // nx-ignore-next-line -const { Linter } = require('@nrwl/linter'); // use require to import to avoid circular dependency +const { Linter } = require('@nrwl/linter'); export interface Schema { name: string; diff --git a/yarn.lock b/yarn.lock index b198a54b1f27e..2ef086ab2ce4d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4908,23 +4908,24 @@ dependencies: nx "15.8.0" -"@nrwl/cli@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/cli/-/cli-15.9.0-beta.4.tgz#0e6d10f18f3da0782bd42f50811ce531575ea46b" - integrity sha512-WFC/LS5/bIVKZUei8xXDNHF4jyHDv5YPK08ImZDKIoqEVhTMe5AJH9NklORiscT7ElHXULxWBDy6SyMWeVFJMA== - dependencies: - nx "15.9.0-beta.4" - -"@nrwl/cypress@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/cypress/-/cypress-15.9.0-beta.4.tgz#52ada11a09fd526981961688f298e92da0372028" - integrity sha512-coSRuvc0paO0bzp26PT+BBz+ihgmfZKpa9qDDEhLfY4xSa9YczISbaSs0+16/tRAXgZTYJvK5pWrmo+vDlbo4A== - dependencies: - "@nrwl/devkit" "15.9.0-beta.4" - "@nrwl/js" "15.9.0-beta.4" - "@nrwl/linter" "15.9.0-beta.4" - "@nrwl/workspace" "15.9.0-beta.4" +"@nrwl/cli@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/cli/-/cli-15.9.0-beta.9.tgz#ce3c39c8cafd57f821173289134f32ac0fdddad6" + integrity sha512-X+Z1pIucZ4eCSBYVaRiI2ynueS38u7tH+6Dp0c2etNiPyZWRDPnDmailcHYQ+YU7cVYb7Tg0xrn7xSIGe2jBMA== + dependencies: + nx "15.9.0-beta.9" + +"@nrwl/cypress@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/cypress/-/cypress-15.9.0-beta.9.tgz#80fce22f7ca75c150447dab650312bab220da0f3" + integrity sha512-bPPnC2Dgz6r7EOiYxvN/JU8NzeFu2wcP+BTe6A6K2S6XEC4EUttWFHKPbfxoyf46Jgy8mRnWhTAH5Tu9JuOH8g== + dependencies: + "@nrwl/devkit" "15.9.0-beta.9" + "@nrwl/js" "15.9.0-beta.9" + "@nrwl/linter" "15.9.0-beta.9" + "@nrwl/workspace" "15.9.0-beta.9" "@phenomnomnominal/tsquery" "4.1.1" + detect-port "^1.5.1" dotenv "~10.0.0" semver "7.3.4" @@ -4940,10 +4941,10 @@ tmp "~0.2.1" tslib "^2.3.0" -"@nrwl/devkit@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-15.9.0-beta.4.tgz#f25a6302203935f3d2edb7dd85a7cf5c38cb3e32" - integrity sha512-yMr9gLdwgXhVOmmwFyY5KrbnIlSxg/a5s++PTkgISlzYloXrKr0oCC+Qjpn0gz3S5OTjMa3BOM0eUMefDuYfqQ== +"@nrwl/devkit@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-15.9.0-beta.9.tgz#458d0689cf058b5f72b9210c52ba03ce2978327f" + integrity sha512-UltyK+oBUPt7dhfH2k8HbWv7+nMiv09QDtIMr6tYH3/2e54Dw8vGB24skvoOCFjZy9lThGaj6JzJGprka5dY0g== dependencies: "@phenomnomnominal/tsquery" "4.1.1" ejs "^3.1.7" @@ -4963,26 +4964,26 @@ semver "7.3.4" tslib "^2.3.0" -"@nrwl/eslint-plugin-nx@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/eslint-plugin-nx/-/eslint-plugin-nx-15.9.0-beta.4.tgz#ef37e69660757bd3844175781b86eed81a020caa" - integrity sha512-ZjIM0bIj0mZg3ODmPr5DnLZ+EVKwmjeZeM0Unn2AG3ES6fKrwsLwkZGSfy/GFekUSoqn+MIkB6PbAAvQPSrJFQ== +"@nrwl/eslint-plugin-nx@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/eslint-plugin-nx/-/eslint-plugin-nx-15.9.0-beta.9.tgz#6bbc6f77d6a11b2dadbcf057b84d814978a64496" + integrity sha512-54MLX1EwYIWWV4Autaz9tw09Att2QYgFVT9223KJBZyN66C9qOEngXeOzuh1FiAOdUoWm2mp241JbsqbBUgp6w== dependencies: - "@nrwl/devkit" "15.9.0-beta.4" + "@nrwl/devkit" "15.9.0-beta.9" "@typescript-eslint/utils" "^5.36.1" chalk "^4.1.0" confusing-browser-globals "^1.0.9" semver "7.3.4" -"@nrwl/jest@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/jest/-/jest-15.9.0-beta.4.tgz#e646b71a61c70b8c6f85de98852666554ce383b8" - integrity sha512-uape1Fuo3/dZkETvxroskxe4GuKHYRYDtAfwPEUZAhubOLD+bLQjR0dPEA/G26uM13XENXuX+OQ05ah+LM6joA== +"@nrwl/jest@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/jest/-/jest-15.9.0-beta.9.tgz#f5a5dc67387cd15eeae8001b2502c9e6d21e0092" + integrity sha512-etS6o9csV5/D4QAjvO2hmaa27L6QqGpAVRI7AMSl1Gjv41Q/OknwyeApG7/2qxQVhFsDhBCY1fD9nwfRvRJNjA== dependencies: "@jest/reporters" "28.1.1" "@jest/test-result" "28.1.1" - "@nrwl/devkit" "15.9.0-beta.4" - "@nrwl/js" "15.9.0-beta.4" + "@nrwl/devkit" "15.9.0-beta.9" + "@nrwl/js" "15.9.0-beta.9" "@phenomnomnominal/tsquery" "4.1.1" chalk "^4.1.0" dotenv "~10.0.0" @@ -5021,10 +5022,10 @@ tree-kill "1.2.2" tslib "^2.3.0" -"@nrwl/js@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/js/-/js-15.9.0-beta.4.tgz#4db4039e7fd289bba191df205af420f65bf17221" - integrity sha512-bkDzlSLCeHEojOMwhvXo3fA+efQJgDH0RRb++5SIWGdl2o6wLp0NjCKqzCmXr/WYo+OnyFjO/J8gopzgRYAMfg== +"@nrwl/js@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/js/-/js-15.9.0-beta.9.tgz#da6a82aeaa8232bb1ab83fde44aba0aa2424284a" + integrity sha512-+Z7GVmLEOM7zTAP8wjI+h84L1ObpgZIzoP38tDv6A9o1REOdd8LKiU7sTmQQMLHld38sOa9HuaEmNP6RrRHMIQ== dependencies: "@babel/core" "^7.15.0" "@babel/plugin-proposal-class-properties" "^7.14.5" @@ -5033,8 +5034,8 @@ "@babel/preset-env" "^7.15.0" "@babel/preset-typescript" "^7.15.0" "@babel/runtime" "^7.14.8" - "@nrwl/devkit" "15.9.0-beta.4" - "@nrwl/workspace" "15.9.0-beta.4" + "@nrwl/devkit" "15.9.0-beta.9" + "@nrwl/workspace" "15.9.0-beta.9" "@phenomnomnominal/tsquery" "4.1.1" babel-plugin-const-enum "^1.0.1" babel-plugin-macros "^2.8.0" @@ -5060,39 +5061,40 @@ tmp "~0.2.1" tslib "^2.3.0" -"@nrwl/linter@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/linter/-/linter-15.9.0-beta.4.tgz#b191a7d2fe23cea9a0f78189302f553a87625968" - integrity sha512-EGRdShm8wN8KbLrVb9BYwaiFC7wRE/cwhLaBcDp4S0IzWfGq02xVj3gExb253ES7/fKforMdbRWSbZAeAWgWkQ== +"@nrwl/linter@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/linter/-/linter-15.9.0-beta.9.tgz#ae8c2b4f6b44269663e2c6a816d572dd395724fe" + integrity sha512-8dSt640cnhdwpnATXPwx42ZF8SzB6FyXpAS06s+ab8g5lya/dB75lxmSH9m9LqyZ1ZUvkfpMoLT7zlZOU4bqnA== dependencies: - "@nrwl/devkit" "15.9.0-beta.4" - "@nrwl/js" "15.9.0-beta.4" + "@nrwl/devkit" "15.9.0-beta.9" + "@nrwl/js" "15.9.0-beta.9" "@phenomnomnominal/tsquery" "4.1.1" tmp "~0.2.1" tslib "^2.3.0" -"@nrwl/next@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/next/-/next-15.9.0-beta.4.tgz#fb2a552aa1b8996bc318608d86efecfabd228606" - integrity sha512-IrCMreYchqcdv1PkUPimaEhKYxk1XK3OUtMGC2tVFNMWjTlZSfgZ2uFOFEnQ4dw/DT8TdnIAksGO02A04iR6kQ== +"@nrwl/next@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/next/-/next-15.9.0-beta.9.tgz#d1c92c9fe895381e2e1b979a0acf7c48dff7f128" + integrity sha512-BPLxL9db0EjoXfZmw58956arMNcvfz/GlRLiND/gNu0bU7cVPkiy+Cw3gBbXUOpTYv0NT8Sm5JGQmknSH/PRSQ== dependencies: "@babel/plugin-proposal-decorators" "^7.14.5" - "@nrwl/cypress" "15.9.0-beta.4" - "@nrwl/devkit" "15.9.0-beta.4" - "@nrwl/jest" "15.9.0-beta.4" - "@nrwl/js" "15.9.0-beta.4" - "@nrwl/linter" "15.9.0-beta.4" - "@nrwl/react" "15.9.0-beta.4" - "@nrwl/webpack" "15.9.0-beta.4" - "@nrwl/workspace" "15.9.0-beta.4" + "@nrwl/devkit" "15.9.0-beta.9" + "@nrwl/js" "15.9.0-beta.9" + "@nrwl/linter" "15.9.0-beta.9" + "@nrwl/react" "15.9.0-beta.9" + "@nrwl/workspace" "15.9.0-beta.9" "@svgr/webpack" "^6.1.2" chalk "^4.1.0" + copy-webpack-plugin "^10.2.4" dotenv "~10.0.0" + express "^4.18.1" fs-extra "^11.1.0" + http-proxy-middleware "^2.0.6" ignore "^5.0.4" semver "7.3.4" ts-node "10.9.1" tsconfig-paths "^4.1.2" + tsconfig-paths-webpack-plugin "4.0.0" url-loader "^4.1.1" webpack-merge "^5.8.0" @@ -5115,140 +5117,114 @@ resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.8.0.tgz#3a1e57f1edf1a3df366f45e4142ec6d5fa875976" integrity sha512-R/DWeF7wVnN9UmUTl7XcKbfLuIeGQJhOYaf9NjADaVjl4p9F1ja6X9b/aPtSx2fmd3rRC2tHfNlEZs34eLtEuQ== -"@nrwl/nx-darwin-arm64@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.9.0-beta.4.tgz#0d358f0e9b12f315c354c1e74d9381ebc095702d" - integrity sha512-7KQQOsShAhhS8Cu26R0+s8lVaPyZWrllCNSJ6O6zycekwJTKf5HQ12rtM7Tj9Hk4LNnZyajwKVcwdNmdl74MDg== +"@nrwl/nx-darwin-arm64@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.9.0-beta.9.tgz#ce780cac52aa1c4e8671f5ce6ff7e39b40a7c5f6" + integrity sha512-0x0v3ogaLk9eVHF4NJo5qLWUEiuRfXx1M+r/qikdO0PubRSaWnPSWDpH7jqP2YKZMlG4815v1SysKhM7WGe4Gg== "@nrwl/nx-darwin-x64@15.8.0": version "15.8.0" resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.8.0.tgz#926802097bd69f2f10eb56fa79be7dd6235866c2" integrity sha512-jq812R8JXTUDDYhnO1c4BNrek10nan4ViHXjhq/bNvkXqA+I2EI0B7UTjr97urDGW8D0E2Skqu3tm9i1W3LJLg== -"@nrwl/nx-darwin-x64@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.9.0-beta.4.tgz#d7647717bfa64ceba012b9c52743db37cc004c59" - integrity sha512-5yzhMM2/PA3hYOputKDcHDZNiwmmfTgD7+1OrVeDXAM4Lf8bBUkvvMWFzRVkMEKf+hO9bpzKaJZN7Xy8DBvmsQ== +"@nrwl/nx-darwin-x64@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.9.0-beta.9.tgz#3349f29d9acbcbd7bcdf7e2bdee172b13cb0a288" + integrity sha512-7ZT7Sa7iwdSCYpCO8oNLiOSjqJmh+6FGG3NQivbfWRms9ktJwN4EXPSjj6ltBmtVkcy/rxaIOt65SrWW2IvWuw== "@nrwl/nx-linux-arm-gnueabihf@15.8.0": version "15.8.0" resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.8.0.tgz#d72d1438b6d7a62907160c247ebf6b03fd17e5f7" integrity sha512-pKNvu3slRElcPjk4qFblaqL25jJKZ9cYw/NUSfchMlvgEMXPSqFXHRg45U3iXMT61YFUKuK9y2l3AlCELagUUA== -"@nrwl/nx-linux-arm-gnueabihf@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.9.0-beta.4.tgz#6f34b9dd13b3d6b508c958f2fd387167748c8ca0" - integrity sha512-kvfGwL+DEOo7G/dJtwl3M6tNyXyhK4kja5a0B3scpAFpj4kHzHUS9LX3PA9qvPFhJhD0o9hYTn7i0+Cy7rLuYQ== +"@nrwl/nx-linux-arm-gnueabihf@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.9.0-beta.9.tgz#0be0ae4f100104292e16d7198af4fea996d4dc92" + integrity sha512-35L7lPKuuFve3il9XxkX+kAE9ESygsVrMBCjrEu2fYvI/F7jDlJKU2/xTFyYUWO4KVLSMAz8JzsGu1Og/Z2bHQ== "@nrwl/nx-linux-arm64-gnu@15.8.0": version "15.8.0" resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.8.0.tgz#5cf5c92219914d26c36b85a86b6efc75d3af8454" integrity sha512-Glkqlb1ln8ERYoDADY1She9wqjVbJuCpJV+lKQOrM7lLblmZIu6w3NtyoTE+bQNDz6yKlxaOftuTUVJKS+zLlw== -"@nrwl/nx-linux-arm64-gnu@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.9.0-beta.4.tgz#290aa4d881bf7f216ff25712f2b768902d621950" - integrity sha512-PK4DtUhK/COBSVJKcSq9DEzu0tRqZoAnZ576DG2dYBmP8V12sSavERSxzwhEIYlTZpO2maocgNbB0ZCs0YxgQQ== +"@nrwl/nx-linux-arm64-gnu@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.9.0-beta.9.tgz#a6b7446b9ae7376ad496ab16c60270c9028eda8d" + integrity sha512-LZUhCTKpnsBrEezfS8i2/UIj9Y2ZT0O5NxMBf0bGNn8wsULqon9mnE462NEb5Po4R4oAt3FsvUIgLmea/7973w== "@nrwl/nx-linux-arm64-musl@15.8.0": version "15.8.0" resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.8.0.tgz#c6092abade41e512877fa672503c7088aa5ed93c" integrity sha512-bQ74bGEXtmN0SkT7fIjGnf4Es7wRQ9j8gxSOkzbVVKEZ4rMj5ufaSc+0kBZ1Kpleg5yrj0dUMbH1tIdrbQPYGg== -"@nrwl/nx-linux-arm64-musl@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.9.0-beta.4.tgz#ef9f73caa0cbcc8b2cfc07bb3921dd2bad84e2a5" - integrity sha512-BZ+wIFW6ceC5yGnPZUFAcTuu0q+iCJRqgIwRykaQd2OeOrFuHVp2WnqcE2o7ie87EygkKN2ETfjS4//7NIIlVQ== +"@nrwl/nx-linux-arm64-musl@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.9.0-beta.9.tgz#68036b585bca6ae0ef9db78c1fb625008e357f0c" + integrity sha512-9dh+WY8CVuxd1g++U16ub4oD737mW3MlYn/snBHvxGDYFCeOwC/PxAQmjtsYPCuNJB9h/chNTuUmM2hf5ALbEw== "@nrwl/nx-linux-x64-gnu@15.8.0": version "15.8.0" resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.8.0.tgz#d5630cabc892c041a8a9c7694b4803e8e8f3e96e" integrity sha512-D4LT/06DtKbVmJBsC7J0O/jdbQZ2aGO2husAbwAEXt/gQXr8g+5ubcjMsJm8+pIkoXskPLQ1jOSw0+HA5W80uQ== -"@nrwl/nx-linux-x64-gnu@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.9.0-beta.4.tgz#7b7dd26cd0a1b0381374460d85484a8e1752c992" - integrity sha512-/Pa+Z6q2zNu+NVT6/BI67Vjs4OUBzflu7epsFGNnvYlEA0NiIpfdMfhzNIDcAYNW/CM4Yur0Owj9G2qA+moftQ== +"@nrwl/nx-linux-x64-gnu@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.9.0-beta.9.tgz#2028fa924ffe31b44e4a09d818c80a6f24d3efe9" + integrity sha512-vheOCSl9gB+lyfUTvhYTYZLB45ZSpLZCXaK6dakXTtFqzAmqHvRa36XtbFZFlV/1ajppG75+dqlswcqFZvaG/Q== "@nrwl/nx-linux-x64-musl@15.8.0": version "15.8.0" resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.8.0.tgz#a8a0fa8428bd1610f11cf739c20e36b08cc67671" integrity sha512-yRbeWH0tvTIxlZNrxIdnZ3dZ0noYqbPfICjlg1Zrok9VE5J+P3JenEdJV6JAKSCUOmH/lu08j+bA/NGgFX6QCg== -"@nrwl/nx-linux-x64-musl@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.9.0-beta.4.tgz#8d7b5670b0ec789dc7a13970a183dc11874b3ed8" - integrity sha512-JGlcUJKuyBsYqHNp9E/RHZKXGdFuo+vu05R3LPRekiJLYYJ1r0cHIeuO8htWAQWfHAXeLAP6/INuvXj+zn2U7w== +"@nrwl/nx-linux-x64-musl@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.9.0-beta.9.tgz#2edabb43e77365d87d79fdc6fb63e7f543a72c55" + integrity sha512-IggOsldzu45P+KAfnMfQyz4e8w6SdDpd5UFnoGaaPIWq51AL6Eab2tvxk6B3h6G3tskpvtYcs7fE4Y2HGBMpyw== "@nrwl/nx-win32-arm64-msvc@15.8.0": version "15.8.0" resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.8.0.tgz#82b205f9de162e28ed7e92965493c546b41a0b09" integrity sha512-TX6c8KYg25rlUzCZA86Hy9Bi/7rgFzdEx9v1fj0NezT8RYrupWekhazngBORKxTnM6bEiuKqnRkETIds8RPIZQ== -"@nrwl/nx-win32-arm64-msvc@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.9.0-beta.4.tgz#a50b0e7d6141d42c9f6bf29a1a217763007deec4" - integrity sha512-eaDkwyL8V03P4XKK7ran9OctpgVVOSyxswsi7Y1uw62f9+GGYuqOx1+2tM8A3VoD0uSTB/Aivf7gCdKEAoi8vg== +"@nrwl/nx-win32-arm64-msvc@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.9.0-beta.9.tgz#e2c75984394dff13d91ee6cb6873e62343e5c92c" + integrity sha512-eQ/Lb6Jpg0vxB1gJex8CiEQIagwmtHTnNUu9stYFuymn5ZGdo61RQ9/NU3gFkMWt3tMpKeaXh7I+1qII9fsD5Q== "@nrwl/nx-win32-x64-msvc@15.8.0": version "15.8.0" resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.8.0.tgz#723bd61f85f47b1683d42064c46330aa7d646bdd" integrity sha512-BsVjhiofd0SF0B2oWgWvYYzoPrxixk/Zb2EEW3RShY5L6jt+B0PpM4LPf/JKAEnmYBgRTzMlXrYMO+clwnKFdw== -"@nrwl/nx-win32-x64-msvc@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.9.0-beta.4.tgz#2c1a425fe3de97d30cf1c8baf1f6d2b1cc0fba5e" - integrity sha512-1DRuNH7qyeZ4lN2tixKHjqkRPj5n/TyaqG0xYuQfl9K1dP0Vtqnr8Vb2TbhlJtqvhRqviMmAc9bXYySb4cEpIA== +"@nrwl/nx-win32-x64-msvc@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.9.0-beta.9.tgz#2c4fff26bd9361f24f2690fcf018b67f04357364" + integrity sha512-XgPiJs0X/Sr5BZLbfrOTzcFjgzVY4qWu5AU0Nz1QIlMF0/AoRRDYKzepDX+93/Z4E1g+NKb/WDR32TZNXDzRYg== -"@nrwl/react@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/react/-/react-15.9.0-beta.4.tgz#f9dc433e0da3050579ac49664f21dfb3d2142f51" - integrity sha512-BlGFxGgJI7n6dbEPnoTtSuT14vcxezR0bGxiTs0VScrfT/OfPCWceirCXn06fg9IOvGhZL3ORkcIRPIzp/D64A== +"@nrwl/react@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/react/-/react-15.9.0-beta.9.tgz#00e88805c4e9c46b35566ff577dca4aad06dd679" + integrity sha512-+hhDtzB9Xc6F5o5Cqt9DXgvSLfqUMUBl4PqlzaJwEogFuYkXDucv5vEnph7XTXhuol3NhYtT0m5XTA8VOY15vQ== dependencies: - "@nrwl/devkit" "15.9.0-beta.4" - "@nrwl/js" "15.9.0-beta.4" - "@nrwl/linter" "15.9.0-beta.4" - "@nrwl/workspace" "15.9.0-beta.4" + "@nrwl/devkit" "15.9.0-beta.9" + "@nrwl/js" "15.9.0-beta.9" + "@nrwl/linter" "15.9.0-beta.9" + "@nrwl/workspace" "15.9.0-beta.9" "@phenomnomnominal/tsquery" "4.1.1" chalk "^4.1.0" minimatch "3.0.5" -"@nrwl/rollup@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/rollup/-/rollup-15.9.0-beta.4.tgz#512a3f60d9be1ebfa489773ae08f24a2fbe5aabc" - integrity sha512-qho4/ETUEP/FdgR0isL8NXrGzwyAK0tYpwX61f4d3SWCU9SuXGqzefvB5fr/xli9zenAbP42LyALCzbai9AZDg== - dependencies: - "@nrwl/devkit" "15.9.0-beta.4" - "@nrwl/js" "15.9.0-beta.4" - "@nrwl/workspace" "15.9.0-beta.4" - "@rollup/plugin-babel" "^5.3.0" - "@rollup/plugin-commonjs" "^20.0.0" - "@rollup/plugin-image" "^2.1.0" - "@rollup/plugin-json" "^4.1.0" - "@rollup/plugin-node-resolve" "^13.0.4" - autoprefixer "^10.4.9" - babel-plugin-transform-async-to-promises "^0.8.15" - chalk "^4.1.0" - dotenv "~10.0.0" - postcss "^8.4.14" - rollup "^2.56.2" - rollup-plugin-copy "^3.4.0" - rollup-plugin-peer-deps-external "^2.2.4" - rollup-plugin-postcss "^4.0.1" - rollup-plugin-typescript2 "0.34.1" - rxjs "^6.5.4" - tslib "^2.3.0" - -"@nrwl/storybook@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/storybook/-/storybook-15.9.0-beta.4.tgz#8205d1aabf743b29f3668540faf65c478e314fe7" - integrity sha512-L3J8ckQPU1UhIiOxNoloc/HWAY3zdEAbdIhLSl7EfseZseP1fEzrm0cPTNi02sHxlK7ts3xgVFKNlkfXCwfQzg== +"@nrwl/storybook@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/storybook/-/storybook-15.9.0-beta.9.tgz#a14984a25935c3f15b2433385c784be0d9402cf2" + integrity sha512-HVHQXxMvI8mYc3P/khQH7PjugUGqU9WpmxWfB409VfgPOWAW8sYY19l2ju973vnSUHhU9JcrP6NWSWZbzTHHTQ== dependencies: - "@nrwl/cypress" "15.9.0-beta.4" - "@nrwl/devkit" "15.9.0-beta.4" - "@nrwl/js" "15.9.0-beta.4" - "@nrwl/linter" "15.9.0-beta.4" - "@nrwl/workspace" "15.9.0-beta.4" + "@nrwl/cypress" "15.9.0-beta.9" + "@nrwl/devkit" "15.9.0-beta.9" + "@nrwl/js" "15.9.0-beta.9" + "@nrwl/linter" "15.9.0-beta.9" + "@nrwl/workspace" "15.9.0-beta.9" "@phenomnomnominal/tsquery" "4.1.1" dotenv "~10.0.0" semver "7.3.4" @@ -5267,39 +5243,35 @@ dependencies: nx "15.8.0" -"@nrwl/tao@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-15.9.0-beta.4.tgz#4750de5818a05efcc484631c9f15ec2fee6b45ac" - integrity sha512-rpJdLA1+DikO5hRUVXJuh8EfisR9iBw+4Y7GZ1loL4K7i0xzG5gHlZZhXKoSKg1GGJB6FsU1TeZ2ft7uyooimA== - dependencies: - nx "15.9.0-beta.4" - -"@nrwl/web@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/web/-/web-15.9.0-beta.4.tgz#9337584491c4d06c36e2271ad200995c0810249a" - integrity sha512-Xs/9UAYSolDHEyuXoxCJrPE4U5xsDhPyNTbKR2J61LQNJp72+YvV8VCGpKjhnEjgJmqdLWvfz4/5oXUnUIhqwg== - dependencies: - "@nrwl/cypress" "15.9.0-beta.4" - "@nrwl/devkit" "15.9.0-beta.4" - "@nrwl/jest" "15.9.0-beta.4" - "@nrwl/js" "15.9.0-beta.4" - "@nrwl/linter" "15.9.0-beta.4" - "@nrwl/rollup" "15.9.0-beta.4" - "@nrwl/workspace" "15.9.0-beta.4" +"@nrwl/tao@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-15.9.0-beta.9.tgz#75fc0f77fe761242b49975eb40540fb1997e789b" + integrity sha512-q8FvTgbNTfw0bBrm4LqAlxgRyWWwG+VeI2vfxvnlqqqQ9sT4ZZG4YnMNLhP3wxgzaqyEHfux3F8wdi4ZmMO5fQ== + dependencies: + nx "15.9.0-beta.9" + +"@nrwl/web@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/web/-/web-15.9.0-beta.9.tgz#f8fddb9bf3defa1ddb4d9d50eb0c0918ffc2f03c" + integrity sha512-+TMxK9ylG5W9clQD1ZDl/G/YOpyQ6oxPbQLHYwzIekv4/FX8ploTTUToxTH5eejo3fGB3vGJjsaaEuaC6D0o1Q== + dependencies: + "@nrwl/devkit" "15.9.0-beta.9" + "@nrwl/js" "15.9.0-beta.9" + "@nrwl/workspace" "15.9.0-beta.9" chalk "^4.1.0" chokidar "^3.5.1" http-server "^14.1.0" ignore "^5.0.4" tslib "^2.3.0" -"@nrwl/webpack@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/webpack/-/webpack-15.9.0-beta.4.tgz#f9c065222acced337cf4dcaddeb9b31f51b008c7" - integrity sha512-L4tYnlEMotK2NVgM+ev0bcdB+Bov8qlihjY7yr/WZY9EJzuI0AV7VOK06JhZBJRm4RO0Cuk6MEmiUKOq64AkwQ== +"@nrwl/webpack@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/webpack/-/webpack-15.9.0-beta.9.tgz#63862e5cb8a824169c60d5e6933f1b7e3f950df6" + integrity sha512-UQOOPVjg1LRylncm33q7PtLSPYETeHy0gamEEHOQ4ir0Zyk/06PR2vzylbWzhfPUuhIywxjwSaESCjm4WuT0Ag== dependencies: - "@nrwl/devkit" "15.9.0-beta.4" - "@nrwl/js" "15.9.0-beta.4" - "@nrwl/workspace" "15.9.0-beta.4" + "@nrwl/devkit" "15.9.0-beta.9" + "@nrwl/js" "15.9.0-beta.9" + "@nrwl/workspace" "15.9.0-beta.9" autoprefixer "^10.4.9" babel-loader "^9.1.2" chalk "^4.1.0" @@ -5366,13 +5338,13 @@ yargs "^17.6.2" yargs-parser "21.1.1" -"@nrwl/workspace@15.9.0-beta.4": - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/@nrwl/workspace/-/workspace-15.9.0-beta.4.tgz#99c5d473cbfb540fb47be8faf293491aee72b33c" - integrity sha512-39Tq+uKQuT9Mci/IRVge8Rm/ssWmVU6b0XRFuMCH38xJ6D2QQlnZeaggloLOb8qha8l2zsxX7dPtJTmKcrBW6g== +"@nrwl/workspace@15.9.0-beta.9": + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/@nrwl/workspace/-/workspace-15.9.0-beta.9.tgz#9fb1aa2b861d99ddffac1d8b82da4649cf67b7be" + integrity sha512-ZXHL5MO+tA70qKfRKNyNdvh+HksZoGBMhU8YoPJt0iKSr+5AQ0M7J5nPBzh0W9hEbmhGh2tyH3oFl2DFKITDqw== dependencies: - "@nrwl/devkit" "15.9.0-beta.4" - "@nrwl/linter" "15.9.0-beta.4" + "@nrwl/devkit" "15.9.0-beta.9" + "@nrwl/linter" "15.9.0-beta.9" "@parcel/watcher" "2.0.4" chalk "^4.1.0" chokidar "^3.5.1" @@ -5385,7 +5357,7 @@ ignore "^5.0.4" minimatch "3.0.5" npm-run-path "^4.0.1" - nx "15.9.0-beta.4" + nx "15.9.0-beta.9" open "^8.4.0" rxjs "^6.5.4" tmp "~0.2.1" @@ -19644,13 +19616,13 @@ nx@15.8.0: "@nrwl/nx-win32-arm64-msvc" "15.8.0" "@nrwl/nx-win32-x64-msvc" "15.8.0" -nx@15.9.0-beta.4: - version "15.9.0-beta.4" - resolved "https://registry.yarnpkg.com/nx/-/nx-15.9.0-beta.4.tgz#7192e4f84d3bd669a7fd005ce5b7465335a45c6e" - integrity sha512-kVfBQ2TE0jxmlzO6NXsxWo/GbJRAnWXAv4vwkBU5X8UgqhcJ9DWu7QkrTfXZ6GA1+03djqqmz+B4jnMuJc0ixA== +nx@15.9.0-beta.9: + version "15.9.0-beta.9" + resolved "https://registry.yarnpkg.com/nx/-/nx-15.9.0-beta.9.tgz#ab46c8efd9656dcd5c3ba034ddb976d6981fb578" + integrity sha512-IwpHnF/vEJQxdh5WIWGM1/0E68zMJpr3JwFl9I8QUTVVsMQKOitxrrEoH9kRBttk8+KABWTpIfy1LW7J/JANsg== dependencies: - "@nrwl/cli" "15.9.0-beta.4" - "@nrwl/tao" "15.9.0-beta.4" + "@nrwl/cli" "15.9.0-beta.9" + "@nrwl/tao" "15.9.0-beta.9" "@parcel/watcher" "2.0.4" "@yarnpkg/lockfile" "^1.1.0" "@yarnpkg/parsers" "^3.0.0-rc.18" @@ -19685,15 +19657,15 @@ nx@15.9.0-beta.4: yargs "^17.6.2" yargs-parser "21.1.1" optionalDependencies: - "@nrwl/nx-darwin-arm64" "15.9.0-beta.4" - "@nrwl/nx-darwin-x64" "15.9.0-beta.4" - "@nrwl/nx-linux-arm-gnueabihf" "15.9.0-beta.4" - "@nrwl/nx-linux-arm64-gnu" "15.9.0-beta.4" - "@nrwl/nx-linux-arm64-musl" "15.9.0-beta.4" - "@nrwl/nx-linux-x64-gnu" "15.9.0-beta.4" - "@nrwl/nx-linux-x64-musl" "15.9.0-beta.4" - "@nrwl/nx-win32-arm64-msvc" "15.9.0-beta.4" - "@nrwl/nx-win32-x64-msvc" "15.9.0-beta.4" + "@nrwl/nx-darwin-arm64" "15.9.0-beta.9" + "@nrwl/nx-darwin-x64" "15.9.0-beta.9" + "@nrwl/nx-linux-arm-gnueabihf" "15.9.0-beta.9" + "@nrwl/nx-linux-arm64-gnu" "15.9.0-beta.9" + "@nrwl/nx-linux-arm64-musl" "15.9.0-beta.9" + "@nrwl/nx-linux-x64-gnu" "15.9.0-beta.9" + "@nrwl/nx-linux-x64-musl" "15.9.0-beta.9" + "@nrwl/nx-win32-arm64-msvc" "15.9.0-beta.9" + "@nrwl/nx-win32-x64-msvc" "15.9.0-beta.9" oauth-sign@~0.9.0: version "0.9.0" From 2c13d4ae3d70058a88f493706240d3da472e90cf Mon Sep 17 00:00:00 2001 From: FrozenPandaz Date: Thu, 23 Mar 2023 21:50:51 -0400 Subject: [PATCH 46/76] chore(misc): publish 15.9.0-beta.10 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index b449d65da2d7a..502fea15a6dfb 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["build/packages/*", "build/packages/nx/native-packages/*"], - "version": "15.9.0-beta.9", + "version": "15.9.0-beta.10", "granularPathspec": false, "command": { "publish": { From 20434787a36dd8628417b2269b867429222ff22c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leosvel=20P=C3=A9rez=20Espinosa?= Date: Fri, 24 Mar 2023 13:54:49 +0000 Subject: [PATCH 47/76] cleanup(misc): remove internal rootProject flag from generators that can derive it (#15834) --- .../cypress/generators/cypress-project.json | 7 ------- .../jest/generators/jest-project.json | 7 ------- .../src/generators/application/lib/add-e2e.ts | 1 - .../application/lib/add-unit-test-runner.ts | 1 - .../cypress-project/cypress-project.spec.ts | 2 -- .../cypress-project/cypress-project.ts | 1 + .../generators/cypress-project/schema.d.ts | 1 - .../generators/cypress-project/schema.json | 7 ------- .../jest-project/jest-project.spec.ts | 2 -- .../generators/jest-project/jest-project.ts | 21 ++++++++++++------- .../jest-project/lib/check-for-test-target.ts | 7 +++++-- .../jest-project/lib/create-files.ts | 4 ++-- .../jest-project/lib/update-jestconfig.ts | 7 +++++-- .../jest-project/lib/update-tsconfig.ts | 7 +++++-- .../jest-project/lib/update-workspace.ts | 7 +++++-- .../src/generators/jest-project/schema.d.ts | 5 ++++- .../src/generators/jest-project/schema.json | 7 ------- 17 files changed, 40 insertions(+), 54 deletions(-) diff --git a/docs/generated/packages/cypress/generators/cypress-project.json b/docs/generated/packages/cypress/generators/cypress-project.json index dd445d8c118e5..2664ff37123e8 100644 --- a/docs/generated/packages/cypress/generators/cypress-project.json +++ b/docs/generated/packages/cypress/generators/cypress-project.json @@ -64,13 +64,6 @@ "description": "Do not add dependencies to `package.json`.", "x-priority": "internal" }, - "rootProject": { - "description": "Create a application at the root of the workspace", - "type": "boolean", - "default": false, - "hidden": true, - "x-priority": "internal" - }, "bundler": { "description": "The Cypress bundler to use.", "type": "string", diff --git a/docs/generated/packages/jest/generators/jest-project.json b/docs/generated/packages/jest/generators/jest-project.json index 36cbb1933c04a..aa49372db0464 100644 --- a/docs/generated/packages/jest/generators/jest-project.json +++ b/docs/generated/packages/jest/generators/jest-project.json @@ -75,13 +75,6 @@ "type": "boolean", "default": false, "description": "Use JavaScript instead of TypeScript for config files" - }, - "rootProject": { - "description": "Add Jest to an application at the root of the workspace", - "type": "boolean", - "default": false, - "hidden": true, - "x-priority": "internal" } }, "required": [], diff --git a/packages/angular/src/generators/application/lib/add-e2e.ts b/packages/angular/src/generators/application/lib/add-e2e.ts index bf3b1104cbdb4..2da653ec36c22 100644 --- a/packages/angular/src/generators/application/lib/add-e2e.ts +++ b/packages/angular/src/generators/application/lib/add-e2e.ts @@ -23,7 +23,6 @@ export async function addE2e(tree: Tree, options: NormalizedSchema) { skipFormat: options.skipFormat, standaloneConfig: options.standaloneConfig, skipPackageJson: options.skipPackageJson, - rootProject: options.rootProject, }); } } diff --git a/packages/angular/src/generators/application/lib/add-unit-test-runner.ts b/packages/angular/src/generators/application/lib/add-unit-test-runner.ts index 487267d69e4ae..e94d3aeb02c14 100644 --- a/packages/angular/src/generators/application/lib/add-unit-test-runner.ts +++ b/packages/angular/src/generators/application/lib/add-unit-test-runner.ts @@ -13,7 +13,6 @@ export async function addUnitTestRunner(host: Tree, options: NormalizedSchema) { supportTsx: false, skipSerializers: false, skipPackageJson: options.skipPackageJson, - rootProject: options.rootProject, }); } } diff --git a/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts b/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts index cd52cc3dba4f2..8ad18b51dfa3e 100644 --- a/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts +++ b/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts @@ -4,7 +4,6 @@ import { readProjectConfiguration, Tree, updateProjectConfiguration, - WorkspaceJsonConfiguration, } from '@nrwl/devkit'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; import { cypressProjectGenerator } from './cypress-project'; @@ -264,7 +263,6 @@ describe('Cypress Project', () => { name: 'e2e-tests', baseUrl: 'http://localhost:1234', project: 'root', - rootProject: true, }); expect(tree.listChanges().map((c) => c.path)).toEqual( expect.arrayContaining([ diff --git a/packages/cypress/src/generators/cypress-project/cypress-project.ts b/packages/cypress/src/generators/cypress-project/cypress-project.ts index 643ef7546e674..afd73efcdc366 100644 --- a/packages/cypress/src/generators/cypress-project/cypress-project.ts +++ b/packages/cypress/src/generators/cypress-project/cypress-project.ts @@ -43,6 +43,7 @@ import { Schema } from './schema'; export interface CypressProjectSchema extends Schema { projectName: string; projectRoot: string; + rootProject: boolean; } function createFiles(tree: Tree, options: CypressProjectSchema) { diff --git a/packages/cypress/src/generators/cypress-project/schema.d.ts b/packages/cypress/src/generators/cypress-project/schema.d.ts index b991f6cad0973..f497ddf87087a 100644 --- a/packages/cypress/src/generators/cypress-project/schema.d.ts +++ b/packages/cypress/src/generators/cypress-project/schema.d.ts @@ -11,6 +11,5 @@ export interface Schema { setParserOptionsProject?: boolean; standaloneConfig?: boolean; skipPackageJson?: boolean; - rootProject?: boolean; bundler?: 'webpack' | 'vite' | 'none'; } diff --git a/packages/cypress/src/generators/cypress-project/schema.json b/packages/cypress/src/generators/cypress-project/schema.json index badf188844a3f..20aaf2ccaf009 100644 --- a/packages/cypress/src/generators/cypress-project/schema.json +++ b/packages/cypress/src/generators/cypress-project/schema.json @@ -66,13 +66,6 @@ "description": "Do not add dependencies to `package.json`.", "x-priority": "internal" }, - "rootProject": { - "description": "Create a application at the root of the workspace", - "type": "boolean", - "default": false, - "hidden": true, - "x-priority": "internal" - }, "bundler": { "description": "The Cypress bundler to use.", "type": "string", diff --git a/packages/jest/src/generators/jest-project/jest-project.spec.ts b/packages/jest/src/generators/jest-project/jest-project.spec.ts index c53d7886a3ed6..4b345f1e0dff6 100644 --- a/packages/jest/src/generators/jest-project/jest-project.spec.ts +++ b/packages/jest/src/generators/jest-project/jest-project.spec.ts @@ -379,7 +379,6 @@ describe('jestProject', () => { await jestProjectGenerator(tree, { ...defaultOptions, project: 'my-project', - rootProject: true, }); expect(tree.read('jest.config.ts', 'utf-8')).toMatchInlineSnapshot(` "/* eslint-disable */ @@ -411,7 +410,6 @@ describe('jestProject', () => { await jestProjectGenerator(tree, { ...defaultOptions, project: 'my-project', - rootProject: true, js: true, }); expect(tree.read('jest.config.js', 'utf-8')).toMatchInlineSnapshot(` diff --git a/packages/jest/src/generators/jest-project/jest-project.ts b/packages/jest/src/generators/jest-project/jest-project.ts index 341987668f533..f3af021e1b2c1 100644 --- a/packages/jest/src/generators/jest-project/jest-project.ts +++ b/packages/jest/src/generators/jest-project/jest-project.ts @@ -3,12 +3,13 @@ import { checkForTestTarget } from './lib/check-for-test-target'; import { createFiles } from './lib/create-files'; import { updateTsConfig } from './lib/update-tsconfig'; import { updateWorkspace } from './lib/update-workspace'; -import { JestProjectSchema } from './schema'; +import { JestProjectSchema, NormalizedJestProjectSchema } from './schema'; import { formatFiles, Tree, convertNxGenerator, GeneratorCallback, + readProjectConfiguration, } from '@nrwl/devkit'; const schemaDefaults = { @@ -17,11 +18,13 @@ const schemaDefaults = { supportTsx: false, skipSetupFile: false, skipSerializers: false, - rootProject: false, testEnvironment: 'jsdom', } as const; -function normalizeOptions(options: JestProjectSchema) { +function normalizeOptions( + tree: Tree, + options: JestProjectSchema +): NormalizedJestProjectSchema { if (!options.testEnvironment) { options.testEnvironment = 'jsdom'; } @@ -39,15 +42,17 @@ function normalizeOptions(options: JestProjectSchema) { options.skipSerializers = true; } - if (!options.skipSetupFile) { - return options; + if (options.skipSetupFile) { + // setupFile is always 'none' + options.setupFile = schemaDefaults.setupFile; } - // setupFile is always 'none' - options.setupFile = schemaDefaults.setupFile; + const project = readProjectConfiguration(tree, options.project); + return { ...schemaDefaults, ...options, + rootProject: project.root === '.' || project.root === '', }; } @@ -55,7 +60,7 @@ export async function jestProjectGenerator( tree: Tree, schema: JestProjectSchema ): Promise { - const options = normalizeOptions(schema); + const options = normalizeOptions(tree, schema); const installTask = await init(tree, options); checkForTestTarget(tree, options); diff --git a/packages/jest/src/generators/jest-project/lib/check-for-test-target.ts b/packages/jest/src/generators/jest-project/lib/check-for-test-target.ts index a1f4269a6fdae..d1c2405bd589e 100644 --- a/packages/jest/src/generators/jest-project/lib/check-for-test-target.ts +++ b/packages/jest/src/generators/jest-project/lib/check-for-test-target.ts @@ -1,7 +1,10 @@ import { readProjectConfiguration, Tree } from '@nrwl/devkit'; -import { JestProjectSchema } from '../schema'; +import { NormalizedJestProjectSchema } from '../schema'; -export function checkForTestTarget(tree: Tree, options: JestProjectSchema) { +export function checkForTestTarget( + tree: Tree, + options: NormalizedJestProjectSchema +) { const projectConfig = readProjectConfiguration(tree, options.project); if (projectConfig?.targets?.test) { throw new Error(`${options.project}: already has a test target set.`); diff --git a/packages/jest/src/generators/jest-project/lib/create-files.ts b/packages/jest/src/generators/jest-project/lib/create-files.ts index c473c4cba8e99..2995f1551c87f 100644 --- a/packages/jest/src/generators/jest-project/lib/create-files.ts +++ b/packages/jest/src/generators/jest-project/lib/create-files.ts @@ -5,9 +5,9 @@ import { Tree, } from '@nrwl/devkit'; import { join } from 'path'; -import { JestProjectSchema } from '../schema'; +import { NormalizedJestProjectSchema } from '../schema'; -export function createFiles(tree: Tree, options: JestProjectSchema) { +export function createFiles(tree: Tree, options: NormalizedJestProjectSchema) { const projectConfig = readProjectConfiguration(tree, options.project); const filesFolder = diff --git a/packages/jest/src/generators/jest-project/lib/update-jestconfig.ts b/packages/jest/src/generators/jest-project/lib/update-jestconfig.ts index f04769928d50f..1aa9d449dd0f6 100644 --- a/packages/jest/src/generators/jest-project/lib/update-jestconfig.ts +++ b/packages/jest/src/generators/jest-project/lib/update-jestconfig.ts @@ -1,5 +1,5 @@ import { findRootJestConfig } from '../../../utils/config/find-root-jest-files'; -import { JestProjectSchema } from '../schema'; +import { NormalizedJestProjectSchema } from '../schema'; import { addPropertyToJestConfig } from '../../../utils/config/update-config'; import { readProjectConfiguration, Tree } from '@nrwl/devkit'; @@ -10,7 +10,10 @@ function isUsingUtilityFunction(host: Tree) { ); } -export function updateJestConfig(host: Tree, options: JestProjectSchema) { +export function updateJestConfig( + host: Tree, + options: NormalizedJestProjectSchema +) { if (isUsingUtilityFunction(host)) { return; } diff --git a/packages/jest/src/generators/jest-project/lib/update-tsconfig.ts b/packages/jest/src/generators/jest-project/lib/update-tsconfig.ts index 13b22c29004ed..8d834293aa693 100644 --- a/packages/jest/src/generators/jest-project/lib/update-tsconfig.ts +++ b/packages/jest/src/generators/jest-project/lib/update-tsconfig.ts @@ -1,8 +1,11 @@ import { join } from 'path'; -import { JestProjectSchema } from '../schema'; +import { NormalizedJestProjectSchema } from '../schema'; import { readProjectConfiguration, Tree, updateJson } from '@nrwl/devkit'; -export function updateTsConfig(host: Tree, options: JestProjectSchema) { +export function updateTsConfig( + host: Tree, + options: NormalizedJestProjectSchema +) { const projectConfig = readProjectConfiguration(host, options.project); if (!host.exists(join(projectConfig.root, 'tsconfig.json'))) { throw new Error( diff --git a/packages/jest/src/generators/jest-project/lib/update-workspace.ts b/packages/jest/src/generators/jest-project/lib/update-workspace.ts index c9807756422dd..009dc32115d48 100644 --- a/packages/jest/src/generators/jest-project/lib/update-workspace.ts +++ b/packages/jest/src/generators/jest-project/lib/update-workspace.ts @@ -1,4 +1,4 @@ -import { JestProjectSchema } from '../schema'; +import { NormalizedJestProjectSchema } from '../schema'; import { readProjectConfiguration, Tree, @@ -7,7 +7,10 @@ import { normalizePath, } from '@nrwl/devkit'; -export function updateWorkspace(tree: Tree, options: JestProjectSchema) { +export function updateWorkspace( + tree: Tree, + options: NormalizedJestProjectSchema +) { const projectConfig = readProjectConfiguration(tree, options.project); projectConfig.targets.test = { executor: '@nrwl/jest:jest', diff --git a/packages/jest/src/generators/jest-project/schema.d.ts b/packages/jest/src/generators/jest-project/schema.d.ts index e5480c12866aa..67af979a821d0 100644 --- a/packages/jest/src/generators/jest-project/schema.d.ts +++ b/packages/jest/src/generators/jest-project/schema.d.ts @@ -16,5 +16,8 @@ export interface JestProjectSchema { compiler?: 'tsc' | 'babel' | 'swc'; skipPackageJson?: boolean; js?: boolean; - rootProject?: boolean; } + +export type NormalizedJestProjectSchema = JestProjectSchema & { + rootProject: boolean; +}; diff --git a/packages/jest/src/generators/jest-project/schema.json b/packages/jest/src/generators/jest-project/schema.json index c1755115c9cc2..a39db66f6bfd5 100644 --- a/packages/jest/src/generators/jest-project/schema.json +++ b/packages/jest/src/generators/jest-project/schema.json @@ -74,13 +74,6 @@ "type": "boolean", "default": false, "description": "Use JavaScript instead of TypeScript for config files" - }, - "rootProject": { - "description": "Add Jest to an application at the root of the workspace", - "type": "boolean", - "default": false, - "hidden": true, - "x-priority": "internal" } }, "required": [] From a8f77df6f1ef71f855314ed72d8457f2376e7a27 Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Fri, 24 Mar 2023 11:35:54 -0400 Subject: [PATCH 48/76] fix(core): fix bin path (#15883) --- packages/tao/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tao/package.json b/packages/tao/package.json index f8a9bb31acd69..d3e64b5a9d021 100644 --- a/packages/tao/package.json +++ b/packages/tao/package.json @@ -27,7 +27,7 @@ "url": "https://github.com/nrwl/nx/issues" }, "bin": { - "tao": "./index" + "tao": "./index.js" }, "homepage": "https://nx.dev", "dependencies": { From 7a07b23de563cdf0e743577d5c19521c461fc887 Mon Sep 17 00:00:00 2001 From: FrozenPandaz Date: Fri, 24 Mar 2023 11:37:02 -0400 Subject: [PATCH 49/76] chore(misc): publish 15.9.0-beta.11 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 502fea15a6dfb..279a825ecd07d 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["build/packages/*", "build/packages/nx/native-packages/*"], - "version": "15.9.0-beta.10", + "version": "15.9.0-beta.11", "granularPathspec": false, "command": { "publish": { From 04b6ddb5966e90ea1e9c4fb9d8d6b73e11b67683 Mon Sep 17 00:00:00 2001 From: Emily Xiong Date: Fri, 24 Mar 2023 12:51:35 -0400 Subject: [PATCH 50/76] feat(react-native): update @react-native-community/cli to 10.2.1 (#15738) --- docs/generated/manifests/menus.json | 16 +++ docs/generated/manifests/packages.json | 18 +++ docs/generated/packages-metadata.json | 18 +++ .../react-native/documents/overview.md | 80 ++++++++++-- .../react-native/executors/build-android.json | 74 ++++++++++- .../react-native/executors/build-ios.json | 106 ++++++++++++++++ .../react-native/executors/bundle.json | 11 +- .../react-native/executors/pod-install.json | 28 +++++ .../react-native/executors/run-android.json | 82 ++++++++---- .../react-native/executors/run-ios.json | 86 ++++++++----- .../react-native/react-native-plugin.md | 80 ++++++++++-- e2e/react-native/src/react-native.test.ts | 51 +++++++- packages/expo/src/utils/symlink-task.ts | 4 +- .../docs/build-android-examples.md | 21 ++-- .../react-native/docs/build-ios-examples.md | 109 ++++++++++++++++ .../react-native/docs/run-android-examples.md | 4 +- .../react-native/docs/run-ios-examples.md | 31 ++++- packages/react-native/executors.json | 20 +++ packages/react-native/migrations.json | 19 +++ .../build-android/build-android.impl.ts | 83 +++++++----- .../src/executors/build-android/schema.d.ts | 28 ++++- .../src/executors/build-android/schema.json | 82 +++++++++++- .../src/executors/build-ios/build-ios.impl.ts | 118 ++++++++++++++++++ .../src/executors/build-ios/compat.ts | 5 + .../src/executors/build-ios/schema.d.ts | 22 ++++ .../src/executors/build-ios/schema.json | 106 ++++++++++++++++ .../src/executors/bundle/schema.json | 20 ++- .../src/executors/pod-install/compat.ts | 5 + .../executors/pod-install/pod-install.impl.ts | 21 ++++ .../src/executors/pod-install/schema.d.ts | 3 + .../src/executors/pod-install/schema.json | 18 +++ .../executors/run-android/run-android.impl.ts | 37 ++---- .../src/executors/run-android/schema.d.ts | 29 +++-- .../src/executors/run-android/schema.json | 83 ++++++++---- .../src/executors/run-ios/run-ios.impl.ts | 39 +++--- .../src/executors/run-ios/schema.d.ts | 24 ++-- .../src/executors/run-ios/schema.json | 93 +++++++++----- .../src/executors/start/schema.d.ts | 2 + .../generators/application/lib/add-project.ts | 13 ++ .../add-build-ios-target.spec.ts | 36 ++++++ .../update-15-9-1/add-build-ios-target.ts | 35 ++++++ .../react-native/src/utils/get-cli-options.ts | 30 +++++ .../src/utils/pod-install-task.ts | 19 ++- .../react-native/src/utils/symlink-task.ts | 4 +- packages/react-native/src/utils/versions.ts | 4 +- 45 files changed, 1542 insertions(+), 275 deletions(-) create mode 100644 docs/generated/packages/react-native/executors/build-ios.json create mode 100644 docs/generated/packages/react-native/executors/pod-install.json create mode 100644 packages/react-native/docs/build-ios-examples.md create mode 100644 packages/react-native/src/executors/build-ios/build-ios.impl.ts create mode 100644 packages/react-native/src/executors/build-ios/compat.ts create mode 100644 packages/react-native/src/executors/build-ios/schema.d.ts create mode 100644 packages/react-native/src/executors/build-ios/schema.json create mode 100644 packages/react-native/src/executors/pod-install/compat.ts create mode 100644 packages/react-native/src/executors/pod-install/pod-install.impl.ts create mode 100644 packages/react-native/src/executors/pod-install/schema.d.ts create mode 100644 packages/react-native/src/executors/pod-install/schema.json create mode 100644 packages/react-native/src/migrations/update-15-9-1/add-build-ios-target.spec.ts create mode 100644 packages/react-native/src/migrations/update-15-9-1/add-build-ios-target.ts create mode 100644 packages/react-native/src/utils/get-cli-options.ts diff --git a/docs/generated/manifests/menus.json b/docs/generated/manifests/menus.json index 1360c31836833..d40c6c527ea51 100644 --- a/docs/generated/manifests/menus.json +++ b/docs/generated/manifests/menus.json @@ -5743,6 +5743,14 @@ "isExternal": false, "disableCollapsible": false }, + { + "id": "build-ios", + "path": "/packages/react-native/executors/build-ios", + "name": "build-ios", + "children": [], + "isExternal": false, + "disableCollapsible": false + }, { "id": "start", "path": "/packages/react-native/executors/start", @@ -5774,6 +5782,14 @@ "children": [], "isExternal": false, "disableCollapsible": false + }, + { + "id": "pod-install", + "path": "/packages/react-native/executors/pod-install", + "name": "pod-install", + "children": [], + "isExternal": false, + "disableCollapsible": false } ], "isExternal": false, diff --git a/docs/generated/manifests/packages.json b/docs/generated/manifests/packages.json index 5ed45108f1c83..4a32dbdc955a0 100644 --- a/docs/generated/manifests/packages.json +++ b/docs/generated/manifests/packages.json @@ -2213,6 +2213,15 @@ "path": "/packages/react-native/executors/build-android", "type": "executor" }, + "/packages/react-native/executors/build-ios": { + "description": "Build iOS app", + "file": "generated/packages/react-native/executors/build-ios.json", + "hidden": false, + "name": "build-ios", + "originalFilePath": "/packages/react-native/src/executors/build-ios/schema.json", + "path": "/packages/react-native/executors/build-ios", + "type": "executor" + }, "/packages/react-native/executors/start": { "description": "Starts the Javascript server that communicates with connected devices.", "file": "generated/packages/react-native/executors/start.json", @@ -2248,6 +2257,15 @@ "originalFilePath": "/packages/react-native/src/executors/storybook/schema.json", "path": "/packages/react-native/executors/storybook", "type": "executor" + }, + "/packages/react-native/executors/pod-install": { + "description": "Run `pod install` in the `ios` directory.", + "file": "generated/packages/react-native/executors/pod-install.json", + "hidden": false, + "name": "pod-install", + "originalFilePath": "/packages/react-native/src/executors/pod-install/schema.json", + "path": "/packages/react-native/executors/pod-install", + "type": "executor" } }, "generators": { diff --git a/docs/generated/packages-metadata.json b/docs/generated/packages-metadata.json index a97571dc94f5c..89a282adffef0 100644 --- a/docs/generated/packages-metadata.json +++ b/docs/generated/packages-metadata.json @@ -2186,6 +2186,15 @@ "path": "react-native/executors/build-android", "type": "executor" }, + { + "description": "Build iOS app", + "file": "generated/packages/react-native/executors/build-ios.json", + "hidden": false, + "name": "build-ios", + "originalFilePath": "/packages/react-native/src/executors/build-ios/schema.json", + "path": "react-native/executors/build-ios", + "type": "executor" + }, { "description": "Starts the Javascript server that communicates with connected devices.", "file": "generated/packages/react-native/executors/start.json", @@ -2221,6 +2230,15 @@ "originalFilePath": "/packages/react-native/src/executors/storybook/schema.json", "path": "react-native/executors/storybook", "type": "executor" + }, + { + "description": "Run `pod install` in the `ios` directory.", + "file": "generated/packages/react-native/executors/pod-install.json", + "hidden": false, + "name": "pod-install", + "originalFilePath": "/packages/react-native/src/executors/pod-install/schema.json", + "path": "react-native/executors/pod-install", + "type": "executor" } ], "generators": [ diff --git a/docs/generated/packages/react-native/documents/overview.md b/docs/generated/packages/react-native/documents/overview.md index a183575fcf724..4c58fdabd4ded 100644 --- a/docs/generated/packages/react-native/documents/overview.md +++ b/docs/generated/packages/react-native/documents/overview.md @@ -52,7 +52,7 @@ yarn add --dev @nrwl/react-native To create additional React Native apps run: ```shell -nx g @nrwl/react-native:app your-app-name +nx g @nrwl/react-native:app ``` ### Generating Libraries @@ -60,7 +60,7 @@ nx g @nrwl/react-native:app your-app-name To generate a new library run: ```shell -npx nx g @nrwl/react-native:lib your-lib-name +nx g @nrwl/react-native:lib your-lib-name ``` ### Generating Components @@ -68,20 +68,78 @@ npx nx g @nrwl/react-native:lib your-lib-name To generate a new component inside library run: ```shell -npx nx g @nrwl/react-native:component your-component-name --project=your-lib-name --export +nx g @nrwl/react-native:component your-component-name --project=your-lib-name --export ``` Replace `your-lib-name` with the app's name as defined in your `tsconfig.base.json` file or the `name` property of your `package.json` -## Using React Native +### Upgrade React Native -- [run-ios](/packages/react-native/executors/run-ios) - Builds your app and starts it on iOS simulator or device -- [run-android](/packages/react-native/executors/run-android) - Builds your app and starts it on a connected Android emulator or device -- [build-android](/packages/react-native/executors/build-android) - Release Build for Android -- [start](/packages/react-native/executors/start) - Starts the server that communicates with connected devices -- [bundle](/packages/react-native/executors/bundle) - Builds the JavaScript bundle for offline use -- [sync-deps](/packages/react-native/executors/sync-deps) - Syncs dependencies to package.json (required for autolinking) -- [ensure-symlink](/packages/react-native/executors/ensure-symlink) - Ensure workspace node_modules is symlink under app's node_modules folder +The Nx CLI provides the [`migrate` command](/core-features/automate-updating-dependencies) to help you stay up to date with the latest version of Nx. + +#### Use upgrade-native Generator + +To upgrade native iOS and Android code to latest, you can use the [upgrade-native](/packages/react-native/generators/upgrade-native) generator: + +```shell +nx generate @nrwl/react-native:upgrade-native +``` + +This is a command that will replace the iOS and Android native code folder entirely. + +#### Upgrade Manually + +You can also upgrade React Native iOS and Android code using the [rn-diff-purge](https://react-native-community.github.io/upgrade-helper/) project. + +### Start Metro Server + +To start the server that communicates with connected devices: + +```shell +nx start +``` + +### Run iOS + +To build your app and start it on iOS simulator or device: + +```shell +nx run-ios +``` + +### Run Android + +To build your app and start it on a connected Android emulator or device: + +```shell +nx run-android +``` + +### Build iOS + +To build an iOS app: + +```shell +nx build-ios +``` + +The build artifacts will be located under `/ios/build`. + +You can specify the build folder by setting the `buildFolder` option: + +```shell +nx build ios --buildFolder="./build" +``` + +### Build Android + +To build an Android app, run: + +```shell +nx build-android +``` + +The build artifacts will be located under `/android/app/build`. ## More Documentation diff --git a/docs/generated/packages/react-native/executors/build-android.json b/docs/generated/packages/react-native/executors/build-android.json index 3be3792e0e492..a3589d9e8f8f2 100644 --- a/docs/generated/packages/react-native/executors/build-android.json +++ b/docs/generated/packages/react-native/executors/build-android.json @@ -10,23 +10,87 @@ "title": "Release Build for Android", "description": "Build target options for Android.", "type": "object", + "presets": [ + { + "name": "Build Android for current device architecture", + "keys": ["activeArchOnly"] + }, + { "name": "Build Android without metro cache", "keys": ["resetCache"] }, + { "name": "Build Android with specific tasks", "keys": ["tasks"] }, + { "name": "Build Android with a specific mode", "keys": ["mode"] } + ], "properties": { "apk": { "type": "boolean", - "description": "Generate apk file(s) rather than a bundle (`.aab`)." + "description": "Generate apk file(s) rather than a bundle (`.aab`).", + "x-deprecated": "Use `tasks` option instead, e.g. `tasks=['bundleRelease']` to generate aab, `tasks=['assembleDebug']` to generate apk. Will be removed in Nx 17." }, "debug": { "type": "boolean", - "description": "Generate a debug build instead of a release build." + "description": "Generate a debug build instead of a release build.", + "x-deprecated": "Use `mode` option instead, e.g. `mode='debug'`. Deprecated from @react-native-community/cli. Will be removed in Nx 17." }, "gradleTask": { "type": "string", - "description": "Override default gradle task incase of multi build variants" + "description": "Override default gradle task incase of multi build variants", + "x-deprecated": "Use `tasks` option instead, e.g. `tasks=['assembleDebug']`. Will be removed in Nx 17." + }, + "mode": { + "type": "string", + "description": "Specify your app's build variant", + "default": "debug", + "examples": ["debug", "release"], + "x-priority": "important" + }, + "packager": { + "type": "boolean", + "description": "Launch packager while building", + "default": true + }, + "port": { + "type": "number", + "description": "The port where the packager server is listening on.", + "default": 8081 + }, + "tasks": { + "type": "array", + "items": { "type": "string" }, + "description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.", + "examples": [ + "assembleDebug", + "assembleRelease", + "bundleDebug", + "bundleRelease", + "installDebug", + "installRelease" + ] + }, + "activeArchOnly": { + "type": "boolean", + "description": "Build native libraries only for the current device architecture for debug builds.", + "default": false + }, + "extraParams": { + "type": "string", + "description": "Custom params passed to gradle build command" + }, + "interactive": { + "type": "boolean", + "description": "Explicitly select build type and flavour to use before running a build" + }, + "sync": { + "type": "boolean", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", + "default": true + }, + "resetCache": { + "type": "boolean", + "description": "Resets metro cache.", + "default": false } }, "required": [], - "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {}\n }\n }\n}\n```\n\n```bash\nnx run mobile:build-android\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Build with custom gradleTask\" %}\nThe `gradleTask` option accepts any custom gradle task, such as `assembleDebug`, `assembleRelease`, `bundleDebug`, `bundleRelease`:\n\n```json\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {\n \"gradleTask\": \"assembleDebug\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Create a build with apk format\" %}\n\nThe `apk` option allows you determine the format of android build. If set as true, it will create a build with `.apk` extension under apk folder; if set as false, it will create with `.aab` extension under bundle folder.\n\n```json\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {\n \"apk\": true\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Build for debug/release\" %}\n\nIf set `debug` option as `true`, it will create a debug build; if set as `false`, it will create a release build.\n\n```json\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {\n \"debug\": true\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n", - "presets": [] + "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {}\n }\n }\n}\n```\n\n```bash\nnx run mobile:build-android\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Build with custom tasks\" %}\nThe `tasks` option accepts any custom gradle task, such as `assembleDebug`, `assembleRelease`, `bundleDebug`, `bundleRelease`, `installDebug`, `installRelease`.\nFor example, pass in `bundleRelease` or `bundleRelease` to tasks, it will create with `.aab` extension under bundle folder.\nPass in `assembleDebug` or `assembleRelease` to tasks, it will create a build with `.apk` extension under apk folder.\nPass in `installDebug` or `installRelease` to tasks, it will create a build with `.apk` extension and immediately install it on a running emulator or connected device.\n\n```json\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {\n \"tasks\": [\"bundleRelease\"]\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Build for debug/release\" %}\n\nThe `mode` option allows you determine whether to build for debug/release apk.\n\n```json\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {\n \"mode\": \"debug\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Build for current device architecture\" %}\n\nThe `activeArchOnly` option allows you to build native libraries only for the current device architecture for debug builds.\n\n```json\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {\n \"activeArchOnly\": true\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n" }, "description": "Release Build for Android.", "aliases": [], diff --git a/docs/generated/packages/react-native/executors/build-ios.json b/docs/generated/packages/react-native/executors/build-ios.json new file mode 100644 index 0000000000000..b946ec070e411 --- /dev/null +++ b/docs/generated/packages/react-native/executors/build-ios.json @@ -0,0 +1,106 @@ +{ + "name": "build-ios", + "implementation": "/packages/react-native/src/executors/build-ios/build-ios.impl.ts", + "schema": { + "$schema": "http://json-schema.org/schema", + "version": 2, + "cli": "nx", + "title": "React Native Build iOS executor", + "description": "Build iOS app.", + "type": "object", + "presets": [ + { "name": "Build iOS for a simulator", "keys": ["simulator"] }, + { "name": "Build iOS for a device", "keys": ["device"] }, + { "name": "Build iOS for a device with udid", "keys": ["udid"] }, + { + "name": "Run `pod install` before building iOS app", + "keys": ["install"] + } + ], + "properties": { + "simulator": { + "type": "string", + "description": "Explicitly set simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version: \"iPhone 6 (10.0)\"", + "examples": [ + "iPhone 14", + "iPhone 13", + "iPhone 12", + "iPhone 11", + "iPhone X" + ], + "x-priority": "important" + }, + "mode": { + "type": "string", + "description": "Explicitly set the scheme configuration to use", + "default": "Debug", + "examples": ["Debug", "Release"], + "x-priority": "important" + }, + "schema": { + "type": "string", + "description": "Explicitly set Xcode scheme to use" + }, + "device": { + "type": "string", + "description": "Explicitly set device to use by name. The value is not required if you have a single device connected." + }, + "udid": { + "type": "string", + "description": "Explicitly set device to use by udid" + }, + "verbose": { + "type": "boolean", + "description": "Do not use xcbeautify or xcpretty even if installed" + }, + "port": { + "type": "number", + "description": "The port where the packager server is listening on.", + "default": 8081 + }, + "xcconfig": { + "type": "string", + "description": "Explicitly set xcconfig to use" + }, + "buildFolder": { + "type": "string", + "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory", + "default": "./build" + }, + "interactive": { + "type": "boolean", + "description": "Explicitly select which scheme and configuration to use before running a build" + }, + "extraParams": { + "type": "string", + "description": "Custom params that will be passed to xcodebuild command." + }, + "install": { + "type": "boolean", + "description": "Runs `pod install` for native modules before building iOS app." + }, + "sync": { + "type": "boolean", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", + "default": true + }, + "resetCache": { + "type": "boolean", + "description": "Resets metro cache.", + "default": false + }, + "packager": { + "type": "boolean", + "description": "Launch packager while building", + "default": true + } + }, + "required": [], + "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"build-ios\": {\n \"executor\": \"@nrwl/react-native:build-ios\",\n \"options\": {}\n }\n }\n}\n```\n\n```bash\nnx run mobile:build-ios\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Build the Debug/Release app\" %}\nThe `buildFolder` option allows to specify the location for ios build artifacts. It corresponds to Xcode's -derivedDataPath.\n\n```json\n \"build-ios\": {\n \"executor\": \"@nrwl/react-native:build-ios\",\n \"options\": {\n \"buildFolder\": \"dist/ios/build\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Build the Debug/Release app\" %}\nThe `mode` option allows to specify the xcode configuartion, such as `Debug` or `Release`.\n\n```json\n \"build-ios\": {\n \"executor\": \"@nrwl/react-native:build-ios\",\n \"options\": {\n \"mode\": \"Release\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Build for a simulator\" %}\nTo see all the available simulators, run command:\n\n```bash\nxcrun simctl list\n```\n\nThe `simulator` option allows you to launch your iOS app in a specific simulator:\n\n```json\n \"build-ios\": {\n \"executor\": \"@nrwl/react-native:build-ios\",\n \"options\": {\n \"simulator\": \"iPhone 14 Pro\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Build for a device\" %}\nThe `device` option allows you to launch your iOS app in a specific device.\n\nTo see all the available device, run command:\n\n```bash\nxcrun simctl list\n```\n\n```json\n \"build-ios\": {\n \"executor\": \"@nrwl/react-native:build-ios\",\n \"options\": {\n \"device\": \"deviceName\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Set Device by udid\" %}\nThe `udid` option allows you to explicitly set device to use by udid.\n\nTo see all the available simulators and devices with udid, run command:\n\n```bash\nxcrun simctl list\n```\n\n```json\n \"build-ios\": {\n \"executor\": \"@nrwl/react-native:build-ios\",\n \"options\": {\n \"udid\": \"device udid\"\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n" + }, + "description": "Build iOS app", + "aliases": [], + "hidden": false, + "path": "/packages/react-native/src/executors/build-ios/schema.json", + "type": "executor" +} diff --git a/docs/generated/packages/react-native/executors/bundle.json b/docs/generated/packages/react-native/executors/bundle.json index 3b7238b1d75bb..ab66fe2ace182 100644 --- a/docs/generated/packages/react-native/executors/bundle.json +++ b/docs/generated/packages/react-native/executors/bundle.json @@ -10,6 +10,12 @@ "title": "Offline JS Bundle for React Native", "description": "JS Bundle target options.", "type": "object", + "presets": [ + { "name": "Bundle for a specific platform", "keys": ["platform"] }, + { "name": "Bundle a development build", "keys": ["dev"] }, + { "name": "Bundle to a specific output path", "keys": ["bundleOutput"] }, + { "name": "Bundle without global cache", "keys": ["resetCache"] } + ], "properties": { "entryFile": { "type": "string", @@ -68,13 +74,12 @@ }, "readGlobalCache": { "type": "boolean", - "description": "Removes cached files.", + "description": "Try to fetch transformed JS code from the global cache, if configured.", "default": false } }, "required": ["platform", "entryFile", "bundleOutput"], - "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"outputs\": [\"{projectRoot}/build\"],\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\"\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\"\n }\n }\n }\n}\n```\n\n```bash\nnx run mobile:bundle-ios\nnx run mobile:bundle-android\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Bundle with sourcemap\" %}\nThe `sourcemapOutput` option allows you to specify the path of the source map relative to app folder:\n\n```json\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\",\n \"sourcemapOutput\": \"../../dist/apps/mobile/ios/main.map\",\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\",\n \"sourcemapOutput\": \"../../dist/apps/mobile/android/main.map\",\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Create a dev/release bundle\" %}\n\nThe `dev` option determines whether to create a dev or release bundle. The default value is `true`, by setting it as `false`, warnings are disabled and the bundle is minified.\n\n```json\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\",\n \"dev\": false\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\",\n \"dev\": false\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Create a minified bundle\" %}\n\nThe `minify` option allows you to create a minified bundle:\n\n```json\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\",\n \"minify\": true\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\",\n \"minify\": true\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n", - "presets": [] + "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"outputs\": [\"{projectRoot}/build\"],\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\"\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\"\n }\n }\n }\n}\n```\n\n```bash\nnx run mobile:bundle-ios\nnx run mobile:bundle-android\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Bundle with sourcemap\" %}\nThe `sourcemapOutput` option allows you to specify the path of the source map relative to app folder:\n\n```json\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\",\n \"sourcemapOutput\": \"../../dist/apps/mobile/ios/main.map\",\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\",\n \"sourcemapOutput\": \"../../dist/apps/mobile/android/main.map\",\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Create a dev/release bundle\" %}\n\nThe `dev` option determines whether to create a dev or release bundle. The default value is `true`, by setting it as `false`, warnings are disabled and the bundle is minified.\n\n```json\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\",\n \"dev\": false\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\",\n \"dev\": false\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Create a minified bundle\" %}\n\nThe `minify` option allows you to create a minified bundle:\n\n```json\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\",\n \"minify\": true\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\",\n \"minify\": true\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n" }, "description": "Builds the JavaScript bundle for offline use.", "aliases": [], diff --git a/docs/generated/packages/react-native/executors/pod-install.json b/docs/generated/packages/react-native/executors/pod-install.json new file mode 100644 index 0000000000000..da7c4b7f32ed1 --- /dev/null +++ b/docs/generated/packages/react-native/executors/pod-install.json @@ -0,0 +1,28 @@ +{ + "name": "pod-install", + "implementation": "/packages/react-native/src/executors/pod-install/pod-install.impl.ts", + "schema": { + "version": 2, + "outputCapture": "direct-nodejs", + "cli": "nx", + "$id": "NxReactNativePodInstall", + "$schema": "http://json-schema.org/schema", + "title": "Run Pod Install for React Native iOS Project", + "description": "Run `pod install` for React Native iOS Project.", + "type": "object", + "properties": { + "buildFolder": { + "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory", + "type": "string", + "default": "./build" + } + }, + "required": ["buildFolder"], + "presets": [] + }, + "description": "Run `pod install` in the `ios` directory.", + "aliases": [], + "hidden": false, + "path": "/packages/react-native/src/executors/pod-install/schema.json", + "type": "executor" +} diff --git a/docs/generated/packages/react-native/executors/run-android.json b/docs/generated/packages/react-native/executors/run-android.json index 3e94190db3231..9eadf2db19b9d 100644 --- a/docs/generated/packages/react-native/executors/run-android.json +++ b/docs/generated/packages/react-native/executors/run-android.json @@ -12,9 +12,14 @@ "type": "object", "presets": [ { - "name": "Run Android without cache", - "keys": ["variant", "sync", "port", "packager", "resetCache"] - } + "name": "Run Android for the current device architecture", + "keys": ["activeArchOnly"] + }, + { + "name": "Lists all available Android devices and simulators", + "keys": ["listDevices"] + }, + { "name": "Run Android without metro cache", "keys": ["resetCache"] } ], "properties": { "variant": { @@ -22,7 +27,12 @@ "description": "Specify your app's build variant (e.g. `debug`, `release`).", "default": "debug", "examples": ["debug", "release"], - "x-priority": "important" + "x-deprecated": "Deprecated from @react-native-community/cli, use mode instead, e.g. mode=debug. Will be remove in Nx 17." + }, + "jetifier": { + "type": "boolean", + "description": "Run Jetifier – the AndroidX transition tool. By default it runs before Gradle to ease working with libraries that don't support AndroidX yet.", + "x-deprecated": "Deprecated from @react-native-community/cli. Will be remove in Nx 17." }, "appId": { "type": "string", @@ -41,18 +51,25 @@ "type": "string", "description": "Builds your app and starts it on a specific device/simulator with the given device id (listed by running `adb devices` on the command line)." }, - "tasks": { + "listDevices": { + "type": "boolean", + "description": "Lists all available Android devices and simulators and let you choose one to run the app", + "default": false + }, + "binaryPath": { "type": "string", - "description": "Run custom Gradle tasks. If this argument is provided, then `--variant` option is ignored. Example: `yarn react-native run-android --tasks clean,installDebug`." + "description": "Path relative to project root where pre-built .apk binary lives." }, - "jetifier": { - "type": "boolean", - "description": "Run Jetifier – the AndroidX transition tool. By default it runs before Gradle to ease working with libraries that don't support AndroidX yet.", - "default": true + "mode": { + "type": "string", + "description": "Specify your app's build variant", + "default": "debug", + "examples": ["debug", "release"], + "x-priority": "important" }, - "sync": { + "packager": { "type": "boolean", - "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", + "description": "Launch packager while building", "default": true }, "port": { @@ -60,32 +77,45 @@ "description": "The port where the packager server is listening on.", "default": 8081 }, - "terminal": { - "type": "string", - "description": "Launches the Metro Bundler in a new window using the specified terminal path." - }, - "packager": { - "type": "boolean", - "description": "Starts the packager server.", - "default": true + "tasks": { + "type": "array", + "items": { "type": "string" }, + "description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.", + "examples": [ + "assembleDebug", + "assembleRelease", + "bundleDebug", + "bundleRelease", + "installDebug", + "installRelease" + ] }, - "resetCache": { + "activeArchOnly": { "type": "boolean", - "description": "Resets metro cache.", + "description": "Build native libraries only for the current device architecture for debug builds.", + "examples": ["x86_64", "arm64-v8a"], "default": false }, + "extraParams": { + "type": "string", + "description": "Custom params passed to gradle build command" + }, "interactive": { "type": "boolean", - "description": "Run packager server in interactive mode.", + "description": "Explicitly select build type and flavour to use before running a build" + }, + "sync": { + "type": "boolean", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "default": true }, - "activeArchOnly": { + "resetCache": { "type": "boolean", - "description": "Builds only for the active architecture (e.g. x86_64, arm64-v8a).", + "description": "Resets metro cache.", "default": false } }, - "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"run-android\": {\n \"executor\": \"@nrwl/react-native:run-android\",\n \"options\": {}\n }\n }\n}\n```\n\n```bash\nnx run mobile:run-android\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Run on a specific device/simulator\" %}\nTo see all the avaiable emulators, run command:\n\n```bash\nemulator -list-avds\n```\n\nThe `deviceId` option allows you to launch your android app in a specific device/simulator:\n\n```json\n \"run-android\": {\n \"executor\": \"@nrwl/react-native:run-android\",\n \"options\": {\n \"deviceId\": \"Pixel_5_API_30\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Run the debug/release app\" %}\nThe `variant` option allows to specify the build variant, such as `debug` or `release`.\n\n```json\n \"run-android\": {\n \"executor\": \"@nrwl/react-native:run-android\",\n \"options\": {\n \"variant\": \"release\"\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n" + "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"run-android\": {\n \"executor\": \"@nrwl/react-native:run-android\",\n \"options\": {}\n }\n }\n}\n```\n\n```bash\nnx run mobile:run-android\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Run on a specific device/simulator\" %}\nTo see all the avaiable emulators, run command:\n\n```bash\nemulator -list-avds\n```\n\nThe `deviceId` option allows you to launch your android app in a specific device/simulator:\n\n```json\n \"run-android\": {\n \"executor\": \"@nrwl/react-native:run-android\",\n \"options\": {\n \"deviceId\": \"Pixel_5_API_30\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Run the debug/release app\" %}\nThe `mode` option allows to specify the build variant, such as `debug` or `release`.\n\n```json\n \"run-android\": {\n \"executor\": \"@nrwl/react-native:run-android\",\n \"options\": {\n \"mode\": \"release\"\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n" }, "description": "Runs Android application.", "aliases": [], diff --git a/docs/generated/packages/react-native/executors/run-ios.json b/docs/generated/packages/react-native/executors/run-ios.json index 035c0df6c13d6..fad11adbf51f0 100644 --- a/docs/generated/packages/react-native/executors/run-ios.json +++ b/docs/generated/packages/react-native/executors/run-ios.json @@ -11,16 +11,12 @@ "description": "Run iOS target options.", "type": "object", "presets": [ + { "name": "Run iOS on a simulator", "keys": ["simulator"] }, + { "name": "Run iOS on a device", "keys": ["device"] }, + { "name": "Run iOS on a device with udid", "keys": ["udid"] }, { - "name": "Run iOS without cache", - "keys": [ - "xcodeConfiguration", - "install", - "sync", - "port", - "packager", - "resetCache" - ] + "name": "Run `pod install` before building iOS app", + "keys": ["install"] } ], "properties": { @@ -29,16 +25,11 @@ "description": "Explicitly set the Xcode configuration to use.", "default": "Debug", "examples": ["Debug", "Release"], - "x-priority": "important" - }, - "scheme": { - "type": "string", - "description": "Explicitly set the Xcode scheme to use." + "x-deprecated": "Use `mode` instead. Deprecated from @react-native-community/cli. Will be removed in Nx 17." }, "simulator": { "type": "string", - "description": "Explicitly set simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version: `iPhone X (12.1)`.", - "default": "iPhone 14", + "description": "Explicitly set simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version: \"iPhone 6 (10.0)\"", "examples": [ "iPhone 14", "iPhone 13", @@ -48,34 +39,59 @@ ], "x-priority": "important" }, - "device": { + "mode": { "type": "string", - "description": "Explicitly set device to use by name. The value is not required if you have a single device connected.", + "description": "Explicitly set the scheme configuration to use", + "default": "Debug", + "examples": ["Debug", "Release"], "x-priority": "important" }, - "install": { - "type": "boolean", - "description": "Runs `pod install` for native modules before building iOS app.", - "default": false + "schema": { + "type": "string", + "description": "Explicitly set Xcode scheme to use" }, - "sync": { + "device": { + "type": "string", + "description": "Explicitly set device to use by name. The value is not required if you have a single device connected." + }, + "udid": { + "type": "string", + "description": "Explicitly set device to use by udid" + }, + "verbose": { "type": "boolean", - "description": "Syncs npm dependencies to `package.json` (for React Native autolink). Always true when `--install` is used.", - "default": true, - "x-priority": "internal" + "description": "Do not use xcbeautify or xcpretty even if installed" }, "port": { "type": "number", "description": "The port where the packager server is listening on.", "default": 8081 }, - "terminal": { + "xcconfig": { "type": "string", - "description": "Launches the Metro Bundler in a new window using the specified terminal path." + "description": "Explicitly set xcconfig to use" }, - "packager": { + "buildFolder": { + "type": "string", + "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory.", + "buildFolder": "./build" + }, + "interactive": { "type": "boolean", - "description": "Starts the packager server.", + "description": "Explicitly select which scheme and configuration to use before running a build" + }, + "extraParams": { + "type": "string", + "description": "Custom params that will be passed to xcodebuild command." + }, + "install": { + "type": "boolean", + "description": "Runs `pod install` for native modules before building iOS app.", + "default": false + }, + "sync": { + "type": "boolean", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "default": true }, "resetCache": { @@ -83,13 +99,17 @@ "description": "Resets metro cache.", "default": false }, - "interactive": { + "packager": { "type": "boolean", - "description": "Run packager server in interactive mode.", + "description": "Launch packager while building", "default": true + }, + "binaryPath": { + "type": "string", + "description": "Path relative to project root where pre-built .app binary lives." } }, - "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {}\n }\n }\n}\n```\n\n```bash\nnx run mobile:run-ios\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Run on a simulator\" %}\nTo see all the available simulators, run command:\n\n```bash\nxcrun simctl list\n```\n\nThe `simulator` option allows you to launch your iOS app in a specific simulator:\n\n```json\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {\n \"simulator\": \"iPhone 14 Pro\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Run on a device\" %}\nThe `device` option allows you to launch your iOS app in a specific device.\n\n```json\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {\n \"device\": \"deviceName\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Run the Debug/Release app\" %}\nThe `xcodeConfiguration` option allows to specify the xcode configuartion, such as `Debug` or `Release`.\n\n```json\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {\n \"xcodeConfiguration\": \"Release\"\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n" + "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {}\n }\n }\n}\n```\n\n```bash\nnx run mobile:run-ios\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Build the Debug/Release app\" %}\nThe `mode` option allows to specify the xcode configuartion schema, such as `Debug` or `Release`.\n\n```json\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {\n \"mode\": \"Release\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Run on a simulator\" %}\nTo see all the available simulators, run command:\n\n```bash\nxcrun simctl list\n```\n\nThe `simulator` option allows you to launch your iOS app in a specific simulator:\n\n```json\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {\n \"simulator\": \"iPhone 14 Pro\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Run on a device\" %}\nThe `device` option allows you to launch your iOS app in a specific device.\n\nTo see all the available devices, run command:\n\n```bash\nxcrun simctl list\n```\n\n```json\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {\n \"device\": \"deviceName\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Set Device by udid\" %}\nThe `udid` option allows you to explicitly set device to use by udid.\n\nTo see all the available simulators and devices with udid, run command:\n\n```bash\nxcrun simctl list\n```\n\n```json\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {\n \"udid\": \"device udid\"\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n" }, "description": "Runs iOS application.", "aliases": [], diff --git a/docs/shared/packages/react-native/react-native-plugin.md b/docs/shared/packages/react-native/react-native-plugin.md index a183575fcf724..4c58fdabd4ded 100644 --- a/docs/shared/packages/react-native/react-native-plugin.md +++ b/docs/shared/packages/react-native/react-native-plugin.md @@ -52,7 +52,7 @@ yarn add --dev @nrwl/react-native To create additional React Native apps run: ```shell -nx g @nrwl/react-native:app your-app-name +nx g @nrwl/react-native:app ``` ### Generating Libraries @@ -60,7 +60,7 @@ nx g @nrwl/react-native:app your-app-name To generate a new library run: ```shell -npx nx g @nrwl/react-native:lib your-lib-name +nx g @nrwl/react-native:lib your-lib-name ``` ### Generating Components @@ -68,20 +68,78 @@ npx nx g @nrwl/react-native:lib your-lib-name To generate a new component inside library run: ```shell -npx nx g @nrwl/react-native:component your-component-name --project=your-lib-name --export +nx g @nrwl/react-native:component your-component-name --project=your-lib-name --export ``` Replace `your-lib-name` with the app's name as defined in your `tsconfig.base.json` file or the `name` property of your `package.json` -## Using React Native +### Upgrade React Native -- [run-ios](/packages/react-native/executors/run-ios) - Builds your app and starts it on iOS simulator or device -- [run-android](/packages/react-native/executors/run-android) - Builds your app and starts it on a connected Android emulator or device -- [build-android](/packages/react-native/executors/build-android) - Release Build for Android -- [start](/packages/react-native/executors/start) - Starts the server that communicates with connected devices -- [bundle](/packages/react-native/executors/bundle) - Builds the JavaScript bundle for offline use -- [sync-deps](/packages/react-native/executors/sync-deps) - Syncs dependencies to package.json (required for autolinking) -- [ensure-symlink](/packages/react-native/executors/ensure-symlink) - Ensure workspace node_modules is symlink under app's node_modules folder +The Nx CLI provides the [`migrate` command](/core-features/automate-updating-dependencies) to help you stay up to date with the latest version of Nx. + +#### Use upgrade-native Generator + +To upgrade native iOS and Android code to latest, you can use the [upgrade-native](/packages/react-native/generators/upgrade-native) generator: + +```shell +nx generate @nrwl/react-native:upgrade-native +``` + +This is a command that will replace the iOS and Android native code folder entirely. + +#### Upgrade Manually + +You can also upgrade React Native iOS and Android code using the [rn-diff-purge](https://react-native-community.github.io/upgrade-helper/) project. + +### Start Metro Server + +To start the server that communicates with connected devices: + +```shell +nx start +``` + +### Run iOS + +To build your app and start it on iOS simulator or device: + +```shell +nx run-ios +``` + +### Run Android + +To build your app and start it on a connected Android emulator or device: + +```shell +nx run-android +``` + +### Build iOS + +To build an iOS app: + +```shell +nx build-ios +``` + +The build artifacts will be located under `/ios/build`. + +You can specify the build folder by setting the `buildFolder` option: + +```shell +nx build ios --buildFolder="./build" +``` + +### Build Android + +To build an Android app, run: + +```shell +nx build-android +``` + +The build artifacts will be located under `/android/app/build`. ## More Documentation diff --git a/e2e/react-native/src/react-native.test.ts b/e2e/react-native/src/react-native.test.ts index bed990bcd2266..e809176861d25 100644 --- a/e2e/react-native/src/react-native.test.ts +++ b/e2e/react-native/src/react-native.test.ts @@ -2,13 +2,18 @@ import { checkFilesExist, cleanupProject, expectTestsPass, + isOSX, + killPorts, newProject, + promisifiedTreeKill, readJson, runCLI, runCLIAsync, + runCommandUntil, uniq, updateFile, } from '@nrwl/e2e/utils'; +import { ChildProcess } from 'child_process'; import { join } from 'path'; describe('react native', () => { @@ -27,7 +32,7 @@ describe('react native', () => { }); afterAll(() => cleanupProject()); - it('should test, create ios and android JS bundles', async () => { + it('should test and lint', async () => { const componentName = uniq('component'); runCLI( `generate @nrwl/react-native:component ${componentName} --project=${libName} --export --no-interactive` @@ -46,7 +51,9 @@ describe('react native', () => { const libLintResults = await runCLIAsync(`lint ${libName}`); expect(libLintResults.combinedOutput).toContain('All files pass linting.'); + }); + it('should bundle-ios', async () => { const iosBundleResult = await runCLIAsync( `bundle-ios ${appName} --sourcemapOutput=../../dist/apps/${appName}/ios/main.map` ); @@ -57,7 +64,9 @@ describe('react native', () => { checkFilesExist(`dist/apps/${appName}/ios/main.jsbundle`); checkFilesExist(`dist/apps/${appName}/ios/main.map`); }).not.toThrow(); + }); + it('should bundle-android', async () => { const androidBundleResult = await runCLIAsync( `bundle-android ${appName} --sourcemapOutput=../../dist/apps/${appName}/android/main.map` ); @@ -68,7 +77,45 @@ describe('react native', () => { checkFilesExist(`dist/apps/${appName}/android/main.jsbundle`); checkFilesExist(`dist/apps/${appName}/android/main.map`); }).not.toThrow(); - }, 1000000); + }); + + it('should start', async () => { + let process: ChildProcess; + const port = 8081; + + try { + process = await runCommandUntil( + `start ${appName} --interactive=false --port=${port}`, + (output) => { + return ( + output.includes(`Packager is ready at http://localhost::${port}`) || + output.includes('Starting JS server...') + ); + } + ); + } catch (err) { + console.error(err); + } + + // port and process cleanup + try { + if (process && process.pid) { + await promisifiedTreeKill(process.pid, 'SIGKILL'); + await killPorts(port); + } + } catch (err) { + expect(err).toBeFalsy(); + } + }); + + if (isOSX()) { + it('should pod install', async () => { + expect(async () => { + await runCLIAsync(`pod-install ${appName}`); + checkFilesExist(`apps/${appName}/ios/Podfile.lock`); + }).not.toThrow(); + }); + } it('should create storybook with application', async () => { runCLI( diff --git a/packages/expo/src/utils/symlink-task.ts b/packages/expo/src/utils/symlink-task.ts index 47662c59e55c9..bc527097d20ab 100644 --- a/packages/expo/src/utils/symlink-task.ts +++ b/packages/expo/src/utils/symlink-task.ts @@ -3,13 +3,13 @@ import * as chalk from 'chalk'; import { GeneratorCallback, logger } from '@nrwl/devkit'; export function runSymlink( - worksapceRoot: string, + workspaceRoot: string, projectRoot: string ): GeneratorCallback { return () => { logger.info(`creating symlinks for ${chalk.bold(projectRoot)}`); try { - ensureNodeModulesSymlink(worksapceRoot, projectRoot); + ensureNodeModulesSymlink(workspaceRoot, projectRoot); } catch { throw new Error( `Failed to create symlinks for ${chalk.bold(projectRoot)}` diff --git a/packages/react-native/docs/build-android-examples.md b/packages/react-native/docs/build-android-examples.md index 464236bd25f38..4607fa70f1cce 100644 --- a/packages/react-native/docs/build-android-examples.md +++ b/packages/react-native/docs/build-android-examples.md @@ -25,8 +25,11 @@ nx run mobile:build-android ## Examples {% tabs %} -{% tab label="Build with custom gradleTask" %} -The `gradleTask` option accepts any custom gradle task, such as `assembleDebug`, `assembleRelease`, `bundleDebug`, `bundleRelease`: +{% tab label="Build with custom tasks" %} +The `tasks` option accepts any custom gradle task, such as `assembleDebug`, `assembleRelease`, `bundleDebug`, `bundleRelease`, `installDebug`, `installRelease`. +For example, pass in `bundleRelease` or `bundleRelease` to tasks, it will create with `.aab` extension under bundle folder. +Pass in `assembleDebug` or `assembleRelease` to tasks, it will create a build with `.apk` extension under apk folder. +Pass in `installDebug` or `installRelease` to tasks, it will create a build with `.apk` extension and immediately install it on a running emulator or connected device. ```json "build-android": { @@ -36,15 +39,15 @@ The `gradleTask` option accepts any custom gradle task, such as `assembleDebug`, "{projectRoot}/build/outputs/apk" ], "options": { - "gradleTask": "assembleDebug" + "tasks": ["bundleRelease"] } } ``` {% /tab %} -{% tab label="Create a build with apk format" %} +{% tab label="Build for debug/release" %} -The `apk` option allows you determine the format of android build. If set as true, it will create a build with `.apk` extension under apk folder; if set as false, it will create with `.aab` extension under bundle folder. +The `mode` option allows you determine whether to build for debug/release apk. ```json "build-android": { @@ -54,15 +57,15 @@ The `apk` option allows you determine the format of android build. If set as tru "{projectRoot}/build/outputs/apk" ], "options": { - "apk": true + "mode": "debug" } } ``` {% /tab %} -{% tab label="Build for debug/release" %} +{% tab label="Build for current device architecture" %} -If set `debug` option as `true`, it will create a debug build; if set as `false`, it will create a release build. +The `activeArchOnly` option allows you to build native libraries only for the current device architecture for debug builds. ```json "build-android": { @@ -72,7 +75,7 @@ If set `debug` option as `true`, it will create a debug build; if set as `false` "{projectRoot}/build/outputs/apk" ], "options": { - "debug": true + "activeArchOnly": true } } ``` diff --git a/packages/react-native/docs/build-ios-examples.md b/packages/react-native/docs/build-ios-examples.md new file mode 100644 index 0000000000000..a2c4004df1201 --- /dev/null +++ b/packages/react-native/docs/build-ios-examples.md @@ -0,0 +1,109 @@ +`project.json`: + +```json +{ + "name": "mobile", + //... + "targets": { + //... + "build-ios": { + "executor": "@nrwl/react-native:build-ios", + "options": {} + } + } +} +``` + +```bash +nx run mobile:build-ios +``` + +## Examples + +{% tabs %} +{% tab label="Build the Debug/Release app" %} +The `buildFolder` option allows to specify the location for ios build artifacts. It corresponds to Xcode's -derivedDataPath. + +```json + "build-ios": { + "executor": "@nrwl/react-native:build-ios", + "options": { + "buildFolder": "dist/ios/build" + } + } +``` + +{% /tab %} +{% tab label="Build the Debug/Release app" %} +The `mode` option allows to specify the xcode configuartion, such as `Debug` or `Release`. + +```json + "build-ios": { + "executor": "@nrwl/react-native:build-ios", + "options": { + "mode": "Release" + } + } +``` + +{% /tab %} +{% tab label="Build for a simulator" %} +To see all the available simulators, run command: + +```bash +xcrun simctl list +``` + +The `simulator` option allows you to launch your iOS app in a specific simulator: + +```json + "build-ios": { + "executor": "@nrwl/react-native:build-ios", + "options": { + "simulator": "iPhone 14 Pro" + } + } +``` + +{% /tab %} +{% tab label="Build for a device" %} +The `device` option allows you to launch your iOS app in a specific device. + +To see all the available device, run command: + +```bash +xcrun simctl list +``` + +```json + "build-ios": { + "executor": "@nrwl/react-native:build-ios", + "options": { + "device": "deviceName" + } + } +``` + +{% /tab %} +{% tab label="Set Device by udid" %} +The `udid` option allows you to explicitly set device to use by udid. + +To see all the available simulators and devices with udid, run command: + +```bash +xcrun simctl list +``` + +```json + "build-ios": { + "executor": "@nrwl/react-native:build-ios", + "options": { + "udid": "device udid" + } + } +``` + +{% /tab %} +{% /tabs %} + +--- diff --git a/packages/react-native/docs/run-android-examples.md b/packages/react-native/docs/run-android-examples.md index b7d8d1ba2a445..c963ee13196ee 100644 --- a/packages/react-native/docs/run-android-examples.md +++ b/packages/react-native/docs/run-android-examples.md @@ -41,13 +41,13 @@ The `deviceId` option allows you to launch your android app in a specific device {% /tab %} {% tab label="Run the debug/release app" %} -The `variant` option allows to specify the build variant, such as `debug` or `release`. +The `mode` option allows to specify the build variant, such as `debug` or `release`. ```json "run-android": { "executor": "@nrwl/react-native:run-android", "options": { - "variant": "release" + "mode": "release" } } ``` diff --git a/packages/react-native/docs/run-ios-examples.md b/packages/react-native/docs/run-ios-examples.md index 7b159f34a9bcf..3a6ea25103b08 100644 --- a/packages/react-native/docs/run-ios-examples.md +++ b/packages/react-native/docs/run-ios-examples.md @@ -21,6 +21,19 @@ nx run mobile:run-ios ## Examples {% tabs %} +{% tab label="Build the Debug/Release app" %} +The `mode` option allows to specify the xcode configuartion schema, such as `Debug` or `Release`. + +```json + "run-ios": { + "executor": "@nrwl/react-native:run-ios", + "options": { + "mode": "Release" + } + } +``` + +{% /tab %} {% tab label="Run on a simulator" %} To see all the available simulators, run command: @@ -43,6 +56,12 @@ The `simulator` option allows you to launch your iOS app in a specific simulator {% tab label="Run on a device" %} The `device` option allows you to launch your iOS app in a specific device. +To see all the available devices, run command: + +```bash +xcrun simctl list +``` + ```json "run-ios": { "executor": "@nrwl/react-native:run-ios", @@ -53,14 +72,20 @@ The `device` option allows you to launch your iOS app in a specific device. ``` {% /tab %} -{% tab label="Run the Debug/Release app" %} -The `xcodeConfiguration` option allows to specify the xcode configuartion, such as `Debug` or `Release`. +{% tab label="Set Device by udid" %} +The `udid` option allows you to explicitly set device to use by udid. + +To see all the available simulators and devices with udid, run command: + +```bash +xcrun simctl list +``` ```json "run-ios": { "executor": "@nrwl/react-native:run-ios", "options": { - "xcodeConfiguration": "Release" + "udid": "device udid" } } ``` diff --git a/packages/react-native/executors.json b/packages/react-native/executors.json index 214e05a25a6df..f8dd98f444a06 100644 --- a/packages/react-native/executors.json +++ b/packages/react-native/executors.json @@ -20,6 +20,11 @@ "schema": "./src/executors/build-android/schema.json", "description": "Release Build for Android." }, + "build-ios": { + "implementation": "./src/executors/build-ios/build-ios.impl", + "schema": "./src/executors/build-ios/schema.json", + "description": "Build iOS app" + }, "start": { "implementation": "./src/executors/start/start.impl", "schema": "./src/executors/start/schema.json", @@ -39,6 +44,11 @@ "implementation": "./src/executors/storybook/storybook.impl", "schema": "./src/executors/storybook/schema.json", "description": "Serve React Native Storybook." + }, + "pod-install": { + "implementation": "./src/executors/pod-install/pod-install.impl", + "schema": "./src/executors/pod-install/schema.json", + "description": "Run `pod install` in the `ios` directory." } }, "builders": { @@ -62,6 +72,11 @@ "schema": "./src/executors/build-android/schema.json", "description": "Release Build for Android." }, + "build-ios": { + "implementation": "./src/executors/build-ios/compat", + "schema": "./src/executors/build-ios/schema.json", + "description": "Build iOS app" + }, "start": { "implementation": "./src/executors/start/compat", "schema": "./src/executors/start/schema.json", @@ -81,6 +96,11 @@ "implementation": "./src/executors/storybook/compat", "schema": "./src/executors/storybook/schema.json", "description": "Serve React Native Storybook." + }, + "pod-install": { + "implementation": "./src/executors/pod-install/compat", + "schema": "./src/executors/pod-install/schema.json", + "description": "Run `pod install` in the `ios` directory." } } } diff --git a/packages/react-native/migrations.json b/packages/react-native/migrations.json index 6da8c55c56ffe..9cc708eb8bcfe 100644 --- a/packages/react-native/migrations.json +++ b/packages/react-native/migrations.json @@ -71,6 +71,12 @@ "version": "15.0.0-beta.0", "description": "Adds babel.config.json to the hash of all tasks", "factory": "./src/migrations/update-15-0-0/add-babel-inputs" + }, + "add-build-ios-target": { + "cli": "nx", + "version": "15.9.1-beta.0", + "description": "Add target build-ios and pod-install for react native apps", + "factory": "./src/migrations/update-15-9-1/add-build-ios-target" } }, "packageJsonUpdates": { @@ -1261,6 +1267,19 @@ "alwaysAddToPackageJson": false } } + }, + "15.9.1": { + "version": "15.9.1-beta.0", + "packages": { + "@react-native-community/cli": { + "version": "10.2.1", + "alwaysAddToPackageJson": false + }, + "@react-native-community/cli-platform-ios": { + "version": "10.2.1", + "alwaysAddToPackageJson": false + } + } } } } diff --git a/packages/react-native/src/executors/build-android/build-android.impl.ts b/packages/react-native/src/executors/build-android/build-android.impl.ts index 7e2eba95adbcd..56be92996db08 100644 --- a/packages/react-native/src/executors/build-android/build-android.impl.ts +++ b/packages/react-native/src/executors/build-android/build-android.impl.ts @@ -1,9 +1,16 @@ -import { ExecutorContext } from '@nrwl/devkit'; +import { ExecutorContext, names } from '@nrwl/devkit'; import { join } from 'path'; import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; -import { ChildProcess, spawn } from 'child_process'; -import { ReactNativeBuildOptions } from './schema'; +import { ChildProcess, fork } from 'child_process'; +import { ReactNativeBuildAndroidOptions } from './schema'; import { chmodAndroidGradlewFiles } from '../../utils/chmod-android-gradle-files'; +import { runCliStart } from '../start/start.impl'; +import { + displayNewlyAddedDepsMessage, + syncDeps, +} from '../sync-deps/sync-deps.impl'; +import { getCliOptions } from '../../utils/get-cli-options'; + export interface ReactNativeBuildOutput { success: boolean; } @@ -11,16 +18,39 @@ export interface ReactNativeBuildOutput { let childProcess: ChildProcess; export default async function* buildAndroidExecutor( - options: ReactNativeBuildOptions, + options: ReactNativeBuildAndroidOptions, context: ExecutorContext ): AsyncGenerator { const projectRoot = context.projectsConfigurations.projects[context.projectName].root; ensureNodeModulesSymlink(context.root, projectRoot); + if (options.sync) { + displayNewlyAddedDepsMessage( + context.projectName, + await syncDeps( + context.projectName, + projectRoot, + context.root, + context.projectGraph + ) + ); + } + chmodAndroidGradlewFiles(join(projectRoot, 'android')); try { - await runCliBuild(context.root, projectRoot, options); + const tasks = [runCliBuild(context.root, projectRoot, options)]; + if (options.packager && options.mode !== 'release') { + tasks.push( + runCliStart(context.root, projectRoot, { + port: options.port, + resetCache: options.resetCache, + interactive: options.interactive, + }) + ); + } + + await Promise.all(tasks); yield { success: true }; } finally { if (childProcess) { @@ -32,17 +62,19 @@ export default async function* buildAndroidExecutor( function runCliBuild( workspaceRoot: string, projectRoot: string, - options: ReactNativeBuildOptions + options: ReactNativeBuildAndroidOptions ) { return new Promise((resolve, reject) => { - const gradleCommand = getGradleCommand(options); - - childProcess = spawn( - process.platform === 'win32' ? 'gradlew.bat' : './gradlew', - [gradleCommand], + /** + * Call the react native cli with option `--no-packager` + * Not passing '--packager' due to cli will launch start command from the project root + */ + childProcess = fork( + join(workspaceRoot, './node_modules/react-native/cli.js'), + ['run-android', ...createBuildAndroidOptions(options), '--no-packager'], { - cwd: join(workspaceRoot, projectRoot, 'android'), - stdio: [0, 1, 2], + cwd: join(workspaceRoot, projectRoot), + env: { ...process.env, RCT_METRO_PORT: options.port.toString() }, } ); @@ -63,19 +95,14 @@ function runCliBuild( }); } -function getGradleCommand(options: ReactNativeBuildOptions) { - if (options?.gradleTask) { - return options.gradleTask; - } - if (options.apk) { - if (options.debug) { - return 'assembleDebug'; - } - return 'assembleRelease'; - } else { - if (options.debug) { - return 'bundleDebug'; - } - return 'bundleRelease'; - } +const nxOptions = ['sync', 'packager']; +const startOptions = ['port', 'resetCache']; +const deprecatedOptions = ['apk', 'debug', 'gradleTask']; + +function createBuildAndroidOptions(options: ReactNativeBuildAndroidOptions) { + return getCliOptions(options, [ + ...nxOptions, + ...startOptions, + ...deprecatedOptions, + ]); } diff --git a/packages/react-native/src/executors/build-android/schema.d.ts b/packages/react-native/src/executors/build-android/schema.d.ts index 4c7f574a07a13..24c8d29ce7401 100644 --- a/packages/react-native/src/executors/build-android/schema.d.ts +++ b/packages/react-native/src/executors/build-android/schema.d.ts @@ -1,5 +1,31 @@ -export interface ReactNativeBuildOptions { +// options taken from https://github.com/react-native-community/cli/blob/main/packages/cli-platform-android/src/commands/buildAndroid/index.ts + +import { ReactNativeStartOptions } from '../start/schema'; + +export interface ReactNativeBuildAndroidOptions + extends ReactNativeStartOptions { + /** + * @deprecated, use tasks instead. e.g. tasks=['bundleRelease'] for aab, and tasks=['assembleRelease'] for apk. Will be removed in nx 17. + */ apk?: boolean; + /** + * @deprecated, use mode='debug' instead. Will be removed in nx 17. + */ debug?: boolean; + /** + * @deprecated, use tasks instead instead. Will be removed in nx 17. + */ gradleTask?: string; + + // react native options + mode: string; // default is debug + activeArchOnly: boolean; // default is false + port: number; // default is 8081 + tasks?: Array; + extraParams?: Array; + interactive?: boolean; + + // nx options + packager: boolean; // default is true + sync: boolean; } diff --git a/packages/react-native/src/executors/build-android/schema.json b/packages/react-native/src/executors/build-android/schema.json index 400ab878bec79..16284032ae8cb 100644 --- a/packages/react-native/src/executors/build-android/schema.json +++ b/packages/react-native/src/executors/build-android/schema.json @@ -7,18 +7,94 @@ "title": "Release Build for Android", "description": "Build target options for Android.", "type": "object", + "presets": [ + { + "name": "Build Android for current device architecture", + "keys": ["activeArchOnly"] + }, + { + "name": "Build Android without metro cache", + "keys": ["resetCache"] + }, + { + "name": "Build Android with specific tasks", + "keys": ["tasks"] + }, + { + "name": "Build Android with a specific mode", + "keys": ["mode"] + } + ], "properties": { "apk": { "type": "boolean", - "description": "Generate apk file(s) rather than a bundle (`.aab`)." + "description": "Generate apk file(s) rather than a bundle (`.aab`).", + "x-deprecated": "Use `tasks` option instead, e.g. `tasks=['bundleRelease']` to generate aab, `tasks=['assembleDebug']` to generate apk. Will be removed in Nx 17." }, "debug": { "type": "boolean", - "description": "Generate a debug build instead of a release build." + "description": "Generate a debug build instead of a release build.", + "x-deprecated": "Use `mode` option instead, e.g. `mode='debug'`. Deprecated from @react-native-community/cli. Will be removed in Nx 17." }, "gradleTask": { "type": "string", - "description": "Override default gradle task incase of multi build variants" + "description": "Override default gradle task incase of multi build variants", + "x-deprecated": "Use `tasks` option instead, e.g. `tasks=['assembleDebug']`. Will be removed in Nx 17." + }, + "mode": { + "type": "string", + "description": "Specify your app's build variant", + "default": "debug", + "examples": ["debug", "release"], + "x-priority": "important" + }, + "packager": { + "type": "boolean", + "description": "Launch packager while building", + "default": true + }, + "port": { + "type": "number", + "description": "The port where the packager server is listening on.", + "default": 8081 + }, + "tasks": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.", + "examples": [ + "assembleDebug", + "assembleRelease", + "bundleDebug", + "bundleRelease", + "installDebug", + "installRelease" + ] + }, + "activeArchOnly": { + "type": "boolean", + "description": "Build native libraries only for the current device architecture for debug builds.", + "default": false + }, + "extraParams": { + "type": "string", + "description": "Custom params passed to gradle build command" + }, + "interactive": { + "type": "boolean", + "description": "Explicitly select build type and flavour to use before running a build" + }, + "sync": { + "type": "boolean", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", + "default": true + }, + "resetCache": { + "type": "boolean", + "description": "Resets metro cache.", + "default": false } }, "required": [], diff --git a/packages/react-native/src/executors/build-ios/build-ios.impl.ts b/packages/react-native/src/executors/build-ios/build-ios.impl.ts new file mode 100644 index 0000000000000..f2a36eb002023 --- /dev/null +++ b/packages/react-native/src/executors/build-ios/build-ios.impl.ts @@ -0,0 +1,118 @@ +import { ExecutorContext } from '@nrwl/devkit'; +import { join } from 'path'; +import { ChildProcess, fork } from 'child_process'; +import { platform } from 'os'; + +import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; +import { + displayNewlyAddedDepsMessage, + syncDeps, +} from '../sync-deps/sync-deps.impl'; +import { podInstall } from '../../utils/pod-install-task'; +import { ReactNativeBuildIosOptions } from './schema'; +import { runCliStart } from '../start/start.impl'; +import { getCliOptions } from '../../utils/get-cli-options'; + +export interface ReactNativeBuildIosOutput { + success: boolean; +} + +let childProcess: ChildProcess; + +export default async function* runIosExecutor( + options: ReactNativeBuildIosOptions, + context: ExecutorContext +): AsyncGenerator { + if (platform() !== 'darwin') { + throw new Error(`The run-ios build requires Mac to run`); + } + const projectRoot = + context.projectsConfigurations.projects[context.projectName].root; + ensureNodeModulesSymlink(context.root, projectRoot); + if (options.sync) { + displayNewlyAddedDepsMessage( + context.projectName, + await syncDeps( + context.projectName, + projectRoot, + context.root, + context.projectGraph + ) + ); + } + + if (options.install) { + await podInstall( + join(context.root, projectRoot, 'ios'), + options.buildFolder + ); + } + + try { + const tasks = [runCliBuildIOS(context.root, projectRoot, options)]; + if (options.packager && options.mode !== 'Release') { + tasks.push( + runCliStart(context.root, projectRoot, { + port: options.port, + resetCache: options.resetCache, + interactive: options.interactive, + }) + ); + } + + await Promise.all(tasks); + + yield { success: true }; + } finally { + if (childProcess) { + childProcess.kill(); + } + } +} + +function runCliBuildIOS( + workspaceRoot: string, + projectRoot: string, + options: ReactNativeBuildIosOptions +) { + return new Promise((resolve, reject) => { + /** + * Call the react native cli with option `--no-packager` + * Not passing '--packager' due to cli will launch start command from the project root + */ + childProcess = fork( + join(workspaceRoot, './node_modules/react-native/cli.js'), + ['run-ios', ...createBuildIOSOptions(options), '--no-packager'], + { + cwd: join(workspaceRoot, projectRoot), + env: { ...process.env, RCT_METRO_PORT: options.port.toString() }, + } + ); + + // Ensure the child process is killed when the parent exits + process.on('exit', () => childProcess.kill()); + process.on('SIGTERM', () => childProcess.kill()); + + childProcess.on('error', (err) => { + reject(err); + }); + childProcess.on('exit', (code) => { + if (code === 0) { + resolve(code); + } else { + reject(code); + } + }); + }); +} + +const nxOptions = ['sync', 'install', 'packager']; +const startOptions = ['port', 'resetCache']; + +function createBuildIOSOptions(options: ReactNativeBuildIosOptions) { + return getCliOptions( + options, + [...nxOptions, ...startOptions], + ['buildFolder'] + ); +} diff --git a/packages/react-native/src/executors/build-ios/compat.ts b/packages/react-native/src/executors/build-ios/compat.ts new file mode 100644 index 0000000000000..3803011250b70 --- /dev/null +++ b/packages/react-native/src/executors/build-ios/compat.ts @@ -0,0 +1,5 @@ +import { convertNxExecutor } from '@nrwl/devkit'; + +import buildIosExecutor from './build-ios.impl'; + +export default convertNxExecutor(buildIosExecutor); diff --git a/packages/react-native/src/executors/build-ios/schema.d.ts b/packages/react-native/src/executors/build-ios/schema.d.ts new file mode 100644 index 0000000000000..df4fac0ecc331 --- /dev/null +++ b/packages/react-native/src/executors/build-ios/schema.d.ts @@ -0,0 +1,22 @@ +// options from https://github.com/react-native-community/cli/blob/main/packages/cli-platform-ios/src/commands/buildIOS/index.ts + +import { ReactNativeStartOptions } from '../start/schema'; + +export interface ReactNativeBuildIosOptions extends ReactNativeStartOptions { + // react native options + simulator?: string; + mode: string; // default if 'Debug' + scheme?: string; + device?: string; + udid?: string; + verbose?: boolean; + port: number; // default is 8081 + xcconfig?: string; + buildFolder?: string; + extraParams?: string; + + // nx options + packager: boolean; // default is true + install: boolean; // default is true + sync: boolean; // default is true +} diff --git a/packages/react-native/src/executors/build-ios/schema.json b/packages/react-native/src/executors/build-ios/schema.json new file mode 100644 index 0000000000000..0ee7fbb7e3804 --- /dev/null +++ b/packages/react-native/src/executors/build-ios/schema.json @@ -0,0 +1,106 @@ +{ + "$schema": "http://json-schema.org/schema", + "version": 2, + "cli": "nx", + "title": "React Native Build iOS executor", + "description": "Build iOS app.", + "type": "object", + "presets": [ + { + "name": "Build iOS for a simulator", + "keys": ["simulator"] + }, + { + "name": "Build iOS for a device", + "keys": ["device"] + }, + { + "name": "Build iOS for a device with udid", + "keys": ["udid"] + }, + { + "name": "Run `pod install` before building iOS app", + "keys": ["install"] + } + ], + "properties": { + "simulator": { + "type": "string", + "description": "Explicitly set simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version: \"iPhone 6 (10.0)\"", + "examples": [ + "iPhone 14", + "iPhone 13", + "iPhone 12", + "iPhone 11", + "iPhone X" + ], + "x-priority": "important" + }, + "mode": { + "type": "string", + "description": "Explicitly set the scheme configuration to use", + "default": "Debug", + "examples": ["Debug", "Release"], + "x-priority": "important" + }, + "schema": { + "type": "string", + "description": "Explicitly set Xcode scheme to use" + }, + "device": { + "type": "string", + "description": "Explicitly set device to use by name. The value is not required if you have a single device connected." + }, + "udid": { + "type": "string", + "description": "Explicitly set device to use by udid" + }, + "verbose": { + "type": "boolean", + "description": "Do not use xcbeautify or xcpretty even if installed" + }, + "port": { + "type": "number", + "description": "The port where the packager server is listening on.", + "default": 8081 + }, + "xcconfig": { + "type": "string", + "description": "Explicitly set xcconfig to use" + }, + "buildFolder": { + "type": "string", + "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory", + "default": "./build" + }, + "interactive": { + "type": "boolean", + "description": "Explicitly select which scheme and configuration to use before running a build" + }, + "extraParams": { + "type": "string", + "description": "Custom params that will be passed to xcodebuild command." + }, + "install": { + "type": "boolean", + "description": "Runs `pod install` for native modules before building iOS app." + }, + "sync": { + "type": "boolean", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", + "default": true + }, + "resetCache": { + "type": "boolean", + "description": "Resets metro cache.", + "default": false + }, + "packager": { + "type": "boolean", + "description": "Launch packager while building", + "default": true + } + }, + "required": [], + "examplesFile": "../../../docs/build-ios-examples.md" +} diff --git a/packages/react-native/src/executors/bundle/schema.json b/packages/react-native/src/executors/bundle/schema.json index 23843b4cb1b4a..3bf30ce819a34 100644 --- a/packages/react-native/src/executors/bundle/schema.json +++ b/packages/react-native/src/executors/bundle/schema.json @@ -7,6 +7,24 @@ "title": "Offline JS Bundle for React Native", "description": "JS Bundle target options.", "type": "object", + "presets": [ + { + "name": "Bundle for a specific platform", + "keys": ["platform"] + }, + { + "name": "Bundle a development build", + "keys": ["dev"] + }, + { + "name": "Bundle to a specific output path", + "keys": ["bundleOutput"] + }, + { + "name": "Bundle without global cache", + "keys": ["resetCache"] + } + ], "properties": { "entryFile": { "type": "string", @@ -65,7 +83,7 @@ }, "readGlobalCache": { "type": "boolean", - "description": "Removes cached files.", + "description": "Try to fetch transformed JS code from the global cache, if configured.", "default": false } }, diff --git a/packages/react-native/src/executors/pod-install/compat.ts b/packages/react-native/src/executors/pod-install/compat.ts new file mode 100644 index 0000000000000..cc20f27361001 --- /dev/null +++ b/packages/react-native/src/executors/pod-install/compat.ts @@ -0,0 +1,5 @@ +import { convertNxExecutor } from '@nrwl/devkit'; + +import podInstall from './pod-install.impl'; + +export default convertNxExecutor(podInstall); diff --git a/packages/react-native/src/executors/pod-install/pod-install.impl.ts b/packages/react-native/src/executors/pod-install/pod-install.impl.ts new file mode 100644 index 0000000000000..ec595c75b70ac --- /dev/null +++ b/packages/react-native/src/executors/pod-install/pod-install.impl.ts @@ -0,0 +1,21 @@ +import { join } from 'path'; +import { ExecutorContext } from '@nrwl/devkit'; + +import { runPodInstall } from '../../utils/pod-install-task'; +import { ReactNativePodInstallOptions } from './schema'; + +export interface ReactNativePodInstallOutput { + success: boolean; +} + +export default async function* podInstall( + options: ReactNativePodInstallOptions, + context: ExecutorContext +): AsyncGenerator { + const projectRoot = + context.projectsConfigurations.projects[context.projectName].root; + const iosDirectory = join(context.root, projectRoot, 'ios'); + await runPodInstall(iosDirectory, true, options.buildFolder)(); + + yield { success: true }; +} diff --git a/packages/react-native/src/executors/pod-install/schema.d.ts b/packages/react-native/src/executors/pod-install/schema.d.ts new file mode 100644 index 0000000000000..11f7664df8e04 --- /dev/null +++ b/packages/react-native/src/executors/pod-install/schema.d.ts @@ -0,0 +1,3 @@ +export interface ReactNativePodInstallOptions { + buildFolder: string; +} diff --git a/packages/react-native/src/executors/pod-install/schema.json b/packages/react-native/src/executors/pod-install/schema.json new file mode 100644 index 0000000000000..0cc954d982e9e --- /dev/null +++ b/packages/react-native/src/executors/pod-install/schema.json @@ -0,0 +1,18 @@ +{ + "version": 2, + "outputCapture": "direct-nodejs", + "cli": "nx", + "$id": "NxReactNativePodInstall", + "$schema": "http://json-schema.org/schema", + "title": "Run Pod Install for React Native iOS Project", + "description": "Run `pod install` for React Native iOS Project.", + "type": "object", + "properties": { + "buildFolder": { + "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory", + "type": "string", + "default": "./build" + } + }, + "required": ["buildFolder"] +} diff --git a/packages/react-native/src/executors/run-android/run-android.impl.ts b/packages/react-native/src/executors/run-android/run-android.impl.ts index 682de05d6b215..006b6567151fa 100644 --- a/packages/react-native/src/executors/run-android/run-android.impl.ts +++ b/packages/react-native/src/executors/run-android/run-android.impl.ts @@ -7,10 +7,10 @@ import { displayNewlyAddedDepsMessage, syncDeps, } from '../sync-deps/sync-deps.impl'; -import { chmodSync } from 'fs'; import { ReactNativeRunAndroidOptions } from './schema'; import { runCliStart } from '../start/start.impl'; import { chmodAndroidGradlewFiles } from '../../utils/chmod-android-gradle-files'; +import { getCliOptions } from '../../utils/get-cli-options'; export interface ReactNativeRunAndroidOutput { success: boolean; @@ -97,31 +97,14 @@ function runCliRunAndroid( }); } -const nxOrStartOptions = [ - 'sync', - 'install', - 'packager', - 'port', - 'resetCache', - 'interactive', -]; +const nxOptions = ['sync', 'packager']; +const startOptions = ['port', 'resetCache']; +const deprecatedOptions = ['variant', 'jetifier']; -function createRunAndroidOptions(options) { - return Object.keys(options).reduce((acc, k) => { - const v = options[k]; - if (k === 'mainActivity') { - acc.push(`--main-activity`, v); - } else if (k === 'jetifier') { - if (!v) { - acc.push(`--no-jetifier`); - } - } else if (k === 'activeArchOnly') { - if (v) { - acc.push(`--active-arch-only`); - } - } else if (v && !nxOrStartOptions.includes(k)) { - acc.push(`--${k}`, v); - } - return acc; - }, []); +function createRunAndroidOptions(options: ReactNativeRunAndroidOptions) { + return getCliOptions( + options, + [...nxOptions, ...startOptions, ...deprecatedOptions], + ['appId', 'appIdSuffix'] + ); } diff --git a/packages/react-native/src/executors/run-android/schema.d.ts b/packages/react-native/src/executors/run-android/schema.d.ts index 95213c0f8ad52..710d98a8e58c8 100644 --- a/packages/react-native/src/executors/run-android/schema.d.ts +++ b/packages/react-native/src/executors/run-android/schema.d.ts @@ -1,17 +1,24 @@ -// part of options from https://github.com/react-native-community/cli/blob/master/packages/platform-android/src/commands/runAndroid/index.ts#L314 -export interface ReactNativeRunAndroidOptions { +import { ReactNativeBuildAndroidOptions } from '../build-android/schema'; +import { ReactNativeStartOptions } from '../start/schema'; + +// part of options from https://github.com/react-native-community/cli/blob/main/packages/cli-platform-android/src/commands/runAndroid/index.ts +export interface ReactNativeRunAndroidOptions + extends ReactNativeBuildAndroidOptions { + /** + * @deprecated use mode instead + */ variant: string; + /** + * @deprecated no longer supported in react native cli + * https://github.com/react-native-community/cli/commit/7c003f2b1d9d80ec5c167614ba533a004272c685 + */ + jetifier: boolean; + + // react native options appId: string; appIdSuffix: string; mainActiviy: string; deviceId: string; - tasks?: string; - jetifier: boolean; - sync: boolean; - port: number; - terminal?: string; - packager: boolean; // default is true - resetCache: boolean; // default is false - interactive: boolean; // default is true - activeArchOnly?: boolean; + listDevices?: boolean; + binaryPath?: string; } diff --git a/packages/react-native/src/executors/run-android/schema.json b/packages/react-native/src/executors/run-android/schema.json index 2b5111bf09857..256fed692b7e8 100644 --- a/packages/react-native/src/executors/run-android/schema.json +++ b/packages/react-native/src/executors/run-android/schema.json @@ -9,8 +9,16 @@ "type": "object", "presets": [ { - "name": "Run Android without cache", - "keys": ["variant", "sync", "port", "packager", "resetCache"] + "name": "Run Android for the current device architecture", + "keys": ["activeArchOnly"] + }, + { + "name": "Lists all available Android devices and simulators", + "keys": ["listDevices"] + }, + { + "name": "Run Android without metro cache", + "keys": ["resetCache"] } ], "properties": { @@ -19,7 +27,12 @@ "description": "Specify your app's build variant (e.g. `debug`, `release`).", "default": "debug", "examples": ["debug", "release"], - "x-priority": "important" + "x-deprecated": "Deprecated from @react-native-community/cli, use mode instead, e.g. mode=debug. Will be remove in Nx 17." + }, + "jetifier": { + "type": "boolean", + "description": "Run Jetifier – the AndroidX transition tool. By default it runs before Gradle to ease working with libraries that don't support AndroidX yet.", + "x-deprecated": "Deprecated from @react-native-community/cli. Will be remove in Nx 17." }, "appId": { "type": "string", @@ -38,18 +51,25 @@ "type": "string", "description": "Builds your app and starts it on a specific device/simulator with the given device id (listed by running `adb devices` on the command line)." }, - "tasks": { + "listDevices": { + "type": "boolean", + "description": "Lists all available Android devices and simulators and let you choose one to run the app", + "default": false + }, + "binaryPath": { "type": "string", - "description": "Run custom Gradle tasks. If this argument is provided, then `--variant` option is ignored. Example: `yarn react-native run-android --tasks clean,installDebug`." + "description": "Path relative to project root where pre-built .apk binary lives." }, - "jetifier": { - "type": "boolean", - "description": "Run Jetifier – the AndroidX transition tool. By default it runs before Gradle to ease working with libraries that don't support AndroidX yet.", - "default": true + "mode": { + "type": "string", + "description": "Specify your app's build variant", + "default": "debug", + "examples": ["debug", "release"], + "x-priority": "important" }, - "sync": { + "packager": { "type": "boolean", - "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", + "description": "Launch packager while building", "default": true }, "port": { @@ -57,28 +77,43 @@ "description": "The port where the packager server is listening on.", "default": 8081 }, - "terminal": { - "type": "string", - "description": "Launches the Metro Bundler in a new window using the specified terminal path." - }, - "packager": { - "type": "boolean", - "description": "Starts the packager server.", - "default": true + "tasks": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.", + "examples": [ + "assembleDebug", + "assembleRelease", + "bundleDebug", + "bundleRelease", + "installDebug", + "installRelease" + ] }, - "resetCache": { + "activeArchOnly": { "type": "boolean", - "description": "Resets metro cache.", + "description": "Build native libraries only for the current device architecture for debug builds.", + "examples": ["x86_64", "arm64-v8a"], "default": false }, + "extraParams": { + "type": "string", + "description": "Custom params passed to gradle build command" + }, "interactive": { "type": "boolean", - "description": "Run packager server in interactive mode.", + "description": "Explicitly select build type and flavour to use before running a build" + }, + "sync": { + "type": "boolean", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "default": true }, - "activeArchOnly": { + "resetCache": { "type": "boolean", - "description": "Builds only for the active architecture (e.g. x86_64, arm64-v8a).", + "description": "Resets metro cache.", "default": false } }, diff --git a/packages/react-native/src/executors/run-ios/run-ios.impl.ts b/packages/react-native/src/executors/run-ios/run-ios.impl.ts index 9543f7dab4205..8150aa95055b9 100644 --- a/packages/react-native/src/executors/run-ios/run-ios.impl.ts +++ b/packages/react-native/src/executors/run-ios/run-ios.impl.ts @@ -1,4 +1,4 @@ -import { ExecutorContext } from '@nrwl/devkit'; +import { ExecutorContext, logger, names } from '@nrwl/devkit'; import { join } from 'path'; import { ChildProcess, fork } from 'child_process'; import { platform } from 'os'; @@ -11,6 +11,8 @@ import { import { podInstall } from '../../utils/pod-install-task'; import { ReactNativeRunIosOptions } from './schema'; import { runCliStart } from '../start/start.impl'; +import { getCliOptions } from '../../utils/get-cli-options'; +import { rmdirSync } from 'fs-extra'; export interface ReactNativeRunIosOutput { success: boolean; @@ -39,13 +41,17 @@ export default async function* runIosExecutor( ) ); } + if (options.install) { - await podInstall(join(context.root, projectRoot, 'ios')); + await podInstall( + join(context.root, projectRoot, 'ios'), + options.buildFolder + ); } try { const tasks = [runCliRunIOS(context.root, projectRoot, options)]; - if (options.packager && options.xcodeConfiguration !== 'Release') { + if (options.packager && options.mode !== 'Release') { tasks.push( runCliStart(context.root, projectRoot, { port: options.port, @@ -101,23 +107,14 @@ function runCliRunIOS( }); } -const nxOrStartOptions = [ - 'sync', - 'install', - 'packager', - 'port', - 'resetCache', - 'interactive', -]; +const nxOptions = ['sync', 'install', 'packager']; +const startOptions = ['port', 'resetCache']; +const deprecatedOptions = ['xcodeConfiguration']; -function createRunIOSOptions(options) { - return Object.keys(options).reduce((acc, k) => { - const v = options[k]; - if (k === 'xcodeConfiguration') { - acc.push('--configuration', v); - } else if (v && !nxOrStartOptions.includes(k)) { - acc.push(`--${k}`, options[k]); - } - return acc; - }, []); +function createRunIOSOptions(options: ReactNativeRunIosOptions) { + return getCliOptions( + options, + [...nxOptions, ...startOptions, ...deprecatedOptions], + ['buildFolder'] + ); } diff --git a/packages/react-native/src/executors/run-ios/schema.d.ts b/packages/react-native/src/executors/run-ios/schema.d.ts index 1b5c73ad15328..934e49183eb8e 100644 --- a/packages/react-native/src/executors/run-ios/schema.d.ts +++ b/packages/react-native/src/executors/run-ios/schema.d.ts @@ -1,14 +1,12 @@ -// part of options form https://github.com/react-native-community/cli/blob/master/packages/platform-ios/src/commands/runIOS/index.ts#L541 -export interface ReactNativeRunIosOptions { - xcodeConfiguration: string; - port: number; - scheme: string; - simulator: string; - device: string; - packager: boolean; // default is true - install?: boolean; - sync?: boolean; - terminal?: string; - resetCache: boolean; // default is false - interactive: boolean; // default is true +import { ReactNativeBuildIosOptions } from '../build-ios/schema'; +import { ReactNativeStartOptions } from '../start/schema'; + +// part of options form https://github.com/react-native-community/cli/blob/main/packages/cli-platform-ios/src/commands/runIOS/index.ts +export interface ReactNativeRunIosOptions extends ReactNativeBuildIosOptions { + /** + * @deprecated use mode instead, will be removed in nx 17. + */ + xcodeConfiguration?: string; + + binaryPath?: string; } diff --git a/packages/react-native/src/executors/run-ios/schema.json b/packages/react-native/src/executors/run-ios/schema.json index c841597f40b95..9e82e7dcc0a9d 100644 --- a/packages/react-native/src/executors/run-ios/schema.json +++ b/packages/react-native/src/executors/run-ios/schema.json @@ -9,15 +9,20 @@ "type": "object", "presets": [ { - "name": "Run iOS without cache", - "keys": [ - "xcodeConfiguration", - "install", - "sync", - "port", - "packager", - "resetCache" - ] + "name": "Run iOS on a simulator", + "keys": ["simulator"] + }, + { + "name": "Run iOS on a device", + "keys": ["device"] + }, + { + "name": "Run iOS on a device with udid", + "keys": ["udid"] + }, + { + "name": "Run `pod install` before building iOS app", + "keys": ["install"] } ], "properties": { @@ -26,16 +31,11 @@ "description": "Explicitly set the Xcode configuration to use.", "default": "Debug", "examples": ["Debug", "Release"], - "x-priority": "important" - }, - "scheme": { - "type": "string", - "description": "Explicitly set the Xcode scheme to use." + "x-deprecated": "Use `mode` instead. Deprecated from @react-native-community/cli. Will be removed in Nx 17." }, "simulator": { "type": "string", - "description": "Explicitly set simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version: `iPhone X (12.1)`.", - "default": "iPhone 14", + "description": "Explicitly set simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version: \"iPhone 6 (10.0)\"", "examples": [ "iPhone 14", "iPhone 13", @@ -45,34 +45,59 @@ ], "x-priority": "important" }, - "device": { + "mode": { "type": "string", - "description": "Explicitly set device to use by name. The value is not required if you have a single device connected.", + "description": "Explicitly set the scheme configuration to use", + "default": "Debug", + "examples": ["Debug", "Release"], "x-priority": "important" }, - "install": { - "type": "boolean", - "description": "Runs `pod install` for native modules before building iOS app.", - "default": false + "schema": { + "type": "string", + "description": "Explicitly set Xcode scheme to use" }, - "sync": { + "device": { + "type": "string", + "description": "Explicitly set device to use by name. The value is not required if you have a single device connected." + }, + "udid": { + "type": "string", + "description": "Explicitly set device to use by udid" + }, + "verbose": { "type": "boolean", - "description": "Syncs npm dependencies to `package.json` (for React Native autolink). Always true when `--install` is used.", - "default": true, - "x-priority": "internal" + "description": "Do not use xcbeautify or xcpretty even if installed" }, "port": { "type": "number", "description": "The port where the packager server is listening on.", "default": 8081 }, - "terminal": { + "xcconfig": { "type": "string", - "description": "Launches the Metro Bundler in a new window using the specified terminal path." + "description": "Explicitly set xcconfig to use" }, - "packager": { + "buildFolder": { + "type": "string", + "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory.", + "buildFolder": "./build" + }, + "interactive": { + "type": "boolean", + "description": "Explicitly select which scheme and configuration to use before running a build" + }, + "extraParams": { + "type": "string", + "description": "Custom params that will be passed to xcodebuild command." + }, + "install": { + "type": "boolean", + "description": "Runs `pod install` for native modules before building iOS app.", + "default": false + }, + "sync": { "type": "boolean", - "description": "Starts the packager server.", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "default": true }, "resetCache": { @@ -80,10 +105,14 @@ "description": "Resets metro cache.", "default": false }, - "interactive": { + "packager": { "type": "boolean", - "description": "Run packager server in interactive mode.", + "description": "Launch packager while building", "default": true + }, + "binaryPath": { + "type": "string", + "description": "Path relative to project root where pre-built .app binary lives." } }, "examplesFile": "../../../docs/run-ios-examples.md" diff --git a/packages/react-native/src/executors/start/schema.d.ts b/packages/react-native/src/executors/start/schema.d.ts index 1681796682b8f..6a50e82fcdc7f 100644 --- a/packages/react-native/src/executors/start/schema.d.ts +++ b/packages/react-native/src/executors/start/schema.d.ts @@ -1,3 +1,5 @@ +// options from https://github.com/react-native-community/cli/blob/main/packages/cli-plugin-metro/src/commands/start/index.ts + export interface ReactNativeStartOptions { port: number; resetCache: boolean; // default is false diff --git a/packages/react-native/src/generators/application/lib/add-project.ts b/packages/react-native/src/generators/application/lib/add-project.ts index 52aaffdcf282e..6e145b0387c85 100644 --- a/packages/react-native/src/generators/application/lib/add-project.ts +++ b/packages/react-native/src/generators/application/lib/add-project.ts @@ -44,6 +44,7 @@ function getTargets(options: NormalizedSchema) { architect['bundle-ios'] = { executor: '@nrwl/react-native:bundle', + outputs: ['{options.bundleOutput}'], options: { entryFile: options.entryFile, platform: 'ios', @@ -65,8 +66,20 @@ function getTargets(options: NormalizedSchema) { options: {}, }; + architect['build-ios'] = { + executor: '@nrwl/react-native:build-ios', + outputs: ['{projectRoot}/ios/build/Build'], + options: {}, + }; + + architect['pod-install'] = { + executor: '@nrwl/react-native:pod-install', + options: {}, + }; + architect['bundle-android'] = { executor: '@nrwl/react-native:bundle', + outputs: ['{options.bundleOutput}'], options: { entryFile: options.entryFile, platform: 'android', diff --git a/packages/react-native/src/migrations/update-15-9-1/add-build-ios-target.spec.ts b/packages/react-native/src/migrations/update-15-9-1/add-build-ios-target.spec.ts new file mode 100644 index 0000000000000..3011e395cc9af --- /dev/null +++ b/packages/react-native/src/migrations/update-15-9-1/add-build-ios-target.spec.ts @@ -0,0 +1,36 @@ +import { addProjectConfiguration, getProjects, Tree } from '@nrwl/devkit'; +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import update from './add-build-ios-target'; + +describe('add-build-ios-target', () => { + let tree: Tree; + + beforeEach(async () => { + tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); + addProjectConfiguration(tree, 'product', { + root: 'apps/product', + sourceRoot: 'apps/product/src', + targets: { + start: { + executor: '@nrwl/react-native:start', + }, + }, + }); + }); + + it(`should update project.json with target build-ios and pod-install`, async () => { + await update(tree); + + getProjects(tree).forEach((project) => { + expect(project.targets['build-ios']).toEqual({ + executor: '@nrwl/react-native:build-ios', + outputs: ['{projectRoot}/ios/build/Build'], + options: {}, + }); + expect(project.targets['pod-install']).toEqual({ + executor: '@nrwl/react-native:pod-install', + options: {}, + }); + }); + }); +}); diff --git a/packages/react-native/src/migrations/update-15-9-1/add-build-ios-target.ts b/packages/react-native/src/migrations/update-15-9-1/add-build-ios-target.ts new file mode 100644 index 0000000000000..0ccaa673a4fe3 --- /dev/null +++ b/packages/react-native/src/migrations/update-15-9-1/add-build-ios-target.ts @@ -0,0 +1,35 @@ +import { + Tree, + formatFiles, + getProjects, + updateProjectConfiguration, +} from '@nrwl/devkit'; + +/** + * Add build-ios target for react-native + */ +export default async function update(tree: Tree) { + const projects = getProjects(tree); + + for (const [name, config] of projects.entries()) { + if (config.targets?.['start']?.executor === '@nrwl/react-native:start') { + if (!config.targets['build-ios']) { + config.targets['build-ios'] = { + executor: '@nrwl/react-native:build-ios', + outputs: ['{projectRoot}/ios/build/Build'], + options: {}, + }; + } + if (!config.targets['pod-install']) { + config.targets['pod-install'] = { + executor: '@nrwl/react-native:pod-install', + options: {}, + }; + } + } + + updateProjectConfiguration(tree, name, config); + } + + await formatFiles(tree); +} diff --git a/packages/react-native/src/utils/get-cli-options.ts b/packages/react-native/src/utils/get-cli-options.ts new file mode 100644 index 0000000000000..29b870896dd6d --- /dev/null +++ b/packages/react-native/src/utils/get-cli-options.ts @@ -0,0 +1,30 @@ +import { names } from '@nrwl/devkit'; + +/** + * This function normalizes the options passed in to the Nx and returns an array of strings that can be passed to the React Native CLI. + * @param options Nx options + * @param optionKeysToIgnore Keys to ignore + * @param optionKeysInCamelName Keys that are in camel case. Most react native cli options are in kebab case, but some are in camel case. + * @returns options that can be passed to the React Native CLI + */ +export function getCliOptions( + options: T, + optionKeysToIgnore: string[] = [], + optionKeysInCamelName: string[] = [] +): string[] { + return Object.keys(options).reduce((acc, optionKey) => { + const optionValue = options[optionKey]; + if (!optionKeysToIgnore.includes(optionKey)) { + const cliKey = optionKeysInCamelName.includes(optionKey) + ? names(optionKey).propertyName + : names(optionKey).fileName; // cli uses kebab case as default + if (typeof optionValue === 'boolean' && optionValue) { + // no need to pass in the value when it is true, just the flag name + acc.push(`--${cliKey}`); + } else { + acc.push(`--${cliKey}`, optionValue); + } + } + return acc; + }, []); +} diff --git a/packages/react-native/src/utils/pod-install-task.ts b/packages/react-native/src/utils/pod-install-task.ts index fda361d92a0f1..6fd047394438b 100644 --- a/packages/react-native/src/utils/pod-install-task.ts +++ b/packages/react-native/src/utils/pod-install-task.ts @@ -2,6 +2,8 @@ import { execSync } from 'child_process'; import { platform } from 'os'; import * as chalk from 'chalk'; import { GeneratorCallback, logger } from '@nrwl/devkit'; +import { rmdirSync, existsSync } from 'fs-extra'; +import { join } from 'path'; const podInstallErrorMessage = ` Running ${chalk.bold('pod install')} failed, see above. @@ -21,7 +23,8 @@ ${chalk.bold('sudo xcode-select --switch /Applications/Xcode.app')} */ export function runPodInstall( iosDirectory: string, - install: boolean = true + install: boolean = true, + buildFolder?: string ): GeneratorCallback { return () => { if (platform() !== 'darwin') { @@ -36,17 +39,27 @@ export function runPodInstall( logger.info(`Running \`pod install\` from "${iosDirectory}"`); - return podInstall(iosDirectory); + return podInstall(iosDirectory, buildFolder); }; } -export function podInstall(iosDirectory: string): Promise { +export function podInstall( + iosDirectory: string, + buildFolder?: string +): Promise { return new Promise((resolve, reject) => { const result = execSync('pod install', { cwd: iosDirectory, }); logger.info(result.toString()); if (result.toString().includes('Pod installation complete')) { + // Remove build folder after pod install + if (buildFolder) { + buildFolder = join(iosDirectory, buildFolder); + if (existsSync(buildFolder)) { + rmdirSync(buildFolder, { recursive: true }); + } + } resolve(); } else { reject(new Error(podInstallErrorMessage)); diff --git a/packages/react-native/src/utils/symlink-task.ts b/packages/react-native/src/utils/symlink-task.ts index 47662c59e55c9..bc527097d20ab 100644 --- a/packages/react-native/src/utils/symlink-task.ts +++ b/packages/react-native/src/utils/symlink-task.ts @@ -3,13 +3,13 @@ import * as chalk from 'chalk'; import { GeneratorCallback, logger } from '@nrwl/devkit'; export function runSymlink( - worksapceRoot: string, + workspaceRoot: string, projectRoot: string ): GeneratorCallback { return () => { logger.info(`creating symlinks for ${chalk.bold(projectRoot)}`); try { - ensureNodeModulesSymlink(worksapceRoot, projectRoot); + ensureNodeModulesSymlink(workspaceRoot, projectRoot); } catch { throw new Error( `Failed to create symlinks for ${chalk.bold(projectRoot)}` diff --git a/packages/react-native/src/utils/versions.ts b/packages/react-native/src/utils/versions.ts index bd875c05caa05..86a4a9c7d73e1 100644 --- a/packages/react-native/src/utils/versions.ts +++ b/packages/react-native/src/utils/versions.ts @@ -7,8 +7,8 @@ export const typesNodeVersion = '18.14.4'; export const metroVersion = '0.74.1'; -export const reactNativeCommunityCli = '10.2.0'; -export const reactNativeCommunityCliIos = '10.2.0'; +export const reactNativeCommunityCli = '10.2.1'; +export const reactNativeCommunityCliIos = '10.2.1'; export const reactNativeCommunityCliAndroid = '10.2.0'; export const reactVersion = '18.2.0'; From ac7fbbd121a2d82a8f01dc9344c902c21e094d75 Mon Sep 17 00:00:00 2001 From: Victor Savkin Date: Fri, 24 Mar 2023 10:57:59 -0400 Subject: [PATCH 51/76] docs(misc): minor changes in dte docs --- docs/nx-cloud/reference/env-vars.md | 49 ++++++++---- docs/shared/ci-overview.md | 49 ------------ .../distribute-task-execution.md | 80 ++++++++++++++----- docs/shared/monorepo-ci-azure.md | 12 ++- .../shared/monorepo-ci-bitbucket-pipelines.md | 8 +- docs/shared/monorepo-ci-circle-ci.md | 12 ++- docs/shared/monorepo-ci-github-actions.md | 12 ++- docs/shared/monorepo-ci-gitlab.md | 12 ++- docs/shared/monorepo-ci-jenkins.md | 20 +++-- 9 files changed, 124 insertions(+), 130 deletions(-) diff --git a/docs/nx-cloud/reference/env-vars.md b/docs/nx-cloud/reference/env-vars.md index 8f8b73a5987b0..c4880f4133c94 100644 --- a/docs/nx-cloud/reference/env-vars.md +++ b/docs/nx-cloud/reference/env-vars.md @@ -1,20 +1,43 @@ # Environment Variables -### NX_BRANCH +## NX_BRANCH and NX_CI_EXECUTION_ID -The `@nrwl/nx-cloud` requires the `NX_BRANCH` environment variables to be set. For many CI providers (e.g., GitHub Actions), the runner is able to set it automatically. For others, the variable will have to be set manually. If you set it manually, note that `NX_BRANCH` has to be set to a PR number for the GitHub integration to work. +When running commands on CI, `nx-cloud` needs to know the current branch and the current CI execution ID (something that +uniquely identifies the current CI run or job). **For most CI providers, `nx-cloud` is able to determine both +automatically, and you don't need to do anything.** For others, you need to set the `NX_BRANCH` and `NX_CI_EXECUTION_ID` +env variables manually. You need to set them on the main job and on all agents, and the values have to match. + +To make GitHub, BitBucket and GitLab integrations works, you need to set `NX_BRANCH` to the PR number, when the PR +number is available. + +Nx Cloud uses `NX_CI_EXECUTION_ID` to match the agents and the main job. Sometimes you might have multiple +main jobs (e.g., when running CI on both Linux and Windows or when running the same commands against different versions +of Node.js or Java). In this case you can set the `NX_CI_EXECUTION_ENV` env variable on main jobs and agents. The main +job where the `NX_CI_EXECUTION_ENV` is set to, say, `macos`, will connect to the agents with the same env name. + +#### NX_RUN_GROUP + +Older versions of `@nrwl/nx-cloud` used `NX_RUN_GROUP` instead of `NX_CI_EXECUTION_ID` and `NX_CI_EXECUTION_ENV`. It +served the same purpose. ### NX_CLOUD_ACCESS_TOKEN -You can also configure the access token by setting the `NX_CLOUD_ACCESS_TOKEN` environment variable. `NX_CLOUD_ACCESS_TOKEN` takes precedence over the `accessToken` property. It's common to have a read-only token stored in `nx.json` and a read-write token set via `NX_CLOUD_ACCESS_TOKEN` in CI. If you are using this environment variable with Distributed Task Execution, the value on the main and agent jobs must match. +You can also configure the access token by setting the `NX_CLOUD_ACCESS_TOKEN` environment +variable. `NX_CLOUD_ACCESS_TOKEN` takes precedence over the `accessToken` property. It's common to have a read-only +token stored in `nx.json` and a read-write token set via `NX_CLOUD_ACCESS_TOKEN` in CI. If you are using this +environment variable with Distributed Task Execution, the value on the main and agent jobs must match. ### NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT -The Nx Cloud plans distributed task execution based on the available information from the running agents. Due to asynchronous nature of CI jobs, an agent might not have been created or started at the moment when DTE is initiated. Setting `NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT` to say 8 will inform Nx Cloud to assume that there will be 8 agents running. This can have an impact on better distribution of the tasks and allocation of the agents. +The Nx Cloud plans distributed task execution based on the available information from the running agents. Due to +asynchronous nature of CI jobs, an agent might not have been created or started at the moment when DTE is initiated. +Setting `NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT` to say 8 will inform Nx Cloud to assume that there will be 8 agents +running. This can have an impact on better distribution of the tasks and allocation of the agents. ### NX_CLOUD_DISTRIBUTED_EXECUTION_STOP_AGENTS_ON_FAILURE -Setting `NX_CLOUD_DISTRIBUTED_EXECUTION_STOP_AGENTS_ON_FAILURE` to `true` will tell Nx Cloud to stop agents if a command fails. When set to false (default value), you need to make sure to invoke `nx-cloud stop-all-agents` even if CI fails. +Setting `NX_CLOUD_DISTRIBUTED_EXECUTION_STOP_AGENTS_ON_FAILURE` to `true` will tell Nx Cloud to stop agents if a command +fails. When set to false (default value), you need to make sure to invoke `nx-cloud stop-all-agents` even if CI fails. ### NX_CLOUD_DISTRIBUTED_EXECUTION @@ -22,22 +45,14 @@ Setting `NX_CLOUD_DISTRIBUTED_EXECUTION` to `true` enables distributed task exec ### NX_CLOUD_ENCRYPTION_KEY -You can set the `encryptionKey` property in `nx.json` or set the `NX_CLOUD_ENCRYPTION_KEY` environment variable to enable the e2e encryption of your artifacts. In this case, the artifacts will be encrypted/decrypted on your machine. - -### NX_CLOUD_ENV_NAME - -Setting `NX_CLOUD_ENV_NAME` will prefix all your commands so you can easily distinguish them in the UI and in GitHub comments. For instance, if you run the same set of commands on Windows and Linux machines, you can set `NX_CLOUD_ENV_NAME` to `win` on the Windows agent, and `linux` on Linux agents. +You can set the `encryptionKey` property in `nx.json` or set the `NX_CLOUD_ENCRYPTION_KEY` environment variable to +enable the e2e encryption of your artifacts. In this case, the artifacts will be encrypted/decrypted on your machine. ### NX_CLOUD_NO_TIMEOUTS By default, Nx Cloud requests will time out after 10 seconds. `NX_CLOUD_NO_TIMEOUTS` disables the timeout. -### NX_RUN_GROUP - -- The `@nrwl/nx-cloud` requires the `NX_RUN_GROUP` environment variables to be set. For many CI providers (e.g., GitHub - Actions), the runner is able to set it automatically. For others, the variable will have to be set manually. If you set - it manually, note that `NX_RUN_GROUP` has to be a unique value associated with a CI run. - ### NX_VERBOSE_LOGGING -Setting `NX_VERBOSE_LOGGING` to true will output the debug information about agents communicating with the main job. This can be useful for debugging unexpected cache misses and issues with on-prem setups. +Setting `NX_VERBOSE_LOGGING` to true will output the debug information about agents communicating with the main job. +This can be useful for debugging unexpected cache misses and issues with on-prem setups. diff --git a/docs/shared/ci-overview.md b/docs/shared/ci-overview.md index 339088674f1ba..bc8f19db93bb1 100644 --- a/docs/shared/ci-overview.md +++ b/docs/shared/ci-overview.md @@ -1,54 +1,5 @@ # Configuring CI for Nx workspaces -Nx is a smart, fast and extensible build system, and it works really well with monorepos. Monorepos provide a lot of advantages: - -- Everything at that current commit works together. Changes can be verified across all affected parts of the organization. -- Easy to split code into composable modules -- Easier dependency management -- One toolchain setup -- Code editors and IDEs are "workspace" aware -- Consistent developer experience -- And more ... - -But they come with their own technical challenges. The more code you add into your repository and the more code you have to build/test/lint, the slower the CI gets. There are two ways to look at this time spent. In average time, and worst case scenario time. When configured properly, your average CI is the time it takes a given change to go through the CI process. The worst case scenario time is the time it takes to rebuild every project in your monorepo based on a given change. These are baseline you use to measure how long it takes to process pull requests in your CI/CD environment. - -Adding Nx to your CI pipeline makes this more efficient. - -Nx provides out-of-the-box implementation of CI workflows for `GitHub`, `Azure` and `CircleCI` during the [creation of the Nx workspace](/packages/nx/documents/create-nx-workspace#ci) or later using the [ci-workflow](/packages/workspace/generators/ci-workflow) generator. - -{% nx-cloud-section %} - -## Distributed CI with Nx Cloud - -A computation cache is created on your local machine to make the developer experience faster. This allows you to not waste time re-building, re-testing, re-linting, or any number of other actions you might take on code that hasn't changed. Because the cache is stored locally, you are the only member of your team that can take advantage of these instant commands. You can manage and share this cache manually. - -Nx Cloud allows this cache to be shared across your entire organization, meaning that any cacheable operation completed on your workspace only needs to be run once. Nx Cloud also allows you to distribute your CI across multiple machines to make sure the CI is fast even for very large repos. - -In order to use distributed task execution, we need to start agents and set the `NX_CLOUD_DISTRIBUTED_EXECUTION` flag to `true`. -We enable agents to listen for Nx commands using: - -```shell -npx nx-cloud start-agent -``` - -and notify Nx Cloud of the upcoming Nx commands using: - -```shell -npx nx-cloud start-ci-run -``` - -Once all tasks are finished, we must not forget to cleanup used agents: - -```shell -npx nx-cloud stop-all-agents -``` - -Learn more about configuring your CI environment using Nx Cloud with [Distributed Caching](/core-features/share-your-cache) and [Distributed Task Execution](/core-features/distribute-task-execution). - -{% /nx-cloud-section %} - -## CI provider specific documentation - The following guides cover optimizing your CI/CD environments with affected commands and distributed caching: - [Setting up CI using Azure Pipelines](/recipes/ci/monorepo-ci-azure) diff --git a/docs/shared/core-features/distribute-task-execution.md b/docs/shared/core-features/distribute-task-execution.md index 2a7d639f85a22..844e1471fe0a6 100644 --- a/docs/shared/core-features/distribute-task-execution.md +++ b/docs/shared/core-features/distribute-task-execution.md @@ -1,24 +1,36 @@ # Distribute Task Execution (DTE) -Nx speeds up your average CI time with [caching](/core-features/cache-task-results) and the [affected command](/concepts/affected). But neither of these features help with the worst case scenario. When something at the core of your repo has been modified and every task needs to be run in CI, the only way to improve the performance is by adding more agent jobs and efficiently parallelizing the tasks. +Nx speeds up your average CI time with [caching](/core-features/cache-task-results) and +the [affected command](/concepts/affected). But neither of these features help with the worst case scenario. When +something at the core of your repo has been modified and every task needs to be run in CI, the only way to improve the +performance is by adding more agent jobs and efficiently parallelizing the tasks. -The most obvious way to parallelize tasks is to split tasks up by type: running all tests on one job, all builds on another and all lint tasks on a third. This strategy is called binning. This can be made difficult if some test tasks have build tasks as prerequisites, but assuming you figure out some way to handle that, a typical set up can look like the diagram below. Here the test tasks are delayed until all necessary build artifacts are ready, but the build and lint tasks can start right away. +The most obvious way to parallelize tasks is to split tasks up by type: running all tests on one job, all builds on +another and all lint tasks on a third. This strategy is called binning. This can be made difficult if some test tasks +have build tasks as prerequisites, but assuming you figure out some way to handle that, a typical set up can look like +the diagram below. Here the test tasks are delayed until all necessary build artifacts are ready, but the build and lint +tasks can start right away. ![CI using binning](../images/dte/binning.svg) -The problem with the binning approach is you'll end up with some idle time on one or more jobs. Nx's distributed task execution reduces that idle time to the minimum possible by assigning each individual task to agent jobs based on the task's average run time. Nx also guarantees that tasks are executed in the correct order and uses distributed caching to make sure that build artifacts from previous tasks are present on every agent job that needs them. +The problem with the binning approach is you'll end up with some idle time on one or more jobs. Nx's distributed task +execution reduces that idle time to the minimum possible by assigning each individual task to agent jobs based on the +task's average run time. Nx also guarantees that tasks are executed in the correct order and uses distributed caching to +make sure that build artifacts from previous tasks are present on every agent job that needs them. When you set up Nx's distributed task execution, your task graph will look more like this: ![CI using DTE](../images/dte/3agents.svg) -And not only will CI finish faster, but the debugging experience is the same as if you ran all of your CI on a single job. That's because Nx uses distributed caching to recreate all of the logs and build artifacts on the main job. +And not only will CI finish faster, but the debugging experience is the same as if you ran all of your CI on a single +job. That's because Nx uses distributed caching to recreate all of the logs and build artifacts on the main job. Find more information in this [detailed guide to improve your worst case CI times](/concepts/dte). ## Set up -To distribute your task execution, you need to (1) connect to Nx Cloud and (2) enable DTE in your CI workflow. Each of these steps can be enabled with a single command: +To distribute your task execution, you need to (1) connect to Nx Cloud and (2) enable DTE in your CI workflow. Each of +these steps can be enabled with a single command: ```shell title="1. Connect to Nx Cloud" nx connect @@ -28,17 +40,23 @@ nx connect This command installs the latest version of `@nrwl/nx-cloud`. The latest version works with any version of Nx >= 13.0. {% /callout %} +If you have a new workspace, you can generate the CI configuration as follows: + ```shell title="2. Enable DTE in CI" nx generate @nrwl/workspace:ci-workflow --ci=github ``` -The `--ci` flag can be `github`, `circleci` or `azure`. For more details on setting up DTE, read [this guide](https://nx.dev/nx-cloud/set-up/set-up-dte). +The `--ci` flag can be `github`, `circleci` or `azure`. + +For existing workspaces you would probably want to adjust your configuration by hand. See below for examples. ## CI Execution Flow -Distributed task execution can work on any CI provider. You are responsible for launching jobs in your CI system. Nx Cloud then coordinates the way those jobs work together. There are two different kinds of jobs that you'll need to create in your CI system. +Distributed task execution can work on any CI provider. You are responsible for launching jobs in your CI system. Nx +Cloud then coordinates the way those jobs work together. There are two different kinds of jobs that you'll need to +create in your CI system. -1. One main job (CI) that controls what is going to be executed +1. Main job that controls what is going to be executed 2. Multiple agent jobs that actually execute the tasks ![Diagram showing Nx Cloud distributing tasks to multiple agents](../images/dte/distributed-caching-and-task-execution.svg) @@ -47,28 +65,40 @@ The main CI job execution flow looks like this: ```yaml # Coordinate the agents to run the tasks -- npx nx-cloud start-ci-run +- npx nx-cloud start-ci-run --stop-agents-after="build" # makes all nx commands distributed by default # Run any commands you want here -- nx affected --target=lint & nx affected --target=test & nx affected --target=build -# Stop any run away agents -- npx nx-cloud stop-all-agents +- nx affected -t test lint build # run on agents in a distributed fashion +- nx affected -t deploy --no-dte # run the main job ``` -The agent job execution flow is very simple: +The agent job execution flow is simple: ```yaml # Wait for tasks to execute - npx nx-cloud start-agent ``` +Depending on your CI setup, you might want to stop the agents explicitly. You can do it as follows + +```yam +# Stop any run away agents +- npx nx-cloud stop-all-agents +``` + +> For most CI providers, Nx Cloud is able to able to match the main and the agents automatically but for some you might +> need to provision the `NX_BRANCH` and `NX_CI_EXECUTION_ID` env variables ( +> see [Environment Variables](/nx-cloud/reference/env-vars) for more info). + The main job looks more or less the same way as if you haven't used any distribution. The only thing you need to do is -to invoke `npx nx-cloud start-ci-run` at the beginning and optionally invoke `npx nx-cloud stop-all-agents` at the end. +to invoke `npx nx-cloud start-ci-run` at the beginning and, optionally, invoke `npx nx-cloud stop-all-agents` at the +end. The agent jobs run long-running `start-agent` processes that execute all the tasks associated with a given CI run. The only thing you need to do to set them up is to invoke `npx nx-cloud start-agent`. This process will keep running until Nx Cloud tells it to terminate. -> Note it's important that the main job and the agent jobs have the same environment and the same source code. They start +> Note it's important that the main job and the agent jobs have the same environment and the same source code. They +> start > around the same time. And, once the main job completes, all the agents > will be stopped. @@ -83,25 +113,33 @@ on agents are copied over to the main job, as if the main job had built everythi ## Running Things in Parallel -`--parallel` is propagated to the agents. E.g., `npx nx affected --target=build --parallel=3 --dte` tells Nx Cloud to run +`--parallel` is propagated to the agents. E.g., `npx nx affected -t build --parallel=3` tells Nx Cloud to +run up to 3 build targets in parallel on each agent. So if you have say 10 agents, you will run up to 30 builds in parallel across all of them. You also want to run as many commands in parallel as you can. For instance, ```yaml -- nx affected --target=build -- nx affected --target=test -- nx affected --target=lint +- nx affected -t build +- nx affected -t test +- nx affected -t lint ``` is worse than ```yaml -- nx affected --target=build & nx affected --target=test & nx affected --target=lint +- nx affected -t build & nx affected -t test & nx affected -t lint +``` + +or + +```yaml +- nx affected -t build test lint ``` -The latter is going to schedule all the three commands at the same time, so if an agent cannot find anything to build, it will start running tests and lints. The result is better agent utilization and shorter CI time. +The latter two are going to schedule all the three commands at the same time, so if an agent cannot find anything to +build, they will start running tests and lints. The result is better agent utilization and shorter CI time. ## CI/CD Examples diff --git a/docs/shared/monorepo-ci-azure.md b/docs/shared/monorepo-ci-azure.md index db055c34ef388..1dd7489d1b858 100644 --- a/docs/shared/monorepo-ci-azure.md +++ b/docs/shared/monorepo-ci-azure.md @@ -32,9 +32,9 @@ jobs: - script: npx nx format:check - - script: npx nx affected --base=$(BASE_SHA) --target=lint --parallel=3 - - script: npx nx affected --base=$(BASE_SHA) --target=test --parallel=3 --configuration=ci - - script: npx nx affected --base=$(BASE_SHA) --target=build --parallel=3 + - script: npx nx affected --base=$(BASE_SHA) -t lint --parallel=3 + - script: npx nx affected --base=$(BASE_SHA) -t test --parallel=3 --configuration=ci + - script: npx nx affected --base=$(BASE_SHA) -t build --parallel=3 ``` The `main` job implements the CI workflow. @@ -43,9 +43,7 @@ The `main` job implements the CI workflow. ## Distributed CI with Nx Cloud -To use distributed task execution, we need to start agents and set the `NX_CLOUD_DISTRIBUTED_EXECUTION` flag to `true`. - -Read more about the [Distributed CI setup with Nx Cloud](/recipes/ci/ci-setup#distributed-ci-with-nx-cloud). +Read more about [Distributed Task Execution (DTE)](/core-features/distribute-task-execution). ```yaml trigger: @@ -84,7 +82,7 @@ jobs: - script: npx nx-cloud start-ci-run --stop-agents-after="build" - script: npx nx-cloud record -- npx nx format:check --base=$(BASE_SHA) --head=$(HEAD_SHA) - - script: npx nx affected --base=$(BASE_SHA) --head=$(HEAD_SHA) --target=lint --parallel=3 & npx nx affected --base=$(BASE_SHA) --head=$(HEAD_SHA) --target=test --parallel=3 --configuration=ci & npx nx affected --base=$(BASE_SHA) --head=$(HEAD_SHA) --target=build --parallel=3 + - script: npx nx affected --base=$(BASE_SHA) --head=$(HEAD_SHA) -t lint --parallel=3 & npx nx affected --base=$(BASE_SHA) --head=$(HEAD_SHA) -t test --parallel=3 --configuration=ci & npx nx affected --base=$(BASE_SHA) --head=$(HEAD_SHA) -t build --parallel=3 ``` You can also use our [ci-workflow generator](/packages/workspace/generators/ci-workflow) to generate the pipeline file. diff --git a/docs/shared/monorepo-ci-bitbucket-pipelines.md b/docs/shared/monorepo-ci-bitbucket-pipelines.md index 6824bbc8f32fa..a3de13ec3d11c 100644 --- a/docs/shared/monorepo-ci-bitbucket-pipelines.md +++ b/docs/shared/monorepo-ci-bitbucket-pipelines.md @@ -14,9 +14,9 @@ pipelines: script: - npm ci - npx nx format:check - - npx nx affected --target=lint --base=origin/master --parallel --max-parallel=3 - - npx nx affected --target=test --base=origin/master --parallel --max-parallel=3 --configuration=ci - - npx nx affected --target=build --base=origin/master --head=HEAD --parallel --max-parallel=3 + - npx nx affected -t lint --base=origin/master --parallel --max-parallel=3 + - npx nx affected -t test --base=origin/master --parallel --max-parallel=3 --configuration=ci + - npx nx affected -t build --base=origin/master --head=HEAD --parallel --max-parallel=3 branches: main: @@ -27,7 +27,7 @@ pipelines: script: - npm ci - npx nx format:check - - npx nx affected --target=lint --base=origin/master --parallel --max-parallel=3 & npx nx affected --target=test --base=HEAD~1 --parallel --max-parallel=3 --configuration=ci & npx nx affected --target=build --base=HEAD~1 --parallel --max-parallel=3 + - npx nx affected -t lint --base=origin/master --parallel --max-parallel=3 & npx nx affected -t test --base=HEAD~1 --parallel --max-parallel=3 --configuration=ci & npx nx affected -t build --base=HEAD~1 --parallel --max-parallel=3 ``` The `pull-requests` and `main` jobs implement the CI workflow. diff --git a/docs/shared/monorepo-ci-circle-ci.md b/docs/shared/monorepo-ci-circle-ci.md index e8d5355028714..966a69644af52 100644 --- a/docs/shared/monorepo-ci-circle-ci.md +++ b/docs/shared/monorepo-ci-circle-ci.md @@ -19,9 +19,9 @@ jobs: - nx/set-shas - run: npx nx format:check - - run: npx nx affected --base=$NX_BASE --head=$NX_HEAD --target=lint --parallel=3 - - run: npx nx affected --base=$NX_BASE --head=$NX_HEAD --target=test --parallel=3 --configuration=ci - - run: npx nx affected --base=$NX_BASE --head=$NX_HEAD --target=build --parallel=3 + - run: npx nx affected --base=$NX_BASE --head=$NX_HEAD -t lint --parallel=3 + - run: npx nx affected --base=$NX_BASE --head=$NX_HEAD -t test --parallel=3 --configuration=ci + - run: npx nx affected --base=$NX_BASE --head=$NX_HEAD -t build --parallel=3 workflows: build: jobs: @@ -42,9 +42,7 @@ It should be a user token, not the project token. ## Distributed CI with Nx Cloud -To use distributed task execution, we need to start agents and set the `NX_CLOUD_DISTRIBUTED_EXECUTION` flag to `true`. - -Read more about the [Distributed CI setup with Nx Cloud](/recipes/ci/ci-setup#distributed-ci-with-nx-cloud). +Read more about [Distributed Task Execution (DTE)](/core-features/distribute-task-execution). ```yaml version: 2.1 @@ -73,7 +71,7 @@ jobs: - run: npx nx-cloud start-ci-run --stop-agents-after="build" - run: npx nx-cloud record -- npx nx format:check - - run: npx nx affected --base=$NX_BASE --head=$NX_HEAD --target=lint --parallel=3 & npx nx affected --base=$NX_BASE --head=$NX_HEAD --target=test --parallel=3 --configuration=ci & npx nx affected --base=$NX_BASE --head=$NX_HEAD --target=build --parallel=3 + - run: npx nx affected --base=$NX_BASE --head=$NX_HEAD -t lint --parallel=3 & npx nx affected --base=$NX_BASE --head=$NX_HEAD -t test --parallel=3 --configuration=ci & npx nx affected --base=$NX_BASE --head=$NX_HEAD -t build --parallel=3 workflows: build: jobs: diff --git a/docs/shared/monorepo-ci-github-actions.md b/docs/shared/monorepo-ci-github-actions.md index c404fa54a687d..855527b65151f 100644 --- a/docs/shared/monorepo-ci-github-actions.md +++ b/docs/shared/monorepo-ci-github-actions.md @@ -24,9 +24,9 @@ jobs: - run: npm ci - run: npx nx format:check - - run: npx nx affected --target=lint --parallel=3 - - run: npx nx affected --target=test --parallel=3 --configuration=ci - - run: npx nx affected --target=build --parallel=3 + - run: npx nx affected -t lint --parallel=3 + - run: npx nx affected -t test --parallel=3 --configuration=ci + - run: npx nx affected -t build --parallel=3 ``` The `pr` and `main` jobs implement the CI workflow. Setting `timeout-minutes` is needed only if you have very slow tasks. @@ -41,9 +41,7 @@ If you're using this action in the context of a branch you may need to add `run: ## Distributed CI with Nx Cloud -To use distributed task execution, we need to start agents and set the `NX_CLOUD_DISTRIBUTED_EXECUTION` flag to `true`. - -Read more about the [Distributed CI setup with Nx Cloud](/recipes/ci/ci-setup#distributed-ci-with-nx-cloud). +Read more about [Distributed Task Execution (DTE)](/core-features/distribute-task-execution). ```yaml name: CI @@ -62,7 +60,7 @@ jobs: parallel-commands: | npx nx-cloud record -- npx nx format:check parallel-commands-on-agents: | - npx nx affected --target=lint --parallel=3 & npx nx affected --target=test --parallel=3 --configuration=ci & npx nx affected --target=build --parallel=3 + npx nx affected -t lint --parallel=3 & npx nx affected -t test --parallel=3 --configuration=ci & npx nx affected -t build --parallel=3 agents: name: Nx Cloud - Agents diff --git a/docs/shared/monorepo-ci-gitlab.md b/docs/shared/monorepo-ci-gitlab.md index 41d57d22b307a..8ab51b43d8cf2 100644 --- a/docs/shared/monorepo-ci-gitlab.md +++ b/docs/shared/monorepo-ci-gitlab.md @@ -38,19 +38,19 @@ lint: stage: test extends: .distributed script: - - npx nx affected --base=$NX_BASE --head=$NX_HEAD --target=lint --parallel=3 + - npx nx affected --base=$NX_BASE --head=$NX_HEAD -t lint --parallel=3 test: stage: test extends: .distributed script: - - npx nx affected --base=$NX_BASE --head=$NX_HEAD --target=test --parallel=3 --configuration=ci + - npx nx affected --base=$NX_BASE --head=$NX_HEAD -t test --parallel=3 --configuration=ci build: stage: build extends: .distributed script: - - npx nx affected --base=$NX_BASE --head=$NX_HEAD --target=build --parallel=3 + - npx nx affected --base=$NX_BASE --head=$NX_HEAD -t build --parallel=3 ``` The `build` and `test` jobs implement the CI workflow using `.distributed` as a template to keep the CI configuration file more readable. @@ -59,9 +59,7 @@ The `build` and `test` jobs implement the CI workflow using `.distributed` as a ## Distributed CI with Nx Cloud -To use distributed task execution, we need to start agents and set the `NX_CLOUD_DISTRIBUTED_EXECUTION` flag to `true`. - -Read more about the [Distributed CI setup with Nx Cloud](/recipes/ci/ci-setup#distributed-ci-with-nx-cloud). +Read more about [Distributed Task Execution (DTE)](/core-features/distribute-task-execution). ```yaml image: node:18 @@ -109,7 +107,7 @@ nx-dte: script: - yarn nx-cloud start-ci-run --stop-agents-after="build" - yarn nx-cloud record -- yarn nx format:check --base=$NX_BASE --head=$NX_HEAD - - yarn nx affected --base=$NX_BASE --head=$NX_HEAD --target=lint --parallel=3 & yarn nx affected --base=$NX_BASE --head=$NX_HEAD --target=test --parallel=3 --configuration=ci & yarn nx affected --base=$NX_BASE --head=$NX_HEAD --target=e2e --parallel=3 & yarn nx affected --base=$NX_BASE --head=$NX_HEAD --target=build --parallel=3 + - yarn nx affected --base=$NX_BASE --head=$NX_HEAD -t lint --parallel=3 & yarn nx affected --base=$NX_BASE --head=$NX_HEAD -t test --parallel=3 --configuration=ci & yarn nx affected --base=$NX_BASE --head=$NX_HEAD -t e2e --parallel=3 & yarn nx affected --base=$NX_BASE --head=$NX_HEAD -t build --parallel=3 # Create as many agents as you want nx-dte-agent1: diff --git a/docs/shared/monorepo-ci-jenkins.md b/docs/shared/monorepo-ci-jenkins.md index 29c8fe5230f34..b987140adae9b 100644 --- a/docs/shared/monorepo-ci-jenkins.md +++ b/docs/shared/monorepo-ci-jenkins.md @@ -23,9 +23,9 @@ pipeline { steps { sh "npm ci" sh "npx nx format:check" - sh "npx nx affected --base=HEAD~1 --target=lint --parallel=3" - sh "npx nx affected --base=HEAD~1 --target=test --parallel=3" - sh "npx nx affected --base=HEAD~1 --target=build --parallel=3" + sh "npx nx affected --base=HEAD~1 -t lint --parallel=3" + sh "npx nx affected --base=HEAD~1 -t test --parallel=3" + sh "npx nx affected --base=HEAD~1 -t build --parallel=3" } } stage('PR') { @@ -36,9 +36,9 @@ pipeline { steps { sh "npm ci" sh "npx nx format:check" - sh "npx nx affected --base origin/${env.CHANGE_TARGET} --target=lint --parallel=3" - sh "npx nx affected --base origin/${env.CHANGE_TARGET} --target=test --parallel=3 --configuration=ci" - sh "npx nx affected --base origin/${env.CHANGE_TARGET} --target=build --parallel=3" + sh "npx nx affected --base origin/${env.CHANGE_TARGET} -t lint --parallel=3" + sh "npx nx affected --base origin/${env.CHANGE_TARGET} -t test --parallel=3 --configuration=ci" + sh "npx nx affected --base origin/${env.CHANGE_TARGET} -t build --parallel=3" } } } @@ -53,9 +53,7 @@ The `pr` and `main` jobs implement the CI workflow. ## Distributed CI with Nx Cloud -To use distributed task execution, we need to start agents and set the `NX_CLOUD_DISTRIBUTED_EXECUTION` flag to `true`. - -Read more about the [Distributed CI setup with Nx Cloud](/recipes/ci/ci-setup#distributed-ci-with-nx-cloud). +Read more about [Distributed Task Execution (DTE)](/core-features/distribute-task-execution). ```groovy pipeline { @@ -75,7 +73,7 @@ pipeline { sh "npm ci" sh "npx nx-cloud start-ci-run --stop-agents-after='build'" sh "npx nx format:check" - sh "npx nx affected --base=HEAD~1 --target=lint --parallel=3 & npx nx affected --base=HEAD~1 --target=test --parallel=3 --configuration=ci & npx nx affected --base=HEAD~1 --target=build --parallel=3" + sh "npx nx affected --base=HEAD~1 -t lint --parallel=3 & npx nx affected --base=HEAD~1 -t test --parallel=3 --configuration=ci & npx nx affected --base=HEAD~1 -t build --parallel=3" } } stage('PR') { @@ -87,7 +85,7 @@ pipeline { sh "npm ci" sh "npx nx-cloud start-ci-run --stop-agents-after='build'" sh "npx nx format:check" - sh "npx nx affected --base origin/${env.CHANGE_TARGET} --target=lint --parallel=3 & npx nx affected --base origin/${env.CHANGE_TARGET} --target=test --parallel=3 --configuration=ci & npx nx affected --base origin/${env.CHANGE_TARGET} --target=build --parallel=3" + sh "npx nx affected --base origin/${env.CHANGE_TARGET} -t lint --parallel=3 & npx nx affected --base origin/${env.CHANGE_TARGET} -t test --parallel=3 --configuration=ci & npx nx affected --base origin/${env.CHANGE_TARGET} -t build --parallel=3" } } From aee6ece6f86edd3b39de9fd4ec7fe7a86a480391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Jona=C5=A1?= Date: Fri, 24 Mar 2023 20:03:50 +0100 Subject: [PATCH 52/76] chore(repo): disable gh workflows for forks (#15887) --- .github/workflows/do-not-merge.yml | 5 +++-- .github/workflows/issue-notifier.yml | 3 ++- .github/workflows/lock-threads.yml | 1 + .github/workflows/publish.yml | 1 + .github/workflows/schedule-stale.yml | 4 ++++ 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/do-not-merge.yml b/.github/workflows/do-not-merge.yml index 07aed4e3b5769..ca55ef8599ed1 100644 --- a/.github/workflows/do-not-merge.yml +++ b/.github/workflows/do-not-merge.yml @@ -6,6 +6,7 @@ on: jobs: do-not-merge: + if: ${{ github.repository_owner == 'nrwl' }} name: Prevent Merging runs-on: ubuntu-latest steps: @@ -14,7 +15,7 @@ jobs: echo "${{ toJSON(github.event.*.labels.*.name) }}" node -e 'const forbidden = ["target: next major version", "PR Status: needs tests", "PR Status: in-progress", "blocked: needs rebase"]; const match = ${{ toJSON(github.event.*.labels.*.name) }}.find(l => forbidden.includes(l)); - if (match) { + if (match) { console.log("Cannot merge PRs that are labeled with " + match); - process.exit(1) + process.exit(1) }' diff --git a/.github/workflows/issue-notifier.yml b/.github/workflows/issue-notifier.yml index 01aae7b27e1d4..676a505411bed 100644 --- a/.github/workflows/issue-notifier.yml +++ b/.github/workflows/issue-notifier.yml @@ -11,6 +11,7 @@ permissions: jobs: issues-report: + if: ${{ github.repository_owner == 'nrwl' }} runs-on: ubuntu-latest name: Report status steps: @@ -45,7 +46,7 @@ jobs: - name: Collect Issue Data id: collect run: npx ts-node ./scripts/issues-scraper/index.ts - env: + env: GITHUB_TOKEN: ${{ github.token }} - name: Send GitHub Action trigger data to Slack workflow diff --git a/.github/workflows/lock-threads.yml b/.github/workflows/lock-threads.yml index 140480a2b9f25..770b4cba63f0f 100644 --- a/.github/workflows/lock-threads.yml +++ b/.github/workflows/lock-threads.yml @@ -14,6 +14,7 @@ concurrency: jobs: action: + if: ${{ github.repository_owner == 'nrwl' }} runs-on: ubuntu-latest steps: - uses: dessant/lock-threads@v4 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e049331bd0328..5b1cbc6abdb1e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -193,6 +193,7 @@ jobs: # path: packages/*/*.node # if-no-files-found: error publish: + if: ${{ github.repository_owner == 'nrwl' }} name: Publish runs-on: ubuntu-latest needs: diff --git a/.github/workflows/schedule-stale.yml b/.github/workflows/schedule-stale.yml index af780fbe4d601..8af7f25932e7d 100644 --- a/.github/workflows/schedule-stale.yml +++ b/.github/workflows/schedule-stale.yml @@ -1,10 +1,14 @@ on: schedule: - cron: "0 0 * * *" + name: Stale Bot workflow + permissions: {} + jobs: build: + if: ${{ github.repository_owner == 'nrwl' }} permissions: issues: write # to close stale issues (actions/stale) pull-requests: write # to close stale PRs (actions/stale) From 18c2d80fe0e5b3494914ec04b4a0c0b6d8298d72 Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Fri, 24 Mar 2023 19:13:15 +0000 Subject: [PATCH 53/76] docs(angular): add serve target usage #15756 (#15877) --- .../3-task-running.md | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/docs/shared/angular-standalone-tutorial/3-task-running.md b/docs/shared/angular-standalone-tutorial/3-task-running.md index cde42ccd6af47..5b83b98fc5077 100644 --- a/docs/shared/angular-standalone-tutorial/3-task-running.md +++ b/docs/shared/angular-standalone-tutorial/3-task-running.md @@ -62,8 +62,11 @@ You can see that three targets are defined here: `build`, `test` and `lint`. The properties inside each of these these targets is defined as follows: - `executor` - which Nx executor to run. The syntax here is: `:` -- `outputs` - this is an array of files that would be created by running this target. (This informs Nx on what to save for it's caching mechanisms you'll learn about in [4 - Task Pipelines](/angular-standalone-tutorial/4-task-pipelines)). -- `options` - this is an object defining which executor options to use for the given target. Every Nx executor allows for options as a way to parameterize it's functionality. +- `outputs` - this is an array of files that would be created by running this target. (This informs Nx on what to save + for it's caching mechanisms you'll learn about in [4 - Task Pipelines](/angular-standalone-tutorial/4-task-pipelines)) + . +- `options` - this is an object defining which executor options to use for the given target. Every Nx executor allows + for options as a way to parameterize it's functionality. ## Running Tasks @@ -107,6 +110,35 @@ All files pass linting. > NX Successfully ran target lint for project shared-ui (2s) ``` +Also, by running the `serve` target for your `store` application, you can run local web server during development that +will allow you to manually check the changes you've made: + +```{% command="npx nx serve store" path="~/store" %} + +> nx run store:serve:development + +✔ Browser application bundle generation complete. + +Initial Chunk Files | Names | Raw Size +vendor.js | vendor | 2.04 MB | +polyfills.js | polyfills | 316.06 kB | +styles.css, styles.js | styles | 211.31 kB | +main.js | main | 47.60 kB | +runtime.js | runtime | 12.61 kB | + + | Initial Total | 2.62 MB + +Lazy Chunk Files | Names | Raw Size +cart_src_index_ts.js | store-cart | 4.93 kB | + +Build at: 2023-03-24T10:13:24.014Z - Hash: e52a884fc7a311e1 - Time: 8609ms + +** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ ** + + +✔ Compiled successfully. +``` + ## What's Next - Continue to [4: Workspace Optimization](/angular-standalone-tutorial/4-task-pipelines) From 78fb3b9a4fcdd534c14f6ea185fd8907a1248594 Mon Sep 17 00:00:00 2001 From: Caleb Ukle Date: Fri, 24 Mar 2023 14:53:47 -0700 Subject: [PATCH 54/76] feat(testing): add cypress-e2e-configuration generator (#15736) --- docs/generated/manifests/menus.json | 8 + docs/generated/manifests/packages.json | 9 + docs/generated/packages-metadata.json | 9 + .../generators/cypress-e2e-configuration.json | 95 +++++ .../docs/cypress-e2e-config-examples.md | 129 ++++++ packages/cypress/generators.json | 12 + packages/cypress/plugins/cypress-preset.ts | 19 +- .../src/executors/cypress/cypress.impl.ts | 12 +- .../convert-tslint-to-eslint.ts | 25 +- .../cypress-e2e-configuration.spec.ts.snap | 58 +++ .../cypress-e2e-configuration.spec.ts | 375 ++++++++++++++++++ .../cypress-e2e-configuration.ts | 224 +++++++++++ .../files/v10/__dir__/e2e/app.cy.ts__tmpl__ | 13 + .../v10/__dir__/fixtures/example.json__tmpl__ | 4 + .../v10/__dir__/support/app.po.ts__tmpl__ | 1 + .../v10/__dir__/support/commands.ts__tmpl__ | 33 ++ .../files/v10/__dir__/support/e2e.ts__tmpl__ | 17 + .../files/v10/cypress.config.ts__tmpl__ | 9 + .../files/v10/tsconfig__tsFileExt__ | 10 + .../v9/__dir__/fixtures/example.json__tmpl__ | 4 + .../__dir__/integration/app.spec.ts__tmpl__ | 13 + .../files/v9/__dir__/plugins/index.js | 22 + .../v9/__dir__/support/app.po.ts__tmpl__ | 1 + .../v9/__dir__/support/commands.ts__tmpl__ | 33 ++ .../files/v9/__dir__/support/index.ts__tmpl__ | 17 + .../files/v9/cypress.json | 12 + .../files/v9/tsconfig__tsFileExt__ | 10 + .../cypress-e2e-configuration/schema.json | 91 +++++ .../cypress-project/cypress-project.ts | 118 +----- packages/cypress/src/utils/add-linter.ts | 132 ++++++ 30 files changed, 1393 insertions(+), 122 deletions(-) create mode 100644 docs/generated/packages/cypress/generators/cypress-e2e-configuration.json create mode 100644 packages/cypress/docs/cypress-e2e-config-examples.md create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/__snapshots__/cypress-e2e-configuration.spec.ts.snap create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/cypress-e2e-configuration.spec.ts create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/cypress-e2e-configuration.ts create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/e2e/app.cy.ts__tmpl__ create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/fixtures/example.json__tmpl__ create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/support/app.po.ts__tmpl__ create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/support/commands.ts__tmpl__ create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/support/e2e.ts__tmpl__ create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/files/v10/cypress.config.ts__tmpl__ create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/files/v10/tsconfig__tsFileExt__ create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/fixtures/example.json__tmpl__ create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/integration/app.spec.ts__tmpl__ create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/plugins/index.js create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/support/app.po.ts__tmpl__ create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/support/commands.ts__tmpl__ create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/support/index.ts__tmpl__ create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/files/v9/cypress.json create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/files/v9/tsconfig__tsFileExt__ create mode 100644 packages/cypress/src/generators/cypress-e2e-configuration/schema.json create mode 100644 packages/cypress/src/utils/add-linter.ts diff --git a/docs/generated/manifests/menus.json b/docs/generated/manifests/menus.json index d40c6c527ea51..fa98bfc940261 100644 --- a/docs/generated/manifests/menus.json +++ b/docs/generated/manifests/menus.json @@ -3999,6 +3999,14 @@ "isExternal": false, "disableCollapsible": false }, + { + "id": "cypress-e2e-configuration", + "path": "/packages/cypress/generators/cypress-e2e-configuration", + "name": "cypress-e2e-configuration", + "children": [], + "isExternal": false, + "disableCollapsible": false + }, { "id": "cypress-component-project", "path": "/packages/cypress/generators/cypress-component-project", diff --git a/docs/generated/manifests/packages.json b/docs/generated/manifests/packages.json index 4a32dbdc955a0..a96e1f5d1a962 100644 --- a/docs/generated/manifests/packages.json +++ b/docs/generated/manifests/packages.json @@ -498,6 +498,15 @@ "path": "/packages/cypress/generators/cypress-project", "type": "generator" }, + "/packages/cypress/generators/cypress-e2e-configuration": { + "description": "Add a Cypress E2E Configuration to an existing project.", + "file": "generated/packages/cypress/generators/cypress-e2e-configuration.json", + "hidden": false, + "name": "cypress-e2e-configuration", + "originalFilePath": "/packages/cypress/src/generators/cypress-e2e-configuration/schema.json", + "path": "/packages/cypress/generators/cypress-e2e-configuration", + "type": "generator" + }, "/packages/cypress/generators/cypress-component-project": { "description": "Set up Cypress Component Test for a project", "file": "generated/packages/cypress/generators/cypress-component-project.json", diff --git a/docs/generated/packages-metadata.json b/docs/generated/packages-metadata.json index 89a282adffef0..24b1b57c488aa 100644 --- a/docs/generated/packages-metadata.json +++ b/docs/generated/packages-metadata.json @@ -488,6 +488,15 @@ "path": "cypress/generators/cypress-project", "type": "generator" }, + { + "description": "Add a Cypress E2E Configuration to an existing project.", + "file": "generated/packages/cypress/generators/cypress-e2e-configuration.json", + "hidden": false, + "name": "cypress-e2e-configuration", + "originalFilePath": "/packages/cypress/src/generators/cypress-e2e-configuration/schema.json", + "path": "cypress/generators/cypress-e2e-configuration", + "type": "generator" + }, { "description": "Set up Cypress Component Test for a project", "file": "generated/packages/cypress/generators/cypress-component-project.json", diff --git a/docs/generated/packages/cypress/generators/cypress-e2e-configuration.json b/docs/generated/packages/cypress/generators/cypress-e2e-configuration.json new file mode 100644 index 0000000000000..2cefaf1da7b71 --- /dev/null +++ b/docs/generated/packages/cypress/generators/cypress-e2e-configuration.json @@ -0,0 +1,95 @@ +{ + "name": "cypress-e2e-configuration", + "aliases": ["e2e", "e2e-config"], + "factory": "./src/generators/cypress-e2e-configuration/cypress-e2e-configuration", + "schema": { + "$schema": "http://json-schema.org/schema", + "$id": "NxCypressE2EConfigGenerator", + "cli": "nx", + "title": "Add a Cypress Configuration.", + "description": "Add a Cypress configuration to an existing project.", + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "The project to add a Cypress configuration to", + "$default": { "$source": "projectName" }, + "x-priority": "important", + "x-prompt": "What project do you want to add Cypress to?" + }, + "devServerTarget": { + "type": "string", + "description": "A devServerTarget,':[:], that will be used to run tests against. This is usually the app this project will be used in. Pass --baseUrl if you wish to not use a devServerTarget.", + "x-priority": "important" + }, + "port": { + "oneOf": [ + { "type": "string", "enum": ["cypress-auto"] }, + { "type": "number" } + ], + "description": "Set the 'port' option on the e2e target. It's recommend to set a different port so you can run tests e2e targets in parallel. Most dev servers support using '0' to automatically find a free port. The value 'cypress-auto' can be used if the underlying dev server does not support automatically finding a free port.", + "x-priority": "important" + }, + "baseUrl": { + "type": "string", + "description": "The address (with the port) which your application is running on. If you wish to start your application when running the e2e target, then use --devServerTarget instead." + }, + "directory": { + "type": "string", + "description": "A directory where the project is placed relative from the project root", + "x-priority": "important", + "default": "cypress" + }, + "linter": { + "description": "The tool to use for running lint checks.", + "type": "string", + "enum": ["eslint", "none"], + "default": "eslint" + }, + "js": { + "description": "Generate JavaScript files rather than TypeScript files.", + "type": "boolean", + "default": false + }, + "skipFormat": { + "description": "Skip formatting files.", + "type": "boolean", + "default": false, + "x-priority": "internal" + }, + "setParserOptionsProject": { + "type": "boolean", + "description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.", + "default": false + }, + "skipPackageJson": { + "type": "boolean", + "default": false, + "description": "Do not add dependencies to `package.json`.", + "x-priority": "internal" + }, + "rootProject": { + "description": "Create a application at the root of the workspace", + "type": "boolean", + "default": false, + "hidden": true, + "x-priority": "internal" + }, + "bundler": { + "description": "The Cypress bundler to use.", + "type": "string", + "enum": ["vite", "webpack", "none"], + "x-prompt": "Which Cypress bundler do you want to use?", + "default": "webpack" + } + }, + "required": ["project"], + "examplesFile": "This is a generator to add a cypress e2e configuration to an existing project.\n\n```bash\nnx g cypress-e2e-configuration --project=my-cool-project --devServerTarget=some-app:serve\n```\n\nRunning this generator, adds the required files to run cypress tests for a project,\nMainly a `cypress.config.ts` file and default files in the `/cypress/` directory.\nTests will be located in `/cypress/e2e/*` by default.\n\nYou can customize the directory used via the `--directory` flag, the value is relative to the project root.\n\nFor example if you wanted to place the files inside an `e2e` folder\n\n```bash\nnx g cypress-e2e-configuration --project=my-cool-project --devServerTarget=some-app:serve --directory=e2e\n```\n\nProviding a `--devServerTarget` is optional if you provide a `--baseUrl` or the project you're adding the configuration to has a `serve` target already.\nOtherwise, a `--devServerTarget` is recommend for the `@nrwl/cypress:cypress` executor to spin up the dev server for you automatically when needed.\n\n## Feature Based Testing\n\nThis generator helps in creating feature based e2e/integration testing setups where you can place the feature tests in the same project as the feature library.\nThis differs from creating a separate e2e project that contained all tests for an application which can easily cause more tests to run than is strictly necessary.\n\nTake the following workspace where the `feature-cart` project is affected.\n\n{% graph height=\"450px\" %}\n\n```json\n{\n \"projects\": [\n {\n \"type\": \"app\",\n \"name\": \"fancy-app\",\n \"data\": {\n \"tags\": []\n }\n },\n {\n \"type\": \"app\",\n \"name\": \"fancy-app-e2e\",\n \"data\": {\n \"tags\": []\n }\n },\n {\n \"type\": \"lib\",\n \"name\": \"feature-user\",\n \"data\": {\n \"tags\": []\n }\n },\n {\n \"type\": \"lib\",\n \"name\": \"feature-dashboard\",\n \"data\": {\n \"tags\": []\n }\n },\n {\n \"type\": \"lib\",\n \"name\": \"feature-cart\",\n \"data\": {\n \"tags\": []\n }\n }\n ],\n \"groupByFolder\": false,\n \"workspaceLayout\": {\n \"appsDir\": \"apps\",\n \"libsDir\": \"libs\"\n },\n \"dependencies\": {\n \"fancy-app\": [\n {\n \"target\": \"feature-user\",\n \"source\": \"fancy-app\",\n \"type\": \"static\"\n },\n {\n \"target\": \"feature-cart\",\n \"source\": \"fancy-app\",\n \"type\": \"static\"\n }\n ],\n \"fancy-app-e2e\": [\n {\n \"target\": \"fancy-app\",\n \"source\": \"fancy-app-e2e\",\n \"type\": \"implicit\"\n }\n ],\n \"feature-user\": [\n {\n \"target\": \"feature-dashboard\",\n \"source\": \"feature-user\",\n \"type\": \"direct\"\n }\n ],\n \"feature-cart\": [],\n \"feature-dashboard\": []\n },\n \"affectedProjectIds\": [\"feature-cart\", \"fancy-app\", \"fancy-app-e2e\"]\n}\n```\n\n{% /graph %}\n\nIn this case, if tests for the all the `feature-*` projects where contained in the `fancy-app-e2e` project, then all of those features will be tested in the app, when only the `feature-cart` tests need to run.\n\nRunning these e2e/integration tests more often than they should results in longer CI times.\n\nBrining those e2e test closer to each feature can result is lowering CI times since we don't need to test those features if they did not change.\n\nBuilding this way leaves the `fancy-app-e2e` for mostly smoke type testing instead of more in-depth feature testing.\n\nUsing the `cypress-e2e-configuration` generator can help you accomplish this in your workspace.\n\n```bash\nnx g cypress-e2e-configuration --project=feature-cart --devServerTarget=fancy-app:serve\nnx g cypress-e2e-configuration --project=feature-user --devServerTarget=fancy-app:serve\nnx g cypress-e2e-configuration --project=feature-dashboard --devServerTarget=fancy-app:serve\n```\n\nEach project will now get their own `e2e` target, where the feature project is only concerned with itself. This allows for more focused tests without worrying about forcing unrelated tests to also run.\n\nIt's important to remember that these feature tests are still going to be leveraging the same app to run the tests against so if you plan to run in parallel, you can leverage using a file server and the ability for `@nrwl/cypress:cypress` executor to pass through a port or find a free port to allow running tests in parallel. Read more [about the --port flag](/packages/cypress/executors/cypress#port)\n", + "presets": [] + }, + "description": "Add a Cypress E2E Configuration to an existing project.", + "implementation": "/packages/cypress/src/generators/cypress-e2e-configuration/cypress-e2e-configuration.ts", + "hidden": false, + "path": "/packages/cypress/src/generators/cypress-e2e-configuration/schema.json", + "type": "generator" +} diff --git a/packages/cypress/docs/cypress-e2e-config-examples.md b/packages/cypress/docs/cypress-e2e-config-examples.md new file mode 100644 index 0000000000000..fa90d5f4d58dc --- /dev/null +++ b/packages/cypress/docs/cypress-e2e-config-examples.md @@ -0,0 +1,129 @@ +This is a generator to add a cypress e2e configuration to an existing project. + +```bash +nx g cypress-e2e-configuration --project=my-cool-project --devServerTarget=some-app:serve +``` + +Running this generator, adds the required files to run cypress tests for a project, +Mainly a `cypress.config.ts` file and default files in the `/cypress/` directory. +Tests will be located in `/cypress/e2e/*` by default. + +You can customize the directory used via the `--directory` flag, the value is relative to the project root. + +For example if you wanted to place the files inside an `e2e` folder + +```bash +nx g cypress-e2e-configuration --project=my-cool-project --devServerTarget=some-app:serve --directory=e2e +``` + +Providing a `--devServerTarget` is optional if you provide a `--baseUrl` or the project you're adding the configuration to has a `serve` target already. +Otherwise, a `--devServerTarget` is recommend for the `@nrwl/cypress:cypress` executor to spin up the dev server for you automatically when needed. + +## Feature Based Testing + +This generator helps in creating feature based e2e/integration testing setups where you can place the feature tests in the same project as the feature library. +This differs from creating a separate e2e project that contained all tests for an application which can easily cause more tests to run than is strictly necessary. + +Take the following workspace where the `feature-cart` project is affected. + +{% graph height="450px" %} + +```json +{ + "projects": [ + { + "type": "app", + "name": "fancy-app", + "data": { + "tags": [] + } + }, + { + "type": "app", + "name": "fancy-app-e2e", + "data": { + "tags": [] + } + }, + { + "type": "lib", + "name": "feature-user", + "data": { + "tags": [] + } + }, + { + "type": "lib", + "name": "feature-dashboard", + "data": { + "tags": [] + } + }, + { + "type": "lib", + "name": "feature-cart", + "data": { + "tags": [] + } + } + ], + "groupByFolder": false, + "workspaceLayout": { + "appsDir": "apps", + "libsDir": "libs" + }, + "dependencies": { + "fancy-app": [ + { + "target": "feature-user", + "source": "fancy-app", + "type": "static" + }, + { + "target": "feature-cart", + "source": "fancy-app", + "type": "static" + } + ], + "fancy-app-e2e": [ + { + "target": "fancy-app", + "source": "fancy-app-e2e", + "type": "implicit" + } + ], + "feature-user": [ + { + "target": "feature-dashboard", + "source": "feature-user", + "type": "direct" + } + ], + "feature-cart": [], + "feature-dashboard": [] + }, + "affectedProjectIds": ["feature-cart", "fancy-app", "fancy-app-e2e"] +} +``` + +{% /graph %} + +In this case, if tests for the all the `feature-*` projects where contained in the `fancy-app-e2e` project, then all of those features will be tested in the app, when only the `feature-cart` tests need to run. + +Running these e2e/integration tests more often than they should results in longer CI times. + +Brining those e2e test closer to each feature can result is lowering CI times since we don't need to test those features if they did not change. + +Building this way leaves the `fancy-app-e2e` for mostly smoke type testing instead of more in-depth feature testing. + +Using the `cypress-e2e-configuration` generator can help you accomplish this in your workspace. + +```bash +nx g cypress-e2e-configuration --project=feature-cart --devServerTarget=fancy-app:serve +nx g cypress-e2e-configuration --project=feature-user --devServerTarget=fancy-app:serve +nx g cypress-e2e-configuration --project=feature-dashboard --devServerTarget=fancy-app:serve +``` + +Each project will now get their own `e2e` target, where the feature project is only concerned with itself. This allows for more focused tests without worrying about forcing unrelated tests to also run. + +It's important to remember that these feature tests are still going to be leveraging the same app to run the tests against so if you plan to run in parallel, you can leverage using a file server and the ability for `@nrwl/cypress:cypress` executor to pass through a port or find a free port to allow running tests in parallel. Read more [about the --port flag](/packages/cypress/executors/cypress#port) diff --git a/packages/cypress/generators.json b/packages/cypress/generators.json index e6fb81b48143c..7af9d7578a8a3 100644 --- a/packages/cypress/generators.json +++ b/packages/cypress/generators.json @@ -14,6 +14,12 @@ "schema": "./src/generators/cypress-project/schema.json", "description": "Add a Cypress E2E Project." }, + "cypress-e2e-configuration": { + "aliases": ["e2e", "e2e-config"], + "factory": "./src/generators/cypress-e2e-configuration/cypress-e2e-configuration#compat", + "schema": "./src/generators/cypress-e2e-configuration/schema.json", + "description": "Add a Cypress E2E Configuration to an existing project." + }, "cypress-component-project": { "factory": "./src/generators/cypress-component-project/cypress-component-project#cypressComponentProject", "schema": "./src/generators/cypress-component-project/schema.json", @@ -39,6 +45,12 @@ "description": "Add a Cypress E2E Project.", "hidden": true }, + "cypress-e2e-configuration": { + "aliases": ["e2e", "e2e-config"], + "factory": "./src/generators/cypress-e2e-configuration/cypress-e2e-configuration", + "schema": "./src/generators/cypress-e2e-configuration/schema.json", + "description": "Add a Cypress E2E Configuration to an existing project." + }, "cypress-component-project": { "factory": "./src/generators/cypress-component-project/cypress-component-project#cypressComponentProject", "schema": "./src/generators/cypress-component-project/schema.json", diff --git a/packages/cypress/plugins/cypress-preset.ts b/packages/cypress/plugins/cypress-preset.ts index 2e969f7928095..d38a2296121bc 100644 --- a/packages/cypress/plugins/cypress-preset.ts +++ b/packages/cypress/plugins/cypress-preset.ts @@ -64,14 +64,15 @@ export function nxBaseCypressPreset(pathToConfig: string): BaseCypressPreset { */ export function nxE2EPreset( pathToConfig: string, - options?: { bundler?: string } + options?: NxCypressE2EPresetOptions ) { + const basePath = options?.cypressDir || 'src'; const baseConfig = { ...nxBaseCypressPreset(pathToConfig), fileServerFolder: '.', - supportFile: 'src/support/e2e.ts', - specPattern: 'src/**/*.cy.{js,jsx,ts,tsx}', - fixturesFolder: 'src/fixtures', + supportFile: `${basePath}/support/e2e.ts`, + specPattern: `${basePath}/**/*.cy.{js,jsx,ts,tsx}`, + fixturesFolder: `${basePath}/fixtures`, }; if (options?.bundler === 'vite') { @@ -84,3 +85,13 @@ export function nxE2EPreset( } return baseConfig; } + +export type NxCypressE2EPresetOptions = { + bundler?: string; + /** + * The directory from the project root, where the cypress files (support, fixtures, specs) are located. + * i.e. 'cypress/' or 'src' + * default is 'src' + **/ + cypressDir?: string; +}; diff --git a/packages/cypress/src/executors/cypress/cypress.impl.ts b/packages/cypress/src/executors/cypress/cypress.impl.ts index df721352a3a37..998c35ea52c3a 100644 --- a/packages/cypress/src/executors/cypress/cypress.impl.ts +++ b/packages/cypress/src/executors/cypress/cypress.impl.ts @@ -403,7 +403,17 @@ function getValueFromSchema( target: Target, property: string ): [hasPropertyOpt: boolean, value?: unknown] { - const targetOpts = readTargetOptions(target, context); + let targetOpts: any; + try { + targetOpts = readTargetOptions(target, context); + } catch (e) { + throw new Error(`Unable to read the target options for ${targetToTargetString( + target + )}. +Are you sure this is a valid target? +Was trying to read the target for the property: '${property}', but got the following error: +${e.message || e}`); + } let targetHasOpt = Object.keys(targetOpts).includes(property); if (!targetHasOpt) { diff --git a/packages/cypress/src/generators/convert-tslint-to-eslint/convert-tslint-to-eslint.ts b/packages/cypress/src/generators/convert-tslint-to-eslint/convert-tslint-to-eslint.ts index da0dd512554fc..e8d16b9c9b96b 100755 --- a/packages/cypress/src/generators/convert-tslint-to-eslint/convert-tslint-to-eslint.ts +++ b/packages/cypress/src/generators/convert-tslint-to-eslint/convert-tslint-to-eslint.ts @@ -4,12 +4,13 @@ import { GeneratorCallback, Tree, } from '@nrwl/devkit'; -import { ConvertTSLintToESLintSchema, ProjectConverter } from '@nrwl/linter'; -import type { Linter } from 'eslint'; import { - addLinter, - CypressProjectSchema, -} from '../cypress-project/cypress-project'; + ConvertTSLintToESLintSchema, + Linter, + ProjectConverter, +} from '@nrwl/linter'; +import { addLinterToCyProject } from '../../utils/add-linter'; +import type { Linter as ESLinter } from 'eslint'; export async function conversionGenerator( host: Tree, @@ -30,18 +31,18 @@ export async function conversionGenerator( projectName: options.project, ignoreExistingTslintConfig: options.ignoreExistingTslintConfig, eslintInitializer: async ({ projectName, projectConfig }) => { - await addLinter(host, { - linter: 'eslint', - projectName, - projectRoot: projectConfig.root, + await addLinterToCyProject(host, { + linter: Linter.EsLint, + project: projectName, /** * We set the parserOptions.project config just in case the converted config uses * rules which require type-checking. Later in the conversion we check if it actually * does and remove the config again if it doesn't, so that it is most efficient. */ setParserOptionsProject: true, - skipFormat: options.skipFormat, - } as CypressProjectSchema); + cypressDir: 'src', + overwriteExisting: true, + }); }, }); @@ -115,7 +116,7 @@ export const conversionSchematic = convertNxGenerator(conversionGenerator); * Remove any @angular-eslint rules that were applied as a result of converting prior codelyzer * rules, because they are only relevant for Angular projects. */ -function removeCodelyzerRelatedRules(json: Linter.Config): Linter.Config { +function removeCodelyzerRelatedRules(json: ESLinter.Config): ESLinter.Config { for (const ruleName of Object.keys(json.rules)) { if (ruleName.startsWith('@angular-eslint')) { delete json.rules[ruleName]; diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/__snapshots__/cypress-e2e-configuration.spec.ts.snap b/packages/cypress/src/generators/cypress-e2e-configuration/__snapshots__/cypress-e2e-configuration.spec.ts.snap new file mode 100644 index 0000000000000..243b5fffc78ca --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/__snapshots__/cypress-e2e-configuration.spec.ts.snap @@ -0,0 +1,58 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Cypress e2e configuration v10+ should not override eslint settings if preset 1`] = ` +Object { + "extends": Array [ + "plugin:cypress/recommended", + "../../.eslintrc.json", + ], + "ignorePatterns": Array [ + "!**/*", + ], + "overrides": Array [ + Object { + "extends": Array [ + "plugin:@nrwl/nx/angular", + "plugin:@angular-eslint/template/process-inline-templates", + ], + "files": Array [ + "*.ts", + ], + "rules": Object { + "@angular-eslint/component-selector": Array [ + "error", + Object { + "prefix": "cy-port-test", + "style": "kebab-case", + "type": "element", + }, + ], + "@angular-eslint/directive-selector": Array [ + "error", + Object { + "prefix": "cyPortTest", + "style": "camelCase", + "type": "attribute", + }, + ], + }, + }, + Object { + "extends": Array [ + "plugin:@nrwl/nx/angular-template", + ], + "files": Array [ + "*.html", + ], + "rules": Object {}, + }, + Object { + "files": Array [ + "*.cy.{ts,js,tsx,jsx}", + "cypress/**/*.{ts,js,tsx,jsx}", + ], + "rules": Object {}, + }, + ], +} +`; diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/cypress-e2e-configuration.spec.ts b/packages/cypress/src/generators/cypress-e2e-configuration/cypress-e2e-configuration.spec.ts new file mode 100644 index 0000000000000..0d9e0fc21dd97 --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/cypress-e2e-configuration.spec.ts @@ -0,0 +1,375 @@ +import { + addProjectConfiguration, + ProjectConfiguration, + readJson, + readProjectConfiguration, + Tree, + updateProjectConfiguration, +} from '@nrwl/devkit'; +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import { cypressE2EConfigurationGenerator } from './cypress-e2e-configuration'; + +import { installedCypressVersion } from '../../utils/cypress-version'; + +jest.mock('../../utils/cypress-version'); +describe('Cypress e2e configuration', () => { + let tree: Tree; + let mockedInstalledCypressVersion: jest.Mock< + ReturnType + > = installedCypressVersion as never; + + beforeEach(() => { + tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); + }); + + afterAll(() => { + jest.resetAllMocks(); + }); + + describe('v10+', () => { + beforeAll(() => { + mockedInstalledCypressVersion.mockReturnValue(10); + }); + + it('should add e2e target to existing app', async () => { + addProject(tree, { name: 'my-app', type: 'apps' }); + + await cypressE2EConfigurationGenerator(tree, { + project: 'my-app', + }); + expect(tree.read('apps/my-app/cypress.config.ts', 'utf-8')) + .toMatchInlineSnapshot(` + "import { defineConfig } from 'cypress'; + import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; + + export default defineConfig({ + e2e: nxE2EPreset(__dirname, { + cypressDir: \\"src\\", + + }) + }); + " + `); + expect(readProjectConfiguration(tree, 'my-app').targets.e2e) + .toMatchInlineSnapshot(` + Object { + "configurations": Object { + "production": Object { + "devServerTarget": "my-app:serve:production", + }, + }, + "executor": "@nrwl/cypress:cypress", + "options": Object { + "cypressConfig": "apps/my-app/cypress.config.ts", + "devServerTarget": "my-app:serve", + "testingType": "e2e", + }, + } + `); + + expect(readJson(tree, 'apps/my-app/tsconfig.json')) + .toMatchInlineSnapshot(` + Object { + "compilerOptions": Object { + "allowJs": true, + "outDir": "../../dist/out-tsc", + "sourceMap": false, + "types": Array [ + "cypress", + "node", + ], + }, + "extends": "../../tsconfig.base.json", + "include": Array [ + "src/**/*.ts", + "src/**/*.js", + "cypress.config.ts", + ], + } + `); + assertCypressFiles(tree, 'apps/my-app/src'); + }); + + it('should add e2e target to existing lib', async () => { + addProject(tree, { name: 'my-lib', type: 'libs' }); + addProject(tree, { name: 'my-app', type: 'apps' }); + await cypressE2EConfigurationGenerator(tree, { + project: 'my-lib', + directory: 'cypress', + devServerTarget: 'my-app:serve', + }); + expect(tree.read('libs/my-lib/cypress.config.ts', 'utf-8')) + .toMatchInlineSnapshot(` + "import { defineConfig } from 'cypress'; + import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; + + export default defineConfig({ + e2e: nxE2EPreset(__dirname, { + cypressDir: \\"cypress\\", + + }) + }); + " + `); + assertCypressFiles(tree, 'libs/my-lib/cypress'); + }); + + it('should use --baseUrl', async () => { + addProject(tree, { name: 'my-app', type: 'apps' }); + await cypressE2EConfigurationGenerator(tree, { + project: 'my-app', + baseUrl: 'http://localhost:4200', + }); + assertCypressFiles(tree, 'apps/my-app/src'); + expect( + readProjectConfiguration(tree, 'my-app').targets.e2e.options.baseUrl + ).toEqual('http://localhost:4200'); + }); + + it('should not overwrite existing e2e target', async () => { + addProject(tree, { name: 'my-app', type: 'apps' }); + const pc = readProjectConfiguration(tree, 'my-app'); + pc.targets.e2e = {}; + updateProjectConfiguration(tree, 'my-app', pc); + await expect(async () => { + await cypressE2EConfigurationGenerator(tree, { + project: 'my-app', + }); + }).rejects.toThrowErrorMatchingInlineSnapshot(` + "Project my-app already has an e2e target. + Rename or remove the existing e2e target." + `); + }); + + it('should customize directory name', async () => { + addProject(tree, { name: 'my-app', type: 'apps' }); + tree.write( + 'apps/my-app/tsconfig.json', + JSON.stringify( + { + compilerOptions: { + target: 'es2022', + useDefineForClassFields: false, + forceConsistentCasingInFileNames: true, + strict: true, + noImplicitOverride: true, + noPropertyAccessFromIndexSignature: true, + noImplicitReturns: true, + noFallthroughCasesInSwitch: true, + }, + files: [], + include: [], + references: [ + { + path: './tsconfig.app.json', + }, + { + path: './tsconfig.spec.json', + }, + { + path: './tsconfig.editor.json', + }, + ], + extends: '../../tsconfig.base.json', + angularCompilerOptions: { + enableI18nLegacyMessageIdFormat: false, + strictInjectionParameters: true, + strictInputAccessModifiers: true, + strictTemplates: true, + }, + }, + null, + 2 + ) + ); + await cypressE2EConfigurationGenerator(tree, { + project: 'my-app', + directory: 'e2e/something', + }); + assertCypressFiles(tree, 'apps/my-app/e2e/something'); + expect(readJson(tree, 'apps/my-app/tsconfig.cy.json')) + .toMatchInlineSnapshot(` + Object { + "compilerOptions": Object { + "allowJs": true, + "outDir": "../../dist/out-tsc", + "sourceMap": false, + "types": Array [ + "cypress", + "node", + ], + }, + "extends": "./tsconfig.json", + "include": Array [ + "e2e/something/**/*.ts", + "e2e/something/**/*.js", + "cypress.config.ts", + ], + } + `); + expect(readJson(tree, 'apps/my-app/tsconfig.json').references).toEqual( + expect.arrayContaining([{ path: './tsconfig.cy.json' }]) + ); + }); + + it('should use js instead of ts files with --js', async () => { + addProject(tree, { name: 'my-lib', type: 'libs' }); + await cypressE2EConfigurationGenerator(tree, { + project: 'my-lib', + directory: 'src/e2e', + js: true, + baseUrl: 'http://localhost:4200', + }); + assertCypressFiles(tree, 'libs/my-lib/src/e2e', 'js'); + }); + + it('should not override eslint settings if preset', async () => { + addProject(tree, { name: 'my-lib', type: 'libs' }); + const ngEsLintContents = { + extends: ['../../.eslintrc.json'], + ignorePatterns: ['!**/*'], + overrides: [ + { + files: ['*.ts'], + rules: { + '@angular-eslint/directive-selector': [ + 'error', + { + type: 'attribute', + prefix: 'cyPortTest', + style: 'camelCase', + }, + ], + '@angular-eslint/component-selector': [ + 'error', + { + type: 'element', + prefix: 'cy-port-test', + style: 'kebab-case', + }, + ], + }, + extends: [ + 'plugin:@nrwl/nx/angular', + 'plugin:@angular-eslint/template/process-inline-templates', + ], + }, + { + files: ['*.html'], + extends: ['plugin:@nrwl/nx/angular-template'], + rules: {}, + }, + ], + }; + tree.write( + 'libs/my-lib/.eslintrc.json', + JSON.stringify(ngEsLintContents, null, 2) + ); + + await cypressE2EConfigurationGenerator(tree, { + project: 'my-lib', + directory: 'cypress', + baseUrl: 'http://localhost:4200', + }); + expect(readJson(tree, 'libs/my-lib/.eslintrc.json')).toMatchSnapshot(); + }); + + it('should add serve-static target to CI configuration', async () => { + addProject(tree, { name: 'my-lib', type: 'libs' }); + addProject(tree, { name: 'my-app', type: 'apps' }); + const pc = readProjectConfiguration(tree, 'my-app'); + pc.targets['serve-static'] = { + executor: 'some-file-server', + }; + + updateProjectConfiguration(tree, 'my-lib', pc); + await cypressE2EConfigurationGenerator(tree, { + project: 'my-lib', + devServerTarget: 'my-app:serve', + directory: 'cypress', + }); + assertCypressFiles(tree, 'libs/my-lib/cypress'); + expect( + readProjectConfiguration(tree, 'my-lib').targets['e2e'].configurations + .ci + ).toMatchInlineSnapshot(` + Object { + "devServerTarget": "my-app:serve-static", + } + `); + }); + + it('should set --port', async () => { + addProject(tree, { name: 'my-app', type: 'apps' }); + await cypressE2EConfigurationGenerator(tree, { + project: 'my-app', + port: 0, + }); + + expect(readProjectConfiguration(tree, 'my-app').targets['e2e'].options) + .toMatchInlineSnapshot(` + Object { + "cypressConfig": "apps/my-app/cypress.config.ts", + "devServerTarget": "my-app:serve", + "port": 0, + "testingType": "e2e", + } + `); + }); + + it('should throw if cypress config already exists', async () => { + addProject(tree, { name: 'my-lib', type: 'libs' }); + tree.write('libs/my-lib/cypress.config.ts', 'some content'); + await expect(async () => { + await cypressE2EConfigurationGenerator(tree, { + project: 'my-lib', + baseUrl: 'http://localhost:4200', + }); + }).rejects.toThrowErrorMatchingInlineSnapshot(` + "The project, my-lib, already has a 'cypress.config.ts' file. + This means that Cypress is already setup in this project. + If you want to re-add Cypress to this project, remove the 'libs/my-lib/cypress.config.ts' file and try again." + `); + }); + it('should not overwrite --directory', async () => { + addProject(tree, { name: 'my-lib', type: 'libs' }); + tree.write('libs/my-lib/cypress.config.ts', 'some content'); + tree.write('libs/my-lib/cypress/e2e/app.cy.ts', 'some content'); + await expect(async () => { + await cypressE2EConfigurationGenerator(tree, { + project: 'my-lib', + baseUrl: 'http://localhost:4200', + directory: 'cypress', + }); + }).rejects.toThrowErrorMatchingInlineSnapshot(` + "The project, my-lib, already has a 'cypress' directory. + This most likely means you already have Cypress setup in this project. + Make sure Cypress is not already setup in this project and try again." + `); + }); + }); +}); + +function addProject( + tree: Tree, + opts: { name: string; standalone?: boolean; type: 'apps' | 'libs' } +) { + const config: ProjectConfiguration = { + name: opts.name, + root: `${opts.type}/${opts.name}`, + sourceRoot: `${opts.type}/${opts.name}`, + targets: { + serve: opts.type === 'apps' ? {} : undefined, + }, + }; + + addProjectConfiguration(tree, opts.name, config); +} + +function assertCypressFiles(tree: Tree, basePath: string, ext = 'ts') { + expect(tree.exists(`${basePath}/fixtures/example.json`)).toBeTruthy(); + expect(tree.exists(`${basePath}/support/e2e.${ext}`)).toBeTruthy(); + expect(tree.exists(`${basePath}/support/commands.${ext}`)).toBeTruthy(); + expect(tree.exists(`${basePath}/support/app.po.${ext}`)).toBeTruthy(); + expect(tree.exists(`${basePath}/e2e/app.cy.${ext}`)).toBeTruthy(); +} diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/cypress-e2e-configuration.ts b/packages/cypress/src/generators/cypress-e2e-configuration/cypress-e2e-configuration.ts new file mode 100644 index 0000000000000..4e278251b6317 --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/cypress-e2e-configuration.ts @@ -0,0 +1,224 @@ +import { + addDependenciesToPackageJson, + convertNxGenerator, + formatFiles, + generateFiles, + GeneratorCallback, + joinPathFragments, + offsetFromRoot, + parseTargetString, + readProjectConfiguration, + runTasksInSerial, + toJS, + Tree, + updateJson, + updateProjectConfiguration, +} from '@nrwl/devkit'; +import { getRelativePathToRootTsConfig } from '@nrwl/js'; +import { Linter } from '@nrwl/linter'; +import { join } from 'path'; +import { addLinterToCyProject } from '../../utils/add-linter'; +import { installedCypressVersion } from '../../utils/cypress-version'; +import { viteVersion } from '../../utils/versions'; +import cypressInitGenerator from '../init/init'; + +export interface CypressE2EConfigSchema { + project: string; + baseUrl?: string; + directory?: string; + js?: boolean; + skipFormat?: boolean; + setParserOptionsProject?: boolean; + skipPackageJson?: boolean; + bundler?: 'webpack' | 'vite' | 'none'; + devServerTarget?: string; + linter?: Linter; + port?: number | 'cypress-auto'; +} +type NormalizedSchema = ReturnType; + +export async function cypressE2EConfigurationGenerator( + tree: Tree, + options: CypressE2EConfigSchema +) { + const opts = normalizeOptions(tree, options); + const tasks: GeneratorCallback[] = []; + if (!installedCypressVersion()) { + tasks.push(await cypressInitGenerator(tree, opts)); + } + if (opts.bundler === 'vite') { + tasks.push(addDependenciesToPackageJson(tree, {}, { vite: viteVersion })); + } + addFiles(tree, opts); + addTarget(tree, opts); + addLinterToCyProject(tree, { + ...opts, + cypressDir: opts.directory, + }); + + if (!opts.skipFormat) { + await formatFiles(tree); + } + + return runTasksInSerial(...tasks); +} + +function normalizeOptions(tree: Tree, options: CypressE2EConfigSchema) { + const projectConfig = readProjectConfiguration(tree, options.project); + if (projectConfig?.targets?.e2e) { + throw new Error(`Project ${options.project} already has an e2e target. +Rename or remove the existing e2e target.`); + } + + if ( + !options.baseUrl && + !options.devServerTarget && + !projectConfig.targets.serve + ) { + throw new Error(`The project ${options.project} does not have a 'serve' target. +In this case you need to provide a devServerTarget,':[:]', or a baseUrl option`); + } + + options.directory ??= 'src'; + + if (tree.exists(joinPathFragments(projectConfig.root, options.directory))) { + throw new Error(`The project, ${options.project}, already has a '${options.directory}' directory. +This most likely means you already have Cypress setup in this project. +Make sure Cypress is not already setup in this project and try again.`); + } + + const cyVersion = installedCypressVersion(); + const cyFile = + cyVersion && cyVersion < 10 ? 'cypress.json' : 'cypress.config.ts'; + + if (tree.exists(joinPathFragments(projectConfig.root, cyFile))) { + throw new Error(`The project, ${ + options.project + }, already has a '${cyFile}' file. +This means that Cypress is already setup in this project. +If you want to re-add Cypress to this project, remove the '${joinPathFragments( + projectConfig.root, + cyFile + )}' file and try again.`); + } + + return { + ...options, + bundler: options.bundler ?? 'webpack', + rootProject: projectConfig.root === '.', + linter: options.linter ?? Linter.EsLint, + devServerTarget: + options.devServerTarget ?? + (projectConfig.targets.serve ? `${options.project}:serve` : undefined), + }; +} + +function addFiles(tree: Tree, options: NormalizedSchema) { + const projectConfig = readProjectConfiguration(tree, options.project); + const cyVersion = installedCypressVersion(); + const filesToUse = cyVersion && cyVersion < 10 ? 'v9' : 'v10'; + const hasTsConfig = tree.exists( + joinPathFragments(projectConfig.root, 'tsconfig.json') + ); + + generateFiles( + tree, + join(__dirname, 'files', filesToUse), + projectConfig.root, + { + ...options, + dir: options.directory ?? 'src', + // create a tsconfig.cy.json if there is an existing tsconfig. aka being added to a new project + ext: options.js ? 'js' : 'ts', + offsetFromRoot: offsetFromRoot(projectConfig.root), + tsFileExt: hasTsConfig ? '.cy.json' : '.json', + tsConfigPath: hasTsConfig + ? './tsconfig.json' + : getRelativePathToRootTsConfig(tree, projectConfig.root), + tmpl: '', + } + ); + + if (hasTsConfig) { + updateJson( + tree, + joinPathFragments(projectConfig.root, 'tsconfig.json'), + (json) => { + json.references = json.references || []; + json.references.push({ + path: './tsconfig.cy.json', + }); + return json; + } + ); + } + + if (cyVersion && cyVersion < 7) { + updateJson(tree, join(projectConfig.root, 'cypress.json'), (json) => { + json.pluginsFile = './src/plugins/index'; + return json; + }); + } else if (cyVersion < 10) { + const pluginPath = join(projectConfig.root, 'src/plugins/index.js'); + if (tree.exists(pluginPath)) { + tree.delete(pluginPath); + } + } + + if (options.js) { + toJS(tree); + } +} + +function addTarget(tree: Tree, opts: NormalizedSchema) { + const projectConfig = readProjectConfiguration(tree, opts.project); + const cyVersion = installedCypressVersion(); + projectConfig.targets.e2e = { + executor: '@nrwl/cypress:cypress', + options: { + cypressConfig: joinPathFragments( + projectConfig.root, + cyVersion && cyVersion < 10 ? 'cypress.json' : 'cypress.config.ts' + ), + testingType: 'e2e', + }, + }; + if (opts.baseUrl) { + projectConfig.targets.e2e.options = { + ...projectConfig.targets.e2e.options, + baseUrl: opts.baseUrl, + }; + } else if (opts.devServerTarget) { + const parsedTarget = parseTargetString(opts.devServerTarget); + + projectConfig.targets.e2e.options = { + ...projectConfig.targets.e2e.options, + devServerTarget: opts.devServerTarget, + port: opts.port, + }; + + projectConfig.targets.e2e.configurations = { + [parsedTarget.configuration || 'production']: { + devServerTarget: `${opts.devServerTarget}${ + parsedTarget.configuration ? '' : ':production' + }`, + }, + }; + const devServerProjectConfig = readProjectConfiguration( + tree, + parsedTarget.project + ); + if (devServerProjectConfig.targets?.['serve-static']) { + projectConfig.targets.e2e.configurations.ci = { + devServerTarget: `${parsedTarget.project}:serve-static`, + }; + } + } else { + throw new Error('Either baseUrl or devServerTarget must be provided'); + } + + updateProjectConfiguration(tree, opts.project, projectConfig); +} + +export default cypressE2EConfigurationGenerator; +export const compat = convertNxGenerator(cypressE2EConfigurationGenerator); diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/e2e/app.cy.ts__tmpl__ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/e2e/app.cy.ts__tmpl__ new file mode 100644 index 0000000000000..7b40058fb8499 --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/e2e/app.cy.ts__tmpl__ @@ -0,0 +1,13 @@ +import { getGreeting } from '../support/app.po'; + +describe('<%= project %>', () => { + beforeEach(() => cy.visit('/')); + + it('should display welcome message', () => { + // Custom command example, see `../support/commands.ts` file + cy.login('my-email@something.com', 'myPassword'); + + // Function helper example, see `../support/app.po.ts` file + getGreeting().contains(/Welcome/); + }); +}); diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/fixtures/example.json__tmpl__ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/fixtures/example.json__tmpl__ new file mode 100644 index 0000000000000..294cbed6ce9e0 --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/fixtures/example.json__tmpl__ @@ -0,0 +1,4 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io" +} diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/support/app.po.ts__tmpl__ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/support/app.po.ts__tmpl__ new file mode 100644 index 0000000000000..32934246969c2 --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/support/app.po.ts__tmpl__ @@ -0,0 +1 @@ +export const getGreeting = () => cy.get('h1'); diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/support/commands.ts__tmpl__ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/support/commands.ts__tmpl__ new file mode 100644 index 0000000000000..fc4dc5d3d285d --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/support/commands.ts__tmpl__ @@ -0,0 +1,33 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +<% if (linter === 'eslint') { %> +// eslint-disable-next-line @typescript-eslint/no-namespace<% } %> +declare namespace Cypress {<% if (linter === 'eslint') { %> + // eslint-disable-next-line @typescript-eslint/no-unused-vars<% } %> + interface Chainable { + login(email: string, password: string): void; + } +} +// +// -- This is a parent command -- +Cypress.Commands.add('login', (email, password) => { + console.log('Custom command example: Login', email, password); +}); +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/support/e2e.ts__tmpl__ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/support/e2e.ts__tmpl__ new file mode 100644 index 0000000000000..3d469a6b6cf31 --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/__dir__/support/e2e.ts__tmpl__ @@ -0,0 +1,17 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands'; diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/cypress.config.ts__tmpl__ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/cypress.config.ts__tmpl__ new file mode 100644 index 0000000000000..d33c468d6aa93 --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/cypress.config.ts__tmpl__ @@ -0,0 +1,9 @@ +import { defineConfig } from 'cypress'; +import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; + +export default defineConfig({ + e2e: nxE2EPreset(__dirname, { + cypressDir: "<%= dir %>", + <% if (bundler === 'vite'){ %>bundler: 'vite',<% } %> + }) +}); diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/tsconfig__tsFileExt__ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/tsconfig__tsFileExt__ new file mode 100644 index 0000000000000..9d5c0678662b4 --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v10/tsconfig__tsFileExt__ @@ -0,0 +1,10 @@ +{ + "extends": "<%= tsConfigPath %>", + "compilerOptions": { + "sourceMap": false, + "outDir": "<%= offsetFromRoot %>dist/out-tsc", + "allowJs": true, + "types": ["cypress", "node"] + }, + "include": ["<%= dir %>/**/*.ts", "<%= dir %>/**/*.js", "cypress.config.ts"] +} diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/fixtures/example.json__tmpl__ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/fixtures/example.json__tmpl__ new file mode 100644 index 0000000000000..294cbed6ce9e0 --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/fixtures/example.json__tmpl__ @@ -0,0 +1,4 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io" +} diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/integration/app.spec.ts__tmpl__ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/integration/app.spec.ts__tmpl__ new file mode 100644 index 0000000000000..7b40058fb8499 --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/integration/app.spec.ts__tmpl__ @@ -0,0 +1,13 @@ +import { getGreeting } from '../support/app.po'; + +describe('<%= project %>', () => { + beforeEach(() => cy.visit('/')); + + it('should display welcome message', () => { + // Custom command example, see `../support/commands.ts` file + cy.login('my-email@something.com', 'myPassword'); + + // Function helper example, see `../support/app.po.ts` file + getGreeting().contains(/Welcome/); + }); +}); diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/plugins/index.js b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/plugins/index.js new file mode 100644 index 0000000000000..9067e75a258da --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/plugins/index.js @@ -0,0 +1,22 @@ +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +const { preprocessTypescript } = require('@nrwl/cypress/plugins/preprocessor'); + +module.exports = (on, config) => { + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config + + // Preprocess Typescript file using Nx helper + on('file:preprocessor', preprocessTypescript(config)); +}; diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/support/app.po.ts__tmpl__ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/support/app.po.ts__tmpl__ new file mode 100644 index 0000000000000..32934246969c2 --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/support/app.po.ts__tmpl__ @@ -0,0 +1 @@ +export const getGreeting = () => cy.get('h1'); diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/support/commands.ts__tmpl__ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/support/commands.ts__tmpl__ new file mode 100644 index 0000000000000..fc4dc5d3d285d --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/support/commands.ts__tmpl__ @@ -0,0 +1,33 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +<% if (linter === 'eslint') { %> +// eslint-disable-next-line @typescript-eslint/no-namespace<% } %> +declare namespace Cypress {<% if (linter === 'eslint') { %> + // eslint-disable-next-line @typescript-eslint/no-unused-vars<% } %> + interface Chainable { + login(email: string, password: string): void; + } +} +// +// -- This is a parent command -- +Cypress.Commands.add('login', (email, password) => { + console.log('Custom command example: Login', email, password); +}); +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/support/index.ts__tmpl__ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/support/index.ts__tmpl__ new file mode 100644 index 0000000000000..459189af2288e --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/__dir__/support/index.ts__tmpl__ @@ -0,0 +1,17 @@ +// *********************************************************** +// This example support/index.ts is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.ts using ES2015 syntax: +import './commands'; diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/cypress.json b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/cypress.json new file mode 100644 index 0000000000000..6311ceced6f38 --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/cypress.json @@ -0,0 +1,12 @@ +{ + "fileServerFolder": ".", + "fixturesFolder": "./<%= dir %>/fixtures", + "integrationFolder": "./<%= dir %>/integration", + "modifyObstructiveCode": false, + "supportFile": "./<%= dir %>/support/index.<%= ext %>", + "pluginsFile": false, + "video": true, + "videosFolder": "<%= offsetFromRoot %>dist/cypress/<%= projectRoot %>/videos", + "screenshotsFolder": "<%= offsetFromRoot %>dist/cypress/<%= projectRoot %>/screenshots", + "chromeWebSecurity": false +} diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/tsconfig__tsFileExt__ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/tsconfig__tsFileExt__ new file mode 100644 index 0000000000000..76c2f84970d2e --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/files/v9/tsconfig__tsFileExt__ @@ -0,0 +1,10 @@ +{ + "extends": "<%= tsConfigPath %>", + "compilerOptions": { + "sourceMap": false, + "outDir": "<%= offsetFromRoot %>dist/out-tsc", + "allowJs": true, + "types": ["cypress", "node"] + }, + "include": ["src/**/*.ts", "src/**/*.js"] +} diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/schema.json b/packages/cypress/src/generators/cypress-e2e-configuration/schema.json new file mode 100644 index 0000000000000..120eb500cd203 --- /dev/null +++ b/packages/cypress/src/generators/cypress-e2e-configuration/schema.json @@ -0,0 +1,91 @@ +{ + "$schema": "http://json-schema.org/schema", + "$id": "NxCypressE2EConfigGenerator", + "cli": "nx", + "title": "Add a Cypress Configuration.", + "description": "Add a Cypress configuration to an existing project.", + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "The project to add a Cypress configuration to", + "$default": { + "$source": "projectName" + }, + "x-priority": "important", + "x-prompt": "What project do you want to add Cypress to?" + }, + "devServerTarget": { + "type": "string", + "description": "A devServerTarget,':[:], that will be used to run tests against. This is usually the app this project will be used in. Pass --baseUrl if you wish to not use a devServerTarget.", + "x-priority": "important" + }, + "port": { + "oneOf": [ + { + "type": "string", + "enum": ["cypress-auto"] + }, + { + "type": "number" + } + ], + "description": "Set the 'port' option on the e2e target. It's recommend to set a different port so you can run tests e2e targets in parallel. Most dev servers support using '0' to automatically find a free port. The value 'cypress-auto' can be used if the underlying dev server does not support automatically finding a free port.", + "x-priority": "important" + }, + "baseUrl": { + "type": "string", + "description": "The address (with the port) which your application is running on. If you wish to start your application when running the e2e target, then use --devServerTarget instead." + }, + "directory": { + "type": "string", + "description": "A directory where the project is placed relative from the project root", + "x-priority": "important", + "default": "cypress" + }, + "linter": { + "description": "The tool to use for running lint checks.", + "type": "string", + "enum": ["eslint", "none"], + "default": "eslint" + }, + "js": { + "description": "Generate JavaScript files rather than TypeScript files.", + "type": "boolean", + "default": false + }, + "skipFormat": { + "description": "Skip formatting files.", + "type": "boolean", + "default": false, + "x-priority": "internal" + }, + "setParserOptionsProject": { + "type": "boolean", + "description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.", + "default": false + }, + "skipPackageJson": { + "type": "boolean", + "default": false, + "description": "Do not add dependencies to `package.json`.", + "x-priority": "internal" + }, + "rootProject": { + "description": "Create a application at the root of the workspace", + "type": "boolean", + "default": false, + "hidden": true, + "x-priority": "internal" + }, + "bundler": { + "description": "The Cypress bundler to use.", + "type": "string", + "enum": ["vite", "webpack", "none"], + "x-prompt": "Which Cypress bundler do you want to use?", + "default": "webpack" + } + }, + "required": ["project"], + "examplesFile": "../../../docs/cypress-e2e-config-examples.md" +} diff --git a/packages/cypress/src/generators/cypress-project/cypress-project.ts b/packages/cypress/src/generators/cypress-project/cypress-project.ts index afd73efcdc366..263de32329ff6 100644 --- a/packages/cypress/src/generators/cypress-project/cypress-project.ts +++ b/packages/cypress/src/generators/cypress-project/cypress-project.ts @@ -20,25 +20,18 @@ import { Tree, updateJson, } from '@nrwl/devkit'; -import { Linter, lintProjectGenerator } from '@nrwl/linter'; +import { Linter } from '@nrwl/linter'; import { getRelativePathToRootTsConfig } from '@nrwl/js'; -import { - globalJavaScriptOverrides, - globalTypeScriptOverrides, -} from '@nrwl/linter/src/generators/init/global-eslint-config'; import { join } from 'path'; import { installedCypressVersion } from '../../utils/cypress-version'; import { filePathPrefix } from '../../utils/project-name'; -import { - cypressVersion, - eslintPluginCypressVersion, - viteVersion, -} from '../../utils/versions'; +import { cypressVersion, viteVersion } from '../../utils/versions'; import { cypressInitGenerator } from '../init/init'; // app import { Schema } from './schema'; +import { addLinterToCyProject } from '../../utils/add-linter'; export interface CypressProjectSchema extends Schema { projectName: string; @@ -173,100 +166,9 @@ function addProject(tree: Tree, options: CypressProjectSchema) { addProjectConfiguration(tree, options.projectName, e2eProjectConfig); } -export async function addLinter(host: Tree, options: CypressProjectSchema) { - if (options.linter === Linter.None) { - return () => {}; - } - - const installTask = await lintProjectGenerator(host, { - project: options.projectName, - linter: options.linter, - skipFormat: true, - tsConfigPaths: [joinPathFragments(options.projectRoot, 'tsconfig.json')], - eslintFilePatterns: [ - `${options.projectRoot}/**/*.${options.js ? 'js' : '{js,ts}'}`, - ], - setParserOptionsProject: options.setParserOptionsProject, - skipPackageJson: options.skipPackageJson, - rootProject: options.rootProject, - }); - - if (!options.linter || options.linter !== Linter.EsLint) { - return installTask; - } - - const installTask2 = !options.skipPackageJson - ? addDependenciesToPackageJson( - host, - {}, - { 'eslint-plugin-cypress': eslintPluginCypressVersion } - ) - : () => {}; - - updateJson(host, join(options.projectRoot, '.eslintrc.json'), (json) => { - if (options.rootProject) { - json.plugins = ['@nrwl/nx']; - json.extends = ['plugin:cypress/recommended']; - } else { - json.extends = ['plugin:cypress/recommended', ...json.extends]; - } - json.overrides = [ - ...(options.rootProject - ? [globalTypeScriptOverrides, globalJavaScriptOverrides] - : []), - /** - * In order to ensure maximum efficiency when typescript-eslint generates TypeScript Programs - * behind the scenes during lint runs, we need to make sure the project is configured to use its - * own specific tsconfigs, and not fall back to the ones in the root of the workspace. - */ - { - files: ['*.ts', '*.tsx', '*.js', '*.jsx'], - /** - * NOTE: We no longer set parserOptions.project by default when creating new projects. - * - * We have observed that users rarely add rules requiring type-checking to their Nx workspaces, and therefore - * do not actually need the capabilites which parserOptions.project provides. When specifying parserOptions.project, - * typescript-eslint needs to create full TypeScript Programs for you. When omitting it, it can perform a simple - * parse (and AST tranformation) of the source files it encounters during a lint run, which is much faster and much - * less memory intensive. - * - * In the rare case that users attempt to add rules requiring type-checking to their setup later on (and haven't set - * parserOptions.project), the executor will attempt to look for the particular error typescript-eslint gives you - * and provide feedback to the user. - */ - parserOptions: !options.setParserOptionsProject - ? undefined - : { - project: `${options.projectRoot}/tsconfig.*?.json`, - }, - /** - * Having an empty rules object present makes it more obvious to the user where they would - * extend things from if they needed to - */ - rules: {}, - }, - ]; - - if (installedCypressVersion() < 7) { - /** - * We need this override because we enabled allowJS in the tsconfig to allow for JS based Cypress tests. - * That however leads to issues with the CommonJS Cypress plugin file. - */ - json.overrides.push({ - files: ['src/plugins/index.js'], - rules: { - '@typescript-eslint/no-var-requires': 'off', - 'no-undef': 'off', - }, - }); - } - - return json; - }); - - return runTasksInSerial(installTask, installTask2); -} - +/** + * @deprecated use cypressE2EConfigurationGenerator instead + **/ export async function cypressProjectGenerator(host: Tree, schema: Schema) { const options = normalizeOptions(host, schema); const tasks: GeneratorCallback[] = []; @@ -291,7 +193,13 @@ export async function cypressProjectGenerator(host: Tree, schema: Schema) { createFiles(host, options); addProject(host, options); - const installTask = await addLinter(host, options); + const installTask = await addLinterToCyProject(host, { + ...options, + cypressDir: 'src', + linter: schema.linter, + project: options.projectName, + overwriteExisting: true, + }); tasks.push(installTask); if (!options.skipFormat) { await formatFiles(host); diff --git a/packages/cypress/src/utils/add-linter.ts b/packages/cypress/src/utils/add-linter.ts new file mode 100644 index 0000000000000..15d9be6d217ae --- /dev/null +++ b/packages/cypress/src/utils/add-linter.ts @@ -0,0 +1,132 @@ +import { + addDependenciesToPackageJson, + GeneratorCallback, + joinPathFragments, + readProjectConfiguration, + runTasksInSerial, + Tree, + updateJson, +} from '@nrwl/devkit'; +import { Linter, lintProjectGenerator } from '@nrwl/linter'; +import { globalJavaScriptOverrides } from '@nrwl/linter/src/generators/init/global-eslint-config'; +import { installedCypressVersion } from './cypress-version'; +import { eslintPluginCypressVersion } from './versions'; + +export interface CyLinterOptions { + project: string; + linter: Linter; + setParserOptionsProject?: boolean; + skipPackageJson?: boolean; + rootProject?: boolean; + js?: boolean; + /** + * Directory from the project root, where the cypress files will be located. + * typically src/ or cypress/ + **/ + cypressDir: string; + /** + * overwrite existing eslint rules for the cypress defaults + * This is useful when adding linting to a brand new project vs an existing one + **/ + overwriteExisting?: boolean; +} + +export async function addLinterToCyProject( + tree: Tree, + options: CyLinterOptions +) { + if (options.linter === Linter.None) { + return () => {}; + } + + const tasks: GeneratorCallback[] = []; + const projectConfig = readProjectConfiguration(tree, options.project); + + if (!tree.exists(joinPathFragments(projectConfig.root, '.eslintrc.json'))) { + tasks.push( + await lintProjectGenerator(tree, { + project: options.project, + linter: options.linter, + skipFormat: true, + tsConfigPaths: [joinPathFragments(projectConfig.root, 'tsconfig.json')], + eslintFilePatterns: [ + `${projectConfig.root}/**/*.${options.js ? 'js' : '{js,ts}'}`, + ], + setParserOptionsProject: options.setParserOptionsProject, + skipPackageJson: options.skipPackageJson, + rootProject: options.rootProject, + }) + ); + } + + if (!options.linter || options.linter !== Linter.EsLint) { + return runTasksInSerial(...tasks); + } + + tasks.push( + !options.skipPackageJson + ? addDependenciesToPackageJson( + tree, + {}, + { 'eslint-plugin-cypress': eslintPluginCypressVersion } + ) + : () => {} + ); + + updateJson( + tree, + joinPathFragments(projectConfig.root, '.eslintrc.json'), + (json) => { + if (options.rootProject) { + json.plugins = ['@nrwl/nx']; + json.extends = ['plugin:cypress/recommended']; + } else { + json.extends = ['plugin:cypress/recommended', ...json.extends]; + } + json.overrides ??= []; + const globals = options.rootProject ? [globalJavaScriptOverrides] : []; + const override = { + files: ['*.ts', '*.tsx', '*.js', '*.jsx'], + parserOptions: !options.setParserOptionsProject + ? undefined + : { + project: `${projectConfig.root}/tsconfig.*?.json`, + }, + rules: {}, + }; + const cyFiles = [ + { + ...override, + files: [ + '*.cy.{ts,js,tsx,jsx}', + `${options.cypressDir}/**/*.{ts,js,tsx,jsx}`, + ], + }, + ]; + if (options.overwriteExisting) { + json.overrides = [...globals, override]; + } else { + json.overrides.push(...globals); + json.overrides.push(...cyFiles); + } + + const cyVersion = installedCypressVersion(); + if (cyVersion && cyVersion < 7) { + /** + * We need this override because we enabled allowJS in the tsconfig to allow for JS based Cypress tests. + * That however leads to issues with the CommonJS Cypress plugin file. + */ + json.overrides.push({ + files: [`${options.cypressDir}/plugins/index.js`], + rules: { + '@typescript-eslint/no-var-requires': 'off', + 'no-undef': 'off', + }, + }); + } + return json; + } + ); + + return runTasksInSerial(...tasks); +} From 597832e88f57046c277ff7acac8344eac434727d Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Mon, 27 Mar 2023 09:42:53 +0100 Subject: [PATCH 55/76] fix(angular): use @nrwl/web:file-server (#15849) --- docs/generated/manifests/menus.json | 8 - docs/generated/manifests/packages.json | 9 - docs/generated/packages-metadata.json | 9 - .../packages/web/executors/file-server.json | 5 + nx-dev/nx-dev-e2e/src/e2e/packages.cy.ts | 4 - packages/angular/executors.json | 10 - packages/angular/migrations.json | 6 + .../src/executors/file-server/compat.ts | 5 - .../executors/file-server/file-server.impl.ts | 221 ------------------ .../src/executors/file-server/schema.d.ts | 16 -- .../src/executors/file-server/schema.json | 80 ------- .../setup-mf/lib/setup-serve-target.ts | 2 +- .../src/generators/setup-mf/setup-mf.ts | 15 +- .../update-file-server-executor.spec.ts | 67 ++++++ .../update-file-server-executor.ts | 56 +++++ .../executors/file-server/file-server.impl.ts | 7 +- .../web/src/executors/file-server/schema.d.ts | 1 + .../web/src/executors/file-server/schema.json | 5 + 18 files changed, 161 insertions(+), 365 deletions(-) delete mode 100644 packages/angular/src/executors/file-server/compat.ts delete mode 100644 packages/angular/src/executors/file-server/file-server.impl.ts delete mode 100644 packages/angular/src/executors/file-server/schema.d.ts delete mode 100644 packages/angular/src/executors/file-server/schema.json create mode 100644 packages/angular/src/migrations/update-15-9-0/update-file-server-executor.spec.ts create mode 100644 packages/angular/src/migrations/update-15-9-0/update-file-server-executor.ts diff --git a/docs/generated/manifests/menus.json b/docs/generated/manifests/menus.json index fa98bfc940261..e033a70c743b4 100644 --- a/docs/generated/manifests/menus.json +++ b/docs/generated/manifests/menus.json @@ -3602,14 +3602,6 @@ "isExternal": false, "disableCollapsible": false }, - { - "id": "file-server", - "path": "/packages/angular/executors/file-server", - "name": "file-server", - "children": [], - "isExternal": false, - "disableCollapsible": false - }, { "id": "webpack-browser", "path": "/packages/angular/executors/webpack-browser", diff --git a/docs/generated/manifests/packages.json b/docs/generated/manifests/packages.json index a96e1f5d1a962..64b37593bc7f4 100644 --- a/docs/generated/manifests/packages.json +++ b/docs/generated/manifests/packages.json @@ -70,15 +70,6 @@ "path": "/packages/angular/executors/package", "type": "executor" }, - "/packages/angular/executors/file-server": { - "description": "Serves a static web application from a folder.", - "file": "generated/packages/angular/executors/file-server.json", - "hidden": false, - "name": "file-server", - "originalFilePath": "/packages/angular/src/executors/file-server/schema.json", - "path": "/packages/angular/executors/file-server", - "type": "executor" - }, "/packages/angular/executors/webpack-browser": { "description": "The `webpack-browser` executor is very similar to the standard `browser` builder provided by the Angular Devkit. It allows you to build your Angular application to a build artifact that can be hosted online. There are some key differences: \n- Supports Custom Webpack Configurations \n- Supports Incremental Building", "file": "generated/packages/angular/executors/webpack-browser.json", diff --git a/docs/generated/packages-metadata.json b/docs/generated/packages-metadata.json index 24b1b57c488aa..b23b2fd5f872c 100644 --- a/docs/generated/packages-metadata.json +++ b/docs/generated/packages-metadata.json @@ -64,15 +64,6 @@ "path": "angular/executors/package", "type": "executor" }, - { - "description": "Serves a static web application from a folder.", - "file": "generated/packages/angular/executors/file-server.json", - "hidden": false, - "name": "file-server", - "originalFilePath": "/packages/angular/src/executors/file-server/schema.json", - "path": "angular/executors/file-server", - "type": "executor" - }, { "description": "The `webpack-browser` executor is very similar to the standard `browser` builder provided by the Angular Devkit. It allows you to build your Angular application to a build artifact that can be hosted online. There are some key differences: \n- Supports Custom Webpack Configurations \n- Supports Incremental Building", "file": "generated/packages/angular/executors/webpack-browser.json", diff --git a/docs/generated/packages/web/executors/file-server.json b/docs/generated/packages/web/executors/file-server.json index 1481eec902b5f..ecb1a5e1f80fb 100644 --- a/docs/generated/packages/web/executors/file-server.json +++ b/docs/generated/packages/web/executors/file-server.json @@ -70,6 +70,11 @@ "staticFilePath": { "type": "string", "description": "Path where the build artifacts are located. If not provided then it will be infered from the buildTarget executor options as outputPath" + }, + "cors": { + "type": "boolean", + "description": "Enable CORS", + "default": true } }, "additionalProperties": false, diff --git a/nx-dev/nx-dev-e2e/src/e2e/packages.cy.ts b/nx-dev/nx-dev-e2e/src/e2e/packages.cy.ts index 30bb82cf9a33b..35d9306a809b1 100644 --- a/nx-dev/nx-dev-e2e/src/e2e/packages.cy.ts +++ b/nx-dev/nx-dev-e2e/src/e2e/packages.cy.ts @@ -96,10 +96,6 @@ describe('nx-dev: Packages Section', () => { title: '@nrwl/angular:package', path: '/packages/angular/executors/package', }, - { - title: '@nrwl/angular:file-server', - path: '/packages/angular/executors/file-server', - }, { title: 'cra-to-nx', path: '/packages/cra-to-nx' }, { title: 'create-nx-plugin', path: '/packages/create-nx-plugin' }, { diff --git a/packages/angular/executors.json b/packages/angular/executors.json index 984b24f41cc5b..99dcde902c9bb 100644 --- a/packages/angular/executors.json +++ b/packages/angular/executors.json @@ -14,11 +14,6 @@ "implementation": "./src/executors/package/package.impl", "schema": "./src/executors/package/schema.json", "description": "Builds and packages an Angular library producing an output following the Angular Package Format (APF) to be distributed as an NPM package.\nThis executor is similar to the `@angular-devkit/build-angular:ng-packagr` with additional support for incremental builds." - }, - "file-server": { - "implementation": "./src/executors/file-server/file-server.impl", - "schema": "./src/executors/file-server/schema.json", - "description": "Serves a static web application from a folder." } }, "builders": { @@ -61,11 +56,6 @@ "implementation": "./src/builders/module-federation-dev-ssr/module-federation-dev-ssr.impl", "schema": "./src/builders/module-federation-dev-ssr/schema.json", "description": "The module-federation-dev-ssr executor is reserved exclusively for use with host Module Federation applications that use SSR. It allows the user to specify which remote applications should be served with the host." - }, - "file-server": { - "implementation": "./src/executors/file-server/compat", - "schema": "./src/executors/file-server/schema.json", - "description": "Serve a web application from a folder." } } } diff --git a/packages/angular/migrations.json b/packages/angular/migrations.json index 6b20adf59580a..7c7de99331197 100644 --- a/packages/angular/migrations.json +++ b/packages/angular/migrations.json @@ -158,6 +158,12 @@ "version": "15.9.0-beta.3", "description": "Update the tsconfig.spec.json to use target es2016 for jest-preset-angular v13", "factory": "./src/migrations/update-15-9-0/update-testing-tsconfig" + }, + "update-file-server-executor": { + "cli": "nx", + "version": "15.9.0-beta.9", + "description": "Update the file-server executor to use @nrwl/web:file-server", + "factory": "./src/migrations/update-15-9-0/update-file-server-executor" } }, "packageJsonUpdates": { diff --git a/packages/angular/src/executors/file-server/compat.ts b/packages/angular/src/executors/file-server/compat.ts deleted file mode 100644 index 094efc4231523..0000000000000 --- a/packages/angular/src/executors/file-server/compat.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { convertNxExecutor } from '@nrwl/devkit'; - -import fileServerExecutor from './file-server.impl'; - -export default convertNxExecutor(fileServerExecutor); diff --git a/packages/angular/src/executors/file-server/file-server.impl.ts b/packages/angular/src/executors/file-server/file-server.impl.ts deleted file mode 100644 index 1ae84a4293703..0000000000000 --- a/packages/angular/src/executors/file-server/file-server.impl.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { - ExecutorContext, - joinPathFragments, - workspaceLayout, -} from '@nrwl/devkit'; -import { execFileSync, fork } from 'child_process'; -import { watch } from 'chokidar'; -import { copyFileSync, readFileSync, unlinkSync } from 'fs'; -import ignore from 'ignore'; -import { readModulePackageJson } from 'nx/src/utils/package-json'; -import { platform } from 'os'; -import { join, resolve } from 'path'; -import { Schema } from './schema'; - -// platform specific command name -const pmCmd = platform() === 'win32' ? `npx.cmd` : 'npx'; - -function getHttpServerArgs(options: Schema) { - const args = ['-c-1']; - - if (options.cors) { - args.push(`--cors`); - } - - if (options.port) { - args.push(`-p=${options.port}`); - } - if (options.host) { - args.push(`-a=${options.host}`); - } - if (options.ssl) { - args.push(`-S`); - } - if (options.sslCert) { - args.push(`-C=${options.sslCert}`); - } - if (options.sslKey) { - args.push(`-K=${options.sslKey}`); - } - if (options.proxyUrl) { - args.push(`-P=${options.proxyUrl}`); - } - - if (options.proxyOptions) { - Object.keys(options.proxyOptions).forEach((key) => { - args.push(`--proxy-options.${key}=options.proxyOptions[key]`); - }); - } - - return args; -} - -function getBuildTargetCommand(options: Schema) { - const cmd = ['nx', 'run', options.buildTarget]; - if (options.parallel) { - cmd.push(`--parallel`); - } - if (options.maxParallel) { - cmd.push(`--maxParallel=${options.maxParallel}`); - } - return cmd; -} - -function getBuildTargetOutputPath(options: Schema, context: ExecutorContext) { - let buildOptions; - try { - const [project, target, config] = options.buildTarget.split(':'); - - const buildTarget = - context.projectsConfigurations.projects[project].targets[target]; - buildOptions = config - ? { ...buildTarget.options, ...buildTarget.configurations[config] } - : buildTarget.options; - } catch (e) { - throw new Error(`Invalid buildTarget: ${options.buildTarget}`); - } - - // TODO: vsavkin we should also check outputs - const outputPath = buildOptions.outputPath; - if (!outputPath) { - throw new Error( - `Invalid buildTarget: ${options.buildTarget}. The target must contain outputPath property.` - ); - } - - return outputPath; -} - -function getIgnoredGlobs(root: string) { - const ig = ignore(); - try { - ig.add(readFileSync(`${root}/.gitignore`, 'utf-8')); - } catch {} - try { - ig.add(readFileSync(`${root}/.nxignore`, 'utf-8')); - } catch {} - return ig; -} - -function createFileWatcher( - root: string, - changeHandler: () => void -): () => void { - const ignoredGlobs = getIgnoredGlobs(root); - const layout = workspaceLayout(); - - const watcher = watch( - [ - joinPathFragments(layout.appsDir, '**'), - joinPathFragments(layout.libsDir, '**'), - ], - { - cwd: root, - ignoreInitial: true, - } - ); - watcher.on('all', (_event: string, path: string) => { - if (ignoredGlobs.ignores(path)) return; - changeHandler(); - }); - return () => watcher.close(); -} - -export default async function* fileServerExecutor( - options: Schema, - context: ExecutorContext -) { - let running = false; - - const run = () => { - if (!running) { - running = true; - try { - const args = getBuildTargetCommand(options); - execFileSync(pmCmd, args, { - stdio: [0, 1, 2], - }); - } catch { - throw new Error( - "Project failed to build. Check the build's error output for more information." - ); - } - running = false; - } - }; - - let disposeWatch: () => void; - if (options.watch) { - disposeWatch = createFileWatcher(context.root, run); - } - - // perform initial run - run(); - - const outputPath = getBuildTargetOutputPath(options, context); - - if (options.spa) { - const src = join(outputPath, 'index.html'); - const dst = join(outputPath, '404.html'); - - // See: https://github.com/http-party/http-server#magic-files - copyFileSync(src, dst); - } - - const args = getHttpServerArgs(options); - - const { path: pathToHttpServerPkgJson, packageJson } = - readModulePackageJson('http-server'); - const pathToHttpServerBin = packageJson.bin['http-server']; - const pathToHttpServer = resolve( - pathToHttpServerPkgJson.replace('package.json', ''), - pathToHttpServerBin - ); - - const serve = fork(pathToHttpServer, [outputPath, ...args], { - stdio: 'pipe', - cwd: context.root, - env: { - FORCE_COLOR: 'true', - ...process.env, - }, - }); - - const processExitListener = () => { - serve.kill(); - if (disposeWatch) { - disposeWatch(); - } - - if (options.spa) { - unlinkSync(join(outputPath, '404.html')); - } - }; - process.on('exit', processExitListener); - process.on('SIGTERM', processExitListener); - serve.stdout.on('data', (chunk) => { - if (chunk.toString().indexOf('GET') === -1) { - process.stdout.write(chunk); - } - }); - serve.stderr.on('data', (chunk) => { - process.stderr.write(chunk); - }); - - yield { - success: true, - baseUrl: `${options.ssl ? 'https' : 'http'}://${options.host}:${ - options.port - }`, - }; - - return new Promise<{ success: boolean }>((res) => { - serve.on('exit', (code) => { - if (code == 0) { - res({ success: true }); - } else { - res({ success: false }); - } - }); - }); -} diff --git a/packages/angular/src/executors/file-server/schema.d.ts b/packages/angular/src/executors/file-server/schema.d.ts deleted file mode 100644 index 424335c8e8102..0000000000000 --- a/packages/angular/src/executors/file-server/schema.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -export interface Schema { - host: string; - port: number; - ssl: boolean; - sslKey?: string; - sslCert?: string; - proxyUrl?: string; - buildTarget: string; - parallel: boolean; - maxParallel?: number; - withDeps: boolean; - proxyOptions?: object; - watch?: boolean; - spa?: boolean; - cors?: boolean; -} diff --git a/packages/angular/src/executors/file-server/schema.json b/packages/angular/src/executors/file-server/schema.json deleted file mode 100644 index 89e0230d400a8..0000000000000 --- a/packages/angular/src/executors/file-server/schema.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "version": 2, - "outputCapture": "direct-nodejs", - "title": "File Server", - "description": "Serves a static web application from a folder.", - "type": "object", - "cli": "nx", - "properties": { - "buildTarget": { - "type": "string", - "description": "Target which builds the application." - }, - "parallel": { - "type": "boolean", - "description": "Build the target in parallel.", - "default": true - }, - "maxParallel": { - "type": "number", - "description": "Max number of parallel jobs." - }, - "port": { - "type": "number", - "description": "Port to listen on.", - "default": 4200 - }, - "host": { - "type": "string", - "description": "Host to listen on.", - "default": "localhost" - }, - "ssl": { - "type": "boolean", - "description": "Serve using `HTTPS`.", - "default": false - }, - "sslKey": { - "type": "string", - "description": "SSL key to use for serving `HTTPS`." - }, - "sslCert": { - "type": "string", - "description": "SSL certificate to use for serving `HTTPS`." - }, - "proxyUrl": { - "type": "string", - "description": "URL to proxy unhandled requests to." - }, - "proxyOptions": { - "type": "object", - "description": "Options for the proxy used by `http-server`.", - "default": {}, - "properties": { - "secure": { - "type": "boolean", - "default": false - } - }, - "additionalProperties": true - }, - "watch": { - "type": "boolean", - "description": "Watch for file changes.", - "default": false - }, - "spa": { - "type": "boolean", - "description": "Redirect 404 errors to index.html (useful for SPA's).", - "default": false - }, - "cors": { - "type": "boolean", - "description": "Enable CORS", - "default": true - } - }, - "additionalProperties": false, - "required": ["buildTarget"], - "examplesFile": "../../../docs/file-server-examples.md" -} diff --git a/packages/angular/src/generators/setup-mf/lib/setup-serve-target.ts b/packages/angular/src/generators/setup-mf/lib/setup-serve-target.ts index f60a5dcdd26cc..0a2c650fb6c90 100644 --- a/packages/angular/src/generators/setup-mf/lib/setup-serve-target.ts +++ b/packages/angular/src/generators/setup-mf/lib/setup-serve-target.ts @@ -23,7 +23,7 @@ export function setupServeTarget(host: Tree, options: Schema) { if (options.mfType === 'remote') { appConfig.targets['serve-static'] = { - executor: '@nrwl/angular:file-server', + executor: '@nrwl/web:file-server', defaultConfiguration: 'development', options: { buildTarget: `${options.appName}:build`, diff --git a/packages/angular/src/generators/setup-mf/setup-mf.ts b/packages/angular/src/generators/setup-mf/setup-mf.ts index 96fb80422a4ec..9266005641539 100644 --- a/packages/angular/src/generators/setup-mf/setup-mf.ts +++ b/packages/angular/src/generators/setup-mf/setup-mf.ts @@ -1,5 +1,9 @@ import type { Tree } from '@nrwl/devkit'; -import { formatFiles, readProjectConfiguration } from '@nrwl/devkit'; +import { + addDependenciesToPackageJson, + formatFiles, + readProjectConfiguration, +} from '@nrwl/devkit'; import type { Schema } from './schema'; import { @@ -17,6 +21,7 @@ import { updateTsConfigTarget, } from './lib'; import { getInstalledAngularVersionInfo } from '../utils/version-utils'; +import { nxVersion } from '../../utils/versions'; import { lt } from 'semver'; export async function setupMf(tree: Tree, options: Schema) { @@ -36,10 +41,16 @@ export async function setupMf(tree: Tree, options: Schema) { updateHostAppRoutes(tree, options); } + let installTask = () => {}; if (options.mfType === 'remote') { addRemoteToHost(tree, options); addRemoteEntry(tree, options, projectConfig.root); removeDeadCodeFromRemote(tree, options); + installTask = addDependenciesToPackageJson( + tree, + {}, + { '@nrwl/web': nxVersion } + ); } const remotesWithPorts = getRemotesWithPorts(tree, options); @@ -60,6 +71,8 @@ export async function setupMf(tree: Tree, options: Schema) { if (!options.skipFormat) { await formatFiles(tree); } + + return installTask; } export default setupMf; diff --git a/packages/angular/src/migrations/update-15-9-0/update-file-server-executor.spec.ts b/packages/angular/src/migrations/update-15-9-0/update-file-server-executor.spec.ts new file mode 100644 index 0000000000000..27d7bff6a645e --- /dev/null +++ b/packages/angular/src/migrations/update-15-9-0/update-file-server-executor.spec.ts @@ -0,0 +1,67 @@ +import { + addProjectConfiguration, + readJson, + readNxJson, + readProjectConfiguration, + updateNxJson, +} from '@nrwl/devkit'; +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import updateFileServerExecutor from './update-file-server-executor'; + +describe('updateFileServerExecutor', () => { + it('it should change the executor correctly', async () => { + // ARRANGE + const tree = createTreeWithEmptyWorkspace(); + + const project = { + name: 'test', + root: '/', + sourceRoot: '/src', + targets: { + serve: { + executor: '@nrwl/angular:file-server', + options: {}, + }, + }, + }; + + addProjectConfiguration(tree, 'test', project); + + // ACT + await updateFileServerExecutor(tree); + + // AWAIT + const updatedProject = readProjectConfiguration(tree, 'test'); + expect(updatedProject.targets.serve.executor).toEqual( + '@nrwl/web:file-server' + ); + expect( + readJson(tree, 'package.json').devDependencies['@nrwl/web'] + ).toBeTruthy(); + }); + + it('it should change the executor correctly in nx.json', async () => { + // ARRANGE + const tree = createTreeWithEmptyWorkspace(); + + const nxJson = readNxJson(tree); + updateNxJson(tree, { + ...nxJson, + targetDefaults: { + ...(nxJson.targetDefaults ?? {}), + serve: { + executor: '@nrwl/angular:file-server', + }, + }, + }); + + // ACT + await updateFileServerExecutor(tree); + + // AWAIT + const updatedNxJson = readNxJson(tree); + expect(updatedNxJson.targetDefaults.serve.executor).toEqual( + '@nrwl/web:file-server' + ); + }); +}); diff --git a/packages/angular/src/migrations/update-15-9-0/update-file-server-executor.ts b/packages/angular/src/migrations/update-15-9-0/update-file-server-executor.ts new file mode 100644 index 0000000000000..eb683eed7cb05 --- /dev/null +++ b/packages/angular/src/migrations/update-15-9-0/update-file-server-executor.ts @@ -0,0 +1,56 @@ +import type { Tree } from '@nrwl/devkit'; +import { + addDependenciesToPackageJson, + formatFiles, + getProjects, + readNxJson, + updateNxJson, + updateProjectConfiguration, +} from '@nrwl/devkit'; +import { nxVersion } from '../../utils/versions'; + +export default async function updateFileServerExecutor(tree: Tree) { + const oldExecutor = '@nrwl/angular:file-server'; + const newExecutor = '@nrwl/web:file-server'; + let needsNrwlWeb = false; + + const nxJson = readNxJson(tree); + if (nxJson.targetDefaults) { + let nxJsonChanged = false; + + for (const [targetName, target] of Object.entries(nxJson.targetDefaults)) { + if (target.executor === oldExecutor) { + nxJson.targetDefaults[targetName].executor = newExecutor; + nxJsonChanged = true; + needsNrwlWeb = true; + } + } + + if (nxJsonChanged) { + updateNxJson(tree, nxJson); + } + } + + const projects = getProjects(tree); + for (const [projectName, project] of projects.entries()) { + let projectChanged = false; + + for (const [targetName, target] of Object.entries(project.targets)) { + if (target.executor === oldExecutor) { + project.targets[targetName].executor = newExecutor; + projectChanged = true; + needsNrwlWeb = true; + } + } + + if (projectChanged) { + updateProjectConfiguration(tree, projectName, project); + } + } + + if (needsNrwlWeb) { + addDependenciesToPackageJson(tree, {}, { '@nrwl/web': nxVersion }); + } + + await formatFiles(tree); +} diff --git a/packages/web/src/executors/file-server/file-server.impl.ts b/packages/web/src/executors/file-server/file-server.impl.ts index 7cec109ced4ff..49aef06c2c40d 100644 --- a/packages/web/src/executors/file-server/file-server.impl.ts +++ b/packages/web/src/executors/file-server/file-server.impl.ts @@ -19,7 +19,12 @@ import { readModulePackageJson } from 'nx/src/utils/package-json'; const pmCmd = platform() === 'win32' ? `npx.cmd` : 'npx'; function getHttpServerArgs(options: Schema) { - const args = ['-c-1', '--cors']; + const args = ['-c-1']; + + if (options.cors) { + args.push(`--cors`); + } + if (options.port) { args.push(`-p=${options.port}`); } diff --git a/packages/web/src/executors/file-server/schema.d.ts b/packages/web/src/executors/file-server/schema.d.ts index 6cb580a663c61..dc383296765e4 100644 --- a/packages/web/src/executors/file-server/schema.d.ts +++ b/packages/web/src/executors/file-server/schema.d.ts @@ -13,4 +13,5 @@ export interface Schema { watch?: boolean; spa: boolean; staticFilePath?: string; + cors?: boolean; } diff --git a/packages/web/src/executors/file-server/schema.json b/packages/web/src/executors/file-server/schema.json index 8a9919395763f..2a049e2fd0a16 100644 --- a/packages/web/src/executors/file-server/schema.json +++ b/packages/web/src/executors/file-server/schema.json @@ -72,6 +72,11 @@ "staticFilePath": { "type": "string", "description": "Path where the build artifacts are located. If not provided then it will be infered from the buildTarget executor options as outputPath" + }, + "cors": { + "type": "boolean", + "description": "Enable CORS", + "default": true } }, "additionalProperties": false, From eec6041220e74aebf307c2ff65e21018f9d2ca2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Jona=C5=A1?= Date: Mon, 27 Mar 2023 15:22:39 +0200 Subject: [PATCH 56/76] fix(repo): add missing git config on nightly for lerna e2e (#15900) --- .github/workflows/e2e-matrix.yml | 5 +++++ .github/workflows/e2e-windows.yml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/e2e-matrix.yml b/.github/workflows/e2e-matrix.yml index 816a34d456a67..71227f10cac82 100644 --- a/.github/workflows/e2e-matrix.yml +++ b/.github/workflows/e2e-matrix.yml @@ -230,6 +230,11 @@ jobs: HOMEBREW_NO_AUTO_UPDATE=1 brew install applesimutils >/dev/null xcrun simctl shutdown all && xcrun simctl erase all + - name: Configure git metadata (needed for lerna smoke tests) + run: | + git config --global user.email test@test.com + git config --global user.name "Test Test" + - name: Run e2e tests id: e2e-run run: yarn nx run-many --target=e2e --projects="${{ join(matrix.project) }}" --parallel=1 diff --git a/.github/workflows/e2e-windows.yml b/.github/workflows/e2e-windows.yml index db34abf6e1b0b..0c6e38f66e895 100644 --- a/.github/workflows/e2e-windows.yml +++ b/.github/workflows/e2e-windows.yml @@ -155,6 +155,11 @@ jobs: if: steps.cache-modules.outputs.cache-hit != 'true' run: yarn install --prefer-offline --frozen-lockfile --non-interactive + - name: Configure git metadata (needed for lerna smoke tests) + run: | + git config --global user.email test@test.com + git config --global user.name "Test Test" + - name: Run e2e tests id: e2e-run run: yarn nx run-many --target=e2e --projects="${{ join(matrix.project) }}" --parallel=1 From b94b669035bc370660868dc609a0b8dd7b602ada Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Mon, 27 Mar 2023 17:44:10 +0300 Subject: [PATCH 57/76] fix(storybook): proper user prompts for config generator (#15860) --- .../storybook/generators/configuration.json | 2 - packages/storybook/package.json | 1 + .../configuration-nested.spec.ts | 17 +++- .../configuration/configuration-v7.spec.ts | 22 +---- .../configuration/configuration.spec.ts | 61 +++++++++++++ .../generators/configuration/configuration.ts | 20 +++-- .../configuration/lib/user-prompts.ts | 85 +++++++++++++++++++ .../configuration/{ => lib}/util-functions.ts | 10 +-- .../src/generators/configuration/schema.d.ts | 2 +- .../src/generators/configuration/schema.json | 2 - .../migrate-defaults-5-to-6.ts | 2 +- 11 files changed, 189 insertions(+), 35 deletions(-) create mode 100644 packages/storybook/src/generators/configuration/lib/user-prompts.ts rename packages/storybook/src/generators/configuration/{ => lib}/util-functions.ts (97%) diff --git a/docs/generated/packages/storybook/generators/configuration.json b/docs/generated/packages/storybook/generators/configuration.json index 62e1dd54992bf..7d5680fbb1154 100644 --- a/docs/generated/packages/storybook/generators/configuration.json +++ b/docs/generated/packages/storybook/generators/configuration.json @@ -31,7 +31,6 @@ "@storybook/vue3", "@storybook/svelte" ], - "x-prompt": "What UI framework plugin should storybook use?", "x-priority": "important" }, "configureCypress": { @@ -80,7 +79,6 @@ "description": "The Storybook builder to use.", "type": "string", "enum": ["vite", "webpack"], - "x-prompt": "Which Storybook builder do you want to use?", "default": "webpack", "x-priority": "important" }, diff --git a/packages/storybook/package.json b/packages/storybook/package.json index 48a44cdb6f893..8a1afd470fb33 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -36,6 +36,7 @@ "@nrwl/js": "file:../js", "@nrwl/workspace": "file:../workspace", "dotenv": "~10.0.0", + "enquirer": "~2.3.6", "@phenomnomnominal/tsquery": "4.1.1", "semver": "7.3.4" }, diff --git a/packages/storybook/src/generators/configuration/configuration-nested.spec.ts b/packages/storybook/src/generators/configuration/configuration-nested.spec.ts index 25d3541b7a781..2dcd6037dfdd4 100644 --- a/packages/storybook/src/generators/configuration/configuration-nested.spec.ts +++ b/packages/storybook/src/generators/configuration/configuration-nested.spec.ts @@ -6,6 +6,7 @@ import { writeJson, } from '@nrwl/devkit'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import * as enquirer from 'enquirer'; import configurationGenerator from './configuration'; import * as workspaceConfiguration from './test-configs/root-workspace-configuration.json'; @@ -18,10 +19,23 @@ jest.mock('nx/src/project-graph/project-graph', () => ({ .mockImplementation(async () => ({ nodes: {}, dependencies: {} })), })); +jest.mock('enquirer'); +// @ts-ignore +enquirer.prompt = jest.fn(); describe('@nrwl/storybook:configuration for workspaces with Root project', () => { + beforeAll(() => { + process.env.NX_INTERACTIVE = 'true'; + }); + afterAll(() => { + // cleanup + delete process.env.NX_INTERACTIVE; + }); describe('basic functionalities', () => { let tree: Tree; - + // @ts-ignore + enquirer.prompt = jest + .fn() + .mockReturnValue(Promise.resolve({ bundler: 'webpack' })); beforeEach(async () => { tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); updateJson(tree, 'nx.json', (json) => { @@ -83,6 +97,7 @@ describe('@nrwl/storybook:configuration for workspaces with Root project', () => uiFramework: '@storybook/react', }); + expect(enquirer.prompt).toHaveBeenCalled(); expect(tree.exists('.storybook/main.js')).toBeTruthy(); expect(tree.exists('.storybook/tsconfig.json')).toBeTruthy(); expect(tree.exists('.storybook/preview.js')).toBeTruthy(); diff --git a/packages/storybook/src/generators/configuration/configuration-v7.spec.ts b/packages/storybook/src/generators/configuration/configuration-v7.spec.ts index abfdbd054c8fa..4dd50ad6c6864 100644 --- a/packages/storybook/src/generators/configuration/configuration-v7.spec.ts +++ b/packages/storybook/src/generators/configuration/configuration-v7.spec.ts @@ -10,6 +10,7 @@ import { writeJson, } from '@nrwl/devkit'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import * as enquirer from 'enquirer'; import { Linter } from '@nrwl/linter'; import { libraryGenerator } from '@nrwl/workspace/generators'; @@ -25,6 +26,9 @@ jest.mock('nx/src/project-graph/project-graph', () => ({ .fn() .mockImplementation(async () => ({ nodes: {}, dependencies: {} })), })); +jest.mock('enquirer'); +// @ts-ignore +enquirer.prompt = jest.fn(); describe('@nrwl/storybook:configuration for Storybook v7', () => { describe('basic functionalities', () => { @@ -53,7 +57,6 @@ describe('@nrwl/storybook:configuration for Storybook v7', () => { it('should generate TypeScript Configuration files', async () => { await configurationGenerator(tree, { name: 'test-ui-lib', - uiFramework: '@storybook/react', // it's wrong on purpose to verify that it's being ignored standaloneConfig: false, tsConfiguration: true, storybook7Configuration: true, @@ -77,7 +80,6 @@ describe('@nrwl/storybook:configuration for Storybook v7', () => { it('should update `tsconfig.lib.json` file', async () => { await configurationGenerator(tree, { name: 'test-ui-lib', - uiFramework: '@storybook/angular', // it's wrong on purpose to verify that it's being ignored standaloneConfig: false, storybook7Configuration: true, storybook7UiFramework: '@storybook/react-webpack5', @@ -96,7 +98,6 @@ describe('@nrwl/storybook:configuration for Storybook v7', () => { it('should update `tsconfig.json` file', async () => { await configurationGenerator(tree, { name: 'test-ui-lib', - uiFramework: '@storybook/react', standaloneConfig: false, storybook7Configuration: true, storybook7UiFramework: '@storybook/react-webpack5', @@ -137,7 +138,6 @@ describe('@nrwl/storybook:configuration for Storybook v7', () => { await configurationGenerator(tree, { name: 'test-ui-lib2', - uiFramework: '@storybook/angular', // it's wrong on purpose to verify that it's being ignored standaloneConfig: false, storybook7Configuration: true, storybook7UiFramework: '@storybook/react-webpack5', @@ -162,7 +162,6 @@ describe('@nrwl/storybook:configuration for Storybook v7', () => { await configurationGenerator(tree, { name: 'test-ui-lib2', - uiFramework: '@storybook/react', standaloneConfig: false, storybook7Configuration: true, storybook7UiFramework: '@storybook/react-webpack5', @@ -176,7 +175,6 @@ describe('@nrwl/storybook:configuration for Storybook v7', () => { it('should generate TS config for project if tsConfiguration true', async () => { await configurationGenerator(tree, { name: 'test-ui-lib', - uiFramework: '@storybook/angular', standaloneConfig: false, tsConfiguration: true, storybook7Configuration: true, @@ -196,7 +194,6 @@ describe('@nrwl/storybook:configuration for Storybook v7', () => { it('should add test-storybook target', async () => { await configurationGenerator(tree, { name: 'test-ui-lib', - uiFramework: '@storybook/react', configureTestRunner: true, storybook7Configuration: true, storybook7UiFramework: '@storybook/react-webpack5', @@ -254,74 +251,63 @@ describe('@nrwl/storybook:configuration for Storybook v7', () => { await configurationGenerator(tree, { name: 'reapp', tsConfiguration: false, - uiFramework: '@storybook/react', storybook7Configuration: true, storybook7UiFramework: '@storybook/react-vite', }); await configurationGenerator(tree, { name: 'main-vite', tsConfiguration: false, - uiFramework: '@storybook/react', storybook7Configuration: true, storybook7UiFramework: '@storybook/react-vite', }); await configurationGenerator(tree, { name: 'main-vite-ts', tsConfiguration: true, - uiFramework: '@storybook/react', storybook7Configuration: true, storybook7UiFramework: '@storybook/react-vite', }); await configurationGenerator(tree, { name: 'main-webpack', - uiFramework: '@storybook/react', storybook7Configuration: true, storybook7UiFramework: '@storybook/react-webpack5', }); await configurationGenerator(tree, { name: 'reappw', - uiFramework: '@storybook/react', storybook7Configuration: true, storybook7UiFramework: '@storybook/react-webpack5', }); await configurationGenerator(tree, { name: 'react-rollup', - uiFramework: '@storybook/react', storybook7Configuration: true, storybook7UiFramework: '@storybook/react-webpack5', }); await configurationGenerator(tree, { name: 'react-vite', - uiFramework: '@storybook/react', storybook7Configuration: true, storybook7UiFramework: '@storybook/react-vite', }); await configurationGenerator(tree, { name: 'nextapp', - uiFramework: '@storybook/react', storybook7Configuration: true, storybook7UiFramework: '@storybook/nextjs', }); await configurationGenerator(tree, { name: 'react-swc', - uiFramework: '@storybook/react', storybook7Configuration: true, storybook7UiFramework: '@storybook/react-webpack5', }); await configurationGenerator(tree, { name: 'wv1', - uiFramework: '@storybook/react', storybook7Configuration: true, storybook7UiFramework: '@storybook/web-components-vite', }); await configurationGenerator(tree, { name: 'ww1', - uiFramework: '@storybook/react', storybook7Configuration: true, storybook7UiFramework: '@storybook/web-components-webpack5', }); diff --git a/packages/storybook/src/generators/configuration/configuration.spec.ts b/packages/storybook/src/generators/configuration/configuration.spec.ts index cb5ad298af525..685c2b82bd22b 100644 --- a/packages/storybook/src/generators/configuration/configuration.spec.ts +++ b/packages/storybook/src/generators/configuration/configuration.spec.ts @@ -8,6 +8,7 @@ import { writeJson, } from '@nrwl/devkit'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import * as enquirer from 'enquirer'; import { Linter } from '@nrwl/linter'; import { libraryGenerator } from '@nrwl/workspace/generators'; @@ -23,8 +24,18 @@ jest.mock('nx/src/project-graph/project-graph', () => ({ .fn() .mockImplementation(async () => ({ nodes: {}, dependencies: {} })), })); +jest.mock('enquirer'); +// @ts-ignore +enquirer.prompt = jest.fn(); describe('@nrwl/storybook:configuration', () => { + beforeAll(() => { + process.env.NX_INTERACTIVE = 'true'; + }); + afterAll(() => { + // cleanup + delete process.env.NX_INTERACTIVE; + }); describe('basic functionalities', () => { let tree: Tree; @@ -110,12 +121,18 @@ describe('@nrwl/storybook:configuration', () => { }); it('should update workspace file for react libs', async () => { + // @ts-ignore + enquirer.prompt = jest + .fn() + .mockReturnValue(Promise.resolve({ bundler: 'vite' })); + await configurationGenerator(tree, { name: 'test-ui-lib', uiFramework: '@storybook/react', }); const project = readProjectConfiguration(tree, 'test-ui-lib'); + expect(enquirer.prompt).toHaveBeenCalled(); expect(project.targets.storybook).toEqual({ executor: '@nrwl/storybook:storybook', configurations: { @@ -212,6 +229,11 @@ describe('@nrwl/storybook:configuration', () => { }); it('should update `tsconfig.lib.json` file', async () => { + // @ts-ignore + enquirer.prompt = jest + .fn() + .mockReturnValue(Promise.resolve({ bundler: 'vite' })); + await configurationGenerator(tree, { name: 'test-ui-lib', uiFramework: '@storybook/react', @@ -221,6 +243,7 @@ describe('@nrwl/storybook:configuration', () => { 'libs/test-ui-lib/tsconfig.lib.json' ) as Required; + expect(enquirer.prompt).toHaveBeenCalled(); expect(tsconfigJson.exclude).toContain('**/*.stories.ts'); expect(tsconfigJson.exclude).toContain('**/*.stories.js'); expect(tsconfigJson.exclude).toContain('**/*.stories.jsx'); @@ -228,6 +251,11 @@ describe('@nrwl/storybook:configuration', () => { }); it('should update `tsconfig.json` file', async () => { + // @ts-ignore + enquirer.prompt = jest + .fn() + .mockReturnValue(Promise.resolve({ bundler: 'vite' })); + await configurationGenerator(tree, { name: 'test-ui-lib', uiFramework: '@storybook/react', @@ -237,6 +265,7 @@ describe('@nrwl/storybook:configuration', () => { 'libs/test-ui-lib/tsconfig.json' ); + expect(enquirer.prompt).toHaveBeenCalled(); expect(tsconfigJson.references).toMatchInlineSnapshot(` Array [ Object { @@ -253,6 +282,11 @@ describe('@nrwl/storybook:configuration', () => { }); it("should update the project's .eslintrc.json if config exists", async () => { + // @ts-ignore + enquirer.prompt = jest + .fn() + .mockReturnValue(Promise.resolve({ bundler: 'vite' })); + await libraryGenerator(tree, { name: 'test-ui-lib2', linter: Linter.EsLint, @@ -270,6 +304,7 @@ describe('@nrwl/storybook:configuration', () => { uiFramework: '@storybook/react', }); + expect(enquirer.prompt).toHaveBeenCalled(); expect(readJson(tree, 'libs/test-ui-lib2/.eslintrc.json').parserOptions) .toMatchInlineSnapshot(` Object { @@ -281,6 +316,11 @@ describe('@nrwl/storybook:configuration', () => { }); it('should have the proper typings', async () => { + // @ts-ignore + enquirer.prompt = jest + .fn() + .mockReturnValue(Promise.resolve({ bundler: 'vite' })); + await libraryGenerator(tree, { name: 'test-ui-lib2', linter: Linter.EsLint, @@ -291,6 +331,7 @@ describe('@nrwl/storybook:configuration', () => { uiFramework: '@storybook/react', }); + expect(enquirer.prompt).toHaveBeenCalled(); expect( readJson(tree, 'libs/test-ui-lib2/.storybook/tsconfig.json').files ).toMatchSnapshot(); @@ -311,12 +352,18 @@ describe('@nrwl/storybook:configuration', () => { }); it('should add test-storybook target', async () => { + // @ts-ignore + enquirer.prompt = jest + .fn() + .mockReturnValue(Promise.resolve({ bundler: 'vite' })); + await configurationGenerator(tree, { name: 'test-ui-lib', uiFramework: '@storybook/react', configureTestRunner: true, }); + expect(enquirer.prompt).toHaveBeenCalled(); expect( readJson(tree, 'package.json').devDependencies['@storybook/test-runner'] ).toBeTruthy(); @@ -336,6 +383,7 @@ describe('@nrwl/storybook:configuration', () => { name: 'test-ui-lib', uiFramework: '@storybook/react', configureStaticServe: true, + bundler: 'webpack', }); expect( @@ -365,6 +413,7 @@ describe('@nrwl/storybook:configuration', () => { name: 'test-ui-lib', uiFramework: '@storybook/react', configureStaticServe: true, + bundler: 'webpack', configureCypress: true, }); @@ -423,26 +472,32 @@ describe('@nrwl/storybook:configuration', () => { await configurationGenerator(tree, { name: 'nxapp', uiFramework: '@storybook/react', + bundler: 'webpack', }); await configurationGenerator(tree, { name: 'reapp', uiFramework: '@storybook/react', + bundler: 'webpack', }); await configurationGenerator(tree, { name: 'nxlib', uiFramework: '@storybook/react', + bundler: 'webpack', }); await configurationGenerator(tree, { name: 'nxlib-buildable', uiFramework: '@storybook/react', + bundler: 'webpack', }); await configurationGenerator(tree, { name: 'relib-buildable', uiFramework: '@storybook/react', + bundler: 'webpack', }); await configurationGenerator(tree, { name: 'reapp-swc', uiFramework: '@storybook/react', + bundler: 'webpack', }); }); @@ -522,31 +577,37 @@ describe('@nrwl/storybook:configuration', () => { name: 'nxapp', uiFramework: '@storybook/react', tsConfiguration: true, + bundler: 'webpack', }); await configurationGenerator(tree, { name: 'reapp', uiFramework: '@storybook/react', tsConfiguration: true, + bundler: 'webpack', }); await configurationGenerator(tree, { name: 'nxlib', uiFramework: '@storybook/react', tsConfiguration: true, + bundler: 'webpack', }); await configurationGenerator(tree, { name: 'nxlib-buildable', uiFramework: '@storybook/react', tsConfiguration: true, + bundler: 'webpack', }); await configurationGenerator(tree, { name: 'relib-buildable', uiFramework: '@storybook/react', tsConfiguration: true, + bundler: 'webpack', }); await configurationGenerator(tree, { name: 'reapp-swc', uiFramework: '@storybook/react', tsConfiguration: true, + bundler: 'webpack', }); }); diff --git a/packages/storybook/src/generators/configuration/configuration.ts b/packages/storybook/src/generators/configuration/configuration.ts index ba6e0364b4f2b..e3a78430a204e 100644 --- a/packages/storybook/src/generators/configuration/configuration.ts +++ b/packages/storybook/src/generators/configuration/configuration.ts @@ -26,7 +26,7 @@ import { getViteConfigFilePath, projectIsRootProjectInStandaloneWorkspace, updateLintConfig, -} from './util-functions'; +} from './lib/util-functions'; import { Linter } from '@nrwl/linter'; import { findStorybookAndBuildTargetsAndCompiler, @@ -40,11 +40,16 @@ import { storybookVersion, tsNodeVersion, } from '../../utils/versions'; +import { getGeneratorConfigurationOptions } from './lib/user-prompts'; export async function configurationGenerator( tree: Tree, rawSchema: StorybookConfigureSchema ) { + if (process.env.NX_INTERACTIVE === 'true') { + rawSchema = await getGeneratorConfigurationOptions(rawSchema); + } + const schema = normalizeSchema(rawSchema); const tasks: GeneratorCallback[] = []; @@ -79,10 +84,15 @@ export async function configurationGenerator( if (viteBuildTarget) { if (schema.bundler !== 'vite') { - logger.info( - `Your project ${schema.name} uses Vite as a bundler. - Nx will configure Storybook for this project to use Vite as well.` - ); + if (!schema.storybook7Configuration) { + // The warnings for v7 are handled in the next if statement + logger.info( + `Your project ${schema.name} uses Vite as a bundler. + Nx will configure Storybook for this project to use Vite as well.` + ); + } + // We need this regardless of Storybook version + // because we use it in the init task schema.bundler = 'vite'; } diff --git a/packages/storybook/src/generators/configuration/lib/user-prompts.ts b/packages/storybook/src/generators/configuration/lib/user-prompts.ts new file mode 100644 index 0000000000000..b65a0f65de120 --- /dev/null +++ b/packages/storybook/src/generators/configuration/lib/user-prompts.ts @@ -0,0 +1,85 @@ +import { UiFramework, UiFramework7 } from '@nrwl/storybook/src/utils/models'; +import { Constants } from '@nrwl/storybook/src/utils/utilities'; +import { prompt } from 'enquirer'; +import { StorybookConfigureSchema } from '../schema'; + +export async function getGeneratorConfigurationOptions( + rawSchema: StorybookConfigureSchema +): Promise { + if (rawSchema.storybook7Configuration) { + if (!rawSchema.storybook7UiFramework) { + rawSchema.storybook7UiFramework = await getStorybook7Framework(); + } + } else { + if (!rawSchema.uiFramework) { + rawSchema.uiFramework = await getStorybook6Framework(); + } + } + if ( + rawSchema?.uiFramework === '@storybook/react' || + rawSchema?.uiFramework === '@storybook/web-components' + ) { + if (!rawSchema?.bundler) { + rawSchema.bundler = await getBundler(); + } + } + + return rawSchema; +} + +export async function getBundler(): Promise<'vite' | 'webpack'> { + const a = await prompt<{ bundler: 'vite' | 'webpack' }>([ + { + name: 'bundler', + message: `Choose the builder you want to use for Storybook`, + type: 'autocomplete', + choices: [ + { + name: 'vite', + message: 'Use Vite.js with the @storybook/builder-vite package', + }, + { + name: 'webpack', + message: 'Use Webpack 5 with the @storybook/builder-webpack5 package', + }, + ], + }, + ]); + return a.bundler; +} + +export async function getStorybook7Framework(): Promise { + const a = await prompt<{ UiFramework7: UiFramework7 }>([ + { + name: 'UiFramework7', + message: `Choose the Storybook 7 framework that you need to use`, + type: 'autocomplete', + choices: [ + ...Constants.uiFrameworks7.map((uiFramework7) => ({ + name: uiFramework7, + message: uiFramework7, + })), + ], + }, + ]); + return a.UiFramework7; +} + +export async function getStorybook6Framework(): Promise { + const a = await prompt<{ UiFramework: UiFramework }>([ + { + name: 'UiFramework', + message: `Choose the Storybook framework that you need to use`, + type: 'autocomplete', + choices: [ + ...Object.entries(Constants.uiFrameworks).map( + ([_key, uiFramework]) => ({ + name: uiFramework, + message: uiFramework, + }) + ), + ], + }, + ]); + return a.UiFramework; +} diff --git a/packages/storybook/src/generators/configuration/util-functions.ts b/packages/storybook/src/generators/configuration/lib/util-functions.ts similarity index 97% rename from packages/storybook/src/generators/configuration/util-functions.ts rename to packages/storybook/src/generators/configuration/lib/util-functions.ts index c8d7b681d1c93..2898c9eeebe3b 100644 --- a/packages/storybook/src/generators/configuration/util-functions.ts +++ b/packages/storybook/src/generators/configuration/lib/util-functions.ts @@ -24,10 +24,10 @@ import { dedupe, findStorybookAndBuildTargetsAndCompiler, TsConfig, -} from '../../utils/utilities'; -import { StorybookConfigureSchema } from './schema'; -import { UiFramework, UiFramework7 } from '../../utils/models'; -import { nxVersion } from '../../utils/versions'; +} from '../../../utils/utilities'; +import { StorybookConfigureSchema } from '../schema'; +import { UiFramework, UiFramework7 } from '../../../utils/models'; +import { nxVersion } from '../../../utils/versions'; const DEFAULT_PORT = 4400; @@ -383,7 +383,7 @@ export function createProjectStorybookDir( const templatePath = join( __dirname, - `./project-files${usesV7 ? '-7' : ''}${tsConfiguration ? '-ts' : ''}` + `../project-files${usesV7 ? '-7' : ''}${tsConfiguration ? '-ts' : ''}` ); generateFiles(tree, templatePath, root, { diff --git a/packages/storybook/src/generators/configuration/schema.d.ts b/packages/storybook/src/generators/configuration/schema.d.ts index c5d6cc60c4238..5f3c8ed9a5ae8 100644 --- a/packages/storybook/src/generators/configuration/schema.d.ts +++ b/packages/storybook/src/generators/configuration/schema.d.ts @@ -3,7 +3,7 @@ import { UiFramework7, UiFramework } from '../../utils/models'; export interface StorybookConfigureSchema { name: string; - uiFramework: UiFramework; // TODO(katerina): Remove when Storybook 7 + uiFramework?: UiFramework; // TODO(katerina): Remove when Storybook 7 configureCypress?: boolean; bundler?: 'webpack' | 'vite'; // TODO(katerina): Remove when Storybook 7 linter?: Linter; diff --git a/packages/storybook/src/generators/configuration/schema.json b/packages/storybook/src/generators/configuration/schema.json index e6c5c53c6b667..744f89894eb27 100644 --- a/packages/storybook/src/generators/configuration/schema.json +++ b/packages/storybook/src/generators/configuration/schema.json @@ -31,7 +31,6 @@ "@storybook/vue3", "@storybook/svelte" ], - "x-prompt": "What UI framework plugin should storybook use?", "x-priority": "important" }, "configureCypress": { @@ -80,7 +79,6 @@ "description": "The Storybook builder to use.", "type": "string", "enum": ["vite", "webpack"], - "x-prompt": "Which Storybook builder do you want to use?", "default": "webpack", "x-priority": "important" }, diff --git a/packages/storybook/src/migrations/update-14-0-0/migrate-defaults-5-to-6/migrate-defaults-5-to-6.ts b/packages/storybook/src/migrations/update-14-0-0/migrate-defaults-5-to-6/migrate-defaults-5-to-6.ts index 5c68e85e30b79..5d9ee4c67b410 100644 --- a/packages/storybook/src/migrations/update-14-0-0/migrate-defaults-5-to-6/migrate-defaults-5-to-6.ts +++ b/packages/storybook/src/migrations/update-14-0-0/migrate-defaults-5-to-6/migrate-defaults-5-to-6.ts @@ -11,7 +11,7 @@ import { import { lte } from 'semver'; import { checkAndCleanWithSemver } from '@nrwl/devkit/src/utils/semver'; import { storybookVersion } from '../../../utils/versions'; -import { createProjectStorybookDir } from '../../../generators/configuration/util-functions'; +import { createProjectStorybookDir } from '../../../generators/configuration/lib/util-functions'; import { StorybookConfigureSchema } from '../../../generators/configuration/schema'; import { findStorybookAndBuildTargetsAndCompiler } from '../../../utils/utilities'; From e96f72b2f5c665c989caa5cc7eab29e321d0fd06 Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Mon, 27 Mar 2023 15:58:45 +0100 Subject: [PATCH 58/76] cleanup(angular): add workaround for schematic usage warning (#15901) --- .../angular/src/generators/utils/warn-for-schematic-usage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/angular/src/generators/utils/warn-for-schematic-usage.ts b/packages/angular/src/generators/utils/warn-for-schematic-usage.ts index 2d10ec100f0de..5f847a61fece7 100644 --- a/packages/angular/src/generators/utils/warn-for-schematic-usage.ts +++ b/packages/angular/src/generators/utils/warn-for-schematic-usage.ts @@ -1,6 +1,6 @@ export function warnForSchematicUsage(convertedGenerator: T): T { console.warn( - 'Running generators as schematics is deprecated and will be removed in v17.' + 'Running generators as schematics is deprecated and will be removed in v17. Prefer `callRule(convertNxGenerator(generator)(options), tree, context)` where "generator" is the name of the generator you wish to use.' ); return convertedGenerator; From 15e29b2a3e096fe0cbd1b87e96865f872e8e39f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Jona=C5=A1?= Date: Mon, 27 Mar 2023 17:13:10 +0200 Subject: [PATCH 59/76] fix(linter): remove outdated e2e test check (#15899) --- e2e/linter/src/linter.test.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/e2e/linter/src/linter.test.ts b/e2e/linter/src/linter.test.ts index 93c9b8ab3bfbc..c4262b69275fb 100644 --- a/e2e/linter/src/linter.test.ts +++ b/e2e/linter/src/linter.test.ts @@ -505,7 +505,6 @@ describe('Linter', () => { expect(appEslint.overrides[0].extends).toBeDefined(); expect(appEslint.overrides[1].extends).toBeDefined(); expect(e2eEslint.overrides[0].extends).toBeDefined(); - expect(e2eEslint.overrides[1].extends).toBeDefined(); runCLI(`generate @nrwl/workspace:lib ${mylib} --unitTestRunner=jest`); verifySuccessfulMigratedSetup(myapp, mylib); @@ -517,7 +516,6 @@ describe('Linter', () => { expect(appEslint.overrides[0].extends).toBeUndefined(); expect(appEslint.overrides[1].extends).toBeUndefined(); expect(e2eEslint.overrides[0].extends).toBeUndefined(); - expect(e2eEslint.overrides[1].extends).toBeUndefined(); }); it('(Angular standalone) should set root project config to app and e2e app and migrate when another lib is added', () => { @@ -536,7 +534,6 @@ describe('Linter', () => { expect(appEslint.overrides[0].extends).toBeDefined(); expect(appEslint.overrides[1].extends).toBeDefined(); expect(e2eEslint.overrides[0].extends).toBeDefined(); - expect(e2eEslint.overrides[1].extends).toBeDefined(); runCLI(`generate @nrwl/workspace:lib ${mylib} --no-interactive`); verifySuccessfulMigratedSetup(myapp, mylib); @@ -550,7 +547,6 @@ describe('Linter', () => { 'plugin:@angular-eslint/template/process-inline-templates', ]); expect(e2eEslint.overrides[0].extends).toBeUndefined(); - expect(e2eEslint.overrides[1].extends).toBeUndefined(); }); it('(Node standalone) should set root project config to app and e2e app and migrate when another lib is added', () => { @@ -569,7 +565,6 @@ describe('Linter', () => { expect(appEslint.overrides[0].extends).toBeDefined(); expect(appEslint.overrides[1].extends).toBeDefined(); expect(e2eEslint.overrides[0].extends).toBeDefined(); - expect(e2eEslint.overrides[1].extends).toBeDefined(); runCLI(`generate @nrwl/workspace:lib ${mylib} --no-interactive`); verifySuccessfulMigratedSetup(myapp, mylib); @@ -581,7 +576,6 @@ describe('Linter', () => { expect(appEslint.overrides[0].extends).toBeUndefined(); expect(appEslint.overrides[1].extends).toBeUndefined(); expect(e2eEslint.overrides[0].extends).toBeUndefined(); - expect(e2eEslint.overrides[1].extends).toBeUndefined(); }); }); }); From 1f935a7361f9c1831fc8abb4e97371cae2bcb8b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Jona=C5=A1?= Date: Mon, 27 Mar 2023 17:15:35 +0200 Subject: [PATCH 60/76] feat(core): optimize npm pruning path elevation function (#15880) --- packages/nx/src/lock-file/npm-parser.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/nx/src/lock-file/npm-parser.ts b/packages/nx/src/lock-file/npm-parser.ts index 7000c79c07ddf..f094f4020be75 100644 --- a/packages/nx/src/lock-file/npm-parser.ts +++ b/packages/nx/src/lock-file/npm-parser.ts @@ -568,10 +568,11 @@ function elevateNestedPaths( sortedPaths.forEach((path) => { const segments = path.split('/node_modules/'); + const mappedPackage = remappedPackages.get(path); // we keep hoisted packages intact if (segments.length === 1) { - result.set(path, remappedPackages.get(path)); + result.set(path, mappedPackage); return; } @@ -581,13 +582,12 @@ function elevateNestedPaths( // check if grandparent has the same package const shouldElevate = (segs: string[]) => { - const newPath = getNewPath(segs.slice(0, -1)); - if (result.has(newPath)) { - const match = result.get(newPath); - const source = remappedPackages.get(path); + const elevatedPath = getNewPath(segs.slice(0, -1)); + if (result.has(elevatedPath)) { + const match = result.get(elevatedPath); return ( - match.valueV1?.version === source.valueV1?.version && - match.valueV3?.version === source.valueV3?.version + match.valueV1?.version === mappedPackage.valueV1?.version && + match.valueV3?.version === mappedPackage.valueV3?.version ); } return true; @@ -598,12 +598,12 @@ function elevateNestedPaths( } const newPath = getNewPath(segments); if (path !== newPath) { - result.set(newPath, { - ...remappedPackages.get(path), - path: newPath, - }); + if (!result.has(newPath)) { + mappedPackage.path = newPath; + result.set(newPath, mappedPackage); + } } else { - result.set(path, remappedPackages.get(path)); + result.set(path, mappedPackage); } }); From 4e2b07e179a12b241f3731391e1714b5a68274fa Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Mon, 27 Mar 2023 18:42:52 +0300 Subject: [PATCH 61/76] fix(storybook): improve and simplify e2e tests (#15734) --- .../src/storybook-angular.test.ts | 50 +++++++-------- e2e/storybook/src/storybook-nested.test.ts | 62 +++---------------- e2e/storybook/src/storybook.test.ts | 38 +++++------- 3 files changed, 47 insertions(+), 103 deletions(-) diff --git a/e2e/storybook-angular/src/storybook-angular.test.ts b/e2e/storybook-angular/src/storybook-angular.test.ts index b5f8e09d80b98..15a58fcac27c6 100644 --- a/e2e/storybook-angular/src/storybook-angular.test.ts +++ b/e2e/storybook-angular/src/storybook-angular.test.ts @@ -1,56 +1,51 @@ import { checkFilesExist, cleanupProject, - getPackageManagerCommand, killPorts, newProject, runCLI, - runCommand, + runCommandUntil, runCypressTests, tmpProjPath, uniq, - updateJson, } from '@nrwl/e2e/utils'; import { writeFileSync } from 'fs'; -describe('Storybook for Angular', () => { - const angularStorybookLib = uniq('test-ui-lib'); +describe('Storybook executors for Angular', () => { + const angularStorybookLib = uniq('test-ui-ng-lib'); beforeAll(() => { newProject(); createTestUILib(angularStorybookLib); runCLI( `generate @nrwl/angular:storybook-configuration ${angularStorybookLib} --configureCypress --generateStories --generateCypressSpecs --no-interactive` ); - - // TODO(jack): Overriding enhanced-resolve to 5.10.0 now until the package is fixed. - // TODO: Use --storybook7Configuration and remove this - // See: https://github.com/webpack/enhanced-resolve/issues/362 - updateJson('package.json', (json) => { - json['overrides'] = { - 'enhanced-resolve': '5.10.0', - }; - - return json; - }); - runCommand(getPackageManagerCommand().install); - - console.log('Here is the Nx report: '); - runCLI(`report`); }); afterAll(() => { cleanupProject(); }); - // TODO: Use --storybook7Configuration and re-enable this - xdescribe('Storybook builder', () => { - it('shoud build storybook', () => { + // TODO: Enable on SB7 + xdescribe('serve and build storybook', () => { + afterAll(() => killPorts()); + + it('should serve an Angular based Storybook setup', async () => { + const p = await runCommandUntil( + `run ${angularStorybookLib}:storybook`, + (output) => { + return /Storybook.*started/gi.test(output); + } + ); + p.kill(); + }, 200_000); + + it('shoud build an Angular based storybook', () => { runCLI(`run ${angularStorybookLib}:build-storybook --verbose`); checkFilesExist(`dist/storybook/${angularStorybookLib}/index.html`); - }); + }, 200_000); }); - // TODO: Use --storybook7Configuration and re-enable this + // However much I increase the timeout, this takes forever? xdescribe('run cypress tests using storybook', () => { it('should execute e2e tests using Cypress running against Storybook', async () => { if (runCypressTests()) { @@ -83,7 +78,7 @@ describe('Storybook for Angular', () => { expect(e2eResults).toContain('All specs passed!'); expect(await killPorts()).toBeTruthy(); } - }, 1000000); + }, 1000_000); }); }); @@ -125,7 +120,4 @@ export function createTestUILib(libName: string): void { ` ); - runCLI( - `g @nrwl/angular:component test-other --project=${libName} --no-interactive` - ); } diff --git a/e2e/storybook/src/storybook-nested.test.ts b/e2e/storybook/src/storybook-nested.test.ts index 33e60caf5fbd9..51e93312212fd 100644 --- a/e2e/storybook/src/storybook-nested.test.ts +++ b/e2e/storybook/src/storybook-nested.test.ts @@ -1,35 +1,26 @@ import { checkFilesExist, cleanupProject, - getPackageManagerCommand, - getSelectedPackageManager, killPorts, readJson, runCLI, - runCommand, runCommandUntil, runCreateWorkspace, tmpProjPath, uniq, - updateJson, } from '@nrwl/e2e/utils'; import { writeFileSync } from 'fs'; -describe('Storybook generators for nested workspaces', () => { - const previousPM = process.env.SELECTED_PM; +describe('Storybook generators and executors for standalone workspaces - using React + Vite', () => { const wsName = uniq('react'); const appName = uniq('app'); - const packageManager = getSelectedPackageManager() || 'yarn'; beforeAll(() => { - process.env.SELECTED_PM = 'yarn'; - // create a workspace with a single react app at the root runCreateWorkspace(wsName, { preset: 'react-standalone', appName, style: 'css', - packageManager, bundler: 'vite', }); @@ -37,26 +28,11 @@ describe('Storybook generators for nested workspaces', () => { `generate @nrwl/react:storybook-configuration ${appName} --generateStories --no-interactive` ); - // TODO(jack): Overriding enhanced-resolve to 5.10.0 now until the package is fixed. - // TODO: Use --storybook7Configuration and remove this - // See: https://github.com/webpack/enhanced-resolve/issues/362 - updateJson('package.json', (json) => { - json['overrides'] = { - 'enhanced-resolve': '5.10.0', - }; - - return json; - }); - - runCommand(getPackageManagerCommand().install); - - console.log('Here is the Nx report: '); runCLI(`report`); }); afterAll(() => { cleanupProject(); - process.env.SELECTED_PM = previousPM; }); describe('Storybook generated files', () => { @@ -72,47 +48,29 @@ describe('Storybook generators for nested workspaces', () => { const tsconfig = readJson(`tsconfig.json`); expect(tsconfig['ts-node']?.compilerOptions?.module).toEqual('commonjs'); }); - - it('should generate correct files for nested app', () => { - const nestedAppName = uniq('other-app'); - runCLI(`generate @nrwl/react:app ${nestedAppName} --no-interactive`); - runCLI( - `generate @nrwl/react:storybook-configuration ${nestedAppName} --generateStories --no-interactive` - ); - checkFilesExist( - `${nestedAppName}/.storybook/main.js`, - `${nestedAppName}/.storybook/tsconfig.json` - ); - }); }); - // TODO: Use --storybook7Configuration and re-enable this test + // TODO: Use --storybook7Configuration and re-enable this test - or else it NEEDS NODE 16 xdescribe('serve storybook', () => { afterEach(() => killPorts()); - it('should run a React based Storybook setup', async () => { - // serve the storybook + it('should serve a React based Storybook setup that uses Vite', async () => { const p = await runCommandUntil(`run ${appName}:storybook`, (output) => { return /Storybook.*started/gi.test(output); }); p.kill(); - }, 1000000); + }, 40000); }); - // TODO: Use --storybook7Configuration and re-enable this test + // TODO: Use --storybook7Configuration and re-enable this test - or else it NEEDS NODE 16 xdescribe('build storybook', () => { - it('should build and lint a React based storybook', () => { - // build + it('should build a React based storybook that uses Vite', () => { runCLI(`run ${appName}:build-storybook --verbose`); checkFilesExist(`dist/storybook/${appName}/index.html`); + }, 40000); - // lint - const output = runCLI(`run ${appName}:lint`); - expect(output).toContain('All files pass linting.'); - }, 1000000); - - // Not sure how much sense this test makes - maybe it's noise? - xit('should build a React based storybook that references another lib', () => { + // This test makes sure path resolution works + xit('should build a React based storybook that references another lib and uses Vite', () => { const reactLib = uniq('test-lib-react'); runCLI(`generate @nrwl/react:lib ${reactLib} --no-interactive`); // create a React component we can reference @@ -168,6 +126,6 @@ describe('Storybook generators for nested workspaces', () => { // build React lib runCLI(`run ${reactLib}:build-storybook --verbose`); checkFilesExist(`dist/storybook/${reactLib}/index.html`); - }, 1000000); + }, 40000); }); }); diff --git a/e2e/storybook/src/storybook.test.ts b/e2e/storybook/src/storybook.test.ts index 98a1942516540..eeead6c87e574 100644 --- a/e2e/storybook/src/storybook.test.ts +++ b/e2e/storybook/src/storybook.test.ts @@ -13,14 +13,18 @@ import { } from '@nrwl/e2e/utils'; import { writeFileSync } from 'fs'; -describe('Storybook generators for non-angular projects', () => { +describe('Storybook generators and executors for monorepos', () => { + const previousPM = process.env.SELECTED_PM; const reactStorybookLib = uniq('test-ui-lib-react'); let proj; beforeAll(() => { - proj = newProject(); + process.env.SELECTED_PM = 'yarn'; + proj = newProject({ + packageManager: 'yarn', + }); runCLI(`generate @nrwl/react:lib ${reactStorybookLib} --no-interactive`); runCLI( - `generate @nrwl/react:storybook-configuration ${reactStorybookLib} --generateStories --no-interactive` + `generate @nrwl/react:storybook-configuration ${reactStorybookLib} --generateStories --no-interactive --bundler=webpack` ); // TODO(jack): Overriding enhanced-resolve to 5.10.0 now until the package is fixed. @@ -34,20 +38,17 @@ describe('Storybook generators for non-angular projects', () => { return json; }); runCommand(getPackageManagerCommand().install); - - console.log('Here is the Nx report: '); - runCLI(`report`); }); afterAll(() => { cleanupProject(); + process.env.SELECTED_PM = previousPM; }); - // TODO: Use --storybook7Configuration and re-enable this test - xdescribe('serve storybook', () => { + describe('serve and build storybook', () => { afterEach(() => killPorts()); - it('should run a React based Storybook setup', async () => { + it('should serve a React based Storybook setup that uses webpack', async () => { // serve the storybook const p = await runCommandUntil( `run ${reactStorybookLib}:storybook`, @@ -56,23 +57,16 @@ describe('Storybook generators for non-angular projects', () => { } ); p.kill(); - }, 1000000); - }); + }, 20000); - // TODO: Use --storybook7Configuration and re-enable this test - xdescribe('build storybook', () => { - it('should build and lint a React based storybook', () => { + it('should build a React based storybook setup that uses webpack', () => { // build runCLI(`run ${reactStorybookLib}:build-storybook --verbose`); checkFilesExist(`dist/storybook/${reactStorybookLib}/index.html`); + }, 40000); - // lint - const output = runCLI(`run ${reactStorybookLib}:lint`); - expect(output).toContain('All files pass linting.'); - }, 1000000); - - // I am not sure how much sense this test makes - Maybe it's just adding noise - xit('should build a React based storybook that references another lib', () => { + // This test makes sure path resolution works + it('should build a React based storybook that references another lib and uses webpack', () => { const anotherReactLib = uniq('test-another-lib-react'); runCLI(`generate @nrwl/react:lib ${anotherReactLib} --no-interactive`); // create a React component we can reference @@ -123,6 +117,6 @@ describe('Storybook generators for non-angular projects', () => { // build React lib runCLI(`run ${reactStorybookLib}:build-storybook --verbose`); checkFilesExist(`dist/storybook/${reactStorybookLib}/index.html`); - }, 1000000); + }, 40000); }); }); From 117868ca3f65a259f0ea800dc400678a1a66ff33 Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Mon, 27 Mar 2023 17:10:56 -0400 Subject: [PATCH 62/76] chore(repo): swap to artifacts rather than cache for issues data (#15912) --- .github/workflows/issue-notifier.yml | 12 ++++++++---- scripts/issues-scraper/index.ts | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/issue-notifier.yml b/.github/workflows/issue-notifier.yml index 676a505411bed..da2b367b92999 100644 --- a/.github/workflows/issue-notifier.yml +++ b/.github/workflows/issue-notifier.yml @@ -37,11 +37,10 @@ jobs: - name: Install packages run: yarn install --prefer-offline --frozen-lockfile --non-interactive - - name: Retrieve Previous Results - uses: actions/cache@v3 + - uses: actions/download-artifact@v3 with: - path: scripts/issues-scraper/data.json - key: 'always-hit' + name: cached-issue-data + path: ./scripts/isses-scraper/cached - name: Collect Issue Data id: collect @@ -49,6 +48,11 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} + - uses: actions/upload-artifact@v3 + with: + name: cached-issue-data + path: ./scripts/issues-scraper/cached/data.json + - name: Send GitHub Action trigger data to Slack workflow id: slack uses: slackapi/slack-github-action@v1.23.0 diff --git a/scripts/issues-scraper/index.ts b/scripts/issues-scraper/index.ts index db27a5ffedb65..bd01bc9101d0d 100644 --- a/scripts/issues-scraper/index.ts +++ b/scripts/issues-scraper/index.ts @@ -6,7 +6,7 @@ import { formatGhReport, getSlackMessageJson } from './format-slack-message'; import { setOutput } from '@actions/core'; import isCI from 'is-ci'; -const CACHE_FILE = join(__dirname, 'data.json'); +const CACHE_FILE = join(__dirname, 'cached', 'data.json'); async function main() { const currentData = await scrapeIssues(); From b03d630d57a8534632dae269768b31de52080cbe Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Mon, 27 Mar 2023 17:18:20 -0400 Subject: [PATCH 63/76] chore(repo): issues report should still work when no artifact found (#15914) --- .github/workflows/issue-notifier.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/issue-notifier.yml b/.github/workflows/issue-notifier.yml index da2b367b92999..c657d0e6be2af 100644 --- a/.github/workflows/issue-notifier.yml +++ b/.github/workflows/issue-notifier.yml @@ -41,6 +41,7 @@ jobs: with: name: cached-issue-data path: ./scripts/isses-scraper/cached + if_no_artifact_found: ignore - name: Collect Issue Data id: collect From 77d40893973815285ce6674921394e128fc15187 Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Mon, 27 Mar 2023 17:35:22 -0400 Subject: [PATCH 64/76] chore(repo): use continue-on-error for issue-notifier (#15915) --- .github/workflows/issue-notifier.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue-notifier.yml b/.github/workflows/issue-notifier.yml index c657d0e6be2af..8ccf54a2b5453 100644 --- a/.github/workflows/issue-notifier.yml +++ b/.github/workflows/issue-notifier.yml @@ -41,7 +41,7 @@ jobs: with: name: cached-issue-data path: ./scripts/isses-scraper/cached - if_no_artifact_found: ignore + continue-on-error: true - name: Collect Issue Data id: collect From 9cdc584dad56880138305e25fdf2ae46d4c9a7f2 Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Mon, 27 Mar 2023 17:45:50 -0400 Subject: [PATCH 65/76] chore(repo): ensure cache file directory exists during issues scraping (#15916) --- scripts/issues-scraper/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/issues-scraper/index.ts b/scripts/issues-scraper/index.ts index bd01bc9101d0d..1fedfb5f9a502 100644 --- a/scripts/issues-scraper/index.ts +++ b/scripts/issues-scraper/index.ts @@ -1,5 +1,5 @@ -import { readJsonSync, writeJsonSync } from 'fs-extra'; -import { join } from 'path'; +import { ensureDirSync, readJsonSync, writeJsonSync } from 'fs-extra'; +import { dirname, join } from 'path'; import { ReportData, ScopeData } from './model'; import { getScopeLabels, scrapeIssues } from './scrape-issues'; import { formatGhReport, getSlackMessageJson } from './format-slack-message'; @@ -49,6 +49,7 @@ function getTrendData(newData: ReportData, oldData: ReportData): ReportData { function saveCacheData(report: ReportData) { if (isCI) { + ensureDirSync(dirname(CACHE_FILE)); writeJsonSync(CACHE_FILE, report); } } From 4a55ee9a338fcbd33d6026d007e0d10497a907ac Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Mon, 27 Mar 2023 18:00:24 -0400 Subject: [PATCH 66/76] fix(js): run prettier with default options when theres no prettierrc (#15888) --- .../__snapshots__/application.spec.ts.snap | 68 +- .../component-cypress-spec.spec.ts.snap | 18 +- .../component-cypress-spec.spec.ts | 12 +- .../component-cypress-spec.ts | 6 +- .../component-story.spec.ts.snap | 13 +- .../component-story/component-story.spec.ts | 12 +- .../component-story/component-story.ts | 6 +- .../__snapshots__/component.spec.ts.snap | 30 +- .../generators/component/component.spec.ts | 20 +- .../convert-to-with-mf.spec.ts.snap | 15 +- .../__snapshots__/directive.spec.ts.snap | 147 ++-- .../host/__snapshots__/host.spec.ts.snap | 70 +- .../angular/src/generators/host/host.spec.ts | 13 +- ...library-secondary-entry-point.spec.ts.snap | 8 +- .../__snapshots__/library.spec.ts.snap | 238 ++++-- .../src/generators/library/library.spec.ts | 69 +- .../ngrx/__snapshots__/ngrx.spec.ts.snap | 410 +++++++---- .../pipe/__snapshots__/pipe.spec.ts.snap | 133 ++-- .../remote/__snapshots__/remote.spec.ts.snap | 115 ++- .../src/generators/remote/remote.spec.ts | 6 +- .../scam-directive/scam-directive.spec.ts | 38 +- .../generators/scam-pipe/scam-pipe.spec.ts | 32 +- .../scam-to-standalone.spec.ts | 26 +- .../angular/src/generators/scam/scam.spec.ts | 38 +- .../__snapshots__/setup-mf.spec.ts.snap | 85 ++- .../src/generators/setup-mf/setup-mf.spec.ts | 27 +- .../__snapshots__/setup-ssr.spec.ts.snap | 42 +- .../generators/setup-ssr/setup-ssr.spec.ts | 45 +- .../setup-tailwind.application.spec.ts.snap | 83 +++ .../setup-tailwind.application.spec.ts | 200 +++--- .../setup-tailwind.library.spec.ts | 104 +-- .../__snapshots__/stories-app.spec.ts.snap | 678 +----------------- .../__snapshots__/stories-lib.spec.ts.snap | 51 +- .../generators/stories/stories-app.spec.ts | 30 +- .../generators/stories/stories-lib.spec.ts | 33 +- .../angular/src/generators/stories/stories.ts | 76 +- .../storybook-configuration.spec.ts.snap | 19 +- .../lib/generate-stories.ts | 4 +- .../storybook-configuration.ts | 2 +- .../angular/src/generators/utils/testing.ts | 5 +- .../generators/web-worker/web-worker.spec.ts | 19 +- .../update-router-initial-navigation.spec.ts | 153 +++- .../update-karma-main-file.spec.ts | 19 +- .../cypress-e2e-configuration.spec.ts | 10 +- .../cypress-project.spec.ts.snap | 21 +- .../migrate-to-cypress-11.spec.ts.snap | 262 ++++--- .../update-cy-mount-usage.spec.ts.snap | 128 ++-- .../__snapshots__/cypress-11.spec.ts.snap | 264 +++---- .../update-15-1-0/cypress-11.spec.ts | 31 +- .../migrations/update-15-1-0/cypress-11.ts | 4 +- .../devkit/src/generators/format-files.ts | 17 +- .../generators/component/component.spec.ts | 4 +- ...d-project-root-metro-config-14-0-0.spec.ts | 146 ++-- .../update-15-0-3/change-jest-preset.spec.ts | 2 +- .../__snapshots__/jest-project.spec.ts.snap | 21 +- .../update-jest-config-ext.spec.ts.snap | 19 +- .../update-configs-jest-29.spec.ts.snap | 484 +++++++------ .../update-tests-jest-29.spec.ts.snap | 72 +- packages/js/src/generators/init/init.ts | 18 +- .../__snapshots__/library.spec.ts.snap | 6 +- .../js/src/generators/library/library.spec.ts | 4 +- packages/js/src/utils/add-babel-inputs.ts | 4 +- .../__snapshots__/lint-project.spec.ts.snap | 50 +- .../__snapshots__/workspace-rule.spec.ts.snap | 15 +- .../workspace-rule/workspace-rule.spec.ts | 43 +- .../workspace-rules-project.spec.ts.snap | 33 +- .../update-13-3-0/eslint-8-updates.spec.ts | 75 +- .../experimental-to-utils-rules.spec.ts | 87 ++- .../__snapshots__/library.spec.ts.snap | 21 +- .../__snapshots__/application.spec.ts.snap | 423 +++++++++++ .../application/application.spec.ts | 2 +- .../generators/application/lib/add-jest.ts | 1 + .../application/application.spec.ts | 4 +- .../src/generators/library/library.spec.ts | 4 +- .../update-configs-jest-29.spec.ts.snap | 484 +++++++------ .../update-tests-jest-29.spec.ts.snap | 48 +- .../application/application.spec.ts | 17 +- .../generators/application/lib/add-detox.ts | 1 + .../react-native/src/generators/init/init.ts | 2 +- .../src/generators/library/library.spec.ts | 4 +- .../add-react-native-svg-12-10-0.spec.ts | 2 +- .../add-babel-config-root-13-5-0.ts | 4 +- ...d-project-root-metro-config-14-0-0.spec.ts | 146 ++-- .../change-main-to-class-name-14-0-2.spec.ts | 2 +- .../rename-blockList-metro-config.spec.ts | 74 +- .../update-15-0-0/add-babel-inputs.ts | 3 +- packages/react-native/src/utils/add-jest.ts | 1 + .../__snapshots__/application.spec.ts.snap | 75 +- .../component-story.spec.ts.snap | 430 ++++------- .../setup-tailwind/setup-tailwind.spec.ts | 2 +- .../generators/stories/stories.app.spec.ts | 2 +- ...date-react-dom-render-for-v18.spec.ts.snap | 45 ++ .../update-react-dom-render-for-v18.spec.ts | 35 +- .../webpack-config-setup.spec.ts.snap | 108 +-- .../update-15-0-0/add-babel-inputs.ts | 3 +- .../configuration-nested.spec.ts.snap | 55 +- .../configuration-v7.spec.ts.snap | 366 ++++------ .../__snapshots__/configuration.spec.ts.snap | 570 +++++++-------- .../cypress-project.spec.ts.snap | 2 +- .../add-addon-essentials-to-all.spec.ts.snap | 591 ++++++++------- .../__snapshots__/configuration.spec.ts.snap | 636 ++++++++-------- .../init/__snapshots__/init.spec.ts.snap | 3 + .../vite/src/generators/init/init.spec.ts | 2 +- packages/vite/src/generators/init/init.ts | 18 +- .../vitest/__snapshots__/vitest.spec.ts.snap | 193 +++-- .../src/generators/vitest/vitest-generator.ts | 2 +- .../application/application.spec.ts | 8 +- packages/web/src/generators/init/init.ts | 2 +- .../update-15-0-0/add-babel-inputs.ts | 3 +- .../update-15-0-0/add-babel-inputs.ts | 3 +- .../webpack-config-setup.spec.ts.snap | 84 ++- .../__snapshots__/ci-workflow.spec.ts.snap | 6 +- .../src/generators/library/library.spec.ts | 31 +- .../npm-package/npm-package.spec.ts | 2 +- 114 files changed, 4798 insertions(+), 4760 deletions(-) create mode 100644 packages/angular/src/generators/setup-tailwind/__snapshots__/setup-tailwind.application.spec.ts.snap create mode 100644 packages/next/src/generators/application/__snapshots__/application.spec.ts.snap create mode 100644 packages/react/src/migrations/update-14-0-0/__snapshots__/update-react-dom-render-for-v18.spec.ts.snap diff --git a/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap b/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap index b94b0842d739d..bb755dec72a69 100644 --- a/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap +++ b/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap @@ -41,7 +41,7 @@ describe('AppComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [RouterTestingModule], - declarations: [AppComponent] + declarations: [AppComponent], }).compileComponents(); }); @@ -49,14 +49,17 @@ describe('AppComponent', () => { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Welcome plain'); + expect(compiled.querySelector('h1')?.textContent).toContain( + 'Welcome plain' + ); }); }); " `; exports[`app --minimal should skip "nx-welcome.component.ts" file and references for non-standalone apps with routing 4`] = ` -"

Welcome plain

+"

Welcome plain

+ " `; @@ -67,9 +70,7 @@ import { AppComponent } from './app.component'; @NgModule({ declarations: [AppComponent], - imports: [ - BrowserModule, - ], + imports: [BrowserModule], providers: [], bootstrap: [AppComponent], }) @@ -97,7 +98,7 @@ describe('AppComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [], - declarations: [AppComponent] + declarations: [AppComponent], }).compileComponents(); }); @@ -105,14 +106,16 @@ describe('AppComponent', () => { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Welcome plain'); + expect(compiled.querySelector('h1')?.textContent).toContain( + 'Welcome plain' + ); }); }); " `; exports[`app --minimal should skip "nx-welcome.component.ts" file and references for non-standalone apps without routing 4`] = ` -"

Welcome plain

+"

Welcome plain

" `; @@ -144,17 +147,20 @@ describe('AppComponent', () => { }); it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Welcome plain'); + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain( + 'Welcome plain' + ); }); }); " `; exports[`app --minimal should skip "nx-welcome.component.ts" file and references for standalone apps with routing 3`] = ` -"

Welcome plain

+"

Welcome plain

+ " `; @@ -184,17 +190,19 @@ describe('AppComponent', () => { }); it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Welcome plain'); + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain( + 'Welcome plain' + ); }); }); " `; exports[`app --minimal should skip "nx-welcome.component.ts" file and references for standalone apps without routing 3`] = ` -"

Welcome plain

+"

Welcome plain

" `; @@ -252,10 +260,12 @@ describe('AppComponent', () => { }); it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Welcome standalone'); + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain( + 'Welcome standalone' + ); }); it(\`should have as title 'standalone'\`, () => { @@ -283,7 +293,7 @@ import { NxWelcomeComponent } from './nx-welcome.component'; @Component({ standalone: true, - imports: [NxWelcomeComponent, ], + imports: [NxWelcomeComponent], selector: 'proj-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], @@ -307,10 +317,12 @@ describe('AppComponent', () => { }); it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Welcome standalone'); + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain( + 'Welcome standalone' + ); }); it(\`should have as title 'standalone'\`, () => { diff --git a/packages/angular/src/generators/component-cypress-spec/__snapshots__/component-cypress-spec.spec.ts.snap b/packages/angular/src/generators/component-cypress-spec/__snapshots__/component-cypress-spec.spec.ts.snap index 509541a52a137..d3ad8d2a0119f 100644 --- a/packages/angular/src/generators/component-cypress-spec/__snapshots__/component-cypress-spec.spec.ts.snap +++ b/packages/angular/src/generators/component-cypress-spec/__snapshots__/component-cypress-spec.spec.ts.snap @@ -2,18 +2,28 @@ exports[`componentCypressSpec generator should generate .spec.ts when using cypress.json 1`] = ` "describe('ng-app1', () => { - beforeEach(() => cy.visit('/iframe.html?id=testbuttoncomponent--primary&args=buttonType:button;style:default;age;isOn:false;')); + beforeEach(() => + cy.visit( + '/iframe.html?id=testbuttoncomponent--primary&args=buttonType:button;style:default;age;isOn:false;' + ) + ); it('should render the component', () => { cy.get('proj-test-button').should('exist'); }); -});" +}); +" `; exports[`componentCypressSpec generator should generate the component spec file 1`] = ` "describe('ng-app1', () => { - beforeEach(() => cy.visit('/iframe.html?id=testbuttoncomponent--primary&args=buttonType:button;style:default;age;isOn:false;')); + beforeEach(() => + cy.visit( + '/iframe.html?id=testbuttoncomponent--primary&args=buttonType:button;style:default;age;isOn:false;' + ) + ); it('should render the component', () => { cy.get('proj-test-button').should('exist'); }); -});" +}); +" `; diff --git a/packages/angular/src/generators/component-cypress-spec/component-cypress-spec.spec.ts b/packages/angular/src/generators/component-cypress-spec/component-cypress-spec.spec.ts index 4a404512e3f1e..646398743ee8e 100644 --- a/packages/angular/src/generators/component-cypress-spec/component-cypress-spec.spec.ts +++ b/packages/angular/src/generators/component-cypress-spec/component-cypress-spec.spec.ts @@ -52,13 +52,13 @@ export class TestButtonComponent { ); }); - it('should not generate the component spec file when it already exists', () => { + it('should not generate the component spec file when it already exists', async () => { mockedInstalledCypressVersion.mockReturnValue(10); jest.spyOn(storybookUtils, 'getComponentProps'); jest.spyOn(devkit, 'generateFiles'); tree.write(specFile, ''); - componentCypressSpecGenerator(tree, { + await componentCypressSpecGenerator(tree, { componentFileName: 'test-button.component', componentName: 'TestButtonComponent', componentPath: `test-button`, @@ -71,9 +71,9 @@ export class TestButtonComponent { expect(tree.read(specFile).toString()).toBe(''); }); - it('should generate the component spec file', () => { + it('should generate the component spec file', async () => { mockedInstalledCypressVersion.mockReturnValue(10); - componentCypressSpecGenerator(tree, { + await componentCypressSpecGenerator(tree, { componentFileName: 'test-button.component', componentName: 'TestButtonComponent', componentPath: `test-button`, @@ -86,13 +86,13 @@ export class TestButtonComponent { expect(specFileContent).toMatchSnapshot(); }); - it('should generate .spec.ts when using cypress.json', () => { + it('should generate .spec.ts when using cypress.json', async () => { mockedInstalledCypressVersion.mockReturnValue(9); const v9SpecFile = `apps/${appName}-e2e/src/integration/test-button/test-button.component.spec.ts`; tree.delete(`apps/${appName}-e2e/cypress.config.ts`); tree.write(`apps/${appName}-e2e/cypress.json`, `{}`); - componentCypressSpecGenerator(tree, { + await componentCypressSpecGenerator(tree, { componentFileName: 'test-button.component', componentName: 'TestButtonComponent', componentPath: `test-button`, diff --git a/packages/angular/src/generators/component-cypress-spec/component-cypress-spec.ts b/packages/angular/src/generators/component-cypress-spec/component-cypress-spec.ts index dbee3c2d86a5f..0a5ad44c97d4f 100644 --- a/packages/angular/src/generators/component-cypress-spec/component-cypress-spec.ts +++ b/packages/angular/src/generators/component-cypress-spec/component-cypress-spec.ts @@ -10,10 +10,10 @@ import { getArgsDefaultValue } from './lib/get-args-default-value'; import { getComponentSelector } from './lib/get-component-selector'; import type { ComponentCypressSpecGeneratorOptions } from './schema'; -export function componentCypressSpecGenerator( +export async function componentCypressSpecGenerator( tree: Tree, options: ComponentCypressSpecGeneratorOptions -): void { +): Promise { const { cypressProject, projectName, @@ -65,7 +65,7 @@ export function componentCypressSpecGenerator( }); if (!options.skipFormat) { - formatFiles(tree); + await formatFiles(tree); } } diff --git a/packages/angular/src/generators/component-story/__snapshots__/component-story.spec.ts.snap b/packages/angular/src/generators/component-story/__snapshots__/component-story.spec.ts.snap index 9a14b41748b3a..2d774f7aa55ee 100644 --- a/packages/angular/src/generators/component-story/__snapshots__/component-story.spec.ts.snap +++ b/packages/angular/src/generators/component-story/__snapshots__/component-story.spec.ts.snap @@ -6,7 +6,7 @@ import { TestButtonComponent } from './test-button.component'; export default { title: 'TestButtonComponent', - component: TestButtonComponent + component: TestButtonComponent, } as Meta; export const Primary = { @@ -14,10 +14,11 @@ export const Primary = { props: args, }), args: { - buttonType: 'button', - style: 'default', - age: 0, - isOn: false, + buttonType: 'button', + style: 'default', + age: 0, + isOn: false, }, -};" +}; +" `; diff --git a/packages/angular/src/generators/component-story/component-story.spec.ts b/packages/angular/src/generators/component-story/component-story.spec.ts index d138ce785e53b..b8741cb941574 100644 --- a/packages/angular/src/generators/component-story/component-story.spec.ts +++ b/packages/angular/src/generators/component-story/component-story.spec.ts @@ -48,12 +48,12 @@ describe('componentStory generator', () => { ); }); - it('should not generate the component stories file when it already exists', () => { + it('should not generate the component stories file when it already exists', async () => { jest.spyOn(storybookUtils, 'getComponentProps'); jest.spyOn(devkit, 'generateFiles'); tree.write(storyFile, ''); - componentStoryGenerator(tree, { + await componentStoryGenerator(tree, { componentFileName: 'test-button.component', componentName: 'TestButtonComponent', componentPath: `src/lib/test-button`, @@ -65,8 +65,8 @@ describe('componentStory generator', () => { expect(tree.read(storyFile, 'utf-8')).toBe(''); }); - it('should generate the component stories file', () => { - componentStoryGenerator(tree, { + it('should generate the component stories file', async () => { + await componentStoryGenerator(tree, { componentFileName: 'test-button.component', componentName: 'TestButtonComponent', componentPath: `src/lib/test-button`, @@ -76,8 +76,8 @@ describe('componentStory generator', () => { expect(tree.exists(storyFile)).toBe(true); }); - it('should generate the right props', () => { - componentStoryGenerator(tree, { + it('should generate the right props', async () => { + await componentStoryGenerator(tree, { componentFileName: 'test-button.component', componentName: 'TestButtonComponent', componentPath: `src/lib/test-button`, diff --git a/packages/angular/src/generators/component-story/component-story.ts b/packages/angular/src/generators/component-story/component-story.ts index ae80a8b6e9901..989f662e99b31 100644 --- a/packages/angular/src/generators/component-story/component-story.ts +++ b/packages/angular/src/generators/component-story/component-story.ts @@ -3,10 +3,10 @@ import { formatFiles, generateFiles, joinPathFragments } from '@nrwl/devkit'; import { getComponentProps } from '../utils/storybook-ast/storybook-inputs'; import type { ComponentStoryGeneratorOptions } from './schema'; -export function componentStoryGenerator( +export async function componentStoryGenerator( tree: Tree, options: ComponentStoryGeneratorOptions -): void { +): Promise { const { componentFileName, componentName, componentPath, projectPath } = options; @@ -34,7 +34,7 @@ export function componentStoryGenerator( }); if (!options.skipFormat) { - formatFiles(tree); + await formatFiles(tree); } } diff --git a/packages/angular/src/generators/component/__snapshots__/component.spec.ts.snap b/packages/angular/src/generators/component/__snapshots__/component.spec.ts.snap index ddc3b5f0a990b..0f57ec8cc61ec 100644 --- a/packages/angular/src/generators/component/__snapshots__/component.spec.ts.snap +++ b/packages/angular/src/generators/component/__snapshots__/component.spec.ts.snap @@ -6,7 +6,7 @@ exports[`component Generator --flat should create the component correctly and ex @Component({ selector: 'proj-example', templateUrl: './example.component.html', - styleUrls: ['./example.component.css'] + styleUrls: ['./example.component.css'], }) export class ExampleComponent {} " @@ -18,7 +18,7 @@ exports[`component Generator --flat should create the component correctly and no @Component({ selector: 'proj-example', templateUrl: './example.component.html', - styleUrls: ['./example.component.css'] + styleUrls: ['./example.component.css'], }) export class ExampleComponent {} " @@ -30,7 +30,7 @@ exports[`component Generator --path should create the component correctly and ex @Component({ selector: 'proj-example', templateUrl: './example.component.html', - styleUrls: ['./example.component.css'] + styleUrls: ['./example.component.css'], }) export class ExampleComponent {} " @@ -42,15 +42,16 @@ exports[`component Generator secondary entry points should create the component @Component({ selector: 'proj-example', templateUrl: './example.component.html', - styleUrls: ['./example.component.css'] + styleUrls: ['./example.component.css'], }) export class ExampleComponent {} " `; exports[`component Generator secondary entry points should create the component correctly and export it in the entry point 2`] = ` -"export * from \\"./lib/secondary.module\\"; -export * from \\"./lib/example/example.component\\";" +"export * from './lib/secondary.module'; +export * from './lib/example/example.component'; +" `; exports[`component Generator should create the component correctly and export it in the entry point when "export=true" 1`] = ` @@ -59,15 +60,16 @@ exports[`component Generator should create the component correctly and export it @Component({ selector: 'proj-example', templateUrl: './example.component.html', - styleUrls: ['./example.component.css'] + styleUrls: ['./example.component.css'], }) export class ExampleComponent {} " `; exports[`component Generator should create the component correctly and export it in the entry point when "export=true" 2`] = ` -"export * from \\"./lib/lib.module\\"; -export * from \\"./lib/example/example.component\\";" +"export * from './lib/lib.module'; +export * from './lib/example/example.component'; +" `; exports[`component Generator should create the component correctly and export it in the entry point when is standalone and "export=true" 1`] = ` @@ -79,7 +81,7 @@ import { CommonModule } from '@angular/common'; standalone: true, imports: [CommonModule], templateUrl: './example.component.html', - styleUrls: ['./example.component.css'] + styleUrls: ['./example.component.css'], }) export class ExampleComponent {} " @@ -91,7 +93,7 @@ exports[`component Generator should create the component correctly and not expor @Component({ selector: 'proj-example', templateUrl: './example.component.html', - styleUrls: ['./example.component.css'] + styleUrls: ['./example.component.css'], }) export class ExampleComponent {} " @@ -106,7 +108,7 @@ import { CommonModule } from '@angular/common'; standalone: true, imports: [CommonModule], templateUrl: './example.component.html', - styleUrls: ['./example.component.css'] + styleUrls: ['./example.component.css'], }) export class ExampleComponent {} " @@ -118,7 +120,7 @@ exports[`component Generator should create the component correctly and not expor @Component({ selector: 'proj-example', templateUrl: './example.component.html', - styleUrls: ['./example.component.css'] + styleUrls: ['./example.component.css'], }) export class ExampleComponent {} " @@ -130,7 +132,7 @@ exports[`component Generator should create the component correctly but not expor @Component({ selector: 'proj-example', templateUrl: './example.component.html', - styleUrls: ['./example.component.css'] + styleUrls: ['./example.component.css'], }) export class ExampleComponent {} " diff --git a/packages/angular/src/generators/component/component.spec.ts b/packages/angular/src/generators/component/component.spec.ts index 1039d0a83ff66..d2240416fff8b 100644 --- a/packages/angular/src/generators/component/component.spec.ts +++ b/packages/angular/src/generators/component/component.spec.ts @@ -84,9 +84,10 @@ describe('component Generator', () => { expect(componentSource).toMatchSnapshot(); const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8'); - expect(indexSource).toMatchInlineSnapshot( - `"export * from \\"./lib/example/example.component\\";"` - ); + expect(indexSource).toMatchInlineSnapshot(` + "export * from './lib/example/example.component'; + " + `); }); it('should create the component correctly and not export it in the entry point when "export=false"', async () => { @@ -322,7 +323,7 @@ describe('component Generator', () => { expect(componentSource).toMatchSnapshot(); const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8'); - expect(indexSource).toContain(`export * from "./lib/example.component";`); + expect(indexSource).toContain(`export * from './lib/example.component';`); }); it('should create the component correctly and not export it when "export=false"', async () => { @@ -407,7 +408,7 @@ describe('component Generator', () => { const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8'); expect(indexSource).toContain( - `export * from "./lib/mycomp/example/example.component";` + `export * from './lib/mycomp/example/example.component';` ); }); @@ -489,7 +490,7 @@ describe('component Generator', () => { // ASSERT const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8'); expect(indexSource).toContain( - `export * from "./lib/example/example.component";` + `export * from './lib/example/example.component';` ); } ); @@ -536,9 +537,10 @@ describe('component Generator', () => { // ASSERT const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8'); - expect(indexSource).toMatchInlineSnapshot( - `"export * from \\"./lib/lib.module\\";"` - ); + expect(indexSource).toMatchInlineSnapshot(` + "export * from './lib/lib.module'; + " + `); }); }); diff --git a/packages/angular/src/generators/convert-to-with-mf/__snapshots__/convert-to-with-mf.spec.ts.snap b/packages/angular/src/generators/convert-to-with-mf/__snapshots__/convert-to-with-mf.spec.ts.snap index 02b2879adf490..8ad2bd6467d43 100644 --- a/packages/angular/src/generators/convert-to-with-mf/__snapshots__/convert-to-with-mf.spec.ts.snap +++ b/packages/angular/src/generators/convert-to-with-mf/__snapshots__/convert-to-with-mf.spec.ts.snap @@ -2,18 +2,21 @@ exports[`convertToWithMF should migrate a standard previous generated host config correctly 1`] = ` "const { withModuleFederation } = require('@nrwl/angular/module-federation'); - const config = require('./module-federation.config'); - module.exports = withModuleFederation(config);" +const config = require('./module-federation.config'); +module.exports = withModuleFederation(config); +" `; exports[`convertToWithMF should migrate a standard previous generated remote config correctly 1`] = ` "const { withModuleFederation } = require('@nrwl/angular/module-federation'); - const config = require('./module-federation.config'); - module.exports = withModuleFederation(config);" +const config = require('./module-federation.config'); +module.exports = withModuleFederation(config); +" `; exports[`convertToWithMF should migrate a standard previous generated remote config using object shared syntax correctly 1`] = ` "const { withModuleFederation } = require('@nrwl/angular/module-federation'); - const config = require('./module-federation.config'); - module.exports = withModuleFederation(config);" +const config = require('./module-federation.config'); +module.exports = withModuleFederation(config); +" `; diff --git a/packages/angular/src/generators/directive/__snapshots__/directive.spec.ts.snap b/packages/angular/src/generators/directive/__snapshots__/directive.spec.ts.snap index 151a4a837173b..3ef67f10cab0e 100644 --- a/packages/angular/src/generators/directive/__snapshots__/directive.spec.ts.snap +++ b/packages/angular/src/generators/directive/__snapshots__/directive.spec.ts.snap @@ -4,12 +4,10 @@ exports[`directive generator should export the directive correctly when flat=fal "import { Directive } from '@angular/core'; @Directive({ - selector: '[projTest]' + selector: '[projTest]', }) export class TestDirective { - - constructor() { } - + constructor() {} } " `; @@ -27,26 +25,25 @@ describe('TestDirective', () => { `; exports[`directive generator should export the directive correctly when flat=false and path is nested deeper 3`] = ` -"import {NgModule} from \\"@angular/core\\"; +"import { NgModule } from '@angular/core'; import { TestDirective } from './my-directives/test/test.directive'; - @NgModule({ - imports: [], - declarations: [, TestDirective], - exports: [, TestDirective] - }) - export class TestModule {}" +@NgModule({ + imports: [], + declarations: [, TestDirective], + exports: [, TestDirective], +}) +export class TestModule {} +" `; exports[`directive generator should generate a directive with test files and attach to the NgModule automatically 1`] = ` "import { Directive } from '@angular/core'; @Directive({ - selector: '[projTest]' + selector: '[projTest]', }) export class TestDirective { - - constructor() { } - + constructor() {} } " `; @@ -64,26 +61,25 @@ describe('TestDirective', () => { `; exports[`directive generator should generate a directive with test files and attach to the NgModule automatically 3`] = ` -"import {NgModule} from \\"@angular/core\\"; +"import { NgModule } from '@angular/core'; import { TestDirective } from './test.directive'; - @NgModule({ - imports: [], - declarations: [, TestDirective], - exports: [] - }) - export class TestModule {}" +@NgModule({ + imports: [], + declarations: [, TestDirective], + exports: [], +}) +export class TestModule {} +" `; exports[`directive generator should import the directive correctly when flat=false 1`] = ` "import { Directive } from '@angular/core'; @Directive({ - selector: '[projTest]' + selector: '[projTest]', }) export class TestDirective { - - constructor() { } - + constructor() {} } " `; @@ -101,26 +97,25 @@ describe('TestDirective', () => { `; exports[`directive generator should import the directive correctly when flat=false 3`] = ` -"import {NgModule} from \\"@angular/core\\"; +"import { NgModule } from '@angular/core'; import { TestDirective } from './test/test.directive'; - @NgModule({ - imports: [], - declarations: [, TestDirective], - exports: [] - }) - export class TestModule {}" +@NgModule({ + imports: [], + declarations: [, TestDirective], + exports: [], +}) +export class TestModule {} +" `; exports[`directive generator should import the directive correctly when flat=false and path is nested deeper 1`] = ` "import { Directive } from '@angular/core'; @Directive({ - selector: '[projTest]' + selector: '[projTest]', }) export class TestDirective { - - constructor() { } - + constructor() {} } " `; @@ -138,51 +133,49 @@ describe('TestDirective', () => { `; exports[`directive generator should import the directive correctly when flat=false and path is nested deeper 3`] = ` -"import {NgModule} from \\"@angular/core\\"; +"import { NgModule } from '@angular/core'; import { TestDirective } from './my-directives/test/test.directive'; - @NgModule({ - imports: [], - declarations: [, TestDirective], - exports: [] - }) - export class TestModule {}" +@NgModule({ + imports: [], + declarations: [, TestDirective], + exports: [], +}) +export class TestModule {} +" `; exports[`directive generator should not generate test file when skipTests=true 1`] = ` "import { Directive } from '@angular/core'; @Directive({ - selector: '[projTest]' + selector: '[projTest]', }) export class TestDirective { - - constructor() { } - + constructor() {} } " `; exports[`directive generator should not generate test file when skipTests=true 2`] = ` -"import {NgModule} from \\"@angular/core\\"; +"import { NgModule } from '@angular/core'; import { TestDirective } from './my-directives/test/test.directive'; - @NgModule({ - imports: [], - declarations: [, TestDirective], - exports: [] - }) - export class TestModule {}" +@NgModule({ + imports: [], + declarations: [, TestDirective], + exports: [], +}) +export class TestModule {} +" `; exports[`directive generator should not import the directive when skipImport=true 1`] = ` "import { Directive } from '@angular/core'; @Directive({ - selector: '[projTest]' + selector: '[projTest]', }) export class TestDirective { - - constructor() { } - + constructor() {} } " `; @@ -200,13 +193,14 @@ describe('TestDirective', () => { `; exports[`directive generator should not import the directive when skipImport=true 3`] = ` -"import {NgModule} from \\"@angular/core\\"; - @NgModule({ - imports: [], - declarations: [], - exports: [] - }) - export class TestModule {}" +"import { NgModule } from '@angular/core'; +@NgModule({ + imports: [], + declarations: [], + exports: [], +}) +export class TestModule {} +" `; exports[`directive generator should not import the directive when standalone=true 1`] = ` @@ -214,12 +208,10 @@ exports[`directive generator should not import the directive when standalone=tru @Directive({ selector: '[projTest]', - standalone: true + standalone: true, }) export class TestDirective { - - constructor() { } - + constructor() {} } " `; @@ -237,11 +229,12 @@ describe('TestDirective', () => { `; exports[`directive generator should not import the directive when standalone=true 3`] = ` -"import {NgModule} from \\"@angular/core\\"; - @NgModule({ - imports: [], - declarations: [], - exports: [] - }) - export class TestModule {}" +"import { NgModule } from '@angular/core'; +@NgModule({ + imports: [], + declarations: [], + exports: [], +}) +export class TestModule {} +" `; diff --git a/packages/angular/src/generators/host/__snapshots__/host.spec.ts.snap b/packages/angular/src/generators/host/__snapshots__/host.spec.ts.snap index f6a00e855d7e4..e2fe53cb788e5 100644 --- a/packages/angular/src/generators/host/__snapshots__/host.spec.ts.snap +++ b/packages/angular/src/generators/host/__snapshots__/host.spec.ts.snap @@ -28,16 +28,16 @@ import { AppModule } from './app/app.module'; function bootstrap() { platformBrowserDynamic() - .bootstrapModule(AppModule) - .catch((err) => console.error(err)); -}; - + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); +} - if (document.readyState !== 'loading') { - bootstrap(); - } else { - document.addEventListener('DOMContentLoaded', bootstrap); - }" +if (document.readyState !== 'loading') { + bootstrap(); +} else { + document.addEventListener('DOMContentLoaded', bootstrap); +} +" `; exports[`Host App Generator --ssr should generate the correct files 3`] = ` @@ -51,7 +51,8 @@ exports[`Host App Generator --ssr should generate the correct files 3`] = ` import '@angular/platform-server/init'; export { AppServerModule } from './app/app.server.module'; -export { renderModule } from '@angular/platform-server';" +export { renderModule } from '@angular/platform-server'; +" `; exports[`Host App Generator --ssr should generate the correct files 4`] = ` @@ -124,30 +125,38 @@ export * from './bootstrap.server'; " `; -exports[`Host App Generator --ssr should generate the correct files 5`] = `"import('./src/main.server');"`; +exports[`Host App Generator --ssr should generate the correct files 5`] = ` +"import('./src/main.server'); +" +`; exports[`Host App Generator --ssr should generate the correct files 6`] = ` "module.exports = { name: 'test', - remotes: [] -}" + remotes: [], +}; +" `; exports[`Host App Generator --ssr should generate the correct files 7`] = ` -"const { withModuleFederationForSSR } = require('@nrwl/angular/module-federation'); +"const { + withModuleFederationForSSR, +} = require('@nrwl/angular/module-federation'); const config = require('./module-federation.config'); -module.exports = withModuleFederationForSSR(config)" +module.exports = withModuleFederationForSSR(config); +" `; exports[`Host App Generator --ssr should generate the correct files 8`] = ` "import { NxWelcomeComponent } from './nx-welcome.component'; - import { Route } from '@angular/router'; +import { Route } from '@angular/router'; export const appRoutes: Route[] = [ - { - path: '', - component: NxWelcomeComponent - },]; + { + path: '', + component: NxWelcomeComponent, + }, +]; " `; @@ -199,19 +208,22 @@ Object { exports[`Host App Generator should generate a host app with a remote 1`] = ` "const { withModuleFederation } = require('@nrwl/angular/module-federation'); const config = require('./module-federation.config'); -module.exports = withModuleFederation(config);" +module.exports = withModuleFederation(config); +" `; exports[`Host App Generator should generate a host app with a remote 2`] = ` "const { withModuleFederation } = require('@nrwl/angular/module-federation'); const config = require('./module-federation.config'); -module.exports = withModuleFederation(config);" +module.exports = withModuleFederation(config); +" `; exports[`Host App Generator should generate a host app with no remotes 1`] = ` "const { withModuleFederation } = require('@nrwl/angular/module-federation'); const config = require('./module-federation.config'); -module.exports = withModuleFederation(config);" +module.exports = withModuleFederation(config); +" `; exports[`Host App Generator should generate a host with remotes using standalone components 1`] = ` @@ -230,8 +242,8 @@ bootstrapApplication(AppComponent, { `; exports[`Host App Generator should generate a host with remotes using standalone components 2`] = ` +"declare module 'remote1/Routes'; " -declare module 'remote1/Routes';" `; exports[`Host App Generator should generate a host with remotes using standalone components 3`] = ` @@ -292,7 +304,8 @@ describe('AppComponent', () => { const compiled = fixture.nativeElement as HTMLElement; expect(compiled.querySelector('h1')?.textContent).toContain('Welcome host'); })); -});" +}); +" `; exports[`Host App Generator should generate the correct app component spec file with a directory 1`] = ` @@ -333,7 +346,10 @@ describe('AppComponent', () => { tick(); fixture.detectChanges(); const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Welcome test-dashboard'); + expect(compiled.querySelector('h1')?.textContent).toContain( + 'Welcome test-dashboard' + ); })); -});" +}); +" `; diff --git a/packages/angular/src/generators/host/host.spec.ts b/packages/angular/src/generators/host/host.spec.ts index 6afc45ac61ca2..8800bd7ecf7cc 100644 --- a/packages/angular/src/generators/host/host.spec.ts +++ b/packages/angular/src/generators/host/host.spec.ts @@ -61,14 +61,13 @@ describe('Host App Generator', () => { expect(tree.exists('apps/remote2/project.json')).toBeTruthy(); expect( tree.read('apps/host-app/module-federation.config.js', 'utf-8') - ).toContain(`'remote1','remote2'`); + ).toContain(`'remote1', 'remote2'`); expect(tree.read('apps/host-app/src/app/app.component.html', 'utf-8')) .toMatchInlineSnapshot(` " " @@ -94,7 +93,7 @@ describe('Host App Generator', () => { expect(tree.exists('apps/remote3/project.json')).toBeTruthy(); expect( tree.read('apps/host-app/module-federation.config.js', 'utf-8') - ).toContain(`'remote1','remote2','remote3'`); + ).toContain(`'remote1', 'remote2', 'remote3'`); }); it('should generate a host, integrate existing remotes and generate any remotes that dont exist, in a directory', async () => { @@ -117,7 +116,7 @@ describe('Host App Generator', () => { expect(tree.exists('apps/foo/remote3/project.json')).toBeTruthy(); expect( tree.read('apps/foo/host-app/module-federation.config.js', 'utf-8') - ).toContain(`'remote1','foo-remote2','foo-remote3'`); + ).toContain(`'remote1', 'foo-remote2', 'foo-remote3'`); }); it('should generate a host with remotes using standalone components', async () => { diff --git a/packages/angular/src/generators/library-secondary-entry-point/__snapshots__/library-secondary-entry-point.spec.ts.snap b/packages/angular/src/generators/library-secondary-entry-point/__snapshots__/library-secondary-entry-point.spec.ts.snap index 5ac32181e1f48..8a260b3dbe72e 100644 --- a/packages/angular/src/generators/library-secondary-entry-point/__snapshots__/library-secondary-entry-point.spec.ts.snap +++ b/packages/angular/src/generators/library-secondary-entry-point/__snapshots__/library-secondary-entry-point.spec.ts.snap @@ -1,15 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`librarySecondaryEntryPoint generator --skipModule should not generate a module 1`] = ` -" -export const greeting = 'Hello World!'; - +"export const greeting = 'Hello World!'; " `; exports[`librarySecondaryEntryPoint generator should generate files for the secondary entry point 1`] = ` -" -export * from './lib/testing.module'; - +"export * from './lib/testing.module'; " `; diff --git a/packages/angular/src/generators/library/__snapshots__/library.spec.ts.snap b/packages/angular/src/generators/library/__snapshots__/library.spec.ts.snap index ab699cd741fbf..0d449b1ac06e0 100644 --- a/packages/angular/src/generators/library/__snapshots__/library.spec.ts.snap +++ b/packages/angular/src/generators/library/__snapshots__/library.spec.ts.snap @@ -1,6 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`lib --angular-14 should generate a library with a standalone component as entry point with angular 14.1.0 1`] = `"export * from \\"./lib/my-lib/my-lib.component\\";"`; +exports[`lib --angular-14 should generate a library with a standalone component as entry point with angular 14.1.0 1`] = ` +"export * from './lib/my-lib/my-lib.component'; +" +`; exports[`lib --angular-14 should generate a library with a standalone component as entry point with angular 14.1.0 2`] = ` "import { Component } from '@angular/core'; @@ -11,7 +14,7 @@ import { CommonModule } from '@angular/common'; standalone: true, imports: [CommonModule], templateUrl: './my-lib.component.html', - styleUrls: ['./my-lib.component.css'] + styleUrls: ['./my-lib.component.css'], }) export class MyLibComponent {} " @@ -27,9 +30,8 @@ describe('MyLibComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ MyLibComponent ] - }) - .compileComponents(); + imports: [MyLibComponent], + }).compileComponents(); fixture = TestBed.createComponent(MyLibComponent); component = fixture.componentInstance; @@ -43,7 +45,10 @@ describe('MyLibComponent', () => { " `; -exports[`lib --standalone should generate a library with a standalone component and have it flat 1`] = `"export * from \\"./lib/my-lib.component\\";"`; +exports[`lib --standalone should generate a library with a standalone component and have it flat 1`] = ` +"export * from './lib/my-lib.component'; +" +`; exports[`lib --standalone should generate a library with a standalone component and have it flat 2`] = ` "import { Component } from '@angular/core'; @@ -54,7 +59,7 @@ import { CommonModule } from '@angular/common'; standalone: true, imports: [CommonModule], templateUrl: './my-lib.component.html', - styleUrls: ['./my-lib.component.css'] + styleUrls: ['./my-lib.component.css'], }) export class MyLibComponent {} " @@ -70,9 +75,8 @@ describe('MyLibComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ MyLibComponent ] - }) - .compileComponents(); + imports: [MyLibComponent], + }).compileComponents(); fixture = TestBed.createComponent(MyLibComponent); component = fixture.componentInstance; @@ -89,7 +93,8 @@ describe('MyLibComponent', () => { exports[`lib --standalone should generate a library with a standalone component and have it flat with routing setup 1`] = ` "export * from './lib/lib.routes'; -export * from \\"./lib/my-lib.component\\";" +export * from './lib/my-lib.component'; +" `; exports[`lib --standalone should generate a library with a standalone component and have it flat with routing setup 2`] = ` @@ -101,7 +106,7 @@ import { CommonModule } from '@angular/common'; standalone: true, imports: [CommonModule], templateUrl: './my-lib.component.html', - styleUrls: ['./my-lib.component.css'] + styleUrls: ['./my-lib.component.css'], }) export class MyLibComponent {} " @@ -117,9 +122,8 @@ describe('MyLibComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ MyLibComponent ] - }) - .compileComponents(); + imports: [MyLibComponent], + }).compileComponents(); fixture = TestBed.createComponent(MyLibComponent); component = fixture.componentInstance; @@ -137,13 +141,14 @@ exports[`lib --standalone should generate a library with a standalone component "import { Route } from '@angular/router'; import { MyLibComponent } from './my-lib.component'; -export const myLibRoutes: Route[] = [ - {path: '', component: MyLibComponent} -]; +export const myLibRoutes: Route[] = [{ path: '', component: MyLibComponent }]; " `; -exports[`lib --standalone should generate a library with a standalone component as entry point 1`] = `"export * from \\"./lib/my-lib/my-lib.component\\";"`; +exports[`lib --standalone should generate a library with a standalone component as entry point 1`] = ` +"export * from './lib/my-lib/my-lib.component'; +" +`; exports[`lib --standalone should generate a library with a standalone component as entry point 2`] = ` "import { Component } from '@angular/core'; @@ -154,7 +159,7 @@ import { CommonModule } from '@angular/common'; standalone: true, imports: [CommonModule], templateUrl: './my-lib.component.html', - styleUrls: ['./my-lib.component.css'] + styleUrls: ['./my-lib.component.css'], }) export class MyLibComponent {} " @@ -170,9 +175,8 @@ describe('MyLibComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ MyLibComponent ] - }) - .compileComponents(); + imports: [MyLibComponent], + }).compileComponents(); fixture = TestBed.createComponent(MyLibComponent); component = fixture.componentInstance; @@ -186,10 +190,17 @@ describe('MyLibComponent', () => { " `; -exports[`lib --standalone should generate a library with a standalone component as entry point and set up view encapsulation and change detection 1`] = `"export * from \\"./lib/my-lib/my-lib.component\\";"`; +exports[`lib --standalone should generate a library with a standalone component as entry point and set up view encapsulation and change detection 1`] = ` +"export * from './lib/my-lib/my-lib.component'; +" +`; exports[`lib --standalone should generate a library with a standalone component as entry point and set up view encapsulation and change detection 2`] = ` -"import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core'; +"import { + ChangeDetectionStrategy, + Component, + ViewEncapsulation, +} from '@angular/core'; import { CommonModule } from '@angular/common'; @Component({ @@ -197,16 +208,18 @@ import { CommonModule } from '@angular/common'; standalone: true, imports: [CommonModule], template: \`

my-lib works!

\`, - styles: [ - ], + styles: [], encapsulation: ViewEncapsulation.ShadowDom, - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class MyLibComponent {} " `; -exports[`lib --standalone should generate a library with a standalone component as entry point and skip tests 1`] = `"export * from \\"./lib/my-lib/my-lib.component\\";"`; +exports[`lib --standalone should generate a library with a standalone component as entry point and skip tests 1`] = ` +"export * from './lib/my-lib/my-lib.component'; +" +`; exports[`lib --standalone should generate a library with a standalone component as entry point and skip tests 2`] = ` "import { Component } from '@angular/core'; @@ -217,14 +230,16 @@ import { CommonModule } from '@angular/common'; standalone: true, imports: [CommonModule], template: \`

my-lib works!

\`, - styles: [ - ] + styles: [], }) export class MyLibComponent {} " `; -exports[`lib --standalone should generate a library with a standalone component as entry point following SFC pattern 1`] = `"export * from \\"./lib/my-lib/my-lib.component\\";"`; +exports[`lib --standalone should generate a library with a standalone component as entry point following SFC pattern 1`] = ` +"export * from './lib/my-lib/my-lib.component'; +" +`; exports[`lib --standalone should generate a library with a standalone component as entry point following SFC pattern 2`] = ` "import { Component } from '@angular/core'; @@ -235,8 +250,7 @@ import { CommonModule } from '@angular/common'; standalone: true, imports: [CommonModule], template: \`

my-lib works!

\`, - styles: [ - ] + styles: [], }) export class MyLibComponent {} " @@ -252,9 +266,8 @@ describe('MyLibComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ MyLibComponent ] - }) - .compileComponents(); + imports: [MyLibComponent], + }).compileComponents(); fixture = TestBed.createComponent(MyLibComponent); component = fixture.componentInstance; @@ -271,16 +284,15 @@ describe('MyLibComponent', () => { exports[`lib --standalone should generate a library with a standalone component as entry point with routing setup 1`] = ` "export * from './lib/lib.routes'; -export * from \\"./lib/my-lib/my-lib.component\\";" +export * from './lib/my-lib/my-lib.component'; +" `; exports[`lib --standalone should generate a library with a standalone component as entry point with routing setup 2`] = ` "import { Route } from '@angular/router'; import { MyLibComponent } from './my-lib/my-lib.component'; -export const myLibRoutes: Route[] = [ - {path: '', component: MyLibComponent} -]; +export const myLibRoutes: Route[] = [{ path: '', component: MyLibComponent }]; " `; @@ -293,7 +305,7 @@ import { CommonModule } from '@angular/common'; standalone: true, imports: [CommonModule], templateUrl: './my-lib.component.html', - styleUrls: ['./my-lib.component.css'] + styleUrls: ['./my-lib.component.css'], }) export class MyLibComponent {} " @@ -309,9 +321,8 @@ describe('MyLibComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ MyLibComponent ] - }) - .compileComponents(); + imports: [MyLibComponent], + }).compileComponents(); fixture = TestBed.createComponent(MyLibComponent); component = fixture.componentInstance; @@ -328,16 +339,15 @@ describe('MyLibComponent', () => { exports[`lib --standalone should generate a library with a standalone component as entry point with routing setup and attach it to parent module as a lazy child 1`] = ` "export * from './lib/lib.routes'; -export * from \\"./lib/my-lib/my-lib.component\\";" +export * from './lib/my-lib/my-lib.component'; +" `; exports[`lib --standalone should generate a library with a standalone component as entry point with routing setup and attach it to parent module as a lazy child 2`] = ` "import { Route } from '@angular/router'; import { MyLibComponent } from './my-lib/my-lib.component'; -export const myLibRoutes: Route[] = [ - {path: '', component: MyLibComponent} -]; +export const myLibRoutes: Route[] = [{ path: '', component: MyLibComponent }]; " `; @@ -345,23 +355,26 @@ exports[`lib --standalone should generate a library with a standalone component "import { Route } from '@angular/router'; export const appRoutes: Route[] = [ - {path: 'my-lib', loadChildren: () => import('@proj/my-lib').then(m => m.myLibRoutes)},]; + { + path: 'my-lib', + loadChildren: () => import('@proj/my-lib').then((m) => m.myLibRoutes), + }, +]; " `; exports[`lib --standalone should generate a library with a standalone component as entry point with routing setup and attach it to parent module as direct child 1`] = ` "export * from './lib/lib.routes'; -export * from \\"./lib/my-lib/my-lib.component\\";" +export * from './lib/my-lib/my-lib.component'; +" `; exports[`lib --standalone should generate a library with a standalone component as entry point with routing setup and attach it to parent module as direct child 2`] = ` "import { Route } from '@angular/router'; import { MyLibComponent } from './my-lib/my-lib.component'; -export const myLibRoutes: Route[] = [ - {path: '', component: MyLibComponent} -]; +export const myLibRoutes: Route[] = [{ path: '', component: MyLibComponent }]; " `; @@ -369,8 +382,7 @@ exports[`lib --standalone should generate a library with a standalone component "import { Route } from '@angular/router'; import { myLibRoutes } from '@proj/my-lib'; -export const appRoutes: Route[] = [ - { path: 'my-lib', children: myLibRoutes },]; +export const appRoutes: Route[] = [{ path: 'my-lib', children: myLibRoutes }]; " `; @@ -379,8 +391,11 @@ exports[`lib --standalone should generate a library with a standalone component import { MyLibComponent } from './my-lib/my-lib.component'; export const myLibRoutes: Route[] = [ - {path: 'second', loadChildren: () => import('@proj/second').then(m => m.secondRoutes)}, - {path: '', component: MyLibComponent} + { + path: 'second', + loadChildren: () => import('@proj/second').then((m) => m.secondRoutes), + }, + { path: '', component: MyLibComponent }, ]; " `; @@ -391,13 +406,16 @@ import { MyLibComponent } from './my-lib/my-lib.component'; import { secondRoutes } from '@proj/second'; export const myLibRoutes: Route[] = [ - { path: 'second', children: secondRoutes }, - {path: '', component: MyLibComponent} + { path: 'second', children: secondRoutes }, + { path: '', component: MyLibComponent }, ]; " `; -exports[`lib --standalone should generate a library with a standalone component in a directory 1`] = `"export * from \\"./lib/my-dir-my-lib/my-dir-my-lib.component\\";"`; +exports[`lib --standalone should generate a library with a standalone component in a directory 1`] = ` +"export * from './lib/my-dir-my-lib/my-dir-my-lib.component'; +" +`; exports[`lib --standalone should generate a library with a standalone component in a directory 2`] = ` "import { Component } from '@angular/core'; @@ -408,7 +426,7 @@ import { CommonModule } from '@angular/common'; standalone: true, imports: [CommonModule], templateUrl: './my-dir-my-lib.component.html', - styleUrls: ['./my-dir-my-lib.component.css'] + styleUrls: ['./my-dir-my-lib.component.css'], }) export class MyDirMyLibComponent {} " @@ -424,9 +442,8 @@ describe('MyDirMyLibComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ MyDirMyLibComponent ] - }) - .compileComponents(); + imports: [MyDirMyLibComponent], + }).compileComponents(); fixture = TestBed.createComponent(MyDirMyLibComponent); component = fixture.componentInstance; @@ -440,7 +457,10 @@ describe('MyDirMyLibComponent', () => { " `; -exports[`lib --standalone should generate a library with a standalone component in a directory with a simple name 1`] = `"export * from \\"./lib/my-lib/my-lib.component\\";"`; +exports[`lib --standalone should generate a library with a standalone component in a directory with a simple name 1`] = ` +"export * from './lib/my-lib/my-lib.component'; +" +`; exports[`lib --standalone should generate a library with a standalone component in a directory with a simple name 2`] = ` "import { Component } from '@angular/core'; @@ -451,7 +471,7 @@ import { CommonModule } from '@angular/common'; standalone: true, imports: [CommonModule], templateUrl: './my-lib.component.html', - styleUrls: ['./my-lib.component.css'] + styleUrls: ['./my-lib.component.css'], }) export class MyLibComponent {} " @@ -467,9 +487,8 @@ describe('MyLibComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ MyLibComponent ] - }) - .compileComponents(); + imports: [MyLibComponent], + }).compileComponents(); fixture = TestBed.createComponent(MyLibComponent); component = fixture.componentInstance; @@ -482,3 +501,82 @@ describe('MyLibComponent', () => { }); " `; + +exports[`lib router lazy should update the parent module 1`] = ` +"import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { RouterModule } from '@angular/router'; +import { AppComponent } from './app.component'; +@NgModule({ + imports: [ + BrowserModule, + RouterModule.forRoot([ + { + path: 'my-dir-my-lib', + loadChildren: () => + import('@proj/my-dir/my-lib').then((m) => m.MyDirMyLibModule), + }, + ]), + ], + declarations: [AppComponent], + bootstrap: [AppComponent], +}) +export class AppModule {} +" +`; + +exports[`lib router lazy should update the parent module 3`] = ` +"import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { RouterModule } from '@angular/router'; +import { AppComponent } from './app.component'; +@NgModule({ + imports: [ + BrowserModule, + RouterModule.forRoot([ + { + path: 'my-dir-my-lib', + loadChildren: () => + import('@proj/my-dir/my-lib').then((m) => m.MyDirMyLibModule), + }, + { + path: 'my-lib2', + loadChildren: () => + import('@proj/my-dir/my-lib2').then((m) => m.MyLib2Module), + }, + { + path: 'my-lib3', + loadChildren: () => + import('@proj/my-dir/my-lib3').then((m) => m.MyLib3Module), + }, + ]), + ], + declarations: [AppComponent], + bootstrap: [AppComponent], +}) +export class AppModule {} +" +`; + +exports[`lib router lazy should update the parent module even if the route is declared outside the .forRoot(...) 1`] = ` +"import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { RouterModule } from '@angular/router'; +import { AppComponent } from './app.component'; + +const routes = [ + { + path: 'my-dir-my-lib', + loadChildren: () => + import('@proj/my-dir/my-lib').then((m) => m.MyDirMyLibModule), + }, +]; + +@NgModule({ + imports: [BrowserModule, RouterModule.forRoot(routes)], + declarations: [AppComponent], + bootstrap: [AppComponent], +}) +export class AppModule {} +" +`; diff --git a/packages/angular/src/generators/library/library.spec.ts b/packages/angular/src/generators/library/library.spec.ts index 26a905c207d83..c5050ef72893a 100644 --- a/packages/angular/src/generators/library/library.spec.ts +++ b/packages/angular/src/generators/library/library.spec.ts @@ -158,10 +158,7 @@ describe('lib', () => { ); expect(moduleFileExists).toBeFalsy(); const indexApi = tree.read('libs/my-lib/src/index.ts', 'utf-8'); - expect(indexApi).toMatchInlineSnapshot(` - " - " - `); + expect(indexApi).toMatchInlineSnapshot(`""`); }); it('should remove "build" target from project.json when a library is not publishable', async () => { @@ -739,10 +736,7 @@ describe('lib', () => { ); // ASSERT - expect(moduleContents).toContain('RouterModule.forRoot(['); - expect(moduleContents).toContain( - `{path: 'my-dir-my-lib', loadChildren: () => import('@proj/my-dir/my-lib').then(m => m.MyDirMyLibModule)}` - ); + expect(moduleContents).toMatchSnapshot(); expect(tsConfigLibJson.exclude).toEqual([ 'src/**/*.spec.ts', @@ -751,13 +745,33 @@ describe('lib', () => { 'src/**/*.test.ts', ]); - expect(moduleContents2).toContain('RouterModule.forRoot(['); - expect(moduleContents2).toContain( - `{path: 'my-dir-my-lib', loadChildren: () => import('@proj/my-dir/my-lib').then(m => m.MyDirMyLibModule)}` - ); - expect(moduleContents2).toContain( - `{path: 'my-lib2', loadChildren: () => import('@proj/my-dir/my-lib2').then(m => m.MyLib2Module)}` - ); + expect(moduleContents2).toMatchInlineSnapshot(` + "import { NgModule } from '@angular/core'; + import { BrowserModule } from '@angular/platform-browser'; + import { RouterModule } from '@angular/router'; + import { AppComponent } from './app.component'; + @NgModule({ + imports: [ + BrowserModule, + RouterModule.forRoot([ + { + path: 'my-dir-my-lib', + loadChildren: () => + import('@proj/my-dir/my-lib').then((m) => m.MyDirMyLibModule), + }, + { + path: 'my-lib2', + loadChildren: () => + import('@proj/my-dir/my-lib2').then((m) => m.MyLib2Module), + }, + ]), + ], + declarations: [AppComponent], + bootstrap: [AppComponent], + }) + export class AppModule {} + " + `); expect(tsConfigLibJson2.exclude).toEqual([ 'src/**/*.spec.ts', @@ -766,16 +780,7 @@ describe('lib', () => { 'src/**/*.test.ts', ]); - expect(moduleContents3).toContain('RouterModule.forRoot(['); - expect(moduleContents3).toContain( - `{path: 'my-dir-my-lib', loadChildren: () => import('@proj/my-dir/my-lib').then(m => m.MyDirMyLibModule)}` - ); - expect(moduleContents3).toContain( - `{path: 'my-lib2', loadChildren: () => import('@proj/my-dir/my-lib2').then(m => m.MyLib2Module)}` - ); - expect(moduleContents3).toContain( - `{path: 'my-lib3', loadChildren: () => import('@proj/my-dir/my-lib3').then(m => m.MyLib3Module)}` - ); + expect(moduleContents3).toMatchSnapshot(); expect(tsConfigLibJson3.exclude).toEqual([ 'src/**/*.spec.ts', @@ -820,10 +825,7 @@ describe('lib', () => { .read('apps/myapp/src/app/app.module.ts') .toString(); - expect(moduleContents).toContain('RouterModule.forRoot(routes)'); - expect(moduleContents).toContain( - `const routes = [{path: 'my-dir-my-lib', loadChildren: () => import('@proj/my-dir/my-lib').then(m => m.MyDirMyLibModule)}];` - ); + expect(moduleContents).toMatchSnapshot(); }); }); @@ -1472,8 +1474,7 @@ describe('lib', () => { "import { Route } from '@angular/router'; import { myLibRoutes } from '@proj/my-lib'; - export const appRoutes: Route[] = [ - { path: 'my-lib', children: myLibRoutes },]; + export const appRoutes: Route[] = [{ path: 'my-lib', children: myLibRoutes }]; " `); }); @@ -1500,7 +1501,11 @@ describe('lib', () => { "import { Route } from '@angular/router'; export const appRoutes: Route[] = [ - {path: 'my-lib', loadChildren: () => import('@proj/my-lib').then(m => m.myLibRoutes)},]; + { + path: 'my-lib', + loadChildren: () => import('@proj/my-lib').then((m) => m.myLibRoutes), + }, + ]; " `); }); diff --git a/packages/angular/src/generators/ngrx/__snapshots__/ngrx.spec.ts.snap b/packages/angular/src/generators/ngrx/__snapshots__/ngrx.spec.ts.snap index cb8a076091066..4503545631e00 100644 --- a/packages/angular/src/generators/ngrx/__snapshots__/ngrx.spec.ts.snap +++ b/packages/angular/src/generators/ngrx/__snapshots__/ngrx.spec.ts.snap @@ -21,7 +21,7 @@ describe('SuperUsersEffects', () => { providers: [ SuperUsersEffects, provideMockActions(() => actions), - provideMockStore() + provideMockStore(), ], }); @@ -32,7 +32,9 @@ describe('SuperUsersEffects', () => { it('should work', () => { actions = hot('-a-|', { a: SuperUsersActions.initSuperUsers() }); - const expected = hot('-a-|', { a: SuperUsersActions.loadSuperUsersSuccess({ superUsers: [] }) }); + const expected = hot('-a-|', { + a: SuperUsersActions.loadSuperUsersSuccess({ superUsers: [] }), + }); expect(effects.init$).toBeObservable(expected); }); @@ -56,7 +58,7 @@ import { SUPER_USERS_FEATURE_KEY, SuperUsersState, initialSuperUsersState, - superUsersReducer + superUsersReducer, } from './super-users.reducer'; import * as SuperUsersSelectors from './super-users.selectors'; @@ -69,7 +71,7 @@ describe('SuperUsersFacade', () => { let store: Store; const createSuperUsersEntity = (id: string, name = ''): SuperUsersEntity => ({ id, - name: name || \`name-\${id}\` + name: name || \`name-\${id}\`, }); describe('used in NgModule', () => { @@ -77,9 +79,9 @@ describe('SuperUsersFacade', () => { @NgModule({ imports: [ StoreModule.forFeature(SUPER_USERS_FEATURE_KEY, superUsersReducer), - EffectsModule.forFeature([SuperUsersEffects]) + EffectsModule.forFeature([SuperUsersEffects]), ], - providers: [SuperUsersFacade] + providers: [SuperUsersFacade], }) class CustomFeatureModule {} @@ -88,7 +90,7 @@ describe('SuperUsersFacade', () => { StoreModule.forRoot({}), EffectsModule.forRoot([]), CustomFeatureModule, - ] + ], }) class RootModule {} TestBed.configureTestingModule({ imports: [RootModule] }); @@ -126,11 +128,13 @@ describe('SuperUsersFacade', () => { expect(list.length).toBe(0); expect(isLoaded).toBe(false); - store.dispatch(SuperUsersActions.loadSuperUsersSuccess({ - superUsers: [ - createSuperUsersEntity('AAA'), - createSuperUsersEntity('BBB') - ]}) + store.dispatch( + SuperUsersActions.loadSuperUsersSuccess({ + superUsers: [ + createSuperUsersEntity('AAA'), + createSuperUsersEntity('BBB'), + ], + }) ); list = await readFirst(facade.allSuperUsers$); @@ -149,23 +153,30 @@ exports[`ngrx NgModule Syntax generated unit tests should generate specs for the import * as SuperUsersActions from './super-users.actions'; import { SuperUsersEntity } from './super-users.models'; -import { SuperUsersState, initialSuperUsersState, superUsersReducer } from './super-users.reducer'; +import { + SuperUsersState, + initialSuperUsersState, + superUsersReducer, +} from './super-users.reducer'; describe('SuperUsers Reducer', () => { const createSuperUsersEntity = (id: string, name = ''): SuperUsersEntity => ({ id, - name: name || \`name-\${id}\` + name: name || \`name-\${id}\`, }); describe('valid SuperUsers actions', () => { it('loadSuperUsersSuccess should return the list of known SuperUsers', () => { const superUsers = [ createSuperUsersEntity('PRODUCT-AAA'), - createSuperUsersEntity('PRODUCT-zzz') + createSuperUsersEntity('PRODUCT-zzz'), ]; const action = SuperUsersActions.loadSuperUsersSuccess({ superUsers }); - const result: SuperUsersState = superUsersReducer(initialSuperUsersState, action); + const result: SuperUsersState = superUsersReducer( + initialSuperUsersState, + action + ); expect(result.loaded).toBe(true); expect(result.ids.length).toBe(2); @@ -187,31 +198,39 @@ describe('SuperUsers Reducer', () => { exports[`ngrx NgModule Syntax generated unit tests should generate specs for the ngrx selectors 1`] = ` "import { SuperUsersEntity } from './super-users.models'; -import { superUsersAdapter, SuperUsersPartialState, initialSuperUsersState } from './super-users.reducer'; +import { + superUsersAdapter, + SuperUsersPartialState, + initialSuperUsersState, +} from './super-users.reducer'; import * as SuperUsersSelectors from './super-users.selectors'; describe('SuperUsers Selectors', () => { const ERROR_MSG = 'No Error Available'; const getSuperUsersId = (it: SuperUsersEntity) => it.id; - const createSuperUsersEntity = (id: string, name = '') => ({ - id, - name: name || \`name-\${id}\` - }) as SuperUsersEntity; + const createSuperUsersEntity = (id: string, name = '') => + ({ + id, + name: name || \`name-\${id}\`, + } as SuperUsersEntity); let state: SuperUsersPartialState; beforeEach(() => { state = { - superUsers: superUsersAdapter.setAll([ - createSuperUsersEntity('PRODUCT-AAA'), - createSuperUsersEntity('PRODUCT-BBB'), - createSuperUsersEntity('PRODUCT-CCC') - ], { - ...initialSuperUsersState, - selectedId : 'PRODUCT-BBB', - error: ERROR_MSG, - loaded: true - }) + superUsers: superUsersAdapter.setAll( + [ + createSuperUsersEntity('PRODUCT-AAA'), + createSuperUsersEntity('PRODUCT-BBB'), + createSuperUsersEntity('PRODUCT-CCC'), + ], + { + ...initialSuperUsersState, + selectedId: 'PRODUCT-BBB', + error: ERROR_MSG, + loaded: true, + } + ), }; }); @@ -225,7 +244,9 @@ describe('SuperUsers Selectors', () => { }); it('selectEntity() should return the selected Entity', () => { - const result = SuperUsersSelectors.selectEntity(state) as SuperUsersEntity; + const result = SuperUsersSelectors.selectEntity( + state + ) as SuperUsersEntity; const selId = getSuperUsersId(result); expect(selId).toBe('PRODUCT-BBB'); @@ -248,79 +269,105 @@ describe('SuperUsers Selectors', () => { `; exports[`ngrx NgModule Syntax should add a root module with feature module when minimal is set to false 1`] = ` -" - import { NgModule } from '@angular/core'; - import { BrowserModule } from '@angular/platform-browser'; - import { RouterModule } from '@angular/router'; - import { AppComponent } from './app.component'; +"import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { RouterModule } from '@angular/router'; +import { AppComponent } from './app.component'; import { StoreModule } from '@ngrx/store'; import { EffectsModule } from '@ngrx/effects'; import * as fromUsers from './+state/users.reducer'; import { UsersEffects } from './+state/users.effects'; import { StoreRouterConnectingModule } from '@ngrx/router-store'; - @NgModule({ - imports: [BrowserModule, RouterModule.forRoot([]), StoreModule.forRoot({}, { - metaReducers: [], - runtimeChecks: { - strictActionImmutability: true, - strictStateImmutability: true +@NgModule({ + imports: [ + BrowserModule, + RouterModule.forRoot([]), + StoreModule.forRoot( + {}, + { + metaReducers: [], + runtimeChecks: { + strictActionImmutability: true, + strictStateImmutability: true, + }, } - }), EffectsModule.forRoot([UsersEffects]), StoreRouterConnectingModule.forRoot(), StoreModule.forFeature(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer)], - declarations: [AppComponent], - bootstrap: [AppComponent] - }) - export class AppModule {} - " + ), + EffectsModule.forRoot([UsersEffects]), + StoreRouterConnectingModule.forRoot(), + StoreModule.forFeature(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer), + ], + declarations: [AppComponent], + bootstrap: [AppComponent], +}) +export class AppModule {} +" `; exports[`ngrx NgModule Syntax should add a root module with feature module when minimal is set to false using --module 1`] = ` -" - import { NgModule } from '@angular/core'; - import { BrowserModule } from '@angular/platform-browser'; - import { RouterModule } from '@angular/router'; - import { AppComponent } from './app.component'; +"import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { RouterModule } from '@angular/router'; +import { AppComponent } from './app.component'; import { StoreModule } from '@ngrx/store'; import { EffectsModule } from '@ngrx/effects'; import * as fromUsers from './+state/users.reducer'; import { UsersEffects } from './+state/users.effects'; import { StoreRouterConnectingModule } from '@ngrx/router-store'; - @NgModule({ - imports: [BrowserModule, RouterModule.forRoot([]), StoreModule.forRoot({}, { - metaReducers: [], - runtimeChecks: { - strictActionImmutability: true, - strictStateImmutability: true +@NgModule({ + imports: [ + BrowserModule, + RouterModule.forRoot([]), + StoreModule.forRoot( + {}, + { + metaReducers: [], + runtimeChecks: { + strictActionImmutability: true, + strictStateImmutability: true, + }, } - }), EffectsModule.forRoot([UsersEffects]), StoreRouterConnectingModule.forRoot(), StoreModule.forFeature(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer)], - declarations: [AppComponent], - bootstrap: [AppComponent] - }) - export class AppModule {} - " + ), + EffectsModule.forRoot([UsersEffects]), + StoreRouterConnectingModule.forRoot(), + StoreModule.forFeature(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer), + ], + declarations: [AppComponent], + bootstrap: [AppComponent], +}) +export class AppModule {} +" `; exports[`ngrx NgModule Syntax should add an empty root module when minimal and root are set to true 1`] = ` -" - import { NgModule } from '@angular/core'; - import { BrowserModule } from '@angular/platform-browser'; - import { RouterModule } from '@angular/router'; - import { AppComponent } from './app.component'; +"import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { RouterModule } from '@angular/router'; +import { AppComponent } from './app.component'; import { StoreModule } from '@ngrx/store'; import { EffectsModule } from '@ngrx/effects'; import { StoreRouterConnectingModule } from '@ngrx/router-store'; - @NgModule({ - imports: [BrowserModule, RouterModule.forRoot([]), StoreModule.forRoot({}, { - metaReducers: [], - runtimeChecks: { - strictActionImmutability: true, - strictStateImmutability: true +@NgModule({ + imports: [ + BrowserModule, + RouterModule.forRoot([]), + StoreModule.forRoot( + {}, + { + metaReducers: [], + runtimeChecks: { + strictActionImmutability: true, + strictStateImmutability: true, + }, } - }), EffectsModule.forRoot([]), StoreRouterConnectingModule.forRoot()], - declarations: [AppComponent], - bootstrap: [AppComponent] - }) - export class AppModule {} - " + ), + EffectsModule.forRoot([]), + StoreRouterConnectingModule.forRoot(), + ], + declarations: [AppComponent], + bootstrap: [AppComponent], +}) +export class AppModule {} +" `; exports[`ngrx NgModule Syntax should generate a models file for the feature 1`] = ` @@ -330,16 +377,15 @@ exports[`ngrx NgModule Syntax should generate a models file for the feature 1`] export interface UsersEntity { id: string | number; // Primary ID name: string; -};" +} +" `; exports[`ngrx NgModule Syntax should generate the ngrx actions 1`] = ` "import { createAction, props } from '@ngrx/store'; import { UsersEntity } from './users.models'; -export const initUsers = createAction( - '[Users Page] Init' -); +export const initUsers = createAction('[Users Page] Init'); export const loadUsersSuccess = createAction( '[Users/API] Load Users Success', @@ -360,21 +406,22 @@ import { createEffect, Actions, ofType } from '@ngrx/effects'; import * as UsersActions from './users.actions'; import * as UsersFeature from './users.reducer'; -import {switchMap, catchError, of} from 'rxjs'; +import { switchMap, catchError, of } from 'rxjs'; @Injectable() export class UsersEffects { private actions$ = inject(Actions); - init$ = createEffect(() => this.actions$.pipe( - ofType(UsersActions.initUsers), - switchMap(() => of(UsersActions.loadUsersSuccess({ users: [] }))), - catchError((error) => { + init$ = createEffect(() => + this.actions$.pipe( + ofType(UsersActions.initUsers), + switchMap(() => of(UsersActions.loadUsersSuccess({ users: [] }))), + catchError((error) => { console.error('Error', error); return of(UsersActions.loadUsersFailure({ error })); - } + }) ) - )); + ); } " `; @@ -429,24 +476,25 @@ export interface UsersPartialState { readonly [USERS_FEATURE_KEY]: UsersState; } -export const usersAdapter: EntityAdapter = createEntityAdapter(); +export const usersAdapter: EntityAdapter = + createEntityAdapter(); export const initialUsersState: UsersState = usersAdapter.getInitialState({ // set initial required properties - loaded: false + loaded: false, }); const reducer = createReducer( initialUsersState, - on(UsersActions.initUsers, - state => ({ ...state, loaded: false, error: null }) - ), - on(UsersActions.loadUsersSuccess, - (state, { users }) => usersAdapter.setAll(users, { ...state, loaded: true }) - ), - on(UsersActions.loadUsersFailure, - (state, { error }) => ({ ...state, error }) + on(UsersActions.initUsers, (state) => ({ + ...state, + loaded: false, + error: null, + })), + on(UsersActions.loadUsersSuccess, (state, { users }) => + usersAdapter.setAll(users, { ...state, loaded: true }) ), + on(UsersActions.loadUsersFailure, (state, { error }) => ({ ...state, error })) ); export function usersReducer(state: UsersState | undefined, action: Action) { @@ -460,7 +508,8 @@ exports[`ngrx NgModule Syntax should generate the ngrx selectors 1`] = ` import { USERS_FEATURE_KEY, UsersState, usersAdapter } from './users.reducer'; // Lookup the 'Users' feature state managed by NgRx -export const selectUsersState = createFeatureSelector(USERS_FEATURE_KEY); +export const selectUsersState = + createFeatureSelector(USERS_FEATURE_KEY); const { selectAll, selectEntities } = usersAdapter.getSelectors(); @@ -498,18 +547,17 @@ export const selectEntity = createSelector( `; exports[`ngrx NgModule Syntax should not generate imports when skipImport is true 1`] = ` +"import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { RouterModule } from '@angular/router'; +import { AppComponent } from './app.component'; +@NgModule({ + imports: [BrowserModule, RouterModule.forRoot([])], + declarations: [AppComponent], + bootstrap: [AppComponent], +}) +export class AppModule {} " - import { NgModule } from '@angular/core'; - import { BrowserModule } from '@angular/platform-browser'; - import { RouterModule } from '@angular/router'; - import { AppComponent } from './app.component'; - @NgModule({ - imports: [BrowserModule, RouterModule.forRoot([])], - declarations: [AppComponent], - bootstrap: [AppComponent] - }) - export class AppModule {} - " `; exports[`ngrx NgModule Syntax should update the entry point file correctly when barrels is true 1`] = ` @@ -525,8 +573,8 @@ export * from './lib/+state/super-users.models'; export { SuperUsersActions, SuperUsersFeature, SuperUsersSelectors }; - export * from './lib/flights.module'; - " +export * from './lib/flights.module'; +" `; exports[`ngrx NgModule Syntax should update the entry point file with no facade 1`] = ` @@ -535,8 +583,8 @@ export * from './lib/+state/super-users.selectors'; export * from './lib/+state/super-users.reducer'; export * from './lib/+state/super-users.actions'; - export * from './lib/flights.module'; - " +export * from './lib/flights.module'; +" `; exports[`ngrx NgModule Syntax should update the entry point file with the right exports 1`] = ` @@ -546,38 +594,68 @@ export * from './lib/+state/super-users.selectors'; export * from './lib/+state/super-users.reducer'; export * from './lib/+state/super-users.actions'; - export * from './lib/flights.module'; - " +export * from './lib/flights.module'; +" `; exports[`ngrx Standalone APIs should add a feature module when route is non-empty 1`] = ` "import { Routes } from '@angular/router'; - import { NxWelcomeComponent } from './nx-welcome.component'; +import { NxWelcomeComponent } from './nx-welcome.component'; import { provideStore, provideState } from '@ngrx/store'; import { provideEffects } from '@ngrx/effects'; import * as fromUsers from './+state/users.reducer'; -import { UsersEffects } from './+state/users.effects'; - export const appRoutes: Routes = [{ path: 'home', component: NxWelcomeComponent , providers: [provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer), provideEffects(UsersEffects)]}];" +import { UsersEffects } from './+state/users.effects'; +export const appRoutes: Routes = [ + { + path: 'home', + component: NxWelcomeComponent, + providers: [ + provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer), + provideEffects(UsersEffects), + ], + }, +]; +" `; exports[`ngrx Standalone APIs should add a feature module when route is set to default 1`] = ` "import { Routes } from '@angular/router'; - import { NxWelcomeComponent } from './nx-welcome.component'; +import { NxWelcomeComponent } from './nx-welcome.component'; import { provideStore, provideState } from '@ngrx/store'; import { provideEffects } from '@ngrx/effects'; import * as fromUsers from './+state/users.reducer'; -import { UsersEffects } from './+state/users.effects'; - export const appRoutes: Routes = [{ path: '', component: NxWelcomeComponent , providers: [provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer), provideEffects(UsersEffects)]}];" +import { UsersEffects } from './+state/users.effects'; +export const appRoutes: Routes = [ + { + path: '', + component: NxWelcomeComponent, + providers: [ + provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer), + provideEffects(UsersEffects), + ], + }, +]; +" `; exports[`ngrx Standalone APIs should add a feature module when route is undefined 1`] = ` "import { Routes } from '@angular/router'; - import { NxWelcomeComponent } from './nx-welcome.component'; +import { NxWelcomeComponent } from './nx-welcome.component'; import { provideStore, provideState } from '@ngrx/store'; import { provideEffects } from '@ngrx/effects'; import * as fromUsers from './+state/users.reducer'; -import { UsersEffects } from './+state/users.effects'; - export const appRoutes: Routes = [{ path: '', component: NxWelcomeComponent , providers: [provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer), provideEffects(UsersEffects)]}];" +import { UsersEffects } from './+state/users.effects'; +export const appRoutes: Routes = [ + { + path: '', + component: NxWelcomeComponent, + providers: [ + provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer), + provideEffects(UsersEffects), + ], + }, +]; +" `; exports[`ngrx Standalone APIs should add a root module with feature module when minimal is set to false 1`] = ` @@ -594,7 +672,13 @@ import * as fromUsers from './+state/users.reducer'; import { UsersEffects } from './+state/users.effects'; bootstrapApplication(AppComponent, { - providers: [provideEffects(UsersEffects),provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),provideEffects(),provideStore(),provideRouter(appRoutes, withEnabledBlockingInitialNavigation())], + providers: [ + provideEffects(UsersEffects), + provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer), + provideEffects(), + provideStore(), + provideRouter(appRoutes, withEnabledBlockingInitialNavigation()), + ], }).catch((err) => console.error(err)); " `; @@ -611,7 +695,11 @@ import { provideStore, provideState } from '@ngrx/store'; import { provideEffects } from '@ngrx/effects'; bootstrapApplication(AppComponent, { - providers: [provideEffects(),provideStore(),provideRouter(appRoutes, withEnabledBlockingInitialNavigation())], + providers: [ + provideEffects(), + provideStore(), + provideRouter(appRoutes, withEnabledBlockingInitialNavigation()), + ], }).catch((err) => console.error(err)); " `; @@ -631,20 +719,38 @@ import { UsersEffects } from './+state/users.effects'; import { UsersFacade } from './+state/users.facade'; bootstrapApplication(AppComponent, { - providers: [provideEffects(UsersEffects),provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),provideEffects(),provideStore(),UsersFacade,provideRouter(appRoutes, withEnabledBlockingInitialNavigation())], + providers: [ + provideEffects(UsersEffects), + provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer), + provideEffects(), + provideStore(), + UsersFacade, + provideRouter(appRoutes, withEnabledBlockingInitialNavigation()), + ], }).catch((err) => console.error(err)); " `; exports[`ngrx Standalone APIs should add facade provider when facade is true and --root is false 1`] = ` "import { Routes } from '@angular/router'; - import { NxWelcomeComponent } from './nx-welcome.component'; +import { NxWelcomeComponent } from './nx-welcome.component'; import { provideStore, provideState } from '@ngrx/store'; import { provideEffects } from '@ngrx/effects'; import * as fromUsers from './+state/users.reducer'; import { UsersEffects } from './+state/users.effects'; -import { UsersFacade } from './+state/users.facade'; - export const appRoutes: Routes = [{ path: '', component: NxWelcomeComponent , providers: [UsersFacade, provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer), provideEffects(UsersEffects)]}];" +import { UsersFacade } from './+state/users.facade'; +export const appRoutes: Routes = [ + { + path: '', + component: NxWelcomeComponent, + providers: [ + UsersFacade, + provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer), + provideEffects(UsersEffects), + ], + }, +]; +" `; exports[`ngrx angular v14 support should generate the ngrx effects using "inject" for versions >= 14.1.0 1`] = ` @@ -654,21 +760,22 @@ import { createEffect, Actions, ofType } from '@ngrx/effects'; import * as UsersActions from './users.actions'; import * as UsersFeature from './users.reducer'; -import {switchMap, catchError, of} from 'rxjs'; +import { switchMap, catchError, of } from 'rxjs'; @Injectable() export class UsersEffects { private actions$ = inject(Actions); - init$ = createEffect(() => this.actions$.pipe( - ofType(UsersActions.initUsers), - switchMap(() => of(UsersActions.loadUsersSuccess({ users: [] }))), - catchError((error) => { + init$ = createEffect(() => + this.actions$.pipe( + ofType(UsersActions.initUsers), + switchMap(() => of(UsersActions.loadUsersSuccess({ users: [] }))), + catchError((error) => { console.error('Error', error); return of(UsersActions.loadUsersFailure({ error })); - } + }) ) - )); + ); } " `; @@ -680,19 +787,20 @@ import { createEffect, Actions, ofType } from '@ngrx/effects'; import * as UsersActions from './users.actions'; import * as UsersFeature from './users.reducer'; -import {switchMap, catchError, of} from 'rxjs'; +import { switchMap, catchError, of } from 'rxjs'; @Injectable() export class UsersEffects { - init$ = createEffect(() => this.actions$.pipe( - ofType(UsersActions.initUsers), - switchMap(() => of(UsersActions.loadUsersSuccess({ users: [] }))), - catchError((error) => { + init$ = createEffect(() => + this.actions$.pipe( + ofType(UsersActions.initUsers), + switchMap(() => of(UsersActions.loadUsersSuccess({ users: [] }))), + catchError((error) => { console.error('Error', error); return of(UsersActions.loadUsersFailure({ error })); - } + }) ) - )); + ); constructor(private readonly actions$: Actions) {} } diff --git a/packages/angular/src/generators/pipe/__snapshots__/pipe.spec.ts.snap b/packages/angular/src/generators/pipe/__snapshots__/pipe.spec.ts.snap index c8b4ff1ff739e..3e6b4d5da814b 100644 --- a/packages/angular/src/generators/pipe/__snapshots__/pipe.spec.ts.snap +++ b/packages/angular/src/generators/pipe/__snapshots__/pipe.spec.ts.snap @@ -4,14 +4,12 @@ exports[`pipe generator should export the pipe correctly when flat=false and pat "import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ - name: 'test' + name: 'test', }) export class TestPipe implements PipeTransform { - transform(value: unknown, ...args: unknown[]): unknown { return null; } - } " `; @@ -29,28 +27,27 @@ describe('TestPipe', () => { `; exports[`pipe generator should export the pipe correctly when flat=false and path is nested deeper 3`] = ` -"import {NgModule} from \\"@angular/core\\"; +"import { NgModule } from '@angular/core'; import { TestPipe } from './my-pipes/test/test.pipe'; - @NgModule({ - imports: [], - declarations: [, TestPipe], - exports: [, TestPipe] - }) - export class TestModule {}" +@NgModule({ + imports: [], + declarations: [, TestPipe], + exports: [, TestPipe], +}) +export class TestModule {} +" `; exports[`pipe generator should generate a pipe with test files and attach to the NgModule automatically 1`] = ` "import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ - name: 'test' + name: 'test', }) export class TestPipe implements PipeTransform { - transform(value: unknown, ...args: unknown[]): unknown { return null; } - } " `; @@ -68,28 +65,27 @@ describe('TestPipe', () => { `; exports[`pipe generator should generate a pipe with test files and attach to the NgModule automatically 3`] = ` -"import {NgModule} from \\"@angular/core\\"; +"import { NgModule } from '@angular/core'; import { TestPipe } from './test.pipe'; - @NgModule({ - imports: [], - declarations: [, TestPipe], - exports: [] - }) - export class TestModule {}" +@NgModule({ + imports: [], + declarations: [, TestPipe], + exports: [], +}) +export class TestModule {} +" `; exports[`pipe generator should import the pipe correctly when flat=false 1`] = ` "import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ - name: 'test' + name: 'test', }) export class TestPipe implements PipeTransform { - transform(value: unknown, ...args: unknown[]): unknown { return null; } - } " `; @@ -107,28 +103,27 @@ describe('TestPipe', () => { `; exports[`pipe generator should import the pipe correctly when flat=false 3`] = ` -"import {NgModule} from \\"@angular/core\\"; +"import { NgModule } from '@angular/core'; import { TestPipe } from './test/test.pipe'; - @NgModule({ - imports: [], - declarations: [, TestPipe], - exports: [] - }) - export class TestModule {}" +@NgModule({ + imports: [], + declarations: [, TestPipe], + exports: [], +}) +export class TestModule {} +" `; exports[`pipe generator should import the pipe correctly when flat=false and path is nested deeper 1`] = ` "import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ - name: 'test' + name: 'test', }) export class TestPipe implements PipeTransform { - transform(value: unknown, ...args: unknown[]): unknown { return null; } - } " `; @@ -146,55 +141,53 @@ describe('TestPipe', () => { `; exports[`pipe generator should import the pipe correctly when flat=false and path is nested deeper 3`] = ` -"import {NgModule} from \\"@angular/core\\"; +"import { NgModule } from '@angular/core'; import { TestPipe } from './my-pipes/test/test.pipe'; - @NgModule({ - imports: [], - declarations: [, TestPipe], - exports: [] - }) - export class TestModule {}" +@NgModule({ + imports: [], + declarations: [, TestPipe], + exports: [], +}) +export class TestModule {} +" `; exports[`pipe generator should not generate test file when skipTests=true 1`] = ` "import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ - name: 'test' + name: 'test', }) export class TestPipe implements PipeTransform { - transform(value: unknown, ...args: unknown[]): unknown { return null; } - } " `; exports[`pipe generator should not generate test file when skipTests=true 2`] = ` -"import {NgModule} from \\"@angular/core\\"; +"import { NgModule } from '@angular/core'; import { TestPipe } from './my-pipes/test/test.pipe'; - @NgModule({ - imports: [], - declarations: [, TestPipe], - exports: [] - }) - export class TestModule {}" +@NgModule({ + imports: [], + declarations: [, TestPipe], + exports: [], +}) +export class TestModule {} +" `; exports[`pipe generator should not import the pipe when skipImport=true 1`] = ` "import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ - name: 'test' + name: 'test', }) export class TestPipe implements PipeTransform { - transform(value: unknown, ...args: unknown[]): unknown { return null; } - } " `; @@ -212,13 +205,14 @@ describe('TestPipe', () => { `; exports[`pipe generator should not import the pipe when skipImport=true 3`] = ` -"import {NgModule} from \\"@angular/core\\"; - @NgModule({ - imports: [], - declarations: [], - exports: [] - }) - export class TestModule {}" +"import { NgModule } from '@angular/core'; +@NgModule({ + imports: [], + declarations: [], + exports: [], +}) +export class TestModule {} +" `; exports[`pipe generator should not import the pipe when standalone=true 1`] = ` @@ -226,14 +220,12 @@ exports[`pipe generator should not import the pipe when standalone=true 1`] = ` @Pipe({ name: 'test', - standalone: true + standalone: true, }) export class TestPipe implements PipeTransform { - transform(value: unknown, ...args: unknown[]): unknown { return null; } - } " `; @@ -251,11 +243,12 @@ describe('TestPipe', () => { `; exports[`pipe generator should not import the pipe when standalone=true 3`] = ` -"import {NgModule} from \\"@angular/core\\"; - @NgModule({ - imports: [], - declarations: [], - exports: [] - }) - export class TestModule {}" +"import { NgModule } from '@angular/core'; +@NgModule({ + imports: [], + declarations: [], + exports: [], +}) +export class TestModule {} +" `; diff --git a/packages/angular/src/generators/remote/__snapshots__/remote.spec.ts.snap b/packages/angular/src/generators/remote/__snapshots__/remote.spec.ts.snap index c9a8a533c3c90..a5ddb1745f046 100644 --- a/packages/angular/src/generators/remote/__snapshots__/remote.spec.ts.snap +++ b/packages/angular/src/generators/remote/__snapshots__/remote.spec.ts.snap @@ -7,18 +7,27 @@ import { RouterModule } from '@angular/router'; import { AppComponent } from './app.component'; @NgModule({ - declarations: [AppComponent], - imports: [ - BrowserModule.withServerTransition({ appId: 'serverApp' }), - RouterModule.forRoot([{ - path: '', - loadChildren: () => import('./remote-entry/entry.module').then(m => m.RemoteEntryModule) - }], { initialNavigation: 'enabledBlocking' }), - ], - providers: [], - bootstrap: [AppComponent], + declarations: [AppComponent], + imports: [ + BrowserModule.withServerTransition({ appId: 'serverApp' }), + RouterModule.forRoot( + [ + { + path: '', + loadChildren: () => + import('./remote-entry/entry.module').then( + (m) => m.RemoteEntryModule + ), + }, + ], + { initialNavigation: 'enabledBlocking' } + ), + ], + providers: [], + bootstrap: [AppComponent], }) -export class AppModule {}" +export class AppModule {} +" `; exports[`MF Remote App Generator --ssr should generate the correct files 2`] = ` @@ -42,7 +51,8 @@ exports[`MF Remote App Generator --ssr should generate the correct files 3`] = ` import '@angular/platform-server/init'; export { AppServerModule } from './app/app.server.module'; -export { renderModule } from '@angular/platform-server';" +export { renderModule } from '@angular/platform-server'; +" `; exports[`MF Remote App Generator --ssr should generate the correct files 4`] = ` @@ -79,7 +89,6 @@ export function app(): express.Express { server.set('view engine', 'html'); server.set('views', browserBundles); - // Example Express Rest API endpoints // server.get('/api/**', (req, res) => { }); // Serve static files from /browser @@ -89,7 +98,6 @@ export function app(): express.Express { // All regular routes use the Universal engine server.get('*', (req, res) => { - res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }], @@ -123,7 +131,10 @@ export * from './bootstrap.server'; " `; -exports[`MF Remote App Generator --ssr should generate the correct files 5`] = `"import('./src/main.server');"`; +exports[`MF Remote App Generator --ssr should generate the correct files 5`] = ` +"import('./src/main.server'); +" +`; exports[`MF Remote App Generator --ssr should generate the correct files 6`] = ` "module.exports = { @@ -131,13 +142,17 @@ exports[`MF Remote App Generator --ssr should generate the correct files 6`] = ` exposes: { './Module': 'apps/test/src/app/remote-entry/entry.module.ts', }, -}" +}; +" `; exports[`MF Remote App Generator --ssr should generate the correct files 7`] = ` -"const { withModuleFederationForSSR } = require('@nrwl/angular/module-federation'); +"const { + withModuleFederationForSSR, +} = require('@nrwl/angular/module-federation'); const config = require('./module-federation.config'); -module.exports = withModuleFederationForSSR(config)" +module.exports = withModuleFederationForSSR(config); +" `; exports[`MF Remote App Generator --ssr should generate the correct files 8`] = ` @@ -145,7 +160,7 @@ exports[`MF Remote App Generator --ssr should generate the correct files 8`] = ` @Component({ selector: 'proj-test-entry', - template: \`\` + template: \`\`, }) export class RemoteEntryComponent {} " @@ -155,7 +170,12 @@ exports[`MF Remote App Generator --ssr should generate the correct files 9`] = ` "import { Route } from '@angular/router'; export const appRoutes: Route[] = [ - {path: '', loadChildren: () => import('./remote-entry/entry.module').then(m => m.RemoteEntryModule)},]; + { + path: '', + loadChildren: () => + import('./remote-entry/entry.module').then((m) => m.RemoteEntryModule), + }, +]; " `; @@ -163,7 +183,10 @@ exports[`MF Remote App Generator --ssr should generate the correct files 10`] = "import { Route } from '@angular/router'; import { RemoteEntryComponent } from './entry.component'; -export const remoteRoutes: Route[] = [{ path: '', component: RemoteEntryComponent }];" +export const remoteRoutes: Route[] = [ + { path: '', component: RemoteEntryComponent }, +]; +" `; exports[`MF Remote App Generator --ssr should generate the correct files 11`] = ` @@ -198,7 +221,10 @@ exports[`MF Remote App Generator --ssr should generate the correct files 12`] = "import { Route } from '@angular/router'; import { RemoteEntryComponent } from './entry.component'; -export const remoteRoutes: Route[] = [{ path: '', component: RemoteEntryComponent }];" +export const remoteRoutes: Route[] = [ + { path: '', component: RemoteEntryComponent }, +]; +" `; exports[`MF Remote App Generator --ssr should generate the correct files 13`] = ` @@ -217,35 +243,39 @@ Object { exports[`MF Remote App Generator should generate a remote mf app with a host 1`] = ` "const { withModuleFederation } = require('@nrwl/angular/module-federation'); const config = require('./module-federation.config'); -module.exports = withModuleFederation(config);" +module.exports = withModuleFederation(config); +" `; exports[`MF Remote App Generator should generate a remote mf app with a host 2`] = ` "const { withModuleFederation } = require('@nrwl/angular/module-federation'); const config = require('./module-federation.config'); -module.exports = withModuleFederation(config);" +module.exports = withModuleFederation(config); +" `; exports[`MF Remote App Generator should generate a remote mf app with no host 1`] = ` "const { withModuleFederation } = require('@nrwl/angular/module-federation'); const config = require('./module-federation.config'); -module.exports = withModuleFederation(config);" +module.exports = withModuleFederation(config); +" `; exports[`MF Remote App Generator should generate the a remote setup for standalone components 1`] = ` -"import {importProvidersFrom} from \\"@angular/core\\"; -import {bootstrapApplication} from \\"@angular/platform-browser\\"; -import {RouterModule} from \\"@angular/router\\"; -import {RemoteEntryComponent} from \\"./app/remote-entry/entry.component\\"; -import {appRoutes} from \\"./app/app.routes\\"; +"import { importProvidersFrom } from '@angular/core'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { RouterModule } from '@angular/router'; +import { RemoteEntryComponent } from './app/remote-entry/entry.component'; +import { appRoutes } from './app/app.routes'; bootstrapApplication(RemoteEntryComponent, { providers: [ importProvidersFrom( - RouterModule.forRoot(appRoutes, {initialNavigation: 'enabledBlocking'}) - ) - ] -});" + RouterModule.forRoot(appRoutes, { initialNavigation: 'enabledBlocking' }) + ), + ], +}); +" `; exports[`MF Remote App Generator should generate the a remote setup for standalone components 2`] = ` @@ -254,7 +284,8 @@ exports[`MF Remote App Generator should generate the a remote setup for standalo exposes: { './Routes': 'apps/test/src/app/remote-entry/entry.routes.ts', }, -}" +}; +" `; exports[`MF Remote App Generator should generate the a remote setup for standalone components 3`] = ` @@ -266,7 +297,7 @@ import { NxWelcomeComponent } from './nx-welcome.component'; standalone: true, imports: [CommonModule, NxWelcomeComponent], selector: 'proj-test-entry', - template: \`\` + template: \`\`, }) export class RemoteEntryComponent {} " @@ -276,7 +307,12 @@ exports[`MF Remote App Generator should generate the a remote setup for standalo "import { Route } from '@angular/router'; export const appRoutes: Route[] = [ - {path: '', loadChildren: () => import('./remote-entry/entry.routes').then(m => m.remoteRoutes)},]; + { + path: '', + loadChildren: () => + import('./remote-entry/entry.routes').then((m) => m.remoteRoutes), + }, +]; " `; @@ -284,5 +320,8 @@ exports[`MF Remote App Generator should generate the a remote setup for standalo "import { Route } from '@angular/router'; import { RemoteEntryComponent } from './entry.component'; -export const remoteRoutes: Route[] = [{ path: '', component: RemoteEntryComponent }];" +export const remoteRoutes: Route[] = [ + { path: '', component: RemoteEntryComponent }, +]; +" `; diff --git a/packages/angular/src/generators/remote/remote.spec.ts b/packages/angular/src/generators/remote/remote.spec.ts index 8bcc69014d707..c675ef7f457c0 100644 --- a/packages/angular/src/generators/remote/remote.spec.ts +++ b/packages/angular/src/generators/remote/remote.spec.ts @@ -174,10 +174,10 @@ describe('MF Remote App Generator', () => { @Component({ selector: 'proj-root', - template: '' - + template: '', }) - export class AppComponent {}" + export class AppComponent {} + " `); }); diff --git a/packages/angular/src/generators/scam-directive/scam-directive.spec.ts b/packages/angular/src/generators/scam-directive/scam-directive.spec.ts index 0ac73056faa3f..aa57aff56859d 100644 --- a/packages/angular/src/generators/scam-directive/scam-directive.spec.ts +++ b/packages/angular/src/generators/scam-directive/scam-directive.spec.ts @@ -30,12 +30,10 @@ describe('SCAM Directive Generator', () => { import { CommonModule } from '@angular/common'; @Directive({ - selector: '[projExample]' + selector: '[projExample]', }) export class ExampleDirective { - - constructor() { } - + constructor() {} } @NgModule({ @@ -43,7 +41,8 @@ describe('SCAM Directive Generator', () => { declarations: [ExampleDirective], exports: [ExampleDirective], }) - export class ExampleDirectiveModule {}" + export class ExampleDirectiveModule {} + " `); }); @@ -79,7 +78,8 @@ describe('SCAM Directive Generator', () => { declarations: [ExampleDirective], exports: [ExampleDirective], }) - export class ExampleDirectiveModule {}" + export class ExampleDirectiveModule {} + " `); }); @@ -120,15 +120,17 @@ describe('SCAM Directive Generator', () => { declarations: [ExampleDirective], exports: [ExampleDirective], }) - export class ExampleDirectiveModule {}" + export class ExampleDirectiveModule {} + " `); const secondaryEntryPointSource = tree.read( `libs/lib1/feature/src/index.ts`, 'utf-8' ); expect(secondaryEntryPointSource).toMatchInlineSnapshot(` - "export * from \\"./lib/example.directive\\"; - export * from \\"./lib/example.module\\";" + "export * from './lib/example.directive'; + export * from './lib/example.module'; + " `); }); @@ -161,12 +163,10 @@ describe('SCAM Directive Generator', () => { import { CommonModule } from '@angular/common'; @Directive({ - selector: '[projExample]' + selector: '[projExample]', }) export class ExampleDirective { - - constructor() { } - + constructor() {} } @NgModule({ @@ -174,7 +174,8 @@ describe('SCAM Directive Generator', () => { declarations: [ExampleDirective], exports: [ExampleDirective], }) - export class ExampleDirectiveModule {}" + export class ExampleDirectiveModule {} + " `); }); @@ -206,12 +207,10 @@ describe('SCAM Directive Generator', () => { import { CommonModule } from '@angular/common'; @Directive({ - selector: '[projExample]' + selector: '[projExample]', }) export class ExampleDirective { - - constructor() { } - + constructor() {} } @NgModule({ @@ -219,7 +218,8 @@ describe('SCAM Directive Generator', () => { declarations: [ExampleDirective], exports: [ExampleDirective], }) - export class ExampleDirectiveModule {}" + export class ExampleDirectiveModule {} + " `); }); diff --git a/packages/angular/src/generators/scam-pipe/scam-pipe.spec.ts b/packages/angular/src/generators/scam-pipe/scam-pipe.spec.ts index 1be8ef100273a..f03b0d82edb4f 100644 --- a/packages/angular/src/generators/scam-pipe/scam-pipe.spec.ts +++ b/packages/angular/src/generators/scam-pipe/scam-pipe.spec.ts @@ -30,14 +30,12 @@ describe('SCAM Pipe Generator', () => { import { CommonModule } from '@angular/common'; @Pipe({ - name: 'example' + name: 'example', }) export class ExamplePipe implements PipeTransform { - transform(value: unknown, ...args: unknown[]): unknown { return null; } - } @NgModule({ @@ -45,7 +43,8 @@ describe('SCAM Pipe Generator', () => { declarations: [ExamplePipe], exports: [ExamplePipe], }) - export class ExamplePipeModule {}" + export class ExamplePipeModule {} + " `); }); @@ -81,7 +80,8 @@ describe('SCAM Pipe Generator', () => { declarations: [ExamplePipe], exports: [ExamplePipe], }) - export class ExamplePipeModule {}" + export class ExamplePipeModule {} + " `); }); @@ -122,15 +122,17 @@ describe('SCAM Pipe Generator', () => { declarations: [ExamplePipe], exports: [ExamplePipe], }) - export class ExamplePipeModule {}" + export class ExamplePipeModule {} + " `); const secondaryEntryPointSource = tree.read( `libs/lib1/feature/src/index.ts`, 'utf-8' ); expect(secondaryEntryPointSource).toMatchInlineSnapshot(` - "export * from \\"./lib/example.pipe\\"; - export * from \\"./lib/example.module\\";" + "export * from './lib/example.pipe'; + export * from './lib/example.module'; + " `); }); @@ -163,14 +165,12 @@ describe('SCAM Pipe Generator', () => { import { CommonModule } from '@angular/common'; @Pipe({ - name: 'example' + name: 'example', }) export class ExamplePipe implements PipeTransform { - transform(value: unknown, ...args: unknown[]): unknown { return null; } - } @NgModule({ @@ -178,7 +178,8 @@ describe('SCAM Pipe Generator', () => { declarations: [ExamplePipe], exports: [ExamplePipe], }) - export class ExamplePipeModule {}" + export class ExamplePipeModule {} + " `); }); @@ -210,14 +211,12 @@ describe('SCAM Pipe Generator', () => { import { CommonModule } from '@angular/common'; @Pipe({ - name: 'example' + name: 'example', }) export class ExamplePipe implements PipeTransform { - transform(value: unknown, ...args: unknown[]): unknown { return null; } - } @NgModule({ @@ -225,7 +224,8 @@ describe('SCAM Pipe Generator', () => { declarations: [ExamplePipe], exports: [ExamplePipe], }) - export class ExamplePipeModule {}" + export class ExamplePipeModule {} + " `); }); diff --git a/packages/angular/src/generators/scam-to-standalone/scam-to-standalone.spec.ts b/packages/angular/src/generators/scam-to-standalone/scam-to-standalone.spec.ts index 0e7e75e1a2bee..964d2bd021a75 100644 --- a/packages/angular/src/generators/scam-to-standalone/scam-to-standalone.spec.ts +++ b/packages/angular/src/generators/scam-to-standalone/scam-to-standalone.spec.ts @@ -31,26 +31,25 @@ describe('scam-to-standalone', () => { import { CommonModule } from '@angular/common'; @Component({ - standalone: true, - imports: [CommonModule], + standalone: true, + imports: [CommonModule], selector: 'proj-bar', templateUrl: './bar.component.html', - styleUrls: ['./bar.component.css'] + styleUrls: ['./bar.component.css'], }) - export class BarComponent { - - } + export class BarComponent {} " `); expect(tree.read('apps/foo/src/app/mymodule.module.ts', 'utf-8')) .toMatchInlineSnapshot(` "import { BarComponent } from './bar/bar.component'; - - @NgModule({ - imports: [BarComponent] - }) - export class MyModule {}" + + @NgModule({ + imports: [BarComponent], + }) + export class MyModule {} + " `); expect(tree.read('apps/foo/src/app/bar/bar.component.spec.ts', 'utf-8')) @@ -65,9 +64,8 @@ describe('scam-to-standalone', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ BarComponent ] - }) - .compileComponents(); + imports: [BarComponent], + }).compileComponents(); fixture = TestBed.createComponent(BarComponent); component = fixture.componentInstance; diff --git a/packages/angular/src/generators/scam/scam.spec.ts b/packages/angular/src/generators/scam/scam.spec.ts index 8dd1db45d88ef..2a94710cd8408 100644 --- a/packages/angular/src/generators/scam/scam.spec.ts +++ b/packages/angular/src/generators/scam/scam.spec.ts @@ -31,18 +31,17 @@ describe('SCAM Generator', () => { @Component({ selector: 'example', templateUrl: './example.component.html', - styleUrls: ['./example.component.css'] + styleUrls: ['./example.component.css'], }) - export class ExampleComponent { - - } + export class ExampleComponent {} @NgModule({ imports: [CommonModule], declarations: [ExampleComponent], exports: [ExampleComponent], }) - export class ExampleComponentModule {}" + export class ExampleComponentModule {} + " `); }); @@ -77,7 +76,8 @@ describe('SCAM Generator', () => { declarations: [ExampleComponent], exports: [ExampleComponent], }) - export class ExampleComponentModule {}" + export class ExampleComponentModule {} + " `); }); @@ -118,15 +118,17 @@ describe('SCAM Generator', () => { declarations: [ExampleComponent], exports: [ExampleComponent], }) - export class ExampleComponentModule {}" + export class ExampleComponentModule {} + " `); const secondaryEntryPointSource = tree.read( `libs/lib1/feature/src/index.ts`, 'utf-8' ); expect(secondaryEntryPointSource).toMatchInlineSnapshot(` - "export * from \\"./lib/example/example.component\\"; - export * from \\"./lib/example/example.module\\";" + "export * from './lib/example/example.component'; + export * from './lib/example/example.module'; + " `); }); @@ -160,18 +162,17 @@ describe('SCAM Generator', () => { @Component({ selector: 'example', templateUrl: './example.component.html', - styleUrls: ['./example.component.css'] + styleUrls: ['./example.component.css'], }) - export class ExampleComponent { - - } + export class ExampleComponent {} @NgModule({ imports: [CommonModule], declarations: [ExampleComponent], exports: [ExampleComponent], }) - export class ExampleComponentModule {}" + export class ExampleComponentModule {} + " `); }); @@ -204,18 +205,17 @@ describe('SCAM Generator', () => { @Component({ selector: 'example', templateUrl: './example.component.html', - styleUrls: ['./example.component.css'] + styleUrls: ['./example.component.css'], }) - export class ExampleComponent { - - } + export class ExampleComponent {} @NgModule({ imports: [CommonModule], declarations: [ExampleComponent], exports: [ExampleComponent], }) - export class ExampleComponentModule {}" + export class ExampleComponentModule {} + " `); }); diff --git a/packages/angular/src/generators/setup-mf/__snapshots__/setup-mf.spec.ts.snap b/packages/angular/src/generators/setup-mf/__snapshots__/setup-mf.spec.ts.snap index 217c893360cc4..8825e9df18bdf 100644 --- a/packages/angular/src/generators/setup-mf/__snapshots__/setup-mf.spec.ts.snap +++ b/packages/angular/src/generators/setup-mf/__snapshots__/setup-mf.spec.ts.snap @@ -3,80 +3,91 @@ exports[`Init MF --federationType=dynamic should create a host with the correct configurations 1`] = ` "import { setRemoteDefinitions } from '@nrwl/angular/mf'; - fetch('/assets/module-federation.manifest.json') +fetch('/assets/module-federation.manifest.json') .then((res) => res.json()) - .then(definitions => setRemoteDefinitions(definitions)) - .then(() => import('./bootstrap').catch(err => console.error(err)))" + .then((definitions) => setRemoteDefinitions(definitions)) + .then(() => import('./bootstrap').catch((err) => console.error(err))); +" `; exports[`Init MF should add a remote application and add it to a specified host applications router config 1`] = ` "import { NxWelcomeComponent } from './nx-welcome.component'; - import { Route } from '@angular/router'; +import { Route } from '@angular/router'; export const appRoutes: Route[] = [ - { + { path: 'remote2', - loadChildren: () => import('remote2/Module').then(m => m.RemoteEntryModule) - }, - { + loadChildren: () => + import('remote2/Module').then((m) => m.RemoteEntryModule), + }, + { path: 'remote1', - loadChildren: () => import('remote1/Module').then(m => m.RemoteEntryModule) - }, - { - path: '', - component: NxWelcomeComponent - },]; + loadChildren: () => + import('remote1/Module').then((m) => m.RemoteEntryModule), + }, + { + path: '', + component: NxWelcomeComponent, + }, +]; " `; exports[`Init MF should add a remote application and add it to a specified host applications webpack config that contains a remote application already 1`] = ` "module.exports = { name: 'app1', - remotes: ['remote1','remote2',] -}" + remotes: ['remote1', 'remote2'], +}; +" `; exports[`Init MF should add a remote application and add it to a specified host applications webpack config when no other remote has been added to it 1`] = ` "module.exports = { name: 'app1', - remotes: ['remote1',] -}" + remotes: ['remote1'], +}; +" `; exports[`Init MF should add a remote to dynamic host correctly 1`] = ` "import { NxWelcomeComponent } from './nx-welcome.component'; - import { Route } from '@angular/router'; +import { Route } from '@angular/router'; import { loadRemoteModule } from '@nrwl/angular/mf'; export const appRoutes: Route[] = [ - { + { path: 'remote1', - loadChildren: () => loadRemoteModule('remote1', './Module').then(m => m.RemoteEntryModule) - }, - { - path: '', - component: NxWelcomeComponent - },]; + loadChildren: () => + loadRemoteModule('remote1', './Module').then((m) => m.RemoteEntryModule), + }, + { + path: '', + component: NxWelcomeComponent, + }, +]; " `; exports[`Init MF should create webpack and mf configs correctly 1`] = ` "const { withModuleFederation } = require('@nrwl/angular/module-federation'); const config = require('./module-federation.config'); -module.exports = withModuleFederation(config);" +module.exports = withModuleFederation(config); +" `; exports[`Init MF should create webpack and mf configs correctly 2`] = ` "module.exports = { name: 'app1', - remotes: [] -}" + remotes: [], +}; +" `; exports[`Init MF should create webpack and mf configs correctly 3`] = ` "const { withModuleFederation } = require('@nrwl/angular/module-federation'); const config = require('./module-federation.config'); -module.exports = withModuleFederation(config);" +module.exports = withModuleFederation(config); +" `; exports[`Init MF should create webpack and mf configs correctly 4`] = ` @@ -85,7 +96,8 @@ exports[`Init MF should create webpack and mf configs correctly 4`] = ` exposes: { './Module': 'apps/remote1/src/app/remote-entry/entry.module.ts', }, -}" +}; +" `; exports[`Init MF should generate the remote entry component correctly when prefix is not provided 1`] = ` @@ -93,7 +105,7 @@ exports[`Init MF should generate the remote entry component correctly when prefi @Component({ selector: 'proj-remote1-entry', - template: \`\` + template: \`\`, }) export class RemoteEntryComponent {} " @@ -104,7 +116,7 @@ exports[`Init MF should generate the remote entry module and component correctly @Component({ selector: 'my-org-remote1-entry', - template: \`\` + template: \`\`, }) export class RemoteEntryComponent {} " @@ -119,11 +131,10 @@ import { NxWelcomeComponent } from './nx-welcome.component'; @NgModule({ declarations: [RemoteEntryComponent, NxWelcomeComponent], - imports: [ - CommonModule, - ], + imports: [CommonModule], providers: [], exports: [RemoteEntryComponent], }) -export class RemoteEntryModule {}" +export class RemoteEntryModule {} +" `; diff --git a/packages/angular/src/generators/setup-mf/setup-mf.spec.ts b/packages/angular/src/generators/setup-mf/setup-mf.spec.ts index d2996983f39d7..4a9031538d4ab 100644 --- a/packages/angular/src/generators/setup-mf/setup-mf.spec.ts +++ b/packages/angular/src/generators/setup-mf/setup-mf.spec.ts @@ -102,7 +102,7 @@ describe('Init MF', () => { const updatedMainContents = tree.read(`apps/${app}/src/main.ts`, 'utf-8'); expect(updatedMainContents).toEqual( - `import('./bootstrap').catch(err => console.error(err))` + `import('./bootstrap').catch((err) => console.error(err));\n` ); expect(updatedMainContents).not.toEqual(mainContents); } @@ -343,24 +343,25 @@ describe('Init MF', () => { // ASSERT expect(tree.read('apps/ng14/src/bootstrap.ts', 'utf-8')) .toMatchInlineSnapshot(` - "import {importProvidersFrom} from \\"@angular/core\\"; - import {bootstrapApplication} from \\"@angular/platform-browser\\"; - import {RouterModule} from \\"@angular/router\\"; - import {RemoteEntryComponent} from \\"./app/remote-entry/entry.component\\"; - import {appRoutes} from \\"./app/app.routes\\"; - import {enableProdMode} from '@angular/core'; - import {environment} from './environments/environment'; - if(environment.production) { + "import { importProvidersFrom } from '@angular/core'; + import { bootstrapApplication } from '@angular/platform-browser'; + import { RouterModule } from '@angular/router'; + import { RemoteEntryComponent } from './app/remote-entry/entry.component'; + import { appRoutes } from './app/app.routes'; + import { enableProdMode } from '@angular/core'; + import { environment } from './environments/environment'; + if (environment.production) { enableProdMode(); } bootstrapApplication(RemoteEntryComponent, { providers: [ importProvidersFrom( - RouterModule.forRoot(appRoutes, {initialNavigation: 'enabledBlocking'}) - ) - ] - });" + RouterModule.forRoot(appRoutes, { initialNavigation: 'enabledBlocking' }) + ), + ], + }); + " `); }); diff --git a/packages/angular/src/generators/setup-ssr/__snapshots__/setup-ssr.spec.ts.snap b/packages/angular/src/generators/setup-ssr/__snapshots__/setup-ssr.spec.ts.snap index 2e16dd9d542bc..f3a2623ebe656 100644 --- a/packages/angular/src/generators/setup-ssr/__snapshots__/setup-ssr.spec.ts.snap +++ b/packages/angular/src/generators/setup-ssr/__snapshots__/setup-ssr.spec.ts.snap @@ -28,24 +28,29 @@ Object { exports[`setupSSR should create the files correctly for ssr 2`] = ` "import 'zone.js/dist/zone-node'; -import {APP_BASE_HREF} from '@angular/common'; -import {ngExpressEngine} from '@nguniversal/express-engine'; +import { APP_BASE_HREF } from '@angular/common'; +import { ngExpressEngine } from '@nguniversal/express-engine'; import * as express from 'express'; -import {existsSync} from 'fs'; -import {join} from 'path'; +import { existsSync } from 'fs'; +import { join } from 'path'; -import {AppServerModule} from './src/main.server'; +import { AppServerModule } from './src/main.server'; // The Express app is exported so that it can be used by serverless Functions. export function app(): express.Express { const server = express(); const distFolder = join(process.cwd(), 'dist/apps/app1/browser'); - const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index'; + const indexHtml = existsSync(join(distFolder, 'index.original.html')) + ? 'index.original.html' + : 'index'; // Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/modules/express-engine) - server.engine('html', ngExpressEngine({ - bootstrap: AppServerModule, - })); + server.engine( + 'html', + ngExpressEngine({ + bootstrap: AppServerModule, + }) + ); server.set('view engine', 'html'); server.set('views', distFolder); @@ -53,13 +58,19 @@ export function app(): express.Express { // Example Express Rest API endpoints // server.get('/api/**', (req, res) => { }); // Serve static files from /browser - server.get('*.*', express.static(distFolder, { - maxAge: '1y' - })); + server.get( + '*.*', + express.static(distFolder, { + maxAge: '1y', + }) + ); // All regular routes use the Universal engine server.get('*', (req, res) => { - res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] }); + res.render(indexHtml, { + req, + providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }], + }); }); return server; @@ -80,12 +91,13 @@ function run(): void { // The below code is to ensure that the server is run only when not requiring the bundle. declare const __non_webpack_require__: NodeRequire; const mainModule = __non_webpack_require__.main; -const moduleFilename = mainModule && mainModule.filename || ''; +const moduleFilename = (mainModule && mainModule.filename) || ''; if (moduleFilename === __filename || moduleFilename.includes('iisnode')) { run(); } -export * from './src/main.server';" +export * from './src/main.server'; +" `; exports[`setupSSR should use fileReplacements if they already exist 1`] = ` diff --git a/packages/angular/src/generators/setup-ssr/setup-ssr.spec.ts b/packages/angular/src/generators/setup-ssr/setup-ssr.spec.ts index 98f47834956c2..d854ff9678464 100644 --- a/packages/angular/src/generators/setup-ssr/setup-ssr.spec.ts +++ b/packages/angular/src/generators/setup-ssr/setup-ssr.spec.ts @@ -40,7 +40,8 @@ describe('setupSSR', () => { import '@angular/platform-server/init'; export { AppServerModule } from './app/app.server.module'; - export { renderModule } from '@angular/platform-server';" + export { renderModule } from '@angular/platform-server'; + " `); expect(tree.read('apps/app1/src/main.ts', 'utf-8')).toMatchInlineSnapshot(` "import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; @@ -49,16 +50,16 @@ describe('setupSSR', () => { function bootstrap() { platformBrowserDynamic() - .bootstrapModule(AppModule) - .catch((err) => console.error(err)); - }; - + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); + } - if (document.readyState !== 'loading') { - bootstrap(); - } else { - document.addEventListener('DOMContentLoaded', bootstrap); - }" + if (document.readyState !== 'loading') { + bootstrap(); + } else { + document.addEventListener('DOMContentLoaded', bootstrap); + } + " `); expect(tree.read('apps/app1/tsconfig.server.json', 'utf-8')) .toMatchInlineSnapshot(` @@ -68,15 +69,11 @@ describe('setupSSR', () => { \\"compilerOptions\\": { \\"outDir\\": \\"../../out-tsc/server\\", \\"target\\": \\"es2019\\", - \\"types\\": [ - \\"node\\" - ] + \\"types\\": [\\"node\\"] }, - \\"files\\": [ - \\"src/main.server.ts\\", - \\"server.ts\\", - ] - }" + \\"files\\": [\\"src/main.server.ts\\", \\"server.ts\\"] + } + " `); expect(tree.read('apps/app1/src/app/app.server.module.ts', 'utf-8')) .toMatchInlineSnapshot(` @@ -87,13 +84,11 @@ describe('setupSSR', () => { import { AppComponent } from './app.component'; @NgModule({ - imports: [ - AppModule, - ServerModule, - ], + imports: [AppModule, ServerModule], bootstrap: [AppComponent], }) - export class AppServerModule {}" + export class AppServerModule {} + " `); expect(tree.read('apps/app1/src/app/app.module.ts', 'utf-8')) .toMatchInlineSnapshot(` @@ -104,9 +99,7 @@ describe('setupSSR', () => { @NgModule({ declarations: [AppComponent, NxWelcomeComponent], - imports: [ - BrowserModule.withServerTransition({ appId: 'serverApp' }), - ], + imports: [BrowserModule.withServerTransition({ appId: 'serverApp' })], providers: [], bootstrap: [AppComponent], }) diff --git a/packages/angular/src/generators/setup-tailwind/__snapshots__/setup-tailwind.application.spec.ts.snap b/packages/angular/src/generators/setup-tailwind/__snapshots__/setup-tailwind.application.spec.ts.snap new file mode 100644 index 0000000000000..73d6639678152 --- /dev/null +++ b/packages/angular/src/generators/setup-tailwind/__snapshots__/setup-tailwind.application.spec.ts.snap @@ -0,0 +1,83 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`setupTailwind generator application should add tailwind styles to "apps/app1/src/styles.css" when not provided 1`] = ` +"@tailwind base; +@tailwind components; +@tailwind utilities; + +p { + margin: 0; +} +" +`; + +exports[`setupTailwind generator application should add tailwind styles to "apps/app1/src/styles.less" when not provided 1`] = ` +"@tailwind base; +@tailwind components; +@tailwind utilities; + +p { + margin: 0; +} +" +`; + +exports[`setupTailwind generator application should add tailwind styles to "apps/app1/src/styles.sass" when not provided 1`] = ` +"@tailwind base; +@tailwind components; +@tailwind utilities; + +p { margin: 0; }" +`; + +exports[`setupTailwind generator application should add tailwind styles to "apps/app1/src/styles.scss" when not provided 1`] = ` +"@tailwind base; +@tailwind components; +@tailwind utilities; + +p { + margin: 0; +} +" +`; + +exports[`setupTailwind generator support angular v14 application should add tailwind styles to "apps/app1/src/styles.css" when not provided 1`] = ` +"@tailwind base; +@tailwind components; +@tailwind utilities; + +p { + margin: 0; +} +" +`; + +exports[`setupTailwind generator support angular v14 application should add tailwind styles to "apps/app1/src/styles.less" when not provided 1`] = ` +"@tailwind base; +@tailwind components; +@tailwind utilities; + +p { + margin: 0; +} +" +`; + +exports[`setupTailwind generator support angular v14 application should add tailwind styles to "apps/app1/src/styles.sass" when not provided 1`] = ` +"@tailwind base; +@tailwind components; +@tailwind utilities; + +p { margin: 0; }" +`; + +exports[`setupTailwind generator support angular v14 application should add tailwind styles to "apps/app1/src/styles.scss" when not provided 1`] = ` +"@tailwind base; +@tailwind components; +@tailwind utilities; + +p { + margin: 0; +} +" +`; diff --git a/packages/angular/src/generators/setup-tailwind/setup-tailwind.application.spec.ts b/packages/angular/src/generators/setup-tailwind/setup-tailwind.application.spec.ts index 0eff796ea0e42..cf42cd5dcd5ef 100644 --- a/packages/angular/src/generators/setup-tailwind/setup-tailwind.application.spec.ts +++ b/packages/angular/src/generators/setup-tailwind/setup-tailwind.application.spec.ts @@ -195,7 +195,10 @@ describe('setupTailwind generator', () => { @tailwind components; @tailwind utilities; - p { margin: 0; }" + p { + margin: 0; + } + " `); }); @@ -211,13 +214,7 @@ describe('setupTailwind generator', () => { await setupTailwindGenerator(tree, { project }); - expect(tree.read(stylesEntryPoint, 'utf-8')).toMatchInlineSnapshot(` - "@tailwind base; - @tailwind components; - @tailwind utilities; - - p { margin: 0; }" - `); + expect(tree.read(stylesEntryPoint, 'utf-8')).toMatchSnapshot(); } ); @@ -242,7 +239,10 @@ describe('setupTailwind generator', () => { @tailwind components; @tailwind utilities; - p { margin: 0; }" + p { + margin: 0; + } + " `); }); @@ -273,7 +273,10 @@ describe('setupTailwind generator', () => { @tailwind components; @tailwind utilities; - p { margin: 0; }" + p { + margin: 0; + } + " `); }); @@ -305,7 +308,10 @@ describe('setupTailwind generator', () => { @tailwind components; @tailwind utilities; - p { margin: 0; }" + p { + margin: 0; + } + " `); }); @@ -609,12 +615,15 @@ describe('setupTailwind generator', () => { await setupTailwindGenerator(tree, { project, stylesEntryPoint }); expect(tree.read(stylesEntryPoint, 'utf-8')).toMatchInlineSnapshot(` - "@tailwind base; - @tailwind components; - @tailwind utilities; - - p { margin: 0; }" - `); + "@tailwind base; + @tailwind components; + @tailwind utilities; + + p { + margin: 0; + } + " + `); }); it.each([ @@ -629,13 +638,7 @@ describe('setupTailwind generator', () => { await setupTailwindGenerator(tree, { project }); - expect(tree.read(stylesEntryPoint, 'utf-8')).toMatchInlineSnapshot(` - "@tailwind base; - @tailwind components; - @tailwind utilities; - - p { margin: 0; }" - `); + expect(tree.read(stylesEntryPoint, 'utf-8')).toMatchSnapshot(); } ); @@ -656,12 +659,15 @@ describe('setupTailwind generator', () => { await setupTailwindGenerator(tree, { project }); expect(tree.read(stylesEntryPoint, 'utf-8')).toMatchInlineSnapshot(` - "@tailwind base; - @tailwind components; - @tailwind utilities; - - p { margin: 0; }" - `); + "@tailwind base; + @tailwind components; + @tailwind utilities; + + p { + margin: 0; + } + " + `); }); it('should add tailwind styles to the first style inside the project root specified in the build config as an object when inject is not specified', async () => { @@ -687,12 +693,15 @@ describe('setupTailwind generator', () => { await setupTailwindGenerator(tree, { project }); expect(tree.read(stylesEntryPoint, 'utf-8')).toMatchInlineSnapshot(` - "@tailwind base; - @tailwind components; - @tailwind utilities; - - p { margin: 0; }" - `); + "@tailwind base; + @tailwind components; + @tailwind utilities; + + p { + margin: 0; + } + " + `); }); it('should add tailwind styles to the first style inside the project root specified in the build config as an object when "inject: true"', async () => { @@ -719,12 +728,15 @@ describe('setupTailwind generator', () => { await setupTailwindGenerator(tree, { project }); expect(tree.read(stylesEntryPoint, 'utf-8')).toMatchInlineSnapshot(` - "@tailwind base; - @tailwind components; - @tailwind utilities; - - p { margin: 0; }" - `); + "@tailwind base; + @tailwind components; + @tailwind utilities; + + p { + margin: 0; + } + " + `); }); it('should add required packages', async () => { @@ -747,22 +759,22 @@ describe('setupTailwind generator', () => { expect(tree.read(`apps/${project}/tailwind.config.js`, 'utf-8')) .toMatchInlineSnapshot(` - "const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind'); - const { join } = require('path'); - - /** @type {import('tailwindcss').Config} */ - module.exports = { - content: [ - join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), - ...createGlobPatternsForDependencies(__dirname), - ], - theme: { - extend: {}, - }, - plugins: [], - }; - " - `); + "const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind'); + const { join } = require('path'); + + /** @type {import('tailwindcss').Config} */ + module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], + }; + " + `); }); it('should generate the tailwind.config.js file in the project root with the config for v3 when a version greater than 3 is installed', async () => { @@ -777,22 +789,22 @@ describe('setupTailwind generator', () => { expect(tree.read(`apps/${project}/tailwind.config.js`, 'utf-8')) .toMatchInlineSnapshot(` - "const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind'); - const { join } = require('path'); - - /** @type {import('tailwindcss').Config} */ - module.exports = { - content: [ - join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), - ...createGlobPatternsForDependencies(__dirname), - ], - theme: { - extend: {}, - }, - plugins: [], - }; - " - `); + "const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind'); + const { join } = require('path'); + + /** @type {import('tailwindcss').Config} */ + module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], + }; + " + `); }); it('should generate the tailwind.config.js file in the project root with the config for v2 when a version greater than 2 and lower than 3 is installed', async () => { @@ -807,26 +819,26 @@ describe('setupTailwind generator', () => { expect(tree.read(`apps/${project}/tailwind.config.js`, 'utf-8')) .toMatchInlineSnapshot(` - "const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind'); - const { join } = require('path'); - - module.exports = { - mode: 'jit', - purge: [ - join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), - ...createGlobPatternsForDependencies(__dirname), - ], - darkMode: false, // or 'media' or 'class' - theme: { - extend: {}, - }, - variants: { - extend: {}, - }, - plugins: [], - }; - " - `); + "const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind'); + const { join } = require('path'); + + module.exports = { + mode: 'jit', + purge: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + darkMode: false, // or 'media' or 'class' + theme: { + extend: {}, + }, + variants: { + extend: {}, + }, + plugins: [], + }; + " + `); }); it('should format files', async () => { diff --git a/packages/angular/src/generators/setup-tailwind/setup-tailwind.library.spec.ts b/packages/angular/src/generators/setup-tailwind/setup-tailwind.library.spec.ts index 37ff52f01021a..b948e8ef9b160 100644 --- a/packages/angular/src/generators/setup-tailwind/setup-tailwind.library.spec.ts +++ b/packages/angular/src/generators/setup-tailwind/setup-tailwind.library.spec.ts @@ -493,22 +493,22 @@ describe('setupTailwind generator', () => { expect(tree.read(`libs/${project}/tailwind.config.js`, 'utf-8')) .toMatchInlineSnapshot(` - "const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind'); - const { join } = require('path'); - - /** @type {import('tailwindcss').Config} */ - module.exports = { - content: [ - join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), - ...createGlobPatternsForDependencies(__dirname), - ], - theme: { - extend: {}, - }, - plugins: [], - }; - " - `); + "const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind'); + const { join } = require('path'); + + /** @type {import('tailwindcss').Config} */ + module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], + }; + " + `); }); it('should generate the tailwind.config.js file in the project root with the config for v3 when a version greater than 3 is installed', async () => { @@ -526,22 +526,22 @@ describe('setupTailwind generator', () => { expect(tree.read(`libs/${project}/tailwind.config.js`, 'utf-8')) .toMatchInlineSnapshot(` - "const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind'); - const { join } = require('path'); - - /** @type {import('tailwindcss').Config} */ - module.exports = { - content: [ - join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), - ...createGlobPatternsForDependencies(__dirname), - ], - theme: { - extend: {}, - }, - plugins: [], - }; - " - `); + "const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind'); + const { join } = require('path'); + + /** @type {import('tailwindcss').Config} */ + module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], + }; + " + `); }); it('should generate the tailwind.config.js file in the project root with the config for v2 when a version greater than 2 and lower than 3 is installed', async () => { @@ -559,26 +559,26 @@ describe('setupTailwind generator', () => { expect(tree.read(`libs/${project}/tailwind.config.js`, 'utf-8')) .toMatchInlineSnapshot(` - "const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind'); - const { join } = require('path'); - - module.exports = { - mode: 'jit', - purge: [ - join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), - ...createGlobPatternsForDependencies(__dirname), - ], - darkMode: false, // or 'media' or 'class' - theme: { - extend: {}, - }, - variants: { - extend: {}, - }, - plugins: [], - }; - " - `); + "const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind'); + const { join } = require('path'); + + module.exports = { + mode: 'jit', + purge: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + darkMode: false, // or 'media' or 'class' + theme: { + extend: {}, + }, + variants: { + extend: {}, + }, + plugins: [], + }; + " + `); }); it('should format files', async () => { diff --git a/packages/angular/src/generators/stories/__snapshots__/stories-app.spec.ts.snap b/packages/angular/src/generators/stories/__snapshots__/stories-app.spec.ts.snap index ac9b5e25e3799..9a80b69d50876 100644 --- a/packages/angular/src/generators/stories/__snapshots__/stories-app.spec.ts.snap +++ b/packages/angular/src/generators/stories/__snapshots__/stories-app.spec.ts.snap @@ -1,655 +1,37 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`angularStories generator: applications should generate stories file for inline scam component 1`] = ` -Object { - "data": Array [ - 105, - 109, - 112, - 111, - 114, - 116, - 32, - 123, - 32, - 77, - 101, - 116, - 97, - 32, - 125, - 32, - 102, - 114, - 111, - 109, - 32, - 39, - 64, - 115, - 116, - 111, - 114, - 121, - 98, - 111, - 111, - 107, - 47, - 97, - 110, - 103, - 117, - 108, - 97, - 114, - 39, - 59, - 10, - 105, - 109, - 112, - 111, - 114, - 116, - 32, - 123, - 32, - 77, - 121, - 83, - 99, - 97, - 109, - 67, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 32, - 125, - 32, - 102, - 114, - 111, - 109, - 32, - 39, - 46, - 47, - 109, - 121, - 45, - 115, - 99, - 97, - 109, - 46, - 99, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 39, - 59, - 10, - 10, - 101, - 120, - 112, - 111, - 114, - 116, - 32, - 100, - 101, - 102, - 97, - 117, - 108, - 116, - 32, - 123, - 10, - 32, - 32, - 116, - 105, - 116, - 108, - 101, - 58, - 32, - 39, - 77, - 121, - 83, - 99, - 97, - 109, - 67, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 39, - 44, - 10, - 32, - 32, - 99, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 58, - 32, - 77, - 121, - 83, - 99, - 97, - 109, - 67, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 10, - 125, - 32, - 97, - 115, - 32, - 77, - 101, - 116, - 97, - 60, - 77, - 121, - 83, - 99, - 97, - 109, - 67, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 62, - 59, - 10, - 10, - 101, - 120, - 112, - 111, - 114, - 116, - 32, - 99, - 111, - 110, - 115, - 116, - 32, - 80, - 114, - 105, - 109, - 97, - 114, - 121, - 32, - 61, - 32, - 123, - 10, - 32, - 32, - 114, - 101, - 110, - 100, - 101, - 114, - 58, - 32, - 40, - 97, - 114, - 103, - 115, - 58, - 32, - 77, - 121, - 83, - 99, - 97, - 109, - 67, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 41, - 32, - 61, - 62, - 32, - 40, - 123, - 10, - 32, - 32, - 32, - 32, - 112, - 114, - 111, - 112, - 115, - 58, - 32, - 97, - 114, - 103, - 115, - 44, - 10, - 32, - 32, - 125, - 41, - 44, - 10, - 32, - 32, - 97, - 114, - 103, - 115, - 58, - 32, - 123, - 10, - 32, - 32, - 125, - 44, - 10, - 125, - 59, - ], - "type": "Buffer", -} +"import { Meta } from '@storybook/angular'; +import { MyScamComponent } from './my-scam.component'; + +export default { + title: 'MyScamComponent', + component: MyScamComponent, +} as Meta; + +export const Primary = { + render: (args: MyScamComponent) => ({ + props: args, + }), + args: {}, +}; +" `; exports[`angularStories generator: applications should ignore a path that has a nested component, but still generate nested component stories 1`] = ` -Object { - "data": Array [ - 105, - 109, - 112, - 111, - 114, - 116, - 32, - 123, - 32, - 77, - 101, - 116, - 97, - 32, - 125, - 32, - 102, - 114, - 111, - 109, - 32, - 39, - 64, - 115, - 116, - 111, - 114, - 121, - 98, - 111, - 111, - 107, - 47, - 97, - 110, - 103, - 117, - 108, - 97, - 114, - 39, - 59, - 10, - 105, - 109, - 112, - 111, - 114, - 116, - 32, - 123, - 32, - 67, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 66, - 67, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 32, - 125, - 32, - 102, - 114, - 111, - 109, - 32, - 39, - 46, - 47, - 99, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 45, - 98, - 46, - 99, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 39, - 59, - 10, - 10, - 101, - 120, - 112, - 111, - 114, - 116, - 32, - 100, - 101, - 102, - 97, - 117, - 108, - 116, - 32, - 123, - 10, - 32, - 32, - 116, - 105, - 116, - 108, - 101, - 58, - 32, - 39, - 67, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 66, - 67, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 39, - 44, - 10, - 32, - 32, - 99, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 58, - 32, - 67, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 66, - 67, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 10, - 125, - 32, - 97, - 115, - 32, - 77, - 101, - 116, - 97, - 60, - 67, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 66, - 67, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 62, - 59, - 10, - 10, - 101, - 120, - 112, - 111, - 114, - 116, - 32, - 99, - 111, - 110, - 115, - 116, - 32, - 80, - 114, - 105, - 109, - 97, - 114, - 121, - 32, - 61, - 32, - 123, - 10, - 32, - 32, - 114, - 101, - 110, - 100, - 101, - 114, - 58, - 32, - 40, - 97, - 114, - 103, - 115, - 58, - 32, - 67, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 66, - 67, - 111, - 109, - 112, - 111, - 110, - 101, - 110, - 116, - 41, - 32, - 61, - 62, - 32, - 40, - 123, - 10, - 32, - 32, - 32, - 32, - 112, - 114, - 111, - 112, - 115, - 58, - 32, - 97, - 114, - 103, - 115, - 44, - 10, - 32, - 32, - 125, - 41, - 44, - 10, - 32, - 32, - 97, - 114, - 103, - 115, - 58, - 32, - 123, - 10, - 32, - 32, - 125, - 44, - 10, - 125, - 59, - ], - "type": "Buffer", -} +"import { Meta } from '@storybook/angular'; +import { ComponentBComponent } from './component-b.component'; + +export default { + title: 'ComponentBComponent', + component: ComponentBComponent, +} as Meta; + +export const Primary = { + render: (args: ComponentBComponent) => ({ + props: args, + }), + args: {}, +}; +" `; diff --git a/packages/angular/src/generators/stories/__snapshots__/stories-lib.spec.ts.snap b/packages/angular/src/generators/stories/__snapshots__/stories-lib.spec.ts.snap index b5fb99eb46fd9..396bf86c63c60 100644 --- a/packages/angular/src/generators/stories/__snapshots__/stories-lib.spec.ts.snap +++ b/packages/angular/src/generators/stories/__snapshots__/stories-lib.spec.ts.snap @@ -2,11 +2,16 @@ exports[`angularStories generator: libraries Stories for non-empty Angular library should generate cypress spec files 1`] = ` "describe('test-ui-lib', () => { - beforeEach(() => cy.visit('/iframe.html?id=testbuttoncomponent--primary&args=buttonType:button;style:default;age;isOn:false;')); + beforeEach(() => + cy.visit( + '/iframe.html?id=testbuttoncomponent--primary&args=buttonType:button;style:default;age;isOn:false;' + ) + ); it('should render the component', () => { cy.get('proj-test-button').should('exist'); }); -});" +}); +" `; exports[`angularStories generator: libraries Stories for non-empty Angular library should generate stories file for standalone components 1`] = ` @@ -15,16 +20,16 @@ import { StandaloneComponent } from './standalone.component'; export default { title: 'StandaloneComponent', - component: StandaloneComponent + component: StandaloneComponent, } as Meta; export const Primary = { render: (args: StandaloneComponent) => ({ props: args, }), - args: { - }, -};" + args: {}, +}; +" `; exports[`angularStories generator: libraries Stories for non-empty Angular library should generate stories file for standalone components 2`] = ` @@ -33,16 +38,16 @@ import { SecondaryStandaloneComponent } from './secondary-standalone.component'; export default { title: 'SecondaryStandaloneComponent', - component: SecondaryStandaloneComponent + component: SecondaryStandaloneComponent, } as Meta; export const Primary = { render: (args: SecondaryStandaloneComponent) => ({ props: args, }), - args: { - }, -};" + args: {}, +}; +" `; exports[`angularStories generator: libraries Stories for non-empty Angular library should generate stories.ts files 1`] = ` @@ -51,7 +56,7 @@ import { TestButtonComponent } from './test-button.component'; export default { title: 'TestButtonComponent', - component: TestButtonComponent + component: TestButtonComponent, } as Meta; export const Primary = { @@ -59,12 +64,13 @@ export const Primary = { props: args, }), args: { - buttonType: 'button', - style: 'default', - age: 0, - isOn: false, + buttonType: 'button', + style: 'default', + age: 0, + isOn: false, }, -};" +}; +" `; exports[`angularStories generator: libraries Stories for non-empty Angular library should ignore paths 1`] = ` @@ -73,7 +79,7 @@ import { TestButtonComponent } from './test-button.component'; export default { title: 'TestButtonComponent', - component: TestButtonComponent + component: TestButtonComponent, } as Meta; export const Primary = { @@ -81,10 +87,11 @@ export const Primary = { props: args, }), args: { - buttonType: 'button', - style: 'default', - age: 0, - isOn: false, + buttonType: 'button', + style: 'default', + age: 0, + isOn: false, }, -};" +}; +" `; diff --git a/packages/angular/src/generators/stories/stories-app.spec.ts b/packages/angular/src/generators/stories/stories-app.spec.ts index 71fc514034b59..998a2258b52a9 100644 --- a/packages/angular/src/generators/stories/stories-app.spec.ts +++ b/packages/angular/src/generators/stories/stories-app.spec.ts @@ -25,8 +25,8 @@ describe('angularStories generator: applications', () => { }); }); - it('should generate stories file', () => { - angularStoriesGenerator(tree, { name: appName }); + it('should generate stories file', async () => { + await angularStoriesGenerator(tree, { name: appName }); expect( tree.exists(`apps/${appName}/src/app/app.component.stories.ts`) @@ -36,7 +36,7 @@ describe('angularStories generator: applications', () => { it('should generate stories file for scam component', async () => { await scamGenerator(tree, { name: 'my-scam', project: appName }); - angularStoriesGenerator(tree, { name: appName }); + await angularStoriesGenerator(tree, { name: appName }); expect( tree.exists( @@ -48,7 +48,7 @@ describe('angularStories generator: applications', () => { it('should ignore paths', async () => { await scamGenerator(tree, { name: 'my-scam', project: appName }); - angularStoriesGenerator(tree, { + await angularStoriesGenerator(tree, { name: appName, ignorePaths: [`apps/${appName}/src/app/my-scam/**`], }); @@ -63,7 +63,7 @@ describe('angularStories generator: applications', () => { it('should ignore paths when full path to component is provided', async () => { await scamGenerator(tree, { name: 'my-scam', project: appName }); - angularStoriesGenerator(tree, { + await angularStoriesGenerator(tree, { name: appName, ignorePaths: [`apps/${appName}/src/app/my-scam/my-scam.component.ts`], }); @@ -82,7 +82,7 @@ describe('angularStories generator: applications', () => { project: appName, }); - angularStoriesGenerator(tree, { + await angularStoriesGenerator(tree, { name: appName, ignorePaths: [ `apps/${appName}/src/app/component-a/component-a.component.ts`, @@ -90,9 +90,11 @@ describe('angularStories generator: applications', () => { }); expect( - tree.read( - `apps/${appName}/src/app/component-a/component-b/component-b.component.stories.ts` - ) + tree + .read( + `apps/${appName}/src/app/component-a/component-b/component-b.component.stories.ts` + ) + .toString() ).toMatchSnapshot(); expect( tree.exists( @@ -108,15 +110,17 @@ describe('angularStories generator: applications', () => { inlineScam: true, }); - angularStoriesGenerator(tree, { name: appName }); + await angularStoriesGenerator(tree, { name: appName }); expect( - tree.read(`apps/${appName}/src/app/my-scam/my-scam.component.stories.ts`) + tree + .read(`apps/${appName}/src/app/my-scam/my-scam.component.stories.ts`) + .toString() ).toMatchSnapshot(); }); - it('should generate cypress spec file', () => { - angularStoriesGenerator(tree, { + it('should generate cypress spec file', async () => { + await angularStoriesGenerator(tree, { name: appName, generateCypressSpecs: true, }); diff --git a/packages/angular/src/generators/stories/stories-lib.spec.ts b/packages/angular/src/generators/stories/stories-lib.spec.ts index 0bcf28e7c2c11..606bf2a48d3c8 100644 --- a/packages/angular/src/generators/stories/stories-lib.spec.ts +++ b/packages/angular/src/generators/stories/stories-lib.spec.ts @@ -35,11 +35,12 @@ describe('angularStories generator: libraries', () => { }); it('should not fail on empty NgModule declarations', () => { - expect(() => - angularStoriesGenerator(tree, { - name: libName, - generateCypressSpecs: false, - }) + expect( + async () => + await angularStoriesGenerator(tree, { + name: libName, + generateCypressSpecs: false, + }) ).not.toThrow(); }); }); @@ -69,7 +70,7 @@ describe('angularStories generator: libraries', () => { path: `libs/${libName}/secondary-entry-point/src/lib`, }); - angularStoriesGenerator(tree, { name: libName }); + await angularStoriesGenerator(tree, { name: libName }); expect( tree.exists( @@ -110,7 +111,7 @@ describe('angularStories generator: libraries', () => { name: libName, }); - angularStoriesGenerator(tree, { + await angularStoriesGenerator(tree, { name: libName, generateCypressSpecs: true, }); @@ -150,8 +151,8 @@ describe('angularStories generator: libraries', () => { }); try { - angularStoriesGenerator(tree, { name: libName }); - angularStoriesGenerator(tree, { + await angularStoriesGenerator(tree, { name: libName }); + await angularStoriesGenerator(tree, { name: libName, generateCypressSpecs: true, }); @@ -166,7 +167,7 @@ describe('angularStories generator: libraries', () => { name: libName, }); - angularStoriesGenerator(tree, { + await angularStoriesGenerator(tree, { name: libName, generateCypressSpecs: true, }); @@ -199,7 +200,7 @@ describe('angularStories generator: libraries', () => { name: libName, }); - angularStoriesGenerator(tree, { + await angularStoriesGenerator(tree, { name: libName, generateCypressSpecs: true, }); @@ -243,7 +244,7 @@ describe('angularStories generator: libraries', () => { name: libName, }); - angularStoriesGenerator(tree, { + await angularStoriesGenerator(tree, { name: libName, generateCypressSpecs: true, }); @@ -269,7 +270,7 @@ describe('angularStories generator: libraries', () => { it('should generate stories file for scam component', async () => { await scamGenerator(tree, { name: 'my-scam', project: libName }); - angularStoriesGenerator(tree, { name: libName }); + await angularStoriesGenerator(tree, { name: libName }); expect( tree.exists( @@ -285,7 +286,7 @@ describe('angularStories generator: libraries', () => { inlineScam: true, }); - angularStoriesGenerator(tree, { name: libName }); + await angularStoriesGenerator(tree, { name: libName }); expect( tree.exists( @@ -315,7 +316,7 @@ describe('angularStories generator: libraries', () => { standalone: true, }); - angularStoriesGenerator(tree, { name: libName }); + await angularStoriesGenerator(tree, { name: libName }); expect( tree.exists( @@ -355,7 +356,7 @@ describe('angularStories generator: libraries', () => { path: `libs/${libName}/secondary-entry-point/src/lib`, }); - angularStoriesGenerator(tree, { + await angularStoriesGenerator(tree, { name: libName, ignorePaths: [ `libs/${libName}/src/lib/barrel/**`, diff --git a/packages/angular/src/generators/stories/stories.ts b/packages/angular/src/generators/stories/stories.ts index 8d56c6234cfac..c76d37fb9f49f 100644 --- a/packages/angular/src/generators/stories/stories.ts +++ b/packages/angular/src/generators/stories/stories.ts @@ -12,10 +12,10 @@ import { getModuleFilePaths } from '../utils/storybook-ast/module-info'; import type { StoriesGeneratorOptions } from './schema'; import minimatch = require('minimatch'); -export function angularStoriesGenerator( +export async function angularStoriesGenerator( tree: Tree, options: StoriesGeneratorOptions -): void { +): Promise { const e2eProjectName = options.cypressProject ?? `${options.name}-e2e`; const e2eProject = getE2EProject(tree, e2eProjectName); const entryPoints = getProjectEntryPoints(tree, options.name); @@ -34,50 +34,50 @@ export function angularStoriesGenerator( ); } - componentsInfo - .filter( - (f) => - !options.ignorePaths?.some((pattern) => { - const shouldIgnorePath = minimatch( - joinPathFragments( - f.moduleFolderPath, - f.path, - `${f.componentFileName}.ts` - ), - pattern - ); - return shouldIgnorePath; - }) - ) - ?.forEach((info) => { - if (info === undefined) { - return; - } + const componentInfos = componentsInfo.filter( + (f) => + !options.ignorePaths?.some((pattern) => { + const shouldIgnorePath = minimatch( + joinPathFragments( + f.moduleFolderPath, + f.path, + `${f.componentFileName}.ts` + ), + pattern + ); + return shouldIgnorePath; + }) + ); - componentStoryGenerator(tree, { + for (const info of componentInfos) { + if (info === undefined) { + continue; + } + + await componentStoryGenerator(tree, { + projectPath: info.moduleFolderPath, + componentName: info.name, + componentPath: info.path, + componentFileName: info.componentFileName, + skipFormat: true, + }); + + if (options.generateCypressSpecs && e2eProject) { + await componentCypressSpecGenerator(tree, { + projectName: options.name, projectPath: info.moduleFolderPath, + cypressProject: options.cypressProject, componentName: info.name, componentPath: info.path, componentFileName: info.componentFileName, - skipFormat: false, + specDirectory: joinPathFragments(info.entryPointName, info.path), + skipFormat: true, }); - - if (options.generateCypressSpecs && e2eProject) { - componentCypressSpecGenerator(tree, { - projectName: options.name, - projectPath: info.moduleFolderPath, - cypressProject: options.cypressProject, - componentName: info.name, - componentPath: info.path, - componentFileName: info.componentFileName, - specDirectory: joinPathFragments(info.entryPointName, info.path), - skipFormat: false, - }); - } - }); + } + } if (!options.skipFormat) { - formatFiles(tree); + await formatFiles(tree); } } diff --git a/packages/angular/src/generators/storybook-configuration/__snapshots__/storybook-configuration.spec.ts.snap b/packages/angular/src/generators/storybook-configuration/__snapshots__/storybook-configuration.spec.ts.snap index bcb08e7facb28..ea09059325d7d 100644 --- a/packages/angular/src/generators/storybook-configuration/__snapshots__/storybook-configuration.spec.ts.snap +++ b/packages/angular/src/generators/storybook-configuration/__snapshots__/storybook-configuration.spec.ts.snap @@ -1,28 +1,15 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`StorybookConfiguration generator should configure storybook to use webpack 5 1`] = ` -" - - - -module.exports = { +"module.exports = { core: { builder: 'webpack5' }, - stories: [ - - '../**/*.stories.mdx', - '../**/*.stories.@(js|jsx|ts|tsx)' ], - addons: ['@storybook/addon-essentials' - - - ] + stories: ['../**/*.stories.mdx', '../**/*.stories.@(js|jsx|ts|tsx)'], + addons: ['@storybook/addon-essentials'], }; - // To customize your webpack configuration you can use the webpackFinal field. // Check https://storybook.js.org/docs/react/builders/webpack#extending-storybooks-webpack-config // and https://nx.dev/packages/storybook/documents/custom-builder-configs - - " `; diff --git a/packages/angular/src/generators/storybook-configuration/lib/generate-stories.ts b/packages/angular/src/generators/storybook-configuration/lib/generate-stories.ts index 780234988667b..da7b1cd6befb5 100644 --- a/packages/angular/src/generators/storybook-configuration/lib/generate-stories.ts +++ b/packages/angular/src/generators/storybook-configuration/lib/generate-stories.ts @@ -4,7 +4,7 @@ import { readProjectConfiguration } from '@nrwl/devkit'; import { angularStoriesGenerator } from '../../stories/stories'; import type { StorybookConfigurationOptions } from '../schema'; -export function generateStories( +export async function generateStories( tree: Tree, options: StorybookConfigurationOptions ) { @@ -15,7 +15,7 @@ export function generateStories( options.cypressDirectory ); - angularStoriesGenerator(tree, { + await angularStoriesGenerator(tree, { name: options.name, generateCypressSpecs: options.configureCypress && options.generateCypressSpecs, diff --git a/packages/angular/src/generators/storybook-configuration/storybook-configuration.ts b/packages/angular/src/generators/storybook-configuration/storybook-configuration.ts index 6eff28feebb1c..bac9af3083f2c 100644 --- a/packages/angular/src/generators/storybook-configuration/storybook-configuration.ts +++ b/packages/angular/src/generators/storybook-configuration/storybook-configuration.ts @@ -18,7 +18,7 @@ export async function storybookConfigurationGenerator( ); if (options.generateStories) { - generateStories(tree, { ...options, skipFormat: false }); + await generateStories(tree, { ...options, skipFormat: true }); } if (!options.skipFormat) { diff --git a/packages/angular/src/generators/utils/testing.ts b/packages/angular/src/generators/utils/testing.ts index e81e81c0fc770..5bda8a8c66ced 100644 --- a/packages/angular/src/generators/utils/testing.ts +++ b/packages/angular/src/generators/utils/testing.ts @@ -6,6 +6,7 @@ import { UnitTestRunner } from '../../utils/test-runners'; import { angularDevkitVersion } from '../../utils/versions'; import { applicationGenerator } from '../application/application'; import type { Schema as ApplicationOptions } from '../application/schema'; +import { componentGenerator } from '../component/component'; import { host } from '../host/host'; import type { Schema as HostOptions } from '../host/schema'; import { libraryGenerator } from '../library/library'; @@ -61,10 +62,6 @@ export async function createStorybookTestWorkspaceForLib( '@schematics/angular', 'module' ); - const componentGenerator = wrapAngularDevkitSchematic( - '@schematics/angular', - 'component' - ); await libraryGenerator(tree, { name: libName, diff --git a/packages/angular/src/generators/web-worker/web-worker.spec.ts b/packages/angular/src/generators/web-worker/web-worker.spec.ts index c7a4f076f2cdb..ac314971effbb 100644 --- a/packages/angular/src/generators/web-worker/web-worker.spec.ts +++ b/packages/angular/src/generators/web-worker/web-worker.spec.ts @@ -74,16 +74,17 @@ describe('webWorker generator', () => { expect(tree.read(`apps/${appName}/src/app/test-worker.ts`, 'utf-8')) .toMatchInlineSnapshot(` "if (typeof Worker !== 'undefined') { - // Create a new - const worker = new Worker(new URL('./test-worker.worker', import.meta.url)); - worker.onmessage = ({ data }) => { - console.log(\`page got message \${data}\`); - }; - worker.postMessage('hello'); + // Create a new + const worker = new Worker(new URL('./test-worker.worker', import.meta.url)); + worker.onmessage = ({ data }) => { + console.log(\`page got message \${data}\`); + }; + worker.postMessage('hello'); } else { - // Web Workers are not supported in this environment. - // You should add a fallback so that your program still executes correctly. - }" + // Web Workers are not supported in this environment. + // You should add a fallback so that your program still executes correctly. + } + " `); expect(tree.read(`apps/${appName}/src/app/test-worker.worker.ts`, 'utf-8')) .toMatchInlineSnapshot(` diff --git a/packages/angular/src/migrations/update-14-2-0/update-router-initial-navigation.spec.ts b/packages/angular/src/migrations/update-14-2-0/update-router-initial-navigation.spec.ts index 9e5e4d7c62f6e..98677ac14410c 100644 --- a/packages/angular/src/migrations/update-14-2-0/update-router-initial-navigation.spec.ts +++ b/packages/angular/src/migrations/update-14-2-0/update-router-initial-navigation.spec.ts @@ -63,20 +63,24 @@ describe('update-router-initial-navigation migration', () => { expect(tree.read('apps/app1/src/app/app.module.ts', 'utf-8')) .toMatchInlineSnapshot(` "import { NgModule } from '@angular/core'; - import { BrowserModule } from '@angular/platform-browser'; - import { AppComponent } from './app.component'; - import { NxWelcomeComponent } from './nx-welcome.component'; - import { RouterModule } from '@angular/router'; - - @NgModule({ - declarations: [AppComponent, NxWelcomeComponent], - imports: [ - BrowserModule, - RouterModule.forRoot([], { relativeLinkResolution: 'legacy', initialNavigation: 'enabledBlocking' }), - ], - bootstrap: [AppComponent], - }) - export class AppModule {}" + import { BrowserModule } from '@angular/platform-browser'; + import { AppComponent } from './app.component'; + import { NxWelcomeComponent } from './nx-welcome.component'; + import { RouterModule } from '@angular/router'; + + @NgModule({ + declarations: [AppComponent, NxWelcomeComponent], + imports: [ + BrowserModule, + RouterModule.forRoot([], { + relativeLinkResolution: 'legacy', + initialNavigation: 'enabledBlocking', + }), + ], + bootstrap: [AppComponent], + }) + export class AppModule {} + " `); }); @@ -116,9 +120,28 @@ describe('update-router-initial-navigation migration', () => { await updateRouterInitialNavigation(tree); - expect(tree.read('apps/app1/src/app/app.module.ts', 'utf-8')).toBe( - moduleContent - ); + expect(tree.read('apps/app1/src/app/app.module.ts', 'utf-8')) + .toMatchInlineSnapshot(` + "import { NgModule } from '@angular/core'; + import { BrowserModule } from '@angular/platform-browser'; + import { AppComponent } from './app.component'; + import { NxWelcomeComponent } from './nx-welcome.component'; + import { RouterModule } from '@angular/router'; + + @NgModule({ + declarations: [AppComponent, NxWelcomeComponent], + imports: [ + BrowserModule, + RouterModule.forRoot([], { + relativeLinkResolution: 'legacy', + initialNavigation: 'enabledNonBlocking', + }), + ], + bootstrap: [AppComponent], + }) + export class AppModule {} + " + `); }); it('should do nothing when "initialNavigation" is not set', async () => { @@ -157,9 +180,25 @@ describe('update-router-initial-navigation migration', () => { await updateRouterInitialNavigation(tree); - expect(tree.read('apps/app1/src/app/app.module.ts', 'utf-8')).toBe( - moduleContent - ); + expect(tree.read('apps/app1/src/app/app.module.ts', 'utf-8')) + .toMatchInlineSnapshot(` + "import { NgModule } from '@angular/core'; + import { BrowserModule } from '@angular/platform-browser'; + import { AppComponent } from './app.component'; + import { NxWelcomeComponent } from './nx-welcome.component'; + import { RouterModule } from '@angular/router'; + + @NgModule({ + declarations: [AppComponent, NxWelcomeComponent], + imports: [ + BrowserModule, + RouterModule.forRoot([], { relativeLinkResolution: 'legacy' }), + ], + bootstrap: [AppComponent], + }) + export class AppModule {} + " + `); }); it('should do nothing when not passing extra options to "RouterModule.forRoot" call', async () => { @@ -195,9 +234,22 @@ describe('update-router-initial-navigation migration', () => { await updateRouterInitialNavigation(tree); - expect(tree.read('apps/app1/src/app/app.module.ts', 'utf-8')).toBe( - moduleContent - ); + expect(tree.read('apps/app1/src/app/app.module.ts', 'utf-8')) + .toMatchInlineSnapshot(` + "import { NgModule } from '@angular/core'; + import { BrowserModule } from '@angular/platform-browser'; + import { AppComponent } from './app.component'; + import { NxWelcomeComponent } from './nx-welcome.component'; + import { RouterModule } from '@angular/router'; + + @NgModule({ + declarations: [AppComponent, NxWelcomeComponent], + imports: [BrowserModule, RouterModule.forRoot([])], + bootstrap: [AppComponent], + }) + export class AppModule {} + " + `); }); it('should no update "initialNavigation" when using a different module ".forRoot" call with similar API to "RouterModule.forRoot"', async () => { @@ -231,9 +283,23 @@ describe('update-router-initial-navigation migration', () => { await updateRouterInitialNavigation(tree); - expect(tree.read('apps/app1/src/app/app.module.ts', 'utf-8')).toBe( - moduleContent - ); + expect(tree.read('apps/app1/src/app/app.module.ts', 'utf-8')) + .toMatchInlineSnapshot(` + "import { NgModule } from '@angular/core'; + import { OtherModule } from '@foo/bar'; + import { FooComponent } from './foo.component'; + + @NgModule({ + declarations: [AppComponent, NxWelcomeComponent], + imports: [ + BrowserModule, + OtherModule.forRoot([], { initialNavigation: 'enabled' }), + ], + bootstrap: [AppComponent], + }) + export class FooModule {} + " + `); }); it('should do nothing when not using "@angular/router"', async () => { @@ -268,9 +334,21 @@ describe('update-router-initial-navigation migration', () => { await updateRouterInitialNavigation(tree); - expect(tree.read('apps/app1/src/app/app.module.ts', 'utf-8')).toBe( - moduleContent - ); + expect(tree.read('apps/app1/src/app/app.module.ts', 'utf-8')) + .toMatchInlineSnapshot(` + "import { NgModule } from '@angular/core'; + import { BrowserModule } from '@angular/platform-browser'; + import { AppComponent } from './app.component'; + import { NxWelcomeComponent } from './nx-welcome.component'; + + @NgModule({ + declarations: [AppComponent, NxWelcomeComponent], + imports: [BrowserModule], + bootstrap: [AppComponent], + }) + export class AppModule {} + " + `); }); it('should do nothing when using a "RouterModule.forChild" call', async () => { @@ -304,8 +382,19 @@ describe('update-router-initial-navigation migration', () => { await updateRouterInitialNavigation(tree); - expect(tree.read('apps/app1/src/app/foo/foo.module.ts', 'utf-8')).toBe( - moduleContent - ); + expect(tree.read('apps/app1/src/app/foo/foo.module.ts', 'utf-8')) + .toMatchInlineSnapshot(` + "import { NgModule } from '@angular/core'; + import { RouterModule } from '@angular/router'; + import { FooComponent } from './foo.component'; + + @NgModule({ + declarations: [FooComponent], + imports: [CommonModule, RouterModule.forChild([])], + exports: [FooComponent], + }) + export class FooModule {} + " + `); }); }); diff --git a/packages/angular/src/migrations/update-15-2-0/update-karma-main-file.spec.ts b/packages/angular/src/migrations/update-15-2-0/update-karma-main-file.spec.ts index f797e36db2705..0b89fb82ade16 100644 --- a/packages/angular/src/migrations/update-15-2-0/update-karma-main-file.spec.ts +++ b/packages/angular/src/migrations/update-15-2-0/update-karma-main-file.spec.ts @@ -87,34 +87,37 @@ describe(`Migration to karma builder main file (test.ts)`, () => { it(`should remove 'declare const require' and 'require.context' usages`, async () => { await updateKarmaMainFile(tree); - expect(tree.read('test.ts', 'utf-8')).toBe(stripIndents` - import { getTestBed } from '@angular/core/testing'; + expect(tree.read('test.ts', 'utf-8')).toMatchInlineSnapshot(` + "import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, - platformBrowserDynamicTesting + platformBrowserDynamicTesting, } from '@angular/platform-browser-dynamic/testing'; // First, initialize the Angular testing environment. getTestBed().initTestEnvironment( BrowserDynamicTestingModule, - platformBrowserDynamicTesting(), + platformBrowserDynamicTesting() ); + " `); }); it(`should remove multiple 'require.context' usages`, async () => { await updateKarmaMainFile(tree); - expect(tree.read('test-multiple-context.ts', 'utf-8')).toBe(stripIndents` - import { getTestBed } from '@angular/core/testing'; + expect(tree.read('test-multiple-context.ts', 'utf-8')) + .toMatchInlineSnapshot(` + "import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, - platformBrowserDynamicTesting + platformBrowserDynamicTesting, } from '@angular/platform-browser-dynamic/testing'; // First, initialize the Angular testing environment. getTestBed().initTestEnvironment( BrowserDynamicTestingModule, - platformBrowserDynamicTesting(), + platformBrowserDynamicTesting() ); + " `); }); }); diff --git a/packages/cypress/src/generators/cypress-e2e-configuration/cypress-e2e-configuration.spec.ts b/packages/cypress/src/generators/cypress-e2e-configuration/cypress-e2e-configuration.spec.ts index 0d9e0fc21dd97..d191ab16d3648 100644 --- a/packages/cypress/src/generators/cypress-e2e-configuration/cypress-e2e-configuration.spec.ts +++ b/packages/cypress/src/generators/cypress-e2e-configuration/cypress-e2e-configuration.spec.ts @@ -44,9 +44,8 @@ describe('Cypress e2e configuration', () => { export default defineConfig({ e2e: nxE2EPreset(__dirname, { - cypressDir: \\"src\\", - - }) + cypressDir: 'src', + }), }); " `); @@ -105,9 +104,8 @@ describe('Cypress e2e configuration', () => { export default defineConfig({ e2e: nxE2EPreset(__dirname, { - cypressDir: \\"cypress\\", - - }) + cypressDir: 'cypress', + }), }); " `); diff --git a/packages/cypress/src/generators/cypress-project/__snapshots__/cypress-project.spec.ts.snap b/packages/cypress/src/generators/cypress-project/__snapshots__/cypress-project.spec.ts.snap index 63405fbfeb92d..da638becbf089 100644 --- a/packages/cypress/src/generators/cypress-project/__snapshots__/cypress-project.spec.ts.snap +++ b/packages/cypress/src/generators/cypress-project/__snapshots__/cypress-project.spec.ts.snap @@ -198,12 +198,11 @@ exports[`Cypress Project > v10 for bundler:vite should pass the bundler info to import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; export default defineConfig({ - e2e: nxE2EPreset(__dirname, - { - bundler: 'vite' - } - ) -});" + e2e: nxE2EPreset(__dirname, { + bundler: 'vite', + }), +}); +" `; exports[`Cypress Project > v10 nested should set right path names in \`cypress.config.ts\` 1`] = ` @@ -211,8 +210,9 @@ exports[`Cypress Project > v10 nested should set right path names in \`cypress.c import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; export default defineConfig({ - e2e: nxE2EPreset(__dirname) -});" + e2e: nxE2EPreset(__dirname), +}); +" `; exports[`Cypress Project > v10 nested should set right path names in \`tsconfig.e2e.json\` 1`] = ` @@ -240,8 +240,9 @@ exports[`Cypress Project > v10 should set right path names in \`cypress.config.t import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; export default defineConfig({ - e2e: nxE2EPreset(__dirname) -});" + e2e: nxE2EPreset(__dirname), +}); +" `; exports[`Cypress Project > v10 should set right path names in \`tsconfig.e2e.json\` 1`] = ` diff --git a/packages/cypress/src/generators/migrate-to-cypress-11/__snapshots__/migrate-to-cypress-11.spec.ts.snap b/packages/cypress/src/generators/migrate-to-cypress-11/__snapshots__/migrate-to-cypress-11.spec.ts.snap index e1c0e276e74c5..47f22259d1fb4 100644 --- a/packages/cypress/src/generators/migrate-to-cypress-11/__snapshots__/migrate-to-cypress-11.spec.ts.snap +++ b/packages/cypress/src/generators/migrate-to-cypress-11/__snapshots__/migrate-to-cypress-11.spec.ts.snap @@ -1,51 +1,49 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`convertToCypressTen convertCypressProject should handle custom target names 1`] = ` -"import { defineConfig } from 'cypress' +"import { defineConfig } from 'cypress'; import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; - const cypressJsonConfig = { -\\"fileServerFolder\\": \\".\\", -\\"fixturesFolder\\": \\"./src/fixtures\\", -\\"video\\": true, -\\"videosFolder\\": \\"../../dist/cypress/apps/app-e2e/videos\\", -\\"screenshotsFolder\\": \\"../../dist/cypress/apps/app-e2e/screenshots\\", -\\"chromeWebSecurity\\": false, -\\"specPattern\\": \\"src/e2e/**/*.cy.{js,jsx,ts,tsx}\\", -\\"supportFile\\": \\"src/support/e2e.ts\\" -} + fileServerFolder: '.', + fixturesFolder: './src/fixtures', + video: true, + videosFolder: '../../dist/cypress/apps/app-e2e/videos', + screenshotsFolder: '../../dist/cypress/apps/app-e2e/screenshots', + chromeWebSecurity: false, + specPattern: 'src/e2e/**/*.cy.{js,jsx,ts,tsx}', + supportFile: 'src/support/e2e.ts', +}; export default defineConfig({ -e2e: { -...nxE2EPreset(__dirname), -...cypressJsonConfig, - -} -})" + e2e: { + ...nxE2EPreset(__dirname), + ...cypressJsonConfig, + }, +}); +" `; exports[`convertToCypressTen convertCypressProject should handle multiple configurations 1`] = ` -"import { defineConfig } from 'cypress' +"import { defineConfig } from 'cypress'; import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; - const cypressJsonConfig = { -\\"fileServerFolder\\": \\".\\", -\\"fixturesFolder\\": \\"./src/fixtures\\", -\\"video\\": true, -\\"videosFolder\\": \\"../../dist/cypress/apps/app-e2e/videos\\", -\\"screenshotsFolder\\": \\"../../dist/cypress/apps/app-e2e/screenshots\\", -\\"chromeWebSecurity\\": false, -\\"specPattern\\": \\"src/e2e/**/*.cy.{js,jsx,ts,tsx}\\", -\\"supportFile\\": \\"src/support/e2e.ts\\" -} + fileServerFolder: '.', + fixturesFolder: './src/fixtures', + video: true, + videosFolder: '../../dist/cypress/apps/app-e2e/videos', + screenshotsFolder: '../../dist/cypress/apps/app-e2e/screenshots', + chromeWebSecurity: false, + specPattern: 'src/e2e/**/*.cy.{js,jsx,ts,tsx}', + supportFile: 'src/support/e2e.ts', +}; export default defineConfig({ -e2e: { -...nxE2EPreset(__dirname), -...cypressJsonConfig, - -} -})" + e2e: { + ...nxE2EPreset(__dirname), + ...cypressJsonConfig, + }, +}); +" `; exports[`convertToCypressTen convertCypressProject should handle multiple configurations 2`] = ` @@ -68,27 +66,26 @@ Object { `; exports[`convertToCypressTen convertCypressProject should handle multiple configurations with no default cypressConfig option 1`] = ` -"import { defineConfig } from 'cypress' +"import { defineConfig } from 'cypress'; import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; - const cypressJsonConfig = { -\\"fileServerFolder\\": \\".\\", -\\"fixturesFolder\\": \\"./src/fixtures\\", -\\"video\\": true, -\\"videosFolder\\": \\"../../dist/cypress/apps/app-e2e/videos\\", -\\"screenshotsFolder\\": \\"../../dist/cypress/apps/app-e2e/screenshots\\", -\\"chromeWebSecurity\\": false, -\\"specPattern\\": \\"src/e2e/**/*.cy.{js,jsx,ts,tsx}\\", -\\"supportFile\\": \\"src/support/e2e.ts\\" -} + fileServerFolder: '.', + fixturesFolder: './src/fixtures', + video: true, + videosFolder: '../../dist/cypress/apps/app-e2e/videos', + screenshotsFolder: '../../dist/cypress/apps/app-e2e/screenshots', + chromeWebSecurity: false, + specPattern: 'src/e2e/**/*.cy.{js,jsx,ts,tsx}', + supportFile: 'src/support/e2e.ts', +}; export default defineConfig({ -e2e: { -...nxE2EPreset(__dirname), -...cypressJsonConfig, - -} -})" + e2e: { + ...nxE2EPreset(__dirname), + ...cypressJsonConfig, + }, +}); +" `; exports[`convertToCypressTen convertCypressProject should handle multiple configurations with no default cypressConfig option 2`] = ` @@ -112,49 +109,47 @@ Object { `; exports[`convertToCypressTen convertCypressProject should handle sharing the same config across projects 1`] = ` -"import { defineConfig } from 'cypress' +"import { defineConfig } from 'cypress'; import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; - const cypressJsonConfig = { -\\"fileServerFolder\\": \\".\\", -\\"fixturesFolder\\": \\"./src/fixtures\\", -\\"video\\": true, -\\"chromeWebSecurity\\": false, -\\"specPattern\\": \\"src/e2e/**/*.cy.{js,jsx,ts,tsx}\\", -\\"supportFile\\": \\"src/support/e2e.ts\\" -} + fileServerFolder: '.', + fixturesFolder: './src/fixtures', + video: true, + chromeWebSecurity: false, + specPattern: 'src/e2e/**/*.cy.{js,jsx,ts,tsx}', + supportFile: 'src/support/e2e.ts', +}; export default defineConfig({ -e2e: { -...nxE2EPreset(__dirname), -...cypressJsonConfig, - -} -})" + e2e: { + ...nxE2EPreset(__dirname), + ...cypressJsonConfig, + }, +}); +" `; exports[`convertToCypressTen convertCypressProject should infer targets with --all flag 1`] = ` -"import { defineConfig } from 'cypress' +"import { defineConfig } from 'cypress'; import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; - const cypressJsonConfig = { -\\"fileServerFolder\\": \\".\\", -\\"fixturesFolder\\": \\"./src/fixtures\\", -\\"video\\": true, -\\"videosFolder\\": \\"../../dist/cypress/apps/app-e2e/videos\\", -\\"screenshotsFolder\\": \\"../../dist/cypress/apps/app-e2e/screenshots\\", -\\"chromeWebSecurity\\": false, -\\"specPattern\\": \\"src/e2e/**/*.cy.{js,jsx,ts,tsx}\\", -\\"supportFile\\": \\"src/support/e2e.ts\\" -} + fileServerFolder: '.', + fixturesFolder: './src/fixtures', + video: true, + videosFolder: '../../dist/cypress/apps/app-e2e/videos', + screenshotsFolder: '../../dist/cypress/apps/app-e2e/screenshots', + chromeWebSecurity: false, + specPattern: 'src/e2e/**/*.cy.{js,jsx,ts,tsx}', + supportFile: 'src/support/e2e.ts', +}; export default defineConfig({ -e2e: { -...nxE2EPreset(__dirname), -...cypressJsonConfig, - -} -})" + e2e: { + ...nxE2EPreset(__dirname), + ...cypressJsonConfig, + }, +}); +" `; exports[`convertToCypressTen convertCypressProject should infer targets with --all flag 2`] = ` @@ -200,27 +195,26 @@ Object { `; exports[`convertToCypressTen convertCypressProject should not break when an invalid target is passed in 1`] = ` -"import { defineConfig } from 'cypress' +"import { defineConfig } from 'cypress'; import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; - const cypressJsonConfig = { -\\"fileServerFolder\\": \\".\\", -\\"fixturesFolder\\": \\"./src/fixtures\\", -\\"video\\": true, -\\"videosFolder\\": \\"../../dist/cypress/apps/app-e2e/videos\\", -\\"screenshotsFolder\\": \\"../../dist/cypress/apps/app-e2e/screenshots\\", -\\"chromeWebSecurity\\": false, -\\"specPattern\\": \\"src/e2e/**/*.cy.{js,jsx,ts,tsx}\\", -\\"supportFile\\": \\"src/support/e2e.ts\\" -} + fileServerFolder: '.', + fixturesFolder: './src/fixtures', + video: true, + videosFolder: '../../dist/cypress/apps/app-e2e/videos', + screenshotsFolder: '../../dist/cypress/apps/app-e2e/screenshots', + chromeWebSecurity: false, + specPattern: 'src/e2e/**/*.cy.{js,jsx,ts,tsx}', + supportFile: 'src/support/e2e.ts', +}; export default defineConfig({ -e2e: { -...nxE2EPreset(__dirname), -...cypressJsonConfig, - -} -})" + e2e: { + ...nxE2EPreset(__dirname), + ...cypressJsonConfig, + }, +}); +" `; exports[`convertToCypressTen convertCypressProject should not break when an invalid target is passed in 2`] = ` @@ -266,52 +260,52 @@ Object { `; exports[`convertToCypressTen convertCypressProject should update project w/customized config 1`] = ` -"import { defineConfig } from 'cypress' +"import { defineConfig } from 'cypress'; import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; import setupNodeEvents from './src/plugins/index'; const cypressJsonConfig = { -\\"fileServerFolder\\": \\".\\", -\\"fixturesFolder\\": \\"./src/fixtures\\", -\\"video\\": true, -\\"videosFolder\\": \\"../../dist/cypress/apps/app-e2e/videos\\", -\\"screenshotsFolder\\": \\"../../dist/cypress/apps/app-e2e/screenshots\\", -\\"chromeWebSecurity\\": false, -\\"baseUrl\\": \\"http://localhost:4200\\", -\\"specPattern\\": \\"src/e2e/**/*.cy.{js,jsx,ts,tsx}\\", -\\"supportFile\\": false -} + fileServerFolder: '.', + fixturesFolder: './src/fixtures', + video: true, + videosFolder: '../../dist/cypress/apps/app-e2e/videos', + screenshotsFolder: '../../dist/cypress/apps/app-e2e/screenshots', + chromeWebSecurity: false, + baseUrl: 'http://localhost:4200', + specPattern: 'src/e2e/**/*.cy.{js,jsx,ts,tsx}', + supportFile: false, +}; export default defineConfig({ -e2e: { -...nxE2EPreset(__dirname), -...cypressJsonConfig, -setupNodeEvents -} -})" + e2e: { + ...nxE2EPreset(__dirname), + ...cypressJsonConfig, + setupNodeEvents, + }, +}); +" `; exports[`convertToCypressTen convertCypressProject should update project w/defaults 1`] = ` -"import { defineConfig } from 'cypress' +"import { defineConfig } from 'cypress'; import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; - const cypressJsonConfig = { -\\"fileServerFolder\\": \\".\\", -\\"fixturesFolder\\": \\"./src/fixtures\\", -\\"video\\": true, -\\"videosFolder\\": \\"../../dist/cypress/apps/app-e2e/videos\\", -\\"screenshotsFolder\\": \\"../../dist/cypress/apps/app-e2e/screenshots\\", -\\"chromeWebSecurity\\": false, -\\"specPattern\\": \\"src/e2e/**/*.cy.{js,jsx,ts,tsx}\\", -\\"supportFile\\": \\"src/support/e2e.ts\\" -} + fileServerFolder: '.', + fixturesFolder: './src/fixtures', + video: true, + videosFolder: '../../dist/cypress/apps/app-e2e/videos', + screenshotsFolder: '../../dist/cypress/apps/app-e2e/screenshots', + chromeWebSecurity: false, + specPattern: 'src/e2e/**/*.cy.{js,jsx,ts,tsx}', + supportFile: 'src/support/e2e.ts', +}; export default defineConfig({ -e2e: { -...nxE2EPreset(__dirname), -...cypressJsonConfig, - -} -})" + e2e: { + ...nxE2EPreset(__dirname), + ...cypressJsonConfig, + }, +}); +" `; exports[`convertToCypressTen updateImports should update imports 1`] = ` diff --git a/packages/cypress/src/migrations/update-15-0-0/__snapshots__/update-cy-mount-usage.spec.ts.snap b/packages/cypress/src/migrations/update-15-0-0/__snapshots__/update-cy-mount-usage.spec.ts.snap index b1efe88176f17..fe8f77f3c0b6d 100644 --- a/packages/cypress/src/migrations/update-15-0-0/__snapshots__/update-cy-mount-usage.spec.ts.snap +++ b/packages/cypress/src/migrations/update-15-0-0/__snapshots__/update-cy-mount-usage.spec.ts.snap @@ -94,7 +94,7 @@ exports[`update cy.mount usage should update angular react18 test file 1`] = ` `; exports[`update cy.mount usage should work 1`] = ` -"import { mount } from 'cypress/angular' +"import { mount } from 'cypress/angular'; // eslint-disable-next-line @typescript-eslint/no-namespace declare global { @@ -112,57 +112,57 @@ declare global { Cypress.Commands.add('login', (email, password) => { console.log('Custom command example: Login', email, password); }); -Cypress.Commands.add('mount', mount);" +Cypress.Commands.add('mount', mount); +" `; exports[`update cy.mount usage should work 2`] = ` "import { MountConfig } from 'cypress/angular'; - describe('MyComponent', () => { - it('should work', () => { - cy.mount(); - }); +describe('MyComponent', () => { + it('should work', () => { + cy.mount(); + }); - it('should work with config', () => { - cy.mount(,); - }); - }); - " + it('should work with config', () => { + cy.mount(); + }); +}); +" `; exports[`update cy.mount usage should work 3`] = ` "import { MountConfig } from 'cypress/angular'; - describe('MyComponent', () => { - it('should work', () => { - cy.mount(); - }); +describe('MyComponent', () => { + it('should work', () => { + cy.mount(); + }); - it('should work with config', () => { - cy.mount(,); - }); - }); - " + it('should work with config', () => { + cy.mount(); + }); +}); +" `; exports[`update cy.mount usage should work 4`] = ` "import { MountConfig } from 'cypress/angular'; - describe('MyComponent', () => { - it('should work', () => { - cy.mount(MyComponent); - }); +describe('MyComponent', () => { + it('should work', () => { + cy.mount(MyComponent); + }); - it('should work with config', () => { - cy.mount(MyComponent, {...config, componentProperties: {foo: 'bar'}}); - }); - }); - " + it('should work with config', () => { + cy.mount(MyComponent, { ...config, componentProperties: { foo: 'bar' } }); + }); +}); +" `; exports[`update cy.mount usage should work 5`] = ` -" -// eslint-disable-next-line @typescript-eslint/no-namespace +"// eslint-disable-next-line @typescript-eslint/no-namespace declare namespace Cypress { // eslint-disable-next-line @typescript-eslint/no-unused-vars interface Chainable { @@ -182,46 +182,40 @@ Cypress.Commands.add('mount', (any) => { `; exports[`update cy.mount usage should work 6`] = ` -" - - describe('MyComponent', () => { - it('should work', () => { - cy.mount(); - }); - - it('should work with config', () => { - cy.mount(,); - }); - }); - " +"describe('MyComponent', () => { + it('should work', () => { + cy.mount(); + }); + + it('should work with config', () => { + cy.mount(); + }); +}); +" `; exports[`update cy.mount usage should work 7`] = ` -" - - describe('MyComponent', () => { - it('should work', () => { - cy.mount(); - }); - - it('should work with config', () => { - cy.mount(,); - }); - }); - " +"describe('MyComponent', () => { + it('should work', () => { + cy.mount(); + }); + + it('should work with config', () => { + cy.mount(); + }); +}); +" `; exports[`update cy.mount usage should work 8`] = ` -" - - describe('MyComponent', () => { - it('should work', () => { - cy.mount(MyComponent); - }); - - it('should work with config', () => { - cy.mount(MyComponent, {...config, componentProperties: {foo: 'bar'}}); - }); - }); - " +"describe('MyComponent', () => { + it('should work', () => { + cy.mount(MyComponent); + }); + + it('should work with config', () => { + cy.mount(MyComponent, { ...config, componentProperties: { foo: 'bar' } }); + }); +}); +" `; diff --git a/packages/cypress/src/migrations/update-15-1-0/__snapshots__/cypress-11.spec.ts.snap b/packages/cypress/src/migrations/update-15-1-0/__snapshots__/cypress-11.spec.ts.snap index 5ae6cea965065..965d90596ea37 100644 --- a/packages/cypress/src/migrations/update-15-1-0/__snapshots__/cypress-11.spec.ts.snap +++ b/packages/cypress/src/migrations/update-15-1-0/__snapshots__/cypress-11.spec.ts.snap @@ -1,178 +1,198 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Cypress 11 Migration should migrate to v11 1`] = ` -" -it('calls the prop', () => { - cy.mount() - cy.contains('My component') -}) +"it('calls the prop', () => { + cy.mount(); + cy.contains('My component'); +}); describe('again', () => { it('calls the prop', () => { - cy.mount() - cy.contains('My component') - }) -})" + cy.mount(); + cy.contains('My component'); + }); +}); +" `; exports[`Cypress 11 Migration should migrate to v11 2`] = ` -"/** TODO: mountHook is deprecate. -* Use a wrapper component instead. -* See post for details: https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/#reactmounthook-removed -* */ -import { mountHook, getContainerEl } from 'cypress/react18' -import ReactDom from 'react-dom' -import { useCounter } from ‘./useCounter’ +"/** TODO: mountHook is deprecate. + * Use a wrapper component instead. + * See post for details: https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/#reactmounthook-removed + * */ +import { mountHook, getContainerEl } from 'cypress/react18'; +import ReactDom from 'react-dom'; +import { useCounter } from './useCounter'; it('increments the count', () => { mountHook(() => useCounter()).then((result) => { - expect(result.current.count).to.equal(0) - result.current.increment() - expect(result.current.count).to.equal(1) - result.current.increment() - expect(result.current.count).to.equal(2) - }) -}) + expect(result.current.count).to.equal(0); + result.current.increment(); + expect(result.current.count).to.equal(1); + result.current.increment(); + expect(result.current.count).to.equal(2); + }); +}); describe('blah', () => { - it('increments the count', () => { mountHook(() => useCounter()).then((result) => { - expect(result.current.count).to.equal(0) - result.current.increment() - expect(result.current.count).to.equal(1) - result.current.increment() - expect(result.current.count).to.equal(2) - }) - }) -}) - + expect(result.current.count).to.equal(0); + result.current.increment(); + expect(result.current.count).to.equal(1); + result.current.increment(); + expect(result.current.count).to.equal(2); + }); + }); +}); it('calls the prop', () => { - cy.mount() - cy.contains('My component') + cy.mount(); + cy.contains('My component'); - cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl())) + cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl())); - cy.contains('My component').should('not.exist') - cy.get('@onUnmount').should('have.been.calledOnce') -}) + cy.contains('My component').should('not.exist'); + cy.get('@onUnmount').should('have.been.calledOnce'); +}); describe('again', () => { it('calls the prop', () => { - cy.mount() - cy.contains('My component') + cy.mount(); + cy.contains('My component'); - cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl())) + cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl())); - cy.contains('My component').should('not.exist') - cy.get('@onUnmount').should('have.been.calledOnce') - }) -})" + cy.contains('My component').should('not.exist'); + cy.get('@onUnmount').should('have.been.calledOnce'); + }); +}); +" `; exports[`Cypress 11 Migration should migrate to v11 3`] = ` -"/** TODO: mountHook is deprecate. -* Use a wrapper component instead. -* See post for details: https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/#reactmounthook-removed -* */ -import { mountHook, getContainerEl } from 'cypress/react' -import ReactDom from 'react-dom' -import { useCounter } from ‘./useCounter’ +"/** TODO: mountHook is deprecate. + * Use a wrapper component instead. + * See post for details: https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/#reactmounthook-removed + * */ +import { mountHook, getContainerEl } from 'cypress/react'; +import ReactDom from 'react-dom'; +import { useCounter } from './useCounter'; it('increments the count', () => { mountHook(() => useCounter()).then((result) => { - expect(result.current.count).to.equal(0) - result.current.increment() - expect(result.current.count).to.equal(1) - result.current.increment() - expect(result.current.count).to.equal(2) - }) -}) + expect(result.current.count).to.equal(0); + result.current.increment(); + expect(result.current.count).to.equal(1); + result.current.increment(); + expect(result.current.count).to.equal(2); + }); +}); describe('blah', () => { - it('increments the count', () => { mountHook(() => useCounter()).then((result) => { - expect(result.current.count).to.equal(0) - result.current.increment() - expect(result.current.count).to.equal(1) - result.current.increment() - expect(result.current.count).to.equal(2) - }) - }) -}) - + expect(result.current.count).to.equal(0); + result.current.increment(); + expect(result.current.count).to.equal(1); + result.current.increment(); + expect(result.current.count).to.equal(2); + }); + }); +}); it('calls the prop', () => { - cy.mount() - cy.contains('My component') + cy.mount(); + cy.contains('My component'); - cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl())) + cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl())); - cy.contains('My component').should('not.exist') - cy.get('@onUnmount').should('have.been.calledOnce') -}) + cy.contains('My component').should('not.exist'); + cy.get('@onUnmount').should('have.been.calledOnce'); +}); describe('again', () => { it('calls the prop', () => { - cy.mount() - cy.contains('My component') + cy.mount(); + cy.contains('My component'); - cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl())) + cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl())); - cy.contains('My component').should('not.exist') - cy.get('@onUnmount').should('have.been.calledOnce') - }) -})" + cy.contains('My component').should('not.exist'); + cy.get('@onUnmount').should('have.been.calledOnce'); + }); +}); +" `; exports[`Cypress 11 Migration should migrate to v11 4`] = ` -"import {TestBed} from '@angular/core/testing; - - import { MyComponent } from './my.component'; - describe('MyComponent', () => { - const config = { +"import { TestBed } from '@angular/core/testing'; + +import { MyComponent } from './my.component'; +describe('MyComponent', () => { + const config = { + imports: [], + declarations: [], + providers: [{ provide: 'foo', useValue: 'bar' }], + }; + it('direct usage', () => { + TestBed.overrideComponent(MyComponent, { + add: { providers: config.providers }, + }); + cy.mount(MyComponent, config); + }); + it('spread usage', () => { + TestBed.overrideComponent(MyComponent, { + add: { providers: [{ provide: 'foo', useValue: 'bar' }] }, + }); + cy.mount(MyComponent, { ...config, providers: undefined }); + }); + it('inlined usage', () => { + TestBed.overrideComponent(MyComponent, { + add: { providers: [{ provide: 'foo', useValue: 'bar' }] }, + }); + cy.mount(MyComponent, { imports: [], declarations: [], - providers: [{provide: 'foo', useValue: 'bar'}] - }; - it('direct usage', () => { - TestBed.overrideComponent(MyComponent, {add: { providers: config.providers}}); -cy.mount(MyComponent, config); - }); - it('spread usage', () => { - TestBed.overrideComponent(MyComponent, { add: { providers: [{provide: 'foo', useValue: 'bar'}] }}); -cy.mount(MyComponent, {...config, providers: undefined }); - }); - it('inlined usage', () => { - TestBed.overrideComponent(MyComponent, { add: { providers: [{provide: 'foo', useValue: 'bar'}] }}); -cy.mount(MyComponent, {imports: [], declarations: [], providers: undefined}); - }); - " + providers: undefined, + }); + }); +}); +" `; exports[`Cypress 11 Migration should migrate to v11 5`] = ` "import { MountConfig } from 'cypress/angular'; - import { MyComponent } from './my.component'; - import {TestBed} from '@angular/core/testing'; - describe('MyComponent', () => { - const config: MountConfig = { +import { MyComponent } from './my.component'; +import { TestBed } from '@angular/core/testing'; +describe('MyComponent', () => { + const config: MountConfig = { + imports: [], + declarations: [], + providers: [{ provide: 'foo', useValue: 'bar' }], + }; + it('direct usage', () => { + TestBed.overrideComponent(MyComponent, { + add: { providers: config.providers }, + }); + cy.mount(MyComponent, config); + }); + it('spread usage', () => { + TestBed.overrideComponent(MyComponent, { + add: { providers: [{ provide: 'foo', useValue: 'bar' }] }, + }); + cy.mount(MyComponent, { ...config, providers: undefined }); + }); + it('inlined usage', () => { + TestBed.overrideComponent(MyComponent, { + add: { providers: [{ provide: 'foo', useValue: 'bar' }] }, + }); + cy.mount(MyComponent, { imports: [], declarations: [], - providers: [{provide: 'foo', useValue: 'bar'}] - }; - it('direct usage', () => { - TestBed.overrideComponent(MyComponent, {add: { providers: config.providers}}); -cy.mount(MyComponent, config); - }); - it('spread usage', () => { - TestBed.overrideComponent(MyComponent, { add: { providers: [{provide: 'foo', useValue: 'bar'}] }}); -cy.mount(MyComponent, {...config, providers: undefined }); - }); - it('inlined usage', () => { - TestBed.overrideComponent(MyComponent, { add: { providers: [{provide: 'foo', useValue: 'bar'}] }}); -cy.mount(MyComponent, {imports: [], declarations: [], providers: undefined}); - }); - " + providers: undefined, + }); + }); +}); +" `; diff --git a/packages/cypress/src/migrations/update-15-1-0/cypress-11.spec.ts b/packages/cypress/src/migrations/update-15-1-0/cypress-11.spec.ts index 0f1ae7be06ac7..442781759790f 100644 --- a/packages/cypress/src/migrations/update-15-1-0/cypress-11.spec.ts +++ b/packages/cypress/src/migrations/update-15-1-0/cypress-11.spec.ts @@ -89,17 +89,18 @@ describe('Cypress 11 Migration', () => { }, }); - const content = `import {MountConfig} from 'cypress/angular'; - import { MyComponent } from './my.component'; - describe('MyComponent', () => { - const config:MountConfig = { - imports: [], - declarations: [], - providers: [{provide: 'foo', useValue: 'bar'}] - }; - it('direct usage', () => { - cy.mount(MyComponent, config); - }); + const content = `import { MountConfig } from 'cypress/angular'; +import { MyComponent } from './my.component'; +describe('MyComponent', () => { + const config: MountConfig = { + imports: [], + declarations: [], + providers: [{ provide: 'foo', useValue: 'bar' }], + }; + it('direct usage', () => { + cy.mount(MyComponent, config); + }); +}); `; tree.write('apps/my-e2e-app/src/somthing.component.cy.ts', content); await updateToCypress11(tree); @@ -153,7 +154,7 @@ describe('again', () => { tree.write( 'libs/my-react-lib/src/lib/with-import.component.cy.ts', `import { mountHook, unmount } from 'cypress/react' -import { useCounter } from ‘./useCounter’ +import { useCounter } from './useCounter' it('increments the count', () => { mountHook(() => useCounter()).then((result) => { @@ -203,8 +204,8 @@ describe('again', () => { ); tree.write( 'libs/my-react-lib/src/lib/with-import-18.component.cy.ts', - `import { mountHook, unmount } from 'cypress/react18' -import { useCounter } from ‘./useCounter’ + `import { mountHook, unmount } from 'cypress/react18'; +import { useCounter } from './useCounter'; it('increments the count', () => { mountHook(() => useCounter()).then((result) => { @@ -299,6 +300,7 @@ Cypress.Commands.add('mount', mount) it('inlined usage', () => { cy.mount(MyComponent, {imports: [], declarations: [], providers: [{provide: 'foo', useValue: 'bar'}]}); }); + }); ` ); tree.write( @@ -320,6 +322,7 @@ Cypress.Commands.add('mount', mount) it('inlined usage', () => { cy.mount(MyComponent, {imports: [], declarations: [], providers: [{provide: 'foo', useValue: 'bar'}]}); }); + }); ` ); } diff --git a/packages/cypress/src/migrations/update-15-1-0/cypress-11.ts b/packages/cypress/src/migrations/update-15-1-0/cypress-11.ts index f7de2bc6a7542..7bdf6c36c742a 100644 --- a/packages/cypress/src/migrations/update-15-1-0/cypress-11.ts +++ b/packages/cypress/src/migrations/update-15-1-0/cypress-11.ts @@ -157,7 +157,9 @@ export function updateProviderUsage(tree: Tree, filePath: string) { tree.write( filePath, `${ - isTestBedImported ? '' : "import {TestBed} from '@angular/core/testing;\n" + isTestBedImported + ? '' + : "import {TestBed} from '@angular/core/testing';\n" }${updatedProviders}` ); } diff --git a/packages/devkit/src/generators/format-files.ts b/packages/devkit/src/generators/format-files.ts index 2802855904305..1ca43de7ea938 100644 --- a/packages/devkit/src/generators/format-files.ts +++ b/packages/devkit/src/generators/format-files.ts @@ -5,7 +5,7 @@ import type * as Prettier from 'prettier'; import { sortObjectByKeys } from 'nx/src/utils/object-sort'; import { requireNx } from '../../nx'; -const { updateJson } = requireNx(); +const { updateJson, readJson } = requireNx(); /** * Formats all the created or updated files using Prettier @@ -28,19 +28,20 @@ export async function formatFiles(tree: Tree): Promise { await Promise.all( Array.from(files).map(async (file) => { const systemPath = path.join(tree.root, file.path); - let options: any = { - filepath: systemPath, - }; const resolvedOptions = await prettier.resolveConfig(systemPath, { editorconfig: true, }); + + let optionsFromTree; if (!resolvedOptions) { - return; + try { + optionsFromTree = readJson(tree, '.prettierrc'); + } catch {} } - options = { - ...options, - ...resolvedOptions, + const options: any = { + filepath: systemPath, + ...(resolvedOptions ?? optionsFromTree), }; if (file.path.endsWith('.swcrc')) { diff --git a/packages/expo/src/generators/component/component.spec.ts b/packages/expo/src/generators/component/component.spec.ts index 06da011999bf8..7f4b213ed609a 100644 --- a/packages/expo/src/generators/component/component.spec.ts +++ b/packages/expo/src/generators/component/component.spec.ts @@ -28,7 +28,7 @@ describe('component', () => { skipFormat: true, }; - expoApplicationGenerator(appTree, { + await expoApplicationGenerator(appTree, { name: 'my-app', linter: Linter.EsLint, e2eTestRunner: 'none', @@ -36,7 +36,7 @@ describe('component', () => { js: true, unitTestRunner: 'jest', }); - expoLibraryGenerator(appTree, { + await expoLibraryGenerator(appTree, { name: projectName, linter: Linter.EsLint, skipFormat: false, diff --git a/packages/expo/src/migrations/update-14-0-0/add-project-root-metro-config-14-0-0.spec.ts b/packages/expo/src/migrations/update-14-0-0/add-project-root-metro-config-14-0-0.spec.ts index 42aaf54db54b1..b845ffbd8af62 100644 --- a/packages/expo/src/migrations/update-14-0-0/add-project-root-metro-config-14-0-0.spec.ts +++ b/packages/expo/src/migrations/update-14-0-0/add-project-root-metro-config-14-0-0.spec.ts @@ -64,42 +64,46 @@ module.exports = (async () => { ); await update(tree); - expect(tree.read('apps/products/metro.config.js', 'utf-8')).toEqual(` -const { withNxMetro } = require('@nrwl/react-native'); -const { getDefaultConfig } = require('metro-config'); + expect(tree.read('apps/products/metro.config.js', 'utf-8')) + .toMatchInlineSnapshot(` + "const { withNxMetro } = require('@nrwl/react-native'); + const { getDefaultConfig } = require('metro-config'); -module.exports = (async () => { - const { - resolver: { sourceExts, assetExts }, - } = await getDefaultConfig(); - // console.log(getModulesRunBeforeMainModule); - return withNxMetro( - { - transformer: { - getTransformOptions: async () => ({ - transform: { - experimentalImportSupport: false, - inlineRequires: true, + module.exports = (async () => { + const { + resolver: { sourceExts, assetExts }, + } = await getDefaultConfig(); + // console.log(getModulesRunBeforeMainModule); + return withNxMetro( + { + transformer: { + getTransformOptions: async () => ({ + transform: { + experimentalImportSupport: false, + inlineRequires: true, + }, + }), + babelTransformerPath: require.resolve('react-native-svg-transformer'), + }, + resolver: { + assetExts: assetExts.filter((ext) => ext !== 'svg'), + sourceExts: [...sourceExts, 'svg'], + resolverMainFields: ['sbmodern', 'browser', 'main'], + }, }, - }), - babelTransformerPath: require.resolve('react-native-svg-transformer'), - }, - resolver: { - assetExts: assetExts.filter((ext) => ext !== 'svg'), - sourceExts: [...sourceExts, 'svg'], - resolverMainFields: ['sbmodern', 'browser', 'main'], - }, - }, - { - // Change this to true to see debugging info. - // Useful if you have issues resolving modules - projectRoot: __dirname, watchFolders: [], debug: false, - // all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx' - extensions: [], - } - ); -})(); -`); + { + // Change this to true to see debugging info. + // Useful if you have issues resolving modules + projectRoot: __dirname, + watchFolders: [], + debug: false, + // all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx' + extensions: [], + } + ); + })(); + " + `); }); it(`should not udpate metro.config.js if projectRoot already exists`, async () => { @@ -145,44 +149,44 @@ module.exports = (async () => { ); await update(tree); - expect(tree.read('apps/products/metro.config.js', 'utf-8')).toEqual( - ` -const { withNxMetro } = require('@nrwl/react-native'); -const { getDefaultConfig } = require('metro-config'); + expect(tree.read('apps/products/metro.config.js', 'utf-8')) + .toMatchInlineSnapshot(` + "const { withNxMetro } = require('@nrwl/react-native'); + const { getDefaultConfig } = require('metro-config'); -module.exports = (async () => { - const { - resolver: { sourceExts, assetExts }, - } = await getDefaultConfig(); - // console.log(getModulesRunBeforeMainModule); - return withNxMetro( - { - transformer: { - getTransformOptions: async () => ({ - transform: { - experimentalImportSupport: false, - inlineRequires: true, + module.exports = (async () => { + const { + resolver: { sourceExts, assetExts }, + } = await getDefaultConfig(); + // console.log(getModulesRunBeforeMainModule); + return withNxMetro( + { + transformer: { + getTransformOptions: async () => ({ + transform: { + experimentalImportSupport: false, + inlineRequires: true, + }, + }), + babelTransformerPath: require.resolve('react-native-svg-transformer'), + }, + resolver: { + assetExts: assetExts.filter((ext) => ext !== 'svg'), + sourceExts: [...sourceExts, 'svg'], + resolverMainFields: ['sbmodern', 'browser', 'main'], + }, }, - }), - babelTransformerPath: require.resolve('react-native-svg-transformer'), - }, - resolver: { - assetExts: assetExts.filter((ext) => ext !== 'svg'), - sourceExts: [...sourceExts, 'svg'], - resolverMainFields: ['sbmodern', 'browser', 'main'], - }, - }, - { - projectRoot: __dirname, - // Change this to true to see debugging info. - // Useful if you have issues resolving modules - debug: false, - // all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx' - extensions: [], - } - ); -})(); -` - ); + { + projectRoot: __dirname, + // Change this to true to see debugging info. + // Useful if you have issues resolving modules + debug: false, + // all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx' + extensions: [], + } + ); + })(); + " + `); }); }); diff --git a/packages/expo/src/migrations/update-15-0-3/change-jest-preset.spec.ts b/packages/expo/src/migrations/update-15-0-3/change-jest-preset.spec.ts index 852e7ac48ea1f..05c871bc3172c 100644 --- a/packages/expo/src/migrations/update-15-0-3/change-jest-preset.spec.ts +++ b/packages/expo/src/migrations/update-15-0-3/change-jest-preset.spec.ts @@ -41,7 +41,7 @@ describe('Change expo jest preset', () => { await update(tree); const jestConfig = tree.read('apps/products/jest.config.ts', 'utf-8'); - expect(jestConfig).toContain(`"preset": "react-native"`); + expect(jestConfig).toContain(`preset: 'react-native'`); }); it(`should remove transform if the code contains existing preprocessor`, async () => { diff --git a/packages/jest/src/generators/jest-project/__snapshots__/jest-project.spec.ts.snap b/packages/jest/src/generators/jest-project/__snapshots__/jest-project.spec.ts.snap index c3c8441740b7a..d406a1b811fb6 100644 --- a/packages/jest/src/generators/jest-project/__snapshots__/jest-project.spec.ts.snap +++ b/packages/jest/src/generators/jest-project/__snapshots__/jest-project.spec.ts.snap @@ -6,10 +6,13 @@ export default { displayName: 'lib1', preset: '../../jest.preset.js', transform: { - '^.+\\\\\\\\.[tj]sx?$': ['@swc/jest', { jsc: { transform: { react: { runtime: 'automatic' } } } }] + '^.+\\\\\\\\.[tj]sx?$': [ + '@swc/jest', + { jsc: { transform: { react: { runtime: 'automatic' } } } }, + ], }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], - coverageDirectory: '../../coverage/libs/lib1' + coverageDirectory: '../../coverage/libs/lib1', }; " `; @@ -20,10 +23,10 @@ export default { displayName: 'lib1', preset: '../../jest.preset.js', transform: { - '^.+\\\\\\\\.[tj]sx?$': 'babel-jest' + '^.+\\\\\\\\.[tj]sx?$': 'babel-jest', }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], - coverageDirectory: '../../coverage/libs/lib1' + coverageDirectory: '../../coverage/libs/lib1', }; " `; @@ -34,10 +37,10 @@ export default { displayName: 'lib1', preset: '../../jest.preset.js', transform: { - '^.+\\\\\\\\.[tj]s$': 'babel-jest' + '^.+\\\\\\\\.[tj]s$': 'babel-jest', }, moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../coverage/libs/lib1' + coverageDirectory: '../../coverage/libs/lib1', }; " `; @@ -63,7 +66,7 @@ export default { 'jest-preset-angular/build/serializers/no-ng-attributes', 'jest-preset-angular/build/serializers/ng-snapshot', 'jest-preset-angular/build/serializers/html-comment', - ] + ], }; " `; @@ -73,7 +76,7 @@ exports[`jestProject should create a jest.config.ts 1`] = ` export default { displayName: 'lib1', preset: '../../jest.preset.js', - coverageDirectory: '../../coverage/libs/lib1' + coverageDirectory: '../../coverage/libs/lib1', }; " `; @@ -99,7 +102,7 @@ export default { 'jest-preset-angular/build/serializers/no-ng-attributes', 'jest-preset-angular/build/serializers/ng-snapshot', 'jest-preset-angular/build/serializers/html-comment', - ] + ], }; " `; diff --git a/packages/jest/src/migrations/update-14-0-0/__snapshots__/update-jest-config-ext.spec.ts.snap b/packages/jest/src/migrations/update-14-0-0/__snapshots__/update-jest-config-ext.spec.ts.snap index 6b60da1d51d4b..f804e2e68ef93 100644 --- a/packages/jest/src/migrations/update-14-0-0/__snapshots__/update-jest-config-ext.spec.ts.snap +++ b/packages/jest/src/migrations/update-14-0-0/__snapshots__/update-jest-config-ext.spec.ts.snap @@ -9,13 +9,13 @@ module.exports = { globals: { 'ts-jest': { tsconfig: '/tsconfig.spec.json', - } + }, }, transform: { - '^.+\\\\\\\\.[tj]sx?$': 'ts-jest' + '^.+\\\\\\\\.[tj]sx?$': 'ts-jest', }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], - coverageDirectory: '../../coverage/libs/lib-one' + coverageDirectory: '../../coverage/libs/lib-one', }; " `; @@ -39,20 +39,19 @@ Object { exports[`Jest Migration (v14.0.0) should produce the same results when running multiple times 2`] = ` "/* eslint-disable */ - module.exports = { displayName: 'lib-one', preset: '../../jest.preset.js', globals: { 'ts-jest': { tsconfig: '/tsconfig.spec.json', - } + }, }, transform: { - '^.+\\\\\\\\.[tj]sx?$': 'ts-jest' + '^.+\\\\\\\\.[tj]sx?$': 'ts-jest', }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], - coverageDirectory: '../../coverage/libs/lib-one' + coverageDirectory: '../../coverage/libs/lib-one', }; " `; @@ -66,13 +65,13 @@ module.exports = { globals: { 'ts-jest': { tsconfig: '/tsconfig.spec.json', - } + }, }, transform: { - '^.+\\\\\\\\.[tj]sx?$': 'ts-jest' + '^.+\\\\\\\\.[tj]sx?$': 'ts-jest', }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], - coverageDirectory: '../../coverage/libs/lib-one' + coverageDirectory: '../../coverage/libs/lib-one', }; " `; diff --git a/packages/jest/src/migrations/update-15-8-0/__snapshots__/update-configs-jest-29.spec.ts.snap b/packages/jest/src/migrations/update-15-8-0/__snapshots__/update-configs-jest-29.spec.ts.snap index 903ff8202052b..340613e88ffcf 100644 --- a/packages/jest/src/migrations/update-15-8-0/__snapshots__/update-configs-jest-29.spec.ts.snap +++ b/packages/jest/src/migrations/update-15-8-0/__snapshots__/update-configs-jest-29.spec.ts.snap @@ -3,24 +3,24 @@ exports[`Jest Migration - jest 29 update configs should NOT update ts-jest with no globals are preset 1`] = ` "const nxPreset = require('@nrwl/jest/preset').default; module.exports = { -...nxPreset, -testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'], -transform: { - '^.+\\\\.(ts|js|html)$': 'ts-jest', + ...nxPreset, + testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'], + transform: { + '^.+.(ts|js|html)$': 'ts-jest', }, -resolver: '@nrwl/jest/plugins/resolver', -moduleFileExtensions: ['ts', 'js', 'html'], -coverageReporters: ['html'], -/* TODO: Update to latest Jest snapshotFormat - * By default Nx has kept the older style of Jest Snapshot formats - * to prevent breaking of any existing tests with snapshots. - * It's recommend you update to the latest format. - * You can do this by removing snapshotFormat property - * and running tests with --update-snapshot flag. - * Example: \\"nx affected --targets=test --update-snapshot\\" - * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format - */ -snapshotFormat: { escapeString: true, printBasicPrototype: true } + resolver: '@nrwl/jest/plugins/resolver', + moduleFileExtensions: ['ts', 'js', 'html'], + coverageReporters: ['html'], + /* TODO: Update to latest Jest snapshotFormat + * By default Nx has kept the older style of Jest Snapshot formats + * to prevent breaking of any existing tests with snapshots. + * It's recommend you update to the latest format. + * You can do this by removing snapshotFormat property + * and running tests with --update-snapshot flag. + * Example: \\"nx affected --targets=test --update-snapshot\\" + * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format + */ + snapshotFormat: { escapeString: true, printBasicPrototype: true }, }; " `; @@ -28,57 +28,62 @@ snapshotFormat: { escapeString: true, printBasicPrototype: true } exports[`Jest Migration - jest 29 update configs should add snapshot config with no root preset 1`] = ` "/* eslint-disable */ export default { -displayName: 'my-lib', -preset: '../../jest.preset.js', -globals: { }, -transform: { - '^.+\\\\\\\\.[tj]sx?$': ['ts-jest', { - tsconfig: '/tsconfig.spec.json', - }] + displayName: 'my-lib', + preset: '../../jest.preset.js', + globals: {}, + transform: { + '^.+\\\\\\\\.[tj]sx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], }, -moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], -coverageDirectory: '../../coverage/libs/my-lib', -/* TODO: Update to latest Jest snapshotFormat - * By default Nx has kept the older style of Jest Snapshot formats - * to prevent breaking of any existing tests with snapshots. - * It's recommend you update to the latest format. - * You can do this by removing snapshotFormat property - * and running tests with --update-snapshot flag. - * Example: From within the project directory, run \\"nx test --update-snapshot\\" - * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format - */ -snapshotFormat: { escapeString: true, printBasicPrototype: true } + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../coverage/libs/my-lib', + /* TODO: Update to latest Jest snapshotFormat + * By default Nx has kept the older style of Jest Snapshot formats + * to prevent breaking of any existing tests with snapshots. + * It's recommend you update to the latest format. + * You can do this by removing snapshotFormat property + * and running tests with --update-snapshot flag. + * Example: From within the project directory, run \\"nx test --update-snapshot\\" + * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format + */ + snapshotFormat: { escapeString: true, printBasicPrototype: true }, }; " `; exports[`Jest Migration - jest 29 update configs should add snapshot config with no root preset 2`] = ` "module.exports = { -transform: { - '^.+\\\\\\\\.[tj]sx?$': ['ts-jest', { - tsconfig: '/tsconfig.spec.json' - }] -}, -// I am a comment and shouldn't be removed -moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], -globals: { something: 'else', -abc: [1234, true, {abc: 'yes'}] }, -/** - * Multi-line comment shouldn't be removed - */ -displayName: 'jest', -testEnvironment: 'node', -preset: '../../jest.preset.js', -/* TODO: Update to latest Jest snapshotFormat - * By default Nx has kept the older style of Jest Snapshot formats - * to prevent breaking of any existing tests with snapshots. - * It's recommend you update to the latest format. - * You can do this by removing snapshotFormat property - * and running tests with --update-snapshot flag. - * Example: From within the project directory, run \\"nx test --update-snapshot\\" - * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format - */ -snapshotFormat: { escapeString: true, printBasicPrototype: true } + transform: { + '^.+\\\\\\\\.[tj]sx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], + }, + // I am a comment and shouldn't be removed + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], + globals: { something: 'else', abc: [1234, true, { abc: 'yes' }] }, + /** + * Multi-line comment shouldn't be removed + */ + displayName: 'jest', + testEnvironment: 'node', + preset: '../../jest.preset.js', + /* TODO: Update to latest Jest snapshotFormat + * By default Nx has kept the older style of Jest Snapshot formats + * to prevent breaking of any existing tests with snapshots. + * It's recommend you update to the latest format. + * You can do this by removing snapshotFormat property + * and running tests with --update-snapshot flag. + * Example: From within the project directory, run \\"nx test --update-snapshot\\" + * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format + */ + snapshotFormat: { escapeString: true, printBasicPrototype: true }, }; " `; @@ -88,84 +93,93 @@ exports[`Jest Migration - jest 29 update configs should be idempotent 1`] = ` export default { displayName: 'my-lib', preset: '../../jest.preset.js', - globals: { }, + globals: {}, transform: { - '^.+\\\\\\\\.[tj]sx?$': ['ts-jest', { - tsconfig: '/tsconfig.spec.json', - }] + '^.+\\\\\\\\.[tj]sx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], - coverageDirectory: '../../coverage/libs/my-lib' + coverageDirectory: '../../coverage/libs/my-lib', }; " `; exports[`Jest Migration - jest 29 update configs should be idempotent 2`] = ` "module.exports = { -transform: { - '^.+\\\\\\\\.[tj]sx?$': ['ts-jest', { - tsconfig: '/tsconfig.spec.json' - }] -}, -// I am a comment and shouldn't be removed -moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], -globals: { something: 'else', -abc: [1234, true, {abc: 'yes'}] }, -/** - * Multi-line comment shouldn't be removed - */ -displayName: 'jest', -testEnvironment: 'node', -preset: '../../jest.preset.js' + transform: { + '^.+\\\\\\\\.[tj]sx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], + }, + // I am a comment and shouldn't be removed + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], + globals: { something: 'else', abc: [1234, true, { abc: 'yes' }] }, + /** + * Multi-line comment shouldn't be removed + */ + displayName: 'jest', + testEnvironment: 'node', + preset: '../../jest.preset.js', }; " `; exports[`Jest Migration - jest 29 update configs should update globalThis.ngJest.teardown to testEnvironmentOptions 1`] = ` -"globalThis.ngJest = { - -} +"globalThis.ngJest = {}; export default { -globals: { }, -transform: { - '^.+.(ts|mjs|js|html)$': ['jest-preset-angular', { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\\\.(html|svg)$', - }], + globals: {}, + transform: { + '^.+.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '.(html|svg)$', + }, + ], }, -moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], -displayName: 'jest', -testEnvironment: 'node', -preset: '../../jest.preset.js', -testEnvironmentOptions: { teardown: true }, -};" + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], + displayName: 'jest', + testEnvironment: 'node', + preset: '../../jest.preset.js', + testEnvironmentOptions: { teardown: true }, +}; +" `; exports[`Jest Migration - jest 29 update configs should update globalThis.ngJest.teardown to testEnvironmentOptions 2`] = ` -" -globalThis.ngJest = { +"globalThis.ngJest = { ngcc: true, - -} +}; module.exports = { - globals: { }, + globals: {}, transform: { - '^.+.(ts|mjs|js|html)$': ['jest-preset-angular', { - tsconfig: '/tsconfig.spec.json', - stringifyContentPathRegex: '\\\\.(html|svg)$', - }], + '^.+.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '.(html|svg)$', + }, + ], }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], testEnvironmentOptions: { - blah: 123, - teardown: false + blah: 123, + teardown: false, }, displayName: 'jest', testEnvironment: 'node', preset: '../../jest.preset.js', -};" +}; +" `; exports[`Jest Migration - jest 29 update configs should update jest.config.ts 1`] = ` @@ -173,56 +187,61 @@ exports[`Jest Migration - jest 29 update configs should update jest.config.ts 1` export default { displayName: 'my-lib', preset: '../../jest.preset.js', - globals: { }, + globals: {}, transform: { - '^.+\\\\\\\\.[tj]sx?$': ['ts-jest', { - tsconfig: '/tsconfig.spec.json', - }] + '^.+\\\\\\\\.[tj]sx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], - coverageDirectory: '../../coverage/libs/my-lib' + coverageDirectory: '../../coverage/libs/my-lib', }; " `; exports[`Jest Migration - jest 29 update configs should update jest.config.ts 2`] = ` "module.exports = { -transform: { - '^.+\\\\\\\\.[tj]sx?$': ['ts-jest', { - tsconfig: '/tsconfig.spec.json' - }] -}, -// I am a comment and shouldn't be removed -moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], -globals: { something: 'else', -abc: [1234, true, {abc: 'yes'}] }, -/** - * Multi-line comment shouldn't be removed - */ -displayName: 'jest', -testEnvironment: 'node', -preset: '../../jest.preset.js' + transform: { + '^.+\\\\\\\\.[tj]sx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], + }, + // I am a comment and shouldn't be removed + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], + globals: { something: 'else', abc: [1234, true, { abc: 'yes' }] }, + /** + * Multi-line comment shouldn't be removed + */ + displayName: 'jest', + testEnvironment: 'node', + preset: '../../jest.preset.js', }; " `; exports[`Jest Migration - jest 29 update configs should update root preset 1`] = ` -" - const nxPreset = require('@nrwl/jest/preset').default; +"const nxPreset = require('@nrwl/jest/preset').default; - module.exports = { -...nxPreset, -/* TODO: Update to latest Jest snapshotFormat - * By default Nx has kept the older style of Jest Snapshot formats - * to prevent breaking of any existing tests with snapshots. - * It's recommend you update to the latest format. - * You can do this by removing snapshotFormat property - * and running tests with --update-snapshot flag. - * Example: \\"nx affected --targets=test --update-snapshot\\" - * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format - */ -snapshotFormat: { escapeString: true, printBasicPrototype: true } -}" +module.exports = { + ...nxPreset, + /* TODO: Update to latest Jest snapshotFormat + * By default Nx has kept the older style of Jest Snapshot formats + * to prevent breaking of any existing tests with snapshots. + * It's recommend you update to the latest format. + * You can do this by removing snapshotFormat property + * and running tests with --update-snapshot flag. + * Example: \\"nx affected --targets=test --update-snapshot\\" + * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format + */ + snapshotFormat: { escapeString: true, printBasicPrototype: true }, +}; +" `; exports[`Jest Migration - jest 29 update configs should update root preset 2`] = ` @@ -230,35 +249,40 @@ exports[`Jest Migration - jest 29 update configs should update root preset 2`] = export default { displayName: 'my-lib', preset: '../../jest.preset.js', - globals: { }, + globals: {}, transform: { - '^.+\\\\\\\\.[tj]sx?$': ['ts-jest', { - tsconfig: '/tsconfig.spec.json', - }] + '^.+\\\\\\\\.[tj]sx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], - coverageDirectory: '../../coverage/libs/my-lib' + coverageDirectory: '../../coverage/libs/my-lib', }; " `; exports[`Jest Migration - jest 29 update configs should update root preset 3`] = ` "module.exports = { -transform: { - '^.+\\\\\\\\.[tj]sx?$': ['ts-jest', { - tsconfig: '/tsconfig.spec.json' - }] -}, -// I am a comment and shouldn't be removed -moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], -globals: { something: 'else', -abc: [1234, true, {abc: 'yes'}] }, -/** - * Multi-line comment shouldn't be removed - */ -displayName: 'jest', -testEnvironment: 'node', -preset: '../../jest.preset.js' + transform: { + '^.+\\\\\\\\.[tj]sx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], + }, + // I am a comment and shouldn't be removed + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], + globals: { something: 'else', abc: [1234, true, { abc: 'yes' }] }, + /** + * Multi-line comment shouldn't be removed + */ + displayName: 'jest', + testEnvironment: 'node', + preset: '../../jest.preset.js', }; " `; @@ -266,28 +290,30 @@ preset: '../../jest.preset.js' exports[`Jest Migration - jest 29 update configs should update root preset if ts-jest is preset 1`] = ` "const nxPreset = require('@nrwl/jest/preset').default; module.exports = { -...nxPreset, -testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'], -globals: { something: 'else', -abc: [1234, true, {abc: 'yes'}] }, -transform: { - '^.+\\\\.(ts|js|html)$': ['ts-jest', { - tsconfig: '/tsconfig.spec.json' - }], + ...nxPreset, + testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'], + globals: { something: 'else', abc: [1234, true, { abc: 'yes' }] }, + transform: { + '^.+.(ts|js|html)$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], }, -resolver: '@nrwl/jest/plugins/resolver', -moduleFileExtensions: ['ts', 'js', 'html'], -coverageReporters: ['html'], -/* TODO: Update to latest Jest snapshotFormat - * By default Nx has kept the older style of Jest Snapshot formats - * to prevent breaking of any existing tests with snapshots. - * It's recommend you update to the latest format. - * You can do this by removing snapshotFormat property - * and running tests with --update-snapshot flag. - * Example: \\"nx affected --targets=test --update-snapshot\\" - * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format - */ -snapshotFormat: { escapeString: true, printBasicPrototype: true } + resolver: '@nrwl/jest/plugins/resolver', + moduleFileExtensions: ['ts', 'js', 'html'], + coverageReporters: ['html'], + /* TODO: Update to latest Jest snapshotFormat + * By default Nx has kept the older style of Jest Snapshot formats + * to prevent breaking of any existing tests with snapshots. + * It's recommend you update to the latest format. + * You can do this by removing snapshotFormat property + * and running tests with --update-snapshot flag. + * Example: \\"nx affected --targets=test --update-snapshot\\" + * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format + */ + snapshotFormat: { escapeString: true, printBasicPrototype: true }, }; " `; @@ -385,35 +411,40 @@ exports[`Jest Migration - jest 29 update configs should work with multiple proje export default { displayName: 'my-lib', preset: '../../jest.preset.js', - globals: { }, + globals: {}, transform: { - '^.+\\\\\\\\.[tj]sx?$': ['ts-jest', { - tsconfig: '/tsconfig.spec.json', - }] + '^.+\\\\\\\\.[tj]sx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], - coverageDirectory: '../../coverage/libs/my-lib' + coverageDirectory: '../../coverage/libs/my-lib', }; " `; exports[`Jest Migration - jest 29 update configs should work with multiple projects + configs 2`] = ` "module.exports = { -transform: { - '^.+\\\\\\\\.[tj]sx?$': ['ts-jest', { - tsconfig: '/tsconfig.spec.json' - }] -}, -// I am a comment and shouldn't be removed -moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], -globals: { something: 'else', -abc: [1234, true, {abc: 'yes'}] }, -/** - * Multi-line comment shouldn't be removed - */ -displayName: 'jest', -testEnvironment: 'node', -preset: '../../jest.preset.js' + transform: { + '^.+\\\\\\\\.[tj]sx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], + }, + // I am a comment and shouldn't be removed + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], + globals: { something: 'else', abc: [1234, true, { abc: 'yes' }] }, + /** + * Multi-line comment shouldn't be removed + */ + displayName: 'jest', + testEnvironment: 'node', + preset: '../../jest.preset.js', }; " `; @@ -423,35 +454,40 @@ exports[`Jest Migration - jest 29 update configs should work with multiple proje export default { displayName: 'another-lib', preset: '../../jest.preset.js', - globals: { }, + globals: {}, transform: { - '^.+\\\\\\\\.[tj]sx?$': ['ts-jest', { - tsconfig: '/tsconfig.spec.json', - }] + '^.+\\\\\\\\.[tj]sx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], - coverageDirectory: '../../coverage/libs/another-lib' + coverageDirectory: '../../coverage/libs/another-lib', }; " `; exports[`Jest Migration - jest 29 update configs should work with multiple projects + configs 4`] = ` "module.exports = { -transform: { - '^.+\\\\\\\\.[tj]sx?$': ['ts-jest', { - tsconfig: '/tsconfig.spec.json' - }] -}, -// I am a comment and shouldn't be removed -moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], -globals: { something: 'else', -abc: [1234, true, {abc: 'yes'}] }, -/** - * Multi-line comment shouldn't be removed - */ -displayName: 'jest', -testEnvironment: 'node', -preset: '../../jest.preset.js' + transform: { + '^.+\\\\\\\\.[tj]sx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], + }, + // I am a comment and shouldn't be removed + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], + globals: { something: 'else', abc: [1234, true, { abc: 'yes' }] }, + /** + * Multi-line comment shouldn't be removed + */ + displayName: 'jest', + testEnvironment: 'node', + preset: '../../jest.preset.js', }; " `; diff --git a/packages/jest/src/migrations/update-15-8-0/__snapshots__/update-tests-jest-29.spec.ts.snap b/packages/jest/src/migrations/update-15-8-0/__snapshots__/update-tests-jest-29.spec.ts.snap index 1e0bc4d9c2b93..29fe6f352a35b 100644 --- a/packages/jest/src/migrations/update-15-8-0/__snapshots__/update-tests-jest-29.spec.ts.snap +++ b/packages/jest/src/migrations/update-15-8-0/__snapshots__/update-tests-jest-29.spec.ts.snap @@ -1,10 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Jest Migration - jest 29 mocked usage in tests should be idempotent 1`] = ` -" -import{ Mocked, MockedShallow } from 'jest-mock'; -import {expect, jest, test} from '@jest/globals'; -import {song} from './song'; +"import { Mocked, MockedShallow } from 'jest-mock'; +import { expect, jest, test } from '@jest/globals'; +import { song } from './song'; jest.mock('./song'); jest.spyOn(console, 'log'); @@ -27,16 +26,17 @@ test('direct usage', () => { console.log('one more time'); - expect(jest.mocked(console.log, {shallow: true}).mock.calls).toHaveLength(1); + expect(jest.mocked(console.log, { shallow: true }).mock.calls).toHaveLength( + 1 + ); }); - " +" `; exports[`Jest Migration - jest 29 mocked usage in tests should be idempotent 2`] = ` -" -const { Mocked, MockedShallow } = require('jest-mock'); -const {expect, jest, test} = require('@jest/globals'); -const {song} = require('./song'); +"const { Mocked, MockedShallow } = require('jest-mock'); +const { expect, jest, test } = require('@jest/globals'); +const { song } = require('./song'); jest.mock('./song'); jest.spyOn(console, 'log'); @@ -59,16 +59,17 @@ test('direct usage', () => { console.log('one more time'); - expect(jest.mocked(console.log, {shallow: true}).mock.calls).toHaveLength(1); + expect(jest.mocked(console.log, { shallow: true }).mock.calls).toHaveLength( + 1 + ); }); " `; exports[`Jest Migration - jest 29 mocked usage in tests should be idempotent 3`] = ` -" -import{ Mocked, MockedShallow } from 'jest-mock'; -import {expect, jest, test} from '@jest/globals'; -import {song} from './song'; +"import { Mocked, MockedShallow } from 'jest-mock'; +import { expect, jest, test } from '@jest/globals'; +import { song } from './song'; jest.mock('./song'); jest.spyOn(console, 'log'); @@ -91,16 +92,17 @@ test('direct usage', () => { console.log('one more time'); - expect(jest.mocked(console.log, {shallow: true}).mock.calls).toHaveLength(1); + expect(jest.mocked(console.log, { shallow: true }).mock.calls).toHaveLength( + 1 + ); }); - " +" `; exports[`Jest Migration - jest 29 mocked usage in tests should be idempotent 4`] = ` -" -const { Mocked, MockedShallow } = require('jest-mock'); -const {expect, jest, test} = require('@jest/globals'); -const {song} = require('./song'); +"const { Mocked, MockedShallow } = require('jest-mock'); +const { expect, jest, test } = require('@jest/globals'); +const { song } = require('./song'); jest.mock('./song'); jest.spyOn(console, 'log'); @@ -123,16 +125,17 @@ test('direct usage', () => { console.log('one more time'); - expect(jest.mocked(console.log, {shallow: true}).mock.calls).toHaveLength(1); + expect(jest.mocked(console.log, { shallow: true }).mock.calls).toHaveLength( + 1 + ); }); " `; exports[`Jest Migration - jest 29 mocked usage in tests should not update anything if there are no tests 1`] = ` -" -import{ Mocked, MockedShallow } from 'jest-mock'; -import {expect, jest, test} from '@jest/globals'; -import {song} from './song'; +"import { Mocked, MockedShallow } from 'jest-mock'; +import { expect, jest, test } from '@jest/globals'; +import { song } from './song'; jest.mock('./song'); jest.spyOn(console, 'log'); @@ -155,16 +158,17 @@ test('direct usage', () => { console.log('one more time'); - expect(jest.mocked(console.log, {shallow: true}).mock.calls).toHaveLength(1); + expect(jest.mocked(console.log, { shallow: true }).mock.calls).toHaveLength( + 1 + ); }); - " +" `; exports[`Jest Migration - jest 29 mocked usage in tests should not update anything if there are no tests 2`] = ` -" -const { Mocked, MockedShallow } = require('jest-mock'); -const {expect, jest, test} = require('@jest/globals'); -const {song} = require('./song'); +"const { Mocked, MockedShallow } = require('jest-mock'); +const { expect, jest, test } = require('@jest/globals'); +const { song } = require('./song'); jest.mock('./song'); jest.spyOn(console, 'log'); @@ -187,7 +191,9 @@ test('direct usage', () => { console.log('one more time'); - expect(jest.mocked(console.log, {shallow: true}).mock.calls).toHaveLength(1); + expect(jest.mocked(console.log, { shallow: true }).mock.calls).toHaveLength( + 1 + ); }); " `; diff --git a/packages/js/src/generators/init/init.ts b/packages/js/src/generators/init/init.ts index 7a0cc7a272261..7a4fd73998942 100644 --- a/packages/js/src/generators/init/init.ts +++ b/packages/js/src/generators/init/init.ts @@ -73,23 +73,9 @@ export async function initGenerator( : () => {}; tasks.push(installTask); + ensurePackage('prettier', prettierVersion); if (!schema.skipFormat) { - try { - ensurePackage('prettier', prettierVersion); - await formatFiles(tree); - } catch (e) { - if (!formatTaskAdded) { - formatTaskAdded = true; - tasks.push(async () => { - try { - const prettierCli = await import('prettier/cli'); - await prettierCli.run(['.', '--write']); - } catch { - // If --skipPackageJson is passed then prettier is not installed. - } - }); - } - } + await formatFiles(tree); } return async () => { diff --git a/packages/js/src/generators/library/__snapshots__/library.spec.ts.snap b/packages/js/src/generators/library/__snapshots__/library.spec.ts.snap index c3257465fa0c9..9fa7030c34d22 100644 --- a/packages/js/src/generators/library/__snapshots__/library.spec.ts.snap +++ b/packages/js/src/generators/library/__snapshots__/library.spec.ts.snap @@ -2,7 +2,7 @@ exports[`lib --unit-test-runner jest should generate test configuration with swc and js 1`] = ` "/* eslint-disable */ -const { readFileSync } = require('fs') +const { readFileSync } = require('fs'); // Reading the SWC compilation config and remove the \\"exclude\\" // for the test files to be compiled by SWC @@ -13,7 +13,7 @@ const { exclude: _, ...swcJestConfig } = JSON.parse( // disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves. // If we do not disable this, SWC Core will read .swcrc and won't transform our test files due to \\"exclude\\" if (swcJestConfig.swcrc === undefined) { - swcJestConfig.swcrc = false; + swcJestConfig.swcrc = false; } module.exports = { @@ -23,7 +23,7 @@ module.exports = { '^.+\\\\\\\\.[tj]s$': ['@swc/jest', swcJestConfig], }, moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../coverage/libs/my-lib' + coverageDirectory: '../../coverage/libs/my-lib', }; " `; diff --git a/packages/js/src/generators/library/library.spec.ts b/packages/js/src/generators/library/library.spec.ts index 608a2b14a305c..b6c0e5b544a98 100644 --- a/packages/js/src/generators/library/library.spec.ts +++ b/packages/js/src/generators/library/library.spec.ts @@ -692,10 +692,10 @@ describe('lib', () => { displayName: 'my-lib', preset: '../../jest.preset.js', transform: { - '^.+\\\\\\\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }] + '^.+\\\\\\\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], }, moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../coverage/libs/my-lib' + coverageDirectory: '../../coverage/libs/my-lib', }; " `); diff --git a/packages/js/src/utils/add-babel-inputs.ts b/packages/js/src/utils/add-babel-inputs.ts index 26780a0c4977c..ac5fad78b0de3 100644 --- a/packages/js/src/utils/add-babel-inputs.ts +++ b/packages/js/src/utils/add-babel-inputs.ts @@ -7,7 +7,7 @@ import { writeJson, } from '@nrwl/devkit'; -export async function addBabelInputs(tree: Tree) { +export function addBabelInputs(tree: Tree) { const nxJson = readNxJson(tree); let globalBabelFile = ['babel.config.js', 'babel.config.json'].find((file) => tree.exists(file) @@ -29,6 +29,4 @@ export async function addBabelInputs(tree: Tree) { } updateNxJson(tree, nxJson); - - await formatFiles(tree); } diff --git a/packages/linter/src/generators/lint-project/__snapshots__/lint-project.spec.ts.snap b/packages/linter/src/generators/lint-project/__snapshots__/lint-project.spec.ts.snap index fd96b0de82605..aab3a77885221 100644 --- a/packages/linter/src/generators/lint-project/__snapshots__/lint-project.spec.ts.snap +++ b/packages/linter/src/generators/lint-project/__snapshots__/lint-project.spec.ts.snap @@ -2,34 +2,19 @@ exports[`@nrwl/linter:lint-project --linter eslint should extend to .eslintrc.js when an .eslintrc.js already exist 1`] = ` "{ - \\"extends\\": [ - \\"../../.eslintrc.js\\" - ], - \\"ignorePatterns\\": [ - \\"!**/*\\" - ], + \\"extends\\": [\\"../../.eslintrc.js\\"], + \\"ignorePatterns\\": [\\"!**/*\\"], \\"overrides\\": [ { - \\"files\\": [ - \\"*.ts\\", - \\"*.tsx\\", - \\"*.js\\", - \\"*.jsx\\" - ], + \\"files\\": [\\"*.ts\\", \\"*.tsx\\", \\"*.js\\", \\"*.jsx\\"], \\"rules\\": {} }, { - \\"files\\": [ - \\"*.ts\\", - \\"*.tsx\\" - ], + \\"files\\": [\\"*.ts\\", \\"*.tsx\\"], \\"rules\\": {} }, { - \\"files\\": [ - \\"*.js\\", - \\"*.jsx\\" - ], + \\"files\\": [\\"*.js\\", \\"*.jsx\\"], \\"rules\\": {} } ] @@ -39,34 +24,19 @@ exports[`@nrwl/linter:lint-project --linter eslint should extend to .eslintrc.js exports[`@nrwl/linter:lint-project --linter eslint should generate a eslint config 1`] = ` "{ - \\"extends\\": [ - \\"../../.eslintrc.json\\" - ], - \\"ignorePatterns\\": [ - \\"!**/*\\" - ], + \\"extends\\": [\\"../../.eslintrc.json\\"], + \\"ignorePatterns\\": [\\"!**/*\\"], \\"overrides\\": [ { - \\"files\\": [ - \\"*.ts\\", - \\"*.tsx\\", - \\"*.js\\", - \\"*.jsx\\" - ], + \\"files\\": [\\"*.ts\\", \\"*.tsx\\", \\"*.js\\", \\"*.jsx\\"], \\"rules\\": {} }, { - \\"files\\": [ - \\"*.ts\\", - \\"*.tsx\\" - ], + \\"files\\": [\\"*.ts\\", \\"*.tsx\\"], \\"rules\\": {} }, { - \\"files\\": [ - \\"*.js\\", - \\"*.jsx\\" - ], + \\"files\\": [\\"*.js\\", \\"*.jsx\\"], \\"rules\\": {} } ] diff --git a/packages/linter/src/generators/workspace-rule/__snapshots__/workspace-rule.spec.ts.snap b/packages/linter/src/generators/workspace-rule/__snapshots__/workspace-rule.spec.ts.snap index bda94680ad6ef..a349d7485c3ab 100644 --- a/packages/linter/src/generators/workspace-rule/__snapshots__/workspace-rule.spec.ts.snap +++ b/packages/linter/src/generators/workspace-rule/__snapshots__/workspace-rule.spec.ts.snap @@ -172,14 +172,14 @@ exports[`@nrwl/linter:workspace-rule should update the plugin index.ts with the "import { RULE_NAME as myRuleName, rule as myRule } from './rules/my-rule'; /** * Import your custom workspace rules at the top of this file. - * + * * For example: - * + * * import { RULE_NAME as myCustomRuleName, rule as myCustomRule } from './rules/my-custom-rule'; - * + * * In order to quickly get started with writing rules you can use the * following generator command and provide your desired rule name: - * + * * \`\`\`sh * npx nx g @nrwl/linter:workspace-rule {{ NEW_RULE_NAME }} * \`\`\` @@ -188,15 +188,14 @@ exports[`@nrwl/linter:workspace-rule should update the plugin index.ts with the module.exports = { /** * Apply the imported custom rules here. - * + * * For example (using the example import above): - * + * * rules: { * [myCustomRuleName]: myCustomRule * } */ - rules: {[myRuleName]: myRule -} + rules: { [myRuleName]: myRule }, }; " `; diff --git a/packages/linter/src/generators/workspace-rule/workspace-rule.spec.ts b/packages/linter/src/generators/workspace-rule/workspace-rule.spec.ts index b5f95609b07ca..3bfdd026c334d 100644 --- a/packages/linter/src/generators/workspace-rule/workspace-rule.spec.ts +++ b/packages/linter/src/generators/workspace-rule/workspace-rule.spec.ts @@ -57,14 +57,14 @@ describe('@nrwl/linter:workspace-rule', () => { "import { RULE_NAME as myRuleName, rule as myRule } from './rules/my-rule'; /** * Import your custom workspace rules at the top of this file. - * + * * For example: - * + * * import { RULE_NAME as myCustomRuleName, rule as myCustomRule } from './rules/my-custom-rule'; - * + * * In order to quickly get started with writing rules you can use the * following generator command and provide your desired rule name: - * + * * \`\`\`sh * npx nx g @nrwl/linter:workspace-rule {{ NEW_RULE_NAME }} * \`\`\` @@ -73,15 +73,14 @@ describe('@nrwl/linter:workspace-rule', () => { module.exports = { /** * Apply the imported custom rules here. - * + * * For example (using the example import above): - * + * * rules: { * [myCustomRuleName]: myCustomRule * } */ - rules: {[myRuleName]: myRule - } + rules: { [myRuleName]: myRule }, }; " `); @@ -109,13 +108,13 @@ describe('@nrwl/linter:workspace-rule', () => { .toMatchInlineSnapshot(` "import { RULE_NAME as myRuleName, rule as myRule } from './rules/my-rule'; - module.exports = { - rules: { - 'existing-rule-no-comma': 'error' - ,[myRuleName]: myRule - } - }; - " + module.exports = { + rules: { + 'existing-rule-no-comma': 'error', + [myRuleName]: myRule, + }, + }; + " `); // ------------------------------------------- EXISTING RULE, WITH TRAILING COMMA @@ -141,13 +140,13 @@ describe('@nrwl/linter:workspace-rule', () => { .toMatchInlineSnapshot(` "import { RULE_NAME as myRuleName, rule as myRule } from './rules/my-rule'; - module.exports = { - rules: { - 'existing-rule-with-comma': 'error', - [myRuleName]: myRule - } - }; - " + module.exports = { + rules: { + 'existing-rule-with-comma': 'error', + [myRuleName]: myRule, + }, + }; + " `); }); diff --git a/packages/linter/src/generators/workspace-rules-project/__snapshots__/workspace-rules-project.spec.ts.snap b/packages/linter/src/generators/workspace-rules-project/__snapshots__/workspace-rules-project.spec.ts.snap index a956d581eb8e9..6d19b3f994135 100644 --- a/packages/linter/src/generators/workspace-rules-project/__snapshots__/workspace-rules-project.spec.ts.snap +++ b/packages/linter/src/generators/workspace-rules-project/__snapshots__/workspace-rules-project.spec.ts.snap @@ -3,14 +3,14 @@ exports[`@nrwl/linter:workspace-rules-project should generate the required files 1`] = ` "/** * Import your custom workspace rules at the top of this file. - * + * * For example: - * + * * import { RULE_NAME as myCustomRuleName, rule as myCustomRule } from './rules/my-custom-rule'; - * + * * In order to quickly get started with writing rules you can use the * following generator command and provide your desired rule name: - * + * * \`\`\`sh * npx nx g @nrwl/linter:workspace-rule {{ NEW_RULE_NAME }} * \`\`\` @@ -19,14 +19,14 @@ exports[`@nrwl/linter:workspace-rules-project should generate the required files module.exports = { /** * Apply the imported custom rules here. - * + * * For example (using the example import above): - * + * * rules: { * [myCustomRuleName]: myCustomRule * } */ - rules: {} + rules: {}, }; " `; @@ -70,17 +70,9 @@ exports[`@nrwl/linter:workspace-rules-project should generate the required files \\"compilerOptions\\": { \\"outDir\\": \\"../../dist/out-tsc\\", \\"module\\": \\"commonjs\\", - \\"types\\": [ - \\"jest\\", - \\"node\\" - ] + \\"types\\": [\\"jest\\", \\"node\\"] }, - \\"include\\": [ - \\"jest.config.ts\\", - \\"**/*.test.ts\\", - \\"**/*.spec.ts\\", - \\"**/*.d.ts\\" - ] + \\"include\\": [\\"jest.config.ts\\", \\"**/*.test.ts\\", \\"**/*.spec.ts\\", \\"**/*.d.ts\\"] } " `; @@ -91,10 +83,13 @@ export default { displayName: 'eslint-rules', preset: '../../jest.preset.js', transform: { - '^.+\\\\\\\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }] + '^.+\\\\\\\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], }, moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../coverage/tools/eslint-rules',\\"moduleNameMapper\\": {\\"@eslint/eslintrc\\":\\"@eslint/eslintrc/dist/eslintrc-universal.cjs\\"} + coverageDirectory: '../../coverage/tools/eslint-rules', + moduleNameMapper: { + '@eslint/eslintrc': '@eslint/eslintrc/dist/eslintrc-universal.cjs', + }, }; " `; diff --git a/packages/linter/src/migrations/update-13-3-0/eslint-8-updates.spec.ts b/packages/linter/src/migrations/update-13-3-0/eslint-8-updates.spec.ts index d4ab5d2e75eb6..8dd4810106493 100644 --- a/packages/linter/src/migrations/update-13-3-0/eslint-8-updates.spec.ts +++ b/packages/linter/src/migrations/update-13-3-0/eslint-8-updates.spec.ts @@ -61,22 +61,24 @@ describe('eslint8Updates()', () => { expect(tree.read('tools/eslint-rules/jest.config.js').toString('utf-8')) .toMatchInlineSnapshot(` + "module.exports = { + displayName: 'eslint-rules', + preset: '../../jest.preset.js', + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + }, + }, + transform: { + '^.+.[tj]s$': 'ts-jest', + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/tools/eslint-rules', + moduleNameMapper: { + '@eslint/eslintrc': '@eslint/eslintrc/dist/eslintrc-universal.cjs', + }, + }; " - module.exports = { - displayName: 'eslint-rules', - preset: '../../jest.preset.js', - globals: { - 'ts-jest': { - tsconfig: '/tsconfig.spec.json', - }, - }, - transform: { - '^.+\\\\.[tj]s$': 'ts-jest', - }, - moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../coverage/tools/eslint-rules',\\"moduleNameMapper\\": {\\"@eslint/eslintrc\\":\\"@eslint/eslintrc/dist/eslintrc-universal.cjs\\"} - }; - " `); }); @@ -86,29 +88,28 @@ describe('eslint8Updates()', () => { expect( tree.read(`tools/eslint-rules/rules/existing-rule.ts`).toString('utf-8') ).toMatchInlineSnapshot(` + "import { ESLintUtils } from '@typescript-eslint/experimental-utils'; + + // NOTE: The rule will be available in ESLint configs as \\"@nrwl/nx/workspace/existing-rule\\" + export const RULE_NAME = 'existing-rule'; + + export const rule = ESLintUtils.RuleCreator(() => __filename)({ + name: RULE_NAME, + meta: { + type: 'problem', + docs: { + description: \`\`, + recommended: 'error', + }, + schema: [], + messages: {}, + }, + defaultOptions: [], + create(context) { + return {}; + }, + }); " - import { ESLintUtils } from '@typescript-eslint/experimental-utils'; - - // NOTE: The rule will be available in ESLint configs as \\"@nrwl/nx/workspace/existing-rule\\" - export const RULE_NAME = 'existing-rule'; - - export const rule = ESLintUtils.RuleCreator(() => __filename)({ - name: RULE_NAME, - meta: { - type: 'problem', - docs: { - description: \`\`, - recommended: 'error', - }, - schema: [], - messages: {}, - }, - defaultOptions: [], - create(context) { - return {}; - }, - }); - " `); }); }); diff --git a/packages/linter/src/migrations/update-14-4-4/experimental-to-utils-rules.spec.ts b/packages/linter/src/migrations/update-14-4-4/experimental-to-utils-rules.spec.ts index 04b36f9929b59..8ae06892492c1 100644 --- a/packages/linter/src/migrations/update-14-4-4/experimental-to-utils-rules.spec.ts +++ b/packages/linter/src/migrations/update-14-4-4/experimental-to-utils-rules.spec.ts @@ -70,50 +70,48 @@ describe('experimentalToUtilsUpdate()', () => { expect( tree.read(`tools/eslint-rules/rules/existing-rule.ts`).toString('utf-8') ).toMatchInlineSnapshot(` - " - import { ESLintUtils } from '@typescript-eslint/utils'; - import { rule, RULE_NAME } from './existing-rule'; - - // NOTE: The rule will be available in ESLint configs as \\"@nrwl/nx/workspace/existing-rule\\" - export const RULE_NAME = 'existing-rule'; - - export const rule = ESLintUtils.RuleCreator(() => __filename)({ - name: RULE_NAME, - meta: { - type: 'problem', - docs: { - description: \`\`, - recommended: 'error', - }, - schema: [], - messages: {}, - }, - defaultOptions: [], - create(context) { - return {}; - }, - }); - " - `); + "import { ESLintUtils } from '@typescript-eslint/utils'; + import { rule, RULE_NAME } from './existing-rule'; + + // NOTE: The rule will be available in ESLint configs as \\"@nrwl/nx/workspace/existing-rule\\" + export const RULE_NAME = 'existing-rule'; + + export const rule = ESLintUtils.RuleCreator(() => __filename)({ + name: RULE_NAME, + meta: { + type: 'problem', + docs: { + description: \`\`, + recommended: 'error', + }, + schema: [], + messages: {}, + }, + defaultOptions: [], + create(context) { + return {}; + }, + }); + " + `); expect( tree .read(`tools/eslint-rules/rules/existing-rule.spec.ts`) .toString('utf-8') ).toMatchInlineSnapshot(` + "import { TSESLint } from '@typescript-eslint/utils'; + import { rule, RULE_NAME } from './existing-rule'; + + const ruleTester = new TSESLint.RuleTester({ + parser: require.resolve('@typescript-eslint/parser'), + }); + + ruleTester.run(RULE_NAME, rule, { + valid: [\`const example = true;\`], + invalid: [], + }); " - import { TSESLint } from '@typescript-eslint/utils'; - import { rule, RULE_NAME } from './existing-rule'; - - const ruleTester = new TSESLint.RuleTester({ - parser: require.resolve('@typescript-eslint/parser'), - }); - - ruleTester.run(RULE_NAME, rule, { - valid: [\`const example = true;\`], - invalid: [], - }); - " `); }); @@ -123,13 +121,12 @@ describe('experimentalToUtilsUpdate()', () => { expect( tree.read(`tools/eslint-rules/rules/multi-import.ts`).toString('utf-8') ).toMatchInlineSnapshot(` - " - import { ESLintUtils } from '@typescript-eslint/utils'; - import { TSESLint } from '@typescript-eslint/utils'; - import { rule, RULE_NAME } from './existing-rule'; - - // NOTE: remaining code is irrelevant for this test - " - `); + "import { ESLintUtils } from '@typescript-eslint/utils'; + import { TSESLint } from '@typescript-eslint/utils'; + import { rule, RULE_NAME } from './existing-rule'; + + // NOTE: remaining code is irrelevant for this test + " + `); }); }); diff --git a/packages/nest/src/generators/library/__snapshots__/library.spec.ts.snap b/packages/nest/src/generators/library/__snapshots__/library.spec.ts.snap index 708fbf8cc3517..dd6a1226c484e 100644 --- a/packages/nest/src/generators/library/__snapshots__/library.spec.ts.snap +++ b/packages/nest/src/generators/library/__snapshots__/library.spec.ts.snap @@ -6,10 +6,10 @@ export default { displayName: 'my-lib', preset: '../../jest.preset.js', transform: { - '^.+\\\\\\\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }] + '^.+\\\\\\\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], }, moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../coverage/libs/my-lib' + coverageDirectory: '../../coverage/libs/my-lib', }; " `; @@ -21,10 +21,10 @@ export default { preset: '../../jest.preset.js', testEnvironment: 'node', transform: { - '^.+\\\\\\\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }] + '^.+\\\\\\\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], }, moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../coverage/libs/my-lib' + coverageDirectory: '../../coverage/libs/my-lib', }; " `; @@ -83,9 +83,9 @@ exports[`lib not nested should add the @Global decorator 1`] = ` @Global() @Module({ - controllers: [], - providers: [], - exports: [], + controllers: [], + providers: [], + exports: [], }) export class MyLibModule {} " @@ -152,9 +152,9 @@ import { MyLibController } from './my-lib.controller'; import { MyLibService } from './my-lib.service'; @Module({ - controllers: [MyLibController], - providers: [MyLibService], - exports: [MyLibService], + controllers: [MyLibController], + providers: [MyLibService], + exports: [MyLibService], }) export class MyLibModule {} " @@ -175,6 +175,5 @@ exports[`lib not nested should provide the controller and service 3`] = ` "export * from './lib/my-lib.controller'; export * from './lib/my-lib.service'; export * from './lib/my-lib.module'; - " `; diff --git a/packages/next/src/generators/application/__snapshots__/application.spec.ts.snap b/packages/next/src/generators/application/__snapshots__/application.spec.ts.snap new file mode 100644 index 0000000000000..118c567d7c983 --- /dev/null +++ b/packages/next/src/generators/application/__snapshots__/application.spec.ts.snap @@ -0,0 +1,423 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`app --style styled-jsx should use + +
+
+
+

+ Hello there, + Welcome my-app 👋 +

+
+ +
+
+

+ + + + You're up and running +

+ What's next? +
+
+ + + +
+
+ + + +
+

Next steps

+

Here are some things you can do with Nx:

+
+ + + + + Add UI library + +
+                # Generate UI lib
+                nx g @nrwl/next:library ui
+                # Add a component
+                nx g @nrwl/next:component button --project=ui
+              
+
+
+ + + + + View interactive project graph + +
nx graph
+
+
+ + + + + Run affected commands + +
+                # see what's been affected by changes
+                nx affected:graph
+                # run tests for current changes
+                nx affected:test
+                # run e2e tests for current changes
+                nx affected:e2e
+              
+
+
+ +

+ Carefully crafted with + + + +

+
+
+ + ); +} + +export default Index; +" +`; diff --git a/packages/next/src/generators/application/application.spec.ts b/packages/next/src/generators/application/application.spec.ts index 94b22c20ee4c4..12a2885d1b46f 100644 --- a/packages/next/src/generators/application/application.spec.ts +++ b/packages/next/src/generators/application/application.spec.ts @@ -194,7 +194,7 @@ describe('app', () => { const indexContent = tree.read('apps/my-app/pages/index.tsx', 'utf-8'); - expect(indexContent).toMatch(/