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