BannerPlugin.js 3.6 KB

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