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

fix: enforce generating module param when needed in relative url resolving (fixes #9807) #9808

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
60 changes: 52 additions & 8 deletions packages/vite/src/node/build.ts
Expand Up @@ -5,6 +5,7 @@ import type {
ExternalOption,
InternalModuleFormat,
ModuleFormat,
NormalizedOutputOptions,
OutputOptions,
Plugin,
RollupBuild,
Expand All @@ -21,6 +22,7 @@ import commonjsPlugin from '@rollup/plugin-commonjs'
import type { RollupCommonJSOptions } from 'types/commonjs'
import type { RollupDynamicImportVarsOptions } from 'types/dynamicImportVars'
import type { TransformOptions } from 'esbuild'
import type MagicString from 'magic-string'
import type { InlineConfig, ResolvedConfig } from './config'
import { isDepsOptimizerEnabled, resolveConfig } from './config'
import { buildReporterPlugin } from './plugins/reporter'
Expand Down Expand Up @@ -902,7 +904,10 @@ export type RenderBuiltAssetUrl = (
hostType: 'js' | 'css' | 'html'
ssr: boolean
}
) => string | { relative?: boolean; runtime?: string } | undefined
) =>
| string
| { relative?: boolean; runtime?: string; needModuleParam?: boolean }
| undefined

export function toOutputFilePathInString(
filename: string,
Expand All @@ -914,10 +919,13 @@ export function toOutputFilePathInString(
toRelative: (
filename: string,
hostType: string
) => string | { runtime: string } = getToImportMetaURLBasedRelativePath(
format
)
): string | { runtime: string } {
) =>
| string
| {
runtime: string
needModuleParam: boolean
} = getToImportMetaURLBasedRelativePath(format)
): string | { runtime: string; needModuleParam: boolean } {
const { renderBuiltUrl } = config.experimental
let relative = config.base === '' || config.base === './'
if (renderBuiltUrl) {
Expand All @@ -929,7 +937,10 @@ export function toOutputFilePathInString(
})
if (typeof result === 'object') {
if (result.runtime) {
return { runtime: result.runtime }
return {
runtime: result.runtime,
needModuleParam: !!result.needModuleParam
}
}
if (typeof result.relative === 'boolean') {
relative = result.relative
Expand All @@ -944,14 +955,47 @@ export function toOutputFilePathInString(
return config.base + filename
}

export function ensureHavingSystemJSModuleParam(
s: MagicString,
code: string,
opts: NormalizedOutputOptions
): void {
if (opts.format !== 'system') return
const wrapIdx = code.indexOf('System.register')
if (wrapIdx < 0) return
const functionStr = 'function ('
const functionIdx = code.indexOf(functionStr, wrapIdx)
if (functionIdx < 0) return
const functionParameterStartIdx = functionIdx + functionStr.length
const functionParameterEndIdx = code.indexOf(')', functionParameterStartIdx)
const functionParameterModuleIdx = code.indexOf(
'module',
functionParameterStartIdx
)
const hasModuleParameter =
functionParameterModuleIdx >= 0 &&
functionParameterModuleIdx < functionParameterEndIdx
if (!hasModuleParameter) {
s.overwrite(
functionParameterStartIdx - 1,
functionParameterEndIdx + 1,
'(exports, module)'
)
}
}

function getToImportMetaURLBasedRelativePath(
format: InternalModuleFormat
): (filename: string, importer: string) => { runtime: string } {
): (
filename: string,
importer: string
) => { runtime: string; needModuleParam: boolean } {
const toRelativePath = relativeUrlMechanisms[format]
return (filename, importer) => ({
runtime: toRelativePath(
path.posix.relative(path.dirname(importer), filename)
)
),
needModuleParam: format === 'system'
})
}

Expand Down
40 changes: 27 additions & 13 deletions packages/vite/src/node/plugins/asset.ts
Expand Up @@ -10,7 +10,10 @@ import type {
RenderedChunk
} from 'rollup'
import MagicString from 'magic-string'
import { toOutputFilePathInString } from '../build'
import {
ensureHavingSystemJSModuleParam,
toOutputFilePathInString
} from '../build'
import type { Plugin } from '../plugin'
import type { ResolvedConfig } from '../config'
import { cleanUrl, getHash, normalizePath } from '../utils'
Expand Down Expand Up @@ -48,9 +51,10 @@ export function renderAssetUrlInJS(
chunk: RenderedChunk,
opts: NormalizedOutputOptions,
code: string
): MagicString | undefined {
): { s: MagicString; needModuleParam: boolean } | undefined {
let match: RegExpExecArray | null
let s: MagicString | undefined
let needModuleParam = false

// Urls added with JS using e.g.
// imgElement.src = "__VITE_ASSET__5aa0ddc0__" are using quotes
Expand All @@ -76,10 +80,13 @@ export function renderAssetUrlInJS(
config,
opts.format
)
const replacementString =
typeof replacement === 'string'
? JSON.stringify(replacement).slice(1, -1)
: `"+${replacement.runtime}+"`
let replacementString: string
if (typeof replacement === 'string') {
replacementString = JSON.stringify(replacement).slice(1, -1)
} else {
replacementString = `"+${replacement.runtime}+"`
needModuleParam ||= replacement.needModuleParam
}
s.overwrite(match.index, match.index + full.length, replacementString, {
contentOnly: true
})
Expand All @@ -100,16 +107,19 @@ export function renderAssetUrlInJS(
config,
opts.format
)
const replacementString =
typeof replacement === 'string'
? JSON.stringify(replacement).slice(1, -1)
: `"+${replacement.runtime}+"`
let replacementString: string
if (typeof replacement === 'string') {
replacementString = JSON.stringify(replacement).slice(1, -1)
} else {
replacementString = `"+${replacement.runtime}+"`
needModuleParam ||= replacement.needModuleParam
}
s.overwrite(match.index, match.index + full.length, replacementString, {
contentOnly: true
})
}

return s
return s && { s, needModuleParam }
}

/**
Expand Down Expand Up @@ -167,9 +177,13 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
},

renderChunk(code, chunk, opts) {
const s = renderAssetUrlInJS(this, config, chunk, opts, code)
const result = renderAssetUrlInJS(this, config, chunk, opts, code)

if (s) {
if (result) {
const { s, needModuleParam } = result
if (needModuleParam) {
ensureHavingSystemJSModuleParam(s, code, opts)
}
return {
code: s.toString(),
map: config.build.sourcemap ? s.generateMap({ hires: true }) : null
Expand Down
24 changes: 15 additions & 9 deletions packages/vite/src/node/plugins/css.ts
Expand Up @@ -25,7 +25,10 @@ import type { RawSourceMap } from '@ampproject/remapping'
import { getCodeWithSourcemap, injectSourcesContent } from '../server/sourcemap'
import type { ModuleNode } from '../server/moduleGraph'
import type { ResolveFn, ViteDevServer } from '../'
import { toOutputFilePathInCss } from '../build'
import {
ensureHavingSystemJSModuleParam,
toOutputFilePathInCss
} from '../build'
import { CLIENT_PUBLIC_PATH, SPECIAL_QUERY_RE } from '../constants'
import type { ResolvedConfig } from '../config'
import type { Plugin } from '../plugin'
Expand Down Expand Up @@ -567,14 +570,14 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
}
chunkCSS = await finalizeCss(chunkCSS, true, config)
let cssString = JSON.stringify(chunkCSS)
cssString =
renderAssetUrlInJS(
this,
config,
chunk,
opts,
cssString
)?.toString() || cssString
const cssRendered = renderAssetUrlInJS(
this,
config,
chunk,
opts,
cssString
)
cssString = cssRendered?.s.toString() || cssString
const style = `__vite_style__`
const injectCode =
`var ${style} = document.createElement('style');` +
Expand All @@ -585,6 +588,9 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
const insertIdx = code.indexOf(insertMark, wrapIdx)
const s = new MagicString(code)
s.appendLeft(insertIdx + insertMark.length, injectCode)
if (cssRendered?.needModuleParam) {
ensureHavingSystemJSModuleParam(s, code, opts)
}
if (config.build.sourcemap) {
// resolve public URL from CSS paths, we need to use absolute paths
return {
Expand Down
21 changes: 16 additions & 5 deletions packages/vite/src/node/plugins/worker.ts
Expand Up @@ -6,7 +6,11 @@ import type { Plugin } from '../plugin'
import type { ViteDevServer } from '../server'
import { ENV_ENTRY, ENV_PUBLIC_PATH } from '../constants'
import { cleanUrl, getHash, injectQuery, parseRequest } from '../utils'
import { onRollupWarning, toOutputFilePathInString } from '../build'
import {
ensureHavingSystemJSModuleParam,
onRollupWarning,
toOutputFilePathInString
} from '../build'
import { getDepsOptimizer } from '../optimizer'
import { fileToUrl } from './asset'

Expand Down Expand Up @@ -320,6 +324,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
if (code.match(workerAssetUrlRE) || code.includes('import.meta.url')) {
let match: RegExpExecArray | null
s = new MagicString(code)
let needModuleParam = false

// Replace "__VITE_WORKER_ASSET__5aa0ddc0__" using relative paths
const workerMap = workerCache.get(config.mainConfig || config)!
Expand All @@ -336,10 +341,13 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
config,
outputOptions.format
)
const replacementString =
typeof replacement === 'string'
? JSON.stringify(replacement).slice(1, -1)
: `"+${replacement.runtime}+"`
let replacementString: string
if (typeof replacement === 'string') {
replacementString = JSON.stringify(replacement).slice(1, -1)
} else {
replacementString = `"+${replacement.runtime}+"`
needModuleParam ||= replacement.needModuleParam
}
s.overwrite(
match.index,
match.index + full.length,
Expand All @@ -349,6 +357,9 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
}
)
}
if (needModuleParam) {
ensureHavingSystemJSModuleParam(s, code, outputOptions)
}
}
return result()
},
Expand Down