diff --git a/packages/config/src/config/build.js b/packages/config/src/config/build.js index ce3706c92a..32b15274cb 100644 --- a/packages/config/src/config/build.js +++ b/packages/config/src/config/build.js @@ -5,6 +5,7 @@ export default () => ({ analyze: false, profile: process.argv.includes('--profile'), extractCSS: false, + crossorigin: undefined, cssSourceMap: undefined, ssr: undefined, parallel: false, diff --git a/packages/server/src/middleware/nuxt.js b/packages/server/src/middleware/nuxt.js index 8908301d37..628e234fb8 100644 --- a/packages/server/src/middleware/nuxt.js +++ b/packages/server/src/middleware/nuxt.js @@ -52,7 +52,7 @@ export default ({ options, nuxt, renderRoute, resources }) => async function nux const links = pushAssets ? pushAssets(req, res, publicPath, preloadFiles) - : defaultPushAssets(preloadFiles, shouldPush, publicPath, options.dev) + : defaultPushAssets(preloadFiles, shouldPush, publicPath, options) // Pass with single 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) => { - if (shouldPush && isDev) { +const defaultPushAssets = (preloadFiles, shouldPush, publicPath, options) => { + if (shouldPush && options.dev) { consola.warn('http2.shouldPush is deprecated. Use http2.pushAssets function') } const links = [] - preloadFiles.forEach(({ file, asType, fileWithoutQuery }) => { + preloadFiles.forEach(({ file, asType, fileWithoutQuery, modern }) => { // By default, we only preload scripts or css /* istanbul ignore if */ if (!shouldPush && asType !== 'script' && asType !== 'style') { @@ -105,7 +105,11 @@ const defaultPushAssets = (preloadFiles, shouldPush, publicPath, isDev) => { 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 } diff --git a/packages/vue-renderer/src/renderer.js b/packages/vue-renderer/src/renderer.js index 06835e75c9..c7db0ed990 100644 --- a/packages/vue-renderer/src/renderer.js +++ b/packages/vue-renderer/src/renderer.js @@ -55,8 +55,10 @@ export default class VueRenderer { return context.renderScripts().replace(scriptPattern, (scriptTag, jsFile) => { const legacyJsFile = jsFile.replace(publicPath, '') const modernJsFile = this.assetsMapping[legacyJsFile] - const moduleTag = scriptTag.replace(' { const legacyJsFile = jsFile.replace(publicPath, '') 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() diff --git a/packages/webpack/src/config/client.js b/packages/webpack/src/config/client.js index 7b71794a38..c69b0fbbe8 100644 --- a/packages/webpack/src/config/client.js +++ b/packages/webpack/src/config/client.js @@ -5,6 +5,7 @@ import BundleAnalyzer from 'webpack-bundle-analyzer' import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin' import FriendlyErrorsWebpackPlugin from '@nuxt/friendly-errors-webpack-plugin' +import CorsPlugin from '../plugins/vue/cors' import ModernModePlugin from '../plugins/vue/modern' import VueSSRClientPlugin from '../plugins/vue/client' 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 } diff --git a/packages/webpack/src/plugins/vue/cors.js b/packages/webpack/src/plugins/vue/cors.js new file mode 100644 index 0000000000..8ded6acfb0 --- /dev/null +++ b/packages/webpack/src/plugins/vue/cors.js @@ -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 + } + } + }) + } + }) + }) + } +} diff --git a/test/fixtures/modern/nuxt.config.js b/test/fixtures/modern/nuxt.config.js index 438beb95db..5cb35a8467 100644 --- a/test/fixtures/modern/nuxt.config.js +++ b/test/fixtures/modern/nuxt.config.js @@ -1,6 +1,7 @@ export default { modern: true, build: { + crossorigin: 'use-credentials', filenames: { app: ({ isModern }) => { return `${isModern ? 'modern-' : ''}[name].js` diff --git a/test/unit/modern.client.test.js b/test/unit/modern.client.test.js index 4c884a3719..17af063ab0 100644 --- a/test/unit/modern.client.test.js +++ b/test/unit/modern.client.test.js @@ -13,29 +13,29 @@ describe('modern client mode (SSR)', () => { test('should contain nomodule legacy resources', async () => { const response = await rp(url('/')) - expect(response).toContain('script nomodule src="/_nuxt/app.js') - expect(response).toContain('script nomodule src="/_nuxt/commons.app.js') + expect(response).toContain('script nomodule crossorigin="use-credentials" src="/_nuxt/app.js') + expect(response).toContain('script nomodule crossorigin="use-credentials" src="/_nuxt/commons.app.js') }) test('should contain module modern resources', async () => { const response = await rp(url('/')) - expect(response).toContain('