Skip to content

Commit d098414

Browse files
committed
refactor(@schematics/angular): consolidate addDeclarationToNgModule logic
This commits extracts all `addDeclarationToNgModule` logic into a common util which is re-used across a number of schematics.
1 parent 3b1f109 commit d098414

File tree

5 files changed

+92
-191
lines changed

5 files changed

+92
-191
lines changed

packages/schematics/angular/component/index.ts

Lines changed: 9 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -22,79 +22,13 @@ import {
2222
strings,
2323
url,
2424
} from '@angular-devkit/schematics';
25-
import * as ts from '../third_party/github.com/Microsoft/TypeScript/lib/typescript';
26-
import { addDeclarationToModule, addExportToModule } from '../utility/ast-utils';
27-
import { InsertChange } from '../utility/change';
28-
import { buildRelativePath, findModuleFromOptions } from '../utility/find-module';
25+
import { addDeclarationToNgModule } from '../utility/add-declaration-to-ng-module';
26+
import { findModuleFromOptions } from '../utility/find-module';
2927
import { parseName } from '../utility/parse-name';
3028
import { validateHtmlSelector } from '../utility/validation';
3129
import { buildDefaultPath, getWorkspace } from '../utility/workspace';
3230
import { Schema as ComponentOptions, Style } from './schema';
3331

34-
function readIntoSourceFile(host: Tree, modulePath: string): ts.SourceFile {
35-
const sourceText = host.readText(modulePath);
36-
37-
return ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
38-
}
39-
40-
function addDeclarationToNgModule(options: ComponentOptions): Rule {
41-
return (host: Tree) => {
42-
if (options.skipImport || options.standalone || !options.module) {
43-
return host;
44-
}
45-
46-
options.type = options.type != null ? options.type : 'Component';
47-
48-
const modulePath = options.module;
49-
const source = readIntoSourceFile(host, modulePath);
50-
51-
const componentPath =
52-
`/${options.path}/` +
53-
(options.flat ? '' : strings.dasherize(options.name) + '/') +
54-
strings.dasherize(options.name) +
55-
(options.type ? '.' : '') +
56-
strings.dasherize(options.type);
57-
const relativePath = buildRelativePath(modulePath, componentPath);
58-
const classifiedName = strings.classify(options.name) + strings.classify(options.type);
59-
const declarationChanges = addDeclarationToModule(
60-
source,
61-
modulePath,
62-
classifiedName,
63-
relativePath,
64-
);
65-
66-
const declarationRecorder = host.beginUpdate(modulePath);
67-
for (const change of declarationChanges) {
68-
if (change instanceof InsertChange) {
69-
declarationRecorder.insertLeft(change.pos, change.toAdd);
70-
}
71-
}
72-
host.commitUpdate(declarationRecorder);
73-
74-
if (options.export) {
75-
// Need to refresh the AST because we overwrote the file in the host.
76-
const source = readIntoSourceFile(host, modulePath);
77-
78-
const exportRecorder = host.beginUpdate(modulePath);
79-
const exportChanges = addExportToModule(
80-
source,
81-
modulePath,
82-
strings.classify(options.name) + strings.classify(options.type),
83-
relativePath,
84-
);
85-
86-
for (const change of exportChanges) {
87-
if (change instanceof InsertChange) {
88-
exportRecorder.insertLeft(change.pos, change.toAdd);
89-
}
90-
}
91-
host.commitUpdate(exportRecorder);
92-
}
93-
94-
return host;
95-
};
96-
}
97-
9832
function buildSelector(options: ComponentOptions, projectPrefix: string) {
9933
let selector = strings.dasherize(options.name);
10034
if (options.prefix) {
@@ -152,6 +86,12 @@ export default function (options: ComponentOptions): Rule {
15286
move(parsedPath.path),
15387
]);
15488

155-
return chain([addDeclarationToNgModule(options), mergeWith(templateSource)]);
89+
return chain([
90+
addDeclarationToNgModule({
91+
type: 'component',
92+
...options,
93+
}),
94+
mergeWith(templateSource),
95+
]);
15696
};
15797
}

packages/schematics/angular/directive/index.ts

