1 | "use strict";
|
2 |
|
3 | const path = require("path");
|
4 |
|
5 | const {
|
6 | validate
|
7 | } = require("schema-utils");
|
8 |
|
9 | const serialize = require("serialize-javascript");
|
10 |
|
11 | const normalizePath = require("normalize-path");
|
12 |
|
13 | const globParent = require("glob-parent");
|
14 |
|
15 | const fastGlob = require("fast-glob");
|
16 |
|
17 |
|
18 | const {
|
19 | version
|
20 | } = require("../package.json");
|
21 |
|
22 | const schema = require("./options.json");
|
23 |
|
24 | const {
|
25 | readFile,
|
26 | stat,
|
27 | throttleAll
|
28 | } = require("./utils");
|
29 |
|
30 | const template = /\[\\*([\w:]+)\\*\]/i;
|
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 |
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 | class CopyPlugin {
|
166 | |
167 |
|
168 |
|
169 | constructor(options = {
|
170 | patterns: []
|
171 | }) {
|
172 | validate(
|
173 |
|
174 | schema, options, {
|
175 | name: "Copy Plugin",
|
176 | baseDataPath: "options"
|
177 | });
|
178 | |
179 |
|
180 |
|
181 |
|
182 |
|
183 | this.patterns = options.patterns;
|
184 | |
185 |
|
186 |
|
187 |
|
188 |
|
189 | this.options = options.options || {};
|
190 | }
|
191 | |
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 |
|
198 |
|
199 |
|
200 | static async createSnapshot(compilation, startTime, dependency) {
|
201 |
|
202 | return new Promise((resolve, reject) => {
|
203 | compilation.fileSystemInfo.createSnapshot(startTime, [dependency],
|
204 |
|
205 | undefined,
|
206 | undefined, null, (error, snapshot) => {
|
207 | if (error) {
|
208 | reject(error);
|
209 | return;
|
210 | }
|
211 |
|
212 | resolve(
|
213 |
|
214 | snapshot);
|
215 | });
|
216 | });
|
217 | }
|
218 | |
219 |
|
220 |
|
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 | static async checkSnapshotValid(compilation, snapshot) {
|
227 |
|
228 | return new Promise((resolve, reject) => {
|
229 | compilation.fileSystemInfo.checkSnapshotValid(snapshot, (error, isValid) => {
|
230 | if (error) {
|
231 | reject(error);
|
232 | return;
|
233 | }
|
234 |
|
235 | resolve(isValid);
|
236 | });
|
237 | });
|
238 | }
|
239 | |
240 |
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 | static getContentHash(compiler, compilation, source) {
|
249 | const {
|
250 | outputOptions
|
251 | } = compilation;
|
252 | const {
|
253 | hashDigest,
|
254 | hashDigestLength,
|
255 | hashFunction,
|
256 | hashSalt
|
257 | } = outputOptions;
|
258 | const hash = compiler.webpack.util.createHash(
|
259 |
|
260 | hashFunction);
|
261 |
|
262 | if (hashSalt) {
|
263 | hash.update(hashSalt);
|
264 | }
|
265 |
|
266 | hash.update(source);
|
267 | const fullContentHash = hash.digest(hashDigest);
|
268 | return fullContentHash.toString().slice(0, hashDigestLength);
|
269 | }
|
270 | |
271 |
|
272 |
|
273 |
|
274 |
|
275 |
|
276 |
|
277 |
|
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 | static async runPattern(globby, compiler, compilation, logger, cache, inputPattern, index) {
|
284 | const {
|
285 | RawSource
|
286 | } = compiler.webpack.sources;
|
287 | const pattern = { ...inputPattern
|
288 | };
|
289 | const originalFrom = pattern.from;
|
290 | const normalizedOriginalFrom = path.normalize(originalFrom);
|
291 | logger.log(`starting to process a pattern from '${normalizedOriginalFrom}' using '${pattern.context}' context`);
|
292 | let absoluteFrom;
|
293 |
|
294 | if (path.isAbsolute(normalizedOriginalFrom)) {
|
295 | absoluteFrom = normalizedOriginalFrom;
|
296 | } else {
|
297 | absoluteFrom = path.resolve(pattern.context, normalizedOriginalFrom);
|
298 | }
|
299 |
|
300 | logger.debug(`getting stats for '${absoluteFrom}'...`);
|
301 | const {
|
302 | inputFileSystem
|
303 | } = compiler;
|
304 | let stats;
|
305 |
|
306 | try {
|
307 | stats = await stat(inputFileSystem, absoluteFrom);
|
308 | } catch (error) {
|
309 | }
|
310 | |
311 |
|
312 |
|
313 |
|
314 |
|
315 | let fromType;
|
316 |
|
317 | if (stats) {
|
318 | if (stats.isDirectory()) {
|
319 | fromType = "dir";
|
320 | logger.debug(`determined '${absoluteFrom}' is a directory`);
|
321 | } else if (stats.isFile()) {
|
322 | fromType = "file";
|
323 | logger.debug(`determined '${absoluteFrom}' is a file`);
|
324 | } else {
|
325 |
|
326 | fromType = "glob";
|
327 | logger.debug(`determined '${absoluteFrom}' is unknown`);
|
328 | }
|
329 | } else {
|
330 | fromType = "glob";
|
331 | logger.debug(`determined '${absoluteFrom}' is a glob`);
|
332 | }
|
333 |
|
334 |
|
335 |
|
336 | const globOptions = { ...{
|
337 | followSymbolicLinks: true
|
338 | },
|
339 | ...(pattern.globOptions || {}),
|
340 | ...{
|
341 | cwd: pattern.context,
|
342 | objectMode: true
|
343 | }
|
344 | };
|
345 |
|
346 | globOptions.fs = inputFileSystem;
|
347 | let glob;
|
348 |
|
349 | switch (fromType) {
|
350 | case "dir":
|
351 | compilation.contextDependencies.add(absoluteFrom);
|
352 | logger.debug(`added '${absoluteFrom}' as a context dependency`);
|
353 | pattern.context = absoluteFrom;
|
354 | glob = path.posix.join(fastGlob.escapePath(normalizePath(path.resolve(absoluteFrom))), "**/*");
|
355 | absoluteFrom = path.join(absoluteFrom, "**/*");
|
356 |
|
357 | if (typeof globOptions.dot === "undefined") {
|
358 | globOptions.dot = true;
|
359 | }
|
360 |
|
361 | break;
|
362 |
|
363 | case "file":
|
364 | compilation.fileDependencies.add(absoluteFrom);
|
365 | logger.debug(`added '${absoluteFrom}' as a file dependency`);
|
366 | pattern.context = path.dirname(absoluteFrom);
|
367 | glob = fastGlob.escapePath(normalizePath(path.resolve(absoluteFrom)));
|
368 |
|
369 | if (typeof globOptions.dot === "undefined") {
|
370 | globOptions.dot = true;
|
371 | }
|
372 |
|
373 | break;
|
374 |
|
375 | case "glob":
|
376 | default:
|
377 | {
|
378 | const contextDependencies = path.normalize(globParent(absoluteFrom));
|
379 | compilation.contextDependencies.add(contextDependencies);
|
380 | logger.debug(`added '${contextDependencies}' as a context dependency`);
|
381 | glob = path.isAbsolute(originalFrom) ? originalFrom : path.posix.join(fastGlob.escapePath(normalizePath(path.resolve(pattern.context))), originalFrom);
|
382 | }
|
383 | }
|
384 |
|
385 | logger.log(`begin globbing '${glob}'...`);
|
386 | |
387 |
|
388 |
|
389 |
|
390 | let globEntries;
|
391 |
|
392 | try {
|
393 | globEntries = await globby(glob, globOptions);
|
394 | } catch (error) {
|
395 | compilation.errors.push(
|
396 |
|
397 | error);
|
398 | return;
|
399 | }
|
400 |
|
401 | if (globEntries.length === 0) {
|
402 | if (pattern.noErrorOnMissing) {
|
403 | logger.log(`finished to process a pattern from '${normalizedOriginalFrom}' using '${pattern.context}' context to '${pattern.to}'`);
|
404 | return;
|
405 | }
|
406 |
|
407 | const missingError = new Error(`unable to locate '${glob}' glob`);
|
408 | compilation.errors.push(
|
409 |
|
410 | missingError);
|
411 | return;
|
412 | }
|
413 | |
414 |
|
415 |
|
416 |
|
417 |
|
418 | let copiedResult;
|
419 |
|
420 | try {
|
421 | copiedResult = await Promise.all(globEntries.map(
|
422 | |
423 |
|
424 |
|
425 |
|
426 | async globEntry => {
|
427 |
|
428 | if (!globEntry.dirent.isFile()) {
|
429 | return;
|
430 | }
|
431 |
|
432 | if (pattern.filter) {
|
433 | let isFiltered;
|
434 |
|
435 | try {
|
436 | isFiltered = await pattern.filter(globEntry.path);
|
437 | } catch (error) {
|
438 | compilation.errors.push(
|
439 |
|
440 | error);
|
441 | return;
|
442 | }
|
443 |
|
444 | if (!isFiltered) {
|
445 | logger.log(`skip '${globEntry.path}', because it was filtered`);
|
446 | return;
|
447 | }
|
448 | }
|
449 |
|
450 | const from = globEntry.path;
|
451 | logger.debug(`found '${from}'`);
|
452 |
|
453 | const absoluteFilename = path.resolve(pattern.context, from);
|
454 | const to = typeof pattern.to === "function" ? await pattern.to({
|
455 | context: pattern.context,
|
456 | absoluteFilename
|
457 | }) : path.normalize(typeof pattern.to !== "undefined" ? pattern.to : "");
|
458 | const toType = pattern.toType ? pattern.toType : template.test(to) ? "template" : path.extname(to) === "" || to.slice(-1) === path.sep ? "dir" : "file";
|
459 | logger.log(`'to' option '${to}' determinated as '${toType}'`);
|
460 | const relativeFrom = path.relative(pattern.context, absoluteFilename);
|
461 | let filename = toType === "dir" ? path.join(to, relativeFrom) : to;
|
462 |
|
463 | if (path.isAbsolute(filename)) {
|
464 | filename = path.relative(
|
465 |
|
466 | compiler.options.output.path, filename);
|
467 | }
|
468 |
|
469 | logger.log(`determined that '${from}' should write to '${filename}'`);
|
470 | const sourceFilename = normalizePath(path.relative(compiler.context, absoluteFilename));
|
471 |
|
472 | if (fromType === "dir" || fromType === "glob") {
|
473 | compilation.fileDependencies.add(absoluteFilename);
|
474 | logger.debug(`added '${absoluteFilename}' as a file dependency`);
|
475 | }
|
476 |
|
477 | let cacheEntry;
|
478 | logger.debug(`getting cache for '${absoluteFilename}'...`);
|
479 |
|
480 | try {
|
481 | cacheEntry = await cache.getPromise(`${sourceFilename}|${index}`, null);
|
482 | } catch (error) {
|
483 | compilation.errors.push(
|
484 |
|
485 | error);
|
486 | return;
|
487 | }
|
488 | |
489 |
|
490 |
|
491 |
|
492 |
|
493 | let source;
|
494 |
|
495 | if (cacheEntry) {
|
496 | logger.debug(`found cache for '${absoluteFilename}'...`);
|
497 | let isValidSnapshot;
|
498 | logger.debug(`checking snapshot on valid for '${absoluteFilename}'...`);
|
499 |
|
500 | try {
|
501 | isValidSnapshot = await CopyPlugin.checkSnapshotValid(compilation, cacheEntry.snapshot);
|
502 | } catch (error) {
|
503 | compilation.errors.push(
|
504 |
|
505 | error);
|
506 | return;
|
507 | }
|
508 |
|
509 | if (isValidSnapshot) {
|
510 | logger.debug(`snapshot for '${absoluteFilename}' is valid`);
|
511 | ({
|
512 | source
|
513 | } = cacheEntry);
|
514 | } else {
|
515 | logger.debug(`snapshot for '${absoluteFilename}' is invalid`);
|
516 | }
|
517 | } else {
|
518 | logger.debug(`missed cache for '${absoluteFilename}'`);
|
519 | }
|
520 |
|
521 | if (!source) {
|
522 | const startTime = Date.now();
|
523 | logger.debug(`reading '${absoluteFilename}'...`);
|
524 | let data;
|
525 |
|
526 | try {
|
527 | data = await readFile(inputFileSystem, absoluteFilename);
|
528 | } catch (error) {
|
529 | compilation.errors.push(
|
530 |
|
531 | error);
|
532 | return;
|
533 | }
|
534 |
|
535 | logger.debug(`read '${absoluteFilename}'`);
|
536 | source = new RawSource(data);
|
537 | let snapshot;
|
538 | logger.debug(`creating snapshot for '${absoluteFilename}'...`);
|
539 |
|
540 | try {
|
541 | snapshot = await CopyPlugin.createSnapshot(compilation, startTime, absoluteFilename);
|
542 | } catch (error) {
|
543 | compilation.errors.push(
|
544 |
|
545 | error);
|
546 | return;
|
547 | }
|
548 |
|
549 | if (snapshot) {
|
550 | logger.debug(`created snapshot for '${absoluteFilename}'`);
|
551 | logger.debug(`storing cache for '${absoluteFilename}'...`);
|
552 |
|
553 | try {
|
554 | await cache.storePromise(`${sourceFilename}|${index}`, null, {
|
555 | source,
|
556 | snapshot
|
557 | });
|
558 | } catch (error) {
|
559 | compilation.errors.push(
|
560 |
|
561 | error);
|
562 | return;
|
563 | }
|
564 |
|
565 | logger.debug(`stored cache for '${absoluteFilename}'`);
|
566 | }
|
567 | }
|
568 |
|
569 | if (pattern.transform) {
|
570 | |
571 |
|
572 |
|
573 | const transformObj = typeof pattern.transform === "function" ? {
|
574 | transformer: pattern.transform
|
575 | } : pattern.transform;
|
576 |
|
577 | if (transformObj.transformer) {
|
578 | logger.log(`transforming content for '${absoluteFilename}'...`);
|
579 | const buffer = source.buffer();
|
580 |
|
581 | if (transformObj.cache) {
|
582 |
|
583 | const hasher = compiler.webpack && compiler.webpack.util && compiler.webpack.util.createHash ? compiler.webpack.util.createHash("xxhash64") :
|
584 | require("crypto").createHash("md4");
|
585 | const defaultCacheKeys = {
|
586 | version,
|
587 | sourceFilename,
|
588 | transform: transformObj.transformer,
|
589 | contentHash: hasher.update(buffer).digest("hex"),
|
590 | index
|
591 | };
|
592 | const cacheKeys = `transform|${serialize(typeof transformObj.cache === "boolean" ? defaultCacheKeys : typeof transformObj.cache.keys === "function" ? await transformObj.cache.keys(defaultCacheKeys, absoluteFilename) : { ...defaultCacheKeys,
|
593 | ...transformObj.cache.keys
|
594 | })}`;
|
595 | logger.debug(`getting transformation cache for '${absoluteFilename}'...`);
|
596 | const cacheItem = cache.getItemCache(cacheKeys, cache.getLazyHashedEtag(source));
|
597 | source = await cacheItem.getPromise();
|
598 | logger.debug(source ? `found transformation cache for '${absoluteFilename}'` : `no transformation cache for '${absoluteFilename}'`);
|
599 |
|
600 | if (!source) {
|
601 | const transformed = await transformObj.transformer(buffer, absoluteFilename);
|
602 | source = new RawSource(transformed);
|
603 | logger.debug(`caching transformation for '${absoluteFilename}'...`);
|
604 | await cacheItem.storePromise(source);
|
605 | logger.debug(`cached transformation for '${absoluteFilename}'`);
|
606 | }
|
607 | } else {
|
608 | source = new RawSource(await transformObj.transformer(buffer, absoluteFilename));
|
609 | }
|
610 | }
|
611 | }
|
612 |
|
613 | let info = typeof pattern.info === "undefined" ? {} : typeof pattern.info === "function" ? pattern.info({
|
614 | absoluteFilename,
|
615 | sourceFilename,
|
616 | filename,
|
617 | toType
|
618 | }) || {} : pattern.info || {};
|
619 |
|
620 | if (toType === "template") {
|
621 | logger.log(`interpolating template '${filename}' for '${sourceFilename}'...`);
|
622 | const contentHash = CopyPlugin.getContentHash(compiler, compilation, source.buffer());
|
623 | const ext = path.extname(sourceFilename);
|
624 | const base = path.basename(sourceFilename);
|
625 | const name = base.slice(0, base.length - ext.length);
|
626 | const data = {
|
627 | filename: normalizePath(path.relative(pattern.context, absoluteFilename)),
|
628 | contentHash,
|
629 | chunk: {
|
630 | name,
|
631 | id:
|
632 |
|
633 | sourceFilename,
|
634 | hash: contentHash
|
635 | }
|
636 | };
|
637 | const {
|
638 | path: interpolatedFilename,
|
639 | info: assetInfo
|
640 | } = compilation.getPathWithInfo(normalizePath(filename), data);
|
641 | info = { ...info,
|
642 | ...assetInfo
|
643 | };
|
644 | filename = interpolatedFilename;
|
645 | logger.log(`interpolated template '${filename}' for '${sourceFilename}'`);
|
646 | } else {
|
647 | filename = normalizePath(filename);
|
648 | }
|
649 |
|
650 |
|
651 | return {
|
652 | sourceFilename,
|
653 | absoluteFilename,
|
654 | filename,
|
655 | source,
|
656 | info,
|
657 | force: pattern.force
|
658 | };
|
659 | }));
|
660 | } catch (error) {
|
661 | compilation.errors.push(
|
662 |
|
663 | error);
|
664 | return;
|
665 | }
|
666 |
|
667 | if (copiedResult.length === 0) {
|
668 | if (pattern.noErrorOnMissing) {
|
669 | logger.log(`finished to process a pattern from '${normalizedOriginalFrom}' using '${pattern.context}' context to '${pattern.to}'`);
|
670 | return;
|
671 | }
|
672 |
|
673 | const missingError = new Error(`unable to locate '${glob}' glob after filtering paths`);
|
674 | compilation.errors.push(
|
675 |
|
676 | missingError);
|
677 | return;
|
678 | }
|
679 |
|
680 | logger.log(`finished to process a pattern from '${normalizedOriginalFrom}' using '${pattern.context}' context`);
|
681 |
|
682 | return copiedResult;
|
683 | }
|
684 | |
685 |
|
686 |
|
687 |
|
688 |
|
689 | apply(compiler) {
|
690 | const pluginName = this.constructor.name;
|
691 | compiler.hooks.thisCompilation.tap(pluginName, compilation => {
|
692 | const logger = compilation.getLogger("copy-webpack-plugin");
|
693 | const cache = compilation.getCache("CopyWebpackPlugin");
|
694 | |
695 |
|
696 |
|
697 |
|
698 | let globby;
|
699 | compilation.hooks.processAssets.tapAsync({
|
700 | name: "copy-webpack-plugin",
|
701 | stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
|
702 | }, async (unusedAssets, callback) => {
|
703 | if (typeof globby === "undefined") {
|
704 | try {
|
705 |
|
706 | ({
|
707 | globby
|
708 | } = await import("globby"));
|
709 | } catch (error) {
|
710 | callback(
|
711 |
|
712 | error);
|
713 | return;
|
714 | }
|
715 | }
|
716 |
|
717 | logger.log("starting to add additional assets...");
|
718 | const copiedResultMap = new Map();
|
719 | |
720 |
|
721 |
|
722 |
|
723 | const scheduledTasks = [];
|
724 | this.patterns.map(
|
725 | |
726 |
|
727 |
|
728 |
|
729 |
|
730 | (item, index) => scheduledTasks.push(async () => {
|
731 | |
732 |
|
733 |
|
734 | const normalizedPattern = typeof item === "string" ? {
|
735 | from: item
|
736 | } : { ...item
|
737 | };
|
738 | const context = typeof normalizedPattern.context === "undefined" ? compiler.context : path.isAbsolute(normalizedPattern.context) ? normalizedPattern.context : path.join(compiler.context, normalizedPattern.context);
|
739 | normalizedPattern.context = context;
|
740 | |
741 |
|
742 |
|
743 |
|
744 | let copiedResult;
|
745 |
|
746 | try {
|
747 | copiedResult = await CopyPlugin.runPattern(globby, compiler, compilation, logger, cache,
|
748 |
|
749 | normalizedPattern, index);
|
750 | } catch (error) {
|
751 | compilation.errors.push(
|
752 |
|
753 | error);
|
754 | return;
|
755 | }
|
756 |
|
757 | if (!copiedResult) {
|
758 | return;
|
759 | }
|
760 | |
761 |
|
762 |
|
763 |
|
764 |
|
765 | let filteredCopiedResult = copiedResult.filter(
|
766 | |
767 |
|
768 |
|
769 |
|
770 | result => Boolean(result));
|
771 |
|
772 | if (typeof normalizedPattern.transformAll !== "undefined") {
|
773 | if (typeof normalizedPattern.to === "undefined") {
|
774 | compilation.errors.push(
|
775 |
|
776 | new Error(`Invalid "pattern.to" for the "pattern.from": "${normalizedPattern.from}" and "pattern.transformAll" function. The "to" option must be specified.`));
|
777 | return;
|
778 | }
|
779 |
|
780 | filteredCopiedResult.sort((a, b) => a.absoluteFilename > b.absoluteFilename ? 1 : a.absoluteFilename < b.absoluteFilename ? -1 : 0);
|
781 | const mergedEtag = filteredCopiedResult.length === 1 ? cache.getLazyHashedEtag(filteredCopiedResult[0].source) : filteredCopiedResult.reduce(
|
782 | |
783 |
|
784 |
|
785 |
|
786 |
|
787 |
|
788 |
|
789 | (accumulator, asset, i) => {
|
790 |
|
791 | accumulator = cache.mergeEtags(i === 1 ? cache.getLazyHashedEtag(
|
792 |
|
793 | accumulator.source) : accumulator, cache.getLazyHashedEtag(asset.source));
|
794 | return accumulator;
|
795 | });
|
796 | const cacheItem = cache.getItemCache(`transformAll|${serialize({
|
797 | version,
|
798 | from: normalizedPattern.from,
|
799 | to: normalizedPattern.to,
|
800 | transformAll: normalizedPattern.transformAll
|
801 | })}`, mergedEtag);
|
802 | let transformedAsset = await cacheItem.getPromise();
|
803 |
|
804 | if (!transformedAsset) {
|
805 | transformedAsset = {
|
806 | filename: normalizedPattern.to
|
807 | };
|
808 |
|
809 | try {
|
810 | transformedAsset.data = await normalizedPattern.transformAll(filteredCopiedResult.map(asset => {
|
811 | return {
|
812 | data: asset.source.buffer(),
|
813 | sourceFilename: asset.sourceFilename,
|
814 | absoluteFilename: asset.absoluteFilename
|
815 | };
|
816 | }));
|
817 | } catch (error) {
|
818 | compilation.errors.push(
|
819 |
|
820 | error);
|
821 | return;
|
822 | }
|
823 |
|
824 | const filename = typeof normalizedPattern.to === "function" ? await normalizedPattern.to({
|
825 | context
|
826 | }) : normalizedPattern.to;
|
827 |
|
828 | if (template.test(filename)) {
|
829 | const contentHash = CopyPlugin.getContentHash(compiler, compilation, transformedAsset.data);
|
830 | const {
|
831 | path: interpolatedFilename,
|
832 | info: assetInfo
|
833 | } = compilation.getPathWithInfo(normalizePath(filename), {
|
834 | contentHash,
|
835 | chunk: {
|
836 | id: "unknown-copied-asset",
|
837 | hash: contentHash
|
838 | }
|
839 | });
|
840 | transformedAsset.filename = interpolatedFilename;
|
841 | transformedAsset.info = assetInfo;
|
842 | }
|
843 |
|
844 | const {
|
845 | RawSource
|
846 | } = compiler.webpack.sources;
|
847 | transformedAsset.source = new RawSource(transformedAsset.data);
|
848 | transformedAsset.force = normalizedPattern.force;
|
849 | await cacheItem.storePromise(transformedAsset);
|
850 | }
|
851 |
|
852 | filteredCopiedResult = [transformedAsset];
|
853 | }
|
854 |
|
855 | const priority = normalizedPattern.priority || 0;
|
856 |
|
857 | if (!copiedResultMap.has(priority)) {
|
858 | copiedResultMap.set(priority, []);
|
859 | }
|
860 |
|
861 | copiedResultMap.get(priority).push(...filteredCopiedResult);
|
862 | }));
|
863 | await throttleAll(this.options.concurrency || 100, scheduledTasks);
|
864 | const copiedResult = [...copiedResultMap.entries()].sort((a, b) => a[0] - b[0]);
|
865 |
|
866 |
|
867 | copiedResult.reduce((acc, val) => acc.concat(val[1]), []).filter(Boolean).forEach(
|
868 | |
869 |
|
870 |
|
871 |
|
872 | result => {
|
873 | const {
|
874 | absoluteFilename,
|
875 | sourceFilename,
|
876 | filename,
|
877 | source,
|
878 | force
|
879 | } = result;
|
880 | const existingAsset = compilation.getAsset(filename);
|
881 |
|
882 | if (existingAsset) {
|
883 | if (force) {
|
884 | const info = {
|
885 | copied: true,
|
886 | sourceFilename
|
887 | };
|
888 | logger.log(`force updating '${filename}' from '${absoluteFilename}' to compilation assets, because it already exists...`);
|
889 | compilation.updateAsset(filename, source, { ...info,
|
890 | ...result.info
|
891 | });
|
892 | logger.log(`force updated '${filename}' from '${absoluteFilename}' to compilation assets, because it already exists`);
|
893 | return;
|
894 | }
|
895 |
|
896 | logger.log(`skip adding '${filename}' from '${absoluteFilename}' to compilation assets, because it already exists`);
|
897 | return;
|
898 | }
|
899 |
|
900 | const info = {
|
901 | copied: true,
|
902 | sourceFilename
|
903 | };
|
904 | logger.log(`writing '${filename}' from '${absoluteFilename}' to compilation assets...`);
|
905 | compilation.emitAsset(filename, source, { ...info,
|
906 | ...result.info
|
907 | });
|
908 | logger.log(`written '${filename}' from '${absoluteFilename}' to compilation assets`);
|
909 | });
|
910 | logger.log("finished to adding additional assets");
|
911 | callback();
|
912 | });
|
913 |
|
914 | if (compilation.hooks.statsPrinter) {
|
915 | compilation.hooks.statsPrinter.tap(pluginName, stats => {
|
916 | stats.hooks.print.for("asset.info.copied").tap("copy-webpack-plugin", (copied, {
|
917 | green,
|
918 | formatFlag
|
919 | }) => copied ?
|
920 |
|
921 | green(
|
922 |
|
923 | formatFlag("copied")) : "");
|
924 | });
|
925 | }
|
926 | });
|
927 | }
|
928 |
|
929 | }
|
930 |
|
931 | module.exports = CopyPlugin; |
\ | No newline at end of file |