|
| 1 | +'use strict'; |
| 2 | + |
| 3 | +const path = require('path'); |
| 4 | +const HtmlWebpackPlugin = require('html-webpack-plugin'); |
| 5 | +const fs = require('fs-extra'); |
| 6 | + |
| 7 | +const ID = 'html-webpack-esmodules-plugin'; |
| 8 | + |
| 9 | +const safariFix = `(function(){var d=document;var c=d.createElement('script');if(!('noModule' in c)&&'onbeforeload' in c){var s=!1;d.addEventListener('beforeload',function(e){if(e.target===c){s=!0}else if(!e.target.hasAttribute('nomodule')||!s){return}e.preventDefault()},!0);c.type='module';c.src='.';d.head.appendChild(c);c.remove()}}())`; |
| 10 | + |
| 11 | +class HtmlWebpackEsmodulesPlugin { |
| 12 | + constructor() {} |
| 13 | + |
| 14 | + apply(compiler) { |
| 15 | + compiler.hooks.compilation.tap(ID, compilation => { |
| 16 | + HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups.tapAsync( |
| 17 | + ID, |
| 18 | + ({ plugin, bodyTags: body }, cb) => { |
| 19 | + const targetDir = compiler.options.output.path; |
| 20 | + // get stats, write to disk |
| 21 | + const htmlName = path.basename(plugin.options.filename); |
| 22 | + // Watch out for output files in sub directories |
| 23 | + const htmlPath = path.dirname(plugin.options.filename); |
| 24 | + const tempFilename = path.join( |
| 25 | + targetDir, |
| 26 | + htmlPath, |
| 27 | + `assets-${htmlName}.json` |
| 28 | + ); |
| 29 | + |
| 30 | + if (!fs.existsSync(tempFilename)) { |
| 31 | + fs.mkdirpSync(path.dirname(tempFilename)); |
| 32 | + const newBody = body.filter( |
| 33 | + a => a.tagName === 'script' && a.attributes |
| 34 | + ); |
| 35 | + newBody.forEach(a => (a.attributes.nomodule = '')); |
| 36 | + fs.writeFileSync(tempFilename, JSON.stringify(newBody)); |
| 37 | + return cb(); |
| 38 | + } |
| 39 | + |
| 40 | + const legacyAssets = JSON.parse( |
| 41 | + fs.readFileSync(tempFilename, 'utf-8') |
| 42 | + ); |
| 43 | + // TODO: to discuss, an improvement would be to |
| 44 | + // Inject these into the head tag together with the |
| 45 | + // Safari script. |
| 46 | + body.forEach(tag => { |
| 47 | + if (tag.tagName === 'script' && tag.attributes) { |
| 48 | + tag.attributes.type = 'module'; |
| 49 | + } |
| 50 | + }); |
| 51 | + |
| 52 | + body.push({ |
| 53 | + tagName: 'script', |
| 54 | + closeTag: true, |
| 55 | + innerHTML: safariFix, |
| 56 | + }); |
| 57 | + |
| 58 | + body.push(...legacyAssets); |
| 59 | + fs.removeSync(tempFilename); |
| 60 | + cb(); |
| 61 | + } |
| 62 | + ); |
| 63 | + |
| 64 | + HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tap(ID, data => { |
| 65 | + data.html = data.html.replace(/\snomodule="">/g, ' nomodule>'); |
| 66 | + }); |
| 67 | + }); |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +module.exports = HtmlWebpackEsmodulesPlugin; |
0 commit comments