@depup/webpack 5.110.3-depup.0 → 5.111.1-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/README.md +7 -8
  2. package/changes.json +7 -11
  3. package/lib/APIPlugin.js +26 -11
  4. package/lib/AutomaticPrefetchPlugin.js +1 -1
  5. package/lib/Cache.js +8 -3
  6. package/lib/CacheFacade.js +1 -1
  7. package/lib/CircularModulesPlugin.js +157 -55
  8. package/lib/CleanPlugin.js +2 -2
  9. package/lib/CompatibilityPlugin.js +3 -4
  10. package/lib/Compilation.js +336 -337
  11. package/lib/Compiler.js +213 -51
  12. package/lib/ConstPlugin.js +16 -132
  13. package/lib/CopyPlugin.js +1054 -0
  14. package/lib/DefinePlugin.js +24 -8
  15. package/lib/DependencyTemplate.js +2 -1
  16. package/lib/DotenvPlugin.js +24 -28
  17. package/lib/ErrorHelpers.js +113 -0
  18. package/lib/ExportsInfo.js +20 -16
  19. package/lib/ExternalModule.js +47 -22
  20. package/lib/FileSystemInfo.js +17 -14
  21. package/lib/FlagDependencyExportsPlugin.js +53 -34
  22. package/lib/FlagDependencyUsagePlugin.js +9 -13
  23. package/lib/Generator.js +24 -2
  24. package/lib/HotModuleReplacementPlugin.js +55 -7
  25. package/lib/IgnorePlugin.js +1 -1
  26. package/lib/JavascriptMetaInfoPlugin.js +5 -3
  27. package/lib/LoaderOptionsPlugin.js +1 -1
  28. package/lib/MainTemplate.js +5 -1
  29. package/lib/Module.js +19 -10
  30. package/lib/ModuleGraph.js +6 -9
  31. package/lib/MultiCompiler.js +7 -17
  32. package/lib/MultiWatching.js +1 -1
  33. package/lib/NormalModule.js +135 -50
  34. package/lib/NormalModuleFactory.js +1 -1
  35. package/lib/RuntimeGlobals.js +21 -7
  36. package/lib/RuntimeModule.js +28 -12
  37. package/lib/RuntimePlugin.js +30 -3
  38. package/lib/RuntimeTemplate.js +813 -518
  39. package/lib/SourceMapDevToolPlugin.js +54 -104
  40. package/lib/TemplatedPathPlugin.js +16 -59
  41. package/lib/Watching.js +9 -0
  42. package/lib/WebpackOptionsApply.js +84 -91
  43. package/lib/asset/AssetBytesGenerator.js +7 -1
  44. package/lib/asset/AssetGenerator.js +50 -24
  45. package/lib/asset/AssetModule.js +3 -0
  46. package/lib/asset/AssetSourceGenerator.js +7 -1
  47. package/lib/asset/WebManifestParser.js +7 -5
  48. package/lib/buildChunkGraph.js +11 -15
  49. package/lib/cache/AddBuildDependenciesPlugin.js +33 -4
  50. package/lib/cache/IdleFileCachePlugin.js +2 -2
  51. package/lib/cache/MemoryCachePlugin.js +4 -4
  52. package/lib/cache/MemoryWithGcCachePlugin.js +7 -8
  53. package/lib/cache/PackFileCacheStrategy.js +17 -19
  54. package/lib/cache/getLazyHashedEtag.js +3 -5
  55. package/lib/config/browserslistTargetHandler.js +17 -4
  56. package/lib/config/defaults.js +138 -128
  57. package/lib/config/getClaimedAssetTypes.js +10 -11
  58. package/lib/config/normalization.js +82 -12
  59. package/lib/config/target.js +13 -4
  60. package/lib/container/ContainerReferencePlugin.js +6 -0
  61. package/lib/container/RemoteRuntimeModule.js +9 -0
  62. package/lib/container/declaredRemotes.js +39 -0
  63. package/lib/{ContextExclusionPlugin.js → context/ContextExclusionPlugin.js} +1 -1
  64. package/lib/{ContextModule.js → context/ContextModule.js} +54 -115
  65. package/lib/{ContextModuleFactory.js → context/ContextModuleFactory.js} +19 -21
  66. package/lib/{ContextReplacementPlugin.js → context/ContextReplacementPlugin.js} +4 -4
  67. package/lib/css/CssGenerator.js +27 -11
  68. package/lib/css/CssInjectStyleRuntimeModule.js +9 -0
  69. package/lib/css/CssLoadingRuntimeModule.js +94 -36
  70. package/lib/css/CssModule.js +1 -1
  71. package/lib/css/CssModulesPlugin.js +7 -6
  72. package/lib/css/CssParser.js +162 -31
  73. package/lib/css/CssServerStylesRuntimeModule.js +9 -0
  74. package/lib/css/cssMinify.js +43 -32
  75. package/lib/css/data.js +3514 -869
  76. package/lib/css/syntax.js +6370 -1220
  77. package/lib/debug/ProfilingPlugin.js +7 -4
  78. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -3
  79. package/lib/dependencies/AMDRuntimeModules.js +18 -0
  80. package/lib/dependencies/CommonJsExportRequireDependency.js +13 -12
  81. package/lib/dependencies/CommonJsExportsParserPlugin.js +12 -13
  82. package/lib/dependencies/CommonJsImportsParserPlugin.js +5 -5
  83. package/lib/dependencies/CommonJsPlugin.js +19 -3
  84. package/lib/dependencies/CommonJsRequireDependency.js +14 -5
  85. package/lib/dependencies/CommonJsSelfReferenceDependency.js +2 -4
  86. package/lib/dependencies/ContextDependency.js +1 -1
  87. package/lib/dependencies/ContextDependencyHelpers.js +3 -9
  88. package/lib/dependencies/ContextElementDependency.js +1 -1
  89. package/lib/dependencies/CssIcssExportDependency.js +13 -9
  90. package/lib/dependencies/CssIcssImportDependency.js +3 -6
  91. package/lib/dependencies/CssUrlDependency.js +3 -5
  92. package/lib/dependencies/HarmonyCompatibilityDependency.js +67 -22
  93. package/lib/dependencies/HarmonyDetectionParserPlugin.js +3 -4
  94. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +10 -7
  95. package/lib/dependencies/HarmonyExportExpressionDependency.js +47 -33
  96. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +74 -17
  97. package/lib/dependencies/HarmonyExportInitFragment.js +110 -23
  98. package/lib/dependencies/HarmonyExportSpecifierDependency.js +25 -58
  99. package/lib/dependencies/HarmonyImportBareSideEffectDependency.js +59 -0
  100. package/lib/dependencies/HarmonyImportDependency.js +6 -1
  101. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +111 -17
  102. package/lib/dependencies/HarmonyImportSideEffectDependency.js +3 -4
  103. package/lib/dependencies/HarmonyImportSpecifierDependency.js +40 -3
  104. package/lib/dependencies/HarmonyModulesPlugin.js +20 -0
  105. package/lib/dependencies/HtmlEntryDependency.js +124 -102
  106. package/lib/dependencies/HtmlInlineScriptDependency.js +3 -4
  107. package/lib/dependencies/HtmlInlineStyleDependency.js +3 -4
  108. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +1 -1
  109. package/lib/dependencies/ImportMetaGlobHelpers.js +1 -1
  110. package/lib/dependencies/ImportMetaMainDependency.js +104 -0
  111. package/lib/dependencies/ImportMetaPlugin.js +67 -25
  112. package/lib/dependencies/ImportParserPlugin.js +1 -1
  113. package/lib/dependencies/LoaderPlugin.js +26 -1
  114. package/lib/dependencies/RequireContextDependencyParserPlugin.js +1 -1
  115. package/lib/dependencies/SystemRuntimeModule.js +9 -0
  116. package/lib/dependencies/URLContextDependency.js +1 -1
  117. package/lib/dependencies/URLDependency.js +6 -5
  118. package/lib/dependencies/WorkerAndWorkletPlugin.js +15 -21
  119. package/lib/dependencies/WorkerDependency.js +7 -7
  120. package/lib/dependencies/WorkletDependency.js +7 -11
  121. package/lib/dll/DelegatedModule.js +18 -1
  122. package/lib/dll/DllReferencePlugin.js +11 -8
  123. package/lib/dll/LibManifestPlugin.js +1 -1
  124. package/lib/errors/AnalyzableBailoutsWarning.js +41 -0
  125. package/lib/errors/DuplicateModulesWarning.js +12 -7
  126. package/lib/errors/EmptyCopyPatternWarning.js +26 -0
  127. package/lib/errors/ExternalStarReexportError.js +44 -0
  128. package/lib/errors/HookWebpackError.js +3 -2
  129. package/lib/errors/MissingSourceMapsWarning.js +36 -0
  130. package/lib/errors/ModuleParseError.js +2 -1
  131. package/lib/errors/UnusedAssetsWarning.js +36 -0
  132. package/lib/errors/UnusedFederationWarning.js +35 -0
  133. package/lib/errors/UnusedModulesWarning.js +38 -0
  134. package/lib/esm/ExportWebpackRequireRuntimeModule.js +9 -0
  135. package/lib/esm/ModuleChunkFormatPlugin.js +28 -3
  136. package/lib/esm/ModuleChunkLoadingPlugin.js +30 -11
  137. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +92 -60
  138. package/lib/hmr/HotModuleReplacement.runtime.js +8 -2
  139. package/lib/hmr/HotModuleReplacementRuntimeModule.js +9 -0
  140. package/lib/hmr/LazyCompilationPlugin.js +3 -5
  141. package/lib/html/HtmlGenerator.js +72 -87
  142. package/lib/html/HtmlModulesPlugin.js +418 -200
  143. package/lib/html/HtmlParser.js +110 -147
  144. package/lib/html/builtinEmbeddedRenderer.js +107 -0
  145. package/lib/html/data.js +142 -7
  146. package/lib/html/htmlMinify.js +92 -122
  147. package/lib/html/syntax.js +1618 -1391
  148. package/lib/ids/HashedModuleIdsPlugin.js +2 -1
  149. package/lib/index.js +49 -3
  150. package/lib/javascript/ChunkFormatHelpers.js +4 -3
  151. package/lib/javascript/JavascriptGenerator.js +8 -1
  152. package/lib/javascript/JavascriptModule.js +1 -0
  153. package/lib/javascript/JavascriptModulesPlugin.js +73 -72
  154. package/lib/javascript/JavascriptParser.js +1970 -900
  155. package/lib/javascript/StartupHelpers.js +27 -3
  156. package/lib/javascript/data.js +172 -0
  157. package/lib/javascript/grammar.js +5546 -0
  158. package/lib/javascript/parser.js +1500 -0
  159. package/lib/javascript/regexp.js +1762 -0
  160. package/lib/javascript/regexpData.js +51 -0
  161. package/lib/javascript/syntax.js +9061 -5262
  162. package/lib/json/JsonGenerator.js +7 -1
  163. package/lib/library/ModuleLibraryPlugin.js +171 -132
  164. package/lib/loaders/LoaderRunner.js +137 -9
  165. package/lib/node/NodeWatchFileSystem.js +3 -9
  166. package/lib/node/ReadFileChunkLoadingRuntimeModule.js +22 -0
  167. package/lib/node/RequireChunkLoadingRuntimeModule.js +22 -0
  168. package/lib/optimize/AggressiveMergingPlugin.js +3 -5
  169. package/lib/optimize/ConcatenatedModule.js +178 -120
  170. package/lib/optimize/ConstExportsPlugin.js +50 -39
  171. package/lib/optimize/FlagIncludedChunksPlugin.js +3 -11
  172. package/lib/optimize/InlineExports.js +20 -123
  173. package/lib/optimize/InnerGraphPlugin.js +7 -13
  174. package/lib/optimize/LimitChunkCountPlugin.js +5 -8
  175. package/lib/optimize/MangleExportsPlugin.js +13 -4
  176. package/lib/optimize/ModuleConcatenationPlugin.js +27 -5
  177. package/lib/optimize/RealContentHashPlugin.js +68 -9
  178. package/lib/optimize/SideEffectsFlagPlugin.js +24 -28
  179. package/lib/optimize/SplitChunksPlugin.js +2 -4
  180. package/lib/performance/AnalyzableBailoutsPlugin.js +115 -0
  181. package/lib/performance/BroadContextsPlugin.js +1 -1
  182. package/lib/performance/CacheEffectivenessPlugin.js +7 -7
  183. package/lib/performance/DuplicateModulesPlugin.js +57 -13
  184. package/lib/performance/EvalUsagePlugin.js +5 -2
  185. package/lib/performance/MissingSourceMapsPlugin.js +115 -0
  186. package/lib/performance/PureAnnotationsPlugin.js +6 -3
  187. package/lib/performance/SizeLimitsPlugin.js +2 -4
  188. package/lib/performance/TopLevelThisPlugin.js +5 -4
  189. package/lib/performance/UnusedAssetsPlugin.js +109 -0
  190. package/lib/performance/UnusedFederationPlugin.js +147 -0
  191. package/lib/performance/UnusedModulesPlugin.js +141 -0
  192. package/lib/performance/UnusedReexportsPlugin.js +1 -55
  193. package/lib/performance/isOnlyReexported.js +65 -0
  194. package/lib/prefetch/ChunkPrefetchFunctionRuntimeModule.js +25 -5
  195. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +8 -8
  196. package/lib/prefetch/ChunkPrefetchStartupRuntimeModule.js +9 -0
  197. package/lib/prefetch/ChunkPrefetchTriggerRuntimeModule.js +9 -0
  198. package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +9 -0
  199. package/lib/prefetch/ResourceHintPlugin.js +18 -23
  200. package/lib/prefetch/ResourceHintRuntimeModule.js +9 -0
  201. package/lib/prefetch/StartupAssetHintRuntimeModule.js +16 -10
  202. package/lib/rules/RuleSetCompiler.js +211 -4
  203. package/lib/runtime/AsyncModuleRuntimeModule.js +98 -66
  204. package/lib/runtime/AutoPublicPathRuntimeModule.js +9 -0
  205. package/lib/runtime/BaseUriRuntimeModule.js +9 -0
  206. package/lib/runtime/ChunkNameRuntimeModule.js +9 -0
  207. package/lib/runtime/EnsureChunkRuntimeModule.js +35 -16
  208. package/lib/runtime/GetChunkFilenameRuntimeModule.js +12 -4
  209. package/lib/runtime/GetFullHashRuntimeModule.js +9 -0
  210. package/lib/runtime/GetMainFilenameRuntimeModule.js +9 -0
  211. package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +9 -0
  212. package/lib/runtime/GlobalRuntimeModule.js +11 -4
  213. package/lib/runtime/HasOwnPropertyRuntimeModule.js +9 -0
  214. package/lib/runtime/HelperRuntimeModule.js +10 -0
  215. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +305 -224
  216. package/lib/runtime/NonceRuntimeModule.js +9 -0
  217. package/lib/runtime/OnChunksLoadedRuntimeModule.js +67 -20
  218. package/lib/runtime/PublicPathRuntimeModule.js +9 -0
  219. package/lib/runtime/RuntimeIdRuntimeModule.js +9 -0
  220. package/lib/runtime/SpecNamespaceObjectRuntimeModule.js +90 -0
  221. package/lib/runtime/StartupChunkDependenciesRuntimeModule.js +9 -0
  222. package/lib/runtime/StartupEntrypointRuntimeModule.js +9 -0
  223. package/lib/runtime/SystemContextRuntimeModule.js +9 -0
  224. package/lib/runtime/ToBinaryRuntimeModule.js +9 -0
  225. package/lib/schemes/DataUriPlugin.js +3 -6
  226. package/lib/schemes/VirtualUrlPlugin.js +1 -1
  227. package/lib/serialization/BinaryMiddleware.js +9 -9
  228. package/lib/serialization/FileMiddleware.js +16 -16
  229. package/lib/serialization/ObjectMiddleware.js +168 -139
  230. package/lib/sharing/ConsumeSharedPlugin.js +12 -0
  231. package/lib/sharing/ConsumeSharedRuntimeModule.js +11 -0
  232. package/lib/sharing/ShareRuntimeModule.js +9 -0
  233. package/lib/sharing/declaredShared.js +47 -0
  234. package/lib/stats/DefaultStatsFactoryPlugin.js +6 -5
  235. package/lib/stats/DefaultStatsPrinterPlugin.js +3 -0
  236. package/lib/typescript/TypeScriptPlugin.js +2 -3
  237. package/lib/url/URLParserPlugin.js +6 -8
  238. package/lib/util/LocConverter.js +4 -7
  239. package/lib/util/SourceProcessor.js +91 -91
  240. package/lib/util/async.js +292 -0
  241. package/lib/util/buildDiagnostics.js +69 -0
  242. package/lib/util/chainedImports.js +3 -4
  243. package/lib/util/concatenate.js +4 -4
  244. package/lib/util/createHooksRegistry.js +1 -2
  245. package/lib/util/dataURL.js +2 -3
  246. package/lib/util/deterministicGrouping.js +8 -13
  247. package/lib/util/findGraphRoots.js +6 -13
  248. package/lib/util/fs.js +27 -1
  249. package/lib/util/globUtils.js +1 -1
  250. package/lib/util/handlerKeys.js +40 -0
  251. package/lib/util/hash/wasm-hash.js +3 -4
  252. package/lib/util/identifier.js +20 -0
  253. package/lib/util/internalSerializables.js +5 -1
  254. package/lib/util/registerExternalSerializer.js +6 -45
  255. package/lib/util/semver.js +5 -3
  256. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +9 -0
  257. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +9 -0
  258. package/lib/wasm-async/AsyncWebAssemblyGenerator.js +6 -1
  259. package/lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js +5 -1
  260. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +12 -4
  261. package/lib/wasm-sync/WebAssemblyGenerator.js +6 -1
  262. package/lib/wasm-sync/WebAssemblyJavascriptGenerator.js +5 -1
  263. package/lib/wasm-sync/WebAssemblyUtils.js +2 -4
  264. package/lib/web/JsonpChunkLoadingRuntimeModule.js +36 -0
  265. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +19 -0
  266. package/package.json +55 -46
  267. package/schemas/WebpackOptions.check.js +1 -1
  268. package/schemas/WebpackOptions.json +379 -66
  269. package/schemas/plugins/LoaderOptionsPlugin.check.js +1 -1
  270. package/schemas/plugins/ManifestPlugin.check.js +1 -1
  271. package/schemas/plugins/SourceMapDevToolPlugin.check.js +1 -1
  272. package/schemas/plugins/asset/AssetGeneratorOptions.check.js +1 -1
  273. package/schemas/plugins/asset/AssetResourceGeneratorOptions.check.js +1 -1
  274. package/schemas/plugins/container/ContainerPlugin.check.js +1 -1
  275. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  276. package/schemas/plugins/debug/ProfilingPlugin.check.js +1 -1
  277. package/schemas/plugins/dll/DllReferencePlugin.check.js +1 -1
  278. package/schemas/plugins/ids/HashedModuleIdsPlugin.check.js +1 -1
  279. package/schemas/plugins/schemes/HttpUriPlugin.check.js +1 -1
  280. package/types.d.ts +1723 -604
  281. package/lib/dependencies/ExportBindingInitFragment.js +0 -191
  282. package/lib/errors/EntrypointOverlapWarning.js +0 -45
  283. package/lib/performance/EntrypointOverlapPlugin.js +0 -122
@@ -0,0 +1,1054 @@
1
+ /*
2
+ MIT License http://www.opensource.org/licenses/mit-license.php
3
+ Author Alexander Akait @alexander-akait
4
+ */
5
+
6
+ "use strict";
7
+
8
+ const path = require("path");
9
+ const { RawSource } = require("webpack-sources");
10
+ const Compilation = require("./Compilation");
11
+ const EmptyCopyPatternWarning = require("./errors/EmptyCopyPatternWarning");
12
+ const WebpackError = require("./errors/WebpackError");
13
+ const asyncLib = require("./util/async");
14
+ const createHash = require("./util/createHash");
15
+ const { PERMISSIONS_MASK, join } = require("./util/fs");
16
+ const {
17
+ escapeGlobPattern,
18
+ extractGlobBaseDir,
19
+ globMatchNormalizedWithExplicitDot,
20
+ normalizePathSeparators,
21
+ normalizePathSeparatorsForPath,
22
+ unescapeGlobPath
23
+ } = require("./util/globUtils");
24
+ const { ABSOLUTE_PATH_REGEXP } = require("./util/identifier");
25
+
26
+ /** @import { CopyGlobOptions, CopyObjectPattern, CopyOptions, CopyPattern } from "../declarations/WebpackOptions" */
27
+ /** @import Compiler from "./Compiler" */
28
+ /** @import { AssetInfo, AssetSymlink, AssetTimestamps, PathData } from "./Compilation" */
29
+ /** @import CacheFacade from "./CacheFacade" */
30
+ /** @import { Snapshot } from "./FileSystemInfo" */
31
+ /** @import { Logger } from "./logging/Logger" */
32
+ /** @import { Dirent, InputFileSystem, IStats } from "./util/fs" */
33
+ /** @import { GlobMatchOptions } from "./util/globUtils" */
34
+ /** @import { Source } from "webpack-sources" */
35
+
36
+ /**
37
+ * @callback CopyFilenameFunction
38
+ * @param {PathData} pathData path data of the copied file
39
+ * @param {AssetInfo=} assetInfo asset info
40
+ * @returns {string} filename of the copied file, relative to `to`
41
+ */
42
+
43
+ /**
44
+ * @callback CopyTransform
45
+ * @param {Buffer} content content of the file
46
+ * @param {string} absoluteFilename absolute path of the file
47
+ * @returns {string | Buffer | Promise<string | Buffer>} content of the asset
48
+ */
49
+
50
+ /** @typedef {Record<string, EXPECTED_ANY>} CopyTransformCacheKeys */
51
+
52
+ /**
53
+ * @callback CopyTransformCacheKeysFunction
54
+ * @param {CopyTransformCacheKeys} defaultKeys keys the result is cached under by default
55
+ * @param {string} absoluteFilename absolute path of the file
56
+ * @returns {CopyTransformCacheKeys | Promise<CopyTransformCacheKeys>} keys the result is also cached under
57
+ */
58
+
59
+ /**
60
+ * What every callback of a pattern other than `filename` and `transform` reads
61
+ * the file from. `filename` takes webpack's own `(pathData, assetInfo)` instead,
62
+ * as it is a filename template like any other in the configuration.
63
+ * @typedef {object} CopiedFileData
64
+ * @property {string} absoluteFilename absolute path of the source file
65
+ * @property {string} sourceFilename path of the source file, relative to the compiler context
66
+ * @property {string} filename path of the copied file, relative to `to`
67
+ */
68
+
69
+ /**
70
+ * @callback CopyToFunction
71
+ * @param {CopiedFileData} file the copied file
72
+ * @returns {string} directory the file is copied to, relative to `output.path`
73
+ */
74
+
75
+ /**
76
+ * @callback CopyInfoFunction
77
+ * @param {CopiedFileData} file the copied file
78
+ * @returns {AssetInfo} asset info of the copied file
79
+ */
80
+
81
+ /** @typedef {"dir" | "file" | "glob"} TypeOfFrom */
82
+ /** @typedef {{ source: Source, snapshot: InstanceType<Snapshot> }} CacheEntry */
83
+
84
+ /**
85
+ * @typedef {object} CopiedFile
86
+ * @property {string} absoluteFilename absolute path of the source file
87
+ * @property {string} sourceFilename path of the source file, relative to the compiler context
88
+ * @property {string} filename path of the asset, relative to `output.path`
89
+ * @property {Source} source content of the asset
90
+ * @property {AssetInfo} info asset info
91
+ */
92
+
93
+ /** @typedef {{ files: CopiedFile[], errors: WebpackError[], warnings: WebpackError[] }} CopiedPattern */
94
+
95
+ /**
96
+ * @typedef {object} IgnoreMatcher
97
+ * @property {(normalizedPath: string) => boolean} file whether a file is not copied
98
+ * @property {(normalizedPath: string) => boolean} directory whether a directory is not walked at all
99
+ */
100
+
101
+ /**
102
+ * @typedef {object} WalkOptions
103
+ * @property {InputFileSystem} fs input file system
104
+ * @property {boolean} followSymlinks whether a symlink is resolved rather than copied as a link
105
+ * @property {IgnoreMatcher} ignore what is not copied
106
+ * @property {Set<string>} links absolute paths of the symlinks copied as links, filled by the walk
107
+ */
108
+
109
+ /**
110
+ * @typedef {object} ResolvedFrom
111
+ * @property {string} from the `from` as the user wrote it
112
+ * @property {TypeOfFrom} typeOfFrom what it names
113
+ * @property {string} glob absolute glob it resolved to
114
+ * @property {string} base directory the glob is anchored on
115
+ * @property {string[]} entries absolute paths of the files it reaches
116
+ */
117
+
118
+ /**
119
+ * @typedef {object} NormalizedPattern
120
+ * @property {CopyObjectPattern} pattern the pattern as the user wrote it
121
+ * @property {string} base directory the copied paths are relative to
122
+ * @property {number} index index of the pattern
123
+ * @property {Set<string>} links absolute paths of the files copied as symbolic links
124
+ */
125
+
126
+ const PLUGIN_NAME = "CopyPlugin";
127
+
128
+ // keeps the directory structure below `from`, as `publicDir` copies do
129
+ const DEFAULT_FILENAME = "[path][base]";
130
+
131
+ /**
132
+ * Whether a copied asset lands outside `output.path`, which is the directory
133
+ * the compiler joins its name onto.
134
+ * @param {string} filename name of the copied asset
135
+ * @returns {boolean} whether the name leaves the output directory
136
+ */
137
+ const escapesOutputPath = (filename) =>
138
+ ABSOLUTE_PATH_REGEXP.test(filename) ||
139
+ path.posix.normalize(filename).startsWith("../");
140
+ const FILE_CONCURRENCY = 100;
141
+ const PATTERN_CONCURRENCY = 2;
142
+ const DIRECTORY_CONCURRENCY = 20;
143
+
144
+ /**
145
+ * @template T
146
+ * @template R
147
+ * @param {T[]} items items
148
+ * @param {number} limit maximum number of items processed at the same time
149
+ * @param {(item: T) => Promise<R>} fn processor
150
+ * @returns {Promise<R[]>} results, in the order of the items
151
+ */
152
+ const mapLimit = (items, limit, fn) =>
153
+ new Promise((resolve, reject) => {
154
+ asyncLib.mapLimit(
155
+ items,
156
+ limit,
157
+ (item, callback) => {
158
+ fn(item).then((result) => callback(null, result), callback);
159
+ },
160
+ (err, results) => {
161
+ if (err) return reject(err);
162
+ resolve(/** @type {R[]} */ (results));
163
+ }
164
+ );
165
+ });
166
+
167
+ /**
168
+ * @param {InputFileSystem} fs input file system
169
+ * @param {string} filePath path
170
+ * @returns {Promise<IStats | undefined>} stats, undefined when the path does not exist
171
+ */
172
+ const stat = (fs, filePath) =>
173
+ new Promise((resolve, reject) => {
174
+ /** @type {NonNullable<InputFileSystem["stat"]>} */
175
+ (fs.stat)(filePath, (err, stats) => {
176
+ if (err) {
177
+ if (err.code === "ENOENT" || err.code === "ENOTDIR") {
178
+ return resolve(undefined);
179
+ }
180
+ return reject(err);
181
+ }
182
+ resolve(/** @type {IStats} */ (stats));
183
+ });
184
+ });
185
+
186
+ /**
187
+ * @param {InputFileSystem} fs input file system
188
+ * @param {string} filePath path
189
+ * @returns {Promise<Buffer>} content of the file
190
+ */
191
+ const readFile = (fs, filePath) =>
192
+ new Promise((resolve, reject) => {
193
+ fs.readFile(filePath, (err, content) => {
194
+ if (err) return reject(err);
195
+ resolve(/** @type {Buffer} */ (content));
196
+ });
197
+ });
198
+
199
+ /**
200
+ * Reads the directory with the type of each entry, which is what lets the walk
201
+ * classify an entry without a stat of its own.
202
+ * @param {InputFileSystem} fs input file system
203
+ * @param {string} directory path
204
+ * @returns {Promise<Dirent[]>} entries of the directory, empty when it does not exist
205
+ */
206
+ const readDirectory = (fs, directory) =>
207
+ new Promise((resolve, reject) => {
208
+ fs.readdir(directory, { withFileTypes: true }, (err, entries) => {
209
+ if (err) {
210
+ if (err.code === "ENOENT" || err.code === "ENOTDIR") return resolve([]);
211
+ return reject(err);
212
+ }
213
+ resolve(/** @type {Dirent[]} */ (entries));
214
+ });
215
+ });
216
+
217
+ /**
218
+ * @param {InputFileSystem} fs input file system
219
+ * @param {string} directory path
220
+ * @returns {Promise<string>} real path of the directory, the path itself when it can't be resolved
221
+ */
222
+ const realpath = (fs, directory) =>
223
+ new Promise((resolve) => {
224
+ if (!fs.realpath) {
225
+ resolve(directory);
226
+ return;
227
+ }
228
+ fs.realpath(directory, (err, realPath) => {
229
+ resolve(err ? directory : /** @type {string} */ (realPath));
230
+ });
231
+ });
232
+
233
+ /**
234
+ * @param {InputFileSystem} fs input file system
235
+ * @param {string} filePath path of the symbolic link
236
+ * @returns {Promise<string | undefined>} what it points at, undefined when it cannot be read
237
+ */
238
+ const readLink = (fs, filePath) =>
239
+ new Promise((resolve) => {
240
+ fs.readlink(filePath, (err, target) => {
241
+ resolve(err ? undefined : String(target));
242
+ });
243
+ });
244
+
245
+ /**
246
+ * @param {WalkOptions} options options of the walk
247
+ * @param {string} directory absolute path of the directory
248
+ * @param {Set<string>} visited real paths of the symlinked directories already walked, guarding against cycles
249
+ * @param {number} maxDepth how many directories below this one can still hold a match
250
+ * @returns {Promise<string[]>} absolute paths of the files below it, in a stable order
251
+ */
252
+ const walkDirectory = async (options, directory, visited, maxDepth) => {
253
+ const { fs, followSymlinks, ignore, links } = options;
254
+ // sorted so that the copied assets keep a stable order
255
+ const entries = (await readDirectory(fs, directory)).sort((a, b) =>
256
+ a.name < b.name ? -1 : a.name > b.name ? 1 : 0
257
+ );
258
+ const results = await mapLimit(
259
+ entries,
260
+ DIRECTORY_CONCURRENCY,
261
+ async (entry) => {
262
+ const filePath = join(fs, directory, entry.name);
263
+ const isSymlink = entry.isSymbolicLink();
264
+ // the link itself is what is copied, so neither its target nor what is
265
+ // below it is read
266
+ if (isSymlink && !followSymlinks) {
267
+ if (ignore.file(normalizePathSeparatorsForPath(filePath))) return [];
268
+ links.add(filePath);
269
+ return [filePath];
270
+ }
271
+ // a symlink reports neither file nor directory, so it alone pays for a
272
+ // stat, and it alone can lead back to a directory the walk entered
273
+ const stats = isSymlink ? await stat(fs, filePath) : entry;
274
+ // the target is gone, or is neither a file nor a directory
275
+ if (stats === undefined) return [];
276
+ if (stats.isFile()) return [filePath];
277
+ if (!stats.isDirectory() || maxDepth === 0) return [];
278
+ // an ignored directory is not listed at all, which is what makes an
279
+ // `ignore` naming one cheaper than dropping its files afterwards
280
+ if (ignore.directory(normalizePathSeparatorsForPath(filePath))) return [];
281
+ if (isSymlink) {
282
+ const realPath = await realpath(fs, filePath);
283
+ if (visited.has(realPath)) return [];
284
+ visited.add(realPath);
285
+ }
286
+ return walkDirectory(options, filePath, visited, maxDepth - 1);
287
+ }
288
+ );
289
+ /** @type {string[]} */
290
+ const files = [];
291
+ for (const result of results) {
292
+ for (const file of result) files.push(file);
293
+ }
294
+ return files;
295
+ };
296
+
297
+ /**
298
+ * @param {WalkOptions} options options of the walk
299
+ * @param {string} base absolute path of the directory the glob is anchored on
300
+ * @param {number} maxDepth how many directories below it can still hold a match
301
+ * @returns {Promise<string[]>} absolute paths of the files below it, in a stable order
302
+ */
303
+ const walkBase = async (options, base, maxDepth) => {
304
+ // the base is a directory like any below it, so an `ignore` naming it skips
305
+ // what the whole pattern would have copied
306
+ if (options.ignore.directory(normalizePathSeparatorsForPath(base))) return [];
307
+ return walkDirectory(
308
+ options,
309
+ base,
310
+ // the base counts as walked, so a symlink pointing back at it ends there
311
+ new Set(options.followSymlinks ? [await realpath(options.fs, base)] : []),
312
+ maxDepth
313
+ );
314
+ };
315
+
316
+ /**
317
+ * @returns {boolean} false, as nothing is ignored
318
+ */
319
+ const notIgnored = () => false;
320
+
321
+ /** @type {IgnoreMatcher} */
322
+ const NOTHING_IGNORED = { file: notIgnored, directory: notIgnored };
323
+
324
+ // the tail of a glob naming everything inside a directory, which names the
325
+ // directory itself once it is gone
326
+ const IGNORED_DIRECTORY_CONTENTS_REGEXP = /\/\*\*(?:\/\*)?$/;
327
+
328
+ /**
329
+ * @param {Error} error error thrown while copying
330
+ * @returns {WebpackError} the error as a webpack error
331
+ */
332
+ const toWebpackError = (error) => {
333
+ if (error instanceof WebpackError) return error;
334
+ const webpackError = new WebpackError(error.message);
335
+ webpackError.details = error.stack;
336
+ return webpackError;
337
+ };
338
+
339
+ class CopyPlugin {
340
+ /**
341
+ * @param {CopyOptions} options options of the copying
342
+ */
343
+ constructor(options) {
344
+ this.patterns = options.patterns;
345
+ this.concurrency =
346
+ options.concurrency === undefined
347
+ ? FILE_CONCURRENCY
348
+ : options.concurrency;
349
+ this.stage =
350
+ options.stage === undefined
351
+ ? Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
352
+ : options.stage;
353
+ }
354
+
355
+ /**
356
+ * Apply the plugin
357
+ * @param {Compiler} compiler the compiler instance
358
+ * @returns {void}
359
+ */
360
+ apply(compiler) {
361
+ // TODO feed a copied asset into the compilation hash: a rebuild which changed
362
+ // only a copied file does not move it, so a dev server sees nothing to reload
363
+ compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
364
+ const logger = compilation.getLogger("webpack.CopyPlugin");
365
+ const cache = compilation.getCache(PLUGIN_NAME);
366
+ compilation.hooks.processAssets.tapPromise(
367
+ { name: PLUGIN_NAME, stage: this.stage },
368
+ () => this._copy(compiler, compilation, logger, cache)
369
+ );
370
+ });
371
+ }
372
+
373
+ /**
374
+ * @private
375
+ * @param {Compiler} compiler the compiler
376
+ * @param {Compilation} compilation the compilation
377
+ * @param {Logger} logger the logger
378
+ * @param {CacheFacade} cache the cache
379
+ * @returns {Promise<void>}
380
+ */
381
+ async _copy(compiler, compilation, logger, cache) {
382
+ const results = await mapLimit(
383
+ this.patterns.map((pattern, index) => ({ pattern, index })),
384
+ PATTERN_CONCURRENCY,
385
+ async ({ pattern, index }) => {
386
+ try {
387
+ return await this._copyPattern(
388
+ compiler,
389
+ compilation,
390
+ logger,
391
+ cache,
392
+ pattern,
393
+ index
394
+ );
395
+ } catch (err) {
396
+ return {
397
+ files: [],
398
+ errors: [toWebpackError(/** @type {Error} */ (err))],
399
+ warnings: []
400
+ };
401
+ }
402
+ }
403
+ );
404
+ // patterns are processed concurrently, so both diagnostics and assets are
405
+ // ordered by the pattern rather than by which pattern finished first
406
+ for (const result of results) {
407
+ for (const error of result.errors) compilation.errors.push(error);
408
+ for (const warning of result.warnings) {
409
+ compilation.warnings.push(warning);
410
+ }
411
+ }
412
+ // what the copy itself emitted, so a later pattern replacing an earlier
413
+ // one is told apart from a copy landing on an asset of the compilation
414
+ /** @type {Set<string>} */
415
+ const emitted = new Set();
416
+ for (const result of results) {
417
+ for (const file of result.files) {
418
+ const { absoluteFilename, sourceFilename, filename, source } = file;
419
+ /** @type {AssetInfo} */
420
+ const info = { copied: true, sourceFilename, ...file.info };
421
+ if (escapesOutputPath(filename)) {
422
+ compilation.errors.push(
423
+ new WebpackError(
424
+ `Copying '${absoluteFilename}' to '${filename}' would write outside 'output.path', name it something else with the 'filename' or 'to' option of the pattern`
425
+ )
426
+ );
427
+ continue;
428
+ }
429
+ if (compilation.getAsset(filename)) {
430
+ if (!emitted.has(filename)) {
431
+ compilation.errors.push(
432
+ new WebpackError(
433
+ `Copying '${absoluteFilename}' to '${filename}' would replace an asset the compilation emits, name it something else with the 'filename' or 'to' option of the pattern`
434
+ )
435
+ );
436
+ continue;
437
+ }
438
+ // the info replaces rather than merges: an object argument is
439
+ // merged, which would keep a mode or a symlink this file lacks
440
+ compilation.updateAsset(filename, source, () => info);
441
+ logger.log(
442
+ `copied '${absoluteFilename}' over the earlier '${filename}'`
443
+ );
444
+ continue;
445
+ }
446
+ compilation.emitAsset(filename, source, info);
447
+ emitted.add(filename);
448
+ logger.log(`copied '${absoluteFilename}' to '${filename}'`);
449
+ }
450
+ }
451
+ }
452
+
453
+ /**
454
+ * @private
455
+ * @param {Compiler} compiler the compiler
456
+ * @param {Compilation} compilation the compilation
457
+ * @param {Logger} logger the logger
458
+ * @param {CacheFacade} cache the cache
459
+ * @param {CopyPattern} item the pattern
460
+ * @param {number} index index of the pattern
461
+ * @returns {Promise<CopiedPattern>} the copied files and the diagnostics
462
+ */
463
+ async _copyPattern(compiler, compilation, logger, cache, item, index) {
464
+ /** @type {CopyObjectPattern} */
465
+ const pattern = typeof item === "string" ? { from: item } : item;
466
+ const { from } = pattern;
467
+ const context =
468
+ pattern.context === undefined
469
+ ? compiler.context
470
+ : path.resolve(compiler.context, pattern.context);
471
+ const globOptions = pattern.globOptions || {};
472
+ /** @type {GlobMatchOptions} */
473
+ const matchOptions = {
474
+ caseSensitive: globOptions.caseSensitive,
475
+ requireLiteralLeadingDot: globOptions.dot === false
476
+ };
477
+ const ignore = this._createIgnoreMatcher(
478
+ context,
479
+ globOptions.ignore,
480
+ matchOptions
481
+ );
482
+ /** @type {Set<string>} */
483
+ const links = new Set();
484
+ const sources = Array.isArray(from) ? from : [from];
485
+ const resolved = await mapLimit(sources, PATTERN_CONCURRENCY, (one) =>
486
+ this._resolveFrom(compiler, compilation, logger, {
487
+ from: one,
488
+ context,
489
+ globOptions,
490
+ matchOptions,
491
+ ignore,
492
+ links
493
+ })
494
+ );
495
+
496
+ /** @type {WebpackError[]} */
497
+ const warnings = [];
498
+ // a file more than one `from` reaches is copied once, where the first of
499
+ // them puts it
500
+ /** @type {Map<string, string>} */
501
+ const entries = new Map();
502
+ for (const one of resolved) {
503
+ if (one.entries.length === 0) {
504
+ warnings.push(new EmptyCopyPatternWarning(one.from, one.glob));
505
+ continue;
506
+ }
507
+ // an explicit `context` roots every `from` of the pattern at one
508
+ // directory, so what they copy keeps the structure below it
509
+ const base = pattern.context === undefined ? one.base : context;
510
+ for (const entry of one.entries) {
511
+ if (!entries.has(entry)) entries.set(entry, base);
512
+ }
513
+ }
514
+ if (entries.size === 0) return { files: [], errors: [], warnings };
515
+
516
+ const results = await mapLimit(
517
+ [...entries],
518
+ this.concurrency,
519
+ async ([entry, base]) => {
520
+ try {
521
+ return await this._copyFile(
522
+ compiler,
523
+ compilation,
524
+ logger,
525
+ cache,
526
+ { pattern, base, index, links },
527
+ entry
528
+ );
529
+ } catch (err) {
530
+ return toWebpackError(/** @type {Error} */ (err));
531
+ }
532
+ }
533
+ );
534
+ /** @type {CopiedFile[]} */
535
+ const files = [];
536
+ /** @type {WebpackError[]} */
537
+ const errors = [];
538
+ /** @type {Set<string>} */
539
+ const filenames = new Set();
540
+ for (const result of results) {
541
+ if (result instanceof WebpackError) {
542
+ errors.push(result);
543
+ continue;
544
+ }
545
+ // a `to` naming no placeholder collapses every file onto one asset,
546
+ // which silently drops all but the first one
547
+ if (filenames.has(result.filename)) {
548
+ errors.push(
549
+ new WebpackError(
550
+ `Multiple files of the '${sources.join("', '")}' pattern of 'output.copy' are copied to '${result.filename}', keep a '[path]' or '[name]' placeholder in the 'filename' option to tell them apart`
551
+ )
552
+ );
553
+ continue;
554
+ }
555
+ filenames.add(result.filename);
556
+ files.push(result);
557
+ }
558
+
559
+ return { files, errors, warnings };
560
+ }
561
+
562
+ /**
563
+ * @private
564
+ * @param {Compiler} compiler the compiler
565
+ * @param {Compilation} compilation the compilation
566
+ * @param {Logger} logger the logger
567
+ * @param {object} options what the `from` is resolved with
568
+ * @param {string} options.from the `from`
569
+ * @param {string} options.context directory a relative `from` is resolved against
570
+ * @param {CopyGlobOptions} options.globOptions options of the glob
571
+ * @param {GlobMatchOptions} options.matchOptions options the glob is matched with
572
+ * @param {IgnoreMatcher} options.ignore what is not copied
573
+ * @param {Set<string>} options.links absolute paths of the symlinks copied as links, filled by the walk
574
+ * @returns {Promise<ResolvedFrom>} the files it reaches
575
+ */
576
+ async _resolveFrom(
577
+ compiler,
578
+ compilation,
579
+ logger,
580
+ { from, context, globOptions, matchOptions, ignore, links }
581
+ ) {
582
+ const fs = /** @type {InputFileSystem} */ (compiler.inputFileSystem);
583
+ const followSymlinks = globOptions.followSymlinks !== false;
584
+ const absoluteFrom = path.isAbsolute(from)
585
+ ? path.normalize(from)
586
+ : path.resolve(context, from);
587
+ const stats = await stat(fs, absoluteFrom);
588
+ /** @type {TypeOfFrom} */
589
+ const typeOfFrom = !stats
590
+ ? "glob"
591
+ : stats.isDirectory()
592
+ ? "dir"
593
+ : stats.isFile()
594
+ ? "file"
595
+ : "glob";
596
+
597
+ /** @type {string} */
598
+ let glob;
599
+ switch (typeOfFrom) {
600
+ case "dir":
601
+ glob = `${escapeGlobPattern(normalizePathSeparatorsForPath(absoluteFrom))}/**/*`;
602
+ break;
603
+ case "file":
604
+ glob = escapeGlobPattern(normalizePathSeparatorsForPath(absoluteFrom));
605
+ break;
606
+ default:
607
+ glob = this._resolveGlob(context, from);
608
+ }
609
+ // everything is copied relative to what `from` names literally, so a
610
+ // directory, a file and a glob all keep the structure below it
611
+ const escapedBase = extractGlobBaseDir(glob);
612
+ // only `**` matches across depths, so any other glob names how deep a
613
+ // match can sit and the walk never descends past it
614
+ const segments = glob.slice(escapedBase.length).split("/");
615
+ const globDepth = segments.includes("**")
616
+ ? Number.POSITIVE_INFINITY
617
+ : segments.length - 1;
618
+ // `deep` counts the levels which are read, the walk what is left below it
619
+ const maxDepth =
620
+ globOptions.deep === undefined
621
+ ? globDepth
622
+ : Math.min(globDepth, globOptions.deep - 1);
623
+ // the walk reads the base through the cache the watcher purges under the
624
+ // watched directory's own name, so both have to be that one native path
625
+ const base = path.resolve(context, unescapeGlobPath(escapedBase));
626
+ // an absolute pattern keeps the `..` segments the walk resolves away, so
627
+ // the glob is re-anchored on the base the walk reads
628
+ glob = path.posix.join(
629
+ escapeGlobPattern(normalizePathSeparatorsForPath(base)),
630
+ ...segments
631
+ );
632
+ logger.debug(`'${from}' is a ${typeOfFrom}, globbing '${glob}'`);
633
+
634
+ // the glob is normalized already, and the base once here, so the match
635
+ // does not redo either for every entry
636
+ const normalizedBase = normalizePathSeparatorsForPath(base);
637
+ const entries =
638
+ typeOfFrom === "file"
639
+ ? ignore.file(normalizePathSeparatorsForPath(absoluteFrom))
640
+ ? []
641
+ : [absoluteFrom]
642
+ : (
643
+ await walkBase(
644
+ { fs, followSymlinks, ignore, links },
645
+ base,
646
+ maxDepth
647
+ )
648
+ ).filter((filePath) => {
649
+ const normalizedPath = normalizePathSeparatorsForPath(filePath);
650
+ return (
651
+ globMatchNormalizedWithExplicitDot(
652
+ glob,
653
+ normalizedPath,
654
+ normalizedBase,
655
+ matchOptions
656
+ ) && !ignore.file(normalizedPath)
657
+ );
658
+ });
659
+
660
+ await this._addPatternDependency(
661
+ fs,
662
+ compilation,
663
+ typeOfFrom,
664
+ absoluteFrom,
665
+ base,
666
+ // only a glob can name a base which is not there — a directory `from`
667
+ // was stat'd above, and a file one watches itself
668
+ entries.length === 0 && typeOfFrom === "glob"
669
+ );
670
+
671
+ return { from, typeOfFrom, glob, base, entries };
672
+ }
673
+
674
+ /**
675
+ * @private
676
+ * @param {string} context directory the globs are resolved from
677
+ * @param {string[] | undefined} ignore globs of the files which are not copied
678
+ * @param {GlobMatchOptions} matchOptions options the globs are matched with
679
+ * @returns {IgnoreMatcher} what is not copied
680
+ */
681
+ _createIgnoreMatcher(context, ignore, matchOptions) {
682
+ if (ignore === undefined || ignore.length === 0) return NOTHING_IGNORED;
683
+ const globs = ignore.map((one) => {
684
+ const glob = this._resolveGlob(context, one);
685
+ return {
686
+ glob,
687
+ // a glob naming what is inside a directory names the directory too,
688
+ // so the walk stops at it instead of listing what it would drop
689
+ directoryGlob: glob.replace(IGNORED_DIRECTORY_CONTENTS_REGEXP, ""),
690
+ base: normalizePathSeparatorsForPath(
691
+ path.resolve(context, unescapeGlobPath(extractGlobBaseDir(glob)))
692
+ )
693
+ };
694
+ });
695
+ /**
696
+ * @param {string} normalizedPath path
697
+ * @param {boolean} asDirectory whether the path is matched as a directory
698
+ * @returns {boolean} true, when one of the globs matches it
699
+ */
700
+ const matches = (normalizedPath, asDirectory) =>
701
+ globs.some((one) =>
702
+ globMatchNormalizedWithExplicitDot(
703
+ asDirectory ? one.directoryGlob : one.glob,
704
+ normalizedPath,
705
+ one.base,
706
+ matchOptions
707
+ )
708
+ );
709
+ return {
710
+ file: (normalizedPath) => matches(normalizedPath, false),
711
+ directory: (normalizedPath) => matches(normalizedPath, true)
712
+ };
713
+ }
714
+
715
+ /**
716
+ * @private
717
+ * @param {string} context context the pattern is resolved from
718
+ * @param {string} pattern glob or path
719
+ * @returns {string} absolute glob with `/` separators
720
+ */
721
+ _resolveGlob(context, pattern) {
722
+ const normalizedPattern = normalizePathSeparators(pattern);
723
+ // an absolute pattern is a path, so every `\` in it separates rather than
724
+ // escapes — a relative one keeps them, as a name may hold them on posix
725
+ return ABSOLUTE_PATH_REGEXP.test(normalizedPattern)
726
+ ? normalizePathSeparatorsForPath(normalizedPattern)
727
+ : path.posix.join(
728
+ escapeGlobPattern(normalizePathSeparatorsForPath(context)),
729
+ normalizedPattern
730
+ );
731
+ }
732
+
733
+ /**
734
+ * @private
735
+ * @param {InputFileSystem} fs input file system
736
+ * @param {Compilation} compilation the compilation
737
+ * @param {TypeOfFrom} typeOfFrom what `from` names
738
+ * @param {string} absoluteFrom absolute path of `from`
739
+ * @param {string} base native base directory of the glob
740
+ * @param {boolean} checkExistence whether a missing directory is watched for its creation
741
+ * @returns {Promise<void>}
742
+ */
743
+ async _addPatternDependency(
744
+ fs,
745
+ compilation,
746
+ typeOfFrom,
747
+ absoluteFrom,
748
+ base,
749
+ checkExistence
750
+ ) {
751
+ // a single file needs no directory watched, only itself
752
+ if (typeOfFrom === "file") {
753
+ compilation.fileDependencies.add(absoluteFrom);
754
+ return;
755
+ }
756
+ // watching a directory which does not exist yet reports it as removed on
757
+ // the initial aggregation, so it is watched as missing until it is created
758
+ if (checkExistence && !(await stat(fs, base))) {
759
+ compilation.missingDependencies.add(base);
760
+ return;
761
+ }
762
+ compilation.contextDependencies.add(base);
763
+ }
764
+
765
+ /**
766
+ * @private
767
+ * @param {Compiler} compiler the compiler
768
+ * @param {Compilation} compilation the compilation
769
+ * @param {Logger} logger the logger
770
+ * @param {CacheFacade} cache the cache
771
+ * @param {NormalizedPattern} normalizedPattern the pattern the file belongs to
772
+ * @param {string} absoluteFilename absolute path of the file
773
+ * @returns {Promise<CopiedFile>} the copied file
774
+ */
775
+ async _copyFile(
776
+ compiler,
777
+ compilation,
778
+ logger,
779
+ cache,
780
+ normalizedPattern,
781
+ absoluteFilename
782
+ ) {
783
+ const { pattern, base, index, links } = normalizedPattern;
784
+ const fs = /** @type {InputFileSystem} */ (compiler.inputFileSystem);
785
+ const sourceFilename = normalizePathSeparatorsForPath(
786
+ path.relative(compiler.context, absoluteFilename)
787
+ );
788
+
789
+ compilation.fileDependencies.add(absoluteFilename);
790
+
791
+ /** @type {Source} */
792
+ let source;
793
+ /** @type {AssetSymlink | undefined} */
794
+ let symlink;
795
+ /** @type {AssetTimestamps | undefined} */
796
+ let timestamps;
797
+ /** @type {number | undefined} */
798
+ let mode;
799
+ if (links.has(absoluteFilename)) {
800
+ // what the link says is its whole content, so neither its target nor a
801
+ // snapshot of one is read — and a transform has nothing to work on
802
+ const target = await readLink(fs, absoluteFilename);
803
+ if (target === undefined) {
804
+ throw new WebpackError(
805
+ `Unable to read the symbolic link '${absoluteFilename}' of the '${pattern.from}' pattern of 'output.copy'`
806
+ );
807
+ }
808
+ const stats = await stat(fs, absoluteFilename);
809
+ symlink = {
810
+ target,
811
+ isDirectory: stats !== undefined && stats.isDirectory()
812
+ };
813
+ source = new RawSource(target);
814
+ logger.log(`copied the symbolic link '${absoluteFilename}' as a link`);
815
+ } else {
816
+ const cacheIdentifier = `${sourceFilename}|${index}`;
817
+ const cacheEntry = /** @type {CacheEntry | undefined} */ (
818
+ await cache.getPromise(cacheIdentifier, null)
819
+ );
820
+ /** @type {Source | undefined} */
821
+ let cached;
822
+ if (
823
+ cacheEntry &&
824
+ (await this._checkSnapshotValid(compilation, cacheEntry.snapshot))
825
+ ) {
826
+ ({ source: cached } = cacheEntry);
827
+ logger.debug(`restored '${absoluteFilename}' from cache`);
828
+ }
829
+ if (!cached) {
830
+ const startTime = Date.now();
831
+ cached = new RawSource(await readFile(fs, absoluteFilename));
832
+ const snapshot = await this._createSnapshot(
833
+ compilation,
834
+ startTime,
835
+ absoluteFilename
836
+ );
837
+ if (snapshot) {
838
+ await cache.storePromise(cacheIdentifier, null, {
839
+ source: cached,
840
+ snapshot
841
+ });
842
+ }
843
+ }
844
+ source = cached;
845
+
846
+ // a link carries neither of its own, so only a file reads them, and
847
+ // one stat answers for both
848
+ if (pattern.preserveTimestamps || pattern.preservePermissions) {
849
+ const stats = await stat(fs, absoluteFilename);
850
+ if (stats) {
851
+ if (pattern.preserveTimestamps) {
852
+ timestamps = { atime: stats.atimeMs, mtime: stats.mtimeMs };
853
+ }
854
+ if (pattern.preservePermissions) {
855
+ mode = stats.mode & PERMISSIONS_MASK;
856
+ }
857
+ }
858
+ }
859
+
860
+ if (pattern.transform) {
861
+ source = await this._transform(
862
+ cache,
863
+ pattern.transform,
864
+ source,
865
+ sourceFilename,
866
+ absoluteFilename,
867
+ index
868
+ );
869
+ }
870
+ }
871
+
872
+ const template =
873
+ pattern.filename === undefined ? DEFAULT_FILENAME : pattern.filename;
874
+ // only a template asking for a hash pays for one
875
+ const contentHash =
876
+ typeof template === "string" && !template.includes("hash")
877
+ ? undefined
878
+ : this._getContentHash(compilation, source.buffer());
879
+ // `[path]`, `[name]`, `[base]` and `[ext]` are read off this one
880
+ const filename = normalizePathSeparatorsForPath(
881
+ path.relative(base, absoluteFilename)
882
+ );
883
+ const basename = path.basename(filename);
884
+ const { path: interpolatedFilename, info } = compilation.getPathWithInfo(
885
+ template,
886
+ {
887
+ filename,
888
+ contentHash,
889
+ // `[contenthash]` is only read in a chunk context, so the copied file
890
+ // stands in as one — its `name` keeps `[name]` at the file's own name
891
+ chunk:
892
+ contentHash === undefined
893
+ ? undefined
894
+ : {
895
+ name: basename.slice(
896
+ 0,
897
+ basename.length - path.extname(filename).length
898
+ ),
899
+ id: sourceFilename,
900
+ hash: contentHash
901
+ }
902
+ }
903
+ );
904
+
905
+ /** @type {CopiedFileData} */
906
+ const file = {
907
+ absoluteFilename,
908
+ sourceFilename,
909
+ filename: interpolatedFilename
910
+ };
911
+ const to = typeof pattern.to === "function" ? pattern.to(file) : pattern.to;
912
+
913
+ return {
914
+ absoluteFilename,
915
+ sourceFilename,
916
+ filename: to
917
+ ? path.posix.join(
918
+ normalizePathSeparatorsForPath(to),
919
+ interpolatedFilename
920
+ )
921
+ : interpolatedFilename,
922
+ source,
923
+ info: {
924
+ ...info,
925
+ ...(symlink === undefined ? undefined : { symlink }),
926
+ ...(mode === undefined ? undefined : { mode }),
927
+ ...(timestamps === undefined ? undefined : { timestamps }),
928
+ ...(pattern.info === undefined
929
+ ? undefined
930
+ : typeof pattern.info === "function"
931
+ ? pattern.info(file)
932
+ : pattern.info)
933
+ }
934
+ };
935
+ }
936
+
937
+ /**
938
+ * @private
939
+ * @param {CacheFacade} cache the cache
940
+ * @param {NonNullable<CopyObjectPattern["transform"]>} transform the transform
941
+ * @param {Source} source content of the file
942
+ * @param {string} sourceFilename path of the file, relative to the compiler context
943
+ * @param {string} absoluteFilename absolute path of the file
944
+ * @param {number} index index of the pattern
945
+ * @returns {Promise<Source>} transformed content
946
+ */
947
+ async _transform(
948
+ cache,
949
+ transform,
950
+ source,
951
+ sourceFilename,
952
+ absoluteFilename,
953
+ index
954
+ ) {
955
+ const transformer =
956
+ typeof transform === "function" ? transform : transform.transformer;
957
+ const cacheOptions =
958
+ typeof transform === "function" ? true : transform.cache;
959
+ if (cacheOptions === false) {
960
+ return new RawSource(
961
+ await transformer(source.buffer(), absoluteFilename)
962
+ );
963
+ }
964
+ // the transform is part of the configuration, so its source decides
965
+ // together with the content whether a cached result still belongs to it
966
+ let identifier = `transform|${index}|${sourceFilename}|${String(transformer)}`;
967
+ if (
968
+ cacheOptions !== undefined &&
969
+ cacheOptions !== true &&
970
+ cacheOptions.keys !== undefined
971
+ ) {
972
+ const { keys } = cacheOptions;
973
+ identifier += `|${JSON.stringify(
974
+ typeof keys === "function"
975
+ ? await keys({ index, sourceFilename }, absoluteFilename)
976
+ : keys
977
+ )}`;
978
+ }
979
+ const itemCache = cache.getItemCache(
980
+ identifier,
981
+ cache.getLazyHashedEtag(source)
982
+ );
983
+ const cached = /** @type {Source | undefined} */ (
984
+ await itemCache.getPromise()
985
+ );
986
+ if (cached) return cached;
987
+ const transformed = new RawSource(
988
+ await transformer(source.buffer(), absoluteFilename)
989
+ );
990
+ await itemCache.storePromise(transformed);
991
+ return transformed;
992
+ }
993
+
994
+ /**
995
+ * @private
996
+ * @param {Compilation} compilation the compilation
997
+ * @param {Buffer} content content of the asset
998
+ * @returns {string} content hash of the asset
999
+ */
1000
+ _getContentHash(compilation, content) {
1001
+ const { hashDigest, hashDigestLength, hashFunction, hashSalt } =
1002
+ compilation.outputOptions;
1003
+ const hash = createHash(/** @type {string} */ (hashFunction));
1004
+ if (hashSalt) hash.update(hashSalt);
1005
+ hash.update(content);
1006
+ return /** @type {string} */ (hash.digest(hashDigest)).slice(
1007
+ 0,
1008
+ hashDigestLength
1009
+ );
1010
+ }
1011
+
1012
+ /**
1013
+ * @private
1014
+ * @param {Compilation} compilation the compilation
1015
+ * @param {number} startTime time the file was read at
1016
+ * @param {string} absoluteFilename absolute path of the file
1017
+ * @returns {Promise<InstanceType<Snapshot> | undefined>} snapshot of the file
1018
+ */
1019
+ _createSnapshot(compilation, startTime, absoluteFilename) {
1020
+ return new Promise((resolve, reject) => {
1021
+ compilation.fileSystemInfo.createSnapshot(
1022
+ startTime,
1023
+ [absoluteFilename],
1024
+ null,
1025
+ null,
1026
+ compilation.options.snapshot.module,
1027
+ (err, snapshot) => {
1028
+ if (err) return reject(err);
1029
+ resolve(/** @type {InstanceType<Snapshot> | undefined} */ (snapshot));
1030
+ }
1031
+ );
1032
+ });
1033
+ }
1034
+
1035
+ /**
1036
+ * @private
1037
+ * @param {Compilation} compilation the compilation
1038
+ * @param {InstanceType<Snapshot>} snapshot the snapshot
1039
+ * @returns {Promise<boolean>} true, when the snapshot is still valid
1040
+ */
1041
+ _checkSnapshotValid(compilation, snapshot) {
1042
+ return new Promise((resolve, reject) => {
1043
+ compilation.fileSystemInfo.checkSnapshotValid(
1044
+ snapshot,
1045
+ (err, isValid) => {
1046
+ if (err) return reject(err);
1047
+ resolve(isValid === true);
1048
+ }
1049
+ );
1050
+ });
1051
+ }
1052
+ }
1053
+
1054
+ module.exports = CopyPlugin;