BannerPlugin.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { ConcatSource } = require("webpack-sources");
  7. const Compilation = require("./Compilation");
  8. const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
  9. const Template = require("./Template");
  10. const createSchemaValidation = require("./util/create-schema-validation");
  11. /** @typedef {import("webpack-sources").Source} Source */
  12. /** @typedef {import("../declarations/plugins/BannerPlugin").BannerPluginArgument} BannerPluginArgument */
  13. /** @typedef {import("./Compilation").PathData} PathData */
  14. /** @typedef {import("./Compiler")} Compiler */
  15. /** @typedef {import("./Chunk")} Chunk */
  16. /** @typedef {import("./TemplatedPathPlugin").TemplatePath} TemplatePath */
  17. /** @typedef {(data: { hash?: string, chunk: Chunk, filename: string }) => string} BannerFunction */
  18. const validate = createSchemaValidation(
  19. /** @type {((value: typeof import("../schemas/plugins/BannerPlugin.json")) => boolean)} */
  20. (require("../schemas/plugins/BannerPlugin.check")),
  21. () => require("../schemas/plugins/BannerPlugin.json"),
  22. {
  23. name: "Banner Plugin",
  24. baseDataPath: "options"
  25. }
  26. );
  27. /**
  28. * @param {string} str string to wrap
  29. * @returns {string} wrapped string
  30. */
  31. const wrapComment = (str) => {
  32. if (!str.includes("\n")) {
  33. return Template.toComment(str);
  34. }
  35. return `/*!\n * ${str
  36. .replace(/\*\//g, "* /")
  37. .split("\n")
  38. .join("\n * ")
  39. .replace(/\s+\n/g, "\n")
  40. .trimEnd()}\n */`;
  41. };
  42. const PLUGIN_NAME = "BannerPlugin";
  43. class BannerPlugin {
  44. /**
  45. * @param {BannerPluginArgument} options options object
  46. */
  47. constructor(options) {
  48. if (typeof options === "string" || typeof options === "function") {
  49. options = {
  50. banner: options
  51. };
  52. }
  53. validate(options);
  54. this.options = options;
  55. const bannerOption = options.banner;
  56. if (typeof bannerOption === "function") {
  57. const getBanner = bannerOption;
  58. /** @type {BannerFunction} */
  59. this.banner = this.options.raw
  60. ? getBanner
  61. : /** @type {BannerFunction} */ (data) => wrapComment(getBanner(data));
  62. } else {
  63. const banner = this.options.raw
  64. ? bannerOption
  65. : wrapComment(bannerOption);
  66. /** @type {BannerFunction} */
  67. this.banner = () => banner;
  68. }
  69. }
  70. /**
  71. * Apply the plugin
  72. * @param {Compiler} compiler the compiler instance
  73. * @returns {void}
  74. */
  75. apply(compiler) {
  76. const options = this.options;
  77. const banner = this.banner;
  78. const matchObject = ModuleFilenameHelpers.matchObject.bind(
  79. undefined,
  80. options
  81. );
  82. /** @type {WeakMap<Source, { source: ConcatSource, comment: string }>} */
  83. const cache = new WeakMap();
  84. const stage =
  85. this.options.stage || Compilation.PROCESS_ASSETS_STAGE_ADDITIONS;
  86. compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
  87. compilation.hooks.processAssets.tap({ name: PLUGIN_NAME, stage }, () => {
  88. for (const chunk of compilation.chunks) {
  89. if (options.entryOnly && !chunk.canBeInitial()) {
  90. continue;
  91. }
  92. for (const file of chunk.files) {
  93. if (!matchObject(file)) {
  94. continue;
  95. }
  96. /** @type {PathData} */
  97. const data = { chunk, filename: file };
  98. const comment = compilation.getPath(
  99. /** @type {TemplatePath} */
  100. (banner),
  101. data
  102. );
  103. compilation.updateAsset(file, (old) => {
  104. const cached = cache.get(old);
  105. if (!cached || cached.comment !== comment) {
  106. const source = options.footer
  107. ? new ConcatSource(old, "\n", comment)
  108. : new ConcatSource(comment, "\n", old);
  109. cache.set(old, { source, comment });
  110. return source;
  111. }
  112. return cached.source;
  113. });
  114. }
  115. }
  116. });
  117. });
  118. }
  119. }
  120. module.exports = BannerPlugin;