feat(core): don't generate workspace.json for v2 workspaces (#12127)
This commit is contained in:
parent
beef424029
commit
9b63ce167a
3
.gitignore
vendored
3
.gitignore
vendored
@ -21,3 +21,6 @@ CHANGELOG.md
|
||||
|
||||
# Next.js
|
||||
.next
|
||||
|
||||
# Local dev files
|
||||
.env
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -60,7 +60,6 @@ describe('Move Angular Project', () => {
|
||||
expect(moveOutput).toContain(
|
||||
`CREATE apps/${newPath}/src/environments/environment.ts`
|
||||
);
|
||||
expect(moveOutput).toContain(`UPDATE workspace.json`);
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@ -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`
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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');
|
||||
|
||||
|
||||
@ -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 () => {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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', () => {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
});
|
||||
|
||||
@ -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 () => {
|
||||
|
||||
@ -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 () => {
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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' } },
|
||||
});
|
||||
|
||||
@ -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,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@ -12,5 +12,4 @@ export function cleanUpFiles(appName: string) {
|
||||
);
|
||||
|
||||
removeSync('temp-workspace');
|
||||
removeSync('workspace.json');
|
||||
}
|
||||
|
||||
@ -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');
|
||||
});
|
||||
|
||||
@ -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 () => {
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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'
|
||||
);
|
||||
});
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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()) {
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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',
|
||||
});
|
||||
|
||||
@ -26,6 +26,7 @@ const allowedProjectExtensions = [
|
||||
'$schema',
|
||||
'generators',
|
||||
'namedInputs',
|
||||
'name',
|
||||
];
|
||||
|
||||
const allowedWorkspaceExtensions = [
|
||||
|
||||
@ -515,7 +515,7 @@ export class NxScopedHost extends virtualFs.ScopedHost<any> {
|
||||
}
|
||||
// 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<AngularJsonConfiguration, 'projects'> & {
|
||||
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(
|
||||
|
||||
@ -148,7 +148,7 @@ async function runExecutorInternal<T extends { success: boolean }>(
|
||||
mergePluginTargetsWithNxTargets(
|
||||
proj.root,
|
||||
proj.targets,
|
||||
loadNxPlugins(workspace.plugins)
|
||||
loadNxPlugins(workspace.plugins, [root], root)
|
||||
)[target];
|
||||
|
||||
if (!targetConfig) {
|
||||
|
||||
@ -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(',') + '}';
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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,
|
||||
});
|
||||
|
||||
@ -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'];
|
||||
|
||||
|
||||
@ -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<ProjectConfiguration>,
|
||||
@ -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[]);
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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<ProjectConfiguration> {
|
||||
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']);
|
||||
});
|
||||
});
|
||||
@ -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<string>();
|
||||
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);
|
||||
};
|
||||
@ -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(
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 =
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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' },
|
||||
});
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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}`
|
||||
|
||||
@ -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',
|
||||
});
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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, {
|
||||
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
`;
|
||||
|
||||
@ -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 () => {
|
||||
|
||||
@ -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<NxJsonConfiguration>(
|
||||
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');
|
||||
updateJson<NxJsonConfiguration>(
|
||||
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 getWorkspacePath(host: Tree, { directory, cli }: Schema) {
|
||||
return join(directory, cli === 'angular' ? 'angular.json' : 'workspace.json');
|
||||
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 };
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
{
|
||||
"$schema": "./node_modules/nx/schemas/workspace-schema.json",
|
||||
"version": 2,
|
||||
"projects": {}
|
||||
}
|
||||
@ -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 () => {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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) => (
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user