Skip to content

Commit 66e6805

Browse files
committed
feat(builders): support service worker generation with prerendering
Closes: angular#1505
1 parent fb1428a commit 66e6805

File tree

6 files changed

+83
-1
lines changed

6 files changed

+83
-1
lines changed

modules/builders/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ jasmine_node_test(
5757
"@npm//@angular/compiler",
5858
"@npm//@angular/platform-browser",
5959
"@npm//@angular/platform-browser-dynamic",
60+
"@npm//@angular/service-worker",
6061
"@npm//@types/node",
6162
"@npm//browser-sync",
6263
"@npm//express",

modules/builders/src/prerender/index.spec.ts

+35-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import { Architect } from '@angular-devkit/architect';
10-
import { join, virtualFs } from '@angular-devkit/core';
10+
import { join, normalize, virtualFs } from '@angular-devkit/core';
1111
import { createArchitect, host, outputPathBrowser } from '../../testing/utils';
1212

1313
describe('Prerender Builder', () => {
@@ -159,4 +159,38 @@ describe('Prerender Builder', () => {
159159

160160
await run.stop();
161161
});
162+
163+
it('should generate service-worker', async () => {
164+
const manifest = {
165+
index: '/index.html',
166+
assetGroups: [
167+
{
168+
name: 'app',
169+
installMode: 'prefetch',
170+
resources: {
171+
files: [
172+
'/index.html',
173+
],
174+
},
175+
},
176+
],
177+
};
178+
179+
host.writeMultipleFiles({
180+
'ngsw-config.json': JSON.stringify(manifest),
181+
});
182+
183+
const run = await architect.scheduleTarget(target, { routes: ['foo'], browserTarget: 'app:build:sw' });
184+
const output = await run.result;
185+
expect(output.success).toBe(true);
186+
187+
const content = virtualFs.fileBufferToString(
188+
host.scopedSync().read(join(outputPathBrowser, 'foo/index.html'))
189+
);
190+
191+
expect(content).toContain('foo works!');
192+
expect(content).toContain('This page was prerendered with Angular Universal');
193+
expect(host.scopedSync().exists(normalize('dist/app/browser/ngsw.json'))).toBeTrue();
194+
await run.stop();
195+
});
162196
});

modules/builders/src/prerender/index.ts

+36
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
import { BuilderContext, BuilderOutput, createBuilder, targetFromTargetString } from '@angular-devkit/architect';
1010
import { BrowserBuilderOptions } from '@angular-devkit/build-angular';
11+
import { augmentAppWithServiceWorker } from '@angular-devkit/build-angular/src/utils/service-worker';
12+
import { normalize, resolve as resolvePath } from '@angular-devkit/core';
13+
import { NodeJsSyncHost } from '@angular-devkit/core/node';
1114
import { fork } from 'child_process';
1215
import * as fs from 'fs';
1316
import * as ora from 'ora';
@@ -109,6 +112,19 @@ async function _renderUniversal(
109112
browserOptions: BrowserBuilderOptions,
110113
numProcesses?: number,
111114
): Promise<PrerenderBuilderOutput> {
115+
const host = new NodeJsSyncHost();
116+
const projectName = context.target && context.target.project;
117+
if (!projectName) {
118+
throw new Error('The builder requires a target.');
119+
}
120+
121+
const root = normalize(context.workspaceRoot);
122+
const projectMetadata = await context.getProjectMetadata(projectName);
123+
const projectRoot = resolvePath(
124+
root,
125+
normalize((projectMetadata.root as string) || ''),
126+
);
127+
112128
// Users can specify a different base html file e.g. "src/home.html"
113129
const indexFile = getIndexOutputFile(browserOptions);
114130
// We need to render the routes for each locale from the browser output.
@@ -142,6 +158,26 @@ async function _renderUniversal(
142158
}
143159

144160
spinner.succeed(`Prerendering routes to ${outputPath} complete.`);
161+
162+
if (browserOptions.serviceWorker) {
163+
spinner.start('Generating service worker...');
164+
try {
165+
await augmentAppWithServiceWorker(
166+
host,
167+
root,
168+
projectRoot,
169+
normalize(outputPath),
170+
browserOptions.baseHref || '/',
171+
browserOptions.ngswConfigPath,
172+
);
173+
} catch (error) {
174+
spinner.fail('Service worker generation failed.');
175+
176+
return { success: false, error: error.message };
177+
}
178+
179+
spinner.succeed('Service worker generation complete.');
180+
}
145181
}
146182

147183
return browserResult;

modules/builders/testing/hello-world-app/angular.json

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
"scripts": []
3333
},
3434
"configurations": {
35+
"sw": {
36+
"serviceWorker": true
37+
},
3538
"production": {
3639
"fileReplacements": [
3740
{

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"@angular/platform-browser-dynamic": "^11.0.0-rc.0",
5858
"@angular/platform-server": "^11.0.0-rc.0",
5959
"@angular/router": "^11.0.0-rc.0",
60+
"@angular/service-worker": "^11.0.0-rc.0",
6061
"@bazel/bazelisk": "1.7.3",
6162
"@bazel/buildifier": "3.5.0",
6263
"@bazel/hide-bazel-files": "1.7.0",

yarn.lock

+7
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,13 @@
239239
dependencies:
240240
tslib "^2.0.0"
241241

242+
"@angular/service-worker@^11.0.0-rc.0":
243+
version "11.0.0-rc.2"
244+
resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-11.0.0-rc.2.tgz#a9f34190ce86fe8de2fd53fa4b4549541674c06c"
245+
integrity sha512-rWh9EtXfFjl8riov4q+ET94mXJGxGb5KH4LwOfKN/4TKCYyfx03QVyChDH9GxRnolq6sjSeKhI9c0ni3n7dvjA==
246+
dependencies:
247+
tslib "^2.0.0"
248+
242249
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4":
243250
version "7.10.4"
244251
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"

0 commit comments

Comments
 (0)