fest: add build.corssorgin (#4472)

This commit is contained in:
Clark Du 2018-12-05 16:21:58 +00:00 committed by Pooya Parsa
parent 9c15c18d1b
commit e6808c57ef
9 changed files with 75 additions and 36 deletions

View File

@ -5,6 +5,7 @@ export default () => ({
analyze: false, analyze: false,
profile: process.argv.includes('--profile'), profile: process.argv.includes('--profile'),
extractCSS: false, extractCSS: false,
crossorigin: undefined,
cssSourceMap: undefined, cssSourceMap: undefined,
ssr: undefined, ssr: undefined,
parallel: false, parallel: false,

View File

@ -52,7 +52,7 @@ export default ({ options, nuxt, renderRoute, resources }) => async function nux
const links = pushAssets const links = pushAssets
? pushAssets(req, res, publicPath, preloadFiles) ? pushAssets(req, res, publicPath, preloadFiles)
: defaultPushAssets(preloadFiles, shouldPush, publicPath, options.dev) : defaultPushAssets(preloadFiles, shouldPush, publicPath, options)
// Pass with single Link header // Pass with single Link header
// https://blog.cloudflare.com/http-2-server-push-with-multiple-assets-per-link-header // https://blog.cloudflare.com/http-2-server-push-with-multiple-assets-per-link-header
@ -87,13 +87,13 @@ export default ({ options, nuxt, renderRoute, resources }) => async function nux
} }
} }
const defaultPushAssets = (preloadFiles, shouldPush, publicPath, isDev) => { const defaultPushAssets = (preloadFiles, shouldPush, publicPath, options) => {
if (shouldPush && isDev) { if (shouldPush && options.dev) {
consola.warn('http2.shouldPush is deprecated. Use http2.pushAssets function') consola.warn('http2.shouldPush is deprecated. Use http2.pushAssets function')
} }
const links = [] const links = []
preloadFiles.forEach(({ file, asType, fileWithoutQuery }) => { preloadFiles.forEach(({ file, asType, fileWithoutQuery, modern }) => {
// By default, we only preload scripts or css // By default, we only preload scripts or css
/* istanbul ignore if */ /* istanbul ignore if */
if (!shouldPush && asType !== 'script' && asType !== 'style') { if (!shouldPush && asType !== 'script' && asType !== 'style') {
@ -105,7 +105,11 @@ const defaultPushAssets = (preloadFiles, shouldPush, publicPath, isDev) => {
return return
} }
links.push(`<${publicPath}${file}>; rel=preload; as=${asType}`) const crossorigin = options.build.crossorigin
const cors = `${crossorigin ? ` crossorigin=${crossorigin};` : ''}`
const ref = modern ? 'modulepreload' : 'preload'
links.push(`<${publicPath}${file}>; rel=${ref};${cors} as=${asType}`)
}) })
return links return links
} }

View File

@ -55,8 +55,10 @@ export default class VueRenderer {
return context.renderScripts().replace(scriptPattern, (scriptTag, jsFile) => { return context.renderScripts().replace(scriptPattern, (scriptTag, jsFile) => {
const legacyJsFile = jsFile.replace(publicPath, '') const legacyJsFile = jsFile.replace(publicPath, '')
const modernJsFile = this.assetsMapping[legacyJsFile] const modernJsFile = this.assetsMapping[legacyJsFile]
const moduleTag = scriptTag.replace('<script', '<script type="module"').replace(legacyJsFile, modernJsFile) const crossorigin = this.context.options.build.crossorigin
const noModuleTag = scriptTag.replace('<script', '<script nomodule') const cors = `${crossorigin ? ` crossorigin="${crossorigin}"` : ''}`
const moduleTag = scriptTag.replace('<script', `<script type="module"${cors}`).replace(legacyJsFile, modernJsFile)
const noModuleTag = scriptTag.replace('<script', `<script nomodule${cors}`)
return noModuleTag + moduleTag return noModuleTag + moduleTag
}) })
} }
@ -66,7 +68,7 @@ export default class VueRenderer {
getModernFiles(legacyFiles = []) { getModernFiles(legacyFiles = []) {
const modernFiles = [] const modernFiles = []
for (const legacyJsFile of legacyFiles) { for (const legacyJsFile of legacyFiles) {
const modernFile = { ...legacyJsFile } const modernFile = { ...legacyJsFile, modern: true }
if (modernFile.asType === 'script') { if (modernFile.asType === 'script') {
const file = this.assetsMapping[legacyJsFile.file] const file = this.assetsMapping[legacyJsFile.file]
modernFile.file = file modernFile.file = file
@ -91,7 +93,9 @@ export default class VueRenderer {
return context.renderResourceHints().replace(linkPattern, (linkTag, jsFile) => { return context.renderResourceHints().replace(linkPattern, (linkTag, jsFile) => {
const legacyJsFile = jsFile.replace(publicPath, '') const legacyJsFile = jsFile.replace(publicPath, '')
const modernJsFile = this.assetsMapping[legacyJsFile] const modernJsFile = this.assetsMapping[legacyJsFile]
return linkTag.replace('rel="preload"', 'rel="modulepreload"').replace(legacyJsFile, modernJsFile) const crossorigin = this.context.options.build.crossorigin
const cors = `${crossorigin ? ` crossorigin="${crossorigin}"` : ''}`
return linkTag.replace('rel="preload"', `rel="modulepreload"${cors}`).replace(legacyJsFile, modernJsFile)
}) })
} }
return context.renderResourceHints() return context.renderResourceHints()

View File

@ -5,6 +5,7 @@ import BundleAnalyzer from 'webpack-bundle-analyzer'
import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin' import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin'
import FriendlyErrorsWebpackPlugin from '@nuxt/friendly-errors-webpack-plugin' import FriendlyErrorsWebpackPlugin from '@nuxt/friendly-errors-webpack-plugin'
import CorsPlugin from '../plugins/vue/cors'
import ModernModePlugin from '../plugins/vue/modern' import ModernModePlugin from '../plugins/vue/modern'
import VueSSRClientPlugin from '../plugins/vue/client' import VueSSRClientPlugin from '../plugins/vue/client'
import WebpackBaseConfig from './base' import WebpackBaseConfig from './base'
@ -124,6 +125,12 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
})) }))
} }
if (this.options.build.crossorigin) {
plugins.push(new CorsPlugin({
crossorigin: this.options.build.crossorigin
}))
}
return plugins return plugins
} }

