DllReferencePlugin.js 5.5 KB

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