1 | /*
|
---|
2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
---|
3 | Author Tobias Koppers @sokra
|
---|
4 | */
|
---|
5 |
|
---|
6 | "use strict";
|
---|
7 |
|
---|
8 | const asyncLib = require("neo-async");
|
---|
9 | const { ConcatSource, RawSource } = require("webpack-sources");
|
---|
10 | const Compilation = require("./Compilation");
|
---|
11 | const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
|
---|
12 | const ProgressPlugin = require("./ProgressPlugin");
|
---|
13 | const SourceMapDevToolModuleOptionsPlugin = require("./SourceMapDevToolModuleOptionsPlugin");
|
---|
14 | const createSchemaValidation = require("./util/create-schema-validation");
|
---|
15 | const createHash = require("./util/createHash");
|
---|
16 | const { relative, dirname } = require("./util/fs");
|
---|
17 | const { absolutify } = require("./util/identifier");
|
---|
18 |
|
---|
19 | /** @typedef {import("webpack-sources").MapOptions} MapOptions */
|
---|
20 | /** @typedef {import("webpack-sources").Source} Source */
|
---|
21 | /** @typedef {import("../declarations/plugins/SourceMapDevToolPlugin").SourceMapDevToolPluginOptions} SourceMapDevToolPluginOptions */
|
---|
22 | /** @typedef {import("./Cache").Etag} Etag */
|
---|
23 | /** @typedef {import("./CacheFacade").ItemCacheFacade} ItemCacheFacade */
|
---|
24 | /** @typedef {import("./Chunk")} Chunk */
|
---|
25 | /** @typedef {import("./Compilation").AssetInfo} AssetInfo */
|
---|
26 | /** @typedef {import("./Compiler")} Compiler */
|
---|
27 | /** @typedef {import("./Module")} Module */
|
---|
28 | /** @typedef {import("./NormalModule").SourceMap} SourceMap */
|
---|
29 | /** @typedef {import("./util/Hash")} Hash */
|
---|
30 |
|
---|
31 | const validate = createSchemaValidation(
|
---|
32 | require("../schemas/plugins/SourceMapDevToolPlugin.check.js"),
|
---|
33 | () => require("../schemas/plugins/SourceMapDevToolPlugin.json"),
|
---|
34 | {
|
---|
35 | name: "SourceMap DevTool Plugin",
|
---|
36 | baseDataPath: "options"
|
---|
37 | }
|
---|
38 | );
|
---|
39 | /**
|
---|
40 | * @typedef {object} SourceMapTask
|
---|
41 | * @property {Source} asset
|
---|
42 | * @property {AssetInfo} assetInfo
|
---|
43 | * @property {(string | Module)[]} modules
|
---|
44 | * @property {string} source
|
---|
45 | * @property {string} file
|
---|
46 | * @property {SourceMap} sourceMap
|
---|
47 | * @property {ItemCacheFacade} cacheItem cache item
|
---|
48 | */
|
---|
49 |
|
---|
50 | /**
|
---|
51 | * Escapes regular expression metacharacters
|
---|
52 | * @param {string} str String to quote
|
---|
53 | * @returns {string} Escaped string
|
---|
54 | */
|
---|
55 | const quoteMeta = str => {
|
---|
56 | return str.replace(/[-[\]\\/{}()*+?.^$|]/g, "\\$&");
|
---|
57 | };
|
---|
58 |
|
---|
59 | /**
|
---|
60 | * Creating {@link SourceMapTask} for given file
|
---|
61 | * @param {string} file current compiled file
|
---|
62 | * @param {Source} asset the asset
|
---|
63 | * @param {AssetInfo} assetInfo the asset info
|
---|
64 | * @param {MapOptions} options source map options
|
---|
65 | * @param {Compilation} compilation compilation instance
|
---|
66 | * @param {ItemCacheFacade} cacheItem cache item
|
---|
67 | * @returns {SourceMapTask | undefined} created task instance or `undefined`
|
---|
68 | */
|
---|
69 | const getTaskForFile = (
|
---|
70 | file,
|
---|
71 | asset,
|
---|
72 | assetInfo,
|
---|
73 | options,
|
---|
74 | compilation,
|
---|
75 | cacheItem
|
---|
76 | ) => {
|
---|
77 | let source;
|
---|
78 | /** @type {SourceMap} */
|
---|
79 | let sourceMap;
|
---|
80 | /**
|
---|
81 | * Check if asset can build source map
|
---|
82 | */
|
---|
83 | if (asset.sourceAndMap) {
|
---|
84 | const sourceAndMap = asset.sourceAndMap(options);
|
---|
85 | sourceMap = /** @type {SourceMap} */ (sourceAndMap.map);
|
---|
86 | source = sourceAndMap.source;
|
---|
87 | } else {
|
---|
88 | sourceMap = /** @type {SourceMap} */ (asset.map(options));
|
---|
89 | source = asset.source();
|
---|
90 | }
|
---|
91 | if (!sourceMap || typeof source !== "string") return;
|
---|
92 | const context = compilation.options.context;
|
---|
93 | const root = compilation.compiler.root;
|
---|
94 | const cachedAbsolutify = absolutify.bindContextCache(context, root);
|
---|
95 | const modules = sourceMap.sources.map(source => {
|
---|
96 | if (!source.startsWith("webpack://")) return source;
|
---|
97 | source = cachedAbsolutify(source.slice(10));
|
---|
98 | const module = compilation.findModule(source);
|
---|
99 | return module || source;
|
---|
100 | });
|
---|
101 |
|
---|
102 | return {
|
---|
103 | file,
|
---|
104 | asset,
|
---|
105 | source,
|
---|
106 | assetInfo,
|
---|
107 | sourceMap,
|
---|
108 | modules,
|
---|
109 | cacheItem
|
---|
110 | };
|
---|
111 | };
|
---|
112 |
|
---|
113 | class SourceMapDevToolPlugin {
|
---|
114 | /**
|
---|
115 | * @param {SourceMapDevToolPluginOptions} [options] options object
|
---|
116 | * @throws {Error} throws error, if got more than 1 arguments
|
---|
117 | */
|
---|
118 | constructor(options = {}) {
|
---|
119 | validate(options);
|
---|
120 |
|
---|
121 | /** @type {string | false} */
|
---|
122 | this.sourceMapFilename = options.filename;
|
---|
123 | /** @type {string | false} */
|
---|
124 | this.sourceMappingURLComment =
|
---|
125 | options.append === false
|
---|
126 | ? false
|
---|
127 | : options.append || "\n//# source" + "MappingURL=[url]";
|
---|
128 | /** @type {string | Function} */
|
---|
129 | this.moduleFilenameTemplate =
|
---|
130 | options.moduleFilenameTemplate || "webpack://[namespace]/[resourcePath]";
|
---|
131 | /** @type {string | Function} */
|
---|
132 | this.fallbackModuleFilenameTemplate =
|
---|
133 | options.fallbackModuleFilenameTemplate ||
|
---|
134 | "webpack://[namespace]/[resourcePath]?[hash]";
|
---|
135 | /** @type {string} */
|
---|
136 | this.namespace = options.namespace || "";
|
---|
137 | /** @type {SourceMapDevToolPluginOptions} */
|
---|
138 | this.options = options;
|
---|
139 | }
|
---|
140 |
|
---|
141 | /**
|
---|
142 | * Apply the plugin
|
---|
143 | * @param {Compiler} compiler compiler instance
|
---|
144 | * @returns {void}
|
---|
145 | */
|
---|
146 | apply(compiler) {
|
---|
147 | const outputFs = compiler.outputFileSystem;
|
---|
148 | const sourceMapFilename = this.sourceMapFilename;
|
---|
149 | const sourceMappingURLComment = this.sourceMappingURLComment;
|
---|
150 | const moduleFilenameTemplate = this.moduleFilenameTemplate;
|
---|
151 | const namespace = this.namespace;
|
---|
152 | const fallbackModuleFilenameTemplate = this.fallbackModuleFilenameTemplate;
|
---|
153 | const requestShortener = compiler.requestShortener;
|
---|
154 | const options = this.options;
|
---|
155 | options.test = options.test || /\.((c|m)?js|css)($|\?)/i;
|
---|
156 |
|
---|
157 | const matchObject = ModuleFilenameHelpers.matchObject.bind(
|
---|
158 | undefined,
|
---|
159 | options
|
---|
160 | );
|
---|
161 |
|
---|
162 | compiler.hooks.compilation.tap("SourceMapDevToolPlugin", compilation => {
|
---|
163 | new SourceMapDevToolModuleOptionsPlugin(options).apply(compilation);
|
---|
164 |
|
---|
165 | compilation.hooks.processAssets.tapAsync(
|
---|
166 | {
|
---|
167 | name: "SourceMapDevToolPlugin",
|
---|
168 | stage: Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING,
|
---|
169 | additionalAssets: true
|
---|
170 | },
|
---|
171 | (assets, callback) => {
|
---|
172 | const chunkGraph = compilation.chunkGraph;
|
---|
173 | const cache = compilation.getCache("SourceMapDevToolPlugin");
|
---|
174 | /** @type {Map<string | Module, string>} */
|
---|
175 | const moduleToSourceNameMapping = new Map();
|
---|
176 | /**
|
---|
177 | * @type {Function}
|
---|
178 | * @returns {void}
|
---|
179 | */
|
---|
180 | const reportProgress =
|
---|
181 | ProgressPlugin.getReporter(compilation.compiler) || (() => {});
|
---|
182 |
|
---|
183 | /** @type {Map<string, Chunk>} */
|
---|
184 | const fileToChunk = new Map();
|
---|
185 | for (const chunk of compilation.chunks) {
|
---|
186 | for (const file of chunk.files) {
|
---|
187 | fileToChunk.set(file, chunk);
|
---|
188 | }
|
---|
189 | for (const file of chunk.auxiliaryFiles) {
|
---|
190 | fileToChunk.set(file, chunk);
|
---|
191 | }
|
---|
192 | }
|
---|
193 |
|
---|
194 | /** @type {string[]} */
|
---|
195 | const files = [];
|
---|
196 | for (const file of Object.keys(assets)) {
|
---|
197 | if (matchObject(file)) {
|
---|
198 | files.push(file);
|
---|
199 | }
|
---|
200 | }
|
---|
201 |
|
---|
202 | reportProgress(0.0);
|
---|
203 | /** @type {SourceMapTask[]} */
|
---|
204 | const tasks = [];
|
---|
205 | let fileIndex = 0;
|
---|
206 |
|
---|
207 | asyncLib.each(
|
---|
208 | files,
|
---|
209 | (file, callback) => {
|
---|
210 | const asset = compilation.getAsset(file);
|
---|
211 | if (asset.info.related && asset.info.related.sourceMap) {
|
---|
212 | fileIndex++;
|
---|
213 | return callback();
|
---|
214 | }
|
---|
215 | const cacheItem = cache.getItemCache(
|
---|
216 | file,
|
---|
217 | cache.mergeEtags(
|
---|
218 | cache.getLazyHashedEtag(asset.source),
|
---|
219 | namespace
|
---|
220 | )
|
---|
221 | );
|
---|
222 |
|
---|
223 | cacheItem.get((err, cacheEntry) => {
|
---|
224 | if (err) {
|
---|
225 | return callback(err);
|
---|
226 | }
|
---|
227 | /**
|
---|
228 | * If presented in cache, reassigns assets. Cache assets already have source maps.
|
---|
229 | */
|
---|
230 | if (cacheEntry) {
|
---|
231 | const { assets, assetsInfo } = cacheEntry;
|
---|
232 | for (const cachedFile of Object.keys(assets)) {
|
---|
233 | if (cachedFile === file) {
|
---|
234 | compilation.updateAsset(
|
---|
235 | cachedFile,
|
---|
236 | assets[cachedFile],
|
---|
237 | assetsInfo[cachedFile]
|
---|
238 | );
|
---|
239 | } else {
|
---|
240 | compilation.emitAsset(
|
---|
241 | cachedFile,
|
---|
242 | assets[cachedFile],
|
---|
243 | assetsInfo[cachedFile]
|
---|
244 | );
|
---|
245 | }
|
---|
246 | /**
|
---|
247 | * Add file to chunk, if not presented there
|
---|
248 | */
|
---|
249 | if (cachedFile !== file) {
|
---|
250 | const chunk = fileToChunk.get(file);
|
---|
251 | if (chunk !== undefined)
|
---|
252 | chunk.auxiliaryFiles.add(cachedFile);
|
---|
253 | }
|
---|
254 | }
|
---|
255 |
|
---|
256 | reportProgress(
|
---|
257 | (0.5 * ++fileIndex) / files.length,
|
---|
258 | file,
|
---|
259 | "restored cached SourceMap"
|
---|
260 | );
|
---|
261 |
|
---|
262 | return callback();
|
---|
263 | }
|
---|
264 |
|
---|
265 | reportProgress(
|
---|
266 | (0.5 * fileIndex) / files.length,
|
---|
267 | file,
|
---|
268 | "generate SourceMap"
|
---|
269 | );
|
---|
270 |
|
---|
271 | /** @type {SourceMapTask | undefined} */
|
---|
272 | const task = getTaskForFile(
|
---|
273 | file,
|
---|
274 | asset.source,
|
---|
275 | asset.info,
|
---|
276 | {
|
---|
277 | module: options.module,
|
---|
278 | columns: options.columns
|
---|
279 | },
|
---|
280 | compilation,
|
---|
281 | cacheItem
|
---|
282 | );
|
---|
283 |
|
---|
284 | if (task) {
|
---|
285 | const modules = task.modules;
|
---|
286 |
|
---|
287 | for (let idx = 0; idx < modules.length; idx++) {
|
---|
288 | const module = modules[idx];
|
---|
289 | if (!moduleToSourceNameMapping.get(module)) {
|
---|
290 | moduleToSourceNameMapping.set(
|
---|
291 | module,
|
---|
292 | ModuleFilenameHelpers.createFilename(
|
---|
293 | module,
|
---|
294 | {
|
---|
295 | moduleFilenameTemplate: moduleFilenameTemplate,
|
---|
296 | namespace: namespace
|
---|
297 | },
|
---|
298 | {
|
---|
299 | requestShortener,
|
---|
300 | chunkGraph
|
---|
301 | }
|
---|
302 | )
|
---|
303 | );
|
---|
304 | }
|
---|
305 | }
|
---|
306 |
|
---|
307 | tasks.push(task);
|
---|
308 | }
|
---|
309 |
|
---|
310 | reportProgress(
|
---|
311 | (0.5 * ++fileIndex) / files.length,
|
---|
312 | file,
|
---|
313 | "generated SourceMap"
|
---|
314 | );
|
---|
315 |
|
---|
316 | callback();
|
---|
317 | });
|
---|
318 | },
|
---|
319 | err => {
|
---|
320 | if (err) {
|
---|
321 | return callback(err);
|
---|
322 | }
|
---|
323 |
|
---|
324 | reportProgress(0.5, "resolve sources");
|
---|
325 | /** @type {Set<string>} */
|
---|
326 | const usedNamesSet = new Set(moduleToSourceNameMapping.values());
|
---|
327 | /** @type {Set<string>} */
|
---|
328 | const conflictDetectionSet = new Set();
|
---|
329 |
|
---|
330 | /**
|
---|
331 | * all modules in defined order (longest identifier first)
|
---|
332 | * @type {Array<string | Module>}
|
---|
333 | */
|
---|
334 | const allModules = Array.from(
|
---|
335 | moduleToSourceNameMapping.keys()
|
---|
336 | ).sort((a, b) => {
|
---|
337 | const ai = typeof a === "string" ? a : a.identifier();
|
---|
338 | const bi = typeof b === "string" ? b : b.identifier();
|
---|
339 | return ai.length - bi.length;
|
---|
340 | });
|
---|
341 |
|
---|
342 | // find modules with conflicting source names
|
---|
343 | for (let idx = 0; idx < allModules.length; idx++) {
|
---|
344 | const module = allModules[idx];
|
---|
345 | let sourceName = moduleToSourceNameMapping.get(module);
|
---|
346 | let hasName = conflictDetectionSet.has(sourceName);
|
---|
347 | if (!hasName) {
|
---|
348 | conflictDetectionSet.add(sourceName);
|
---|
349 | continue;
|
---|
350 | }
|
---|
351 |
|
---|
352 | // try the fallback name first
|
---|
353 | sourceName = ModuleFilenameHelpers.createFilename(
|
---|
354 | module,
|
---|
355 | {
|
---|
356 | moduleFilenameTemplate: fallbackModuleFilenameTemplate,
|
---|
357 | namespace: namespace
|
---|
358 | },
|
---|
359 | {
|
---|
360 | requestShortener,
|
---|
361 | chunkGraph
|
---|
362 | }
|
---|
363 | );
|
---|
364 | hasName = usedNamesSet.has(sourceName);
|
---|
365 | if (!hasName) {
|
---|
366 | moduleToSourceNameMapping.set(module, sourceName);
|
---|
367 | usedNamesSet.add(sourceName);
|
---|
368 | continue;
|
---|
369 | }
|
---|
370 |
|
---|
371 | // otherwise just append stars until we have a valid name
|
---|
372 | while (hasName) {
|
---|
373 | sourceName += "*";
|
---|
374 | hasName = usedNamesSet.has(sourceName);
|
---|
375 | }
|
---|
376 | moduleToSourceNameMapping.set(module, sourceName);
|
---|
377 | usedNamesSet.add(sourceName);
|
---|
378 | }
|
---|
379 |
|
---|
380 | let taskIndex = 0;
|
---|
381 |
|
---|
382 | asyncLib.each(
|
---|
383 | tasks,
|
---|
384 | (task, callback) => {
|
---|
385 | const assets = Object.create(null);
|
---|
386 | const assetsInfo = Object.create(null);
|
---|
387 | const file = task.file;
|
---|
388 | const chunk = fileToChunk.get(file);
|
---|
389 | const sourceMap = task.sourceMap;
|
---|
390 | const source = task.source;
|
---|
391 | const modules = task.modules;
|
---|
392 |
|
---|
393 | reportProgress(
|
---|
394 | 0.5 + (0.5 * taskIndex) / tasks.length,
|
---|
395 | file,
|
---|
396 | "attach SourceMap"
|
---|
397 | );
|
---|
398 |
|
---|
399 | const moduleFilenames = modules.map(m =>
|
---|
400 | moduleToSourceNameMapping.get(m)
|
---|
401 | );
|
---|
402 | sourceMap.sources = moduleFilenames;
|
---|
403 | if (options.noSources) {
|
---|
404 | sourceMap.sourcesContent = undefined;
|
---|
405 | }
|
---|
406 | sourceMap.sourceRoot = options.sourceRoot || "";
|
---|
407 | sourceMap.file = file;
|
---|
408 | const usesContentHash =
|
---|
409 | sourceMapFilename &&
|
---|
410 | /\[contenthash(:\w+)?\]/.test(sourceMapFilename);
|
---|
411 |
|
---|
412 | // If SourceMap and asset uses contenthash, avoid a circular dependency by hiding hash in `file`
|
---|
413 | if (usesContentHash && task.assetInfo.contenthash) {
|
---|
414 | const contenthash = task.assetInfo.contenthash;
|
---|
415 | let pattern;
|
---|
416 | if (Array.isArray(contenthash)) {
|
---|
417 | pattern = contenthash.map(quoteMeta).join("|");
|
---|
418 | } else {
|
---|
419 | pattern = quoteMeta(contenthash);
|
---|
420 | }
|
---|
421 | sourceMap.file = sourceMap.file.replace(
|
---|
422 | new RegExp(pattern, "g"),
|
---|
423 | m => "x".repeat(m.length)
|
---|
424 | );
|
---|
425 | }
|
---|
426 |
|
---|
427 | /** @type {string | false} */
|
---|
428 | let currentSourceMappingURLComment = sourceMappingURLComment;
|
---|
429 | if (
|
---|
430 | currentSourceMappingURLComment !== false &&
|
---|
431 | /\.css($|\?)/i.test(file)
|
---|
432 | ) {
|
---|
433 | currentSourceMappingURLComment =
|
---|
434 | currentSourceMappingURLComment.replace(
|
---|
435 | /^\n\/\/(.*)$/,
|
---|
436 | "\n/*$1*/"
|
---|
437 | );
|
---|
438 | }
|
---|
439 | const sourceMapString = JSON.stringify(sourceMap);
|
---|
440 | if (sourceMapFilename) {
|
---|
441 | let filename = file;
|
---|
442 | const sourceMapContentHash =
|
---|
443 | usesContentHash &&
|
---|
444 | /** @type {string} */ (
|
---|
445 | createHash("md4").update(sourceMapString).digest("hex")
|
---|
446 | );
|
---|
447 | const pathParams = {
|
---|
448 | chunk,
|
---|
449 | filename: options.fileContext
|
---|
450 | ? relative(
|
---|
451 | outputFs,
|
---|
452 | `/${options.fileContext}`,
|
---|
453 | `/${filename}`
|
---|
454 | )
|
---|
455 | : filename,
|
---|
456 | contentHash: sourceMapContentHash
|
---|
457 | };
|
---|
458 | const { path: sourceMapFile, info: sourceMapInfo } =
|
---|
459 | compilation.getPathWithInfo(
|
---|
460 | sourceMapFilename,
|
---|
461 | pathParams
|
---|
462 | );
|
---|
463 | const sourceMapUrl = options.publicPath
|
---|
464 | ? options.publicPath + sourceMapFile
|
---|
465 | : relative(
|
---|
466 | outputFs,
|
---|
467 | dirname(outputFs, `/${file}`),
|
---|
468 | `/${sourceMapFile}`
|
---|
469 | );
|
---|
470 | /** @type {Source} */
|
---|
471 | let asset = new RawSource(source);
|
---|
472 | if (currentSourceMappingURLComment !== false) {
|
---|
473 | // Add source map url to compilation asset, if currentSourceMappingURLComment is set
|
---|
474 | asset = new ConcatSource(
|
---|
475 | asset,
|
---|
476 | compilation.getPath(
|
---|
477 | currentSourceMappingURLComment,
|
---|
478 | Object.assign({ url: sourceMapUrl }, pathParams)
|
---|
479 | )
|
---|
480 | );
|
---|
481 | }
|
---|
482 | const assetInfo = {
|
---|
483 | related: { sourceMap: sourceMapFile }
|
---|
484 | };
|
---|
485 | assets[file] = asset;
|
---|
486 | assetsInfo[file] = assetInfo;
|
---|
487 | compilation.updateAsset(file, asset, assetInfo);
|
---|
488 | // Add source map file to compilation assets and chunk files
|
---|
489 | const sourceMapAsset = new RawSource(sourceMapString);
|
---|
490 | const sourceMapAssetInfo = {
|
---|
491 | ...sourceMapInfo,
|
---|
492 | development: true
|
---|
493 | };
|
---|
494 | assets[sourceMapFile] = sourceMapAsset;
|
---|
495 | assetsInfo[sourceMapFile] = sourceMapAssetInfo;
|
---|
496 | compilation.emitAsset(
|
---|
497 | sourceMapFile,
|
---|
498 | sourceMapAsset,
|
---|
499 | sourceMapAssetInfo
|
---|
500 | );
|
---|
501 | if (chunk !== undefined)
|
---|
502 | chunk.auxiliaryFiles.add(sourceMapFile);
|
---|
503 | } else {
|
---|
504 | if (currentSourceMappingURLComment === false) {
|
---|
505 | throw new Error(
|
---|
506 | "SourceMapDevToolPlugin: append can't be false when no filename is provided"
|
---|
507 | );
|
---|
508 | }
|
---|
509 | /**
|
---|
510 | * Add source map as data url to asset
|
---|
511 | */
|
---|
512 | const asset = new ConcatSource(
|
---|
513 | new RawSource(source),
|
---|
514 | currentSourceMappingURLComment
|
---|
515 | .replace(/\[map\]/g, () => sourceMapString)
|
---|
516 | .replace(
|
---|
517 | /\[url\]/g,
|
---|
518 | () =>
|
---|
519 | `data:application/json;charset=utf-8;base64,${Buffer.from(
|
---|
520 | sourceMapString,
|
---|
521 | "utf-8"
|
---|
522 | ).toString("base64")}`
|
---|
523 | )
|
---|
524 | );
|
---|
525 | assets[file] = asset;
|
---|
526 | assetsInfo[file] = undefined;
|
---|
527 | compilation.updateAsset(file, asset);
|
---|
528 | }
|
---|
529 |
|
---|
530 | task.cacheItem.store({ assets, assetsInfo }, err => {
|
---|
531 | reportProgress(
|
---|
532 | 0.5 + (0.5 * ++taskIndex) / tasks.length,
|
---|
533 | task.file,
|
---|
534 | "attached SourceMap"
|
---|
535 | );
|
---|
536 |
|
---|
537 | if (err) {
|
---|
538 | return callback(err);
|
---|
539 | }
|
---|
540 | callback();
|
---|
541 | });
|
---|
542 | },
|
---|
543 | err => {
|
---|
544 | reportProgress(1.0);
|
---|
545 | callback(err);
|
---|
546 | }
|
---|
547 | );
|
---|
548 | }
|
---|
549 | );
|
---|
550 | }
|
---|
551 | );
|
---|
552 | });
|
---|
553 | }
|
---|
554 | }
|
---|
555 |
|
---|
556 | module.exports = SourceMapDevToolPlugin;
|
---|