|
1 | 1 | // tslint:disable |
2 | 2 | // TODO: cleanup this file, it's copied as is from Angular CLI. |
3 | | - |
| 3 | +import { Path, join, normalize, virtualFs, dirname, getSystemPath } from '@angular-devkit/core'; |
4 | 4 | import { Filesystem } from '@angular/service-worker/config'; |
5 | 5 | import { oneLine, stripIndent } from 'common-tags'; |
6 | 6 | import * as crypto from 'crypto'; |
7 | 7 | import * as fs from 'fs'; |
8 | | -import * as path from 'path'; |
9 | 8 | import * as semver from 'semver'; |
10 | 9 |
|
11 | 10 | import { resolveProjectModule } from '../require-project-module'; |
| 11 | +import { map, reduce, switchMap } from "rxjs/operators"; |
| 12 | +import { Observable } from "rxjs"; |
| 13 | +import { merge } from "rxjs/observable/merge"; |
| 14 | +import { of } from "rxjs/observable/of"; |
| 15 | + |
12 | 16 |
|
13 | 17 | export const NEW_SW_VERSION = '5.0.0-rc.0'; |
14 | 18 |
|
15 | | -class CliFilesystem implements Filesystem { |
16 | | - constructor(private base: string) { } |
17 | 19 |
|
18 | | - list(_path: string): Promise<string[]> { |
19 | | - return Promise.resolve(this.syncList(_path)); |
20 | | - } |
21 | | - |
22 | | - private syncList(_path: string): string[] { |
23 | | - const dir = this.canonical(_path); |
24 | | - const entries = fs.readdirSync(dir).map( |
25 | | - (entry: string) => ({ entry, stats: fs.statSync(path.posix.join(dir, entry)) })); |
26 | | - const files = entries.filter((entry: any) => !entry.stats.isDirectory()) |
27 | | - .map((entry: any) => path.posix.join(_path, entry.entry)); |
| 20 | +class CliFilesystem implements Filesystem { |
| 21 | + constructor(private _host: virtualFs.Host, private base: string) { } |
28 | 22 |
|
29 | | - return entries.filter((entry: any) => entry.stats.isDirectory()) |
30 | | - .map((entry: any) => path.posix.join(_path, entry.entry)) |
31 | | - .reduce((list: string[], subdir: string) => list.concat(this.syncList(subdir)), files); |
| 23 | + list(path: string): Promise<string[]> { |
| 24 | + return this._host.list(this._resolve(path)).toPromise().then(x => x, _err => []); |
32 | 25 | } |
33 | 26 |
|
34 | | - read(_path: string): Promise<string> { |
35 | | - const file = this.canonical(_path); |
36 | | - return Promise.resolve(fs.readFileSync(file).toString()); |
| 27 | + read(path: string): Promise<string> { |
| 28 | + return this._host.read(this._resolve(path)) |
| 29 | + .toPromise() |
| 30 | + .then(content => virtualFs.fileBufferToString(content)); |
37 | 31 | } |
38 | 32 |
|
39 | | - hash(_path: string): Promise<string> { |
| 33 | + hash(path: string): Promise<string> { |
40 | 34 | const sha1 = crypto.createHash('sha1'); |
41 | | - const file = this.canonical(_path); |
42 | | - const contents: Buffer = fs.readFileSync(file); |
43 | | - sha1.update(contents); |
44 | | - return Promise.resolve(sha1.digest('hex')); |
| 35 | + |
| 36 | + return this.read(path) |
| 37 | + .then(content => sha1.update(content)) |
| 38 | + .then(() => sha1.digest('hex')); |
45 | 39 | } |
46 | 40 |
|
47 | | - write(_path: string, contents: string): Promise<void> { |
48 | | - const file = this.canonical(_path); |
49 | | - fs.writeFileSync(file, contents); |
50 | | - return Promise.resolve(); |
| 41 | + write(path: string, content: string): Promise<void> { |
| 42 | + return this._host.write(this._resolve(path), virtualFs.stringToFileBuffer(content)) |
| 43 | + .toPromise(); |
51 | 44 | } |
52 | 45 |
|
53 | | - private canonical(_path: string): string { return path.posix.join(this.base, _path); } |
| 46 | + private _resolve(path: string): Path { |
| 47 | + return join(normalize(this.base), path); |
| 48 | + } |
54 | 49 | } |
55 | 50 |
|
56 | 51 | export function usesServiceWorker(projectRoot: string): boolean { |
@@ -81,38 +76,75 @@ export function usesServiceWorker(projectRoot: string): boolean { |
81 | 76 | return true; |
82 | 77 | } |
83 | 78 |
|
84 | | -export function augmentAppWithServiceWorker(projectRoot: string, appRoot: string, |
85 | | - outputPath: string, baseHref: string): Promise<void> { |
| 79 | +export function augmentAppWithServiceWorker( |
| 80 | + host: virtualFs.Host, |
| 81 | + projectRoot: Path, |
| 82 | + appRoot: Path, |
| 83 | + outputPath: Path, |
| 84 | + baseHref: string, |
| 85 | +): Promise<void> { |
86 | 86 | // Path to the worker script itself. |
87 | | - const workerPath = resolveProjectModule(projectRoot, '@angular/service-worker/ngsw-worker.js'); |
88 | | - const safetyPath = path.join(path.dirname(workerPath), 'safety-worker.js'); |
89 | | - const configPath = path.resolve(appRoot, 'ngsw-config.json'); |
90 | | - |
91 | | - if (!fs.existsSync(configPath)) { |
92 | | - return Promise.reject(new Error(oneLine` |
93 | | - Error: Expected to find an ngsw-config.json configuration |
94 | | - file in the ${appRoot} folder. Either provide one or disable Service Worker |
95 | | - in .angular-cli.json.`, |
96 | | - )); |
97 | | - } |
98 | | - const config = fs.readFileSync(configPath, 'utf8'); |
| 87 | + const distPath = normalize(outputPath); |
| 88 | + const workerPath = normalize( |
| 89 | + resolveProjectModule(getSystemPath(projectRoot), '@angular/service-worker/ngsw-worker.js'), |
| 90 | + ); |
| 91 | + const swConfigPath = resolveProjectModule( |
| 92 | + getSystemPath(projectRoot), |
| 93 | + '@angular/service-worker/config', |
| 94 | + ); |
| 95 | + const safetyPath = join(dirname(workerPath), 'safety-worker.js'); |
| 96 | + const configPath = join(appRoot, 'ngsw-config.json'); |
| 97 | + |
| 98 | + return host.exists(configPath).pipe( |
| 99 | + switchMap(exists => { |
| 100 | + if (!exists) { |
| 101 | + throw new Error(oneLine` |
| 102 | + Error: Expected to find an ngsw-config.json configuration |
| 103 | + file in the ${appRoot} folder. Either provide one or disable Service Worker |
| 104 | + in your angular.json configuration file.`, |
| 105 | + ); |
| 106 | + } |
99 | 107 |
|
100 | | - const Generator = require('@angular/service-worker/config').Generator; |
101 | | - const gen = new Generator(new CliFilesystem(outputPath), baseHref); |
102 | | - return gen |
103 | | - .process(JSON.parse(config)) |
104 | | - .then((output: Object) => { |
| 108 | + return host.read(configPath) as Observable<virtualFs.FileBuffer>; |
| 109 | + }), |
| 110 | + map(content => JSON.parse(virtualFs.fileBufferToString(content))), |
| 111 | + switchMap(configJson => { |
| 112 | + const Generator = require(swConfigPath).Generator; |
| 113 | + const gen = new Generator(new CliFilesystem(host, outputPath), baseHref); |
| 114 | + |
| 115 | + return gen.process(configJson); |
| 116 | + }), |
| 117 | + |
| 118 | + switchMap(output => { |
105 | 119 | const manifest = JSON.stringify(output, null, 2); |
106 | | - fs.writeFileSync(path.resolve(outputPath, 'ngsw.json'), manifest); |
107 | | - // Copy worker script to dist directory. |
108 | | - const workerCode = fs.readFileSync(workerPath); |
109 | | - fs.writeFileSync(path.resolve(outputPath, 'ngsw-worker.js'), workerCode); |
110 | | - |
111 | | - // If @angular/service-worker has the safety script, copy it into two locations. |
112 | | - if (fs.existsSync(safetyPath)) { |
113 | | - const safetyCode = fs.readFileSync(safetyPath); |
114 | | - fs.writeFileSync(path.resolve(outputPath, 'worker-basic.min.js'), safetyCode); |
115 | | - fs.writeFileSync(path.resolve(outputPath, 'safety-worker.js'), safetyCode); |
| 120 | + return host.read(workerPath).pipe( |
| 121 | + switchMap(workerCode => { |
| 122 | + return merge( |
| 123 | + host.write(join(distPath, 'ngsw.json'), virtualFs.stringToFileBuffer(manifest)), |
| 124 | + host.write(join(distPath, 'ngsw-worker.js'), workerCode), |
| 125 | + ) as Observable<void>; |
| 126 | + }), |
| 127 | + ); |
| 128 | + }), |
| 129 | + |
| 130 | + switchMap(() => host.exists(safetyPath)), |
| 131 | + // If @angular/service-worker has the safety script, copy it into two locations. |
| 132 | + switchMap(exists => { |
| 133 | + if (!exists) { |
| 134 | + return of<void>(undefined); |
116 | 135 | } |
117 | | - }); |
| 136 | + |
| 137 | + return host.read(safetyPath).pipe( |
| 138 | + switchMap(safetyCode => { |
| 139 | + return merge( |
| 140 | + host.write(join(distPath, 'worker-basic.min.js'), safetyCode), |
| 141 | + host.write(join(distPath, 'safety-worker.js'), safetyCode), |
| 142 | + ) as Observable<void>; |
| 143 | + }), |
| 144 | + ); |
| 145 | + }), |
| 146 | + |
| 147 | + // Remove all elements, reduce them to a single emit. |
| 148 | + reduce(() => {}), |
| 149 | + ).toPromise(); |
118 | 150 | } |
0 commit comments