112 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			112 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const fs = require('fs-extra')
 | |
| const path = require('path')
 | |
| 
 | |
| // https://gist.github.com/samthor/64b114e4a4f539915a95b91ffd340acc
 | |
| const safariFix = `!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()},!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();`
 | |
| 
 | |
| class ModernModePlugin {
 | |
|   constructor ({ targetDir, isModernBuild, unsafeInline, jsDirectory }) {
 | |
|     this.targetDir = targetDir
 | |
|     this.isModernBuild = isModernBuild
 | |
|     this.unsafeInline = unsafeInline
 | |
|     this.jsDirectory = jsDirectory
 | |
|   }
 | |
| 
 | |
|   apply (compiler) {
 | |
|     if (!this.isModernBuild) {
 | |
|       this.applyLegacy(compiler)
 | |
|     } else {
 | |
|       this.applyModern(compiler)
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   applyLegacy (compiler) {
 | |
|     const ID = `vue-cli-legacy-bundle`
 | |
|     compiler.hooks.compilation.tap(ID, compilation => {
 | |
|       compilation.hooks.htmlWebpackPluginAlterAssetTags.tapAsync(ID, async (data, cb) => {
 | |
|         // get stats, write to disk
 | |
|         await fs.ensureDir(this.targetDir)
 | |
|         const htmlName = path.basename(data.plugin.options.filename)
 | |
|         // Watch out for output files in sub directories
 | |
|         const htmlPath = path.dirname(data.plugin.options.filename)
 | |
|         const tempFilename = path.join(this.targetDir, htmlPath, `legacy-assets-${htmlName}.json`)
 | |
|         await fs.mkdirp(path.dirname(tempFilename))
 | |
|         await fs.writeFile(tempFilename, JSON.stringify(data.body))
 | |
|         cb()
 | |
|       })
 | |
|     })
 | |
|   }
 | |
| 
 | |
|   applyModern (compiler) {
 | |
|     const ID = `vue-cli-modern-bundle`
 | |
|     compiler.hooks.compilation.tap(ID, compilation => {
 | |
|       compilation.hooks.htmlWebpackPluginAlterAssetTags.tapAsync(ID, async (data, cb) => {
 | |
|         // use <script type="module"> for modern assets
 | |
|         data.body.forEach(tag => {
 | |
|           if (tag.tagName === 'script' && tag.attributes) {
 | |
|             tag.attributes.type = 'module'
 | |
|           }
 | |
|         })
 | |
| 
 | |
|         // use <link rel="modulepreload"> instead of <link rel="preload">
 | |
|         // for modern assets
 | |
|         data.head.forEach(tag => {
 | |
|           if (tag.tagName === 'link' &&
 | |
|               tag.attributes.rel === 'preload' &&
 | |
|               tag.attributes.as === 'script') {
 | |
|             tag.attributes.rel = 'modulepreload'
 | |
|           }
 | |
|         })
 | |
| 
 | |
|         // inject links for legacy assets as <script nomodule>
 | |
|         const htmlName = path.basename(data.plugin.options.filename)
 | |
|         // Watch out for output files in sub directories
 | |
|         const htmlPath = path.dirname(data.plugin.options.filename)
 | |
|         const tempFilename = path.join(this.targetDir, htmlPath, `legacy-assets-${htmlName}.json`)
 | |
|         const legacyAssets = JSON.parse(await fs.readFile(tempFilename, 'utf-8'))
 | |
|           .filter(a => a.tagName === 'script' && a.attributes)
 | |
|         legacyAssets.forEach(a => { a.attributes.nomodule = '' })
 | |
| 
 | |
|         if (this.unsafeInline) {
 | |
|           // inject inline Safari 10 nomodule fix
 | |
|           data.body.push({
 | |
|             tagName: 'script',
 | |
|             closeTag: true,
 | |
|             innerHTML: safariFix
 | |
|           })
 | |
|         } else {
 | |
|           // inject the fix as an external script
 | |
|           const safariFixPath = path.join(this.jsDirectory, 'safari-nomodule-fix.js')
 | |
|           const fullSafariFixPath = path.join(compilation.options.output.publicPath, safariFixPath)
 | |
|           compilation.assets[safariFixPath] = {
 | |
|             source: function () {
 | |
|               return new Buffer(safariFix)
 | |
|             },
 | |
|             size: function () {
 | |
|               return Buffer.byteLength(safariFix)
 | |
|             }
 | |
|           }
 | |
|           data.body.push({
 | |
|             tagName: 'script',
 | |
|             closeTag: true,
 | |
|             attributes: {
 | |
|               src: fullSafariFixPath
 | |
|             }
 | |
|           })
 | |
|         }
 | |
| 
 | |
|         data.body.push(...legacyAssets)
 | |
|         await fs.remove(tempFilename)
 | |
|         cb()
 | |
|       })
 | |
| 
 | |
|       compilation.hooks.htmlWebpackPluginAfterHtmlProcessing.tap(ID, data => {
 | |
|         data.html = data.html.replace(/\snomodule="">/g, ' nomodule>')
 | |
|       })
 | |
|     })
 | |
|   }
 | |
| }
 | |
| 
 | |
| ModernModePlugin.safariFix = safariFix
 | |
| module.exports = ModernModePlugin
 | 