View File

@ -0,0 +1,22 @@
export default class CorsPlugin {
constructor({ crossorigin }) {
this.crossorigin = crossorigin
}
apply(compiler) {
const ID = `vue-cors-plugin`
compiler.hooks.compilation.tap(ID, (compilation) => {
compilation.hooks.htmlWebpackPluginAlterAssetTags.tap(ID, (data) => {
if (this.crossorigin != null) {
[...data.head, ...data.body].forEach((tag) => {
if (tag.tagName === 'script' || tag.tagName === 'link') {
if (tag.attributes) {
tag.attributes.crossorigin = this.crossorigin
}
}
})
}
})
})
}
}

View File

@ -1,6 +1,7 @@
export default { export default {
modern: true, modern: true,
build: { build: {
crossorigin: 'use-credentials',
filenames: { filenames: {
app: ({ isModern }) => { app: ({ isModern }) => {
return `${isModern ? 'modern-' : ''}[name].js` return `${isModern ? 'modern-' : ''}[name].js`

View File

@ -13,29 +13,29 @@ describe('modern client mode (SSR)', () => {
test('should contain nomodule legacy resources', async () => { test('should contain nomodule legacy resources', async () => {
const response = await rp(url('/')) const response = await rp(url('/'))
expect(response).toContain('script nomodule src="/_nuxt/app.js') expect(response).toContain('script nomodule crossorigin="use-credentials" src="/_nuxt/app.js')
expect(response).toContain('script nomodule src="/_nuxt/commons.app.js') expect(response).toContain('script nomodule crossorigin="use-credentials" src="/_nuxt/commons.app.js')
}) })
test('should contain module modern resources', async () => { test('should contain module modern resources', async () => {
const response = await rp(url('/')) const response = await rp(url('/'))
expect(response).toContain('<script type="module" src="/_nuxt/modern-app.js"') expect(response).toContain('<script type="module" crossorigin="use-credentials" src="/_nuxt/modern-app.js"')
expect(response).toContain('<script type="module" src="/_nuxt/modern-commons.app.js"') expect(response).toContain('<script type="module" crossorigin="use-credentials" src="/_nuxt/modern-commons.app.js"')
}) })
test('should contain module preload resources', async () => { test('should contain module preload resources', async () => {
const response = await rp(url('/')) const response = await rp(url('/'))
expect(response).toContain('<link rel="modulepreload" href="/_nuxt/modern-app.js" as="script">') expect(response).toContain('<link rel="modulepreload" crossorigin="use-credentials" href="/_nuxt/modern-app.js" as="script">')
expect(response).toContain('<link rel="modulepreload" href="/_nuxt/modern-commons.app.js" as="script">') expect(response).toContain('<link rel="modulepreload" crossorigin="use-credentials" href="/_nuxt/modern-commons.app.js" as="script">')
}) })
test('should contain module http2 pushed resources', async () => { test('should contain module http2 pushed resources', async () => {
const { headers: { link } } = await rp(url('/'), { resolveWithFullResponse: true }) const { headers: { link } } = await rp(url('/'), { resolveWithFullResponse: true })
expect(link).toEqual([ expect(link).toEqual([
'</_nuxt/modern-runtime.js>; rel=preload; as=script', '</_nuxt/modern-runtime.js>; rel=modulepreload; crossorigin=use-credentials; as=script',
'</_nuxt/modern-commons.app.js>; rel=preload; as=script', '</_nuxt/modern-commons.app.js>; rel=modulepreload; crossorigin=use-credentials; as=script',
'</_nuxt/modern-app.js>; rel=preload; as=script', '</_nuxt/modern-app.js>; rel=modulepreload; crossorigin=use-credentials; as=script',
`</_nuxt/modern-${wChunk('pages/index.js')}>; rel=preload; as=script` `</_nuxt/modern-${wChunk('pages/index.js')}>; rel=modulepreload; crossorigin=use-credentials; as=script`
].join(', ')) ].join(', '))
}) })

View File

@ -47,10 +47,10 @@ describe('modern server mode', () => {
resolveWithFullResponse: true resolveWithFullResponse: true
}) })
expect(link).toEqual([ expect(link).toEqual([
'</_nuxt/runtime.js>; rel=preload; as=script', '</_nuxt/runtime.js>; rel=preload; crossorigin=use-credentials; as=script',
'</_nuxt/commons.app.js>; rel=preload; as=script', '</_nuxt/commons.app.js>; rel=preload; crossorigin=use-credentials; as=script',
'</_nuxt/app.js>; rel=preload; as=script', '</_nuxt/app.js>; rel=preload; crossorigin=use-credentials; as=script',
`</_nuxt/${wChunk('pages/index.js')}>; rel=preload; as=script` `</_nuxt/${wChunk('pages/index.js')}>; rel=preload; crossorigin=use-credentials; as=script`
].join(', ')) ].join(', '))
}) })
@ -60,10 +60,10 @@ describe('modern server mode', () => {
resolveWithFullResponse: true resolveWithFullResponse: true
}) })
expect(link).toEqual([ expect(link).toEqual([
'</_nuxt/modern-runtime.js>; rel=preload; as=script', '</_nuxt/modern-runtime.js>; rel=preload; crossorigin=use-credentials; as=script',
'</_nuxt/modern-commons.app.js>; rel=preload; as=script', '</_nuxt/modern-commons.app.js>; rel=preload; crossorigin=use-credentials; as=script',
'</_nuxt/modern-app.js>; rel=preload; as=script', '</_nuxt/modern-app.js>; rel=preload; crossorigin=use-credentials; as=script',
`</_nuxt/modern-${wChunk('pages/index.js')}>; rel=preload; as=script` `</_nuxt/modern-${wChunk('pages/index.js')}>; rel=preload; crossorigin=use-credentials; as=script`
].join(', ')) ].join(', '))
}) })

