Skip to content

Commit 578b196

Browse files
alan-agius4mgechev
authored andcommitted
feat(@schematics/angular): add migration for ngsw-config.json (#15443)
1 parent 26dd512 commit 578b196

File tree

3 files changed

+221
-0
lines changed

3 files changed

+221
-0
lines changed

packages/schematics/angular/migrations/update-9/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import { Rule, chain } from '@angular-devkit/schematics';
1010
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
1111
import { UpdateLibraries } from './ivy-libraries';
12+
import { updateNGSWConfig } from './ngsw-config';
1213
import { updateDependencies } from './update-dependencies';
1314
import { UpdateWorkspaceConfig } from './update-workspace-config';
1415

@@ -17,6 +18,7 @@ export default function(): Rule {
1718
return chain([
1819
UpdateWorkspaceConfig(),
1920
UpdateLibraries(),
21+
updateNGSWConfig(),
2022
updateDependencies(),
2123
(tree, context) => {
2224
const packageChanges = tree.actions.some(a => a.path.endsWith('/package.json'));
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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+
import { JsonParseMode, parseJsonAst } from '@angular-devkit/core';
9+
import { Rule, SchematicsException, Tree } from '@angular-devkit/schematics';
10+
import { appendValueInAstArray, findPropertyInAstObject } from '../../utility/json-utils';
11+
import { Builders } from '../../utility/workspace-models';
12+
import { getAllOptions, getTargets, getWorkspace } from './utils';
13+
14+
15+
/**
16+
* Update ngsw-config.json to fix issue https://github.com/angular/angular-cli/pull/15277
17+
*/
18+
export function updateNGSWConfig(): Rule {
19+
return (tree: Tree) => {
20+
const workspace = getWorkspace(tree);
21+
22+
for (const { target } of getTargets(workspace, 'build', Builders.Browser)) {
23+
for (const options of getAllOptions(target)) {
24+
const ngswConfigPath = findPropertyInAstObject(options, 'ngswConfigPath');
25+
if (!ngswConfigPath || ngswConfigPath.kind !== 'string') {
26+
continue;
27+
}
28+
29+
const path = ngswConfigPath.value;
30+
const configBuffer = tree.read(path);
31+
if (!configBuffer) {
32+
throw new SchematicsException(`Could not find (${path})`);
33+
}
34+
35+
const content = configBuffer.toString();
36+
const ngswConfigAst = parseJsonAst(content, JsonParseMode.Loose);
37+
if (!ngswConfigAst || ngswConfigAst.kind !== 'object') {
38+
continue;
39+
}
40+
41+
const assetGroups = findPropertyInAstObject(ngswConfigAst, 'assetGroups');
42+
if (!assetGroups || assetGroups.kind !== 'array') {
43+
continue;
44+
}
45+
46+
const prefetchElement = assetGroups.elements.find(element => {
47+
const installMode = element.kind === 'object' && findPropertyInAstObject(element, 'installMode');
48+
49+
return installMode && installMode.value === 'prefetch';
50+
});
51+
52+
if (!prefetchElement || prefetchElement.kind !== 'object') {
53+
continue;
54+
}
55+
56+
const resources = findPropertyInAstObject(prefetchElement, 'resources');
57+
if (!resources || resources.kind !== 'object') {
58+
continue;
59+
}
60+
61+
const files = findPropertyInAstObject(resources, 'files');
62+
if (!files || files.kind !== 'array') {
63+
continue;
64+
}
65+
66+
const hasManifest = files.elements
67+
.some(({ value }) => typeof value === 'string' && value.endsWith('manifest.webmanifest'));
68+
if (hasManifest) {
69+
continue;
70+
}
71+
72+
const recorder = tree.beginUpdate(path);
73+
appendValueInAstArray(recorder, files, '/manifest.webmanifest', 10);
74+
tree.commitUpdate(recorder);
75+
}
76+
}
77+
78+
return tree;
79+
};
80+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
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 { EmptyTree } from '@angular-devkit/schematics';
10+
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
11+
import { WorkspaceTargets } from '../../utility/workspace-models';
12+
13+
// tslint:disable-next-line: no-any
14+
function getWorkspaceTargets(tree: UnitTestTree): any {
15+
return JSON.parse(tree.readContent(workspacePath))
16+
.projects['migration-test'].architect;
17+
}
18+
19+
function updateWorkspaceTargets(tree: UnitTestTree, workspaceTargets: WorkspaceTargets) {
20+
const config = JSON.parse(tree.readContent(workspacePath));
21+
config.projects['migration-test'].architect = workspaceTargets;
22+
tree.overwrite(workspacePath, JSON.stringify(config, undefined, 2));
23+
}
24+
25+
const workspacePath = '/angular.json';
26+
27+
// tslint:disable:no-big-function
28+
describe('Migration to version 9', () => {
29+
describe('Migrate ngsw config', () => {
30+
const schematicRunner = new SchematicTestRunner(
31+
'migrations',
32+
require.resolve('../migration-collection.json'),
33+
);
34+
35+
let tree: UnitTestTree;
36+
37+
beforeEach(async () => {
38+
tree = new UnitTestTree(new EmptyTree());
39+
tree = await schematicRunner
40+
.runExternalSchematicAsync(
41+
require.resolve('../../collection.json'),
42+
'ng-new',
43+
{
44+
name: 'migration-test',
45+
version: '1.2.3',
46+
directory: '.',
47+
},
48+
tree,
49+
)
50+
.toPromise();
51+
});
52+
53+
it(`should add 'manifest.webmanifest' to files in prefetch`, async () => {
54+
const ngswConfigPath = '/ngsw-config.json';
55+
const config = getWorkspaceTargets(tree);
56+
config.build.options.ngswConfigPath = ngswConfigPath;
57+
updateWorkspaceTargets(tree, config);
58+
59+
const ngswConfig = JSON.stringify(
60+
{
61+
assetGroups: [
62+
{
63+
name: 'app',
64+
installMode: 'prefetch',
65+
resources: {
66+
files: [
67+
'/favicon.ico',
68+
'/index.html',
69+
],
70+
},
71+
},
72+
{
73+
name: 'assets',
74+
installMode: 'lazy',
75+
updateMode: 'prefetch',
76+
resources: {
77+
files: [
78+
'/assets/**',
79+
],
80+
},
81+
},
82+
],
83+
},
84+
null,
85+
2,
86+
);
87+
88+
tree.create(ngswConfigPath, ngswConfig);
89+
90+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
91+
const { assetGroups } = JSON.parse(tree2.readContent(ngswConfigPath));
92+
expect(assetGroups[0].resources.files).toEqual([
93+
'/favicon.ico',
94+
'/index.html',
95+
'/manifest.webmanifest',
96+
]);
97+
expect(assetGroups[1].resources.files).toEqual([
98+
'/assets/**',
99+
]);
100+
});
101+
102+
it(`should not add 'manifest.webmanifest' to files if exists`, async () => {
103+
const ngswConfigPath = '/ngsw-config.json';
104+
const config = getWorkspaceTargets(tree);
105+
config.build.options.ngswConfigPath = ngswConfigPath;
106+
updateWorkspaceTargets(tree, config);
107+
108+
const ngswConfig = JSON.stringify(
109+
{
110+
assetGroups: [
111+
{
112+
name: 'app',
113+
installMode: 'prefetch',
114+
resources: {
115+
files: [
116+
'/manifest.webmanifest',
117+
'/favicon.ico',
118+
'/index.html',
119+
],
120+
},
121+
},
122+
],
123+
},
124+
null,
125+
2,
126+
);
127+
128+
tree.create(ngswConfigPath, ngswConfig);
129+
130+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
131+
const { assetGroups } = JSON.parse(tree2.readContent(ngswConfigPath));
132+
expect(assetGroups[0].resources.files).toEqual([
133+
'/manifest.webmanifest',
134+
'/favicon.ico',
135+
'/index.html',
136+
]);
137+
});
138+
});
139+
});

0 commit comments

Comments
 (0)