Lines changed: 10 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -20,71 +20,13 @@ import {
2020
strings,
2121
url,
2222
} from '@angular-devkit/schematics';
23-
import * as ts from '../third_party/github.com/Microsoft/TypeScript/lib/typescript';
24-
import { addDeclarationToModule, addExportToModule } from '../utility/ast-utils';
25-
import { InsertChange } from '../utility/change';
26-
import { buildRelativePath, findModuleFromOptions } from '../utility/find-module';
23+
import { addDeclarationToNgModule } from '../utility/add-declaration-to-ng-module';
24+
import { findModuleFromOptions } from '../utility/find-module';
2725
import { parseName } from '../utility/parse-name';
2826
import { validateHtmlSelector } from '../utility/validation';
2927
import { buildDefaultPath, getWorkspace } from '../utility/workspace';
3028
import { Schema as DirectiveOptions } from './schema';
3129

32-
function addDeclarationToNgModule(options: DirectiveOptions): Rule {
33-
return (host: Tree) => {
34-
if (options.skipImport || options.standalone || !options.module) {
35-
return host;
36-
}
37-
38-
const modulePath = options.module;
39-
const sourceText = host.readText(modulePath);
40-
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
41-
42-
const directivePath =
43-
`/${options.path}/` +
44-
(options.flat ? '' : strings.dasherize(options.name) + '/') +
45-
strings.dasherize(options.name) +
46-
'.directive';
47-
const relativePath = buildRelativePath(modulePath, directivePath);
48-
const classifiedName = strings.classify(`${options.name}Directive`);
49-
const declarationChanges = addDeclarationToModule(
50-
source,
51-
modulePath,
52-
classifiedName,
53-
relativePath,
54-
);
55-
const declarationRecorder = host.beginUpdate(modulePath);
56-
for (const change of declarationChanges) {
57-
if (change instanceof InsertChange) {
58-
declarationRecorder.insertLeft(change.pos, change.toAdd);
59-
}
60-
}
61-
host.commitUpdate(declarationRecorder);
62-
63-
if (options.export) {
64-
// Need to refresh the AST because we overwrote the file in the host.
65-
const sourceText = host.readText(modulePath);
66-
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
67-
68-
const exportRecorder = host.beginUpdate(modulePath);
69-
const exportChanges = addExportToModule(
70-
source,
71-
modulePath,
72-
strings.classify(`${options.name}Directive`),
73-
relativePath,
74-
);
75-
76-
for (const change of exportChanges) {
77-
if (change instanceof InsertChange) {
78-
exportRecorder.insertLeft(change.pos, change.toAdd);
79-
}
80-
}
81-
host.commitUpdate(exportRecorder);
82-
}
83-
84-
return host;
85-
};
86-
}
87-
8830
function buildSelector(options: DirectiveOptions, projectPrefix: string) {
8931
let selector = options.name;
9032
if (options.prefix) {
@@ -127,6 +69,13 @@ export default function (options: DirectiveOptions): Rule {
12769
move(parsedPath.path),
12870
]);
12971

130-
return chain([addDeclarationToNgModule(options), mergeWith(templateSource)]);
72+
return chain([
73+
addDeclarationToNgModule({
74+
type: 'directive',
75+
76+
...options,
77+
}),
78+
mergeWith(templateSource),
79+
]);
13180
};
13281
}

packages/schematics/angular/module/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ function buildRelativeModulePath(options: ModuleOptions, modulePath: string): st
4747
return buildRelativePath(modulePath, importModulePath);
4848
}
4949