View File

@ -21,28 +21,28 @@ describe('modern client mode (SPA)', () => {
test('should contain nomodule legacy resources', async () => { test('should contain nomodule legacy resources', async () => {
const response = await rp(url('/')) const response = await rp(url('/'))
expect(response).toContain('src="/_nuxt/app.js" nomodule') expect(response).toContain('src="/_nuxt/app.js" crossorigin="use-credentials" nomodule')
expect(response).toContain('src="/_nuxt/commons.app.js" nomodule') expect(response).toContain('src="/_nuxt/commons.app.js" crossorigin="use-credentials" nomodule')
}) })
test('should contain module modern resources', async () => { test('should contain module modern resources', async () => {
const response = await rp(url('/')) const response = await rp(url('/'))
expect(response).toContain('<script type="module" src="/_nuxt/modern-app.js"') expect(response).toContain('<script type="module" src="/_nuxt/modern-app.js" crossorigin="use-credentials"')
expect(response).toContain('<script type="module" src="/_nuxt/modern-commons.app.js"') expect(response).toContain('<script type="module" src="/_nuxt/modern-commons.app.js" crossorigin="use-credentials"')
}) })
test.skip('should contain module preload resources', async () => { test.skip('should contain module preload resources', async () => {
const response = await rp(url('/')) const response = await rp(url('/'))
expect(response).toContain('<link rel="modulepreload" href="/_nuxt/modern-app.js" as="script">') expect(response).toContain('<link rel="modulepreload" crossorigin="use-credentials" href="/_nuxt/modern-app.js" as="script">')
expect(response).toContain('<link rel="modulepreload" href="/_nuxt/modern-commons.app.js" as="script">') expect(response).toContain('<link rel="modulepreload" crossorigin="use-credentials" href="/_nuxt/modern-commons.app.js" as="script">')
}) })
test('should contain module http2 pushed resources', async () => { test('should contain module http2 pushed resources', async () => {
const { headers: { link } } = await rp(url('/'), { resolveWithFullResponse: true }) const { headers: { link } } = await rp(url('/'), { resolveWithFullResponse: true })
expect(link).toEqual([ expect(link).toEqual([
'</_nuxt/modern-runtime.js>; rel=preload; as=script', '</_nuxt/modern-runtime.js>; rel=modulepreload; crossorigin=use-credentials; as=script',
'</_nuxt/modern-commons.app.js>; rel=preload; as=script', '</_nuxt/modern-commons.app.js>; rel=modulepreload; crossorigin=use-credentials; as=script',
'</_nuxt/modern-app.js>; rel=preload; as=script' '</_nuxt/modern-app.js>; rel=modulepreload; crossorigin=use-credentials; as=script'
].join(', ')) ].join(', '))
}) })