fix(core): automatically add root to the project.json projects (#9977)

* fix(core): automatically add root to the project.json projects

* chore(core): move project-configuration generator utils to nx package

* fix(core): add migrations to remove root
This commit is contained in:
Jason Jean 2022-04-28 13:24:35 -04:00 committed by GitHub
parent c3d30a4fc7
commit 5e23c07077
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 338 additions and 175 deletions

View File

@ -182,7 +182,6 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
// check project configuration
const projectConfig = readJson(`apps/${project}/project.json`);
expect(projectConfig.root).toEqual(`apps/${project}`);
expect(projectConfig.sourceRoot).toEqual(`apps/${project}/src`);
expect(projectConfig.targets.build).toEqual({
executor: '@angular-devkit/build-angular:browser',
@ -259,7 +258,6 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
// check e2e project config
const e2eProjectConfig = readJson(`apps/${project}-e2e/project.json`);
expect(e2eProjectConfig.root).toEqual(`apps/${project}-e2e`);
expect(e2eProjectConfig.targets.e2e).toEqual({
executor: '@angular-devkit/build-angular:protractor',
options: {
@ -356,7 +354,6 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
// check e2e project config
const e2eProjectConfig = readJson(`apps/${project}-e2e/project.json`);
expect(e2eProjectConfig.root).toEqual(`apps/${e2eProject}`);
expect(e2eProjectConfig.targets['cypress-run']).toEqual({
executor: '@nrwl/cypress:cypress',
options: {

View File

@ -282,7 +282,7 @@ describe('Nx Plugin', () => {
checkFilesExist(`libs/subdir/${plugin}/package.json`);
const pluginProject = readProjectConfig(`subdir-${plugin}`);
const pluginE2EProject = readProjectConfig(`subdir-${plugin}-e2e`);
expect(pluginProject.root).toBe(`libs/subdir/${plugin}`);
expect(pluginProject.targets).toBeDefined();
expect(pluginE2EProject).toBeTruthy();
}, 90000);
});

View File

@ -318,7 +318,6 @@ describe('move project', () => {
expect(workspaceJson.projects[`${lib1}-data-access`]).toBeUndefined();
const project = readProjectConfig(newName);
expect(project).toBeTruthy();
expect(project.root).toBe(newPath);
expect(project.sourceRoot).toBe(`${newPath}/src`);
expect(project.targets.lint.options.lintFilePatterns).toEqual([
`libs/shared/${lib1}/data-access/**/*.ts`,
@ -445,7 +444,6 @@ describe('move project', () => {
expect(workspaceJson.projects[`${lib1}-data-access`]).toBeUndefined();
const project = readProjectConfig(newName);
expect(project).toBeTruthy();
expect(project.root).toBe(newPath);
expect(project.sourceRoot).toBe(`${newPath}/src`);
expect(project.tags).toEqual([]);
const lib3Config = readProjectConfig(lib3);
@ -578,7 +576,6 @@ describe('move project', () => {
expect(workspaceJson.projects[`${lib1}-data-access`]).toBeUndefined();
const project = readProjectConfig(newName);
expect(project).toBeTruthy();
expect(project.root).toBe(newPath);
expect(project.sourceRoot).toBe(`${newPath}/src`);
expect(project.targets.lint.options.lintFilePatterns).toEqual([
`packages/shared/${lib1}/data-access/**/*.ts`,

View File

@ -46,6 +46,9 @@ function updateAppAndE2EProjectConfigurations(
options.ngCliSchematicAppRoot,
options.appProjectRoot
);
// project already has the right root, but the above function, makes it incorrect.
// This corrects it.
project.root = options.appProjectRoot;
}
delete project.targets.test;

View File

@ -138,6 +138,9 @@ function fixProjectWorkspaceConfig(host: Tree, options: NormalizedSchema) {
options.ngCliSchematicLibRoot,
options.projectRoot
);
// project already has the right root, but the above function, makes it incorrect.
// This corrects it.
project.root = options.projectRoot;
}
if (!options.publishable && !options.buildable) {

View File

@ -12,7 +12,7 @@
/**
* @category Tree
*/
export type { Tree, FileChange } from 'nx/src/config/tree';
export type { Tree, FileChange } from 'nx/src/generators/tree';
/**
* @category Workspace
@ -113,7 +113,7 @@ export { generateFiles } from './src/generators/generate-files';
/**
* @category Generators
*/
export type { WorkspaceConfiguration } from './src/generators/project-configuration';
export type { WorkspaceConfiguration } from 'nx/src/generators/utils/project-configuration';
/**
* @category Generators
@ -127,7 +127,7 @@ export {
updateWorkspaceConfiguration,
getProjects,
isStandaloneProject,
} from './src/generators/project-configuration';
} from 'nx/src/generators/utils/project-configuration';
/**
* @category Generators
@ -185,7 +185,7 @@ export { ProjectGraphBuilder } from 'nx/src/project-graph/project-graph-builder'
/**
* @category Utils
*/
export { readJson, writeJson, updateJson } from './src/utils/json';
export { readJson, writeJson, updateJson } from 'nx/src/generators/utils/json';
/**
* @category Utils

View File

@ -1,14 +1,14 @@
import type { Tree } from 'nx/src/config/tree';
import type { Tree } from 'nx/src/generators/tree';
import * as path from 'path';
import type * as Prettier from 'prettier';
import { getWorkspacePath } from '../utils/get-workspace-layout';
import { readJson, updateJson, writeJson } from '../utils/json';
import { sortObjectByKeys } from 'nx/src/utils/object-sort';
import { readJson, updateJson, writeJson } from 'nx/src/generators/utils/json';
import {
getWorkspacePath,
readWorkspaceConfiguration,
updateWorkspaceConfiguration,
WorkspaceConfiguration,
} from './project-configuration';
} from 'nx/src/generators/utils/project-configuration';
import { sortObjectByKeys } from 'nx/src/utils/object-sort';
/**
* Formats all the created or updated files using Prettier

View File

@ -1,5 +1,5 @@
import type { Tree } from 'nx/src/config/tree';
import { createTree } from '../tests/create-tree';
import type { Tree } from 'nx/src/generators/tree';
import { createTree } from 'nx/src/generators/testing-utils/create-tree';
import { generateFiles } from './generate-files';
import { join } from 'path';
import * as FileType from 'file-type';

View File

@ -1,6 +1,6 @@
import { readFileSync, readdirSync, statSync } from 'fs';
import * as path from 'path';
import type { Tree } from 'nx/src/config/tree';
import type { Tree } from 'nx/src/generators/tree';
import { logger } from 'nx/src/utils/logger';
const binaryExts = new Set([

View File

@ -1,4 +1,4 @@
import type { Tree } from 'nx/src/config/tree';
import type { Tree } from 'nx/src/generators/tree';
/**
* Rename and transpile any new typescript files created to javascript files

View File

@ -1,5 +1,5 @@
import type { Tree } from 'nx/src/config/tree';
import { updateJson } from '../utils/json';
import type { Tree } from 'nx/src/generators/tree';
import { updateJson } from 'nx/src/generators/utils/json';
export function updateTsConfigsToJs(
tree: Tree,

View File

@ -1,5 +1,5 @@
import { createTree } from '../tests/create-tree';
import type { Tree } from 'nx/src/config/tree';
import { createTree } from 'nx/src/generators/testing-utils/create-tree';
import type { Tree } from 'nx/src/generators/tree';
import { visitNotIgnoredFiles } from './visit-not-ignored-files';
describe('visitNotIgnoredFiles', () => {

View File

@ -1,4 +1,4 @@
import type { Tree } from 'nx/src/config/tree';
import type { Tree } from 'nx/src/generators/tree';
import ignore, { Ignore } from 'ignore';
import { join, relative, sep } from 'path';

View File

@ -1,4 +1,4 @@
import type { Tree } from 'nx/src/config/tree';
import type { Tree } from 'nx/src/generators/tree';
import { execSync } from 'child_process';
import { join } from 'path';
import {

View File

@ -1,9 +1,11 @@
import { RawWorkspaceJsonConfiguration } from 'nx/src/config/workspace-json-project-json';
import {
getWorkspacePath,
readNxJson,
shouldDefaultToUsingStandaloneConfigs,
} from 'nx/src/generators/utils/project-configuration';
import type { Tree } from 'nx/src/generators/tree';
import { readNxJson } from '../generators/project-configuration';
import { readJson } from './json';
import type { Tree } from 'nx/src/config/tree';
export { getWorkspacePath } from 'nx/src/generators/utils/project-configuration';
/**
* Returns workspace defaults. It includes defaults folders for apps and libs,
@ -23,37 +25,10 @@ export function getWorkspaceLayout(tree: Tree): {
npmScope: string;
} {
const nxJson = readNxJson(tree);
const workspacePath = getWorkspacePath(tree);
const rawWorkspace =
workspacePath && tree.exists(workspacePath)
? readJson<RawWorkspaceJsonConfiguration>(tree, workspacePath)
: null;
return {
appsDir: nxJson?.workspaceLayout?.appsDir ?? 'apps',
libsDir: nxJson?.workspaceLayout?.libsDir ?? 'libs',
npmScope: nxJson?.npmScope ?? '',
standaloneAsDefault: !rawWorkspace
? true // if workspace.json doesn't exist, all projects **must** be standalone
: Object.values(rawWorkspace.projects).reduce(
// default for second, third... projects should be based on all projects being defined as a path
// for configuration read from ng schematics, this is determined by configFilePath's presence
(allStandalone, next) =>
allStandalone &&
(typeof next === 'string' || 'configFilePath' in next),
// default for first project should be true if using Nx Schema
rawWorkspace.version > 1
),
standaloneAsDefault: shouldDefaultToUsingStandaloneConfigs(tree),
};
}
export function getWorkspacePath(
tree: Tree
): '/angular.json' | '/workspace.json' | null {
const possibleFiles: ('/angular.json' | '/workspace.json')[] = [
'/angular.json',
'/workspace.json',
];
return possibleFiles.filter((path) => tree.exists(path))[0];
}

View File

@ -1,9 +1,13 @@
import { logger, stripIndent } from 'nx/src/utils/logger';
import type { FileChange, Tree, TreeWriteOptions } from 'nx/src/config/tree';
import type {
FileChange,
Tree,
TreeWriteOptions,
} from 'nx/src/generators/tree';
import { toNewFormat, toOldFormatOrNull } from 'nx/src/config/workspaces';
import { Generator, GeneratorCallback } from 'nx/src/config/misc-interfaces';
import { parseJson, serializeJson } from 'nx/src/utils/json';
import { join, relative } from 'path';
import { join, relative, dirname } from 'path';
class RunCallbackTask {
constructor(private callback: GeneratorCallback) {}
@ -192,9 +196,10 @@ class DevkitTreeFromAngularDevkitTree implements Tree {
const w = parseJson(content.toString());
for (const [project, configuration] of Object.entries(w.projects)) {
if (typeof configuration === 'string') {
w.projects[project] = parseJson(
this.tree.read(`${configuration}/project.json`)
);
w.projects[project] = {
root: configuration,
...parseJson(this.tree.read(`${configuration}/project.json`)),
};
w.projects[project].configFilePath = `${configuration}/project.json`;
}
}

View File

@ -1,4 +1,4 @@
import { Tree } from 'nx/src/config/tree';
import { Tree } from 'nx/src/generators/tree';
import { relative } from 'path';
import { visitNotIgnoredFiles } from '../generators/visit-not-ignored-files';
import { normalizePath } from 'nx/src/utils/path';

View File

@ -1,7 +1,7 @@
import type { Tree } from 'nx/src/config/tree';
import { readJson, writeJson } from './json';
import type { Tree } from 'nx/src/generators/tree';
import { readJson, writeJson } from 'nx/src/generators/utils/json';
import { addDependenciesToPackageJson } from './package-json';
import { createTree } from '../tests/create-tree';
import { createTree } from 'nx/src/generators/testing-utils/create-tree';
describe('addDependenciesToPackageJson', () => {
let tree: Tree;

View File

@ -1,6 +1,6 @@
import { readJson, updateJson } from './json';
import { readJson, updateJson } from 'nx/src/generators/utils/json';
import { installPackagesTask } from '../tasks/install-packages-task';
import type { Tree } from 'nx/src/config/tree';
import type { Tree } from 'nx/src/generators/tree';
import { GeneratorCallback } from 'nx/src/config/misc-interfaces';
/**

View File

@ -1,2 +1,2 @@
export { createTreeWithEmptyWorkspace } from './src/tests/create-tree-with-empty-workspace';
export { createTree } from './src/tests/create-tree';
export { createTreeWithEmptyWorkspace } from 'nx/src/generators/testing-utils/create-tree-with-empty-workspace';
export { createTree } from 'nx/src/generators/testing-utils/create-tree';

View File

@ -7,7 +7,7 @@ const mockResolveConfig = jest.fn(() =>
);
import { Tree } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from 'packages/devkit/src/tests/create-tree-with-empty-workspace';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import update from './update-base-jest-config';
describe('update 12.6.0', () => {

View File

@ -1,3 +1,10 @@
{
"migrations": {}
"generators": {
"14-0-4-remove-root": {
"cli": "nx",
"version": "14.0.4",
"description": "Remove root property from project.json files",
"factory": "./src/migrations/update-14-0-4/remove-roots"
}
}
}

View File

@ -0,0 +1,12 @@
import path = require('path');
import json = require('./migrations.json');
describe('Node migrations', () => {
it('should have valid paths', () => {
Object.values(json.generators).forEach((m) => {
expect(() =>
require.resolve(path.join(__dirname, `${m.factory}.ts`))
).not.toThrow();
});
});
});

View File

@ -15,7 +15,7 @@ import { createConsoleLogger, NodeJsSyncHost } from '@angular-devkit/core/node';
import { Stats } from 'fs';
import { detectPackageManager } from '../utils/package-manager';
import { GenerateOptions } from '../command-line/generate';
import { FileChange, Tree } from '../config/tree';
import { FileChange, Tree } from '../generators/tree';
import {
buildWorkspaceConfigurationFromGlobs,
globForProjectFiles,
@ -497,6 +497,7 @@ export class NxScopedHost extends virtualFs.ScopedHost<any> {
// project was read from a project.json file
const configPath = projectConfig.configFilePath;
const fileConfigObject = { ...projectConfig };
delete fileConfigObject.root; // remove the root before writing
delete fileConfigObject.configFilePath; // remove the configFilePath before writing
const projectJsonWrite = super.write(
configPath,
@ -539,6 +540,7 @@ export class NxScopedHost extends virtualFs.ScopedHost<any> {
map((x) => ({
project,
projectConfig: {
root: dirname(configFilePath),
...parseJson(Buffer.from(x).toString()),
configFilePath,
},
@ -600,9 +602,10 @@ export class NxScopeHostUsedForWrappedSchematics extends NxScopedHost {
const nxJsonInTree = nxJsonChange
? parseJson(nxJsonChange.content.toString())
: parseJson(this.host.read('nx.json').toString());
const readJsonWithHost = (file) =>
parseJson(this.host.read(file).toString());
const readJsonWithHost = (file) => ({
root: dirname(file),
...parseJson(this.host.read(file).toString()),
});
const staticProjects = buildWorkspaceConfigurationFromGlobs(
nxJsonInTree,
globForProjectFiles(this.host.root).filter(
@ -1214,6 +1217,7 @@ 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));
}

View File

@ -5,7 +5,7 @@ import {
Schema,
} from '../utils/params';
import { Workspaces } from '../config/workspaces';
import { FileChange, flushChanges, FsTree } from '../config/tree';
import { FileChange, flushChanges, FsTree } from '../generators/tree';
import { logger } from '../utils/logger';
import * as chalk from 'chalk';
import { workspaceRoot } from '../utils/app-root';

View File

@ -8,7 +8,7 @@ import {
PackageJsonUpdateForPackage,
} from '../config/misc-interfaces';
import { NxJsonConfiguration } from '../config/nx-json';
import { flushChanges, FsTree } from '../config/tree';
import { flushChanges, FsTree } from '../generators/tree';
import {
extractFileFromTarball,
JsonReadOptions,

View File

@ -25,6 +25,7 @@ import {
CustomHasher,
} from './misc-interfaces';
import { PackageJson } from '../utils/package-json';
import { sortObjectByKeys } from 'nx/src/utils/object-sort';
export function workspaceConfigName(root: string) {
if (existsSync(path.join(root, 'angular.json'))) {
@ -316,7 +317,13 @@ function findFullGeneratorName(
}
export function reformattedWorkspaceJsonOrNull(w: any) {
return w.version === 2 ? toNewFormatOrNull(w) : toOldFormatOrNull(w);
const workspaceJson =
w.version === 2 ? toNewFormatOrNull(w) : toOldFormatOrNull(w);
if (workspaceJson?.projects) {
workspaceJson.projects = sortObjectByKeys(workspaceJson.projects);
}
return workspaceJson;
}
export function toNewFormat(w: any): WorkspaceJsonConfiguration {
@ -411,7 +418,10 @@ function inlineProjectConfigurations(w: any, root: string = workspaceRoot) {
if (typeof config === 'string') {
const configFilePath = path.join(root, config, 'project.json');
const fileConfig = readJsonFile(configFilePath);
w.projects[project] = fileConfig;
w.projects[project] = {
root: config,
...fileConfig,
};
}
}
);
@ -642,6 +652,9 @@ export function buildWorkspaceConfigurationFromGlobs(
// directory as a package.json should overwrite the inferred package.json
// project configuration.
const configuration = readJson(file);
configuration.root = directory;
let name = configuration.name;
if (!configuration.name) {
name = toProjectName(file, nxJson);

View File

@ -0,0 +1,55 @@
import type { Tree } from '../tree';
import * as path from 'path';
import type * as Prettier from 'prettier';
/**
* Formats all the created or updated files using Prettier
* @param tree - the file system tree
*/
export async function formatChangedFilesWithPrettierIfAvailable(
tree: Tree
): Promise<void> {
let prettier: typeof Prettier;
try {
prettier = await import('prettier');
} catch {}
if (!prettier) return;
const files = new Set(
tree.listChanges().filter((file) => file.type !== 'DELETE')
);
await Promise.all(
Array.from(files).map(async (file) => {
const systemPath = path.join(tree.root, file.path);
let options: any = {
filepath: systemPath,
};
const resolvedOptions = await prettier.resolveConfig(systemPath, {
editorconfig: true,
});
if (!resolvedOptions) {
return;
}
options = {
...options,
...resolvedOptions,
};
const support = await prettier.getFileInfo(systemPath, options);
if (support.ignored || !support.inferredParser) {
return;
}
try {
tree.write(
file.path,
prettier.format(file.content.toString('utf-8'), options)
);
} catch (e) {
console.warn(`Could not format ${file.path}. Error: "${e.message}"`);
}
})
);
}

View File

@ -1,5 +1,5 @@
import { FsTree } from 'nx/src/config/tree';
import type { Tree } from 'nx/src/config/tree';
import { FsTree } from 'nx/src/generators/tree';
import type { Tree } from 'nx/src/generators/tree';
/**
* Creates a host for testing.

View File

@ -1,5 +1,5 @@
import { FsTree } from 'nx/src/config/tree';
import type { Tree } from 'nx/src/config/tree';
import { FsTree } from 'nx/src/generators/tree';
import type { Tree } from 'nx/src/generators/tree';
/**
* Creates a host for testing.

View File

@ -1,6 +1,6 @@
import type { Tree } from 'nx/src/config/tree';
import { parseJson, serializeJson } from 'nx/src/utils/json';
import type { JsonParseOptions, JsonSerializeOptions } from 'nx/src/utils/json';
import type { Tree } from '../tree';
import { parseJson, serializeJson } from '../../utils/json';
import type { JsonParseOptions, JsonSerializeOptions } from '../../utils/json';
/**
* Reads a json file, removes all comments and parses JSON.

View File

@ -1,11 +1,12 @@
import { Tree } from 'nx/src/config/tree';
import { ProjectConfiguration } from 'nx/src/config/workspace-json-project-json';
import { Tree } from '../tree';
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
import { createTreeWithEmptyWorkspace } from '../tests/create-tree-with-empty-workspace';
import { createTreeWithEmptyWorkspace } from '../testing-utils/create-tree-with-empty-workspace';
import { readJson, updateJson } from '../utils/json';
import {
addProjectConfiguration,
getProjects,
getWorkspacePath,
readProjectConfiguration,
readWorkspaceConfiguration,
removeProjectConfiguration,
@ -13,7 +14,6 @@ import {
updateWorkspaceConfiguration,
WorkspaceConfiguration,
} from './project-configuration';
import { getWorkspacePath } from '../utils/get-workspace-layout';
type ProjectConfigurationV1 = Pick<
ProjectConfiguration,
@ -301,6 +301,7 @@ describe('project configuration', () => {
addProjectConfiguration(tree, 'test', baseTestProjectConfigV2, true);
const expectedProjectConfig = {
...baseTestProjectConfigV2,
root: undefined,
targets: { build: { executor: '' } },
};
updateProjectConfiguration(tree, 'test', expectedProjectConfig);
@ -471,6 +472,7 @@ describe('project configuration', () => {
addProjectConfiguration(tree, 'test', baseTestProjectConfigV2, true);
const expectedProjectConfig = {
...baseTestProjectConfigV2,
root: undefined,
targets: { build: { executor: '' } },
};
updateProjectConfiguration(tree, 'test', expectedProjectConfig);

View File

@ -4,23 +4,19 @@ import {
globForProjectFiles,
reformattedWorkspaceJsonOrNull,
toNewFormat,
} from 'nx/src/config/workspaces';
} from '../../config/workspaces';
import { basename, dirname, relative } from 'path';
import {
getWorkspaceLayout,
getWorkspacePath,
} from '../utils/get-workspace-layout';
import { readJson, updateJson, writeJson } from '../utils/json';
import { readJson, updateJson, writeJson } from './json';
import type { Tree } from 'nx/src/config/tree';
import type { NxJsonConfiguration } from 'nx/src/config/nx-json';
import { joinPathFragments } from 'nx/src/utils/path';
import type { Tree } from '../tree';
import type { NxJsonConfiguration } from '../../config/nx-json';
import { joinPathFragments } from '../../utils/path';
import {
ProjectConfiguration,
RawWorkspaceJsonConfiguration,
WorkspaceJsonConfiguration,
} from 'nx/src/config/workspace-json-project-json';
} from '../../config/workspace-json-project-json';
export type WorkspaceConfiguration = Omit<
WorkspaceJsonConfiguration,
@ -45,8 +41,7 @@ export function addProjectConfiguration(
projectConfiguration: ProjectConfiguration,
standalone?: boolean
): void {
const workspaceLayout = getWorkspaceLayout(tree);
standalone = standalone ?? workspaceLayout.standaloneAsDefault;
standalone = standalone ?? shouldDefaultToUsingStandaloneConfigs(tree);
setProjectConfiguration(
tree,
projectName,
@ -358,7 +353,7 @@ function addProjectToWorkspaceJson(
workspaceJson.projects[projectName] = project.root;
}
// update the project.json file
writeJson(tree, configFile, project);
writeJson(tree, configFile, { ...project, root: undefined });
}
} else if (mode === 'delete') {
delete workspaceJson.projects[projectName];
@ -399,10 +394,10 @@ function inlineProjectConfigurationsWithTree(
Object.entries(workspaceJson.projects || {}).forEach(([project, config]) => {
if (typeof config === 'string') {
const configFileLocation = joinPathFragments(config, 'project.json');
workspaceJson.projects[project] = readJson<ProjectConfiguration>(
tree,
configFileLocation
);
workspaceJson.projects[project] = {
root: config,
...readJson<ProjectConfiguration>(tree, configFileLocation),
};
}
});
return workspaceJson as WorkspaceJsonConfiguration;
@ -550,3 +545,33 @@ function validateProjectConfigurationOperationsWithoutWorkspaceJson(
);
}
}
export function shouldDefaultToUsingStandaloneConfigs(tree: Tree): boolean {
const workspacePath = getWorkspacePath(tree);
const rawWorkspace =
workspacePath && tree.exists(workspacePath)
? readJson<RawWorkspaceJsonConfiguration>(tree, workspacePath)
: null;
return !rawWorkspace
? true // if workspace.json doesn't exist, all projects **must** be standalone
: Object.values(rawWorkspace.projects).reduce(
// default for second, third... projects should be based on all projects being defined as a path
// for configuration read from ng schematics, this is determined by configFilePath's presence
(allStandalone, next) =>
allStandalone &&
(typeof next === 'string' || 'configFilePath' in next),
// default for first project should be true if using Nx Schema
rawWorkspace.version > 1
);
}
export function getWorkspacePath(
tree: Tree
): '/angular.json' | '/workspace.json' | null {
const possibleFiles: ('/angular.json' | '/workspace.json')[] = [
'/angular.json',
'/workspace.json',
];
return possibleFiles.filter((path) => tree.exists(path))[0];
}

View File

@ -0,0 +1,48 @@
import { Tree } from '../../generators/tree';
import { createTreeWithEmptyWorkspace } from '../../generators/testing-utils/create-tree-with-empty-workspace';
import { addProjectConfiguration } from '../../generators/utils/project-configuration';
import { readJson, updateJson } from '../../generators/utils/json';
import removeRoots from './remove-roots';
describe('remove-roots >', () => {
let tree: Tree;
describe('projects with project.json configs', () => {
beforeEach(() => {
tree = createTreeWithEmptyWorkspace(2);
});
it('should remove the root property', async () => {
addProjectConfiguration(tree, 'proj1', {
root: 'proj1',
});
updateJson(tree, 'proj1/project.json', (config) => ({
...config,
root: 'proj1',
}));
await removeRoots(tree);
expect(readJson(tree, 'proj1/project.json').root).toBeUndefined();
});
});
describe('projects with project.json configs', () => {
beforeEach(() => {
tree = createTreeWithEmptyWorkspace(1);
});
it('should remove the root property', async () => {
addProjectConfiguration(tree, 'proj1', {
root: 'proj1',
});
await removeRoots(tree);
expect(readJson(tree, 'workspace.json').projects.proj1.root).toEqual(
'proj1'
);
});
});
});

View File

@ -0,0 +1,15 @@
import { Tree } from '../../generators/tree';
import {
getProjects,
updateProjectConfiguration,
} from '../../generators/utils/project-configuration';
import { formatChangedFilesWithPrettierIfAvailable } from '../../generators/internal-utils/format-changed-files-with-prettier-if-available';
export default async function (tree: Tree) {
// This looks like it does nothing, but this will actually effectively migrate over all the configs that need to be moved over, but won't touch configs that don't need to be moved
for (const [projName, projConfig] of getProjects(tree)) {
updateProjectConfiguration(tree, projName, projConfig);
}
await formatChangedFilesWithPrettierIfAvailable(tree);
}

View File

@ -4,24 +4,20 @@ exports[`Set the projectBuildConfig option in the Storybook configuration for An
Object {
"projects": Object {
"main-app": Object {
"prefix": "katst",
"projectType": "application",
"root": "apps/main-app",
"sourceRoot": "apps/main-app/src",
"targets": Object {
"architect": Object {
"build": Object {
"executor": "@angular-devkit/build-angular:browser",
"builder": "@angular-devkit/build-angular:browser",
"outputs": Array [
"{options.outputPath}",
],
},
"build-storybook": Object {
"builder": "@nrwl/storybook:build",
"configurations": Object {
"ci": Object {
"quiet": true,
},
},
"executor": "@nrwl/storybook:build",
"options": Object {
"config": Object {
"configFolder": "apps/main-app/.storybook",
@ -35,12 +31,12 @@ Object {
],
},
"storybook": Object {
"builder": "@nrwl/storybook:storybook",
"configurations": Object {
"ci": Object {
"quiet": true,
},
},
"executor": "@nrwl/storybook:storybook",
"options": Object {
"config": Object {
"configFolder": "apps/main-app/.storybook",
@ -51,19 +47,20 @@ Object {
},
},
},
"prefix": "katst",
"projectType": "application",
"root": "apps/main-app",
"sourceRoot": "apps/main-app/src",
},
"ui-one": Object {
"projectType": "library",
"root": "libs/ui/one",
"sourceRoot": "libs/ui/one/src",
"targets": Object {
"architect": Object {
"build-storybook": Object {
"builder": "@nrwl/storybook:build",
"configurations": Object {
"ci": Object {
"quiet": true,
},
},
"executor": "@nrwl/storybook:build",
"options": Object {
"config": Object {
"configFolder": "libs/ui/one/.storybook",
@ -77,7 +74,7 @@ Object {
],
},
"storybook": Object {
"executor": "@nrwl/storybook:storybook",
"builder": "@nrwl/storybook:storybook",
"options": Object {
"config": Object {
"configFolder": "libs/ui/one/.storybook",
@ -88,19 +85,19 @@ Object {
},
},
},
"projectType": "library",
"root": "libs/ui/one",
"sourceRoot": "libs/ui/one/src",
},
"ui-three": Object {
"projectType": "library",
"root": "libs/ui/three",
"sourceRoot": "libs/ui/three/src",
"targets": Object {
"architect": Object {
"build-storybook": Object {
"builder": "@nrwl/storybook:build",
"configurations": Object {
"ci": Object {
"quiet": true,
},
},
"executor": "@nrwl/storybook:build",
"options": Object {
"config": Object {
"configFolder": "libs/ui/three/.storybook",
@ -114,7 +111,7 @@ Object {
],
},
"storybook": Object {
"executor": "@nrwl/storybook:storybook",
"builder": "@nrwl/storybook:storybook",
"options": Object {
"config": Object {
"configFolder": "libs/ui/three/.storybook",
@ -125,19 +122,19 @@ Object {
},
},
},
"projectType": "library",
"root": "libs/ui/three",
"sourceRoot": "libs/ui/three/src",
},
"ui-two": Object {
"projectType": "library",
"root": "libs/ui/two",
"sourceRoot": "libs/ui/two/src",
"targets": Object {
"architect": Object {
"build-storybook": Object {
"builder": "@nrwl/storybook:build",
"configurations": Object {
"ci": Object {
"quiet": true,
},
},
"executor": "@nrwl/storybook:build",
"options": Object {
"config": Object {
"configFolder": "libs/ui/two/.storybook",
@ -151,7 +148,7 @@ Object {
],
},
"storybook": Object {
"executor": "@nrwl/storybook:storybook",
"builder": "@nrwl/storybook:storybook",
"options": Object {
"config": Object {
"configFolder": "libs/ui/two/.storybook",
@ -162,6 +159,9 @@ Object {
},
},
},
"projectType": "library",
"root": "libs/ui/two",
"sourceRoot": "libs/ui/two/src",
},
},
"version": 1,
@ -172,18 +172,14 @@ exports[`Set the projectBuildConfig option in the Storybook configuration for An
Object {
"projects": Object {
"main-app": Object {
"prefix": "katst",
"projectType": "application",
"root": "apps/main-app",
"sourceRoot": "apps/main-app/src",
"targets": Object {
"architect": Object {
"lmfkcn": Object {
"builder": "@nrwl/storybook:storybook",
"configurations": Object {
"ci": Object {
"quiet": true,
},
},
"executor": "@nrwl/storybook:storybook",
"options": Object {
"config": Object {
"configFolder": "apps/main-app/.storybook",
@ -194,18 +190,18 @@ Object {
},
},
"njdfvndfjnv": Object {
"executor": "@angular-devkit/build-angular:browser",
"builder": "@angular-devkit/build-angular:browser",
"outputs": Array [
"{options.outputPath}",
],
},
"odmwjbc": Object {
"builder": "@nrwl/storybook:build",
"configurations": Object {
"ci": Object {
"quiet": true,
},
},
"executor": "@nrwl/storybook:build",
"options": Object {
"config": Object {
"configFolder": "apps/main-app/.storybook",
@ -219,19 +215,20 @@ Object {
],
},
},
"prefix": "katst",
"projectType": "application",
"root": "apps/main-app",
"sourceRoot": "apps/main-app/src",
},
"ui-one": Object {
"projectType": "library",
"root": "libs/ui/one",
"sourceRoot": "libs/ui/one/src",
"targets": Object {
"architect": Object {
"asdgsdfg": Object {
"builder": "@nrwl/storybook:build",
"configurations": Object {
"ci": Object {
"quiet": true,
},
},
"executor": "@nrwl/storybook:build",
"options": Object {
"config": Object {
"configFolder": "libs/ui/one/.storybook",
@ -245,7 +242,7 @@ Object {
],
},
"trthrngb": Object {
"executor": "@nrwl/storybook:storybook",
"builder": "@nrwl/storybook:storybook",
"options": Object {
"config": Object {
"configFolder": "libs/ui/one/.storybook",
@ -256,19 +253,19 @@ Object {
},
},
},
"projectType": "library",
"root": "libs/ui/one",
"sourceRoot": "libs/ui/one/src",
},
"ui-three": Object {
"projectType": "library",
"root": "libs/ui/three",
"sourceRoot": "libs/ui/three/src",
"targets": Object {
"architect": Object {
"aaaa": Object {
"builder": "@nrwl/storybook:build",
"configurations": Object {
"ci": Object {
"quiet": true,
},
},
"executor": "@nrwl/storybook:build",
"options": Object {
"config": Object {
"configFolder": "libs/ui/three/.storybook",
@ -282,7 +279,7 @@ Object {
],
},
"nmkgd": Object {
"executor": "@nrwl/storybook:storybook",
"builder": "@nrwl/storybook:storybook",
"options": Object {
"config": Object {
"configFolder": "libs/ui/three/.storybook",
@ -293,14 +290,14 @@ Object {
},
},
},
"projectType": "library",
"root": "libs/ui/three",
"sourceRoot": "libs/ui/three/src",
},
"ui-two": Object {
"projectType": "library",
"root": "libs/ui/two",
"sourceRoot": "libs/ui/two/src",
"targets": Object {
"architect": Object {
"sdft": Object {
"executor": "@nrwl/storybook:storybook",
"builder": "@nrwl/storybook:storybook",
"options": Object {
"config": Object {
"configFolder": "libs/ui/two/.storybook",
@ -311,12 +308,12 @@ Object {
},
},
"thjkkb": Object {
"builder": "@nrwl/storybook:build",
"configurations": Object {
"ci": Object {
"quiet": true,
},
},
"executor": "@nrwl/storybook:build",
"options": Object {
"config": Object {
"configFolder": "libs/ui/two/.storybook",
@ -330,6 +327,9 @@ Object {
],
},
},
"projectType": "library",
"root": "libs/ui/two",
"sourceRoot": "libs/ui/two/src",
},
},
"version": 1,
@ -340,18 +340,14 @@ exports[`Set the projectBuildConfig option in the Storybook configuration for An
Object {
"projects": Object {
"main-app": Object {
"prefix": "katst",
"projectType": "application",
"root": "apps/main-app",
"sourceRoot": "apps/main-app/src",
"targets": Object {
"architect": Object {
"storybook": Object {
"builder": "@nrwl/storybook:storybook",
"configurations": Object {
"ci": Object {
"quiet": true,
},
},
"executor": "@nrwl/storybook:storybook",
"options": Object {
"config": Object {
"configFolder": "apps/main-app/.storybook",
@ -361,6 +357,10 @@ Object {
},
},
},
"prefix": "katst",
"projectType": "application",
"root": "apps/main-app",
"sourceRoot": "apps/main-app/src",
},
"ui-one": Object {
"projectType": "library",
@ -386,7 +386,7 @@ Object {
],
},
"storybook": Object {
"executor": "@nrwl/storybook:storybook",
"builder": "@nrwl/storybook:storybook",
"options": Object {
"config": Object {
"configFolder": "libs/ui/one/.storybook",
@ -398,6 +398,5 @@ Object {
},
},
},
"version": undefined,
}
`;

View File

@ -1,6 +1,5 @@
import { Tree, writeJson } from '@nrwl/devkit';
import { readJson, Tree, writeJson } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { readWorkspace } from 'packages/devkit/src/generators/project-configuration';
import setProjectBuildConfig from './set-project-build-config';
import * as defaultConfig from './test-configs/default-config.json';
import * as customNames from './test-configs/custom-names-config.json';
@ -17,13 +16,13 @@ describe('Set the projectBuildConfig option in the Storybook configuration for A
it(`should set the projectBuildConfig in the Storybook config according to the type of project`, async () => {
writeJson(tree, 'workspace.json', defaultConfig);
await setProjectBuildConfig(tree);
expect(readWorkspace(tree)).toMatchSnapshot();
expect(readJson(tree, 'workspace.json')).toMatchSnapshot();
});
it(`should still set the projectBuildConfig even if target names are not the default`, async () => {
writeJson(tree, 'workspace.json', customNames);
await setProjectBuildConfig(tree);
expect(readWorkspace(tree)).toMatchSnapshot();
expect(readJson(tree, 'workspace.json')).toMatchSnapshot();
});
});
@ -35,7 +34,7 @@ describe('Set the projectBuildConfig option in the Storybook configuration for A
it(`should not change their Storybook configuration`, async () => {
await setProjectBuildConfig(tree);
expect(readWorkspace(tree)).toMatchSnapshot();
expect(readJson(tree, 'workspace.json')).toMatchSnapshot();
});
});
});

View File

@ -85,6 +85,7 @@ describe('convert-to-nx-project', () => {
getProjectConfigurationPath(config)
);
delete config.root;
expect(config).toEqual(newConfigFile);
});
@ -112,6 +113,7 @@ describe('convert-to-nx-project', () => {
tree,
getProjectConfigurationPath(config)
);
delete config.root;
expect(config).toEqual(newConfigFile);
}
});

View File

@ -68,6 +68,8 @@ To upgrade change the version number at the top of ${getWorkspacePath(
continue;
}
delete configuration.root;
writeJson(host, configPath, configuration);
updateJson(host, getWorkspacePath(host), (value) => {