11/*eslint-env node*/
22'use strict' ;
33
4- var RSVP = require ( 'rsvp' ) ;
54var fs = require ( 'fs' ) ;
65var path = require ( 'path' ) ;
76var zlib = require ( 'zlib' ) ;
@@ -14,181 +13,183 @@ var renameFile = denodeify(fs.rename);
1413var DeployPluginBase = require ( 'ember-cli-deploy-plugin' ) ;
1514var validCompressions = [ 'best' , 'gzip' , 'brotli' ] ;
1615
17- module . exports = {
18- name : 'ember-cli-deploy-compress' ,
19-
20- createDeployPlugin ( options ) {
21- var fs = require ( 'fs' ) ;
22-
23- var DeployPlugin = DeployPluginBase . extend ( {
24- name : options . name ,
25- canUseBrotli : false ,
26- defaultConfig : {
27- filePattern : '**/*.{js,css,json,ico,map,xml,txt,svg,eot,ttf,woff,woff2,appcache,webmanifest}' ,
28- ignorePattern : null ,
29- compression : [ 'best' ] ,
30- zopfli : false ,
31- keep : false ,
32- distDir ( context ) {
33- return context . distDir ;
34- } ,
35- distFiles ( context ) {
36- return context . distFiles ;
37- }
16+ class DeployPlugin extends DeployPluginBase {
17+ constructor ( options ) {
18+ super ( ) ;
19+ this . name = options . name ;
20+ this . canUseBrotli = false ;
21+ this . defaultConfig = {
22+ filePattern : '**/*.{js,css,json,ico,map,xml,txt,svg,eot,ttf,woff,woff2,appcache,webmanifest}' ,
23+ ignorePattern : null ,
24+ compression : [ 'best' ] ,
25+ zopfli : false ,
26+ keep : false ,
27+ distDir ( context ) {
28+ return context . distDir ;
3829 } ,
30+ distFiles ( context ) {
31+ return context . distFiles ;
32+ }
33+ } ;
34+ }
3935
40- configure ( context ) {
41- this . _super . configure . call ( this , context ) ;
42- this . _validateCompressionConfig ( ) ;
43- } ,
36+ configure ( context ) {
37+ super . configure . call ( this , context ) ;
38+ this . _validateCompressionConfig ( ) ;
39+ }
4440
45- willUpload ( /* context */ ) {
46- var self = this ;
47-
48- var filePattern = this . readConfig ( 'filePattern' ) ;
49- var ignorePattern = this . readConfig ( 'ignorePattern' ) ;
50- var distDir = this . readConfig ( 'distDir' ) ;
51- var distFiles = this . readConfig ( 'distFiles' ) || [ ] ;
52- var keep = this . readConfig ( 'keep' ) ;
53-
54- this . log ( 'compressing `' + filePattern + '`' , { verbose : true } ) ;
55- this . log ( 'ignoring `' + ignorePattern + '`' , { verbose : true } ) ;
56-
57- // Intentionally calling this as late as possible to give other addons a
58- // chance to influence it somehow, e.g. through ENV variables.
59- this . _determineBrotliSupport ( ) ;
60-
61- let promises = { gzippedFiles : [ ] , brotliCompressedFiles : [ ] } ;
62- if ( this . _mustCompressWithBrotli ( ) ) {
63- this . log ( 'Compressing files with brotli' , { verbose : true } ) ;
64- promises . brotliCompressedFiles = this . _compressFiles ( distDir , distFiles , filePattern , ignorePattern , keep , 'brotli' ) ;
65- }
66- if ( this . _mustCompressWithGzip ( ) ) {
67- this . log ( 'Compressing files with gzip' , { verbose : true } ) ;
68- promises . gzippedFiles = this . _compressFiles ( distDir , distFiles , filePattern , ignorePattern , keep , 'gzip' ) ;
69- }
70- return RSVP . hash ( promises ) . then ( function ( { gzippedFiles, brotliCompressedFiles } ) {
71- self . log ( `compressed ${ gzippedFiles . length + brotliCompressedFiles . length } files ok` , { verbose : true } ) ;
72- if ( keep ) {
73- self . log ( 'keep is enabled, added compressed files to `context.distFiles`' , { verbose : true } ) ;
74- return {
75- distFiles : [ ] . concat ( gzippedFiles ) . concat ( brotliCompressedFiles ) , // needs to be a copy
76- gzippedFiles,
77- brotliCompressedFiles
78- } ;
79- } else {
80- return { gzippedFiles, brotliCompressedFiles } ;
81- }
82- } ) . catch ( this . _errorMessage . bind ( this ) ) ;
83- } ,
84- _compressFiles ( distDir , distFiles , filePattern , ignorePattern , keep , format ) {
85- var filesToCompress = distFiles . filter ( minimatch . filter ( filePattern , { matchBase : true } ) ) ;
86- if ( ignorePattern != null ) {
87- filesToCompress = filesToCompress . filter ( function ( path ) {
88- return ! minimatch ( path , ignorePattern , { matchBase : true } ) ;
89- } ) ;
90- }
91- return RSVP . map ( filesToCompress , this . _compressFile . bind ( this , distDir , keep , format ) ) ;
92- } ,
93- _compressFile ( distDir , keep , format , filePath ) {
94- var self = this ;
95- var fullPath = path . join ( distDir , filePath ) ;
96- var fileExtension = format === 'brotli' ? '.br' : '.gz' ;
97- var outFilePath = fullPath + fileExtension ;
98-
99- return new RSVP . Promise ( function ( resolve , reject ) {
100- var inp = fs . createReadStream ( fullPath ) ;
101- var out = fs . createWriteStream ( outFilePath ) ;
102- let compressor = self [ format + 'Compressor' ] ( ) ;
103- inp . pipe ( compressor ) . pipe ( out ) ;
104- inp . on ( 'error' , function ( err ) { reject ( err ) ; } ) ;
105- out . on ( 'error' , function ( err ) { reject ( err ) ; } ) ;
106- out . on ( 'finish' , function ( ) { resolve ( ) ; } ) ;
107- } ) . then ( function ( ) {
108- if ( ! keep ) {
109- return renameFile ( fullPath + fileExtension , fullPath ) . then ( function ( ) {
110- return filePath ;
111- } ) ;
112- } else {
113- return filePath + fileExtension ;
114- }
115- } ) . then ( function ( outFilePath ) {
116- self . log ( '✔ ' + outFilePath , { verbose : true } ) ;
117-
118- return outFilePath ;
41+ async willUpload ( /* context */ ) {
42+ var self = this ;
43+
44+ var filePattern = this . readConfig ( 'filePattern' ) ;
45+ var ignorePattern = this . readConfig ( 'ignorePattern' ) ;
46+ var distDir = this . readConfig ( 'distDir' ) ;
47+ var distFiles = this . readConfig ( 'distFiles' ) || [ ] ;
48+ var keep = this . readConfig ( 'keep' ) ;
49+
50+ this . log ( 'compressing `' + filePattern + '`' , { verbose : true } ) ;
51+ this . log ( 'ignoring `' + ignorePattern + '`' , { verbose : true } ) ;
52+
53+ // Intentionally calling this as late as possible to give other addons a
54+ // chance to influence it somehow, e.g. through ENV variables.
55+ this . _determineBrotliSupport ( ) ;
56+
57+ let promises = { gzippedFiles : [ ] , brotliCompressedFiles : [ ] } ;
58+ if ( this . _mustCompressWithBrotli ( ) ) {
59+ this . log ( 'Compressing files with brotli' , { verbose : true } ) ;
60+ promises . brotliCompressedFiles = this . _compressFiles ( distDir , distFiles , filePattern , ignorePattern , keep , 'brotli' ) ;
61+ }
62+ if ( this . _mustCompressWithGzip ( ) ) {
63+ this . log ( 'Compressing files with gzip' , { verbose : true } ) ;
64+ promises . gzippedFiles = this . _compressFiles ( distDir , distFiles , filePattern , ignorePattern , keep , 'gzip' ) ;
65+ }
66+ try {
67+ let { gzippedFiles, brotliCompressedFiles } = await RSVP . hash ( promises ) ;
68+ self . log ( `compressed ${ gzippedFiles . length + brotliCompressedFiles . length } files ok` , { verbose : true } ) ;
69+ if ( keep ) {
70+ self . log ( 'keep is enabled, added compressed files to `context.distFiles`' , { verbose : true } ) ;
71+ return {
72+ distFiles : [ ] . concat ( gzippedFiles ) . concat ( brotliCompressedFiles ) , // needs to be a copy
73+ gzippedFiles,
74+ brotliCompressedFiles
75+ } ;
76+ } else {
77+ return { gzippedFiles, brotliCompressedFiles } ;
78+ }
79+ } catch ( e ) {
80+ this . _errorMessage ( e ) ;
81+ }
82+ }
83+
84+ async _compressFiles ( distDir , distFiles , filePattern , ignorePattern , keep , format ) {
85+ var filesToCompress = distFiles . filter ( minimatch . filter ( filePattern , { matchBase : true } ) ) ;
86+ if ( ignorePattern != null ) {
87+ filesToCompress = filesToCompress . filter ( function ( path ) {
88+ return ! minimatch ( path , ignorePattern , { matchBase : true } ) ;
11989 } ) ;
120- } ,
121- gzipCompressor ( ) {
122- if ( this . readConfig ( 'zopfli' ) ) {
123- let pkgName =
124- this . _hasPackage ( 'node-zopfli-es' ) ? 'node-zopfli-es' :
125- this . _hasPackage ( 'node-zopfli' ) ? 'node-zopfli' : null ;
126-
127- if ( pkgName === null ) {
128- throw new Error ( 'No compatible zopfli package found. Install node-zopfli-es for zopfli support!' ) ;
129- }
130-
131- return this . project . require ( pkgName ) . createGzip ( { format : 'gzip' } )
132- } else {
133- return require ( 'zlib' ) . createGzip ( { format : 'gzip' } ) ;
134- }
135- } ,
90+ }
91+ return RSVP . map ( filesToCompress , this . _compressFile . bind ( this , distDir , keep , format ) ) ;
92+ }
13693
137- brotliCompressor ( ) {
138- // Use native brotli support on Node >= 11
139- if ( zlib . createBrotliCompress ) {
140- var brotliOptions = { } ;
94+ async _compressFile ( distDir , keep , format , filePath ) {
95+ var self = this ;
96+ var fullPath = path . join ( distDir , filePath ) ;
97+ var fileExtension = format === 'brotli' ? '.br' : '.gz' ;
98+ var outFilePath = fullPath + fileExtension ;
99+
100+ await new RSVP . Promise ( async function ( resolve , reject ) {
101+ var inp = fs . createReadStream ( fullPath ) ;
102+ var out = fs . createWriteStream ( outFilePath ) ;
103+ let compressor = await self [ format + 'Compressor' ] ( ) ;
104+ inp . pipe ( compressor ) . pipe ( out ) ;
105+ inp . on ( 'error' , function ( err ) { reject ( err ) ; } ) ;
106+ out . on ( 'error' , function ( err ) { reject ( err ) ; } ) ;
107+ out . on ( 'finish' , function ( ) { resolve ( ) ; } ) ;
108+ } ) ;
141109
142- brotliOptions [ zlib . constants . BROTLI_PARAM_QUALITY ] = 11 ;
110+ if ( ! keep ) {
111+ outFilePath = await renameFile ( fullPath + fileExtension , fullPath ) . then ( function ( ) {
112+ return filePath ;
113+ } ) ;
114+ } else {
115+ outFilePath = filePath + fileExtension ;
116+ }
143117
144- return zlib . createBrotliCompress ( { params : brotliOptions } ) ;
145- }
118+ this . log ( '✔ ' + outFilePath , { verbose : true } ) ;
119+ return outFilePath ;
120+ }
146121
147- return require ( 'iltorb' ) . compressStream ( { quality : 11 } ) ;
148- } ,
122+ async gzipCompressor ( ) {
123+ if ( this . readConfig ( 'zopfli' ) ) {
124+ let pkgName =
125+ this . _hasPackage ( 'node-zopfli-es' ) ? 'node-zopfli-es' :
126+ this . _hasPackage ( 'node-zopfli' ) ? 'node-zopfli' : null ;
149127
150- _hasPackage ( pkgName ) {
151- return pkgName in this . project . dependencies ( ) ;
152- } ,
153- _determineBrotliSupport ( ) {
154- let browsers = this . project && this . project . targets && this . project . targets . browsers ;
155- this . canUseBrotli = ! ! browsers && caniuse . isSupported ( 'brotli' , browsers ) ;
156- } ,
157- _mustCompressWithBrotli ( ) {
158- let compression = this . _getCompression ( ) ;
159- return compression . indexOf ( 'brotli' ) > - 1 || ( compression . indexOf ( 'best' ) > - 1 && this . canUseBrotli ) ;
160- } ,
161- _mustCompressWithGzip ( ) {
162- let compression = this . _getCompression ( ) ;
163- return compression . indexOf ( 'gzip' ) > - 1 || ( compression . indexOf ( 'best' ) > - 1 && ! this . canUseBrotli ) ;
164- } ,
165- _errorMessage ( error ) {
166- this . log ( error , { color : 'red' } ) ;
167- return RSVP . reject ( error ) ;
168- } ,
169- _validateCompressionConfig ( ) {
170- let compression = this . _getCompression ( ) ;
171- compression . forEach ( function ( value ) {
172- if ( validCompressions . indexOf ( value ) === - 1 ) {
173- throw new Error ( `The "compression" config option has a wrong value: "${ value } "` ) ;
174- }
175- } ) ;
176- if ( compression . indexOf ( 'best' ) > - 1 && compression . length > 1 ) {
177- throw new Error ( 'The "compression" config cannot combine "best" with other values' ) ;
178- }
179- if ( compression . length > 1 && ! this . readConfig ( 'keep' ) ) {
180- throw new Error ( 'You cannot compress using both brotli and gzip unless you enable the `keep` option' ) ;
181- }
182- } ,
128+ if ( pkgName === null ) {
129+ throw new Error ( 'No compatible zopfli package found. Install node-zopfli-es for zopfli support!' ) ;
130+ }
183131
184- _getCompression ( ) {
185- let compression = this . readConfig ( 'compression' ) ;
186- if ( ! Array . isArray ( compression ) ) {
187- compression = [ compression ] ;
188- }
189- return compression ;
132+ let { default : pkg } = await import ( pkgName ) ;
133+
134+ return pkg . createGzip ( { format : 'gzip' } )
135+ } else {
136+ return require ( 'zlib' ) . createGzip ( { format : 'gzip' } ) ;
137+ }
138+ }
139+
140+ async brotliCompressor ( ) {
141+ var brotliOptions = { } ;
142+ brotliOptions [ zlib . constants . BROTLI_PARAM_QUALITY ] = 11 ;
143+ return zlib . createBrotliCompress ( { params : brotliOptions } ) ;
144+ }
145+
146+ _hasPackage ( pkgName ) {
147+ return pkgName in this . project . dependencies ( ) ;
148+ }
149+ _determineBrotliSupport ( ) {
150+ let browsers = this . project && this . project . targets && this . project . targets . browsers ;
151+ this . canUseBrotli = ! ! browsers && caniuse . isSupported ( 'brotli' , browsers ) ;
152+ }
153+ _mustCompressWithBrotli ( ) {
154+ let compression = this . _getCompression ( ) ;
155+ return compression . indexOf ( 'brotli' ) > - 1 || ( compression . indexOf ( 'best' ) > - 1 && this . canUseBrotli ) ;
156+ }
157+ _mustCompressWithGzip ( ) {
158+ let compression = this . _getCompression ( ) ;
159+ return compression . indexOf ( 'gzip' ) > - 1 || ( compression . indexOf ( 'best' ) > - 1 && ! this . canUseBrotli ) ;
160+ }
161+ _errorMessage ( error ) {
162+ this . log ( error , { color : 'red' } ) ;
163+ return RSVP . reject ( error ) ;
164+ }
165+ _validateCompressionConfig ( ) {
166+ let compression = this . _getCompression ( ) ;
167+ compression . forEach ( function ( value ) {
168+ if ( validCompressions . indexOf ( value ) === - 1 ) {
169+ throw new Error ( `The "compression" config option has a wrong value: "${ value } "` ) ;
190170 }
191171 } ) ;
192- return new DeployPlugin ( ) ;
172+ if ( compression . indexOf ( 'best' ) > - 1 && compression . length > 1 ) {
173+ throw new Error ( 'The "compression" config cannot combine "best" with other values' ) ;
174+ }
175+ if ( compression . length > 1 && ! this . readConfig ( 'keep' ) ) {
176+ throw new Error ( 'You cannot compress using both brotli and gzip unless you enable the `keep` option' ) ;
177+ }
178+ }
179+
180+ _getCompression ( ) {
181+ let compression = this . readConfig ( 'compression' ) ;
182+ if ( ! Array . isArray ( compression ) ) {
183+ compression = [ compression ] ;
184+ }
185+ return compression ;
186+ }
187+ }
188+
189+ module . exports = {
190+ name : 'ember-cli-deploy-compress' ,
191+
192+ createDeployPlugin ( options ) {
193+ return new DeployPlugin ( options ) ;
193194 }
194195} ;
0 commit comments