Skip to content

Commit

Permalink
cleanup(angular): convert ng cli migrations to nx devkit (#15590)
Browse files Browse the repository at this point in the history
  • Loading branch information
Coly010 authored Mar 10, 2023
1 parent e55882f commit 5b8a663
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import type {
Target,
ValidationResult,
} from '../../utilities';
import { FileChangeRecorder } from '../../utilities';
import { FileChangeRecorder } from '../../../../utils/file-change-recorder';
import { ProjectMigrator } from './project.migrator';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';

Expand Down
1 change: 0 additions & 1 deletion packages/angular/src/generators/ng-add/utilities/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export * from './dependencies';
export * from './file-change-recorder';
export * from './format-files-task';
export * from './logger';
export * from './normalize-options';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import type { Tree } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing-pre16';

import removePlatformServerExports from './remove-platform-server-exports';

describe('remove-platform-server-exports', () => {
let tree: Tree;
const testTypeScriptFilePath = 'test.ts';

beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
});

describe(`Migration to remove '@angular/platform-server' exports`, () => {
it(`should delete '@angular/platform-server' export when 'renderModule' is the only exported symbol`, async () => {
tree.write(
testTypeScriptFilePath,
`
import { Path, join } from '@angular-devkit/core';
export { renderModule } from '@angular/platform-server';
`
);

await removePlatformServerExports(tree);

const content = tree.read(testTypeScriptFilePath, 'utf-8');
expect(content).not.toContain('@angular/platform-server');
expect(content).toContain(
`import { Path, join } from '@angular-devkit/core';`
);
});

it(`should delete only 'renderModule' when there are additional exports`, async () => {
tree.write(
testTypeScriptFilePath,
`
import { Path, join } from '@angular-devkit/core';
export { renderModule, ServerModule } from '@angular/platform-server';
`
);

await removePlatformServerExports(tree);

const content = tree.read(testTypeScriptFilePath, 'utf-8');

expect(content).toContain(
`import { Path, join } from '@angular-devkit/core';`
);
expect(content).toContain(
`export { ServerModule } from '@angular/platform-server';`
);
});

it(`should not delete 'renderModule' when it's exported from another module`, async () => {
tree.write(
testTypeScriptFilePath,
`
export { renderModule } from '@angular/core';
`
);

await removePlatformServerExports(tree);

const content = tree.read(testTypeScriptFilePath, 'utf-8');
expect(content).toContain(
`export { renderModule } from '@angular/core';`
);
});

it(`should not delete 'renderModule' when it's imported from '@angular/platform-server'`, async () => {
tree.write(
testTypeScriptFilePath,
`
import { renderModule } from '@angular/platform-server';
`
);

await removePlatformServerExports(tree);

const content = tree.read(testTypeScriptFilePath, 'utf-8');
expect(content).toContain(
`import { renderModule } from '@angular/platform-server'`
);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,60 +1,27 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {
chain,
DirEntry,
Rule,
UpdateRecorder,
} from '@angular-devkit/schematics';
import type { Tree } from '@nrwl/devkit';
import { formatFiles, visitNotIgnoredFiles } from '@nrwl/devkit';
import * as ts from 'typescript';
import { formatFiles } from '@nrwl/workspace';
import { FileChangeRecorder } from '../../utils/file-change-recorder';

function* visit(directory: DirEntry): IterableIterator<ts.SourceFile> {
for (const path of directory.subfiles) {
export default async function (tree: Tree) {
visitNotIgnoredFiles(tree, '/', (path) => {
if (path.endsWith('.ts') && !path.endsWith('.d.ts')) {
const entry = directory.file(path);
if (entry) {
const content = entry.content;
if (
content.includes('@angular/platform-server') &&
content.includes('renderModule')
) {
const source = ts.createSourceFile(
entry.path,
content.toString().replace(/^\uFEFF/, ''),
ts.ScriptTarget.Latest,
true
);

yield source;
}
}
}
}
const content = tree.read(path, 'utf8');
if (
content.includes('@angular/platform-server') &&
content.includes('renderModule')
) {
const source = ts.createSourceFile(
path,
content.toString().replace(/^\uFEFF/, ''),
ts.ScriptTarget.Latest,
true
);

for (const path of directory.subdirs) {
if (path === 'node_modules' || path.startsWith('.')) {
continue;
}

yield* visit(directory.dir(path));
}
}

export default function (): Rule {
return chain([
(tree) => {
for (const sourceFile of visit(tree.root)) {
let recorder: UpdateRecorder | undefined;
let recorder: FileChangeRecorder | undefined;
let printer: ts.Printer | undefined;

ts.forEachChild(sourceFile, function analyze(node) {
ts.forEachChild(source, function analyze(node) {
if (
!(
ts.isExportDeclaration(node) &&
Expand Down Expand Up @@ -82,7 +49,7 @@ export default function (): Rule {
return;
}

recorder ??= tree.beginUpdate(sourceFile.fileName);
recorder ??= new FileChangeRecorder(tree, path);

if (newElements.length) {
// Update named exports as there are leftovers.
Expand All @@ -94,25 +61,27 @@ export default function (): Rule {
const fix = printer.printNode(
ts.EmitHint.Unspecified,
newExportClause,
sourceFile
source
);

const index = exportClause.getStart();
const length = exportClause.getWidth();
recorder.remove(index, length).insertLeft(index, fix);
recorder.remove(index, index + length);
recorder.insertLeft(index, fix);
} else {
// Delete export as no exports remain.
recorder.remove(node.getStart(), node.getWidth());
recorder.remove(node.getStart(), node.getStart() + node.getWidth());
}

ts.forEachChild(node, analyze);
});

if (recorder) {
tree.commitUpdate(recorder);
recorder.applyChanges();
}
}
},
formatFiles(),
]);
}
});

await formatFiles(tree);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import type { Tree } from '@nrwl/devkit';
import { addProjectConfiguration, stripIndents } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing-pre16';
import { Builders } from '@schematics/angular/utility/workspace-models';
import updateKarmaMainFile from './update-karma-main-file';

describe(`Migration to karma builder main file (test.ts)`, () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
addProjectConfiguration(tree, 'app', {
root: '',
sourceRoot: 'src',
projectType: 'application',
targets: {
test: {
executor: Builders.Karma,
options: {
main: 'test.ts',
karmaConfig: './karma.config.js',
tsConfig: 'test-spec.json',
},
configurations: {
production: {
main: 'test-multiple-context.ts',
},
},
},
},
});

tree.write(
'test.ts',
stripIndents`
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(path: string, deep?: boolean, filter?: RegExp): {
<T>(id: string): T;
keys(): string[];
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
`
);

tree.write(
'test-multiple-context.ts',
stripIndents`
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(path: string, deep?: boolean, filter?: RegExp): {
<T>(id: string): T;
keys(): string[];
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
);
// Then we find all the tests.
const context1 = require.context('./', true, /\.spec\.ts$/);
const context2 = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context2.keys().forEach(context2);
context1.keys().map(context1);
`
);
});

it(`should remove 'declare const require' and 'require.context' usages`, async () => {
await updateKarmaMainFile(tree);

expect(tree.read('test.ts', 'utf-8')).toBe(stripIndents`
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
);
`);
});

it(`should remove multiple 'require.context' usages`, async () => {
await updateKarmaMainFile(tree);

expect(tree.read('test-multiple-context.ts', 'utf-8')).toBe(stripIndents`
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
);
`);
});
});
Loading

0 comments on commit 5b8a663

Please sign in to comment.