1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | "use strict";
|
7 |
|
8 | const WebpackError = require("../WebpackError");
|
9 | const { parseOptions } = require("../container/options");
|
10 | const createSchemaValidation = require("../util/create-schema-validation");
|
11 | const ProvideForSharedDependency = require("./ProvideForSharedDependency");
|
12 | const ProvideSharedDependency = require("./ProvideSharedDependency");
|
13 | const ProvideSharedModuleFactory = require("./ProvideSharedModuleFactory");
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | const validate = createSchemaValidation(
|
20 | require("../../schemas/plugins/sharing/ProvideSharedPlugin.check.js"),
|
21 | () => require("../../schemas/plugins/sharing/ProvideSharedPlugin.json"),
|
22 | {
|
23 | name: "Provide Shared Plugin",
|
24 | baseDataPath: "options"
|
25 | }
|
26 | );
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 | class ProvideSharedPlugin {
|
39 | |
40 |
|
41 |
|
42 | constructor(options) {
|
43 | validate(options);
|
44 |
|
45 |
|
46 | this._provides = parseOptions(
|
47 | options.provides,
|
48 | item => {
|
49 | if (Array.isArray(item))
|
50 | throw new Error("Unexpected array of provides");
|
51 |
|
52 | const result = {
|
53 | shareKey: item,
|
54 | version: undefined,
|
55 | shareScope: options.shareScope || "default",
|
56 | eager: false
|
57 | };
|
58 | return result;
|
59 | },
|
60 | item => ({
|
61 | shareKey: item.shareKey,
|
62 | version: item.version,
|
63 | shareScope: item.shareScope || options.shareScope || "default",
|
64 | eager: !!item.eager
|
65 | })
|
66 | );
|
67 | this._provides.sort(([a], [b]) => {
|
68 | if (a < b) return -1;
|
69 | if (b < a) return 1;
|
70 | return 0;
|
71 | });
|
72 | }
|
73 |
|
74 | |
75 |
|
76 |
|
77 |
|
78 |
|
79 | apply(compiler) {
|
80 |
|
81 | const compilationData = new WeakMap();
|
82 |
|
83 | compiler.hooks.compilation.tap(
|
84 | "ProvideSharedPlugin",
|
85 | (compilation, { normalModuleFactory }) => {
|
86 |
|
87 | const resolvedProvideMap = new Map();
|
88 |
|
89 | const matchProvides = new Map();
|
90 |
|
91 | const prefixMatchProvides = new Map();
|
92 | for (const [request, config] of this._provides) {
|
93 | if (/^(\/|[A-Za-z]:\\|\\\\|\.\.?(\/|$))/.test(request)) {
|
94 |
|
95 | resolvedProvideMap.set(request, {
|
96 | config,
|
97 | version: config.version
|
98 | });
|
99 | } else if (/^(\/|[A-Za-z]:\\|\\\\)/.test(request)) {
|
100 |
|
101 | resolvedProvideMap.set(request, {
|
102 | config,
|
103 | version: config.version
|
104 | });
|
105 | } else if (request.endsWith("/")) {
|
106 |
|
107 | prefixMatchProvides.set(request, config);
|
108 | } else {
|
109 |
|
110 | matchProvides.set(request, config);
|
111 | }
|
112 | }
|
113 | compilationData.set(compilation, resolvedProvideMap);
|
114 | const provideSharedModule = (
|
115 | key,
|
116 | config,
|
117 | resource,
|
118 | resourceResolveData
|
119 | ) => {
|
120 | let version = config.version;
|
121 | if (version === undefined) {
|
122 | let details = "";
|
123 | if (!resourceResolveData) {
|
124 | details = `No resolve data provided from resolver.`;
|
125 | } else {
|
126 | const descriptionFileData =
|
127 | resourceResolveData.descriptionFileData;
|
128 | if (!descriptionFileData) {
|
129 | details =
|
130 | "No description file (usually package.json) found. Add description file with name and version, or manually specify version in shared config.";
|
131 | } else if (!descriptionFileData.version) {
|
132 | details =
|
133 | "No version in description file (usually package.json). Add version to description file, or manually specify version in shared config.";
|
134 | } else {
|
135 | version = descriptionFileData.version;
|
136 | }
|
137 | }
|
138 | if (!version) {
|
139 | const error = new WebpackError(
|
140 | `No version specified and unable to automatically determine one. ${details}`
|
141 | );
|
142 | error.file = `shared module ${key} -> ${resource}`;
|
143 | compilation.warnings.push(error);
|
144 | }
|
145 | }
|
146 | resolvedProvideMap.set(resource, {
|
147 | config,
|
148 | version
|
149 | });
|
150 | };
|
151 | normalModuleFactory.hooks.module.tap(
|
152 | "ProvideSharedPlugin",
|
153 | (module, { resource, resourceResolveData }, resolveData) => {
|
154 | if (resolvedProvideMap.has(resource)) {
|
155 | return module;
|
156 | }
|
157 | const { request } = resolveData;
|
158 | {
|
159 | const config = matchProvides.get(request);
|
160 | if (config !== undefined) {
|
161 | provideSharedModule(
|
162 | request,
|
163 | config,
|
164 | resource,
|
165 | resourceResolveData
|
166 | );
|
167 | resolveData.cacheable = false;
|
168 | }
|
169 | }
|
170 | for (const [prefix, config] of prefixMatchProvides) {
|
171 | if (request.startsWith(prefix)) {
|
172 | const remainder = request.slice(prefix.length);
|
173 | provideSharedModule(
|
174 | resource,
|
175 | {
|
176 | ...config,
|
177 | shareKey: config.shareKey + remainder
|
178 | },
|
179 | resource,
|
180 | resourceResolveData
|
181 | );
|
182 | resolveData.cacheable = false;
|
183 | }
|
184 | }
|
185 | return module;
|
186 | }
|
187 | );
|
188 | }
|
189 | );
|
190 | compiler.hooks.finishMake.tapPromise("ProvideSharedPlugin", compilation => {
|
191 | const resolvedProvideMap = compilationData.get(compilation);
|
192 | if (!resolvedProvideMap) return Promise.resolve();
|
193 | return Promise.all(
|
194 | Array.from(
|
195 | resolvedProvideMap,
|
196 | ([resource, { config, version }]) =>
|
197 | new Promise((resolve, reject) => {
|
198 | compilation.addInclude(
|
199 | compiler.context,
|
200 | new ProvideSharedDependency(
|
201 | config.shareScope,
|
202 | config.shareKey,
|
203 | version || false,
|
204 | resource,
|
205 | config.eager
|
206 | ),
|
207 | {
|
208 | name: undefined
|
209 | },
|
210 | err => {
|
211 | if (err) return reject(err);
|
212 | resolve();
|
213 | }
|
214 | );
|
215 | })
|
216 | )
|
217 | ).then(() => {});
|
218 | });
|
219 |
|
220 | compiler.hooks.compilation.tap(
|
221 | "ProvideSharedPlugin",
|
222 | (compilation, { normalModuleFactory }) => {
|
223 | compilation.dependencyFactories.set(
|
224 | ProvideForSharedDependency,
|
225 | normalModuleFactory
|
226 | );
|
227 |
|
228 | compilation.dependencyFactories.set(
|
229 | ProvideSharedDependency,
|
230 | new ProvideSharedModuleFactory()
|
231 | );
|
232 | }
|
233 | );
|
234 | }
|
235 | }
|
236 |
|
237 | module.exports = ProvideSharedPlugin;
|