8
8
9
9
import type { Config , Filesystem } from '@angular/service-worker/config' ;
10
10
import * as crypto from 'crypto' ;
11
- import { createReadStream , promises as fs , constants as fsConstants } from 'fs' ;
11
+ import { constants as fsConstants , promises as fsPromises } from 'fs' ;
12
12
import * as path from 'path' ;
13
- import { pipeline } from 'stream' ;
14
13
import { assertIsError } from './error' ;
15
14
import { loadEsmModule } from './load-esm' ;
16
15
17
16
class CliFilesystem implements Filesystem {
18
- constructor ( private base : string ) { }
17
+ constructor ( private fs : typeof fsPromises , private base : string ) { }
19
18
20
19
list ( dir : string ) : Promise < string [ ] > {
21
20
return this . _recursiveList ( this . _resolve ( dir ) , [ ] ) ;
22
21
}
23
22
24
23
read ( file : string ) : Promise < string > {
25
- return fs . readFile ( this . _resolve ( file ) , 'utf-8' ) ;
24
+ return this . fs . readFile ( this . _resolve ( file ) , 'utf-8' ) ;
26
25
}
27
26
28
- hash ( file : string ) : Promise < string > {
29
- return new Promise ( ( resolve , reject ) => {
30
- const hash = crypto . createHash ( 'sha1' ) . setEncoding ( 'hex' ) ;
31
- pipeline ( createReadStream ( this . _resolve ( file ) ) , hash , ( error ) =>
32
- error ? reject ( error ) : resolve ( hash . read ( ) ) ,
33
- ) ;
34
- } ) ;
27
+ async hash ( file : string ) : Promise < string > {
28
+ const content = await this . read ( file ) ;
29
+
30
+ return crypto . createHash ( 'sha1' ) . update ( content ) . digest ( 'hex' ) ;
35
31
}
36
32
37
- write ( file : string , content : string ) : Promise < void > {
38
- return fs . writeFile ( this . _resolve ( file ) , content ) ;
33
+ write ( _file : string , _content : string ) : never {
34
+ throw new Error ( 'This should never happen.' ) ;
39
35
}
40
36
41
37
private _resolve ( file : string ) : string {
@@ -44,12 +40,15 @@ class CliFilesystem implements Filesystem {
44
40
45
41
private async _recursiveList ( dir : string , items : string [ ] ) : Promise < string [ ] > {
46
42
const subdirectories = [ ] ;
47
- for await ( const entry of await fs . opendir ( dir ) ) {
48
- if ( entry . isFile ( ) ) {
43
+ for ( const entry of await this . fs . readdir ( dir ) ) {
44
+ const entryPath = path . join ( dir , entry ) ;
45
+ const stats = await this . fs . stat ( entryPath ) ;
46
+
47
+ if ( stats . isFile ( ) ) {
49
48
// Uses posix paths since the service worker expects URLs
50
- items . push ( '/' + path . relative ( this . base , path . join ( dir , entry . name ) ) . replace ( / \\ / g, '/' ) ) ;
51
- } else if ( entry . isDirectory ( ) ) {
52
- subdirectories . push ( path . join ( dir , entry . name ) ) ;
49
+ items . push ( '/' + path . relative ( this . base , entryPath ) . replace ( / \\ / g, '/' ) ) ;
50
+ } else if ( stats . isDirectory ( ) ) {
51
+ subdirectories . push ( entryPath ) ;
53
52
}
54
53
}
55
54
@@ -67,6 +66,8 @@ export async function augmentAppWithServiceWorker(
67
66
outputPath : string ,
68
67
baseHref : string ,
69
68
ngswConfigPath ?: string ,
69
+ inputputFileSystem = fsPromises ,
70
+ outputFileSystem = fsPromises ,
70
71
) : Promise < void > {
71
72
// Determine the configuration file path
72
73
const configPath = ngswConfigPath
@@ -76,7 +77,7 @@ export async function augmentAppWithServiceWorker(
76
77
// Read the configuration file
77
78
let config : Config | undefined ;
78
79
try {
79
- const configurationData = await fs . readFile ( configPath , 'utf-8' ) ;
80
+ const configurationData = await inputputFileSystem . readFile ( configPath , 'utf-8' ) ;
80
81
config = JSON . parse ( configurationData ) as Config ;
81
82
} catch ( error ) {
82
83
assertIsError ( error ) ;
@@ -101,36 +102,37 @@ export async function augmentAppWithServiceWorker(
101
102
) . Generator ;
102
103
103
104
// Generate the manifest
104
- const generator = new GeneratorConstructor ( new CliFilesystem ( outputPath ) , baseHref ) ;
105
+ const generator = new GeneratorConstructor (
106
+ new CliFilesystem ( outputFileSystem , outputPath ) ,
107
+ baseHref ,
108
+ ) ;
105
109
const output = await generator . process ( config ) ;
106
110
107
111
// Write the manifest
108
112
const manifest = JSON . stringify ( output , null , 2 ) ;
109
- await fs . writeFile ( path . join ( outputPath , 'ngsw.json' ) , manifest ) ;
113
+ await outputFileSystem . writeFile ( path . join ( outputPath , 'ngsw.json' ) , manifest ) ;
110
114
111
115
// Find the service worker package
112
116
const workerPath = require . resolve ( '@angular/service-worker/ngsw-worker.js' ) ;
113
117
118
+ const copy = async ( src : string , dest : string ) : Promise < void > => {
119
+ const resolvedDest = path . join ( outputPath , dest ) ;
120
+
121
+ return inputputFileSystem === outputFileSystem
122
+ ? // Native FS (Builder).
123
+ inputputFileSystem . copyFile ( workerPath , resolvedDest , fsConstants . COPYFILE_FICLONE )
124
+ : // memfs (Webpack): Read the file from the input FS (disk) and write it to the output FS (memory).
125
+ outputFileSystem . writeFile ( resolvedDest , await inputputFileSystem . readFile ( src ) ) ;
126
+ } ;
127
+
114
128
// Write the worker code
115
- await fs . copyFile (
116
- workerPath ,
117
- path . join ( outputPath , 'ngsw-worker.js' ) ,
118
- fsConstants . COPYFILE_FICLONE ,
119
- ) ;
129
+ await copy ( workerPath , 'ngsw-worker.js' ) ;
120
130
121
131
// If present, write the safety worker code
122
- const safetyPath = path . join ( path . dirname ( workerPath ) , 'safety-worker.js' ) ;
123
132
try {
124
- await fs . copyFile (
125
- safetyPath ,
126
- path . join ( outputPath , 'worker-basic.min.js' ) ,
127
- fsConstants . COPYFILE_FICLONE ,
128
- ) ;
129
- await fs . copyFile (
130
- safetyPath ,
131
- path . join ( outputPath , 'safety-worker.js' ) ,
132
- fsConstants . COPYFILE_FICLONE ,
133
- ) ;
133
+ const safetyPath = path . join ( path . dirname ( workerPath ) , 'safety-worker.js' ) ;
134
+ await copy ( safetyPath , 'worker-basic.min.js' ) ;
135
+ await copy ( safetyPath , 'safety-worker.js' ) ;
134
136
} catch ( error ) {
135
137
assertIsError ( error ) ;
136
138
if ( error . code !== 'ENOENT' ) {
0 commit comments