@@ -3,6 +3,7 @@ import * as path from 'path';
33import * as glob from 'glob' ;
44import * as denodeify from 'denodeify' ;
55
6+ const flattenDeep = require ( 'lodash/flattenDeep' ) ;
67const globPromise = < any > denodeify ( glob ) ;
78const statPromise = < any > denodeify ( fs . stat ) ;
89
@@ -14,48 +15,90 @@ function isDirectory(path: string) {
1415 }
1516}
1617
18+ interface Asset {
19+ originPath : string ;
20+ destinationPath : string ;
21+ relativePath : string ;
22+ }
23+
24+ export interface Pattern {
25+ glob : string ;
26+ input ?: string ;
27+ output ?: string ;
28+ }
29+
1730export interface GlobCopyWebpackPluginOptions {
18- patterns : string [ ] ;
31+ patterns : ( string | Pattern ) [ ] ;
1932 globOptions : any ;
2033}
2134
35+ // Adds an asset to the compilation assets;
36+ function addAsset ( compilation : any , asset : Asset ) {
37+ const realPath = path . resolve ( asset . originPath , asset . relativePath ) ;
38+ // Make sure that asset keys use forward slashes, otherwise webpack dev server
39+ const servedPath = path . join ( asset . destinationPath , asset . relativePath ) . replace ( / \\ / g, '/' ) ;
40+
41+ // Don't re-add existing assets.
42+ if ( compilation . assets [ servedPath ] ) {
43+ return Promise . resolve ( ) ;
44+ }
45+
46+ // Read file and add it to assets;
47+ return statPromise ( realPath )
48+ . then ( ( stat : any ) => compilation . assets [ servedPath ] = {
49+ size : ( ) => stat . size ,
50+ source : ( ) => fs . readFileSync ( realPath )
51+ } ) ;
52+ }
53+
2254export class GlobCopyWebpackPlugin {
2355 constructor ( private options : GlobCopyWebpackPluginOptions ) { }
2456
2557 apply ( compiler : any ) : void {
2658 let { patterns, globOptions } = this . options ;
27- let context = globOptions . cwd || compiler . options . context ;
28- let optional = ! ! globOptions . optional ;
59+ const defaultCwd = globOptions . cwd || compiler . options . context ;
2960
30- // convert dir patterns to globs
31- patterns = patterns . map ( pattern => isDirectory ( path . resolve ( context , pattern ) )
32- ? pattern += '/**/*'
33- : pattern
34- ) ;
35-
36- // force nodir option, since we can't add dirs to assets
61+ // Force nodir option, since we can't add dirs to assets.
3762 globOptions . nodir = true ;
3863
64+ // Process patterns.
65+ patterns = patterns . map ( pattern => {
66+ // Convert all string patterns to Pattern type.
67+ pattern = typeof pattern === 'string' ? { glob : pattern } : pattern ;
68+ // Add defaults
69+ // Input is always resolved relative to the defaultCwd (appRoot)
70+ pattern . input = path . resolve ( defaultCwd , pattern . input || '' ) ;
71+ pattern . output = pattern . output || '' ;
72+ pattern . glob = pattern . glob || '' ;
73+ // Convert dir patterns to globs.
74+ if ( isDirectory ( path . resolve ( pattern . input , pattern . glob ) ) ) {
75+ pattern . glob = pattern . glob + '/**/*' ;
76+ }
77+ return pattern ;
78+ } ) ;
79+
3980 compiler . plugin ( 'emit' , ( compilation : any , cb : any ) => {
40- let globs = patterns . map ( pattern => globPromise ( pattern , globOptions ) ) ;
41-
42- let addAsset = ( relPath : string ) => compilation . assets [ relPath ]
43- // don't re-add to assets
44- ? Promise . resolve ( )
45- : statPromise ( path . resolve ( context , relPath ) )
46- . then ( ( stat : any ) => compilation . assets [ relPath ] = {
47- size : ( ) => stat . size ,
48- source : ( ) => fs . readFileSync ( path . resolve ( context , relPath ) )
49- } )
50- . catch ( ( err : any ) => optional ? Promise . resolve ( ) : Promise . reject ( err ) ) ;
81+ // Create an array of promises for each pattern glob
82+ const globs = patterns . map ( ( pattern : Pattern ) => new Promise ( ( resolve , reject ) =>
83+ // Individual patterns can override cwd
84+ globPromise ( pattern . glob , Object . assign ( { } , globOptions , { cwd : pattern . input } ) )
85+ // Map the results onto an Asset
86+ . then ( ( globResults : string [ ] ) => globResults . map ( res => ( {
87+ originPath : pattern . input ,
88+ destinationPath : pattern . output ,
89+ relativePath : res
90+ } ) ) )
91+ . then ( ( asset : Asset ) => resolve ( asset ) )
92+ . catch ( reject )
93+ ) ) ;
5194
95+ // Wait for all globs.
5296 Promise . all ( globs )
53- // flatten results
54- . then ( globResults => [ ] . concat . apply ( [ ] , globResults ) )
55- // add each file to compilation assets
56- . then ( ( relPaths : string [ ] ) =>
57- Promise . all ( relPaths . map ( ( relPath : string ) => addAsset ( relPath ) ) ) )
58- . catch ( ( err ) => compilation . errors . push ( err ) )
97+ // Flatten results.
98+ . then ( assets => flattenDeep ( assets ) )
99+ // Add each asset to the compilation.
100+ . then ( assets =>
101+ Promise . all ( assets . map ( ( asset : Asset ) => addAsset ( compilation , asset ) ) ) )
59102 . then ( ( ) => cb ( ) ) ;
60103 } ) ;
61104 }
0 commit comments