BannerPlugin.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  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 basename;
  71. let query = "";
  72. let filename = file;
  73. const hash = compilation.hash;
  74. const querySplit = filename.indexOf("?");
  75. if (querySplit >= 0) {
  76. query = filename.substr(querySplit);
  77. filename = filename.substr(0, querySplit);
  78. }
  79. const lastSlashIndex = filename.lastIndexOf("/");
  80. if (lastSlashIndex === -1) {
  81. basename = filename;
  82. } else {
  83. basename = filename.substr(lastSlashIndex + 1);
  84. }
  85. const data = {
  86. hash,
  87. chunk,
  88. filename,
  89. basename,
  90. query
  91. };
  92. const comment = compilation.getPath(banner(data), data);
  93. compilation.assets[file] = new ConcatSource(
  94. comment,
  95. "\n",
  96. compilation.assets[file]
  97. );
  98. }
  99. }
  100. });
  101. });
  102. }
  103. }
  104. module.exports = BannerPlugin;