Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 557f7c2

Browse files
committed
refactor(@schematics/angular): use a project-based schematic helper to reduce boilerplate
Introduces a new `createProjectSchematic` helper to abstract the common logic of looking up a project within the workspace. This pattern was repeated across many schematics. The following project-scoped schematics have been refactored to use the new helper: - app-shell - component - config - directive - module - pipe - server - service-worker - ssr - web-worker This change simplifies the implementation of these schematics, reduces code duplication, and improves maintainability.
1 parent 290ac55 commit 557f7c2

File tree

11 files changed

+395
-413
lines changed

11 files changed

+395
-413
lines changed

‎packages/schematics/angular/app-shell/index.ts

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
} from '../utility/ast-utils';
1919
import { applyToUpdateRecorder } from '../utility/change';
2020
import { getAppModulePath, isStandaloneApp } from '../utility/ng-ast-utils';
21-
import { targetBuildNotFoundError } from '../utility/project-targets';
21+
import { createProjectSchematic } from '../utility/project';
2222
import { findBootstrapApplicationCall, getMainFilePath } from '../utility/standalone/util';
2323
import { getWorkspace } from '../utility/workspace';
2424
import { Schema as AppShellOptions } from './schema';
@@ -190,27 +190,19 @@ function addServerRoutingConfig(options: AppShellOptions, isStandalone: boolean)
190190
};
191191
}
192192

193-
export default function (options: AppShellOptions): Rule {
194-
return async (tree) => {
195-
const browserEntryPoint = await getMainFilePath(tree, options.project);
196-
const isStandalone = isStandaloneApp(tree, browserEntryPoint);
197-
198-
const workspace = await getWorkspace(tree);
199-
const project = workspace.projects.get(options.project);
200-
if (!project) {
201-
throw targetBuildNotFoundError();
202-
}
203-
204-
return chain([
205-
validateProject(browserEntryPoint),
206-
schematic('server', options),
207-
addServerRoutingConfig(options, isStandalone),
208-
schematic('component', {
209-
name: 'app-shell',
210-
module: 'app.module.server.ts',
211-
project: options.project,
212-
standalone: isStandalone,
213-
}),
214-
]);
215-
};
216-
}
193+
export default createProjectSchematic<AppShellOptions>(async (options, { tree }) => {
194+
const browserEntryPoint = await getMainFilePath(tree, options.project);
195+
const isStandalone = isStandaloneApp(tree, browserEntryPoint);
196+
197+
return chain([
198+
validateProject(browserEntryPoint),
199+
schematic('server', options),
200+
addServerRoutingConfig(options, isStandalone),
201+
schematic('component', {
202+
name: 'app-shell',
203+
module: 'app.module.server.ts',
204+
project: options.project,
205+
standalone: isStandalone,
206+
}),
207+
]);
208+
});

‎packages/schematics/angular/component/index.ts

