mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
feat(core): add migration to remove entryComponents (#44308)
Adds an automated migration that will drop any usages of `entryComponents` from `@NgModule` and `@Component`. PR Close #44308
This commit is contained in:
parent
cd502b76b3
commit
5dff077d50
11 changed files with 595 additions and 0 deletions
|
|
@ -13,6 +13,7 @@ pkg_npm(
|
|||
],
|
||||
visibility = ["//packages/core:__pkg__"],
|
||||
deps = [
|
||||
"//packages/core/schematics/migrations/entry-components",
|
||||
"//packages/core/schematics/migrations/router-link-empty-expression",
|
||||
"//packages/core/schematics/migrations/testbed-teardown",
|
||||
],
|
||||
|
|
|
|||
|
|
@ -9,6 +9,16 @@
|
|||
"version": "13.0.0-beta",
|
||||
"description": "In Angular version 13, the `teardown` flag in `TestBed` will be enabled by default. This migration automatically opts out existing apps from the new teardown behavior.",
|
||||
"factory": "./migrations/testbed-teardown/index"
|
||||
},
|
||||
"migration-v13.1-entry-components": {
|
||||
"version": "13.1.0-beta",
|
||||
"description": "As of Angular version 13, `entryComponents` are no longer necessary.",
|
||||
"factory": "./migrations/entry-components/index"
|
||||
},
|
||||
"migration-v14-entry-components": {
|
||||
"version": "14.0.0-beta",
|
||||
"description": "As of Angular version 13, `entryComponents` are no longer necessary.",
|
||||
"factory": "./migrations/entry-components/index"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "entry-components",
|
||||
srcs = glob(["**/*.ts"]),
|
||||
tsconfig = "//packages/core/schematics:tsconfig.json",
|
||||
visibility = [
|
||||
"//packages/core/schematics:__pkg__",
|
||||
"//packages/core/schematics/migrations/google3:__pkg__",
|
||||
"//packages/core/schematics/test:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//packages/core/schematics/utils",
|
||||
"@npm//@angular-devkit/schematics",
|
||||
"@npm//@types/node",
|
||||
"@npm//typescript",
|
||||
],
|
||||
)
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
## entryComponents migration
|
||||
As of Angular version 13, the `entryComponents` option in `@NgModule` and `@Component` isn't
|
||||
necessary anymore. This migration will automatically remove any usages.
|
||||
|
||||
#### Before
|
||||
```ts
|
||||
import { NgModule, Component } from '@angular/core';
|
||||
|
||||
@Component({selector: 'my-comp', template: ''})
|
||||
export class MyComp {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [MyComp],
|
||||
entryComponents: [MyComp],
|
||||
exports: [MyComp]
|
||||
})
|
||||
export class MyModule {}
|
||||
```
|
||||
|
||||
#### After
|
||||
```ts
|
||||
import { NgModule, Component } from '@angular/core';
|
||||
|
||||
@Component({selector: 'my-comp', template: ''})
|
||||
export class MyComp {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [MyComp],
|
||||
exports: [MyComp]
|
||||
})
|
||||
export class MyModule {}
|
||||
```
|
||||
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* @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 {Rule, SchematicsException, Tree} from '@angular-devkit/schematics';
|
||||
import {relative} from 'path';
|
||||
import ts from 'typescript';
|
||||
|
||||
import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths';
|
||||
import {canMigrateFile, createMigrationProgram} from '../../utils/typescript/compiler_host';
|
||||
|
||||
import {migrateEntryComponentsUsages} from './util';
|
||||
|
||||
|
||||
/** Migration that removes `entryComponents` usages. */
|
||||
export default function(): Rule {
|
||||
return async (tree: Tree) => {
|
||||
const {buildPaths, testPaths} = await getProjectTsConfigPaths(tree);
|
||||
const basePath = process.cwd();
|
||||
const allPaths = [...buildPaths, ...testPaths];
|
||||
|
||||
if (!allPaths.length) {
|
||||
throw new SchematicsException(
|
||||
'Could not find any tsconfig file. Cannot remove `entryComponents`.');
|
||||
}
|
||||
|
||||
for (const tsconfigPath of allPaths) {
|
||||
runEntryComponentsMigration(tree, tsconfigPath, basePath);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function runEntryComponentsMigration(tree: Tree, tsconfigPath: string, basePath: string) {
|
||||
const {program} = createMigrationProgram(tree, tsconfigPath, basePath);
|
||||
const typeChecker = program.getTypeChecker();
|
||||
const printer = ts.createPrinter();
|
||||
|
||||
program.getSourceFiles()
|
||||
.filter(sourceFile => canMigrateFile(basePath, sourceFile, program))
|
||||
.forEach(sourceFile => {
|
||||
const usages = migrateEntryComponentsUsages(typeChecker, printer, sourceFile);
|
||||
|
||||
if (usages.length > 0) {
|
||||
const update = tree.beginUpdate(relative(basePath, sourceFile.fileName));
|
||||
usages.forEach(usage => {
|
||||
update.remove(usage.start, usage.length);
|
||||
update.insertRight(usage.start, usage.replacement);
|
||||
});
|
||||
tree.commitUpdate(update);
|
||||
}
|
||||
});
|
||||
}
|
||||
51
packages/core/schematics/migrations/entry-components/util.ts
Normal file
51
packages/core/schematics/migrations/entry-components/util.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* @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 ts from 'typescript';
|
||||
|
||||
import {getCallDecoratorImport} from '../../utils/typescript/decorators';
|
||||
|
||||
/** Finds and migrates all Angular decorators that pass in `entryComponents`. */
|
||||
export function migrateEntryComponentsUsages(
|
||||
typeChecker: ts.TypeChecker, printer: ts.Printer, sourceFile: ts.SourceFile) {
|
||||
const results: {start: number, length: number, end: number, replacement: string}[] = [];
|
||||
|
||||
sourceFile.forEachChild(function walk(node: ts.Node) {
|
||||
if (ts.isDecorator(node) && ts.isCallExpression(node.expression) &&
|
||||
node.expression.arguments.length === 1 &&
|
||||
ts.isObjectLiteralExpression(node.expression.arguments[0])) {
|
||||
const analysis = getCallDecoratorImport(typeChecker, node);
|
||||
|
||||
if (analysis && analysis.importModule === '@angular/core' &&
|
||||
(analysis.name === 'Component' || analysis.name === 'NgModule')) {
|
||||
const literal = node.expression.arguments[0];
|
||||
const entryComponentsProp = literal.properties.find(
|
||||
property => ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) &&
|
||||
property.name.text === 'entryComponents');
|
||||
|
||||
if (entryComponentsProp) {
|
||||
const replacementNode = ts.updateObjectLiteral(
|
||||
literal, literal.properties.filter(prop => prop !== entryComponentsProp));
|
||||
|
||||
results.push({
|
||||
start: literal.getStart(),
|
||||
length: literal.getWidth(),
|
||||
end: literal.getEnd(),
|
||||
replacement: printer.printNode(ts.EmitHint.Unspecified, replacementNode, sourceFile)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.forEachChild(walk);
|
||||
});
|
||||
|
||||
// Sort the operations in reverse order in order to avoid
|
||||
// issues when migrating multiple usages within the same file.
|
||||
return results.sort((a, b) => b.start - a.start);
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ ts_library(
|
|||
tsconfig = "//packages/core/schematics:tsconfig.json",
|
||||
visibility = ["//packages/core/schematics/test/google3:__pkg__"],
|
||||
deps = [
|
||||
"//packages/core/schematics/migrations/entry-components",
|
||||
"//packages/core/schematics/migrations/testbed-teardown",
|
||||
"//packages/core/schematics/utils",
|
||||
"//packages/core/schematics/utils/tslint",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* @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 {Replacement, RuleFailure, Rules} from 'tslint';
|
||||
import ts from 'typescript';
|
||||
|
||||
import {migrateEntryComponentsUsages} from '../../migrations/entry-components/util';
|
||||
|
||||
|
||||
/** TSLint rule that removes usages of `entryComponents`. */
|
||||
export class Rule extends Rules.TypedRule {
|
||||
override applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] {
|
||||
const typeChecker = program.getTypeChecker();
|
||||
const printer = ts.createPrinter();
|
||||
|
||||
return migrateEntryComponentsUsages(typeChecker, printer, sourceFile).map(usage => {
|
||||
return new RuleFailure(
|
||||
sourceFile, usage.start, usage.end,
|
||||
'entryComponents are deprecated and don\'t need to be passed in.', this.ruleName,
|
||||
new Replacement(usage.start, usage.length, usage.replacement));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ ts_library(
|
|||
"//packages/core/schematics:migrations.json",
|
||||
],
|
||||
deps = [
|
||||
"//packages/core/schematics/migrations/entry-components",
|
||||
"//packages/core/schematics/migrations/router-link-empty-expression",
|
||||
"//packages/core/schematics/migrations/testbed-teardown",
|
||||
"//packages/core/schematics/utils",
|
||||
|
|
|
|||
182
packages/core/schematics/test/entry_components_spec.ts
Normal file
182
packages/core/schematics/test/entry_components_spec.ts
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
/**
|
||||
* @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 {getSystemPath, normalize, virtualFs} from '@angular-devkit/core';
|
||||
import {TempScopedNodeJsSyncHost} from '@angular-devkit/core/node/testing';
|
||||
import {HostTree} from '@angular-devkit/schematics';
|
||||
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
|
||||
import * as shx from 'shelljs';
|
||||
|
||||
|
||||
describe('entryComponents migration', () => {
|
||||
let runner: SchematicTestRunner;
|
||||
let host: TempScopedNodeJsSyncHost;
|
||||
let tree: UnitTestTree;
|
||||
let tmpDirPath: string;
|
||||
let previousWorkingDir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
runner = new SchematicTestRunner('test', require.resolve('../migrations.json'));
|
||||
host = new TempScopedNodeJsSyncHost();
|
||||
tree = new UnitTestTree(new HostTree(host));
|
||||
|
||||
writeFile('/tsconfig.json', JSON.stringify({
|
||||
compilerOptions: {
|
||||
lib: ['es2015'],
|
||||
strictNullChecks: true,
|
||||
},
|
||||
}));
|
||||
writeFile('/angular.json', JSON.stringify({
|
||||
version: 1,
|
||||
projects: {t: {architect: {build: {options: {tsConfig: './tsconfig.json'}}}}}
|
||||
}));
|
||||
|
||||
previousWorkingDir = shx.pwd();
|
||||
tmpDirPath = getSystemPath(host.root);
|
||||
|
||||
// Switch into the temporary directory path. This allows us to run
|
||||
// the schematic against our custom unit test tree.
|
||||
shx.cd(tmpDirPath);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
shx.cd(previousWorkingDir);
|
||||
shx.rm('-r', tmpDirPath);
|
||||
});
|
||||
|
||||
it('should remove `entryComponents` usages from NgModule', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { NgModule, Component } from '@angular/core';
|
||||
|
||||
@Component({selector: 'my-comp', template: ''})
|
||||
export class MyComp {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [MyComp],
|
||||
entryComponents: [MyComp],
|
||||
exports: [MyComp]
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
await runMigration();
|
||||
|
||||
expect(stripWhitespace(tree.readContent('/index.ts'))).toContain(stripWhitespace(`
|
||||
@NgModule({
|
||||
declarations: [MyComp],
|
||||
exports: [MyComp]
|
||||
})
|
||||
`));
|
||||
});
|
||||
|
||||
it('should remove `entryComponents` usages from Component', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({selector: 'comp-a', template: ''})
|
||||
export class CompA {}
|
||||
|
||||
@Component({
|
||||
selector: 'comp-b',
|
||||
entryComponents: [CompA],
|
||||
template: ''
|
||||
})
|
||||
export class CompB {}
|
||||
`);
|
||||
|
||||
await runMigration();
|
||||
|
||||
expect(stripWhitespace(tree.readContent('/index.ts'))).toContain(stripWhitespace(`
|
||||
@Component({
|
||||
selector: 'comp-b',
|
||||
template: ''
|
||||
})
|
||||
`));
|
||||
});
|
||||
|
||||
it('should remove multiple `entryComponents` usages from a single file', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { NgModule, Component } from '@angular/core';
|
||||
|
||||
@Component({selector: 'comp-a', template: ''})
|
||||
export class CompA {}
|
||||
|
||||
@Component({
|
||||
selector: 'comp-b',
|
||||
entryComponents: [CompA],
|
||||
template: ''
|
||||
})
|
||||
export class CompB {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [CompA, CompB],
|
||||
entryComponents: [CompB],
|
||||
exports: [CompA, CompB]
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
await runMigration();
|
||||
|
||||
const content = stripWhitespace(tree.readContent('/index.ts'));
|
||||
|
||||
expect(content).toContain(stripWhitespace(`
|
||||
@Component({
|
||||
selector: 'comp-b',
|
||||
template: ''
|
||||
})
|
||||
`));
|
||||
|
||||
expect(content).toContain(stripWhitespace(`
|
||||
@NgModule({
|
||||
declarations: [CompA, CompB],
|
||||
exports: [CompA, CompB]
|
||||
})
|
||||
`));
|
||||
});
|
||||
|
||||
it('should not remove `entryComponents` usages from decorators that do not come from Angular',
|
||||
async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Component } from '@angular/core';
|
||||
import { NgModule } from '@not-angular/core';
|
||||
|
||||
@Component({selector: 'my-comp', template: ''})
|
||||
export class MyComp {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [MyComp],
|
||||
entryComponents: [MyComp],
|
||||
exports: [MyComp]
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
await runMigration();
|
||||
|
||||
expect(stripWhitespace(tree.readContent('/index.ts'))).toContain(stripWhitespace(`
|
||||
@NgModule({
|
||||
declarations: [MyComp],
|
||||
entryComponents: [MyComp],
|
||||
exports: [MyComp]
|
||||
})
|
||||
`));
|
||||
});
|
||||
|
||||
function writeFile(filePath: string, contents: string) {
|
||||
host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents));
|
||||
}
|
||||
|
||||
function runMigration() {
|
||||
return runner.runSchematicAsync('migration-v13.1-entry-components', {}, tree).toPromise();
|
||||
}
|
||||
|
||||
function stripWhitespace(contents: string) {
|
||||
return contents.replace(/\s/g, '');
|
||||
}
|
||||
});
|
||||
214
packages/core/schematics/test/google3/entry_components_spec.ts
Normal file
214
packages/core/schematics/test/google3/entry_components_spec.ts
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
/**
|
||||
* @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 {readFileSync, writeFileSync} from 'fs';
|
||||
import {dirname, join} from 'path';
|
||||
import * as shx from 'shelljs';
|
||||
import {Configuration, Linter} from 'tslint';
|
||||
|
||||
describe('Google3 entryComponents TSLint rule', () => {
|
||||
const rulesDirectory = dirname(require.resolve('../../migrations/google3/entryComponentsRule'));
|
||||
let tmpDir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test');
|
||||
shx.mkdir('-p', tmpDir);
|
||||
|
||||
writeFile('tsconfig.json', JSON.stringify({
|
||||
compilerOptions: {module: 'es2015', baseUrl: './'},
|
||||
}));
|
||||
});
|
||||
|
||||
afterEach(() => shx.rm('-r', tmpDir));
|
||||
|
||||
function runTSLint(fix: boolean) {
|
||||
const program = Linter.createProgram(join(tmpDir, 'tsconfig.json'));
|
||||
const linter = new Linter({fix, rulesDirectory: [rulesDirectory]}, program);
|
||||
const config = Configuration.parseConfigFile({rules: {'entryComponents': true}});
|
||||
|
||||
program.getRootFileNames().forEach(fileName => {
|
||||
linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config);
|
||||
});
|
||||
|
||||
return linter;
|
||||
}
|
||||
|
||||
function writeFile(fileName: string, content: string) {
|
||||
writeFileSync(join(tmpDir, fileName), content);
|
||||
}
|
||||
|
||||
function getFile(fileName: string) {
|
||||
return readFileSync(join(tmpDir, fileName), 'utf8');
|
||||
}
|
||||
|
||||
|
||||
function stripWhitespace(contents: string) {
|
||||
return contents.replace(/\s/g, '');
|
||||
}
|
||||
|
||||
it('should flag entryComponents in NgModule', () => {
|
||||
writeFile('/index.ts', `
|
||||
import { NgModule, Component } from '@angular/core';
|
||||
|
||||
@Component({selector: 'my-comp', template: ''})
|
||||
export class MyComp {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [MyComp],
|
||||
entryComponents: [MyComp],
|
||||
exports: [MyComp]
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
const linter = runTSLint(false);
|
||||
const failures = linter.getResult().failures.map(failure => failure.getFailure());
|
||||
expect(failures.length).toBe(1);
|
||||
expect(failures[0]).toMatch(/entryComponents are deprecated and don't need to be passed in/);
|
||||
});
|
||||
|
||||
it('should flag entryComponents in Component', () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({selector: 'comp-a', template: ''})
|
||||
export class CompA {}
|
||||
|
||||
@Component({
|
||||
selector: 'comp-b',
|
||||
entryComponents: [CompA],
|
||||
template: ''
|
||||
})
|
||||
export class CompB {}
|
||||
`);
|
||||
|
||||
const linter = runTSLint(false);
|
||||
const failures = linter.getResult().failures.map(failure => failure.getFailure());
|
||||
expect(failures.length).toBe(1);
|
||||
expect(failures[0]).toMatch(/entryComponents are deprecated and don't need to be passed in/);
|
||||
});
|
||||
|
||||
it('should remove `entryComponents` usages from NgModule', () => {
|
||||
writeFile('/index.ts', `
|
||||
import { NgModule, Component } from '@angular/core';
|
||||
|
||||
@Component({selector: 'my-comp', template: ''})
|
||||
export class MyComp {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [MyComp],
|
||||
entryComponents: [MyComp],
|
||||
exports: [MyComp]
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
runTSLint(true);
|
||||
|
||||
expect(stripWhitespace(getFile('/index.ts'))).toContain(stripWhitespace(`
|
||||
@NgModule({
|
||||
declarations: [MyComp],
|
||||
exports: [MyComp]
|
||||
})
|
||||
`));
|
||||
});
|
||||
|
||||
it('should remove `entryComponents` usages from Component', () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({selector: 'comp-a', template: ''})
|
||||
export class CompA {}
|
||||
|
||||
@Component({
|
||||
selector: 'comp-b',
|
||||
entryComponents: [CompA],
|
||||
template: ''
|
||||
})
|
||||
export class CompB {}
|
||||
`);
|
||||
|
||||
runTSLint(true);
|
||||
|
||||
expect(stripWhitespace(getFile('/index.ts'))).toContain(stripWhitespace(`
|
||||
@Component({
|
||||
selector: 'comp-b',
|
||||
template: ''
|
||||
})
|
||||
`));
|
||||
});
|
||||
|
||||
it('should remove multiple `entryComponents` usages from a single file', () => {
|
||||
writeFile('/index.ts', `
|
||||
import { NgModule, Component } from '@angular/core';
|
||||
|
||||
@Component({selector: 'comp-a', template: ''})
|
||||
export class CompA {}
|
||||
|
||||
@Component({
|
||||
selector: 'comp-b',
|
||||
entryComponents: [CompA],
|
||||
template: ''
|
||||
})
|
||||
export class CompB {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [CompA, CompB],
|
||||
entryComponents: [CompB],
|
||||
exports: [CompA, CompB]
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
runTSLint(true);
|
||||
|
||||
const content = stripWhitespace(getFile('/index.ts'));
|
||||
|
||||
expect(content).toContain(stripWhitespace(`
|
||||
@Component({
|
||||
selector: 'comp-b',
|
||||
template: ''
|
||||
})
|
||||
`));
|
||||
|
||||
expect(content).toContain(stripWhitespace(`
|
||||
@NgModule({
|
||||
declarations: [CompA, CompB],
|
||||
exports: [CompA, CompB]
|
||||
})
|
||||
`));
|
||||
});
|
||||
|
||||
it('should not remove `entryComponents` usages from decorators that do not come from Angular',
|
||||
() => {
|
||||
writeFile('/index.ts', `
|
||||
import { Component } from '@angular/core';
|
||||
import { NgModule } from '@not-angular/core';
|
||||
|
||||
@Component({selector: 'my-comp', template: ''})
|
||||
export class MyComp {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [MyComp],
|
||||
entryComponents: [MyComp],
|
||||
exports: [MyComp]
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
|
||||
runTSLint(true);
|
||||
|
||||
expect(stripWhitespace(getFile('/index.ts'))).toContain(stripWhitespace(`
|
||||
@NgModule({
|
||||
declarations: [MyComp],
|
||||
entryComponents: [MyComp],
|
||||
exports: [MyComp]
|
||||
})
|
||||
`));
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue