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