CreateHtmlPlugin.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. const path = require('path');
  2. // 缓存已读取过的数据
  3. let cache = {};
  4. const createHtml = require('./createHtml');
  5. const getTemplate = require('./getTemplate');
  6. class CreateHtmlPlugin {
  7. constructor({templateMap, beforeAppendCss, beforeAppendJs}) {
  8. // 存储静态文件相关信息, 包含css和js信息
  9. this.assetsMap = {};
  10. // 存储已经更新过的文件路径,并且标记是否需要更新
  11. this.updated = {};
  12. // 入口对应的html文件绝对路径
  13. this.templateMap = getTemplate(templateMap);
  14. // 在插入css前需要处理的函数
  15. this.beforeAppendCss = beforeAppendCss;
  16. // 在插入js前需要处理的函数
  17. this.beforeAppendJs = beforeAppendJs;
  18. this.apply = this.apply.bind(this);
  19. }
  20. apply(compiler) {
  21. const {output} = compiler.options;
  22. compiler.hooks.done.tapAsync('CreateHtmlPlugin', (stat, callback) => {
  23. // 取到生成的静态文件路径
  24. let assetsList = Object.keys(stat.compilation.assets);
  25. assetsList.map((item) => {
  26. // 解析路径信息成一个对象
  27. let fileInfo = path.parse(item);
  28. // 只对css和js文件进行处理
  29. if (fileInfo.ext === '.js' || fileInfo.ext === '.css') {
  30. if (!this.assetsMap[fileInfo.dir]) {
  31. this.assetsMap[fileInfo.dir] = {};
  32. }
  33. let currentFile = this.assetsMap[fileInfo.dir];
  34. // 当assetsMap没有存储过当前文件信息,或者当前存储的文件名字改变了
  35. // 就说明有更新,需要重新进行编译
  36. // 文件名字改变针对的是文件名带有hash值
  37. if (
  38. !this.assetsMap[fileInfo.dir][fileInfo.ext] ||
  39. this.assetsMap[fileInfo.dir][fileInfo.ext].name !== fileInfo.name
  40. ) {
  41. // 将需要更新标记置为true
  42. this.updated[fileInfo.dir] = true;
  43. // 并且将存储的信息更新成当前信息
  44. currentFile[fileInfo.ext] = fileInfo;
  45. }
  46. // 判断是否需要更新html
  47. if (this.updated[fileInfo.dir]) {
  48. // 更新前提是js和css都要有
  49. if (
  50. this.assetsMap[fileInfo.dir]['.css'] &&
  51. this.assetsMap[fileInfo.dir]['.js']
  52. ) {
  53. // 更新过的文件需要将标记置否
  54. this.updated[fileInfo.dir] = false;
  55. // 拿到默认的模版内容
  56. let content = this.templateMap['default'].content;
  57. // 拿到默认模版的文件名
  58. let templateName = path.basename(this.templateMap['default'].templatePath);
  59. // 将静态文件路径和模版一一对应起来,缓存起来
  60. // 如果有缓存,直接读取缓存
  61. if (cache[item]) {
  62. content = cache[item]
  63. } else {
  64. for (let key in this.templateMap) {
  65. // 如果静态文件路径和模版定义的入口路径匹配,则取匹配模版
  66. if (item.indexOf(key) >= 0) {
  67. // 缓存模版
  68. cache[item] = this.templateMap[key];
  69. // 取出对应模版内容
  70. content = this.templateMap[key].content;
  71. // 取出对应模版名字
  72. templateName = path.basename(this.templateMap[key].templatePath);
  73. break;
  74. } else {
  75. // 没有对应模版的入口文件,则取默认模版
  76. cache[item] = this.templateMap['default'].content;
  77. }
  78. }
  79. }
  80. /**
  81. * 适配移动端模板
  82. * 判断是否html模板中含有configjs或configcss
  83. * 没有就不能添加,否则会出现移动端引用uap文件报错
  84. */
  85. let hasAppendJs = /<%= configjs %>/.test(content);
  86. let hasAppendCss = /<%= configcss %>/.test(content);
  87. content = content.replace(/(<%= configjs %>|<%= configcss %>)/g, '');
  88. // 生成html文件
  89. createHtml(this.assetsMap[fileInfo.dir], content, templateName, output.publicPath, output.path, hasAppendCss && this.beforeAppendCss, hasAppendJs && this.beforeAppendJs);
  90. }
  91. }
  92. }
  93. });
  94. callback();
  95. })
  96. }
  97. }
  98. module.exports = CreateHtmlPlugin;