BannerPlugin.js 3.2 KB

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