fix(core): misc. ng cli compat layer fixes (#8141)

* fix(core): misc. ng cli compat layer fixes

* chore(core): unit test fixes
This commit is contained in:
Craigory Coppola 2021-12-13 17:48:11 -06:00 committed by GitHub
parent 1f44bbeed9
commit a23284e9ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 100 additions and 49 deletions

View File

@ -1,12 +1,8 @@
import { Tree } from '@angular-devkit/schematics'; import { Tree } from '@angular-devkit/schematics';
import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import { import { readWorkspace } from '@nrwl/workspace';
updateJsonInTree,
readJsonInTree, import * as taoWorkspace from '@nrwl/tao/src/shared/workspace';
updateWorkspaceInTree,
readWorkspace,
getWorkspacePath,
} from '@nrwl/workspace';
import * as path from 'path'; import * as path from 'path';
@ -14,6 +10,12 @@ describe('Update 8-5-0', () => {
let tree: Tree; let tree: Tree;
let schematicRunner: SchematicTestRunner; let schematicRunner: SchematicTestRunner;
beforeAll(() => {
jest
.spyOn(taoWorkspace, 'workspaceConfigName')
.mockReturnValue('workspace.json');
});
beforeEach(async () => { beforeEach(async () => {
tree = Tree.empty(); tree = Tree.empty();
schematicRunner = new SchematicTestRunner( schematicRunner = new SchematicTestRunner(

View File

@ -238,6 +238,11 @@ export class NxScopedHost extends virtualFs.ScopedHost<any> {
nxJson, nxJson,
staticProjects.filter((x) => basename(x) !== 'package.json') staticProjects.filter((x) => basename(x) !== 'package.json')
); );
Object.entries(this.__nxInMemoryWorkspace.projects).forEach(
([project, config]) => {
this.__nxInMemoryWorkspace.projects[project] = config.root as any;
}
);
return of(this.__nxInMemoryWorkspace); return of(this.__nxInMemoryWorkspace);
} }
} }
@ -359,7 +364,8 @@ export class NxScopedHost extends virtualFs.ScopedHost<any> {
return of({ return of({
isWorkspaceConfig: true, isWorkspaceConfig: true,
actualConfigFileName: null, actualConfigFileName: null,
isNewFormat: false, // AngularJson / WorkspaceJson v2 is always used for standalone project config
isNewFormat: true,
}); });
} }
const actualConfigFileName = isAngularJson const actualConfigFileName = isAngularJson
@ -463,7 +469,7 @@ export class NxScopedHost extends virtualFs.ScopedHost<any> {
config config
) { ) {
// copy to avoid removing inlined config files. // copy to avoid removing inlined config files.
let writeObservable: Observable<void>; const writeObservables: Observable<void>[] = [];
const configToWrite = { const configToWrite = {
...config, ...config,
projects: { ...config.projects }, projects: { ...config.projects },
@ -471,7 +477,7 @@ export class NxScopedHost extends virtualFs.ScopedHost<any> {
const projects: [string, any][] = Object.entries(configToWrite.projects); const projects: [string, any][] = Object.entries(configToWrite.projects);
for (const [project, projectConfig] of projects) { for (const [project, projectConfig] of projects) {
if (projectConfig.configFilePath) { if (projectConfig.configFilePath) {
if (!isNewFormat) { if (workspaceFileName && !isNewFormat) {
throw new Error( throw new Error(
'Attempted to write standalone project configuration into a v1 workspace' 'Attempted to write standalone project configuration into a v1 workspace'
); );
@ -484,9 +490,7 @@ export class NxScopedHost extends virtualFs.ScopedHost<any> {
configPath, configPath,
Buffer.from(serializeJson(fileConfigObject)) Buffer.from(serializeJson(fileConfigObject))
); // write back to the project.json file ); // write back to the project.json file
writeObservable = writeObservable writeObservables.push(projectJsonWrite);
? merge(writeObservable, projectJsonWrite)
: projectJsonWrite;
configToWrite.projects[project] = normalize(dirname(configPath)); // update the config object to point to the written file. configToWrite.projects[project] = normalize(dirname(configPath)); // update the config object to point to the written file.
} }
} }
@ -496,11 +500,9 @@ export class NxScopedHost extends virtualFs.ScopedHost<any> {
workspaceFileName, workspaceFileName,
Buffer.from(serializeJson(configToWrite)) Buffer.from(serializeJson(configToWrite))
); );
writeObservable = writeObservable writeObservables.push(workspaceJsonWrite);
? merge(writeObservable, workspaceJsonWrite)
: workspaceJsonWrite;
} }
return writeObservable; return merge(...writeObservables);
} }
protected resolveInlineProjectConfigurations( protected resolveInlineProjectConfigurations(

View File

@ -11,6 +11,7 @@ const Module = require('module');
const originalRequire = Module.prototype.require; const originalRequire = Module.prototype.require;
let patched = false; let patched = false;
let loggedWriteWorkspaceWarning = false;
if (!patched) { if (!patched) {
Module.prototype.require = function () { Module.prototype.require = function () {
@ -21,14 +22,34 @@ if (!patched) {
`@angular-devkit/core/src/workspace/core`, `@angular-devkit/core/src/workspace/core`,
]); ]);
core._test_addWorkspaceFile('workspace.json', core.WorkspaceFormat.JSON); core._test_addWorkspaceFile('workspace.json', core.WorkspaceFormat.JSON);
const originalReadWorkspace = core.readWorkspace;
core.readWorkspace = (path, ...rest) => {
const configFile = workspaceConfigName(appRootPath);
if (!configFile) {
path = 'workspace.json';
}
return originalReadWorkspace.apply(this, [path, ...rest]);
};
const originalWriteWorkspace = core.writeWorkspace; const originalWriteWorkspace = core.writeWorkspace;
core.writeWorkspace = (...args) => { core.writeWorkspace = (...args) => {
const configFile = workspaceConfigName(appRootPath); const configFile = workspaceConfigName(appRootPath);
logger.warn( if (!loggedWriteWorkspaceWarning) {
`[NX] An Angular builder called \`writeWorkspace\`, this may have had unintended consequences in ${configFile}` if (configFile) {
); logger.warn(
logger.warn(`[NX] Double check ${configFile} before proceeding`); `[NX] Angular devkit called \`writeWorkspace\`, this may have had unintended consequences in ${configFile}`
originalWriteWorkspace.apply(this, args); );
logger.warn(`[NX] Double check ${configFile} before proceeding`);
} else {
logger.warn(
`[NX] Angular devkit called \`writeWorkspace\`, this may have created 'workspace.json' or 'angular.json`
);
logger.warn(
`[NX] Double check workspace configuration before proceeding`
);
}
loggedWriteWorkspaceWarning = true;
}
return originalWriteWorkspace.apply(this, args);
}; };
// Patch readJsonWorkspace to inline project configurations // Patch readJsonWorkspace to inline project configurations
@ -37,20 +58,32 @@ if (!patched) {
`@angular-devkit/core/src/workspace/json/reader`, `@angular-devkit/core/src/workspace/json/reader`,
]); ]);
const originalReadJsonWorkspace = readJsonUtils.readJsonWorkspace; const originalReadJsonWorkspace = readJsonUtils.readJsonWorkspace;
readJsonUtils.readJsonWorkspace = () => { readJsonUtils.readJsonWorkspace = (
// Read our v1 workspace schema path,
const w = resolveOldFormatWithInlineProjects( host: { readFile: (p) => Promise<string> }
new Workspaces(appRootPath).readWorkspaceConfiguration() ) => {
); try {
// readJsonWorkspace actually has AST parsing + more, so we return originalReadJsonWorkspace(path, host);
// still need to call it rather than just return our file } catch {
return originalReadJsonWorkspace.apply(this, [ logger.debug(
'workspace.json', // path name, doesn't matter '[NX] Angular devkit readJsonWorkspace fell back to Nx workspaces logic'
{ );
// second arg is a host, only method used is readFile const w = new Workspaces(appRootPath);
readFile: () => JSON.stringify(w),
}, // Read our v1 workspace schema
]); const workspaceConfiguration = resolveOldFormatWithInlineProjects(
w.readWorkspaceConfiguration()
);
// readJsonWorkspace actually has AST parsing + more, so we
// still need to call it rather than just return our file
return originalReadJsonWorkspace.apply(this, [
'workspace.json', // path name, doesn't matter
{
// second arg is a host, only method used is readFile
readFile: () => JSON.stringify(workspaceConfiguration),
},
]);
}
}; };
} }
return result; return result;

View File

@ -157,8 +157,10 @@ export interface TargetConfiguration {
export function workspaceConfigName(root: string) { export function workspaceConfigName(root: string) {
if (existsSync(path.join(root, 'angular.json'))) { if (existsSync(path.join(root, 'angular.json'))) {
return 'angular.json'; return 'angular.json';
} else { } else if (existsSync(path.join(root, 'workspace.json'))) {
return 'workspace.json'; return 'workspace.json';
} else {
return null;
} }
} }
@ -290,14 +292,18 @@ export class Workspaces {
const nxJson = existsSync(nxJsonPath) const nxJson = existsSync(nxJsonPath)
? readJsonFile<NxJsonConfiguration>(nxJsonPath) ? readJsonFile<NxJsonConfiguration>(nxJsonPath)
: ({} as NxJsonConfiguration); : ({} as NxJsonConfiguration);
const workspacePath = path.join(this.root, workspaceConfigName(this.root)); const workspaceFile = workspaceConfigName(this.root);
const workspace = existsSync(workspacePath) const workspacePath = workspaceFile
? this.readFromWorkspaceJson() ? path.join(this.root, workspaceFile)
: buildWorkspaceConfigurationFromGlobs( : null;
nxJson, const workspace =
globForProjectFiles(this.root), workspacePath && existsSync(workspacePath)
(path) => readJsonFile(join(this.root, path)) ? this.readFromWorkspaceJson()
); : buildWorkspaceConfigurationFromGlobs(
nxJson,
globForProjectFiles(this.root),
(path) => readJsonFile(join(this.root, path))
);
assertValidWorkspaceConfiguration(nxJson); assertValidWorkspaceConfiguration(nxJson);
return { ...workspace, ...nxJson }; return { ...workspace, ...nxJson };
@ -701,6 +707,7 @@ export function deduplicateProjectFiles(files: string[], ig?: Ignore) {
) { ) {
return; return;
} }
filtered.set(projectFolder, projectFile); filtered.set(projectFolder, projectFile);
}); });
return Array.from(filtered.entries()).map(([folder, file]) => return Array.from(filtered.entries()).map(([folder, file]) =>

View File

@ -2,7 +2,6 @@ import { Tree } from '@angular-devkit/schematics';
import { createEmptyWorkspace } from '../../utils/testing-utils'; import { createEmptyWorkspace } from '../../utils/testing-utils';
import { callRule, runMigration } from '../../utils/testing'; import { callRule, runMigration } from '../../utils/testing';
import { readJsonInTree, updateJsonInTree } from '../../utils/ast-utils'; import { readJsonInTree, updateJsonInTree } from '../../utils/ast-utils';
import type { NxJsonConfiguration } from '@nrwl/devkit';
describe('Update 8.12.0', () => { describe('Update 8.12.0', () => {
let tree: Tree; let tree: Tree;

View File

@ -9,11 +9,10 @@ import { stripIndents } from '@angular-devkit/core/src/utils/literals';
import { updateJsonInTree } from '../../utils/ast-utils'; import { updateJsonInTree } from '../../utils/ast-utils';
import type { WorkspaceJsonConfiguration } from '@nrwl/devkit'; import type { WorkspaceJsonConfiguration } from '@nrwl/devkit';
import { formatFiles } from '@nrwl/workspace/src/utils/rules/format-files'; import { formatFiles } from '@nrwl/workspace/src/utils/rules/format-files';
import { workspaceConfigName } from '@nrwl/tao/src/shared/workspace';
const addE2eImplicitDependencies: Rule = (tree: Tree) => const addE2eImplicitDependencies: Rule = (tree: Tree) =>
updateJsonInTree<WorkspaceJsonConfiguration>( updateJsonInTree<WorkspaceJsonConfiguration>(
workspaceConfigName(tree.root.path), 'workspace.json', // ngcli-adapter should handle conversion of workspace file names
(json) => { (json) => {
Object.keys(json.projects).forEach((proj) => { Object.keys(json.projects).forEach((proj) => {
const implicitE2eName = proj.replace(/-e2e$/, ''); const implicitE2eName = proj.replace(/-e2e$/, '');

View File

@ -3,12 +3,21 @@ import { Tree } from '@angular-devkit/schematics';
import * as prettier from 'prettier'; import * as prettier from 'prettier';
import * as path from 'path'; import * as path from 'path';
import * as taoWorkspace from '@nrwl/tao/src/shared/workspace';
import { formatFiles } from './format-files'; import { formatFiles } from './format-files';
import { appRootPath } from '@nrwl/tao/src/utils/app-root'; import { appRootPath } from '@nrwl/tao/src/utils/app-root';
describe('formatFiles', () => { describe('formatFiles', () => {
let tree: Tree; let tree: Tree;
let schematicRunner: SchematicTestRunner; let schematicRunner: SchematicTestRunner;
beforeAll(() => {
jest
.spyOn(taoWorkspace, 'workspaceConfigName')
.mockReturnValue('workspace.json');
});
beforeEach(() => { beforeEach(() => {
schematicRunner = new SchematicTestRunner( schematicRunner = new SchematicTestRunner(
'@nrwl/workspace', '@nrwl/workspace',
@ -106,7 +115,7 @@ describe('formatFiles', () => {
}); });
it('should have a readable error when workspace file cannot be formatted', async () => { it('should have a readable error when workspace file cannot be formatted', async () => {
tree.delete('workspace.json'); tree.overwrite('workspace.json', '{ invalidJson: true');
const errorSpy = jest.spyOn(console, 'error'); const errorSpy = jest.spyOn(console, 'error');