/
plugin.js
152 lines (126 loc) · 5.32 KB
/
plugin.js
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import webpack, { DllReferencePlugin } from 'webpack';
import flatMap from 'lodash/flatMap';
import isEmpty from 'lodash/isEmpty';
import once from 'lodash/once';
import { SyncHook } from 'tapable';
import { RawSource } from 'webpack-sources';
import path from 'path';
import { cacheDir, getManifestPath, getInjectPath } from './paths';
import createCompileIfNeeded from './createCompileIfNeeded';
import createConfig from './createConfig';
import createMemory from './createMemory';
import createSettings from './createSettings';
import getInstanceIndex from './getInstanceIndex';
import createHandleStats from './createHandleStats';
import createLogger from './createLogger';
class AutoDLLPlugin {
constructor(settings) {
// first, we store a reference to the settings passed by the user as is.
this._originalSettings = settings;
}
// apply is called once when compiler initialized.
// note that even if the it called using webpack-dev-server
// it still called only once and not on every re-run.
// keep in mind that some user wanted to use multiple instances of the plugin in one config,
// in that case, each instance calls its own apply.
apply(compiler) {
// createSettings responsibe for extending the defaults values with the user's settings.
// It also adds a uniqe hash which in the form of:
// [env]_instance_[index]_[settingsHash]
// [env] - settings.env provided by the user. defaults to NODE_ENV
// [index] - the index of the instance in the user's plugins array.
// [settingsHash] - a hash made of JSON.stringify(settings) with some values omitted.
// hash example: development_instance_0_3289102229a87e84441ca34609c27500
// both [env] & [index] aims to solve the challenge of having muliple instances.
// in the plugin itself its not a problem,
// but since the cache is stored in file system we need to came up with a uniqe path for each instance
// to prevent collision. related to: https://github.com/asfktz/autodll-webpack-plugin/issues/30
const settings = createSettings({
originalSettings: this._originalSettings,
index: getInstanceIndex(compiler.options.plugins, this),
parentConfig: compiler.options,
});
const log = createLogger(settings.debug);
const dllConfig = createConfig(settings, compiler.options);
const compileIfNeeded = createCompileIfNeeded(log, settings);
const memory = createMemory();
const handleStats = createHandleStats(log, settings.hash, memory);
compiler.hooks.autodllStatsRetrieved = new SyncHook(['stats', 'source']);
if (isEmpty(dllConfig.entry)) {
// there's nothing to do.
return;
}
const { context, inject } = settings;
const attachDllReferencePlugin = once(compiler => {
Object.keys(dllConfig.entry)
.map(getManifestPath(settings.hash))
.forEach(manifestPath => {
new DllReferencePlugin({
context: context,
manifest: manifestPath,
}).apply(compiler);
});
});
const beforeCompile = (params, callback) => {
const dependencies = new Set(params.compilationDependencies);
[...dependencies].filter(path => !path.startsWith(cacheDir));
callback();
};
const watchRun = (compiler, callback) => {
compileIfNeeded(() => webpack(dllConfig))
.then(stats => handleStats(stats))
.then(({ source, stats }) => {
compiler.hooks.autodllStatsRetrieved.call(stats, source);
if (source === 'memory') return;
memory.sync(settings.hash, stats);
})
.then(() => {
attachDllReferencePlugin(compiler);
callback();
})
.catch(console.error);
};
const emit = (compilation, callback) => {
const dllAssets = memory.getAssets().reduce((assets, { filename, buffer }) => {
const assetPath = path.join(settings.path, filename);
return {
...assets,
[assetPath]: new RawSource(buffer),
};
}, {});
compilation.assets = { ...compilation.assets, ...dllAssets };
callback();
};
compiler.hooks.beforeCompile.tapAsync('AutoDllPlugin', beforeCompile);
compiler.hooks.run.tapAsync('AutoDllPlugin', watchRun);
compiler.hooks.watchRun.tapAsync('AutoDllPlugin', watchRun);
compiler.hooks.emit.tapAsync('AutoDllPlugin', emit);
if (inject) {
const getDllEntriesPaths = extension =>
flatMap(memory.getStats().entrypoints, 'assets')
.filter(filename => filename.endsWith(extension))
.map(filename =>
getInjectPath({
publicPath: settings.publicPath,
pluginPath: settings.path,
filename,
})
);
const doCompilation = (htmlPluginData, callback) => {
htmlPluginData.assets.js = [...getDllEntriesPaths('.js'), ...htmlPluginData.assets.js];
htmlPluginData.assets.css = [...getDllEntriesPaths('.css'), ...htmlPluginData.assets.css];
callback(null, htmlPluginData);
};
compiler.hooks.compilation.tap('AutoDllPlugin', compilation => {
if (!compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration) {
return;
}
compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration.tapAsync(
'AutoDllPlugin',
doCompilation
);
});
}
}
}
export default AutoDLLPlugin;