Lines changed: 46 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ import {
2525
import { addDeclarationToNgModule } from '../utility/add-declaration-to-ng-module';
2626
import { findModuleFromOptions } from '../utility/find-module';
2727
import { parseName } from '../utility/parse-name';
28+
import { createProjectSchematic } from '../utility/project';
2829
import { validateClassName, validateHtmlSelector } from '../utility/validation';
29-
import { buildDefaultPath,getWorkspace } from '../utility/workspace';
30+
import { buildDefaultPath } from '../utility/workspace';
3031
import { Schema as ComponentOptions, Style } from './schema';
3132

3233
function buildSelector(options: ComponentOptions, projectPrefix: string) {
@@ -40,62 +41,52 @@ function buildSelector(options: ComponentOptions, projectPrefix: string) {
4041
return selector;
4142
}
4243

43-
export default function (options: ComponentOptions): Rule {
44-
return async (host: Tree) => {
45-
const workspace = await getWorkspace(host);
46-
const project = workspace.projects.get(options.project);
47-
48-
if (!project) {
49-
throw new SchematicsException(`Project "${options.project}" does not exist.`);
50-
}
51-
52-
if (options.path === undefined) {
53-
options.path = buildDefaultPath(project);
54-
}
44+
export default createProjectSchematic<ComponentOptions>((options, { project, tree }) => {
45+
if (options.path === undefined) {
46+
options.path = buildDefaultPath(project);
47+
}
5548

56-
options.module = findModuleFromOptions(host, options);
57-
// Schematic templates require a defined type value
58-
options.type ??= '';
49+
options.module = findModuleFromOptions(tree, options);
50+
// Schematic templates require a defined type value
51+
options.type ??= '';
5952

60-
const parsedPath = parseName(options.path, options.name);
61-
options.name = parsedPath.name;
62-
options.path = parsedPath.path;
63-
options.selector =
64-
options.selector || buildSelector(options, (project && project.prefix) || '');
53+
const parsedPath = parseName(options.path, options.name);
54+
options.name = parsedPath.name;
55+
options.path = parsedPath.path;
56+
options.selector = options.selector || buildSelector(options, (project && project.prefix) || '');
6557

66-
validateHtmlSelector(options.selector);
67-
validateClassName(strings.classify(options.name));
58+
validateHtmlSelector(options.selector);
59+
validateClassName(strings.classify(options.name));
6860

69-
const skipStyleFile = options.inlineStyle || options.style === Style.None;
70-
const templateSource = apply(url('./files'), [
71-
options.skipTests ? filter((path) => !path.endsWith('.spec.ts.template')) : noop(),
72-
skipStyleFile ? filter((path) => !path.endsWith('.__style__.template')) : noop(),
73-
options.inlineTemplate ? filter((path) => !path.endsWith('.html.template')) : noop(),
74-
applyTemplates({
75-
...strings,
76-
'if-flat': (s: string) => (options.flat ? '' : s),
77-
'ngext': options.ngHtml ? '.ng' : '',
78-
...options,
79-
}),
80-
!options.type
81-
? forEach(((file) => {
82-
return file.path.includes('..')
83-
? {
84-
content: file.content,
85-
path: file.path.replace('..', '.'),
86-
}
87-
: file;
88-
}) as FileOperator)
89-
: noop(),
90-
move(parsedPath.path),
91-
]);
61+
const skipStyleFile = options.inlineStyle || options.style === Style.None;
62+
const templateSource = apply(url('./files'), [
63+
options.skipTests ? filter((path) => !path.endsWith('.spec.ts.template')) : noop(),
64+
skipStyleFile ? filter((path) => !path.endsWith('.__style__.template')) : noop(),
65+
options.inlineTemplate ? filter((path) => !path.endsWith('.html.template')) : noop(),
66+
applyTemplates({
67+
...strings,
68+
'if-flat': (s: string) => (options.flat ? '' : s),
69+
'ngext': options.ngHtml ? '.ng' : '',
70+
...options,
71+
}),
72+
!options.type
73+
? forEach(((file) => {
74+
return file.path.includes('..')
75+
? {
76+
content: file.content,
77+
path: file.path.replace('..', '.'),
78+
}
79+
: file;
80+
}) as FileOperator)
81+
: noop(),
82+
move(parsedPath.path),
83+
]);
9284

93-
return chain([
94-
addDeclarationToNgModule({
95-
type: 'component',
96-
...options,
97-
}),
98-
mergeWith(templateSource),
99-
]);
100-
};
101-
}
85+
return chain([
86+
addDeclarationToNgModule({
87+
type: 'component',
88+
...options,
89+
}),
90+
mergeWith(templateSource),
91+
]);
92+
});

‎packages/schematics/angular/config/index.ts

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,40 +20,33 @@ import {
2020
import { readFile } from 'node:fs/promises';
2121
import { posix as path } from 'node:path';
2222
import { relativePathToWorkspaceRoot } from '../utility/paths';
23-
import { getWorkspace as readWorkspace, updateWorkspace } from '../utility/workspace';
23+
import { createProjectSchematic } from '../utility/project';
24+
import { updateWorkspace } from '../utility/workspace';
2425
import { Builders as AngularBuilder } from '../utility/workspace-models';
2526
import { Schema as ConfigOptions, Type as ConfigType } from './schema';
2627

27-
export default function(options: ConfigOptions): Rule {
28+
export default createProjectSchematic<ConfigOptions>((options,{ project })=> {
2829
switch (options.type) {
2930
case ConfigType.Karma:
3031
return addKarmaConfig(options);
3132
case ConfigType.Browserslist:
32-
return addBrowserslistConfig(options);
33+
return addBrowserslistConfig(project.root);
3334
default:
3435
throw new SchematicsException(`"${options.type}" is an unknown configuration file type.`);
3536
}
36-
}
37-
38-
function addBrowserslistConfig(options: ConfigOptions): Rule {
39-
return async (host) => {
40-
const workspace = await readWorkspace(host);
41-
const project = workspace.projects.get(options.project);
42-
if (!project) {
43-
throw new SchematicsException(`Project name "${options.project}" doesn't not exist.`);
44-
}
37+
});
4538

46-
// Read Angular's default vendored `.browserslistrc` file.
47-
const config = await readFile(path.join(__dirname, '.browserslistrc'), 'utf8');
39+
async function addBrowserslistConfig(projectRoot: string): Promise<Rule> {
40+
// Read Angular's default vendored `.browserslistrc` file.
41+
const config = await readFile(path.join(__dirname, '.browserslistrc'), 'utf8');
4842

49-
return mergeWith(
50-
apply(url('./files'), [
51-
filter((p) => p.endsWith('.browserslistrc.template')),
52-
applyTemplates({ config }),
53-
move(project.root),
54-
]),
55-
);
56-
};
43+
return mergeWith(
44+
apply(url('./files'), [
45+
filter((p) => p.endsWith('.browserslistrc.template')),
46+
applyTemplates({ config }),
47+
move(projectRoot),
48+
]),
49+
);
5750
}
5851

5952
function addKarmaConfig(options: ConfigOptions): Rule {

‎packages/schematics/angular/directive/index.ts

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import { Rule, SchematicsException,Tree,chain, strings } from '@angular-devkit/schematics';
9+
import { Rule, chain, strings } from '@angular-devkit/schematics';
1010
import { addDeclarationToNgModule } from '../utility/add-declaration-to-ng-module';
1111
import { findModuleFromOptions } from '../utility/find-module';
1212
import { generateFromFiles } from '../utility/generate-from-files';
1313
import { parseName } from '../utility/parse-name';
14+
import { createProjectSchematic } from '../utility/project';
1415
import { validateClassName, validateHtmlSelector } from '../utility/validation';
15-
import { buildDefaultPath,getWorkspace } from '../utility/workspace';
16+
import { buildDefaultPath } from '../utility/workspace';
1617
import { Schema as DirectiveOptions } from './schema';
1718

1819
function buildSelector(options: DirectiveOptions, projectPrefix: string) {
@@ -26,34 +27,26 @@ function buildSelector(options: DirectiveOptions, projectPrefix: string) {
2627
return strings.camelize(selector);
2728
}
2829

29-
export default function (options: DirectiveOptions): Rule {
30-
return async (host: Tree) => {
31-
const workspace = await getWorkspace(host);
32-
const project = workspace.projects.get(options.project);
33-
if (!project) {
34-
throw new SchematicsException(`Project "${options.project}" does not exist.`);
35-
}
36-
37-
if (options.path === undefined) {
38-
options.path = buildDefaultPath(project);
39-
}
40-
41-
options.module = findModuleFromOptions(host, options);
42-
const parsedPath = parseName(options.path, options.name);
43-
options.name = parsedPath.name;
44-
options.path = parsedPath.path;
45-
options.selector = options.selector || buildSelector(options, project.prefix || '');
46-
47-
validateHtmlSelector(options.selector);
48-
validateClassName(strings.classify(options.name));
49-
50-
return chain([
51-
addDeclarationToNgModule({
52-
type: 'directive',
53-
54-
...options,
55-
}),
56-
generateFromFiles(options),
57-
]);
58-
};
59-
}
30+
export default createProjectSchematic<DirectiveOptions>((options, { project, tree }) => {
31+
if (options.path === undefined) {
32+
options.path = buildDefaultPath(project);
33+
}
34+
35+
options.module = findModuleFromOptions(tree, options);
36+
const parsedPath = parseName(options.path, options.name);
37+
options.name = parsedPath.name;
38+
options.path = parsedPath.path;
39+
options.selector = options.selector || buildSelector(options, project.prefix || '');
40+
41+
validateHtmlSelector(options.selector);
42+
validateClassName(strings.classify(options.name));
43+
44+
return chain([
45+
addDeclarationToNgModule({
46+
type: 'directive',
47+
48+
...options,
49+
}),
50+
generateFromFiles(options),
51+
]);
52+
});

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /