DllReferencePlugin.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const DelegatedModuleFactoryPlugin = require("./DelegatedModuleFactoryPlugin");
  7. const ExternalModuleFactoryPlugin = require("./ExternalModuleFactoryPlugin");
  8. const WebpackError = require("./WebpackError");
  9. const DelegatedSourceDependency = require("./dependencies/DelegatedSourceDependency");
  10. const { makePathsRelative } = require("./util/identifier");
  11. const parseJson = require("./util/parseJson");
  12. /** @typedef {import("../declarations/WebpackOptions").Externals} Externals */
  13. /** @typedef {import("../declarations/plugins/DllReferencePlugin").DllReferencePluginOptions} DllReferencePluginOptions */
  14. /** @typedef {import("../declarations/plugins/DllReferencePlugin").DllReferencePluginOptionsContent} DllReferencePluginOptionsContent */
  15. /** @typedef {import("../declarations/plugins/DllReferencePlugin").DllReferencePluginOptionsManifest} DllReferencePluginOptionsManifest */
  16. /** @typedef {import("./Compiler")} Compiler */
  17. /** @typedef {import("./Compiler").CompilationParams} CompilationParams */
  18. /** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
  19. /** @typedef {{ path: string, data: DllReferencePluginOptionsManifest | undefined, error: Error | undefined }} CompilationDataItem */
  20. const PLUGIN_NAME = "DllReferencePlugin";
  21. class DllReferencePlugin {
  22. /**
  23. * Creates an instance of DllReferencePlugin.
  24. * @param {DllReferencePluginOptions} options options object
  25. */
  26. constructor(options) {
  27. this.options = options;
  28. }
  29. /**
  30. * Applies the plugin by registering its hooks on the compiler.
  31. * @param {Compiler} compiler the compiler instance
  32. * @returns {void}
  33. */
  34. apply(compiler) {
  35. compiler.hooks.validate.tap(PLUGIN_NAME, () => {
  36. compiler.validate(
  37. () => require("../schemas/plugins/DllReferencePlugin.json"),
  38. this.options,
  39. {
  40. name: "Dll Reference Plugin",
  41. baseDataPath: "options"
  42. },
  43. (options) =>
  44. require("../schemas/plugins/DllReferencePlugin.check")(options)
  45. );
  46. });
  47. compiler.hooks.compilation.tap(
  48. PLUGIN_NAME,
  49. (compilation, { normalModuleFactory }) => {
  50. compilation.dependencyFactories.set(
  51. DelegatedSourceDependency,
  52. normalModuleFactory
  53. );
  54. }
  55. );
  56. /** @type {WeakMap<CompilationParams, CompilationDataItem>} */
  57. const compilationData = new WeakMap();
  58. compiler.hooks.beforeCompile.tapAsync(PLUGIN_NAME, (params, callback) => {
  59. if ("manifest" in this.options) {
  60. const manifest = this.options.manifest;
  61. if (typeof manifest === "string") {
  62. /** @type {InputFileSystem} */
  63. (compiler.inputFileSystem).readFile(manifest, (err, result) => {
  64. if (err) return callback(err);
  65. /** @type {CompilationDataItem} */
  66. const data = {
  67. path: manifest,
  68. data: undefined,
  69. error: undefined
  70. };
  71. // Catch errors parsing the manifest so that blank
  72. // or malformed manifest files don't kill the process.
  73. try {
  74. data.data =
  75. /** @type {DllReferencePluginOptionsManifest} */
  76. (
  77. /** @type {unknown} */
  78. (parseJson(/** @type {Buffer} */ (result).toString("utf8")))
  79. );
  80. } catch (parseErr) {
  81. // Store the error in the params so that it can
  82. // be added as a compilation error later on.
  83. const manifestPath = makePathsRelative(
  84. compiler.context,
  85. manifest,
  86. compiler.root
  87. );
  88. data.error = new DllManifestError(
  89. manifestPath,
  90. /** @type {Error} */ (parseErr).message
  91. );
  92. }
  93. compilationData.set(params, data);
  94. return callback();
  95. });
  96. return;
  97. }
  98. }
  99. return callback();
  100. });
  101. compiler.hooks.compile.tap(PLUGIN_NAME, (params) => {
  102. let name = this.options.name;
  103. let sourceType = this.options.sourceType;
  104. let resolvedContent =
  105. "content" in this.options ? this.options.content : undefined;
  106. if ("manifest" in this.options) {
  107. const manifestParameter = this.options.manifest;
  108. /** @type {undefined | DllReferencePluginOptionsManifest} */
  109. let manifest;
  110. if (typeof manifestParameter === "string") {
  111. const data =
  112. /** @type {CompilationDataItem} */
  113. (compilationData.get(params));
  114. // If there was an error parsing the manifest
  115. // file, exit now because the error will be added
  116. // as a compilation error in the "compilation" hook.
  117. if (data.error) {
  118. return;
  119. }
  120. manifest = data.data;
  121. } else {
  122. manifest = manifestParameter;
  123. }
  124. if (manifest) {
  125. if (!name) name = manifest.name;
  126. if (!sourceType) sourceType = manifest.type;
  127. if (!resolvedContent) resolvedContent = manifest.content;
  128. }
  129. }
  130. /** @type {Externals} */
  131. const externals = {};
  132. const source = `dll-reference ${name}`;
  133. externals[source] = /** @type {string} */ (name);
  134. const normalModuleFactory = params.normalModuleFactory;
  135. new ExternalModuleFactoryPlugin(sourceType || "var", externals).apply(
  136. normalModuleFactory
  137. );
  138. new DelegatedModuleFactoryPlugin({
  139. source,
  140. type: this.options.type,
  141. scope: this.options.scope,
  142. context: this.options.context || compiler.context,
  143. content:
  144. /** @type {DllReferencePluginOptionsContent} */
  145. (resolvedContent),
  146. extensions: this.options.extensions,
  147. associatedObjectForCache: compiler.root
  148. }).apply(normalModuleFactory);
  149. });
  150. compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation, params) => {
  151. if ("manifest" in this.options) {
  152. const manifest = this.options.manifest;
  153. if (typeof manifest === "string") {
  154. const data =
  155. /** @type {CompilationDataItem} */
  156. (compilationData.get(params));
  157. // If there was an error parsing the manifest file, add the
  158. // error as a compilation error to make the compilation fail.
  159. if (data.error) {
  160. compilation.errors.push(
  161. /** @type {DllManifestError} */ (data.error)
  162. );
  163. }
  164. compilation.fileDependencies.add(manifest);
  165. }
  166. }
  167. });
  168. }
  169. }
  170. class DllManifestError extends WebpackError {
  171. /**
  172. * Creates an instance of DllManifestError.
  173. * @param {string} filename filename of the manifest
  174. * @param {string} message error message
  175. */
  176. constructor(filename, message) {
  177. super();
  178. this.name = "DllManifestError";
  179. this.message = `Dll manifest ${filename}\n${message}`;
  180. }
  181. }
  182. module.exports = DllReferencePlugin;