Skip to content
This repository was archived by the owner on Nov 22, 2024. It is now read-only.

Commit 9ff02c5

Browse files
alan-agius4CaerusKaru
authored andcommitted
feat: add migrations for version 9 (#1266)
1 parent c5ed2d9 commit 9ff02c5

File tree

24 files changed

+595
-84
lines changed

24 files changed

+595
-84
lines changed

modules/common/schematics/BUILD.bazel

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@ ts_library(
2525
"@npm//@angular-devkit/core",
2626
"@npm//@angular-devkit/schematics",
2727
"@npm//@schematics/angular",
28-
"@npm//@types/jasmine",
2928
"@npm//rxjs",
30-
"@npm//typescript",
3129
],
3230
)
3331

modules/common/schematics/add/index.spec.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,26 @@ import {addUniversalCommonRule, AddUniversalOptions} from './index';
1414

1515
describe('Add Schematic Rule', () => {
1616
const defaultOptions: AddUniversalOptions = {
17-
clientProject: 'bar',
17+
clientProject: 'test-app',
1818
serverFileName: 'server.ts',
1919
};
2020

2121
let schematicRunner: SchematicTestRunner;
2222
let appTree: Tree;
2323

2424
beforeEach(async () => {
25-
appTree = await createTestApp().toPromise();
25+
appTree = await createTestApp();
2626
schematicRunner = new SchematicTestRunner('schematics', collectionPath);
2727
});
2828

2929
it('should update angular.json', async () => {
3030
const tree = await schematicRunner
3131
.callRule(addUniversalCommonRule(defaultOptions), appTree).toPromise();
3232
const contents = JSON.parse(tree.read('angular.json')!.toString());
33-
const architect = contents.projects.bar.architect;
33+
const architect = contents.projects['test-app'].architect;
3434
expect(architect.build.configurations.production).toBeDefined();
35-
expect(architect.build.options.outputPath).toBe('dist/bar/browser');
36-
expect(architect.server.options.outputPath).toBe('dist/bar/server');
35+
expect(architect.build.options.outputPath).toBe('dist/test-app/browser');
36+
expect(architect.server.options.outputPath).toBe('dist/test-app/server');
3737

3838
const productionConfig = architect.server.configurations.production;
3939
expect(productionConfig.fileReplacements).toBeDefined();
@@ -45,7 +45,7 @@ describe('Add Schematic Rule', () => {
4545
const tree = await schematicRunner
4646
.callRule(addUniversalCommonRule(defaultOptions), appTree).toPromise();
4747

48-
const contents = JSON.parse(tree.read('/projects/bar/tsconfig.server.json')!.toString());
48+
const contents = JSON.parse(tree.read('/projects/test-app/tsconfig.server.json')!.toString());
4949
expect(contents.files).toEqual([
5050
'src/main.server.ts',
5151
'server.ts',
@@ -60,7 +60,7 @@ describe('Add Schematic Rule', () => {
6060
const tree = await schematicRunner
6161
.callRule(addUniversalCommonRule(defaultOptions), appTree).toPromise();
6262

63-
const contents = JSON.parse(tree.read('/projects/bar/tsconfig.server.json')!.toString());
63+
const contents = JSON.parse(tree.read('/projects/test-app/tsconfig.server.json')!.toString());
6464
expect(contents.files).toEqual([
6565
'src/main.server.ts',
6666
'server.ts',

modules/common/schematics/add/index.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ import {
1919
appendValueInAstArray,
2020
} from '@schematics/angular/utility/json-utils';
2121
import {Schema as UniversalOptions} from '@schematics/angular/universal/schema';
22-
import {stripTsExtension, getDistPaths, getClientProject} from '../utils';
22+
import {stripTsExtension, getDistPaths, getProject} from '../utils';
2323

2424
export interface AddUniversalOptions extends UniversalOptions {
2525
serverFileName?: string;
2626
}
2727

2828
export function addUniversalCommonRule(options: AddUniversalOptions): Rule {
2929
return async host => {
30-
const clientProject = await getClientProject(host, options.clientProject);
30+
const clientProject = await getProject(host, options.clientProject);
3131

3232
return chain([
3333
clientProject.targets.has('server')
@@ -98,7 +98,7 @@ function updateConfigFileRule(options: AddUniversalOptions): Rule {
9898

9999
function updateServerTsConfigRule(options: AddUniversalOptions): Rule {
100100
return async host => {
101-
const clientProject = await getClientProject(host, options.clientProject);
101+
const clientProject = await getProject(host, options.clientProject);
102102
const serverTarget = clientProject.targets.get('server');
103103
if (!serverTarget || !serverTarget.options) {
104104
return;
@@ -123,7 +123,11 @@ function updateServerTsConfigRule(options: AddUniversalOptions): Rule {
123123

124124
const filesAstNode = findPropertyInAstObject(tsConfigAst, 'files');
125125

126-
if (filesAstNode && filesAstNode.kind === 'array') {
126+
const serverFilePath = stripTsExtension(options.serverFileName) + '.ts';
127+
if (
128+
filesAstNode &&
129+
filesAstNode.kind === 'array' &&
130+
!filesAstNode.elements.some(({ text }) => text === serverFilePath)) {
127131
const recorder = host.beginUpdate(tsConfigPath);
128132

129133
appendValueInAstArray(
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. 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 { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
10+
import { version9UpdateRule } from './index';
11+
import { createTestApp, collectionPath } from '../../testing/test-app';
12+
13+
describe('Migration to version 9', () => {
14+
const schematicRunner = new SchematicTestRunner(
15+
'migrations',
16+
collectionPath,
17+
);
18+
19+
let tree: UnitTestTree;
20+
beforeEach(async () => {
21+
tree = await createTestApp();
22+
tree = await schematicRunner
23+
.runExternalSchematicAsync(
24+
'@schematics/angular',
25+
'universal',
26+
{
27+
clientProject: 'test-app',
28+
},
29+
tree,
30+
)
31+
.toPromise();
32+
33+
// create old stucture
34+
tree.create('/projects/test-app/server.ts', 'server content');
35+
tree.create('/projects/test-app/webpack.server.config.js', 'webpack config content');
36+
37+
const pkg = JSON.parse(tree.readContent('/package.json'));
38+
const scripts = pkg.scripts;
39+
scripts['compile:server'] = 'old compile:server';
40+
scripts['serve:ssr'] = 'old serve:ssr';
41+
scripts['build:client-and-server-bundles'] = 'old build:client-and-server-bundles';
42+
43+
tree.overwrite('/package.json', JSON.stringify(pkg, null, 2));
44+
});
45+
46+
it(`should backup old 'server.ts' and 'webpack.server.config.js'`, async () => {
47+
const newTree = await schematicRunner.callRule(version9UpdateRule(''), tree).toPromise();
48+
expect(newTree.exists('/projects/test-app/server.ts.bak')).toBeTruthy();
49+
expect(newTree.exists('/projects/test-app/webpack.server.config.js.bak')).toBeTruthy();
50+
});
51+
52+
it(`should backup old 'package.json' scripts`, async () => {
53+
const newTree = await schematicRunner.callRule(version9UpdateRule(''), tree).toPromise();
54+
55+
const { scripts } = JSON.parse(newTree.read('/package.json')!.toString());
56+
expect(scripts['build:client-and-server-bundles']).toBeUndefined();
57+
expect(scripts['compile:server']).toBeUndefined();
58+
expect(scripts['serve:ssr']).toBeUndefined();
59+
60+
expect(scripts['build:client-and-server-bundles_bak']).toBeDefined();
61+
expect(scripts['compile:server_bak']).toBeDefined();
62+
expect(scripts['serve:ssr_bak']).toBeDefined();
63+
});
64+
65+
it(`should not backup old 'package.json' scripts when target is missing`, async () => {
66+
const newTree = await schematicRunner.callRule(version9UpdateRule(''), tree).toPromise();
67+
68+
const { scripts } = JSON.parse(newTree.read('/package.json')!.toString());
69+
expect(scripts['build:ssr']).toBeUndefined();
70+
expect(scripts['build:ssr_bak']).toBeUndefined();
71+
});
72+
});
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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 {
10+
Rule,
11+
SchematicsException,
12+
chain,
13+
externalSchematic,
14+
} from '@angular-devkit/schematics';
15+
import {getWorkspace} from '@schematics/angular/utility/workspace';
16+
import {NodePackageInstallTask} from '@angular-devkit/schematics/tasks';
17+
import {Builders} from '@schematics/angular/utility/workspace-models';
18+
import {normalize, join} from '@angular-devkit/core';
19+
import {Schema as UniversalOptions} from '@schematics/angular/universal/schema';
20+
21+
export function version9UpdateRule(collectionPath: string): Rule {
22+
return async host => {
23+
return chain([
24+
backupPackageScriptsRule(),
25+
updateProjectsStructureRule(collectionPath),
26+
(tree, context) => {
27+
const packageChanges = tree.actions.some(a => a.path.endsWith('/package.json'));
28+
if (context && packageChanges) {
29+
context.addTask(new NodePackageInstallTask());
30+
}
31+
},
32+
]);
33+
};
34+
}
35+
36+
function backupPackageScriptsRule(): Rule {
37+
return tree => {
38+
// Remove old scripts in 'package.json'
39+
const pkgPath = '/package.json';
40+
const buffer = tree.read(pkgPath);
41+
if (!buffer) {
42+
throw new SchematicsException('Could not find package.json');
43+
}
44+
45+
const pkg = JSON.parse(buffer.toString());
46+
const scripts = pkg.scripts;
47+
if (!scripts) {
48+
return;
49+
}
50+
51+
// Backup script targets
52+
[
53+
'compile:server',
54+
'build:ssr',
55+
'serve:ssr',
56+
'build:client-and-server-bundles',
57+
].forEach(key => {
58+
const keyBackup = `${key}_bak`;
59+
const scriptValue = scripts[key];
60+
// Check if script target exists and it has not been already backed up
61+
if (scriptValue && !scripts[keyBackup]) {
62+
scripts[keyBackup] = scriptValue;
63+
scripts[key] = undefined;
64+
}
65+
});
66+
67+
tree.overwrite(pkgPath, JSON.stringify(pkg, null, 2));
68+
};
69+
}
70+
71+
function updateProjectsStructureRule(collectionPath: string): Rule {
72+
return async tree => {
73+
const workspace = await getWorkspace(tree);
74+
const installRules: Rule[] = [];
75+
76+
for (const [projectName, projectDefinition] of workspace.projects) {
77+
const serverTarget = projectDefinition.targets.get('server');
78+
if (!serverTarget || serverTarget.builder !== Builders.Server) {
79+
// Only process those targets which have a known builder for the CLI
80+
continue;
81+
}
82+
83+
const root = normalize(projectDefinition.root);
84+
85+
// Backup old files
86+
[
87+
'server.ts',
88+
'webpack.server.config.js',
89+
]
90+
.map(f => join(root, f))
91+
.filter(f => tree.exists(f))
92+
.forEach(f => tree.rename(f, `${f}.bak`));
93+
94+
const installOptions: UniversalOptions = {
95+
clientProject: projectName,
96+
// Skip install, so we only do one for the entire workspace at the end.
97+
skipInstall: true,
98+
};
99+
100+
if (!collectionPath) {
101+
continue;
102+
}
103+
// Run the install schematic again so that we re-create the entire stucture.
104+
installRules.push(externalSchematic(collectionPath, 'ng-add', installOptions));
105+
}
106+
107+
return chain(installRules);
108+
};
109+
}

modules/common/schematics/testing/test-app.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@
77
*/
88

99
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
10-
import {Observable} from 'rxjs';
1110
import {switchMap} from 'rxjs/operators';
1211

1312
/** Path to the collection file for the NgUniversal schematics */
1413
export const collectionPath = require.resolve('@schematics/angular/collection.json');
1514

1615
/** Create a base app used for testing. */
17-
export function createTestApp(appOptions = {}): Observable<UnitTestTree> {
16+
export function createTestApp(appOptions = {}): Promise<UnitTestTree> {
1817
const baseRunner =
1918
new SchematicTestRunner('universal-schematics', collectionPath);
2019

@@ -27,6 +26,7 @@ export function createTestApp(appOptions = {}): Observable<UnitTestTree> {
2726
.pipe(
2827
switchMap(workspaceTree => baseRunner.runExternalSchematicAsync(
2928
'@schematics/angular', 'application',
30-
{...appOptions, name: 'bar'}, workspaceTree)),
31-
);
29+
{...appOptions, name: 'test-app'}, workspaceTree)),
30+
)
31+
.toPromise();
3232
}

modules/common/schematics/utils/utils.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@ import {workspaces, join, normalize} from '@angular-devkit/core';
1111
import {getWorkspace} from '@schematics/angular/utility/workspace';
1212
import {SchematicsException} from '@angular-devkit/schematics';
1313

14-
export async function getClientProject(
14+
export async function getProject(
1515
host: Tree,
1616
projectName: string,
1717
): Promise<workspaces.ProjectDefinition> {
1818
const workspace = await getWorkspace(host);
19-
const clientProject = workspace.projects.get(projectName);
19+
const project = workspace.projects.get(projectName);
2020

21-
if (!clientProject || clientProject.extensions.projectType !== 'application') {
21+
if (!project || project.extensions.projectType !== 'application') {
2222
throw new SchematicsException(`Universal requires a project type of 'application'.`);
2323
}
2424

25-
return clientProject as unknown as workspaces.ProjectDefinition;
25+
return project;
2626
}
2727

2828
export function stripTsExtension(file: string): string {
@@ -34,7 +34,7 @@ export async function getDistPaths(host: Tree, clientProjectName: string): Promi
3434
server: string;
3535
}> {
3636
// Generate new output paths
37-
const clientProject = await getClientProject(host, clientProjectName);
37+
const clientProject = await getProject(host, clientProjectName);
3838
const clientBuildTarget = clientProject.targets.get('build');
3939
if (!clientBuildTarget || !clientBuildTarget.options) {
4040
throw new SchematicsException(`Cannot find 'options' for ${clientProjectName} build target.`);

modules/express-engine/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
},
2020
"schematics": "./schematics/collection.json",
2121
"ng-update": {
22+
"migrations": "./schematics/migrations/migration-collection.json",
2223
"packageGroup": "NG_UPDATE_PACKAGE_GROUP"
2324
},
2425
"repository": {

modules/express-engine/schematics/collection.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
"factory": "./install",
77
"schema": "./install/schema.json",
88
"aliases": ["express-engine-shell"]
9-
},
9+
}
1010
}
1111
}

modules/express-engine/schematics/install/index.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ import {Schema as UniversalOptions} from './schema';
1414

1515
describe('Universal Schematic', () => {
1616
const defaultOptions: UniversalOptions = {
17-
clientProject: 'bar',
17+
clientProject: 'test-app',
1818
};
1919

2020
let schematicRunner: SchematicTestRunner;
2121
let appTree: Tree;
2222

2323
beforeEach(async () => {
24-
appTree = await createTestApp().toPromise();
24+
appTree = await createTestApp();
2525
schematicRunner = new SchematicTestRunner('schematics', collectionPath);
2626
});
2727

@@ -57,7 +57,7 @@ describe('Universal Schematic', () => {
5757
.runSchematicAsync('ng-add', defaultOptions, appTree)
5858
.toPromise();
5959

60-
const contents = JSON.parse(tree.readContent('/projects/bar/tsconfig.server.json'));
60+
const contents = JSON.parse(tree.readContent('/projects/test-app/tsconfig.server.json'));
6161
expect(contents.files).toEqual([
6262
'src/main.server.ts',
6363
'server.ts',
@@ -69,7 +69,7 @@ describe('Universal Schematic', () => {
6969
.runSchematicAsync('ng-add', defaultOptions, appTree)
7070
.toPromise();
7171

72-
const content = tree.readContent('/projects/bar/server.ts');
72+
const content = tree.readContent('/projects/test-app/server.ts');
7373
expect(content).toContain(`export * from './src/main.server'`);
7474
});
7575
});

0 commit comments

Comments
 (0)