50-
function addDeclarationToNgModule(options: ModuleOptions): Rule {
50+
function addImportToNgModule(options: ModuleOptions): Rule {
5151
return (host: Tree) => {
5252
if (!options.module) {
5353
return host;
@@ -179,7 +179,7 @@ export default function (options: ModuleOptions): Rule {
179179
};
180180

181181
return chain([
182-
!isLazyLoadedModuleGen ? addDeclarationToNgModule(options) : noop(),
182+
!isLazyLoadedModuleGen ? addImportToNgModule(options) : noop(),
183183
addRouteDeclarationToNgModule(options, routingModulePath),
184184
mergeWith(templateSource),
185185
isLazyLoadedModuleGen ? schematic('component', componentOptions) : noop(),

packages/schematics/angular/pipe/index.ts

Lines changed: 10 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -19,69 +19,13 @@ import {
1919
strings,
2020
url,
2121
} from '@angular-devkit/schematics';
22-
import * as ts from '../third_party/github.com/Microsoft/TypeScript/lib/typescript';
23-
import { addDeclarationToModule, addExportToModule } from '../utility/ast-utils';
24-
import { InsertChange } from '../utility/change';
25-
import { buildRelativePath, findModuleFromOptions } from '../utility/find-module';
22+
import { addDeclarationToNgModule } from '../utility/add-declaration-to-ng-module';
23+
import { findModuleFromOptions } from '../utility/find-module';
2624
import { parseName } from '../utility/parse-name';
2725
import { validateClassName } from '../utility/validation';
2826
import { createDefaultPath } from '../utility/workspace';
2927
import { Schema as PipeOptions } from './schema';
3028

31-
function addDeclarationToNgModule(options: PipeOptions): Rule {
32-
return (host: Tree) => {
33-
if (options.skipImport || options.standalone || !options.module) {
34-
return host;
35-
}
36-
37-
const modulePath = options.module;
38-
const sourceText = host.readText(modulePath);
39-
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
40-
41-
const pipePath =
42-
`/${options.path}/` +
43-
(options.flat ? '' : strings.dasherize(options.name) + '/') +
44-
strings.dasherize(options.name) +
45-
'.pipe';
46-
const relativePath = buildRelativePath(modulePath, pipePath);
47-
const changes = addDeclarationToModule(
48-
source,
49-
modulePath,
50-
strings.classify(`${options.name}Pipe`),
51-
relativePath,
52-
);
53-
const recorder = host.beginUpdate(modulePath);
54-
for (const change of changes) {
55-
if (change instanceof InsertChange) {
56-
recorder.insertLeft(change.pos, change.toAdd);
57-
}
58-
}
59-
host.commitUpdate(recorder);
60-
61-
if (options.export) {
62-
const sourceText = host.readText(modulePath);
63-
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
64-
65-
const exportRecorder = host.beginUpdate(modulePath);
66-
const exportChanges = addExportToModule(
67-
source,
68-
modulePath,
69-
strings.classify(`${options.name}Pipe`),
70-
relativePath,
71-
);
72-
73-
for (const change of exportChanges) {
74-
if (change instanceof InsertChange) {
75-
exportRecorder.insertLeft(change.pos, change.toAdd);
76-
}
77-
}
78-
host.commitUpdate(exportRecorder);
79-
}
80-
81-
return host;
82-
};
83-
}
84-
8529
export default function (options: PipeOptions): Rule {
8630
return async (host: Tree) => {
8731
options.path ??= await createDefaultPath(host, options.project as string);
@@ -102,6 +46,13 @@ export default function (options: PipeOptions): Rule {
10246
move(parsedPath.path),
10347
]);
10448

105-
return chain([addDeclarationToNgModule(options), mergeWith(templateSource)]);
49+
return chain([
50+
addDeclarationToNgModule({
51+
type: 'pipe',
52+
53+
...options,
54+
}),
55+
mergeWith(templateSource),
56+
]);
10657
};
10758
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { Rule, Tree, strings } from '@angular-devkit/schematics';
10+
import * as ts from '../third_party/github.com/Microsoft/TypeScript/lib/typescript';
11+
import { addDeclarationToModule, addSymbolToNgModuleMetadata } from './ast-utils';
12+
import { InsertChange } from './change';
13+
import { buildRelativePath } from './find-module';
14+
15+
export interface DeclarationToNgModuleOptions {
16+
module?: string;
17+
path?: string;
18+
name: string;
19+
flat?: boolean;
20+
export?: boolean;
21+
type: string;
22+
skipImport?: boolean;
23+
standalone?: boolean;
24+
}
25+
26+
export function addDeclarationToNgModule(options: DeclarationToNgModuleOptions): Rule {
27+
return (host: Tree) => {
28+
const modulePath = options.module;
29+
if (options.skipImport || options.standalone || !modulePath) {
30+
return host;
31+
}
32+
33+
const sourceText = host.readText(modulePath);
34+
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
35+
36+
const filePath =
37+
`/${options.path}/` +
38+
(options.flat ? '' : strings.dasherize(options.name) + '/') +
39+
strings.dasherize(options.name) +
40+
(options.type ? '.' : '') +
41+
strings.dasherize(options.type);
42+
43+
const importPath = buildRelativePath(modulePath, filePath);
44+
const classifiedName = strings.classify(options.name) + strings.classify(options.type);
45+
const changes = addDeclarationToModule(source, modulePath, classifiedName, importPath);
46+
47+
if (options.export) {
48+
changes.push(...addSymbolToNgModuleMetadata(source, modulePath, 'exports', classifiedName));
49+
}
50+
51+
const recorder = host.beginUpdate(modulePath);
52+
for (const change of changes) {
53+
if (change instanceof InsertChange) {
54+
recorder.insertLeft(change.pos, change.toAdd);
55+
}
56+
}
57+
host.commitUpdate(recorder);
58+
59+
return host;
60+
};
61+
}

0 commit comments

Comments
 (0)