From 9ee3953281877dd75455a587f96c93a197a7eb3f Mon Sep 17 00:00:00 2001 From: patak-dev Date: Thu, 1 Sep 2022 00:32:10 +0200 Subject: [PATCH 1/9] feat: build.modulePreload options --- docs/config/build-options.md | 54 ++++++++++- packages/vite/src/node/build.ts | 63 +++++++++++- packages/vite/src/node/config.ts | 6 +- packages/vite/src/node/plugins/html.ts | 4 +- .../src/node/plugins/importAnalysisBuild.ts | 97 ++++++++++++++----- packages/vite/src/node/plugins/index.ts | 4 +- .../preload-disabled/preload-disabled.spec.ts | 22 +++++ .../__tests__/preload-disabled/vite.config.js | 1 + .../resolve-deps/preload-resolve-deps.spec.ts | 26 +++++ .../__tests__/resolve-deps/vite.config.js | 1 + playground/preload/package.json | 10 +- .../preload/vite.config-preload-disabled.ts | 18 ++++ .../preload/vite.config-resolve-deps.ts | 31 ++++++ 13 files changed, 301 insertions(+), 36 deletions(-) create mode 100644 playground/preload/__tests__/preload-disabled/preload-disabled.spec.ts create mode 100644 playground/preload/__tests__/preload-disabled/vite.config.js create mode 100644 playground/preload/__tests__/resolve-deps/preload-resolve-deps.spec.ts create mode 100644 playground/preload/__tests__/resolve-deps/vite.config.js create mode 100644 playground/preload/vite.config-preload-disabled.ts create mode 100644 playground/preload/vite.config-resolve-deps.ts diff --git a/docs/config/build-options.md b/docs/config/build-options.md index 291135045560ce..02bf8747c65101 100644 --- a/docs/config/build-options.md +++ b/docs/config/build-options.md @@ -17,14 +17,12 @@ The transform is performed with esbuild and the value should be a valid [esbuild Note the build will fail if the code contains features that cannot be safely transpiled by esbuild. See [esbuild docs](https://esbuild.github.io/content-types/#javascript) for more details. -## build.polyfillModulePreload +## build.modulePreload -- **Type:** `boolean` +- **Type:** `boolean | { polyfill?: boolean, resolveDependencies?: ResolveModulePreloadDependenciesFn }` - **Default:** `true` -Whether to automatically inject [module preload polyfill](https://guybedford.com/es-module-preloading-integrity#modulepreload-polyfill). - -If set to `true`, 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: +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: ```js import 'vite/modulepreload-polyfill' @@ -32,6 +30,52 @@ import 'vite/modulepreload-polyfill' Note: the polyfill does **not** apply to [Library Mode](/guide/build#library-mode). If you need to support browsers without native dynamic import, you should probably avoid using it in your library. +The polyfill can be disabled using `{ polyfill: false }`. + +The list of chunks to preload for each dynamic import is computed by Vite. By default, an absolute path including the `base` will be used when loading these dependencies. If the `base` is relative (`''` or `'./'`), `import.meta.url` is used at runtime to avoid absolute paths that depends on final the deployed base. + +There is experimental support for fine grained control over the dependencies list and their paths using the `resolveDependencies` function. It expects a function of type `ResolveModulePreloadDependenciesFn` + +```ts +type ResolveModulePreloadDependenciesFn = ( + url: string, + deps: string[], + context: { + importer: string + } +) => (string | { runtime?: string })[] +``` + +The `resolveDependencies` function will be called for each dynamic import with a list of the chunks it depends on. A new dependencies array can be returned with these filtered or more dependencies injected, and their paths modified. The `deps` paths are relative to the `build.outDir`. + +```js + modulePreload: { + resolveDependencies: (url, deps, { importer }) => { + return deps.map((dep) => { + if (...) { + // a relative path to the importer makes the dependency independent of the base + // this is the default + return { relative: dep } // ~ `path.relative(path.dirname(importer), dep)` + } + else if (...) { + // a runtime expression can be returned + return { runtime: `globalThis.__preloadPath(${dep}, import.meta.url)` } + } + // returning the dep as is defaults to the regular resolution + return dep + }) + } + } +``` + +## build.polyfillModulePreload + +- **Type:** `boolean` +- **Default:** `true` +- **Deprecated** use `build.modulePreload.polyfill` instead + +Whether to automatically inject [module preload polyfill](https://guybedford.com/es-module-preloading-integrity#modulepreload-polyfill). + ## build.outDir - **Type:** `string` diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 21cdce31180e5d..3d29ef653b2bc1 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -73,8 +73,15 @@ export interface BuildOptions { * whether to inject module preload polyfill. * Note: does not apply to library mode. * @default true + * @deprecated use `modulePreload.polyfill` instead */ polyfillModulePreload?: boolean + /** + * Configure module preload + * Note: does not apply to library mode. + * @default true + */ + modulePreload?: boolean | ModulePreloadOptions /** * Directory relative from `root` where build output will be placed. If the * directory exists, it will be removed before the build. @@ -229,16 +236,59 @@ export interface LibraryOptions { export type LibraryFormats = 'es' | 'cjs' | 'umd' | 'iife' -export type ResolvedBuildOptions = Required +export interface ModulePreloadOptions { + /** + * whether to inject module preload polyfill. + * Note: does not apply to library mode. + * @default true + */ + polyfill?: boolean + /** + * Resolve the list of dependencies to preload for + * a given dynamic import + * @experimental + */ + resolveDependencies?: ResolveModulePreloadDependenciesFn +} + +export type ResolveModulePreloadDependenciesFn = ( + url: string, + deps: string[], + context: { + importer: string + } +) => (string | { runtime?: string; relative?: string })[] + +export type ResolvedBuildOptions = Required< + Omit +> export function resolveBuildOptions( raw: BuildOptions | undefined, isBuild: boolean, logger: Logger ): ResolvedBuildOptions { + const deprecatedPolyfillModulePreload = raw?.polyfillModulePreload + if (raw) { + const { polyfillModulePreload, ...rest } = raw + raw = rest + if (deprecatedPolyfillModulePreload !== undefined) { + logger.warn( + 'polyfillModulePreload is deprecated. Use modulePreload.polyfill instead.' + ) + } + if ( + deprecatedPolyfillModulePreload === false && + raw.modulePreload === undefined + ) { + raw.modulePreload = { polyfill: false } + } + } + + const modulePreload = raw?.modulePreload + const resolved: ResolvedBuildOptions = { target: 'modules', - polyfillModulePreload: true, outDir: 'dist', assetsDir: 'assets', assetsInlineLimit: 4096, @@ -267,7 +317,14 @@ export function resolveBuildOptions( warnOnError: true, exclude: [/node_modules/], ...raw?.dynamicImportVarsOptions - } + }, + modulePreload: + typeof modulePreload === 'object' + ? { + polyfill: true, + ...modulePreload + } + : modulePreload ?? true } // handle special build targets diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 4fb0fcc9a15a93..68c152aeda1696 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -60,7 +60,11 @@ import { resolveSSROptions } from './ssr' const debug = createDebugger('vite:config') -export type { RenderBuiltAssetUrl } from './build' +export type { + RenderBuiltAssetUrl, + ModulePreloadOptions, + ResolveModulePreloadDependenciesFn +} from './build' // NOTE: every export in this file is re-exported from ./index.ts so it will // be part of the public API. diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index a3df68ca539602..4f06f798721dd6 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -581,8 +581,10 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { processedHtml.set(id, s.toString()) // inject module preload polyfill only when configured and needed + const { modulePreload } = config.build if ( - config.build.polyfillModulePreload && + (modulePreload === true || + (typeof modulePreload === 'object' && modulePreload.polyfill)) && (someScriptsAreAsync || someScriptsAreDefer) ) { js = `import "${modulePreloadPolyfillId}";\n${js}` diff --git a/packages/vite/src/node/plugins/importAnalysisBuild.ts b/packages/vite/src/node/plugins/importAnalysisBuild.ts index d78e4c3be77ba7..4444ea2da8f181 100644 --- a/packages/vite/src/node/plugins/importAnalysisBuild.ts +++ b/packages/vite/src/node/plugins/importAnalysisBuild.ts @@ -108,13 +108,26 @@ function preload( export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { const ssr = !!config.build.ssr const isWorker = config.isWorker - const insertPreload = !(ssr || !!config.build.lib || isWorker) - - const relativePreloadUrls = config.base === './' || config.base === '' - - const scriptRel = config.build.polyfillModulePreload - ? `'modulepreload'` - : `(${detectScriptRel.toString()})()` + const insertPreload = !( + ssr || + !!config.build.lib || + isWorker || + config.build.modulePreload === false + ) + + const customResolveModulePreloadDependencies = + typeof config.build.modulePreload === 'object' && + config.build.modulePreload.resolveDependencies + const isRelativeBase = config.base === './' || config.base === '' + const relativePreloadUrls = + isRelativeBase || customResolveModulePreloadDependencies + + const { modulePreload } = config.build + const scriptRel = + modulePreload === true || + (typeof modulePreload === 'object' && modulePreload.polyfill) + ? `'modulepreload'` + : `(${detectScriptRel.toString()})()` const assetsURL = relativePreloadUrls ? `function(dep,importerUrl) { return new URL(dep, importerUrl).href }` : `function(dep) { return ${JSON.stringify(config.base)}+dep }` @@ -367,7 +380,12 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { }, generateBundle({ format }, bundle) { - if (format !== 'es' || ssr || isWorker) { + if ( + format !== 'es' || + ssr || + isWorker || + config.build.modulePreload === false + ) { return } @@ -454,25 +472,56 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { } if (markerStartPos > 0) { - s.overwrite( - markerStartPos, - markerStartPos + preloadMarkerWithQuote.length, - // the dep list includes the main chunk, so only need to reload when there are - // actual other deps. Don't include the assets dir if the default asset file names - // are used, the path will be reconstructed by the import preload helper + const { modulePreload } = config.build + // the dep list includes the main chunk, so only need to reload when there are actual other deps. + const depsArray = deps.size > 1 || - // main chunk is removed - (hasRemovedPureCssChunk && deps.size > 0) - ? `[${[...deps] - .map((d) => - JSON.stringify( - relativePreloadUrls - ? path.relative(path.dirname(file), d) - : d + // main chunk is removed + (hasRemovedPureCssChunk && deps.size > 0) + ? [...deps] + : [] + const toRelative = (dep: string) => + path.relative(path.dirname(file), dep) + const resolvedDependencies = + url && + typeof modulePreload === 'object' && + modulePreload.resolveDependencies + ? modulePreload + .resolveDependencies(url, depsArray, { importer: file }) + .map((dep) => { + if (typeof dep === 'object') { + if (dep.runtime) { + return dep.runtime + } + if (dep.relative) { + return JSON.stringify(toRelative(dep.relative)) + } + throw new Error( + `Invalid dependency object, no runtime or relative option specified` + ) + } + return JSON.stringify( + depsArray.includes(dep) + ? isRelativeBase + ? toRelative(dep) + : config.base + dep + : dep ) + }) + : depsArray.map((d) => + // Don't include the assets dir if the default asset file names + // are used, the path will be reconstructed by the import preload helper + JSON.stringify( + relativePreloadUrls + ? path.relative(path.dirname(file), d) + : d ) - .join(',')}]` - : `[]`, + ) + + s.overwrite( + markerStartPos, + markerStartPos + preloadMarkerWithQuote.length, + `[${resolvedDependencies.join(',')}]`, { contentOnly: true } ) rewroteMarkerStartPos.add(markerStartPos) diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index 75c8297bf26919..6098e3d9b340e7 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -36,6 +36,7 @@ export async function resolvePlugins( const buildPlugins = isBuild ? (await import('../build')).resolveBuildPlugins(config) : { pre: [], post: [] } + const { modulePreload } = config.build return [ isWatch ? ensureWatchPlugin() : null, @@ -43,7 +44,8 @@ export async function resolvePlugins( preAliasPlugin(config), aliasPlugin({ entries: config.resolve.alias }), ...prePlugins, - config.build.polyfillModulePreload + modulePreload === true || + (typeof modulePreload === 'object' && modulePreload.polyfill) ? modulePreloadPolyfillPlugin(config) : null, ...(isDepsOptimizerEnabled(config, false) || diff --git a/playground/preload/__tests__/preload-disabled/preload-disabled.spec.ts b/playground/preload/__tests__/preload-disabled/preload-disabled.spec.ts new file mode 100644 index 00000000000000..5be5b07a0774f6 --- /dev/null +++ b/playground/preload/__tests__/preload-disabled/preload-disabled.spec.ts @@ -0,0 +1,22 @@ +import { describe, expect, test } from 'vitest' +import { browserLogs, isBuild, page, viteTestUrl } from '~utils' + +test('should have no 404s', () => { + browserLogs.forEach((msg) => { + expect(msg).not.toMatch('404') + }) +}) + +describe.runIf(isBuild)('build', () => { + test('dynamic import', async () => { + const appHtml = await page.content() + expect(appHtml).toMatch('This is home page.') + }) + + test('dynamic import with comments', async () => { + await page.goto(viteTestUrl + '/#/hello') + const html = await page.content() + expect(html).not.toMatch(/link rel="modulepreload"/) + expect(html).not.toMatch(/link rel="stylesheet"/) + }) +}) diff --git a/playground/preload/__tests__/preload-disabled/vite.config.js b/playground/preload/__tests__/preload-disabled/vite.config.js new file mode 100644 index 00000000000000..cfd104f2627810 --- /dev/null +++ b/playground/preload/__tests__/preload-disabled/vite.config.js @@ -0,0 +1 @@ +module.exports = require('../../vite.config-preload-disabled') diff --git a/playground/preload/__tests__/resolve-deps/preload-resolve-deps.spec.ts b/playground/preload/__tests__/resolve-deps/preload-resolve-deps.spec.ts new file mode 100644 index 00000000000000..389674218418cc --- /dev/null +++ b/playground/preload/__tests__/resolve-deps/preload-resolve-deps.spec.ts @@ -0,0 +1,26 @@ +import { describe, expect, test } from 'vitest' +import { browserLogs, isBuild, page, viteTestUrl } from '~utils' + +test('should have no 404s', () => { + browserLogs.forEach((msg) => { + expect(msg).not.toMatch('404') + }) +}) + +describe.runIf(isBuild)('build', () => { + test('dynamic import', async () => { + const appHtml = await page.content() + expect(appHtml).toMatch('This is home page.') + }) + + test('dynamic import with comments', async () => { + await page.goto(viteTestUrl + '/#/hello') + const html = await page.content() + expect(html).toMatch( + /link rel="modulepreload".*?href="http.*?\/Hello\.\w{8}\.js"/ + ) + expect(html).toMatch( + /link rel="stylesheet".*?href="http.*?\/Hello\.\w{8}\.css"/ + ) + }) +}) diff --git a/playground/preload/__tests__/resolve-deps/vite.config.js b/playground/preload/__tests__/resolve-deps/vite.config.js new file mode 100644 index 00000000000000..9473b6224a7ce5 --- /dev/null +++ b/playground/preload/__tests__/resolve-deps/vite.config.js @@ -0,0 +1 @@ +module.exports = require('../../vite.config-resolve-deps') diff --git a/playground/preload/package.json b/playground/preload/package.json index 07fad09b3b6f3f..e7cea1bce9de9c 100644 --- a/playground/preload/package.json +++ b/playground/preload/package.json @@ -6,7 +6,15 @@ "dev": "vite", "build": "vite build", "debug": "node --inspect-brk ../../packages/vite/bin/vite", - "preview": "vite preview" + "preview": "vite preview", + "dev:resolve-deps": "vite --config vite.config-resolve-deps.ts", + "build:resolve-deps": "vite build --config vite.config-resolve-deps.ts", + "debug:resolve-deps": "node --inspect-brk ../../packages/vite/bin/vite --config vite.config-resolve-deps.ts", + "preview:resolve-deps": "vite preview --config vite.config-resolve-deps.ts", + "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" }, "dependencies": { "vue": "^3.2.37", diff --git a/playground/preload/vite.config-preload-disabled.ts b/playground/preload/vite.config-preload-disabled.ts new file mode 100644 index 00000000000000..27735976335365 --- /dev/null +++ b/playground/preload/vite.config-preload-disabled.ts @@ -0,0 +1,18 @@ +import vuePlugin from '@vitejs/plugin-vue' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [vuePlugin()], + build: { + minify: 'terser', + terserOptions: { + format: { + beautify: true + }, + compress: { + passes: 3 + } + }, + modulePreload: false + } +}) diff --git a/playground/preload/vite.config-resolve-deps.ts b/playground/preload/vite.config-resolve-deps.ts new file mode 100644 index 00000000000000..1816b08881ec6a --- /dev/null +++ b/playground/preload/vite.config-resolve-deps.ts @@ -0,0 +1,31 @@ +import path from 'node:path' +import vuePlugin from '@vitejs/plugin-vue' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [vuePlugin()], + build: { + minify: 'terser', + terserOptions: { + format: { + beautify: true + }, + compress: { + passes: 3 + } + }, + modulePreload: { + resolveDependencies(url, deps, { importer }) { + return deps.map((dep) => { + return dep.includes('.js') + ? { relative: dep } + : { + runtime: `new URL(${JSON.stringify( + path.relative(path.dirname(importer), dep) + )},import.meta.url).href` + } + }) + } + } + } +}) From 9f1eaa5f7539eb7588506419465aa8c7251b2efd Mon Sep 17 00:00:00 2001 From: patak-dev Date: Sat, 3 Sep 2022 18:18:42 +0200 Subject: [PATCH 2/9] refactor: update --- docs/config/build-options.md | 26 +-- packages/vite/src/node/build.ts | 16 +- packages/vite/src/node/plugins/asset.ts | 17 +- packages/vite/src/node/plugins/html.ts | 35 ++-- .../src/node/plugins/importAnalysisBuild.ts | 149 ++++++++++++------ packages/vite/src/node/plugins/worker.ts | 14 +- .../resolve-deps/preload-resolve-deps.spec.ts | 1 + playground/preload/public/preloaded.js | 1 + .../preload/vite.config-resolve-deps.ts | 24 +-- 9 files changed, 176 insertions(+), 107 deletions(-) create mode 100644 playground/preload/public/preloaded.js diff --git a/docs/config/build-options.md b/docs/config/build-options.md index 02bf8747c65101..d43a1f1fc2ce31 100644 --- a/docs/config/build-options.md +++ b/docs/config/build-options.md @@ -46,28 +46,18 @@ type ResolveModulePreloadDependenciesFn = ( ) => (string | { runtime?: string })[] ``` -The `resolveDependencies` function will be called for each dynamic import with a list of the chunks it depends on. A new dependencies array can be returned with these filtered or more dependencies injected, and their paths modified. The `deps` paths are relative to the `build.outDir`. +The `resolveDependencies` function will be called for each dynamic import with a list of the chunks it depends on, and it will also be called for each chunk imported in entry HTML files. A new dependencies array can be returned with these filtered or more dependencies injected, and their paths modified. The `deps` paths are relative to the `build.outDir`. Returning a relative path to the `hostId` for `hostType === 'js'` is allowed, in which case `new URL(dep, import.meta.url)` is used to get an absolute path when injecting this module preload in the HTML head. ```js - modulePreload: { - resolveDependencies: (url, deps, { importer }) => { - return deps.map((dep) => { - if (...) { - // a relative path to the importer makes the dependency independent of the base - // this is the default - return { relative: dep } // ~ `path.relative(path.dirname(importer), dep)` - } - else if (...) { - // a runtime expression can be returned - return { runtime: `globalThis.__preloadPath(${dep}, import.meta.url)` } - } - // returning the dep as is defaults to the regular resolution - return dep - }) - } - } +modulePreload: { + resolveDependencies: (filename, deps, { hostId, hostType }) => { + return deps.filter(condition) + } +} ``` +The resolved dependency paths can be further modified using [`experimental.renderBuiltUrl`](../guide/build.md#advanced-base-options). + ## build.polyfillModulePreload - **Type:** `boolean` diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 3d29ef653b2bc1..9779a3ce1da219 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -252,12 +252,13 @@ export interface ModulePreloadOptions { } export type ResolveModulePreloadDependenciesFn = ( - url: string, + filename: string, deps: string[], context: { - importer: string + hostId: string + hostType: 'html' | 'js' } -) => (string | { runtime?: string; relative?: string })[] +) => string[] export type ResolvedBuildOptions = Required< Omit @@ -962,19 +963,16 @@ export type RenderBuiltAssetUrl = ( } ) => string | { relative?: boolean; runtime?: string } | undefined -export function toOutputFilePathInString( +export function toOutputFilePathInJS( filename: string, type: 'asset' | 'public', hostId: string, hostType: 'js' | 'css' | 'html', config: ResolvedConfig, - format: InternalModuleFormat, toRelative: ( filename: string, hostType: string - ) => string | { runtime: string } = getToImportMetaURLBasedRelativePath( - format - ) + ) => string | { runtime: string } ): string | { runtime: string } { const { renderBuiltUrl } = config.experimental let relative = config.base === '' || config.base === './' @@ -1002,7 +1000,7 @@ export function toOutputFilePathInString( return config.base + filename } -function getToImportMetaURLBasedRelativePath( +export function createToImportMetaURLBasedRelativeRuntime( format: InternalModuleFormat ): (filename: string, importer: string) => { runtime: string } { const toRelativePath = relativeUrlMechanisms[format] diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index 94c34a40f8d22e..82497028a87398 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -12,7 +12,10 @@ import type { } from 'rollup' import MagicString from 'magic-string' import colors from 'picocolors' -import { toOutputFilePathInString } from '../build' +import { + createToImportMetaURLBasedRelativeRuntime, + toOutputFilePathInJS +} from '../build' import type { Plugin } from '../plugin' import type { ResolvedConfig } from '../config' import { cleanUrl, getHash, normalizePath } from '../utils' @@ -51,6 +54,10 @@ export function renderAssetUrlInJS( opts: NormalizedOutputOptions, code: string ): MagicString | undefined { + const toRelativeRuntime = createToImportMetaURLBasedRelativeRuntime( + opts.format + ) + let match: RegExpExecArray | null let s: MagicString | undefined @@ -70,13 +77,13 @@ export function renderAssetUrlInJS( const file = getAssetFilename(hash, config) || ctx.getFileName(hash) chunk.viteMetadata.importedAssets.add(cleanUrl(file)) const filename = file + postfix - const replacement = toOutputFilePathInString( + const replacement = toOutputFilePathInJS( filename, 'asset', chunk.fileName, 'js', config, - opts.format + toRelativeRuntime ) const replacementString = typeof replacement === 'string' @@ -94,13 +101,13 @@ export function renderAssetUrlInJS( s ||= new MagicString(code) const [full, hash] = match const publicUrl = publicAssetUrlMap.get(hash)!.slice(1) - const replacement = toOutputFilePathInString( + const replacement = toOutputFilePathInJS( publicUrl, 'public', chunk.fileName, 'js', config, - opts.format + toRelativeRuntime ) const replacementString = typeof replacement === 'string' diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 4f06f798721dd6..3fdcb884e890da 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -629,14 +629,14 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { }) const toPreloadTag = ( - chunk: OutputChunk, + filename: string, toOutputPath: (filename: string) => string ): HtmlTagDescriptor => ({ tag: 'link', attrs: { rel: 'modulepreload', crossorigin: true, - href: toOutputPath(chunk.fileName) + href: toOutputPath(filename) } }) @@ -728,15 +728,28 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { // when not inlined, inject