123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- 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
|