# 四.plugin
new NodeEnvironmentPlugin().apply(compiler)
1
NodeEnvironmentPlugin.js
class NodeEnvironmentPlugin {
apply(compiler) {
compiler.inputFileSystem = new CachedInputFileSystem(
new NodeJsInputFileSystem(),
60000
)
const inputFileSystem = compiler.inputFileSystem
compiler.outputFileSystem = new NodeOutputFileSystem()
compiler.watchFileSystem = new NodeWatchFileSystem(compiler.inputFileSystem)
compiler.plugin("before-run", (compiler, callback) => {
if (compiler.inputFileSystem === inputFileSystem) inputFileSystem.purge()
callback()
})
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function CachedInputFileSystem(fileSystem, duration) {
this.fileSystem = fileSystem
this._statStorage = new Storage(duration)
this._readdirStorage = new Storage(duration)
this._readFileStorage = new Storage(duration)
this._readJsonStorage = new Storage(duration)
this._readlinkStorage = new Storage(duration)
this._stat = this.fileSystem.stat
? this.fileSystem.stat.bind(this.fileSystem)
: null
if (!this._stat) this.stat = null
this._statSync = this.fileSystem.statSync
? this.fileSystem.statSync.bind(this.fileSystem)
: null
if (!this._statSync) this.statSync = null
this._readdir = this.fileSystem.readdir
? this.fileSystem.readdir.bind(this.fileSystem)
: null
if (!this._readdir) this.readdir = null
this._readdirSync = this.fileSystem.readdirSync
? this.fileSystem.readdirSync.bind(this.fileSystem)
: null
if (!this._readdirSync) this.readdirSync = null
this._readFile = this.fileSystem.readFile
? this.fileSystem.readFile.bind(this.fileSystem)
: null
if (!this._readFile) this.readFile = null
this._readFileSync = this.fileSystem.readFileSync
? this.fileSystem.readFileSync.bind(this.fileSystem)
: null
if (!this._readFileSync) this.readFileSync = null
if (this.fileSystem.readJson) {
this._readJson = this.fileSystem.readJson.bind(this.fileSystem)
} else if (this.readFile) {
this._readJson = function(path, callback) {
this.readFile(path, function(err, buffer) {
if (err) return callback(err)
try {
var data = JSON.parse(buffer.toString("utf-8"))
} catch (e) {
return callback(e)
}
callback(null, data)
})
}.bind(this)
} else {
this.readJson = null
}
if (this.fileSystem.readJsonSync) {
this._readJsonSync = this.fileSystem.readJsonSync.bind(this.fileSystem)
} else if (this.readFileSync) {
this._readJsonSync = function(path) {
var buffer = this.readFileSync(path)
var data = JSON.parse(buffer.toString("utf-8"))
return data
}.bind(this)
} else {
this.readJsonSync = null
}
this._readlink = this.fileSystem.readlink
? this.fileSystem.readlink.bind(this.fileSystem)
: null
if (!this._readlink) this.readlink = null
this._readlinkSync = this.fileSystem.readlinkSync
? this.fileSystem.readlinkSync.bind(this.fileSystem)
: null
if (!this._readlinkSync) this.readlinkSync = null
}
module.exports = CachedInputFileSystem
CachedInputFileSystem.prototype.stat = function(path, callback) {
this._statStorage.provide(path, this._stat, callback)
}
CachedInputFileSystem.prototype.readdir = function(path, callback) {
this._readdirStorage.provide(path, this._readdir, callback)
}
CachedInputFileSystem.prototype.readFile = function(path, callback) {
this._readFileStorage.provide(path, this._readFile, callback)
}
CachedInputFileSystem.prototype.readJson = function(path, callback) {
this._readJsonStorage.provide(path, this._readJson, callback)
}
CachedInputFileSystem.prototype.readlink = function(path, callback) {
this._readlinkStorage.provide(path, this._readlink, callback)
}
CachedInputFileSystem.prototype.statSync = function(path) {
return this._statStorage.provideSync(path, this._statSync)
}
CachedInputFileSystem.prototype.readdirSync = function(path) {
return this._readdirStorage.provideSync(path, this._readdirSync)
}
CachedInputFileSystem.prototype.readFileSync = function(path) {
return this._readFileStorage.provideSync(path, this._readFileSync)
}
CachedInputFileSystem.prototype.readJsonSync = function(path) {
return this._readJsonStorage.provideSync(path, this._readJsonSync)
}
CachedInputFileSystem.prototype.readlinkSync = function(path) {
return this._readlinkStorage.provideSync(path, this._readlinkSync)
}
CachedInputFileSystem.prototype.purge = function(what) {
this._statStorage.purge(what)
this._readdirStorage.purge(what)
this._readFileStorage.purge(what)
this._readlinkStorage.purge(what)
this._readJsonStorage.purge(what)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
function NodeJsInputFileSystem() {}
module.exports = NodeJsInputFileSystem
NodeJsInputFileSystem.prototype.stat = fs.stat.bind(fs)
NodeJsInputFileSystem.prototype.readdir = function readdir(path, callback) {
fs.readdir(path, function(err, files) {
callback(
err,
files &&
files.map(function(file) {
return file.normalize ? file.normalize("NFC") : file
})
)
})
}
NodeJsInputFileSystem.prototype.readFile = fs.readFile.bind(fs)
NodeJsInputFileSystem.prototype.readlink = fs.readlink.bind(fs)
NodeJsInputFileSystem.prototype.statSync = fs.statSync.bind(fs)
NodeJsInputFileSystem.prototype.readdirSync = function readdirSync(path) {
var files = fs.readdirSync(path)
return (
files &&
files.map(function(file) {
return file.normalize ? file.normalize("NFC") : file
})
)
}
NodeJsInputFileSystem.prototype.readFileSync = fs.readFileSync.bind(fs)
NodeJsInputFileSystem.prototype.readlinkSync = fs.readlinkSync.bind(fs)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class NodeOutputFileSystem {
constructor() {
this.mkdirp = mkdirp
this.mkdir = fs.mkdir.bind(fs)
this.rmdir = fs.rmdir.bind(fs)
this.unlink = fs.unlink.bind(fs)
this.writeFile = fs.writeFile.bind(fs)
this.join = path.join.bind(path)
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
class NodeWatchFileSystem {
constructor(inputFileSystem) {
this.inputFileSystem = inputFileSystem;
this.watcherOptions = {
aggregateTimeout: 0
};
this.watcher = new Watchpack(this.watcherOptions);
}
watch(files, dirs, missing, startTime, options, callback, callbackUndelayed) {
if(!Array.isArray(files))
throw new Error("Invalid arguments: 'files'");
if(!Array.isArray(dirs))
throw new Error("Invalid arguments: 'dirs'");
if(!Array.isArray(missing))
throw new Error("Invalid arguments: 'missing'");
if(typeof callback !== "function")
throw new Error("Invalid arguments: 'callback'");
if(typeof startTime !== "number" && startTime)
throw new Error("Invalid arguments: 'startTime'");
if(typeof options !== "object")
throw new Error("Invalid arguments: 'options'");
if(typeof callbackUndelayed !== "function" && callbackUndelayed)
throw new Error("Invalid arguments: 'callbackUndelayed'");
const oldWatcher = this.watcher;
this.watcher = new Watchpack(options);
if(callbackUndelayed)
this.watcher.once("change", callbackUndelayed);
this.watcher.once("aggregated", (changes, removals) => {
changes = changes.concat(removals);
if(this.inputFileSystem && this.inputFileSystem.purge) {
this.inputFileSystem.purge(changes);
}
const times = this.watcher.getTimes();
callback(null,
changes.filter(file => files.indexOf(file) >= 0).sort(),
changes.filter(file => dirs.indexOf(file) >= 0).sort(),
changes.filter(file => missing.indexOf(file) >= 0).sort(), times, times);
});
this.watcher.watch(files.concat(missing), dirs, startTime);
if(oldWatcher) {
oldWatcher.close();
}
return {
close: () => {
if(this.watcher) {
this.watcher.close();
this.watcher = null;
}
},
pause: () => {
if(this.watcher) {
this.watcher.pause();
}
}
};
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62