1
+ const { parse : parseFile , join, basename, relative } = require ( "path" ) ;
2
+ const minimatch = require ( "minimatch" ) ;
3
+
4
+ function PlatformFSPlugin ( { platform, platforms, ignore} ) {
5
+ this . platform = platform ;
6
+ this . platforms = platforms ;
7
+ this . ignore = ignore || [ ] ;
8
+
9
+ const alienPlatforms = this . platforms . filter ( p => p !== platform ) ;
10
+ const alienPlatformFilters = alienPlatforms . map ( platform => ( {
11
+ endsWithSuffix : `.${ platform } ` ,
12
+ contains : `.${ platform } .`
13
+ } ) ) . map ( ( { endsWithSuffix, contains} ) => baseFileName =>
14
+ baseFileName . endsWith ( endsWithSuffix ) ||
15
+ baseFileName . indexOf ( contains ) != - 1 ) ;
16
+ this . isNotAlienPlatformFile = file => ! alienPlatformFilters . some ( filter => filter ( basename ( file ) ) ) ;
17
+
18
+ const currentPlatformExt = `.${ platform } ` ;
19
+ this . trimPlatformSuffix = file => {
20
+ const { dir, name, ext} = parseFile ( file ) ;
21
+ if ( ext === currentPlatformExt ) {
22
+ return join ( dir , name ) ;
23
+ } else if ( name . endsWith ( currentPlatformExt ) ) {
24
+ return join ( dir , name . substr ( 0 , name . length - currentPlatformExt . length ) + ext ) ;
25
+ }
26
+ return file ;
27
+ }
28
+ }
29
+
30
+ PlatformFSPlugin . prototype . apply = function ( compiler ) {
31
+ const context = this . context = compiler . context ;
32
+ const minimatchFileFilters = this . ignore . map ( pattern => {
33
+ const minimatchFilter = minimatch . filter ( pattern ) ;
34
+ return file => minimatchFilter ( relative ( context , file ) ) ;
35
+ } ) ;
36
+
37
+ this . isIgnored = file => minimatchFileFilters . some ( filter => filter ( file ) ) ;
38
+
39
+ compiler . inputFileSystem = this . mapFileSystem ( compiler . inputFileSystem ) ;
40
+ }
41
+
42
+ PlatformFSPlugin . prototype . mapFileSystem = function ( fs ) {
43
+ const platform = this . platform ;
44
+ const platforms = this . platforms ;
45
+ const alienPlatforms = this . alienPlatforms ;
46
+ const isNotAlienPlatformFile = this . isNotAlienPlatformFile ;
47
+ const trimPlatformSuffix = this . trimPlatformSuffix ;
48
+ const isIgnored = this . isIgnored ;
49
+ const isNotIgnored = file => ! isIgnored ( file ) ;
50
+
51
+ const mappedFS = {
52
+ get _statStorage ( ) { return fs . _statStorage ; } ,
53
+ get _readFileStorage ( ) { return fs . _readFileStorage ; } ,
54
+ get _readdirStorage ( ) { return fs . _readdirStorage ; }
55
+ } ;
56
+
57
+ [ "readFile" , "provide" , "stat" , "readJson" , "readlink" ] . forEach ( mapPath ) ;
58
+ [ "readdir" ] . forEach ( filterResultingFiles ) ;
59
+
60
+ return mappedFS ;
61
+
62
+ /**
63
+ * For FS functions that get as first argument a file path,
64
+ * this will map it to a platform specific file if such file exists or fallback to the default.
65
+ * Also the last argument must be a function that handles results such as (err, files[]),
66
+ * it will invoke err for files that are ignored.
67
+ */
68
+ function mapPath ( name ) {
69
+ const base = fs [ name ] ;
70
+ mappedFS [ name ] = function ( ) {
71
+ const originalFilePath = arguments [ 0 ] ;
72
+ const callback = arguments [ arguments . length - 1 ] ;
73
+ if ( isIgnored ( originalFilePath ) ) {
74
+ callback ( new Error ( "File " + originalFilePath + " is ignored!" ) ) ;
75
+ return ;
76
+ }
77
+ const { dir, name, ext} = parseFile ( originalFilePath ) ;
78
+ const platformFilePath = join ( dir , name + ( "." + platform ) + ext ) ;
79
+ fs . stat ( platformFilePath , ( err , stat ) => {
80
+ if ( ! err && stat && stat . isFile ( ) ) {
81
+ arguments [ 0 ] = platformFilePath ;
82
+ }
83
+ base . apply ( fs , arguments ) ;
84
+ } ) ;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * For FS functions that get as a last argument a function,
90
+ * that handles results such as (err, files[]),
91
+ * will filter and map the returned files[].
92
+ */
93
+ function filterResultingFiles ( name ) {
94
+ const base = fs [ name ] ;
95
+ mappedFS [ name ] = function ( ) {
96
+ const callback = arguments [ arguments . length - 1 ] ;
97
+ const dir = arguments [ 0 ] ;
98
+ if ( isIgnored ( dir ) ) {
99
+ // Return empty file list for filtered directories.
100
+ callback ( null , [ ] ) ;
101
+ return ;
102
+ }
103
+ arguments [ arguments . length - 1 ] = function ( err , files ) {
104
+ if ( err ) {
105
+ callback ( err ) ;
106
+ } else {
107
+ // Create absolute paths for "ignored" testing, map platforms, and return back the base name.
108
+ const result = files
109
+ . map ( file => join ( dir , file ) )
110
+ . filter ( isNotIgnored )
111
+ . filter ( isNotAlienPlatformFile )
112
+ . map ( trimPlatformSuffix )
113
+ . map ( file => basename ( file ) ) ;
114
+
115
+ // app.css and app.android.css will both map into app.css and we remove duplicates:
116
+ const uniqueResults = [ ...new Set ( result ) ] ;
117
+ callback ( null , uniqueResults ) ;
118
+ }
119
+ }
120
+ base . apply ( fs , arguments ) ;
121
+ }
122
+ }
123
+ }
124
+
125
+ exports . PlatformFSPlugin = PlatformFSPlugin ;
0 commit comments