From 0c0a29da71436aed8d3b1771b64dd6cb1cc4c623 Mon Sep 17 00:00:00 2001 From: Victor Savkin Date: Mon, 21 Dec 2020 10:20:23 -0500 Subject: [PATCH] feat(core): better compat layer for angular devkit --- .../devkit/src/utils/get-workspace-layout.ts | 2 +- packages/tao/src/commands/ngcli-adapter.ts | 315 ++++++++++-------- .../workspace/src/utils/cli-config-utils.ts | 2 +- 3 files changed, 184 insertions(+), 135 deletions(-) diff --git a/packages/devkit/src/utils/get-workspace-layout.ts b/packages/devkit/src/utils/get-workspace-layout.ts index b8bcf647be..f77e72ada8 100644 --- a/packages/devkit/src/utils/get-workspace-layout.ts +++ b/packages/devkit/src/utils/get-workspace-layout.ts @@ -23,6 +23,6 @@ export function getWorkspaceLayout( } export function getWorkspacePath(host: Tree) { - const possibleFiles = ['/workspace.json', '/angular.json']; + const possibleFiles = ['/angular.json', '/workspace.json']; return possibleFiles.filter((path) => host.exists(path))[0]; } diff --git a/packages/tao/src/commands/ngcli-adapter.ts b/packages/tao/src/commands/ngcli-adapter.ts index a646bcc51b..44b6983085 100644 --- a/packages/tao/src/commands/ngcli-adapter.ts +++ b/packages/tao/src/commands/ngcli-adapter.ts @@ -42,7 +42,7 @@ import { BuiltinTaskExecutor } from '@angular-devkit/schematics/tasks/node'; import { dirname, extname, join, resolve } from 'path'; import * as stripJsonComments from 'strip-json-comments'; import { FileBuffer } from '@angular-devkit/core/src/virtual-fs/host/interface'; -import { Observable } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { concatMap, map, switchMap } from 'rxjs/operators'; import { NX_ERROR, NX_PREFIX } from '../shared/logger'; @@ -98,7 +98,43 @@ function getCollection(workflow: any, name: string) { return collection; } +function convertEventTypeToHandleMultipleConfigNames( + host: any, + eventPath: string, + content: Buffer | never +) { + const actualConfigName = host.exists('/workspace.json') + ? 'workspace.json' + : 'angular.json'; + const isWorkspaceConfig = + eventPath === 'angular.json' || eventPath === 'workspace.json'; + if (isWorkspaceConfig) { + let isNewFormat = true; + try { + isNewFormat = + JSON.parse(host.read(actualConfigName).toString()).version === 2; + } catch (e) {} + + if (content && isNewFormat) { + const formatted = toNewFormatOrNull(JSON.parse(content.toString())); + if (formatted) { + return { + eventPath: actualConfigName, + content: Buffer.from(JSON.stringify(formatted, null, 2)), + }; + } else { + return { eventPath: actualConfigName, content: content }; + } + } else { + return { eventPath: actualConfigName, content: content }; + } + } else { + return { eventPath, content }; + } +} + function createRecorder( + host: any, record: { loggingQueue: string[]; error: boolean; @@ -109,10 +145,17 @@ function createRecorder( const eventPath = event.path.startsWith('/') ? event.path.substr(1) : event.path; + + const r = convertEventTypeToHandleMultipleConfigNames( + host, + eventPath, + (event as any).content + ); + if (event.kind === 'error') { record.error = true; logger.warn( - `ERROR! ${eventPath} ${ + `ERROR! ${r.eventPath} ${ event.description == 'alreadyExist' ? 'already exists' : 'does not exist.' @@ -120,27 +163,24 @@ function createRecorder( ); } else if (event.kind === 'update') { record.loggingQueue.push( - tags.oneLine`${chalk.white('UPDATE')} ${eventPath} (${ - event.content.length - } bytes)` + tags.oneLine`${chalk.white('UPDATE')} ${r.eventPath}` ); } else if (event.kind === 'create') { record.loggingQueue.push( - tags.oneLine`${chalk.green('CREATE')} ${eventPath} (${ - event.content.length - } bytes)` + tags.oneLine`${chalk.green('CREATE')} ${r.eventPath}` ); } else if (event.kind === 'delete') { - record.loggingQueue.push(`${chalk.yellow('DELETE')} ${eventPath}`); + record.loggingQueue.push(`${chalk.yellow('DELETE')} ${r.eventPath}`); } else if (event.kind === 'rename') { record.loggingQueue.push( - `${chalk.blue('RENAME')} ${eventPath} => ${event.to}` + `${chalk.blue('RENAME')} ${r.eventPath} => ${event.to}` ); } }; } async function runSchematic( + host: any, root: string, workflow: NodeWorkflow, logger: logging.Logger, @@ -153,7 +193,7 @@ async function runSchematic( recorder: any = null ): Promise<{ status: number; loggingQueue: string[] }> { const record = { loggingQueue: [] as string[], error: false }; - workflow.reporter.subscribe(recorder || createRecorder(record, logger)); + workflow.reporter.subscribe(recorder || createRecorder(host, record, logger)); try { await workflow @@ -262,11 +302,11 @@ export class NxScopedHost extends virtualFs.ScopedHost { } read(path: Path): Observable { - if (this.isWorkspaceConfig(path)) { - return this.isNewFormat().pipe( - switchMap((newFormat) => { - if (newFormat) { - return super.read(path).pipe( + return this.context(path).pipe( + switchMap((r) => { + if (r.isWorkspaceConfig) { + if (r.isNewFormat) { + return super.read(r.actualConfigFileName).pipe( map((r) => { try { const w = JSON.parse(Buffer.from(r).toString()); @@ -280,39 +320,136 @@ export class NxScopedHost extends virtualFs.ScopedHost { }) ); } else { - return super.read(path); + return super.read(r.actualConfigFileName); } - }) - ); - } else { - return super.read(path); - } + } else { + return super.read(path); + } + }) + ); } write(path: Path, content: FileBuffer): Observable { - if (this.isWorkspaceConfig(path)) { - return this.isNewFormat().pipe( - switchMap((newFormat) => { - if (newFormat) { + return this.context(path).pipe( + switchMap((r) => { + if (r.isWorkspaceConfig) { + if (r.isNewFormat) { try { const w = JSON.parse(Buffer.from(content).toString()); const formatted = toNewFormatOrNull(w); if (formatted) { return super.write( - path, + r.actualConfigFileName, Buffer.from(JSON.stringify(formatted, null, 2)) ); } else { - return super.write(path, content); + return super.write(r.actualConfigFileName, content); } } catch (e) { - return super.write(path, content); + return super.write(r.actualConfigFileName, content); } } else { - return super.write(path, content); + return super.write(r.actualConfigFileName, content); } + } else { + return super.write(path, content); + } + }) + ); + } + + isFile(path: Path): Observable { + return this.context(path).pipe( + switchMap((r) => { + if (r.isWorkspaceConfig) { + return super.isFile(r.actualConfigFileName); + } else { + return super.isFile(path); + } + }) + ); + } + + exists(path: Path): Observable { + return this.context(path).pipe( + switchMap((r) => { + if (r.isWorkspaceConfig) { + return super.exists(r.actualConfigFileName); + } else { + return super.exists(path); + } + }) + ); + } + + private context( + path: string + ): Observable<{ + isWorkspaceConfig: boolean; + actualConfigFileName: any; + isNewFormat: boolean; + }> { + const p = path.toString(); + if ( + p === 'angular.json' || + p === '/angular.json' || + p === 'workspace.json' || + p === '/workspace.json' + ) { + return super.exists('/angular.json' as any).pipe( + switchMap((isAngularJson) => { + const actualConfigFileName = isAngularJson + ? '/angular.json' + : '/workspace.json'; + return super.read(actualConfigFileName as any).pipe( + map((r) => { + try { + const w = JSON.parse(Buffer.from(r).toString()); + return { + isWorkspaceConfig: true, + actualConfigFileName, + isNewFormat: w.version === 2, + }; + } catch (e) { + return { + isWorkspaceConfig: true, + actualConfigFileName, + isNewFormat: false, + }; + } + }) + ); }) ); + } else { + return of({ + isWorkspaceConfig: false, + actualConfigFileName: null, + isNewFormat: false, + }); + } + } +} + +/** + * This host contains the workaround needed to run Angular migrations + */ +export class NxScopedHostForMigrations extends NxScopedHost { + constructor(root: Path) { + super(root); + } + + read(path: Path): Observable { + if (this.isWorkspaceConfig(path)) { + return super.read(path).pipe(map(processConfigWhenReading)); + } else { + return super.read(path); + } + } + + write(path: Path, content: FileBuffer) { + if (this.isWorkspaceConfig(path)) { + return super.write(path, processConfigWhenWriting(content)); } else { return super.write(path, content); } @@ -327,104 +464,6 @@ export class NxScopedHost extends virtualFs.ScopedHost { p === '/workspace.json' ); } - - private isNewFormat() { - return super.exists('/angular.json' as any).pipe( - switchMap((isAngularJson) => { - return super - .read((isAngularJson ? '/angular.json' : '/workspace.json') as any) - .pipe( - map((r) => JSON.parse(Buffer.from(r).toString()).version === 2) - ); - }) - ); - } -} - -/** - * This host contains the workaround needed to run Angular migrations - */ -export class NxScopedHostForMigrations extends NxScopedHost { - constructor(root: Path) { - super(root); - } - - read(path: Path): Observable { - return this.hasWorkspaceJson().pipe( - concatMap((hasWorkspace) => { - if (this.isWorkspaceConfig(path)) { - if ( - hasWorkspace && - (path == '/angular.json' || path == 'angular.json') - ) { - return super - .read('/workspace.json' as any) - .pipe(map(processConfigWhenReading)); - } else { - return super.read(path).pipe(map(processConfigWhenReading)); - } - } else { - return super.read(path); - } - }) - ); - } - - isFile(path: Path): Observable { - return this.hasWorkspaceJson().pipe( - concatMap((hasWorkspace) => { - if ( - hasWorkspace && - (path == '/angular.json' || path == 'angular.json') - ) { - return super.isFile('/workspace.json' as any); - } else { - return super.isFile(path); - } - }) - ); - } - - exists(path: Path): Observable { - return this.hasWorkspaceJson().pipe( - concatMap((hasWorkspace) => { - if ( - hasWorkspace && - (path == '/angular.json' || path == 'angular.json') - ) { - return super.exists('/workspace.json' as any); - } else { - return super.exists(path); - } - }) - ); - } - - write(path: Path, content: FileBuffer): Observable { - return this.hasWorkspaceJson().pipe( - concatMap((hasWorkspace) => { - if (this.isWorkspaceConfig(path)) { - if ( - hasWorkspace && - (path == '/angular.json' || path == 'angular.json') - ) { - return super.write( - '/workspace.json' as any, - processConfigWhenWriting(content) - ); - } else { - return super.write(path as any, processConfigWhenWriting(content)); - } - } else { - return super.write(path as any, content); - } - }) - ); - } - - private hasWorkspaceJson() { - return super.exists('/workspace.json' as any); - } } function processConfigWhenReading(content: ArrayBuffer) { @@ -483,6 +522,7 @@ export async function generate( const schematic = collection.createSchematic(opts.generatorName, true); return ( await runSchematic( + fsHost, root, workflow, logger as any, @@ -527,18 +567,25 @@ export function wrapAngularDevkitSchematic( emptyLogger.createChild = () => emptyLogger; const recorder = (event: DryRunEvent) => { - const eventPath = event.path.startsWith('/') + let eventPath = event.path.startsWith('/') ? event.path.substr(1) : event.path; + + const r = convertEventTypeToHandleMultipleConfigNames( + host, + eventPath, + (event as any).content + ); + if (event.kind === 'error') { } else if (event.kind === 'update') { - host.write(eventPath, event.content); + host.write(r.eventPath, r.content); } else if (event.kind === 'create') { - host.write(eventPath, event.content); + host.write(r.eventPath, r.content); } else if (event.kind === 'delete') { - host.delete(eventPath); + host.delete(r.eventPath); } else if (event.kind === 'rename') { - host.rename(eventPath, event.to); + host.rename(r.eventPath, event.to); } }; @@ -569,6 +616,7 @@ export function wrapAngularDevkitSchematic( const collection = getCollection(workflow, collectionName); const schematic = collection.createSchematic(generatorName, true); const res = await runSchematic( + fsHost, host.root, workflow, emptyLogger, @@ -596,6 +644,7 @@ export async function invokeNew( const schematic = collection.createSchematic('new', true); return ( await runSchematic( + fsHost, root, workflow, logger as any, diff --git a/packages/workspace/src/utils/cli-config-utils.ts b/packages/workspace/src/utils/cli-config-utils.ts index c6b4546254..ed92cbf198 100644 --- a/packages/workspace/src/utils/cli-config-utils.ts +++ b/packages/workspace/src/utils/cli-config-utils.ts @@ -3,7 +3,7 @@ import { readJsonInTree } from './ast-utils'; import { NxJson } from '@nrwl/workspace/src/core/shared-interfaces'; export function getWorkspacePath(host: Tree) { - const possibleFiles = ['/workspace.json', '/angular.json']; + const possibleFiles = ['/angular.json', '/workspace.json']; return possibleFiles.filter((path) => host.exists(path))[0]; }