From 9b63ce167acb08faaf55bdf899055ae04a87036f Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Tue, 27 Sep 2022 19:16:22 -0400 Subject: [PATCH] feat(core): don't generate workspace.json for v2 workspaces (#12127) --- .gitignore | 3 + e2e/angular-core/src/config.test.ts | 16 --- e2e/angular-core/src/projects.test.ts | 2 +- .../src/cypress-component-tests.test.ts | 2 +- e2e/angular-extensions/src/misc.test.ts | 1 - e2e/angular-extensions/src/tailwind.test.ts | 2 +- e2e/node/src/node.test.ts | 11 -- e2e/nx-misc/src/extras.test.ts | 4 +- e2e/nx-misc/src/workspace.test.ts | 61 +++++---- e2e/nx-plugin/src/nx-plugin.test.ts | 7 - e2e/nx-run/src/affected-graph.test.ts | 12 +- e2e/nx-run/src/run.test.ts | 2 +- e2e/utils/index.ts | 12 +- .../src/create-nx-plugin.test.ts | 4 +- .../src/create-nx-workspace.test.ts | 3 +- .../application/application.spec.ts | 5 +- .../src/generators/library/library.spec.ts | 26 +--- .../ng-add/utilities/e2e.migrator.spec.ts | 4 + ...ve-library-generator-style-default.spec.ts | 18 +-- .../set-build-libs-from-source.spec.ts | 3 + packages/angular/test-setup.ts | 16 +++ packages/cra-to-nx/src/lib/clean-up-files.ts | 1 - .../application/application.spec.ts | 9 +- .../application/application.spec.ts | 6 +- packages/expo/src/utils/add-linting.spec.ts | 4 +- .../jest-project/jest-project.spec.ts | 6 +- .../js/src/generators/library/library.spec.ts | 17 +-- packages/js/src/generators/library/library.ts | 6 +- .../lint-project/lint-project.spec.ts | 4 +- .../workspace-rules-project.spec.ts | 1 + .../add-json-ext-to-eslintrc.spec.ts | 2 +- ...update-eslint-configs-to-use-nx-presets.ts | 2 +- .../project-converter.ts | 4 +- .../src/generators/library/library.spec.ts | 2 +- ...default-development-configurations.spec.ts | 1 + packages/nx/src/adapter/compat.ts | 1 + packages/nx/src/adapter/ngcli-adapter.ts | 25 ++-- packages/nx/src/command-line/run.ts | 2 +- packages/nx/src/config/workspaces.ts | 24 +++- .../create-tree-with-empty-workspace.ts | 1 - .../utils/project-configuration.spec.ts | 125 ++++++++++-------- .../generators/utils/project-configuration.ts | 9 +- .../update-14-2-0/add-json-schema.spec.ts | 3 +- .../affected/affected-project-graph.ts | 9 +- .../locators/implicit-json-changes.spec.ts | 4 +- .../affected/locators/npm-packages.spec.ts | 12 +- .../affected/locators/npm-packages.ts | 8 +- .../locators/project-glob-changes.spec.ts | 52 ++++++++ .../affected/locators/project-glob-changes.ts | 50 +++++++ .../locators/tsconfig-json-changes.ts | 8 +- .../locators/workspace-json-changes.spec.ts | 14 +- .../locators/workspace-json-changes.ts | 8 +- .../build-nodes/workspace-projects.ts | 2 +- .../nx/src/project-graph/file-utils.spec.ts | 35 ++++- packages/nx/src/project-graph/file-utils.ts | 15 ++- packages/nx/src/utils/json-diff.spec.ts | 34 ++--- packages/nx/src/utils/json-diff.ts | 16 +-- packages/nx/src/utils/nx-plugin.ts | 9 +- ...default-development-configurations.spec.ts | 1 + .../convert-to-nx-project.spec.ts | 26 ++-- .../src/generators/library/library.spec.ts | 41 ------ .../lib/move-project-configuration.spec.ts | 2 - .../move/lib/move-project-configuration.ts | 4 + .../move/lib/update-build-targets.spec.ts | 10 ++ .../move/lib/update-build-targets.ts | 38 ++++-- .../new/__snapshots__/new.spec.ts.snap | 8 -- .../workspace/src/generators/new/new.spec.ts | 4 +- packages/workspace/src/generators/new/new.ts | 81 ++++++------ .../remove/lib/remove-project-config.ts | 9 +- .../files/__workspaceFile__.json__tmpl__ | 5 - .../generators/workspace/workspace.spec.ts | 13 +- .../src/generators/workspace/workspace.ts | 21 --- .../config-locations/config-locations.spec.ts | 1 + .../update-tsc-executor-location.spec.ts | 4 +- .../change-run-commands-executor.spec.ts | 1 + .../workspace/src/utils/cli-config-utils.ts | 1 + .../workspace/src/utils/rules/format-files.ts | 12 +- 77 files changed, 573 insertions(+), 454 deletions(-) create mode 100644 packages/nx/src/project-graph/affected/locators/project-glob-changes.spec.ts create mode 100644 packages/nx/src/project-graph/affected/locators/project-glob-changes.ts delete mode 100644 packages/workspace/src/generators/workspace/files/__workspaceFile__.json__tmpl__ diff --git a/.gitignore b/.gitignore index 9add2b060b6d0..f5908672788d9 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,6 @@ CHANGELOG.md # Next.js .next + +# Local dev files +.env diff --git a/e2e/angular-core/src/config.test.ts b/e2e/angular-core/src/config.test.ts index a261e78b14ad5..b91bc076134f7 100644 --- a/e2e/angular-core/src/config.test.ts +++ b/e2e/angular-core/src/config.test.ts @@ -17,22 +17,6 @@ describe('Angular Config', () => { beforeAll(() => newProject()); afterAll(() => cleanupProject()); - it('should support workspaces w/o workspace config file', async () => { - if (isNotWindows()) { - const oldWorkspaceJson = readJson('workspace.json'); - removeFile('workspace.json'); - const myapp = uniq('myapp'); - runCLI(`generate @nrwl/angular:app ${myapp} --directory=myDir --routing`); - - runCLI(`build my-dir-${myapp} --aot`); - expectTestsPass(await runCLIAsync(`test my-dir-${myapp} --no-watch`)); - expect(() => - checkFilesDoNotExist('workspace.json', 'angular.json') - ).not.toThrow(); - createFile('workspace.json', JSON.stringify(oldWorkspaceJson, null, 2)); - } - }, 1000000); - it('should upgrade the config correctly', async () => { const previousCI = process.env.SELECTED_CLI; process.env.SELECTED_CLI = 'angular'; diff --git a/e2e/angular-core/src/projects.test.ts b/e2e/angular-core/src/projects.test.ts index 11f72902d3606..9b519c470a7f1 100644 --- a/e2e/angular-core/src/projects.test.ts +++ b/e2e/angular-core/src/projects.test.ts @@ -97,7 +97,7 @@ describe('Angular Projects', () => { } }, 1000000); - it('should build the dependent buildable lib and its child lib, as well as the app', () => { + it('should build the dependent buildable lib and its child lib, as well as the app', async () => { // ARRANGE const app = uniq('app'); const buildableLib = uniq('buildlib1'); diff --git a/e2e/angular-extensions/src/cypress-component-tests.test.ts b/e2e/angular-extensions/src/cypress-component-tests.test.ts index 22ad10262c757..6794d76d673fe 100644 --- a/e2e/angular-extensions/src/cypress-component-tests.test.ts +++ b/e2e/angular-extensions/src/cypress-component-tests.test.ts @@ -15,7 +15,7 @@ describe('Angular Cypress Component Tests', () => { const usedInAppLibName = uniq('cy-angular-lib'); const buildableLibName = uniq('cy-angular-buildable-lib'); - beforeAll(() => { + beforeAll(async () => { projectName = newProject({ name: uniq('cy-ng') }); runCLI(`generate @nrwl/angular:app ${appName} --no-interactive`); runCLI( diff --git a/e2e/angular-extensions/src/misc.test.ts b/e2e/angular-extensions/src/misc.test.ts index 7deadf8674594..4aabdb644d6d4 100644 --- a/e2e/angular-extensions/src/misc.test.ts +++ b/e2e/angular-extensions/src/misc.test.ts @@ -60,7 +60,6 @@ describe('Move Angular Project', () => { expect(moveOutput).toContain( `CREATE apps/${newPath}/src/environments/environment.ts` ); - expect(moveOutput).toContain(`UPDATE workspace.json`); }); /** diff --git a/e2e/angular-extensions/src/tailwind.test.ts b/e2e/angular-extensions/src/tailwind.test.ts index 0be47652100db..ec9d3aa80974a 100644 --- a/e2e/angular-extensions/src/tailwind.test.ts +++ b/e2e/angular-extensions/src/tailwind.test.ts @@ -360,7 +360,7 @@ describe('Tailwind support', () => { expect(mainBundle).toMatch(expectedStylesRegex); }; - it('should build correctly and only output the tailwind utilities used', () => { + it('should build correctly and only output the tailwind utilities used', async () => { const appWithTailwind = uniq('app-with-tailwind'); runCLI( `generate @nrwl/angular:app ${appWithTailwind} --add-tailwind --no-interactive` diff --git a/e2e/node/src/node.test.ts b/e2e/node/src/node.test.ts index 4409693f84095..710d8f5f6d990 100644 --- a/e2e/node/src/node.test.ts +++ b/e2e/node/src/node.test.ts @@ -528,17 +528,6 @@ exports.FooModel = FooModel; ); }, 300000); - it('should support workspaces w/o workspace config file', async () => { - removeFile('workspace.json'); - const app2 = uniq('app2'); - runCLI(`generate @nrwl/node:app ${app2} --directory=myDir`); - - runCLI(`build my-dir-${app2}`); - expect(() => - checkFilesDoNotExist('workspace.json', 'angular.json') - ).not.toThrow(); - }, 1000000); - it('should run default jest tests', async () => { await expectJestTestsToPass('@nrwl/node:lib'); }, 100000); diff --git a/e2e/nx-misc/src/extras.test.ts b/e2e/nx-misc/src/extras.test.ts index 28402331b30f4..6cae72879444f 100644 --- a/e2e/nx-misc/src/extras.test.ts +++ b/e2e/nx-misc/src/extras.test.ts @@ -188,7 +188,7 @@ describe('Extra Nx Misc Tests', () => { expect(resultArgs).toContain('camel: d'); }, 120000); - it('ttt should fail when a process exits non-zero', () => { + it('ttt should fail when a process exits non-zero', async () => { updateProjectConfig(mylib, (config) => { config.targets.error = { executor: '@nrwl/workspace:run-commands', @@ -209,7 +209,7 @@ describe('Extra Nx Misc Tests', () => { } }); - it('run command should not break if output property is missing in options and arguments', () => { + it('run command should not break if output property is missing in options and arguments', async () => { updateProjectConfig(mylib, (config) => { config.targets.lint.outputs = ['{options.outputFile}']; return config; diff --git a/e2e/nx-misc/src/workspace.test.ts b/e2e/nx-misc/src/workspace.test.ts index d405be2f850d5..35a8ebd5a5ba1 100644 --- a/e2e/nx-misc/src/workspace.test.ts +++ b/e2e/nx-misc/src/workspace.test.ts @@ -9,11 +9,12 @@ import { expectJestTestsToPass, readFile, exists, - workspaceConfigName, renameFile, updateProjectConfig, readProjectConfig, tmpProjPath, + readResolvedWorkspaceConfiguration, + removeFile, } from '@nrwl/e2e/utils'; let proj: string; @@ -23,7 +24,9 @@ describe('Workspace Tests', () => { proj = newProject(); }); - afterAll(() => cleanupProject()); + afterAll(() => { + cleanupProject(); + }); describe('@nrwl/workspace:library', () => { it('should create a library that can be tested and linted', async () => { @@ -211,13 +214,14 @@ describe('Workspace Tests', () => { `workspace-generator ${custom} ${workspace} --no-interactive --directory=dir --skipTsConfig=true -d` ); expect(exists(`libs/dir/${workspace}/src/index.ts`)).toEqual(false); - expect(dryRunOutput).toContain(`UPDATE ${workspaceConfigName()}`); + expect(dryRunOutput).toContain( + `CREATE libs/dir/${workspace}/src/index.ts` + ); const output = runCLI( `workspace-generator ${custom} ${workspace} --no-interactive --directory=dir` ); checkFilesExist(`libs/dir/${workspace}/src/index.ts`); - expect(output).toContain(`UPDATE ${workspaceConfigName()}`); expect(output).not.toContain('UPDATE nx.json'); const jsonFailing = readJson(`tools/generators/${failing}/schema.json`); @@ -281,7 +285,7 @@ describe('Workspace Tests', () => { /** * Tries moving a library from ${lib}/data-access -> shared/${lib}/data-access */ - it('should work for libraries', () => { + it('should work for libraries', async () => { const lib1 = uniq('mylib'); const lib2 = uniq('mylib'); const lib3 = uniq('mylib'); @@ -374,8 +378,8 @@ describe('Workspace Tests', () => { expect(moveOutput).toContain(`CREATE ${rootClassPath}`); checkFilesExist(rootClassPath); - let workspaceJson = readJson(workspaceConfigName()); - expect(workspaceJson.projects[`${lib1}-data-access`]).toBeUndefined(); + let workspace = await readResolvedWorkspaceConfiguration(); + expect(workspace.projects[`${lib1}-data-access`]).toBeUndefined(); const newConfig = readProjectConfig(newName); expect(newConfig).toMatchObject({ tags: [], @@ -396,9 +400,8 @@ describe('Workspace Tests', () => { ] ).toEqual([`libs/shared/${lib1}/data-access/src/index.ts`]); - expect(moveOutput).toContain(`UPDATE workspace.json`); - workspaceJson = readJson(workspaceConfigName()); - expect(workspaceJson.projects[`${lib1}-data-access`]).toBeUndefined(); + workspace = readResolvedWorkspaceConfiguration(); + expect(workspace.projects[`${lib1}-data-access`]).toBeUndefined(); const project = readProjectConfig(newName); expect(project).toBeTruthy(); expect(project.sourceRoot).toBe(`${newPath}/src`); @@ -416,7 +419,7 @@ describe('Workspace Tests', () => { ); }); - it('should work for libs created with --importPath', () => { + it('should work for libs created with --importPath', async () => { const importPath = '@wibble/fish'; const lib1 = uniq('mylib'); const lib2 = uniq('mylib'); @@ -523,9 +526,8 @@ describe('Workspace Tests', () => { ] ).toEqual([`libs/shared/${lib1}/data-access/src/index.ts`]); - expect(moveOutput).toContain(`UPDATE workspace.json`); - const workspaceJson = readJson(workspaceConfigName()); - expect(workspaceJson.projects[`${lib1}-data-access`]).toBeUndefined(); + const workspace = await readResolvedWorkspaceConfiguration(); + expect(workspace.projects[`${lib1}-data-access`]).toBeUndefined(); const project = readProjectConfig(newName); expect(project).toBeTruthy(); expect(project.sourceRoot).toBe(`${newPath}/src`); @@ -547,7 +549,7 @@ describe('Workspace Tests', () => { ); }); - it('should work for custom workspace layouts', () => { + it('should work for custom workspace layouts', async () => { const lib1 = uniq('mylib'); const lib2 = uniq('mylib'); const lib3 = uniq('mylib'); @@ -656,9 +658,8 @@ describe('Workspace Tests', () => { ] ).toEqual([`packages/shared/${lib1}/data-access/src/index.ts`]); - expect(moveOutput).toContain(`UPDATE workspace.json`); - const workspaceJson = readJson(workspaceConfigName()); - expect(workspaceJson.projects[`${lib1}-data-access`]).toBeUndefined(); + const workspace = await readResolvedWorkspaceConfiguration(); + expect(workspace.projects[`${lib1}-data-access`]).toBeUndefined(); const project = readProjectConfig(newName); expect(project).toBeTruthy(); expect(project.sourceRoot).toBe(`${newPath}/src`); @@ -681,7 +682,7 @@ describe('Workspace Tests', () => { updateFile('nx.json', JSON.stringify(nxJson)); }); - it('should work for libraries when scope is unset', () => { + it('should work for libraries when scope is unset', async () => { const json = readJson('nx.json'); delete json.npmScope; updateFile('nx.json', JSON.stringify(json)); @@ -775,8 +776,7 @@ describe('Workspace Tests', () => { rootTsConfig.compilerOptions.paths[`shared/${lib1}/data-access`] ).toEqual([`libs/shared/${lib1}/data-access/src/index.ts`]); - expect(moveOutput).toContain(`UPDATE workspace.json`); - const workspaceJson = readJson(workspaceConfigName()); + const workspaceJson = readResolvedWorkspaceConfiguration(); expect(workspaceJson.projects[`${lib1}-data-access`]).toBeUndefined(); const project = readProjectConfig(newName); expect(project).toBeTruthy(); @@ -800,7 +800,7 @@ describe('Workspace Tests', () => { /** * Tries creating then deleting a lib */ - it('should work', () => { + it('should work', async () => { const lib1 = uniq('myliba'); const lib2 = uniq('mylibb'); @@ -848,20 +848,31 @@ describe('Workspace Tests', () => { expect(exists(tmpProjPath(`libs/${lib1}`))).toBeFalsy(); expect(removeOutputForced).not.toContain(`UPDATE nx.json`); - const workspaceJson = readJson(workspaceConfigName()); + const workspaceJson = readResolvedWorkspaceConfiguration(); expect(workspaceJson.projects[`${lib1}`]).toBeUndefined(); const lib2Config = readProjectConfig(lib2); expect(lib2Config.implicitDependencies).toEqual([]); - expect(removeOutputForced).toContain(`UPDATE workspace.json`); expect(workspaceJson.projects[`${lib1}`]).toBeUndefined(); }); }); describe('workspace-lint', () => { - it('should identify issues with the workspace', () => { + beforeAll(() => { // Unfortunately, this is required as this test is testing a different workspace layout + // workspace-lint only picks up missing projects and such when workspace.json exists. newProject(); + updateFile( + 'workspace.json', + JSON.stringify({ version: 2, projects: {} }) + ); + }); + + afterAll(() => { + removeFile('workspace.json'); + }); + + it('should identify issues with the workspace', () => { const appBefore = uniq('before'); const appAfter = uniq('after'); diff --git a/e2e/nx-plugin/src/nx-plugin.test.ts b/e2e/nx-plugin/src/nx-plugin.test.ts index 4d14a233404d9..0bf4448aff7d5 100644 --- a/e2e/nx-plugin/src/nx-plugin.test.ts +++ b/e2e/nx-plugin/src/nx-plugin.test.ts @@ -286,10 +286,6 @@ describe('Nx Plugin', () => { }); it('should be able to infer projects and targets', async () => { - // Cache workspace json, to test inference and restore afterwards - const workspaceJsonContents = readFile('workspace.json'); - removeFile('workspace.json'); - // Setup project inference + target inference updateFile( `libs/${plugin}/src/index.ts`, @@ -327,9 +323,6 @@ describe('Nx Plugin', () => { expect(runCLI(`build ${inferredProject}`)).toContain( 'custom registered target' ); - - // Restore workspace.json - createFile('workspace.json', workspaceJsonContents); }); it('should be able to use local generators and executors', async () => { diff --git a/e2e/nx-run/src/affected-graph.test.ts b/e2e/nx-run/src/affected-graph.test.ts index f78d33980a82e..72bb99282201f 100644 --- a/e2e/nx-run/src/affected-graph.test.ts +++ b/e2e/nx-run/src/affected-graph.test.ts @@ -2,23 +2,22 @@ import type { NxJsonConfiguration } from '@nrwl/devkit'; import { getPackageManagerCommand, isNotWindows, - listFiles, newProject, readFile, readJson, readProjectConfig, cleanupProject, - rmDist, runCLI, runCLIAsync, runCommand, uniq, updateFile, updateProjectConfig, - workspaceConfigName, checkFilesExist, isWindows, fileExists, + removeFile, + readResolvedWorkspaceConfiguration, } from '@nrwl/e2e/utils'; describe('Nx Affected and Graph Tests', () => { @@ -253,11 +252,8 @@ describe('Nx Affected and Graph Tests', () => { it('should affect all projects by removing projects', () => { generateAll(); - updateFile(workspaceConfigName(), (old) => { - const workspaceJson = JSON.parse(old); - delete workspaceJson.projects[mylib]; - return JSON.stringify(workspaceJson, null, 2); - }); + const root = readResolvedWorkspaceConfiguration().projects[mylib].root; + removeFile(root); expect(runCLI('affected:apps')).toContain(myapp); expect(runCLI('affected:apps')).toContain(myapp2); expect(runCLI('affected:libs')).not.toContain(mylib); diff --git a/e2e/nx-run/src/run.test.ts b/e2e/nx-run/src/run.test.ts index 9ed8924ac2bbb..b8e3c64abd1f1 100644 --- a/e2e/nx-run/src/run.test.ts +++ b/e2e/nx-run/src/run.test.ts @@ -198,7 +198,7 @@ describe('Nx Running Tests', () => { env: { ...process.env, NX_DAEMON: 'true' }, }); - expect(buildAgain).toContain('local cache'); + expect(buildAgain).toContain('[local cache]'); }, 10000); it('should build the project when within the project root', () => { diff --git a/e2e/utils/index.ts b/e2e/utils/index.ts index 99facd5bfcbd4..1b44b1420935d 100644 --- a/e2e/utils/index.ts +++ b/e2e/utils/index.ts @@ -1,4 +1,5 @@ import { + createProjectGraphAsync, joinPathFragments, parseJson, ProjectConfiguration, @@ -89,12 +90,19 @@ export function updateProjectConfig( projectName: string, callback: (c: ProjectConfiguration) => ProjectConfiguration ) { - const root = readJson(workspaceConfigName()).projects[projectName]; + const workspace = readResolvedWorkspaceConfiguration(); + const root = workspace.projects[projectName].root; const path = join(root, 'project.json'); const current = readJson(path); updateFile(path, JSON.stringify(callback(current), null, 2)); } +export function readResolvedWorkspaceConfiguration() { + process.env.NX_PROJECT_GLOB_CACHE = 'false'; + const ws = new Workspaces(tmpProjPath()); + return ws.readWorkspaceConfiguration(); +} + /** * Use readProjectConfig or readInlineProjectConfig instead * if you need a project's configuration. @@ -109,7 +117,7 @@ export function readWorkspaceConfig(): Omit< } export function readProjectConfig(projectName: string): ProjectConfiguration { - const root = readJson(workspaceConfigName()).projects[projectName]; + const root = readResolvedWorkspaceConfiguration().projects[projectName].root; const path = join(root, 'project.json'); return readJson(path); } diff --git a/e2e/workspace-create/src/create-nx-plugin.test.ts b/e2e/workspace-create/src/create-nx-plugin.test.ts index a895c85e2c290..1412b9e7101fc 100644 --- a/e2e/workspace-create/src/create-nx-plugin.test.ts +++ b/e2e/workspace-create/src/create-nx-plugin.test.ts @@ -24,10 +24,10 @@ describe('create-nx-plugin', () => { }); checkFilesExist( - 'workspace.json', 'package.json', packageManagerLockFile[packageManager], - `packages/${pluginName}/package.json` + `packages/${pluginName}/package.json`, + `packages/${pluginName}/project.json` ); expect(() => runCLI(`e2e ${pluginName}-e2e`)).not.toThrow(); diff --git a/e2e/workspace-create/src/create-nx-workspace.test.ts b/e2e/workspace-create/src/create-nx-workspace.test.ts index 4d96fa74c2381..c06c3286eefe5 100644 --- a/e2e/workspace-create/src/create-nx-workspace.test.ts +++ b/e2e/workspace-create/src/create-nx-workspace.test.ts @@ -29,7 +29,6 @@ describe('create-nx-workspace', () => { }); checkFilesExist( - 'workspace.json', 'package.json', packageManagerLockFile[packageManager], 'apps/.gitkeep', @@ -39,7 +38,7 @@ describe('create-nx-workspace', () => { .filter((pm) => pm !== packageManager) .map((pm) => packageManagerLockFile[pm]); - checkFilesDoNotExist(...foreignLockFiles); + checkFilesDoNotExist(...foreignLockFiles, 'workspace.json'); expectNoAngularDevkit(); }); diff --git a/packages/angular/src/generators/application/application.spec.ts b/packages/angular/src/generators/application/application.spec.ts index 8bd7a6938bbab..2ce88770764de 100644 --- a/packages/angular/src/generators/application/application.spec.ts +++ b/packages/angular/src/generators/application/application.spec.ts @@ -360,9 +360,10 @@ describe('app', () => { await generateApp(appTree, 'myApp', { directory: 'src/9-websites' }); // ASSERT - const workspaceJson = readJson(appTree, '/workspace.json'); - expect(workspaceJson.projects['src-9-websites-my-app']).toMatchSnapshot(); + expect( + readProjectConfiguration(appTree, 'src-9-websites-my-app').root + ).toMatchSnapshot(); }); it('should generate files', async () => { diff --git a/packages/angular/src/generators/library/library.spec.ts b/packages/angular/src/generators/library/library.spec.ts index 98fb4c901bb7e..b0e060c332798 100644 --- a/packages/angular/src/generators/library/library.spec.ts +++ b/packages/angular/src/generators/library/library.spec.ts @@ -59,24 +59,9 @@ describe('lib', () => { it('should default to standalone project for first project', async () => { await runLibraryGeneratorWithOpts(); - const workspaceJsonEntry = readJson(tree, 'workspace.json').projects[ - 'my-lib' - ]; const projectConfig = readProjectConfiguration(tree, 'my-lib'); expect(projectConfig.root).toEqual('libs/my-lib'); - expect(workspaceJsonEntry).toEqual('libs/my-lib'); - }); - - it('should obey standalone === false for first project', async () => { - await runLibraryGeneratorWithOpts({ - standaloneConfig: false, - }); - const workspaceJsonEntry = readJson(tree, 'workspace.json').projects[ - 'my-lib' - ]; - const projectConfig = readProjectConfiguration(tree, 'my-lib'); - expect(projectConfig.root).toEqual('libs/my-lib'); - expect(projectConfig).toMatchObject(workspaceJsonEntry); + expect(tree.exists('workspace.json')).toEqual(false); }); }); @@ -732,22 +717,15 @@ describe('lib', () => { it('should accept numbers in the path', async () => { await runLibraryGeneratorWithOpts({ directory: 'src/1-api' }); - // ASSERT - const workspaceJson = readJson(tree, '/workspace.json'); - - expect(workspaceJson.projects['src-api-my-lib']).toEqual( + expect(readProjectConfiguration(tree, 'src-api-my-lib').root).toEqual( 'src/1-api/my-lib' ); }); it('should have root relative routes', async () => { await runLibraryGeneratorWithOpts({ directory: 'myDir' }); - const workspaceJsonEntry = readJson(tree, 'workspace.json').projects[ - 'my-dir-my-lib' - ]; const projectConfig = readProjectConfiguration(tree, 'my-dir-my-lib'); expect(projectConfig.root).toEqual('my-dir/my-lib'); - expect(workspaceJsonEntry).toEqual('my-dir/my-lib'); }); it('should generate files with correct output paths', async () => { diff --git a/packages/angular/src/generators/ng-add/utilities/e2e.migrator.spec.ts b/packages/angular/src/generators/ng-add/utilities/e2e.migrator.spec.ts index 11828d2dae552..5a6dfaa46dc14 100644 --- a/packages/angular/src/generators/ng-add/utilities/e2e.migrator.spec.ts +++ b/packages/angular/src/generators/ng-add/utilities/e2e.migrator.spec.ts @@ -415,6 +415,7 @@ describe('e2e migrator', () => { const e2eProject = readProjectConfiguration(tree, 'app1-e2e'); expect(e2eProject).toStrictEqual({ $schema: '../../node_modules/nx/schemas/project-schema.json', + name: 'app1-e2e', root: 'apps/app1-e2e', sourceRoot: 'apps/app1-e2e/src', projectType: 'application', @@ -544,6 +545,7 @@ describe('e2e migrator', () => { const e2eProject = readProjectConfiguration(tree, 'app1-e2e'); expect(e2eProject).toStrictEqual({ $schema: '../../node_modules/nx/schemas/project-schema.json', + name: 'app1-e2e', root: 'apps/app1-e2e', sourceRoot: 'apps/app1-e2e/src', projectType: 'application', @@ -609,6 +611,7 @@ describe('e2e migrator', () => { const e2eProject = readProjectConfiguration(tree, 'app1-e2e'); expect(e2eProject).toStrictEqual({ $schema: '../../node_modules/nx/schemas/project-schema.json', + name: 'app1-e2e', root: 'apps/app1-e2e', sourceRoot: 'apps/app1-e2e/src', projectType: 'application', @@ -662,6 +665,7 @@ describe('e2e migrator', () => { const e2eProject = readProjectConfiguration(tree, 'app1-e2e'); expect(e2eProject).toStrictEqual({ $schema: '../../node_modules/nx/schemas/project-schema.json', + name: 'app1-e2e', root: 'apps/app1-e2e', sourceRoot: 'apps/app1-e2e/src', projectType: 'application', diff --git a/packages/angular/src/migrations/update-13-5-0/remove-library-generator-style-default.spec.ts b/packages/angular/src/migrations/update-13-5-0/remove-library-generator-style-default.spec.ts index 2bd9de8b9cdb1..abbf393167eea 100644 --- a/packages/angular/src/migrations/update-13-5-0/remove-library-generator-style-default.spec.ts +++ b/packages/angular/src/migrations/update-13-5-0/remove-library-generator-style-default.spec.ts @@ -18,7 +18,7 @@ describe('remove-library-generator-style-default migration', () => { it('should do nothing when angular library generator is not configured', async () => { const workspace: WorkspaceConfiguration = { - version: 1, + version: 2, generators: { '@nrwl/angular:application': { style: 'scss' } }, }; updateWorkspaceConfiguration(tree, workspace); @@ -30,7 +30,7 @@ describe('remove-library-generator-style-default migration', () => { it('should do nothing when other vertical library generator is configured with the style entry', async () => { const workspace: WorkspaceConfiguration = { - version: 1, + version: 2, generators: { '@nrwl/react:library': { style: 'scss' } }, }; updateWorkspaceConfiguration(tree, workspace); @@ -43,7 +43,7 @@ describe('remove-library-generator-style-default migration', () => { describe('collection:generator', () => { it('should remove style entry when configured', async () => { const workspace: WorkspaceConfiguration = { - version: 1, + version: 2, generators: { '@nrwl/angular:library': { style: 'scss' } }, }; updateWorkspaceConfiguration(tree, workspace); @@ -51,14 +51,14 @@ describe('remove-library-generator-style-default migration', () => { await removeLibraryGeneratorStyleDefault(tree); expect(readWorkspaceConfiguration(tree)).toStrictEqual({ - version: 1, + version: 2, generators: { '@nrwl/angular:library': {} }, }); }); it('should do nothing when style is not set', async () => { const workspace: WorkspaceConfiguration = { - version: 1, + version: 2, generators: { '@nrwl/angular:library': { linter: 'eslint' } }, }; updateWorkspaceConfiguration(tree, workspace); @@ -72,7 +72,7 @@ describe('remove-library-generator-style-default migration', () => { describe('nested generator', () => { it('should remove style entry when configured', async () => { const workspace: WorkspaceConfiguration = { - version: 1, + version: 2, generators: { '@nrwl/angular': { library: { style: 'scss' } } }, }; updateWorkspaceConfiguration(tree, workspace); @@ -80,14 +80,14 @@ describe('remove-library-generator-style-default migration', () => { await removeLibraryGeneratorStyleDefault(tree); expect(readWorkspaceConfiguration(tree)).toStrictEqual({ - version: 1, + version: 2, generators: { '@nrwl/angular': { library: {} } }, }); }); it('should do nothing when style is not set', async () => { const workspace: WorkspaceConfiguration = { - version: 1, + version: 2, generators: { '@nrwl/angular': { library: { linter: 'eslint' } } }, }; updateWorkspaceConfiguration(tree, workspace); @@ -101,7 +101,7 @@ describe('remove-library-generator-style-default migration', () => { it('should format files', async () => { jest.spyOn(devkit, 'formatFiles'); const workspace: WorkspaceConfiguration = { - version: 1, + version: 2, generators: { '@nrwl/angular:library': { style: 'scss' } }, }; updateWorkspaceConfiguration(tree, workspace); diff --git a/packages/angular/src/migrations/update-13-9-0/set-build-libs-from-source.spec.ts b/packages/angular/src/migrations/update-13-9-0/set-build-libs-from-source.spec.ts index 0732d8149ceae..fdc7395541b0d 100644 --- a/packages/angular/src/migrations/update-13-9-0/set-build-libs-from-source.spec.ts +++ b/packages/angular/src/migrations/update-13-9-0/set-build-libs-from-source.spec.ts @@ -22,6 +22,7 @@ describe('set-build-libs-from-source migration', () => { it('should not update when not using the @nrwl/angular:webpack-browser executor', async () => { const project: ProjectConfiguration = { + name: 'app1', root: 'apps/app1', targets: { build: { executor: '@nrwl/angular:package' } }, }; @@ -38,6 +39,7 @@ describe('set-build-libs-from-source migration', () => { it('should set buildLibsFromSource to false', async () => { addProjectConfiguration(tree, 'app1', { + name: 'app1', root: 'apps/app1', targets: { build: { executor: '@nrwl/angular:webpack-browser' } }, }); @@ -52,6 +54,7 @@ describe('set-build-libs-from-source migration', () => { it('should support any target name using @nrwl/angular:webpack-browser', async () => { addProjectConfiguration(tree, 'app1', { + name: 'app1', root: 'apps/app1', targets: { 'build-base': { executor: '@nrwl/angular:webpack-browser' } }, }); diff --git a/packages/angular/test-setup.ts b/packages/angular/test-setup.ts index dde53386e1ce3..0ded8a0f31226 100644 --- a/packages/angular/test-setup.ts +++ b/packages/angular/test-setup.ts @@ -4,6 +4,21 @@ const allowedProjectExtensions = [ 'configFilePath', '$schema', 'generators', + 'namedInputs', + 'name', +]; + +const allowedWorkspaceExtensions = [ + 'implicitDependencies', + 'affected', + 'npmScope', + 'tasksRunnerOptions', + 'workspaceLayout', + 'plugins', + 'targetDefaults', + 'files', + 'generators', + 'namedInputs', ]; const possiblePaths = [ @@ -23,6 +38,7 @@ for (const possiblePath of possiblePaths) { return originalReadJsonWorkspace(path, host, { ...options, allowedProjectExtensions, + allowedWorkspaceExtensions, }); }, }; diff --git a/packages/cra-to-nx/src/lib/clean-up-files.ts b/packages/cra-to-nx/src/lib/clean-up-files.ts index acdffd1aaf74a..d770433388035 100644 --- a/packages/cra-to-nx/src/lib/clean-up-files.ts +++ b/packages/cra-to-nx/src/lib/clean-up-files.ts @@ -12,5 +12,4 @@ export function cleanUpFiles(appName: string) { ); removeSync('temp-workspace'); - removeSync('workspace.json'); } diff --git a/packages/detox/src/generators/application/application.spec.ts b/packages/detox/src/generators/application/application.spec.ts index 381ba67a1154d..9e460282fbc9e 100644 --- a/packages/detox/src/generators/application/application.spec.ts +++ b/packages/detox/src/generators/application/application.spec.ts @@ -37,8 +37,7 @@ describe('detox application generator', () => { }); it('should add update `workspace.json` file', async () => { - const workspaceJson = readJson(tree, 'workspace.json'); - const project = workspaceJson.projects['my-app-e2e']; + const project = readProjectConfiguration(tree, 'my-app-e2e'); expect(project.root).toEqual('apps/my-app-e2e'); }); @@ -73,8 +72,7 @@ describe('detox application generator', () => { }); it('should add update `workspace.json` file', async () => { - const workspaceJson = readJson(tree, 'workspace.json'); - const project = workspaceJson.projects['my-dir-my-app-e2e']; + const project = readProjectConfiguration(tree, 'my-dir-my-app-e2e'); expect(project.root).toEqual('apps/my-dir/my-app-e2e'); }); @@ -108,8 +106,7 @@ describe('detox application generator', () => { }); it('should add update `workspace.json` file', async () => { - const workspaceJson = readJson(tree, 'workspace.json'); - const project = workspaceJson.projects['my-dir-my-app-e2e']; + const project = readProjectConfiguration(tree, 'my-dir-my-app-e2e'); expect(project.root).toEqual('apps/my-dir/my-app-e2e'); }); diff --git a/packages/expo/src/generators/application/application.spec.ts b/packages/expo/src/generators/application/application.spec.ts index 9b922b7b4540f..2271d7e316f3d 100644 --- a/packages/expo/src/generators/application/application.spec.ts +++ b/packages/expo/src/generators/application/application.spec.ts @@ -17,7 +17,7 @@ describe('app', () => { appTree.write('.gitignore', ''); }); - it('should update workspace.json', async () => { + it('should update workspace', async () => { await expoApplicationGenerator(appTree, { name: 'myApp', displayName: 'myApp', @@ -27,11 +27,11 @@ describe('app', () => { js: false, unitTestRunner: 'none', }); - const workspaceJson = readWorkspaceConfiguration(appTree); + const workspace = readWorkspaceConfiguration(appTree); const projects = getProjects(appTree); expect(projects.get('my-app').root).toEqual('apps/my-app'); - expect(workspaceJson.defaultProject).toEqual('my-app'); + expect(workspace.defaultProject).toEqual('my-app'); }); it('should update nx.json', async () => { diff --git a/packages/expo/src/utils/add-linting.spec.ts b/packages/expo/src/utils/add-linting.spec.ts index c06e679b3260a..129978de7689e 100644 --- a/packages/expo/src/utils/add-linting.spec.ts +++ b/packages/expo/src/utils/add-linting.spec.ts @@ -15,7 +15,7 @@ describe('Add Linting', () => { }); }); - it('should add update `workspace.json` file properly when eslint is passed', () => { + it('should add update `project configuration` file properly when eslint is passed', () => { addLinting( tree, 'my-lib', @@ -29,7 +29,7 @@ describe('Add Linting', () => { expect(project.targets.lint.executor).toEqual('@nrwl/linter:eslint'); }); - it('should add update `workspace.json` file properly when tslint is passed', () => { + it('should add update `project configuration` file properly when tslint is passed', () => { addLinting( tree, 'my-lib', 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 de66d5847531c..c6ac521865ff6 100644 --- a/packages/jest/src/generators/jest-project/jest-project.spec.ts +++ b/packages/jest/src/generators/jest-project/jest-project.spec.ts @@ -65,7 +65,7 @@ describe('jestProject', () => { expect(tree.exists('babel.config.json')).toBeTruthy(); }); - it('should alter workspace.json', async () => { + it('should alter project configuration', async () => { await jestProjectGenerator(tree, { ...defaultOptions, project: 'lib1', @@ -160,7 +160,7 @@ describe('jestProject', () => { expect(jestConfig).toMatchSnapshot(); }); - it('should not list the setup file in workspace.json', async () => { + it('should not list the setup file in project configuration', async () => { await jestProjectGenerator(tree, { ...defaultOptions, project: 'lib1', @@ -191,7 +191,7 @@ describe('jestProject', () => { expect(tree.exists('src/test-setup.ts')).toBeFalsy(); }); - it('should not list the setup file in workspace.json', async () => { + it('should not list the setup file in project configuration', async () => { await jestProjectGenerator(tree, { ...defaultOptions, project: 'lib1', diff --git a/packages/js/src/generators/library/library.spec.ts b/packages/js/src/generators/library/library.spec.ts index 536d4aa839921..b669b597a2caa 100644 --- a/packages/js/src/generators/library/library.spec.ts +++ b/packages/js/src/generators/library/library.spec.ts @@ -51,10 +51,6 @@ describe('lib', () => { // unitTestRunner property is ignored. // It only works with our executors. expect(tree.exists('libs/my-lib/src/lib/my-lib.spec.ts')).toBeFalsy(); - const workspaceJson = readJson(tree, '/workspace.json'); - // Blocked on Craigory merging optional config PR - // expect(workspaceJson.projects['my-lib']).toBeUndefined(); - // expect(tree.exists('libs/my-lib/project.json')).toBeFalsy(); }); it('should generate an empty ts lib using --config=project', async () => { @@ -63,12 +59,8 @@ describe('lib', () => { name: 'my-lib', config: 'project', }); - const workspaceJsonEntry = readJson(tree, 'workspace.json').projects[ - 'my-lib' - ]; const projectConfig = readProjectConfiguration(tree, 'my-lib'); expect(projectConfig.root).toEqual('libs/my-lib'); - expect(workspaceJsonEntry).toEqual('libs/my-lib'); }); it('should generate an empty ts lib using --config=workspace', async () => { @@ -77,12 +69,8 @@ describe('lib', () => { name: 'my-lib', config: 'workspace', }); - const workspaceJsonEntry = readJson(tree, 'workspace.json').projects[ - 'my-lib' - ]; const projectConfig = readProjectConfiguration(tree, 'my-lib'); expect(projectConfig.root).toEqual('libs/my-lib'); - expect(projectConfig).toMatchObject(workspaceJsonEntry); }); }); @@ -225,16 +213,15 @@ describe('lib', () => { expect(tree.exists(`libs/my-dir/my-lib/package.json`)).toBeFalsy(); }); - it('should update workspace.json', async () => { + it('should update project configuration', async () => { await libraryGenerator(tree, { ...defaultOptions, name: 'myLib', directory: 'myDir', config: 'workspace', }); - const workspaceJson = readJson(tree, '/workspace.json'); - expect(workspaceJson.projects['my-dir-my-lib'].root).toEqual( + expect(readProjectConfiguration(tree, 'my-dir-my-lib').root).toEqual( 'libs/my-dir/my-lib' ); }); diff --git a/packages/js/src/generators/library/library.ts b/packages/js/src/generators/library/library.ts index bb791725d3968..a862a93ff63cc 100644 --- a/packages/js/src/generators/library/library.ts +++ b/packages/js/src/generators/library/library.ts @@ -141,10 +141,8 @@ function addProject( } } - if (options.config === 'workspace') { - addProjectConfiguration(tree, options.name, projectConfiguration, false); - } else if (options.config === 'project') { - addProjectConfiguration(tree, options.name, projectConfiguration, true); + if (options.config === 'workspace' || options.config === 'project') { + addProjectConfiguration(tree, options.name, projectConfiguration); } else { addProjectConfiguration( tree, diff --git a/packages/linter/src/generators/lint-project/lint-project.spec.ts b/packages/linter/src/generators/lint-project/lint-project.spec.ts index 69d1e620b278e..059f99ed21539 100644 --- a/packages/linter/src/generators/lint-project/lint-project.spec.ts +++ b/packages/linter/src/generators/lint-project/lint-project.spec.ts @@ -39,7 +39,7 @@ describe('@nrwl/linter:lint-project', () => { ).toMatchSnapshot(); }); - it('should configure the target in workspace.json', async () => { + it('should configure the target in project configuration', async () => { await lintProjectGenerator(tree, { ...defaultOptions, linter: Linter.EsLint, @@ -96,7 +96,7 @@ describe('@nrwl/linter:lint-project', () => { ).toMatchSnapshot(); }); - it('should configure the target in workspace.json', async () => { + it('should configure the target in project configuration', async () => { await lintProjectGenerator(tree, { ...defaultOptions, linter: Linter.TsLint, diff --git a/packages/linter/src/generators/workspace-rules-project/workspace-rules-project.spec.ts b/packages/linter/src/generators/workspace-rules-project/workspace-rules-project.spec.ts index bebac35632f6c..a8541c60c15be 100644 --- a/packages/linter/src/generators/workspace-rules-project/workspace-rules-project.spec.ts +++ b/packages/linter/src/generators/workspace-rules-project/workspace-rules-project.spec.ts @@ -76,6 +76,7 @@ describe('@nrwl/linter:workspace-rules-project', () => { .toMatchInlineSnapshot(` Object { "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "eslint-rules", "root": "tools/eslint-rules", "sourceRoot": "tools/eslint-rules", "targets": Object { diff --git a/packages/linter/src/migrations/update-10-3-0/add-json-ext-to-eslintrc.spec.ts b/packages/linter/src/migrations/update-10-3-0/add-json-ext-to-eslintrc.spec.ts index 3676ac29c3551..3d44b7cb793d1 100644 --- a/packages/linter/src/migrations/update-10-3-0/add-json-ext-to-eslintrc.spec.ts +++ b/packages/linter/src/migrations/update-10-3-0/add-json-ext-to-eslintrc.spec.ts @@ -73,7 +73,7 @@ describe('Add explicit .json file extension to .eslintrc files', () => { ); }); - it('should rename .eslintrc files to .eslintrc.json and update any workspace.json references', async () => { + it('should rename .eslintrc files to .eslintrc.json and update any project configuration references', async () => { const result = await runMigration('add-json-ext-to-eslintrc', {}, tree); const workspace = readWorkspace(tree); diff --git a/packages/linter/src/migrations/update-10-4-0/update-eslint-configs-to-use-nx-presets.ts b/packages/linter/src/migrations/update-10-4-0/update-eslint-configs-to-use-nx-presets.ts index 79cb513f20b87..db980413970a1 100644 --- a/packages/linter/src/migrations/update-10-4-0/update-eslint-configs-to-use-nx-presets.ts +++ b/packages/linter/src/migrations/update-10-4-0/update-eslint-configs-to-use-nx-presets.ts @@ -77,7 +77,7 @@ function updateReactESLintConfigs(host: Tree) { /** * There isn't a way to know for sure if a project was started with the Nx * original inline React ESLint config (for applications it is easy to know - * from the workspace.json, but that is not the case for all libraries). + * from the project configuration, but that is not the case for all libraries). * * We therefore try and infer it based on the presence of react eslint plugins * within the config that is currently there. diff --git a/packages/linter/src/utils/convert-tslint-to-eslint/project-converter.ts b/packages/linter/src/utils/convert-tslint-to-eslint/project-converter.ts index 3a1666ee50e28..fb446fabddcfb 100644 --- a/packages/linter/src/utils/convert-tslint-to-eslint/project-converter.ts +++ b/packages/linter/src/utils/convert-tslint-to-eslint/project-converter.ts @@ -421,14 +421,14 @@ export class ProjectConverter { ); /** - * Update global linter configuration defaults in workspace.json + * Update global linter configuration defaults in project configuration */ const workspace = readWorkspaceConfiguration(this.host); this.cleanUpGeneratorsConfig(workspace); updateWorkspaceConfiguration(this.host, workspace); /** - * Update project-level linter configuration defaults in workspace.json + * Update project-level linter configuration defaults in project configuration */ const projects = getProjects(this.host); for (const [projectName, { generators }] of projects.entries()) { diff --git a/packages/nest/src/generators/library/library.spec.ts b/packages/nest/src/generators/library/library.spec.ts index b6b95bf258e36..3c0a7e6ff21ad 100644 --- a/packages/nest/src/generators/library/library.spec.ts +++ b/packages/nest/src/generators/library/library.spec.ts @@ -15,7 +15,7 @@ describe('lib', () => { }); describe('not nested', () => { - it('should update workspace.json', async () => { + it('should update project configuration', async () => { await libraryGenerator(tree, { name: libName }); const workspaceJson = readJson(tree, '/workspace.json'); diff --git a/packages/next/src/migrations/update-14-0-0/add-default-development-configurations.spec.ts b/packages/next/src/migrations/update-14-0-0/add-default-development-configurations.spec.ts index df341d82b8a31..eb2e094df7714 100644 --- a/packages/next/src/migrations/update-14-0-0/add-default-development-configurations.spec.ts +++ b/packages/next/src/migrations/update-14-0-0/add-default-development-configurations.spec.ts @@ -96,6 +96,7 @@ describe('React default development configuration', () => { const config = readProjectConfiguration(tree, 'example'); expect(config).toEqual({ $schema: '../../node_modules/nx/schemas/project-schema.json', + name: 'example', root: 'apps/example', projectType: 'application', }); diff --git a/packages/nx/src/adapter/compat.ts b/packages/nx/src/adapter/compat.ts index 4f6eac334c1bc..5325b5fbb2c77 100644 --- a/packages/nx/src/adapter/compat.ts +++ b/packages/nx/src/adapter/compat.ts @@ -26,6 +26,7 @@ const allowedProjectExtensions = [ '$schema', 'generators', 'namedInputs', + 'name', ]; const allowedWorkspaceExtensions = [ diff --git a/packages/nx/src/adapter/ngcli-adapter.ts b/packages/nx/src/adapter/ngcli-adapter.ts index 5f61fdb0e36af..4a96ed313a73e 100644 --- a/packages/nx/src/adapter/ngcli-adapter.ts +++ b/packages/nx/src/adapter/ngcli-adapter.ts @@ -515,7 +515,7 @@ export class NxScopedHost extends virtualFs.ScopedHost { } // project was read from a project.json file const configPath = projectConfig.configFilePath; - const fileConfigObject = { ...projectConfig }; + const fileConfigObject = { name: project, ...projectConfig }; delete fileConfigObject.root; // remove the root before writing delete fileConfigObject.configFilePath; // remove the configFilePath before writing const projectJsonWrite = super.write( @@ -752,12 +752,7 @@ function findWorkspaceConfigFileChange(host: Tree): WorkspaceConfigFileChange { function findCreatedProjects(host: Tree): FileChange[] { return host .listChanges() - .filter( - (f) => - f.type === 'CREATE' && - (basename(f.path) === 'project.json' || - basename(f.path) === 'package.json') - ); + .filter((f) => f.type === 'CREATE' && basename(f.path) === 'project.json'); } function findDeletedProjects(host: Tree): FileChange[] { @@ -1196,7 +1191,9 @@ function saveWorkspaceConfigurationInWrappedSchematic( const workspaceJsonExists = host.exists(r.eventPath); const workspace: Omit & { projects: { - [key: string]: string | { configFilePath?: string; root: string }; + [key: string]: + | string + | ({ configFilePath?: string } & ProjectConfiguration); }; } = parseJson(r.content.toString()); for (const [project, config] of Object.entries(workspace.projects)) { @@ -1206,9 +1203,15 @@ function saveWorkspaceConfigurationInWrappedSchematic( ) { const path = config.configFilePath || join(config.root, 'project.json'); workspace.projects[project] = normalize(dirname(path)); - delete config.root; // remove the root before writing - delete config.configFilePath; - host.write(path, serializeJson(config)); + host.write( + path, + serializeJson({ + name: project, + ...config, + root: undefined, + configFilePath: undefined, + }) + ); } } const nxJson: NxJsonConfiguration = parseJson( diff --git a/packages/nx/src/command-line/run.ts b/packages/nx/src/command-line/run.ts index a7c0da794c281..c4c86de4b6b05 100644 --- a/packages/nx/src/command-line/run.ts +++ b/packages/nx/src/command-line/run.ts @@ -148,7 +148,7 @@ async function runExecutorInternal( mergePluginTargetsWithNxTargets( proj.root, proj.targets, - loadNxPlugins(workspace.plugins) + loadNxPlugins(workspace.plugins, [root], root) )[target]; if (!targetConfig) { diff --git a/packages/nx/src/config/workspaces.ts b/packages/nx/src/config/workspaces.ts index f0215b54131a4..8e6fe241ebdb3 100644 --- a/packages/nx/src/config/workspaces.ts +++ b/packages/nx/src/config/workspaces.ts @@ -28,7 +28,9 @@ import { import { PackageJson } from '../utils/package-json'; import { sortObjectByKeys } from 'nx/src/utils/object-sort'; -export function workspaceConfigName(root: string) { +export function workspaceConfigName( + root: string +): 'angular.json' | 'workspace.json' | null { if (existsSync(path.join(root, 'angular.json'))) { return 'angular.json'; } else if (existsSync(path.join(root, 'workspace.json'))) { @@ -71,8 +73,9 @@ export class Workspaces { if ( this.cachedWorkspaceConfig && process.env.NX_CACHE_WORKSPACE_CONFIG !== 'false' - ) + ) { return this.cachedWorkspaceConfig; + } const nxJson = this.readNxJson(); const workspaceFile = workspaceConfigName(this.root); const workspacePath = workspaceFile @@ -474,6 +477,7 @@ export function toOldFormatOrNull(w: any) { renamePropertyWithStableKeys(projectConfig, 'generators', 'schematics'); formatted = true; } + delete projectConfig.name; Object.values(projectConfig.architect || {}).forEach((target: any) => { if (target.executor !== undefined) { renamePropertyWithStableKeys(target, 'executor', 'builder'); @@ -551,8 +555,12 @@ export function toProjectName( let projectGlobCache: string[]; let projectGlobCacheKey: string; -function getGlobPatternsFromPlugins(nxJson: NxJsonConfiguration): string[] { - const plugins = loadNxPlugins(nxJson?.plugins); +export function getGlobPatternsFromPlugins( + nxJson: NxJsonConfiguration, + paths: string[], + root = workspaceRoot +): string[] { + const plugins = loadNxPlugins(nxJson?.plugins, paths, root); const patterns = []; for (const plugin of plugins) { @@ -570,7 +578,9 @@ function getGlobPatternsFromPlugins(nxJson: NxJsonConfiguration): string[] { /** * Get the package.json globs from package manager workspaces */ -function getGlobPatternsFromPackageManagerWorkspaces(root: string): string[] { +export function getGlobPatternsFromPackageManagerWorkspaces( + root: string +): string[] { try { try { const obj = yaml.load(readFileSync(join(root, 'pnpm-workspace.yaml'))); @@ -635,7 +645,9 @@ export function globForProjectFiles( ]; if (!ignorePluginInference) { - projectGlobPatterns.push(...getGlobPatternsFromPlugins(nxJson)); + projectGlobPatterns.push( + ...getGlobPatternsFromPlugins(nxJson, [root], root) + ); } const combinedProjectGlobPattern = '{' + projectGlobPatterns.join(',') + '}'; diff --git a/packages/nx/src/generators/testing-utils/create-tree-with-empty-workspace.ts b/packages/nx/src/generators/testing-utils/create-tree-with-empty-workspace.ts index ed2eab2df2c19..9906865a14be9 100644 --- a/packages/nx/src/generators/testing-utils/create-tree-with-empty-workspace.ts +++ b/packages/nx/src/generators/testing-utils/create-tree-with-empty-workspace.ts @@ -6,7 +6,6 @@ import type { Tree } from 'nx/src/generators/tree'; */ export function createTreeWithEmptyWorkspace(): Tree { const tree = new FsTree('/virtual', false); - tree.write('/workspace.json', JSON.stringify({ version: 2, projects: {} })); return addCommonFiles(tree); } diff --git a/packages/nx/src/generators/utils/project-configuration.spec.ts b/packages/nx/src/generators/utils/project-configuration.spec.ts index c5b656ec6abb3..cdf8e7b78b131 100644 --- a/packages/nx/src/generators/utils/project-configuration.spec.ts +++ b/packages/nx/src/generators/utils/project-configuration.spec.ts @@ -6,7 +6,7 @@ import { createTreeWithEmptyWorkspace, createTreeWithEmptyV1Workspace, } from '../testing-utils/create-tree-with-empty-workspace'; -import { readJson, updateJson } from '../utils/json'; +import { readJson, updateJson, writeJson } from '../utils/json'; import { addProjectConfiguration, getProjects, @@ -20,6 +20,7 @@ import { } from './project-configuration'; import * as projectSchema from '../../../schemas/project-schema.json'; +import { joinPathFragments } from 'nx/src/utils/path'; type ProjectConfigurationV1 = Pick< ProjectConfiguration, @@ -38,6 +39,7 @@ const baseTestProjectConfigV1: ProjectConfigurationV1 = { architect: {}, }; const baseTestProjectConfigV2: ProjectConfiguration = { + name: 'test', root: 'libs/test', sourceRoot: 'libs/test/src', targets: {}, @@ -195,11 +197,13 @@ describe('project configuration', () => { describe('readProjectConfiguration', () => { it('should get info from workspace.json', () => { - updateJson(tree, getWorkspacePath(tree), (json) => { - json.projects['proj1'] = { - root: 'proj1', - }; - return json; + writeJson(tree, 'workspace.json', { + version: 2, + projects: { + proj1: { + root: 'proj1', + }, + }, }); const config = readProjectConfiguration(tree, 'proj1'); @@ -209,11 +213,8 @@ describe('project configuration', () => { }); it('should should not fail if projects is not defined in nx.json', () => { - updateJson(tree, getWorkspacePath(tree), (json) => { - json.projects['proj1'] = { - root: 'proj1', - }; - return json; + writeJson(tree, 'libs/proj1/project.json', { + name: 'proj1', }); updateJson(tree, 'nx.json', (json) => { delete json.projects; @@ -222,7 +223,8 @@ describe('project configuration', () => { const config = readProjectConfiguration(tree, 'proj1'); expect(config).toEqual({ - root: 'proj1', + name: 'proj1', + root: 'libs/proj1', }); }); }); @@ -244,19 +246,18 @@ describe('project configuration', () => { }, true ); - addProjectConfiguration( - tree, - 'project-b', - { - root: 'apps/project-b', - targets: {}, - }, - true - ); + addProjectConfiguration(tree, 'project-b', { + root: 'apps/project-b', + targets: {}, + }); expect(tree.exists('apps/project-b/project.json')).toBeTruthy(); }); it("should not create project.json file if any other app in the workspace doesn't use project.json", () => { + writeJson(tree, 'workspace.json', { + version: 2, + projects: {}, + }); addProjectConfiguration( tree, 'project-a', @@ -275,6 +276,10 @@ describe('project configuration', () => { }); it('should not create project.json file when adding a project if standalone is false', () => { + writeJson(tree, 'workspace.json', { + version: 2, + projects: {}, + }); addProjectConfiguration(tree, 'test', baseTestProjectConfigV2, false); expect(tree.exists('libs/test/project.json')).toBeFalsy(); @@ -307,17 +312,21 @@ describe('project configuration', () => { addProjectConfiguration(tree, 'test', baseTestProjectConfigV2, true); const expectedProjectConfig = { ...baseTestProjectConfigV2, - root: undefined, targets: { build: { executor: '' } }, }; updateProjectConfiguration(tree, 'test', expectedProjectConfig); - expect(readJson(tree, 'libs/test/project.json')).toEqual( - expectedProjectConfig - ); + expect(readJson(tree, 'libs/test/project.json')).toEqual({ + ...expectedProjectConfig, + root: undefined, + }); }); it('should update workspace.json file when updating an inline project', () => { + writeJson(tree, 'workspace.json', { + version: 2, + projects: {}, + }); addProjectConfiguration(tree, 'test', baseTestProjectConfigV2, false); const expectedProjectConfig = { ...baseTestProjectConfigV2, @@ -334,19 +343,25 @@ describe('project configuration', () => { addProjectConfiguration(tree, 'test', baseTestProjectConfigV2, true); removeProjectConfiguration(tree, 'test'); - expect(readJson(tree, 'workspace.json').projects.test).toBeUndefined(); expect(tree.exists('test/project.json')).toBeFalsy(); }); it('should support workspaces with standalone and inline projects', () => { + writeJson(tree, 'workspace.json', { + version: 2, + projects: {}, + }); addProjectConfiguration(tree, 'test', baseTestProjectConfigV2, true); addProjectConfiguration(tree, 'test2', baseTestProjectConfigV2, false); const configurations = getProjects(tree); expect(configurations.get('test')).toEqual({ $schema: '../../node_modules/nx/schemas/project-schema.json', + name: 'test', + ...baseTestProjectConfigV2, + }); + expect(configurations.get('test2')).toEqual({ ...baseTestProjectConfigV2, }); - expect(configurations.get('test2')).toEqual(baseTestProjectConfigV2); }); describe('JSON schema', () => { @@ -375,6 +390,10 @@ describe('project configuration', () => { let workspaceConfiguration: WorkspaceConfiguration; beforeEach(() => { + writeJson(tree, 'workspace.json', { + version: 2, + projects: {}, + }); workspaceConfiguration = readWorkspaceConfiguration(tree); }); @@ -447,6 +466,10 @@ describe('project configuration', () => { }); it("should not create project.json file if any other app in the workspace doesn't use project.json", () => { + writeJson(tree, 'workspace.json', { + version: 2, + projects: {}, + }); addProjectConfiguration( tree, 'project-a', @@ -456,20 +479,19 @@ describe('project configuration', () => { }, false ); - addProjectConfiguration( - tree, - 'project-b', - { - root: 'apps/project-b', - targets: {}, - }, - false - ); + addProjectConfiguration(tree, 'project-b', { + root: 'apps/project-b', + targets: {}, + }); expect(tree.exists('apps/project-a/project.json')).toBeFalsy(); expect(tree.exists('apps/project-b/project.json')).toBeFalsy(); }); it('should not create project.json file when adding a project if standalone is false', () => { + writeJson(tree, 'workspace.json', { + version: 2, + projects: {}, + }); addProjectConfiguration(tree, 'test', baseTestProjectConfigV2, false); expect(tree.exists('libs/test/project.json')).toBeFalsy(); @@ -502,17 +524,21 @@ describe('project configuration', () => { addProjectConfiguration(tree, 'test', baseTestProjectConfigV2, true); const expectedProjectConfig = { ...baseTestProjectConfigV2, - root: undefined, targets: { build: { executor: '' } }, }; updateProjectConfiguration(tree, 'test', expectedProjectConfig); - expect(readJson(tree, 'libs/test/project.json')).toEqual( - expectedProjectConfig - ); + expect(readJson(tree, 'libs/test/project.json')).toEqual({ + ...expectedProjectConfig, + root: undefined, + }); }); it('should update workspace.json file when updating an inline project', () => { + writeJson(tree, 'workspace.json', { + version: 2, + projects: {}, + }); addProjectConfiguration(tree, 'test', baseTestProjectConfigV2, false); const expectedProjectConfig = { ...baseTestProjectConfigV2, @@ -536,20 +562,11 @@ describe('project configuration', () => { addProjectConfiguration(tree, 'test', baseTestProjectConfigV2, false); removeProjectConfiguration(tree, 'test'); - expect(readJson(tree, 'workspace.json').projects.test).toBeUndefined(); - }); - - it('should support workspaces with standalone and inline projects', () => { - addProjectConfiguration(tree, 'test', baseTestProjectConfigV2, true); - addProjectConfiguration(tree, 'test2', baseTestProjectConfigV2, false); - - const configurations = getProjects(tree); - - expect(configurations.get('test')).toEqual({ - $schema: '../../node_modules/nx/schemas/project-schema.json', - ...baseTestProjectConfigV2, - }); - expect(configurations.get('test2')).toEqual(baseTestProjectConfigV2); + expect( + tree.exists( + joinPathFragments(baseTestProjectConfigV2.root, 'project.json') + ) + ).toBeFalsy(); }); }); }); diff --git a/packages/nx/src/generators/utils/project-configuration.ts b/packages/nx/src/generators/utils/project-configuration.ts index f57a4143899c1..21bfaeab4d435 100644 --- a/packages/nx/src/generators/utils/project-configuration.ts +++ b/packages/nx/src/generators/utils/project-configuration.ts @@ -228,9 +228,11 @@ export function readProjectConfiguration( const workspace = readWorkspace(tree); if (!workspace.projects[projectName]) { throw new Error( - `Cannot find configuration for '${projectName}' in ${getWorkspacePath( - tree - )}.` + getWorkspacePath(tree) + ? `Cannot find configuration for '${projectName}' in ${getWorkspacePath( + tree + )}.` + : `Cannot find configuration for '${projectName}'` ); } @@ -373,6 +375,7 @@ function addProjectToWorkspaceJson( // update the project.json file writeJson(tree, configFile, { ...jsonSchema, + name: mode === 'create' ? projectName : project.name, ...project, root: undefined, }); diff --git a/packages/nx/src/migrations/update-14-2-0/add-json-schema.spec.ts b/packages/nx/src/migrations/update-14-2-0/add-json-schema.spec.ts index 2dccfe87bb9bc..416474e88d8b3 100644 --- a/packages/nx/src/migrations/update-14-2-0/add-json-schema.spec.ts +++ b/packages/nx/src/migrations/update-14-2-0/add-json-schema.spec.ts @@ -1,6 +1,6 @@ import { createTreeWithEmptyWorkspace } from '../../generators/testing-utils/create-tree-with-empty-workspace'; import type { Tree } from '../../generators/tree'; -import { readJson } from '../../generators/utils/json'; +import { readJson, writeJson } from '../../generators/utils/json'; import { addProjectConfiguration } from '../../generators/utils/project-configuration'; import addJsonSchema from './add-json-schema'; @@ -22,6 +22,7 @@ describe('add-json-schema >', () => { }); it('should update workspace.json $schema', async () => { + writeJson(tree, 'workspace.json', { version: 2, projects: {} }); const workspaceJson = readJson(tree, 'workspace.json'); delete workspaceJson['$schema']; diff --git a/packages/nx/src/project-graph/affected/affected-project-graph.ts b/packages/nx/src/project-graph/affected/affected-project-graph.ts index 272497731f59f..08e87e560e596 100644 --- a/packages/nx/src/project-graph/affected/affected-project-graph.ts +++ b/packages/nx/src/project-graph/affected/affected-project-graph.ts @@ -16,6 +16,9 @@ import { ProjectGraph } from '../../config/project-graph'; import { reverse } from '../operators'; import { ProjectConfiguration } from '../../config/workspace-json-project-json'; import { readNxJson } from '../../config/configuration'; +import { workspaceConfigName } from 'nx/src/config/workspaces'; +import { getTouchedProjectsFromProjectGlobChanges } from './locators/project-glob-changes'; +import { workspaceRoot } from 'nx/src/utils/workspace-root'; export function filterAffected( graph: ProjectGraph, @@ -29,9 +32,13 @@ export function filterAffected( getImplicitlyTouchedProjects, getTouchedNpmPackages, getImplicitlyTouchedProjectsByJsonChanges, - getTouchedProjectsInWorkspaceJson, getTouchedProjectsFromTsConfig, ]; + if (workspaceConfigName(workspaceRoot)) { + touchedProjectLocators.push(getTouchedProjectsInWorkspaceJson); + } else { + touchedProjectLocators.push(getTouchedProjectsFromProjectGlobChanges); + } const touchedProjects = touchedProjectLocators.reduce((acc, f) => { return acc.concat(f(touchedFiles, graph.nodes, nxJson, packageJson, graph)); }, [] as string[]); diff --git a/packages/nx/src/project-graph/affected/locators/implicit-json-changes.spec.ts b/packages/nx/src/project-graph/affected/locators/implicit-json-changes.spec.ts index 6842c63ec9f90..88498d4b25039 100644 --- a/packages/nx/src/project-graph/affected/locators/implicit-json-changes.spec.ts +++ b/packages/nx/src/project-graph/affected/locators/implicit-json-changes.spec.ts @@ -1,11 +1,11 @@ import { getImplicitlyTouchedProjectsByJsonChanges } from './implicit-json-changes'; import { WholeFileChange } from '../../file-utils'; -import { DiffType } from '../../../utils/json-diff'; +import { JsonDiffType } from '../../../utils/json-diff'; import { NxJsonConfiguration } from '../../../config/nx-json'; function getModifiedChange(path: string[]) { return { - type: DiffType.Modified, + type: JsonDiffType.Modified, path, value: { lhs: 'before', diff --git a/packages/nx/src/project-graph/affected/locators/npm-packages.spec.ts b/packages/nx/src/project-graph/affected/locators/npm-packages.spec.ts index d2e826c644bb3..97dba48a8befd 100644 --- a/packages/nx/src/project-graph/affected/locators/npm-packages.spec.ts +++ b/packages/nx/src/project-graph/affected/locators/npm-packages.spec.ts @@ -1,6 +1,6 @@ import { NxJsonConfiguration } from '../../../config/nx-json'; import { ProjectGraph } from '../../../config/project-graph'; -import { DiffType } from '../../../utils/json-diff'; +import { JsonDiffType } from '../../../utils/json-diff'; import { WholeFileChange } from '../../file-utils'; import { getTouchedNpmPackages } from './npm-packages'; @@ -76,7 +76,7 @@ describe('getTouchedNpmPackages', () => { hash: 'some-hash', getChanges: () => [ { - type: DiffType.Modified, + type: JsonDiffType.Modified, path: ['dependencies', 'happy-nrwl'], value: { lhs: '0.0.1', @@ -106,7 +106,7 @@ describe('getTouchedNpmPackages', () => { hash: 'some-hash', getChanges: () => [ { - type: DiffType.Modified, + type: JsonDiffType.Modified, path: ['dependencies', '@types/happy-nrwl'], value: { lhs: '0.0.1', @@ -141,7 +141,7 @@ describe('getTouchedNpmPackages', () => { hash: 'some-hash', getChanges: () => [ { - type: DiffType.Modified, + type: JsonDiffType.Modified, path: ['dependencies', '@types/happy-nrwl'], value: { lhs: '0.0.1', @@ -171,7 +171,7 @@ describe('getTouchedNpmPackages', () => { hash: 'some-hash', getChanges: () => [ { - type: DiffType.Deleted, + type: JsonDiffType.Deleted, path: ['dependencies', 'sad-nrwl'], value: { lhs: '0.0.1', @@ -209,7 +209,7 @@ describe('getTouchedNpmPackages', () => { hash: 'some-hash', getChanges: () => [ { - type: DiffType.Added, + type: JsonDiffType.Added, path: ['dependencies', 'awesome-nrwl'], value: { lhs: undefined, diff --git a/packages/nx/src/project-graph/affected/locators/npm-packages.ts b/packages/nx/src/project-graph/affected/locators/npm-packages.ts index be36f5aac0bcf..cfec247231cca 100644 --- a/packages/nx/src/project-graph/affected/locators/npm-packages.ts +++ b/packages/nx/src/project-graph/affected/locators/npm-packages.ts @@ -1,5 +1,9 @@ import { isWholeFileChange, WholeFileChange } from '../../file-utils'; -import { DiffType, isJsonChange, JsonChange } from '../../../utils/json-diff'; +import { + JsonDiffType, + isJsonChange, + JsonChange, +} from '../../../utils/json-diff'; import { TouchedProjectLocator } from '../affected-project-graph-models'; export const getTouchedNpmPackages: TouchedProjectLocator< @@ -20,7 +24,7 @@ export const getTouchedNpmPackages: TouchedProjectLocator< c.path.length === 2 ) { // A package was deleted so mark all workspace projects as touched. - if (c.type === DiffType.Deleted) { + if (c.type === JsonDiffType.Deleted) { touched = Object.keys(projectGraph.nodes); break; } else { diff --git a/packages/nx/src/project-graph/affected/locators/project-glob-changes.spec.ts b/packages/nx/src/project-graph/affected/locators/project-glob-changes.spec.ts new file mode 100644 index 0000000000000..7a3cf19e39a54 --- /dev/null +++ b/packages/nx/src/project-graph/affected/locators/project-glob-changes.spec.ts @@ -0,0 +1,52 @@ +import { ProjectGraphProjectNode } from 'nx/src/config/project-graph'; +import { ProjectConfiguration } from 'nx/src/config/workspace-json-project-json'; + +import { JsonDiffType } from '../../../utils/json-diff'; +import * as nxPlugin from '../../../utils/nx-plugin'; +import { DeletedFileChange, WholeFileChange } from '../../file-utils'; +import { getTouchedProjectsFromProjectGlobChanges } from './project-glob-changes'; + +function makeProjectGraphNode( + name, + configurationFile = 'project.json' +): ProjectGraphProjectNode { + return { + data: { + files: [ + { + file: `libs/${name}/${configurationFile}`, + hash: 'hash' + Math.floor(Math.random() * 10000), + }, + ], + root: `libs/${name}`, + }, + name, + type: 'lib', + }; +} + +describe('getTouchedProjectsFromProjectGlobChanges', () => { + beforeEach(() => { + jest.spyOn(nxPlugin, 'loadNxPlugins').mockReturnValue([]); + }); + + it('should affect all projects if a project is removed', () => { + const result = getTouchedProjectsFromProjectGlobChanges( + [ + { + file: 'libs/proj1/project.json', + hash: 'some-hash', + getChanges: () => [new DeletedFileChange()], + }, + ], + { + proj2: makeProjectGraphNode('proj2'), + proj3: makeProjectGraphNode('proj3'), + }, + { + plugins: [], + } + ); + expect(result).toEqual(['proj2', 'proj3']); + }); +}); diff --git a/packages/nx/src/project-graph/affected/locators/project-glob-changes.ts b/packages/nx/src/project-graph/affected/locators/project-glob-changes.ts new file mode 100644 index 0000000000000..63e0ce2d01f95 --- /dev/null +++ b/packages/nx/src/project-graph/affected/locators/project-glob-changes.ts @@ -0,0 +1,50 @@ +import { + DeletedFileChange, + isDeletedFileChange, + WholeFileChange, +} from '../../file-utils'; +import { JsonChange } from '../../../utils/json-diff'; +import { TouchedProjectLocator } from '../affected-project-graph-models'; +import minimatch = require('minimatch'); +import { + getGlobPatternsFromPackageManagerWorkspaces, + getGlobPatternsFromPlugins, +} from 'nx/src/config/workspaces'; +import { workspaceRoot } from 'nx/src/utils/workspace-root'; + +export const getTouchedProjectsFromProjectGlobChanges: TouchedProjectLocator< + WholeFileChange | JsonChange | DeletedFileChange +> = (touchedFiles, projectGraphNodes, nxJson): string[] => { + const pluginGlobPatterns = getGlobPatternsFromPlugins( + nxJson, + [workspaceRoot], + workspaceRoot + ); + const workspacesGlobPatterns = + getGlobPatternsFromPackageManagerWorkspaces(workspaceRoot); + + const patterns = [ + '**/project.json', + ...pluginGlobPatterns, + ...workspacesGlobPatterns, + ]; + const combinedGlobPattern = '{' + patterns.join(',') + '}'; + + const touchedProjects = new Set(); + for (const touchedFile of touchedFiles) { + const isProjectFile = minimatch(touchedFile.file, combinedGlobPattern); + if (isProjectFile) { + if ( + touchedFile.getChanges().some((change) => isDeletedFileChange(change)) + ) { + // If any project has been deleted, we must assume all projects were affected + return Object.keys(projectGraphNodes); + } + + // Modified project config files are under a project's root, and implicitly + // mark it as affected. Thus, we don't need to handle it here. + } + } + + return Array.from(touchedProjects); +}; diff --git a/packages/nx/src/project-graph/affected/locators/tsconfig-json-changes.ts b/packages/nx/src/project-graph/affected/locators/tsconfig-json-changes.ts index ca612d052a4b2..aa33780101102 100644 --- a/packages/nx/src/project-graph/affected/locators/tsconfig-json-changes.ts +++ b/packages/nx/src/project-graph/affected/locators/tsconfig-json-changes.ts @@ -1,5 +1,9 @@ import { WholeFileChange } from '../../file-utils'; -import { DiffType, isJsonChange, JsonChange } from '../../../utils/json-diff'; +import { + JsonDiffType, + isJsonChange, + JsonChange, +} from '../../../utils/json-diff'; import { getRootTsConfigFileName } from '../../../utils/typescript'; import { TouchedProjectLocator } from '../affected-project-graph-models'; import { ProjectGraphProjectNode } from '../../../config/project-graph'; @@ -33,7 +37,7 @@ export const getTouchedProjectsFromTsConfig: TouchedProjectLocator< } // If a path is deleted, everything is touched - if (change.type === DiffType.Deleted) { + if (change.type === JsonDiffType.Deleted) { return Object.keys(graph.nodes); } touched.push( diff --git a/packages/nx/src/project-graph/affected/locators/workspace-json-changes.spec.ts b/packages/nx/src/project-graph/affected/locators/workspace-json-changes.spec.ts index e8d93d0d53b67..5de31aef4c717 100644 --- a/packages/nx/src/project-graph/affected/locators/workspace-json-changes.spec.ts +++ b/packages/nx/src/project-graph/affected/locators/workspace-json-changes.spec.ts @@ -1,6 +1,6 @@ import { getTouchedProjectsInWorkspaceJson } from './workspace-json-changes'; import { WholeFileChange } from '../../file-utils'; -import { DiffType } from '../../../utils/json-diff'; +import { JsonDiffType } from '../../../utils/json-diff'; describe('getTouchedProjectsInWorkspaceJson', () => { it('should not return changes when workspace.json is not touched', () => { @@ -61,7 +61,7 @@ describe('getTouchedProjectsInWorkspaceJson', () => { hash: 'some-hash', getChanges: () => [ { - type: DiffType.Modified, + type: JsonDiffType.Modified, path: ['newProjectRoot'], value: { lhs: '', @@ -103,7 +103,7 @@ describe('getTouchedProjectsInWorkspaceJson', () => { hash: 'some-hash', getChanges: () => [ { - type: DiffType.Added, + type: JsonDiffType.Added, path: ['projects', 'proj1'], value: { lhs: undefined, @@ -114,7 +114,7 @@ describe('getTouchedProjectsInWorkspaceJson', () => { }, { - type: DiffType.Added, + type: JsonDiffType.Added, path: ['projects', 'proj1', 'root'], value: { lhs: undefined, @@ -147,7 +147,7 @@ describe('getTouchedProjectsInWorkspaceJson', () => { hash: 'some-hash', getChanges: () => [ { - type: DiffType.Deleted, + type: JsonDiffType.Deleted, path: ['projects', 'proj3'], value: { lhs: { @@ -191,7 +191,7 @@ describe('getTouchedProjectsInWorkspaceJson', () => { hash: 'some-hash', getChanges: () => [ { - type: DiffType.Modified, + type: JsonDiffType.Modified, path: ['projects', 'proj1'], value: { lhs: { @@ -203,7 +203,7 @@ describe('getTouchedProjectsInWorkspaceJson', () => { }, }, { - type: DiffType.Modified, + type: JsonDiffType.Modified, path: ['projects', 'proj1', 'root'], value: { lhs: 'proj3', diff --git a/packages/nx/src/project-graph/affected/locators/workspace-json-changes.ts b/packages/nx/src/project-graph/affected/locators/workspace-json-changes.ts index 50a1374231d1d..94809ac56444e 100644 --- a/packages/nx/src/project-graph/affected/locators/workspace-json-changes.ts +++ b/packages/nx/src/project-graph/affected/locators/workspace-json-changes.ts @@ -3,7 +3,11 @@ import { WholeFileChange, workspaceFileName, } from '../../file-utils'; -import { DiffType, isJsonChange, JsonChange } from '../../../utils/json-diff'; +import { + JsonDiffType, + isJsonChange, + JsonChange, +} from '../../../utils/json-diff'; import { TouchedProjectLocator } from '../affected-project-graph-models'; export const getTouchedProjectsInWorkspaceJson: TouchedProjectLocator< @@ -45,7 +49,7 @@ export const getTouchedProjectsInWorkspaceJson: TouchedProjectLocator< } switch (change.type) { - case DiffType.Deleted: { + case JsonDiffType.Deleted: { // We are not sure which projects used to depend on a deleted project // so return all projects to be safe return Object.keys(projectGraphNodes); diff --git a/packages/nx/src/project-graph/build-nodes/workspace-projects.ts b/packages/nx/src/project-graph/build-nodes/workspace-projects.ts index 479505d308869..f18d0f6427e66 100644 --- a/packages/nx/src/project-graph/build-nodes/workspace-projects.ts +++ b/packages/nx/src/project-graph/build-nodes/workspace-projects.ts @@ -50,7 +50,7 @@ export function buildWorkspaceProjectNodes( p.targets = mergePluginTargetsWithNxTargets( p.root, p.targets, - loadNxPlugins(ctx.workspace.plugins) + loadNxPlugins(ctx.workspace.plugins, [p.root], p.root) ); const projectType = diff --git a/packages/nx/src/project-graph/file-utils.spec.ts b/packages/nx/src/project-graph/file-utils.spec.ts index 1e8f4e322e91e..0af327d9ad984 100644 --- a/packages/nx/src/project-graph/file-utils.spec.ts +++ b/packages/nx/src/project-graph/file-utils.spec.ts @@ -1,5 +1,10 @@ -import { calculateFileChanges, WholeFileChange } from './file-utils'; -import { DiffType } from '../utils/json-diff'; +import { + calculateFileChanges, + DeletedFileChange, + WholeFileChange, +} from './file-utils'; +import * as fs from 'fs'; +import { JsonDiffType } from '../utils/json-diff'; import { defaultFileHasher } from '../hasher/file-hasher'; import ignore from 'ignore'; @@ -7,7 +12,8 @@ describe('calculateFileChanges', () => { beforeEach(() => { defaultFileHasher.ensureInitialized(); }); - it('should return a whole file change by default', () => { + it('should return a whole file change by default for files that exist', () => { + jest.spyOn(fs, 'existsSync').mockReturnValue(true); const changes = calculateFileChanges( ['proj/index.ts'], [], @@ -46,7 +52,7 @@ describe('calculateFileChanges', () => { ); expect(changes[0].getChanges()).toContainEqual({ - type: DiffType.Modified, + type: JsonDiffType.Modified, path: ['dependencies', 'happy-nrwl'], value: { lhs: '0.0.1', @@ -54,7 +60,7 @@ describe('calculateFileChanges', () => { }, }); expect(changes[0].getChanges()).toContainEqual({ - type: DiffType.Deleted, + type: JsonDiffType.Deleted, path: ['dependencies', 'not-awesome-nrwl'], value: { lhs: '0.0.1', @@ -62,7 +68,7 @@ describe('calculateFileChanges', () => { }, }); expect(changes[0].getChanges()).toContainEqual({ - type: DiffType.Added, + type: JsonDiffType.Added, path: ['dependencies', 'awesome-nrwl'], value: { lhs: undefined, @@ -71,6 +77,23 @@ describe('calculateFileChanges', () => { }); }); + it('should pick up deleted changes for deleted files', () => { + jest.spyOn(fs, 'existsSync').mockReturnValue(false); + const changes = calculateFileChanges( + ['i-dont-exist.json'], + [], + { + base: 'sha1', + head: 'sha2', + }, + (path, revision) => { + return ''; + } + ); + + expect(changes[0].getChanges()).toEqual([new DeletedFileChange()]); + }); + it('should ignore *.md changes', () => { const ig = ignore(); ig.add('*.md'); diff --git a/packages/nx/src/project-graph/file-utils.ts b/packages/nx/src/project-graph/file-utils.ts index 1bd07d75281d9..2aae12c2ce651 100644 --- a/packages/nx/src/project-graph/file-utils.ts +++ b/packages/nx/src/project-graph/file-utils.ts @@ -29,10 +29,20 @@ export class WholeFileChange implements Change { type = 'WholeFileChange'; } +export class DeletedFileChange implements Change { + type = 'WholeFileDeleted'; +} + export function isWholeFileChange(change: Change): change is WholeFileChange { return change.type === 'WholeFileChange'; } +export function isDeletedFileChange( + change: Change +): change is DeletedFileChange { + return change.type === 'WholeFileDeleted'; +} + export function readFileIfExisting(path: string) { return existsSync(path) ? readFileSync(path, 'utf-8') : ''; } @@ -66,6 +76,10 @@ export function calculateFileChanges( ext, hash, getChanges: (): Change[] => { + if (!existsSync(join(workspaceRoot, f))) { + return [new DeletedFileChange()]; + } + if (!nxArgs) { return [new WholeFileChange()]; } @@ -77,7 +91,6 @@ export function calculateFileChanges( case '.json': const atBase = readFileAtRevision(f, nxArgs.base); const atHead = readFileAtRevision(f, nxArgs.head); - try { return jsonDiff(JSON.parse(atBase), JSON.parse(atHead)); } catch (e) { diff --git a/packages/nx/src/utils/json-diff.spec.ts b/packages/nx/src/utils/json-diff.spec.ts index 032381884374a..8bde4737cf526 100644 --- a/packages/nx/src/utils/json-diff.spec.ts +++ b/packages/nx/src/utils/json-diff.spec.ts @@ -1,4 +1,4 @@ -import { jsonDiff, DiffType } from './json-diff'; +import { jsonDiff, JsonDiffType } from './json-diff'; describe('jsonDiff', () => { it('should return deep diffs of two JSON objects (including parents of children changes)', () => { @@ -10,7 +10,7 @@ describe('jsonDiff', () => { expect(result).toEqual( expect.arrayContaining([ { - type: DiffType.Modified, + type: JsonDiffType.Modified, path: ['a'], value: { lhs: { @@ -27,7 +27,7 @@ describe('jsonDiff', () => { }, }, { - type: DiffType.Modified, + type: JsonDiffType.Modified, path: ['a', 'b'], value: { lhs: { @@ -40,22 +40,22 @@ describe('jsonDiff', () => { }, }, { - type: DiffType.Modified, + type: JsonDiffType.Modified, path: ['a', 'b', 'c'], value: { lhs: 1, rhs: 2 }, }, { - type: DiffType.Deleted, + type: JsonDiffType.Deleted, path: ['x'], value: { lhs: 1, rhs: undefined }, }, { - type: DiffType.Added, + type: JsonDiffType.Added, path: ['y'], value: { lhs: undefined, rhs: 2 }, }, { - type: DiffType.Added, + type: JsonDiffType.Added, path: ['a', 'b', 'd'], value: { lhs: undefined, rhs: 2 }, }, @@ -74,7 +74,7 @@ describe('jsonDiff', () => { } ); expect(result).toContainEqual({ - type: DiffType.Modified, + type: JsonDiffType.Modified, path: ['a'], value: { lhs: { @@ -86,7 +86,7 @@ describe('jsonDiff', () => { }, }); expect(result).toContainEqual({ - type: DiffType.Modified, + type: JsonDiffType.Modified, path: ['a', 'b'], value: { lhs: 0, @@ -102,7 +102,7 @@ describe('jsonDiff', () => { } ); expect(result2).toContainEqual({ - type: DiffType.Added, + type: JsonDiffType.Added, path: ['a'], value: { lhs: undefined, rhs: {} }, }); @@ -121,7 +121,7 @@ describe('jsonDiff', () => { expect(result).toEqual( expect.arrayContaining([ { - type: DiffType.Modified, + type: JsonDiffType.Modified, path: ['rules'], value: { lhs: undefined, @@ -129,7 +129,7 @@ describe('jsonDiff', () => { }, }, { - type: DiffType.Added, + type: JsonDiffType.Added, path: ['rules', '0'], value: { lhs: undefined, @@ -153,7 +153,7 @@ describe('jsonDiff', () => { expect(result).toEqual( expect.arrayContaining([ { - type: DiffType.Modified, + type: JsonDiffType.Modified, path: ['rules'], value: { lhs: ['rule1'], @@ -161,7 +161,7 @@ describe('jsonDiff', () => { }, }, { - type: DiffType.Added, + type: JsonDiffType.Added, path: ['rules', '1'], value: { lhs: undefined, @@ -189,19 +189,19 @@ describe('jsonDiff', () => { ); expect(result).toContainEqual({ - type: DiffType.Modified, + type: JsonDiffType.Modified, path: ['dependencies', 'happy-nrwl'], value: { lhs: '0.0.1', rhs: '0.0.2' }, }); expect(result).toContainEqual({ - type: DiffType.Deleted, + type: JsonDiffType.Deleted, path: ['dependencies', 'not-awesome-nrwl'], value: { lhs: '0.0.1', rhs: undefined }, }); expect(result).toContainEqual({ - type: DiffType.Added, + type: JsonDiffType.Added, path: ['dependencies', 'awesome-nrwl'], value: { lhs: undefined, rhs: '0.0.1' }, }); diff --git a/packages/nx/src/utils/json-diff.ts b/packages/nx/src/utils/json-diff.ts index b5ceb2a02f83b..303c0387bc5ff 100644 --- a/packages/nx/src/utils/json-diff.ts +++ b/packages/nx/src/utils/json-diff.ts @@ -1,13 +1,13 @@ import { Change } from '../project-graph/file-utils'; -export enum DiffType { +export enum JsonDiffType { Deleted = 'JsonPropertyDeleted', Added = 'JsonPropertyAdded', Modified = 'JsonPropertyModified', } export interface JsonChange extends Change { - type: DiffType; + type: JsonDiffType; path: string[]; value: { lhs: any; @@ -17,9 +17,9 @@ export interface JsonChange extends Change { export function isJsonChange(change: Change): change is JsonChange { return ( - change.type === DiffType.Added || - change.type === DiffType.Deleted || - change.type === DiffType.Modified + change.type === JsonDiffType.Added || + change.type === JsonDiffType.Deleted || + change.type === JsonDiffType.Modified ); } @@ -32,7 +32,7 @@ export function jsonDiff(lhs: any, rhs: any): JsonChange[] { const rhsValue = getJsonValue(path, rhs); if (rhsValue === undefined) { result.push({ - type: DiffType.Deleted, + type: JsonDiffType.Deleted, path, value: { lhs: lhsValue, @@ -41,7 +41,7 @@ export function jsonDiff(lhs: any, rhs: any): JsonChange[] { }); } else if (!deepEquals(lhsValue, rhsValue)) { result.push({ - type: DiffType.Modified, + type: JsonDiffType.Modified, path, value: { lhs: lhsValue, @@ -56,7 +56,7 @@ export function jsonDiff(lhs: any, rhs: any): JsonChange[] { const addedInRhs = !seenInLhs.has(hashArray(path)); if (addedInRhs) { result.push({ - type: DiffType.Added, + type: JsonDiffType.Added, path, value: { lhs: undefined, diff --git a/packages/nx/src/utils/nx-plugin.ts b/packages/nx/src/utils/nx-plugin.ts index ec1009a7f68c7..dcd8b66ce1e37 100644 --- a/packages/nx/src/utils/nx-plugin.ts +++ b/packages/nx/src/utils/nx-plugin.ts @@ -46,7 +46,8 @@ export interface NxPlugin { let nxPluginCache: NxPlugin[] = null; export function loadNxPlugins( plugins?: string[], - paths = [workspaceRoot] + paths = [workspaceRoot], + root = workspaceRoot ): NxPlugin[] { return plugins?.length ? nxPluginCache || @@ -58,14 +59,12 @@ export function loadNxPlugins( }); } catch (e) { if (e.code === 'MODULE_NOT_FOUND') { - const plugin = resolveLocalNxPlugin(moduleName); + const plugin = resolveLocalNxPlugin(moduleName, root); if (plugin) { const main = readPluginMainFromProjectConfiguration( plugin.projectConfig ); - pluginPath = main - ? path.join(workspaceRoot, main) - : plugin.path; + pluginPath = main ? path.join(root, main) : plugin.path; } else { logger.error( `Plugin listed in \`nx.json\` not found: ${moduleName}` diff --git a/packages/react/src/migrations/update-14-0-0/add-default-development-configurations.spec.ts b/packages/react/src/migrations/update-14-0-0/add-default-development-configurations.spec.ts index 9c1c3e9f38436..b369cd4fd1b86 100644 --- a/packages/react/src/migrations/update-14-0-0/add-default-development-configurations.spec.ts +++ b/packages/react/src/migrations/update-14-0-0/add-default-development-configurations.spec.ts @@ -106,6 +106,7 @@ describe('React default development configuration', () => { const config = readProjectConfiguration(tree, 'example'); expect(config).toEqual({ $schema: '../../node_modules/nx/schemas/project-schema.json', + name: 'example', root: 'apps/example', projectType: 'application', }); diff --git a/packages/workspace/src/generators/convert-to-nx-project/convert-to-nx-project.spec.ts b/packages/workspace/src/generators/convert-to-nx-project/convert-to-nx-project.spec.ts index 84ea9dccaae64..e69f38e937b52 100644 --- a/packages/workspace/src/generators/convert-to-nx-project/convert-to-nx-project.spec.ts +++ b/packages/workspace/src/generators/convert-to-nx-project/convert-to-nx-project.spec.ts @@ -33,7 +33,7 @@ describe('convert-to-nx-project', () => { }); it('should throw if project && all are both specified', async () => { - const tree = createTreeWithEmptyWorkspace(); + const tree = createTreeWithWorkspaceFile(); await libraryGenerator(tree, { name: 'lib', @@ -47,7 +47,7 @@ describe('convert-to-nx-project', () => { it('should prompt for a project if neither project nor all are specified', async () => { const spy = jest.spyOn(enquirer, 'prompt'); - const tree = createTreeWithEmptyWorkspace(); + const tree = createTreeWithWorkspaceFile(); await libraryGenerator(tree, { name: 'lib', @@ -61,7 +61,7 @@ describe('convert-to-nx-project', () => { it('should not prompt for a project if all is specified', async () => { const spy = jest.spyOn(enquirer, 'prompt'); - const tree = createTreeWithEmptyWorkspace(); + const tree = createTreeWithWorkspaceFile(); await libraryGenerator(tree, { name: 'lib', @@ -73,7 +73,7 @@ describe('convert-to-nx-project', () => { }); it('should extract single project configuration to project.json', async () => { - const tree = createTreeWithEmptyWorkspace(); + const tree = createTreeWithWorkspaceFile(); await libraryGenerator(tree, { name: 'lib', @@ -93,7 +93,7 @@ describe('convert-to-nx-project', () => { }); it('should extract all project configurations to project.json', async () => { - const tree = createTreeWithEmptyWorkspace(); + const tree = createTreeWithWorkspaceFile(); await libraryGenerator(tree, { name: 'lib', @@ -122,7 +122,7 @@ describe('convert-to-nx-project', () => { }); it('should include tags in project.json', async () => { - const tree = createTreeWithEmptyWorkspace(); + const tree = createTreeWithWorkspaceFile(); await libraryGenerator(tree, { name: 'lib', @@ -142,7 +142,7 @@ describe('convert-to-nx-project', () => { }); it('should set workspace.json to point to the root directory', async () => { - const tree = createTreeWithEmptyWorkspace(); + const tree = createTreeWithWorkspaceFile(); await libraryGenerator(tree, { name: 'lib', standaloneConfig: false, @@ -171,7 +171,7 @@ describe('convert-to-nx-project', () => { it('should format files by default', async () => { jest.spyOn(devkit, 'formatFiles'); - const tree = createTreeWithEmptyWorkspace(); + const tree = createTreeWithWorkspaceFile(); await libraryGenerator(tree, { name: 'lib', @@ -187,7 +187,7 @@ describe('convert-to-nx-project', () => { it('should format files when passing skipFormat false', async () => { jest.spyOn(devkit, 'formatFiles'); - const tree = createTreeWithEmptyWorkspace(); + const tree = createTreeWithWorkspaceFile(); await libraryGenerator(tree, { name: 'lib', @@ -203,7 +203,7 @@ describe('convert-to-nx-project', () => { it('should not format files when passing skipFormat true ', async () => { jest.spyOn(devkit, 'formatFiles'); - const tree = createTreeWithEmptyWorkspace(); + const tree = createTreeWithWorkspaceFile(); await libraryGenerator(tree, { name: 'lib', @@ -216,3 +216,9 @@ describe('convert-to-nx-project', () => { expect(devkit.formatFiles).toHaveBeenCalledTimes(0); }); }); + +function createTreeWithWorkspaceFile() { + const tree = createTreeWithEmptyV1Workspace(); + tree.write('workspace.json', JSON.stringify({ version: 2, projects: {} })); + return tree; +} diff --git a/packages/workspace/src/generators/library/library.spec.ts b/packages/workspace/src/generators/library/library.spec.ts index 477e81a9d2ada..9a8abbf763d48 100644 --- a/packages/workspace/src/generators/library/library.spec.ts +++ b/packages/workspace/src/generators/library/library.spec.ts @@ -38,52 +38,11 @@ describe('lib', () => { it('should default to standalone project for first project', async () => { await libraryGenerator(tree, { ...defaultOptions, name: 'my-lib' }); - const workspaceJsonEntry = readJson(tree, 'workspace.json').projects[ - 'my-lib' - ]; const projectConfig = readProjectConfiguration(tree, 'my-lib'); expect(projectConfig.root).toEqual('libs/my-lib'); - expect(workspaceJsonEntry).toEqual('libs/my-lib'); - }); - - it('should obey standalone === false for first project', async () => { - await libraryGenerator(tree, { - ...defaultOptions, - name: 'my-lib', - standaloneConfig: false, - }); - const workspaceJsonEntry = readJson(tree, 'workspace.json').projects[ - 'my-lib' - ]; - const projectConfig = readProjectConfiguration(tree, 'my-lib'); - expect(projectConfig.root).toEqual('libs/my-lib'); - expect(projectConfig).toMatchObject(workspaceJsonEntry); }); }); - // describe('workspace v1', () => { - // beforeEach(() => { - // tree = createTreeWithEmptyV1Workspace(); - // }); - // - // it('should default to inline project for first project', async () => { - // await libraryGenerator(tree, { ...defaultOptions, name: 'my-lib' }); - // const workspaceJsonEntry = toNewFormat(readJson(tree, 'workspace.json')) - // .projects['my-lib']; - // const projectConfig = readProjectConfiguration(tree, 'my-lib'); - // expect(projectConfig.root).toEqual('libs/my-lib'); - // expect(projectConfig).toMatchObject(workspaceJsonEntry); - // }); - // - // it('should throw for standaloneConfig === true', async () => { - // const promise = libraryGenerator(tree, { - // standaloneConfig: true, - // name: 'my-lib', - // }); - // await expect(promise).rejects.toThrow(); - // }); - // }); - describe('not nested', () => { it('should update workspace.json', async () => { await libraryGenerator(tree, { diff --git a/packages/workspace/src/generators/move/lib/move-project-configuration.spec.ts b/packages/workspace/src/generators/move/lib/move-project-configuration.spec.ts index 05345768d4f97..9171ade126a96 100644 --- a/packages/workspace/src/generators/move/lib/move-project-configuration.spec.ts +++ b/packages/workspace/src/generators/move/lib/move-project-configuration.spec.ts @@ -230,8 +230,6 @@ describe('moveProjectConfiguration', () => { expect(() => { readProjectConfiguration(tree, projectName); }).toThrow(); - const ws = readJson(tree, 'workspace.json'); - expect(typeof ws.projects[newProjectName]).toBe('string'); expect(readProjectConfiguration(tree, newProjectName)).toBeDefined(); }); }); diff --git a/packages/workspace/src/generators/move/lib/move-project-configuration.ts b/packages/workspace/src/generators/move/lib/move-project-configuration.ts index 69573f8b263cb..0c7345bed6631 100644 --- a/packages/workspace/src/generators/move/lib/move-project-configuration.ts +++ b/packages/workspace/src/generators/move/lib/move-project-configuration.ts @@ -12,6 +12,10 @@ export function moveProjectConfiguration( schema: NormalizedSchema, projectConfig: ProjectConfiguration ) { + if (projectConfig.name) { + projectConfig.name = schema.newProjectName; + } + const isStandalone = isStandaloneProject(tree, schema.projectName); const projectString = JSON.stringify(projectConfig); const newProjectString = projectString.replace( diff --git a/packages/workspace/src/generators/move/lib/update-build-targets.spec.ts b/packages/workspace/src/generators/move/lib/update-build-targets.spec.ts index 5b0c396280cff..aeb9eff759b88 100644 --- a/packages/workspace/src/generators/move/lib/update-build-targets.spec.ts +++ b/packages/workspace/src/generators/move/lib/update-build-targets.spec.ts @@ -3,9 +3,11 @@ import { readProjectConfiguration, Tree, } from '@nrwl/devkit'; +import * as nxDevkit from '@nrwl/devkit'; import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; import { NormalizedSchema } from '../schema'; import { updateBuildTargets } from './update-build-targets'; +import { array } from 'yargs'; describe('updateBuildTargets', () => { let tree: Tree; @@ -82,4 +84,12 @@ describe('updateBuildTargets', () => { 'subfolder-my-destination:serve' ); }); + + it('should NOT attempt to update unrelated projects', async () => { + addProjectConfiguration(tree, 'unrelated', { root: 'libs/unrelated' }); + const spy = jest.spyOn(nxDevkit, 'updateProjectConfiguration'); + schema.projectName = 'storybook'; + updateBuildTargets(tree, schema); + expect(spy.mock.calls.map((x) => x[1])).not.toContain('unrelated'); + }); }); diff --git a/packages/workspace/src/generators/move/lib/update-build-targets.ts b/packages/workspace/src/generators/move/lib/update-build-targets.ts index a943dce11c930..18ec236c5dca6 100644 --- a/packages/workspace/src/generators/move/lib/update-build-targets.ts +++ b/packages/workspace/src/generators/move/lib/update-build-targets.ts @@ -11,42 +11,52 @@ import { NormalizedSchema } from '../schema'; */ export function updateBuildTargets(tree: Tree, schema: NormalizedSchema) { getProjects(tree).forEach((projectConfig, project) => { + let changed = false; Object.entries(projectConfig.targets || {}).forEach( ([target, targetConfig]) => { - updateJsonValue(targetConfig, (value) => { - const [project, target, configuration] = value.split(':'); - if (project === schema.projectName && target) { - return configuration - ? `${schema.newProjectName}:${target}:${configuration}` - : `${schema.newProjectName}:${target}`; - } - }); + changed = + updateJsonValue(targetConfig, (value) => { + const [project, target, configuration] = value.split(':'); + if (project === schema.projectName && target) { + return configuration + ? `${schema.newProjectName}:${target}:${configuration}` + : `${schema.newProjectName}:${target}`; + } + }) || changed; } ); - updateProjectConfiguration(tree, project, projectConfig); + if (changed) { + updateProjectConfiguration(tree, project, projectConfig); + } }); } function updateJsonValue( config: TargetConfiguration, callback: (x: string) => void | string -) { - function recur(obj, key, value) { +): boolean { + function recur(obj, key, value): boolean { + let changed = false; if (typeof value === 'string') { const result = callback(value); - if (result) { + if (result && obj[key] !== result) { obj[key] = result; + changed = true; } } else if (Array.isArray(value)) { value.forEach((x, idx) => recur(value, idx, x)); } else if (typeof value === 'object') { Object.entries(value).forEach(([k, v]) => { - recur(value, k, v); + changed = recur(value, k, v) || changed; }); } + return changed; } + let changed = false; Object.entries(config).forEach(([k, v]) => { - recur(config, k, v); + changed = recur(config, k, v) || changed; }); + + return changed; } diff --git a/packages/workspace/src/generators/new/__snapshots__/new.spec.ts.snap b/packages/workspace/src/generators/new/__snapshots__/new.spec.ts.snap index 9a525b810263c..d840545eb4084 100644 --- a/packages/workspace/src/generators/new/__snapshots__/new.spec.ts.snap +++ b/packages/workspace/src/generators/new/__snapshots__/new.spec.ts.snap @@ -112,11 +112,3 @@ Object { }, } `; - -exports[`new should generate an empty workspace.json 1`] = ` -Object { - "$schema": "./node_modules/nx/schemas/workspace-schema.json", - "projects": Object {}, - "version": 2, -} -`; diff --git a/packages/workspace/src/generators/new/new.spec.ts b/packages/workspace/src/generators/new/new.spec.ts index 9c8fd5faf73cb..931eb6efc10fd 100644 --- a/packages/workspace/src/generators/new/new.spec.ts +++ b/packages/workspace/src/generators/new/new.spec.ts @@ -19,7 +19,7 @@ describe('new', () => { tree = createTree(); }); - it('should generate an empty workspace.json', async () => { + it('should not generate a workspace.json', async () => { await newGenerator(tree, { ...defaultOptions, name: 'my-workspace', @@ -27,7 +27,7 @@ describe('new', () => { npmScope: 'npmScope', appName: 'app', }); - expect(readJson(tree, 'my-workspace/workspace.json')).toMatchSnapshot(); + expect(tree.exists('my-workspace/workspace.json')).toBeFalsy(); }); it('should generate an empty nx.json', async () => { diff --git a/packages/workspace/src/generators/new/new.ts b/packages/workspace/src/generators/new/new.ts index 0f8cc1082949c..40c4e363798f9 100644 --- a/packages/workspace/src/generators/new/new.ts +++ b/packages/workspace/src/generators/new/new.ts @@ -6,6 +6,7 @@ import { getWorkspacePath as devkitGetWorkspacePath, installPackagesTask, names, + NxJsonConfiguration, PackageManager, Tree, updateJson, @@ -284,60 +285,64 @@ function setDefaultLinter(host: Tree, options: Schema) { * This sets ESLint as the default for any schematics that default to TSLint */ function setESLintDefault(host: Tree, options: Schema) { - updateJson(host, getWorkspacePath(host, options), (json) => { - setDefault(json, '@nrwl/angular', 'application', 'linter', 'eslint'); - setDefault(json, '@nrwl/angular', 'library', 'linter', 'eslint'); - setDefault( - json, - '@nrwl/angular', - 'storybook-configuration', - 'linter', - 'eslint' - ); - return json; - }); + updateJson( + host, + join(options.directory, 'nx.json'), + (json) => { + setDefault(json, '@nrwl/angular', 'application', 'linter', 'eslint'); + setDefault(json, '@nrwl/angular', 'library', 'linter', 'eslint'); + setDefault( + json, + '@nrwl/angular', + 'storybook-configuration', + 'linter', + 'eslint' + ); + return json; + } + ); } /** * This sets TSLint as the default for any schematics that default to ESLint */ function setTSLintDefault(host: Tree, options: Schema) { - updateJson(host, getWorkspacePath(host, options), (json) => { - setDefault(json, '@nrwl/workspace', 'library', 'linter', 'tslint'); - setDefault(json, '@nrwl/cypress', 'cypress-project', 'linter', 'tslint'); - setDefault(json, '@nrwl/cypress', 'cypress-project', 'linter', 'tslint'); - setDefault(json, '@nrwl/node', 'application', 'linter', 'tslint'); - setDefault(json, '@nrwl/node', 'library', 'linter', 'tslint'); - setDefault(json, '@nrwl/nest', 'application', 'linter', 'tslint'); - setDefault(json, '@nrwl/nest', 'library', 'linter', 'tslint'); - setDefault(json, '@nrwl/express', 'application', 'linter', 'tslint'); - setDefault(json, '@nrwl/express', 'library', 'linter', 'tslint'); - - return json; - }); -} - -function getWorkspacePath(host: Tree, { directory, cli }: Schema) { - return join(directory, cli === 'angular' ? 'angular.json' : 'workspace.json'); + updateJson( + host, + join(options.directory, 'nx.json'), + (json) => { + setDefault(json, '@nrwl/workspace', 'library', 'linter', 'tslint'); + setDefault(json, '@nrwl/cypress', 'cypress-project', 'linter', 'tslint'); + setDefault(json, '@nrwl/cypress', 'cypress-project', 'linter', 'tslint'); + setDefault(json, '@nrwl/node', 'application', 'linter', 'tslint'); + setDefault(json, '@nrwl/node', 'library', 'linter', 'tslint'); + setDefault(json, '@nrwl/nest', 'application', 'linter', 'tslint'); + setDefault(json, '@nrwl/nest', 'library', 'linter', 'tslint'); + setDefault(json, '@nrwl/express', 'application', 'linter', 'tslint'); + setDefault(json, '@nrwl/express', 'library', 'linter', 'tslint'); + + return json; + } + ); } function setDefault( - json: any, + json: NxJsonConfiguration, collectionName: string, generatorName: string, key: string, value: any ) { - if (!json.schematics) json.schematics = {}; + if (!json.generators) json.generators = {}; if ( - json.schematics[collectionName] && - json.schematics[collectionName][generatorName] + json.generators[collectionName] && + json.generators[collectionName][generatorName] ) { - json.schematics[collectionName][generatorName][key] = value; - } else if (json.schematics[`${collectionName}:${generatorName}`]) { - json.schematics[`${collectionName}:${generatorName}`][key] = value; + json.generators[collectionName][generatorName][key] = value; + } else if (json.generators[`${collectionName}:${generatorName}`]) { + json.generators[`${collectionName}:${generatorName}`][key] = value; } else { - json.schematics[collectionName] = json.schematics[collectionName] || {}; - json.schematics[collectionName][generatorName] = { [key]: value }; + json.generators[collectionName] = json.generators[collectionName] || {}; + json.generators[collectionName][generatorName] = { [key]: value }; } } diff --git a/packages/workspace/src/generators/remove/lib/remove-project-config.ts b/packages/workspace/src/generators/remove/lib/remove-project-config.ts index 8ac38f77dd6ba..5215292160799 100644 --- a/packages/workspace/src/generators/remove/lib/remove-project-config.ts +++ b/packages/workspace/src/generators/remove/lib/remove-project-config.ts @@ -36,11 +36,16 @@ export function removeProjectConfig(tree: Tree, schema: Schema) { // Remove implicit dependencies onto removed project getProjects(tree).forEach((project, projectName) => { - if (project.implicitDependencies) { + if ( + project.implicitDependencies && + project.implicitDependencies.some( + (projectName) => projectName === schema.projectName + ) + ) { project.implicitDependencies = project.implicitDependencies.filter( (projectName) => projectName !== schema.projectName ); + updateProjectConfiguration(tree, projectName, project); } - updateProjectConfiguration(tree, projectName, project); }); } diff --git a/packages/workspace/src/generators/workspace/files/__workspaceFile__.json__tmpl__ b/packages/workspace/src/generators/workspace/files/__workspaceFile__.json__tmpl__ deleted file mode 100644 index 2aacd396f2dd6..0000000000000 --- a/packages/workspace/src/generators/workspace/files/__workspaceFile__.json__tmpl__ +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "./node_modules/nx/schemas/workspace-schema.json", - "version": 2, - "projects": {} -} diff --git a/packages/workspace/src/generators/workspace/workspace.spec.ts b/packages/workspace/src/generators/workspace/workspace.spec.ts index 15c2da4c0e839..d3b462282c17b 100644 --- a/packages/workspace/src/generators/workspace/workspace.spec.ts +++ b/packages/workspace/src/generators/workspace/workspace.spec.ts @@ -23,12 +23,12 @@ describe('@nrwl/workspace:workspace', () => { defaultBase: 'main', }); expect(tree.exists('/proj/nx.json')).toBe(true); - expect(tree.exists('/proj/workspace.json')).toBe(true); + expect(tree.exists('/proj/workspace.json')).toBe(false); expect(tree.exists('/proj/.prettierrc')).toBe(true); expect(tree.exists('/proj/.prettierignore')).toBe(true); }); - it('should create nx.json and workspace.json', async () => { + it('should create nx.json', async () => { const ajv = new Ajv(); await workspaceGenerator(tree, { @@ -61,15 +61,6 @@ describe('@nrwl/workspace:workspace', () => { }); const validateNxJson = ajv.compile(nxSchema); expect(validateNxJson(nxJson)).toEqual(true); - - const workspaceJson = readJson(tree, '/proj/workspace.json'); - expect(workspaceJson).toEqual({ - $schema: './node_modules/nx/schemas/workspace-schema.json', - version: 2, - projects: {}, - }); - const validateWorkspaceJson = ajv.compile(workspaceSchema); - expect(validateWorkspaceJson(workspaceJson)).toEqual(true); }); it('should setup named inputs and target defaults for non-empty presets', async () => { diff --git a/packages/workspace/src/generators/workspace/workspace.ts b/packages/workspace/src/generators/workspace/workspace.ts index ff320f6f37a7a..279d7de0aef27 100644 --- a/packages/workspace/src/generators/workspace/workspace.ts +++ b/packages/workspace/src/generators/workspace/workspace.ts @@ -151,26 +151,6 @@ function createYarnrcYml(host: Tree, options: Schema) { ); } -function formatWorkspaceJson(host: Tree, options: Schema) { - const path = join( - options.directory, - options.cli === 'angular' ? 'angular.json' : 'workspace.json' - ); - - try { - updateJson(host, path, (workspaceJson) => { - const reformatted = reformattedWorkspaceJsonOrNull(workspaceJson); - if (reformatted) { - return reformatted; - } - return workspaceJson; - }); - } catch (e) { - console.error(`Failed to format: ${path}`); - console.error(e); - } -} - function addNpmScripts(host: Tree, options: Schema) { if (options.cli === 'angular') { updateJson(host, join(options.directory, 'package.json'), (json) => { @@ -222,7 +202,6 @@ export async function workspaceGenerator(host: Tree, options: Schema) { createAppsAndLibsFolders(host, options); await formatFiles(host); - formatWorkspaceJson(host, options); } export const workspaceSchematic = convertNxGenerator(workspaceGenerator); diff --git a/packages/workspace/src/migrations/update-13-0-0/config-locations/config-locations.spec.ts b/packages/workspace/src/migrations/update-13-0-0/config-locations/config-locations.spec.ts index 2fdda0c406355..1d4f03a86235a 100644 --- a/packages/workspace/src/migrations/update-13-0-0/config-locations/config-locations.spec.ts +++ b/packages/workspace/src/migrations/update-13-0-0/config-locations/config-locations.spec.ts @@ -16,6 +16,7 @@ describe('update to v13 config locations', () => { beforeEach(async () => { tree = createTreeWithEmptyWorkspace(); + tree.write('workspace.json', JSON.stringify({ version: 2, projects: {} })); updateJson(tree, 'workspace.json', (json) => ({ ...json, cli: { diff --git a/packages/workspace/src/migrations/update-13-3-0/update-tsc-executor-location.spec.ts b/packages/workspace/src/migrations/update-13-3-0/update-tsc-executor-location.spec.ts index 7c1d68a7170f7..648b5ba49ce92 100644 --- a/packages/workspace/src/migrations/update-13-3-0/update-tsc-executor-location.spec.ts +++ b/packages/workspace/src/migrations/update-13-3-0/update-tsc-executor-location.spec.ts @@ -1,7 +1,5 @@ import { Tree, - writeJson, - readWorkspaceConfiguration, addProjectConfiguration, getProjects, readJson, @@ -44,6 +42,7 @@ describe('add `defaultBase` in nx.json', () => { expect(projects).toEqual({ 'tsc-project': { $schema: '../../node_modules/nx/schemas/project-schema.json', + name: 'tsc-project', root: 'projects/tsc-project', targets: { build: { @@ -56,6 +55,7 @@ describe('add `defaultBase` in nx.json', () => { }, 'other-project': { $schema: '../../node_modules/nx/schemas/project-schema.json', + name: 'other-project', root: 'projects/other-project', targets: { build: { diff --git a/packages/workspace/src/migrations/update-14-8-0/change-run-commands-executor.spec.ts b/packages/workspace/src/migrations/update-14-8-0/change-run-commands-executor.spec.ts index 906f15bdb5f79..6b27810e5b1ac 100644 --- a/packages/workspace/src/migrations/update-14-8-0/change-run-commands-executor.spec.ts +++ b/packages/workspace/src/migrations/update-14-8-0/change-run-commands-executor.spec.ts @@ -33,6 +33,7 @@ describe('changeRunCommandsExecutor', () => { expect(readProjectConfiguration(tree, 'proj1')).toMatchInlineSnapshot(` Object { "$schema": "../node_modules/nx/schemas/project-schema.json", + "name": "proj1", "root": "proj1", "targets": Object { "notScriptTarget": Object { diff --git a/packages/workspace/src/utils/cli-config-utils.ts b/packages/workspace/src/utils/cli-config-utils.ts index eba2d955dcd9c..c3546eb4393aa 100644 --- a/packages/workspace/src/utils/cli-config-utils.ts +++ b/packages/workspace/src/utils/cli-config-utils.ts @@ -64,6 +64,7 @@ export function replaceAppNameWithPath( 'tags', 'defaultConfiguration', 'maximumError', + 'name', ]; // Some of the properties should not be renamed return Object.keys(node).reduce( (m, c) => ( diff --git a/packages/workspace/src/utils/rules/format-files.ts b/packages/workspace/src/utils/rules/format-files.ts index 41f68cf6e2463..bb753b80b762e 100644 --- a/packages/workspace/src/utils/rules/format-files.ts +++ b/packages/workspace/src/utils/rules/format-files.ts @@ -82,8 +82,8 @@ export function formatFiles( function updateWorkspaceJsonToMatchFormatVersion(host: Tree) { const workspaceConfigPath = workspaceConfigName(workspaceRoot); - try { - if (workspaceConfigPath) { + if (workspaceConfigPath) { + try { const workspaceJson = parseJson( host.read(workspaceConfigPath).toString() ); @@ -91,9 +91,11 @@ function updateWorkspaceJsonToMatchFormatVersion(host: Tree) { if (reformatted) { host.overwrite(workspaceConfigPath, serializeJson(reformatted)); } + } catch (e) { + console.error( + `Failed to format workspace config: ${workspaceConfigPath}` + ); + console.error(e); } - } catch (e) { - console.error(`Failed to format workspace config: ${workspaceConfigPath}`); - console.error(e); } }