BannerPlugin.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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 ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
  8. const Template = require("./Template");
  9. const validateOptions = require("schema-utils");
  10. const schema = require("../schemas/plugins/BannerPlugin.json");
  11. /** @typedef {import("../declarations/plugins/BannerPlugin").BannerPluginArgument} BannerPluginArgument */
  12. /** @typedef {import("../declarations/plugins/BannerPlugin").BannerPluginOptions} BannerPluginOptions */
  13. const wrapComment = str => {
  14. if (!str.includes("\n")) {
  15. return Template.toComment(str);
  16. }
  17. return `/*!\n * ${str
  18. .replace(/\*\//g, "* /")
  19. .split("\n")
  20. .join("\n * ")}\n */`;
  21. };
  22. class BannerPlugin {
  23. /**
  24. * @param {BannerPluginArgument} options options object
  25. */
  26. constructor(options) {
  27. if (arguments.length > 1) {
  28. throw new Error(
  29. "BannerPlugin only takes one argument (pass an options object)"
  30. );
  31. }
  32. validateOptions(schema, options, "Banner Plugin");
  33. if (typeof options === "string" || typeof options === "function") {
  34. options = {
  35. banner: options
  36. };
  37. }
  38. /** @type {BannerPluginOptions} */
  39. this.options = options;
  40. const bannerOption = options.banner;
  41. if (typeof bannerOption === "function") {
  42. const getBanner = bannerOption;
  43. this.banner = this.options.raw
  44. ? getBanner
  45. : data => wrapComment(getBanner(data));
  46. } else {
  47. const banner = this.options.raw
  48. ? bannerOption
  49. : wrapComment(bannerOption);
  50. this.banner = () => banner;
  51. }
  52. }
  53. apply(compiler) {
  54. const options = this.options;
  55. const banner = this.banner;
  56. const matchObject = ModuleFilenameHelpers.matchObject.bind(
  57. undefined,
  58. options
  59. );
  60. compiler.hooks.compilation.tap("BannerPlugin", compilation => {
  61. compilation.hooks.optimizeChunkAssets.tap("BannerPlugin", chunks => {
  62. for (const chunk of chunks) {
  63. if (options.entryOnly && !chunk.canBeInitial()) {
  64. continue;
  65. }
  66. for (const file of chunk.files) {
  67. if (!matchObject(file)) {
  68. continue;
  69. }
  70. let query = "";
  71. let filename = file;
  72. const hash = compilation.hash;
  73. const querySplit = filename.indexOf("?");
  74. if (querySplit >= 0) {
  75. query = filename.substr(querySplit);
  76. filename = filename.substr(0, querySplit);
  77. }
  78. const lastSlashIndex = filename.lastIndexOf("/");
  79. const basename =
  80. lastSlashIndex === -1
  81. ? filename
  82. : filename.substr(lastSlashIndex + 1);
  83. const data = {
  84. hash,
  85. chunk,
  86. filename,
  87. basename,
  88. query
  89. };
  90. const comment = compilation.getPath(banner(data), data);
  91. compilation.updateAsset(
  92. file,
  93. old => new ConcatSource(comment, "\n", old)
  94. );
  95. }
  96. }
  97. });
  98. });
  99. }
  100. }
  101. module.exports = BannerPlugin;