@@ -3,6 +3,7 @@ import * as path from 'path';
3
3
import * as glob from 'glob' ;
4
4
import * as denodeify from 'denodeify' ;
5
5
6
+ const flattenDeep = require ( 'lodash/flattenDeep' ) ;
6
7
const globPromise = < any > denodeify ( glob ) ;
7
8
const statPromise = < any > denodeify ( fs . stat ) ;
8
9
@@ -14,48 +15,90 @@ function isDirectory(path: string) {
14
15
}
15
16
}
16
17
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
+
17
30
export interface GlobCopyWebpackPluginOptions {
18
- patterns : string [ ] ;
31
+ patterns : ( string | Pattern ) [ ] ;
19
32
globOptions : any ;
20
33
}
21
34
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
+
22
54
export class GlobCopyWebpackPlugin {
23
55
constructor ( private options : GlobCopyWebpackPluginOptions ) { }
24
56
25
57
apply ( compiler : any ) : void {
26
58
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 ;
29
60
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.
37
62
globOptions . nodir = true ;
38
63
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
+
39
80
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
+ ) ) ;
51
94
95
+ // Wait for all globs.
52
96
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 ) ) ) )
59
102
. then ( ( ) => cb ( ) ) ;
60
103
} ) ;
61
104
}
0 commit comments