Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(node/build): add crossOrigin configuration for modulePreload links #13136

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/config/build-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ Note the build will fail if the code contains features that cannot be safely tra

## build.modulePreload

- **Type:** `boolean | { polyfill?: boolean, resolveDependencies?: ResolveModulePreloadDependenciesFn }`
- **Default:** `{ polyfill: true }`
- **Type:** `boolean | { crossOrigin?: boolean | 'anonymous' | 'use-credentials', polyfill?: boolean, resolveDependencies?: ResolveModulePreloadDependenciesFn }`
- **Default:** `{ polyfill: true, crossOrigin: true }`

By default, a [module preload polyfill](https://guybedford.com/es-module-preloading-integrity#modulepreload-polyfill) is automatically injected. The polyfill is auto injected into the proxy module of each `index.html` entry. If the build is configured to use a non-HTML custom entry via `build.rollupOptions.input`, then it is necessary to manually import the polyfill in your custom entry:

Expand Down Expand Up @@ -66,6 +66,8 @@ modulePreload: {

The resolved dependency paths can be further modified using [`experimental.renderBuiltUrl`](../guide/build.md#advanced-base-options).

The `crossOrigin` option controls setting the `crossorigin` attribute on preloaded dynamic imports chunks. This is true by default and can be disabled using `{ crossOrigin: false }`. It can also be set to one of the string values accepted by the [link tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#acrossorigin).

## build.polyfillModulePreload

- **Type:** `boolean`
Expand Down
7 changes: 7 additions & 0 deletions packages/vite/src/node/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,11 @@ export interface LibraryOptions {
export type LibraryFormats = 'es' | 'cjs' | 'umd' | 'iife' | 'system'

export interface ModulePreloadOptions {
/**
* Whether to add crossorigin attribute to dynamic imported JS link preloads, accepts boolean or string value used by link tag
* @default true
*/
crossOrigin?: boolean | 'anonymous' | 'use-credentials'
/**
* Whether to inject a module preload polyfill.
* Note: does not apply to library mode.
Expand All @@ -288,6 +293,7 @@ export interface ModulePreloadOptions {
resolveDependencies?: ResolveModulePreloadDependenciesFn
}
export interface ResolvedModulePreloadOptions {
crossOrigin: boolean | 'anonymous' | 'use-credentials'
polyfill: boolean
resolveDependencies?: ResolveModulePreloadDependenciesFn
}
Expand Down Expand Up @@ -330,6 +336,7 @@ export function resolveBuildOptions(

const modulePreload = raw?.modulePreload
const defaultModulePreload = {
crossOrigin: true,
polyfill: true,
}

Expand Down
14 changes: 12 additions & 2 deletions packages/vite/src/node/plugins/importAnalysisBuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,11 @@ function preload(
link.rel = isCss ? 'stylesheet' : scriptRel
if (!isCss) {
link.as = 'script'
link.crossOrigin = ''
// @ts-expect-error crossOrigin is declared before preload.toString()
if (crossOrigin)
link.crossOrigin =
// @ts-expect-error crossOrigin is declared before preload.toString()
typeof crossOrigin === 'string' ? crossOrigin : ''
}
link.href = dep
if (cspNonce) {
Expand Down Expand Up @@ -162,6 +166,8 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {

const resolveModulePreloadDependencies =
config.build.modulePreload && config.build.modulePreload.resolveDependencies
const modulePreloadCrossOrigin =
config.build.modulePreload && config.build.modulePreload.crossOrigin
const renderBuiltUrl = config.experimental.renderBuiltUrl
const customModulePreloadPaths = !!(
resolveModulePreloadDependencies || renderBuiltUrl
Expand Down Expand Up @@ -196,7 +202,11 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
: // If the base isn't relative, then the deps are relative to the projects `outDir` and the base
// is appended inside __vitePreload too.
`function(dep) { return ${JSON.stringify(config.base)}+dep }`
const preloadCode = `const scriptRel = ${scriptRel};const assetsURL = ${assetsURL};const seen = {};export const ${preloadMethod} = ${preload.toString()}`
const crossOrigin =
typeof modulePreloadCrossOrigin === 'string'
? `'${modulePreloadCrossOrigin}'`
: modulePreloadCrossOrigin
const preloadCode = `const scriptRel = ${scriptRel};const assetsURL = ${assetsURL};const seen = {};const crossOrigin = ${crossOrigin};export const ${preloadMethod} = ${preload.toString()}`

return {
name: 'vite:build-import-analysis',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { describe, expect, test } from 'vitest'
import { browserLogs, isBuild, page } from '~utils'

test('should have no 404s', () => {
browserLogs.forEach((msg) => {
expect(msg).not.toMatch('404')
})
})

describe.runIf(isBuild)('build', () => {
test('dynamic import', async () => {
await page.waitForSelector('#done')
expect(await page.textContent('#done')).toBe('ran js')
})

test('dynamic import with crossorigin set to use-credentials', async () => {
await page.click('#hello .load')
await page.waitForSelector('#hello output')

const html = await page.content()
expect(html).toMatch(
/link rel="modulepreload" as="script" crossorigin="use-credentials".*?href=".*?\/hello-\w{8}\.js"/,
)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '../../vite.config-preload-credentials'
6 changes: 5 additions & 1 deletion playground/preload/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
"dev:preload-disabled": "vite --config vite.config-preload-disabled.ts",
"build:preload-disabled": "vite build --config vite.config-preload-disabled.ts",
"debug:preload-disabled": "node --inspect-brk ../../packages/vite/bin/vite --config vite.config-preload-disabled.ts",
"preview:preload-disabled": "vite preview --config vite.config-preload-disabled.ts"
"preview:preload-disabled": "vite preview --config vite.config-preload-disabled.ts",
"dev:preload-credentials": "vite --config vite.config-preload-credentials.ts",
"build:preload-credentials": "vite build --config vite.config-preload-credentials.ts",
"debug:preload-credentials": "node --inspect-brk ../../packages/vite/bin/vite --config vite.config-preload-credentials.ts",
"preview:preload-credentials": "vite preview --config vite.config-preload-credentials.ts"
},
"devDependencies": {
"terser": "^5.31.0",
Expand Down
20 changes: 20 additions & 0 deletions playground/preload/vite.config-preload-credentials.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { defineConfig } from 'vite'

export default defineConfig({
build: {
outDir: 'dist/preload-credentials',
minify: 'terser',
terserOptions: {
format: {
beautify: true,
},
compress: {
passes: 3,
},
},
modulePreload: {
crossOrigin: 'use-credentials',
},
},
cacheDir: 'node_modules/.vite-preload-credentials',
})
Loading