forked from angular/angular-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathservice-worker.ts
141 lines (124 loc) · 4.21 KB
/
service-worker.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { Path, getSystemPath, normalize } from '@angular-devkit/core';
import { Config, Filesystem, Generator } from '@angular/service-worker/config';
import * as crypto from 'crypto';
import { constants as fsConstants, createReadStream, promises as fs } from 'fs';
import * as path from 'path';
import { pipeline } from 'stream';
class CliFilesystem implements Filesystem {
constructor(private base: string) {}
list(dir: string): Promise<string[]> {
return this._recursiveList(this._resolve(dir), []);
}
read(file: string): Promise<string> {
return fs.readFile(this._resolve(file), 'utf-8');
}
hash(file: string): Promise<string> {
return new Promise((resolve, reject) => {
const hash = crypto.createHash('sha1').setEncoding('hex');
pipeline(
createReadStream(this._resolve(file)),
hash,
(error) => error ? reject(error) : resolve(hash.read()),
);
});
}
write(file: string, content: string): Promise<void> {
return fs.writeFile(this._resolve(file), content);
}
private _resolve(file: string): string {
return path.join(this.base, file);
}
private async _recursiveList(dir: string, items: string[]): Promise<string[]> {
const subdirectories = [];
for await (const entry of await fs.opendir(dir)) {
if (entry.isFile()) {
// Uses posix paths since the service worker expects URLs
items.push('/' + path.posix.relative(this.base, path.posix.join(dir, entry.name)));
} else if (entry.isDirectory()) {
subdirectories.push(path.join(dir, entry.name));
}
}
for (const subdirectory of subdirectories) {
await this._recursiveList(subdirectory, items);
}
return items;
}
}
export async function augmentAppWithServiceWorker(
projectRoot: Path,
appRoot: Path,
outputPath: Path,
baseHref: string,
ngswConfigPath?: string,
): Promise<void> {
const distPath = getSystemPath(normalize(outputPath));
const systemProjectRoot = getSystemPath(projectRoot);
// Find the service worker package
const workerPath = require.resolve('@angular/service-worker/ngsw-worker.js', {
paths: [systemProjectRoot],
});
const swConfigPath = require.resolve('@angular/service-worker/config', {
paths: [systemProjectRoot],
});
// Determine the configuration file path
let configPath;
if (ngswConfigPath) {
configPath = getSystemPath(normalize(ngswConfigPath));
} else {
configPath = path.join(getSystemPath(appRoot), 'ngsw-config.json');
}
// Read the configuration file
let config: Config | undefined;
try {
const configurationData = await fs.readFile(configPath, 'utf-8');
config = JSON.parse(configurationData) as Config;
} catch (error) {
if (error.code === 'ENOENT') {
throw new Error(
'Error: Expected to find an ngsw-config.json configuration file' +
` in the ${getSystemPath(appRoot)} folder. Either provide one or` +
' disable Service Worker in the angular.json configuration file.',
);
} else {
throw error;
}
}
// Generate the manifest
const GeneratorConstructor = require(swConfigPath).Generator as typeof Generator;
const generator = new GeneratorConstructor(new CliFilesystem(distPath), baseHref);
const output = await generator.process(config);
// Write the manifest
const manifest = JSON.stringify(output, null, 2);
await fs.writeFile(path.join(distPath, 'ngsw.json'), manifest);
// Write the worker code
await fs.copyFile(
workerPath,
path.join(distPath, 'ngsw-worker.js'),
fsConstants.COPYFILE_FICLONE,
);
// If present, write the safety worker code
const safetyPath = path.join(path.dirname(workerPath), 'safety-worker.js');
try {
await fs.copyFile(
safetyPath,
path.join(distPath, 'worker-basic.min.js'),
fsConstants.COPYFILE_FICLONE,
);
await fs.copyFile(
safetyPath,
path.join(distPath, 'safety-worker.js'),
fsConstants.COPYFILE_FICLONE,
);
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
}