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

refactor: [hookName].handler in plugins #19586

Open
wants to merge 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ async function createAssetImportMetaurlPluginTransform() {
const environment = new PartialEnvironment('client', config)

return async (code: string) => {
// @ts-expect-error transform should exist
const result = await instance.transform.call(
// @ts-expect-error transform.handler should exist
const result = await instance.transform.handler.call(
{ environment, parse: parseAst },
code,
'foo.ts',
Expand Down
4 changes: 2 additions & 2 deletions packages/vite/src/node/__tests__/plugins/define.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ async function createDefinePluginTransform(
const environment = new PartialEnvironment(ssr ? 'ssr' : 'client', config)

return async (code: string) => {
// @ts-expect-error transform should exist
const result = await instance.transform.call(
// @ts-expect-error transform.handler should exist
const result = await instance.transform.handler.call(
{ environment },
code,
'foo.ts',
Expand Down
3 changes: 2 additions & 1 deletion packages/vite/src/node/__tests__/plugins/json.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ describe('transform', () => {
isBuild: boolean,
) => {
const plugin = jsonPlugin(opts, isBuild)
return (plugin.transform! as Function)(input, 'test.json').code
// @ts-expect-error transform.handler should exist
return plugin.transform.handler(input, 'test.json').code
}

test("namedExports: true, stringify: 'auto' should not transformed an array input", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ async function createWorkerImportMetaUrlPluginTransform() {
const environment = new PartialEnvironment('client', config)

return async (code: string) => {
// @ts-expect-error transform should exist
const result = await instance.transform.call(
// @ts-expect-error transform.handler should exist
const result = await instance.transform.handler.call(
{ environment, parse: parseAst },
code,
'foo.ts',
Expand Down
96 changes: 50 additions & 46 deletions packages/vite/src/node/plugins/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,60 +148,64 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
cssEntriesMap.set(this.environment, new Set())
},

resolveId(id) {
if (!config.assetsInclude(cleanUrl(id)) && !urlRE.test(id)) {
return
}
// imports to absolute urls pointing to files in /public
// will fail to resolve in the main resolver. handle them here.
const publicFile = checkPublicFile(id, config)
if (publicFile) {
return id
}
resolveId: {
handler(id) {
if (!config.assetsInclude(cleanUrl(id)) && !urlRE.test(id)) {
return
}
// imports to absolute urls pointing to files in /public
// will fail to resolve in the main resolver. handle them here.
const publicFile = checkPublicFile(id, config)
if (publicFile) {
return id
}
},
},

async load(id) {
if (id[0] === '\0') {
// Rollup convention, this id should be handled by the
// plugin that marked it with \0
return
}
load: {
async handler(id) {
if (id[0] === '\0') {
// Rollup convention, this id should be handled by the
// plugin that marked it with \0
return
}

// raw requests, read from disk
if (rawRE.test(id)) {
const file = checkPublicFile(id, config) || cleanUrl(id)
this.addWatchFile(file)
// raw query, read file and return as string
return `export default ${JSON.stringify(
await fsp.readFile(file, 'utf-8'),
)}`
}
// raw requests, read from disk
if (rawRE.test(id)) {
const file = checkPublicFile(id, config) || cleanUrl(id)
this.addWatchFile(file)
// raw query, read file and return as string
return `export default ${JSON.stringify(
await fsp.readFile(file, 'utf-8'),
)}`
}

if (!urlRE.test(id) && !config.assetsInclude(cleanUrl(id))) {
return
}
if (!urlRE.test(id) && !config.assetsInclude(cleanUrl(id))) {
return
}

id = removeUrlQuery(id)
let url = await fileToUrl(this, id)
id = removeUrlQuery(id)
let url = await fileToUrl(this, id)

// Inherit HMR timestamp if this asset was invalidated
if (!url.startsWith('data:') && this.environment.mode === 'dev') {
const mod = this.environment.moduleGraph.getModuleById(id)
if (mod && mod.lastHMRTimestamp > 0) {
url = injectQuery(url, `t=${mod.lastHMRTimestamp}`)
// Inherit HMR timestamp if this asset was invalidated
if (!url.startsWith('data:') && this.environment.mode === 'dev') {
const mod = this.environment.moduleGraph.getModuleById(id)
if (mod && mod.lastHMRTimestamp > 0) {
url = injectQuery(url, `t=${mod.lastHMRTimestamp}`)
}
}
}

return {
code: `export default ${JSON.stringify(encodeURIPath(url))}`,
// Force rollup to keep this module from being shared between other entry points if it's an entrypoint.
// If the resulting chunk is empty, it will be removed in generateBundle.
moduleSideEffects:
config.command === 'build' && this.getModuleInfo(id)?.isEntry
? 'no-treeshake'
: false,
meta: config.command === 'build' ? { 'vite:asset': true } : undefined,
}
return {
code: `export default ${JSON.stringify(encodeURIPath(url))}`,
// Force rollup to keep this module from being shared between other entry points if it's an entrypoint.
// If the resulting chunk is empty, it will be removed in generateBundle.
moduleSideEffects:
config.command === 'build' && this.getModuleInfo(id)?.isEntry
? 'no-treeshake'
: false,
meta: config.command === 'build' ? { 'vite:asset': true } : undefined,
}
},
},

renderChunk(code, chunk, opts) {
Expand Down
204 changes: 103 additions & 101 deletions packages/vite/src/node/plugins/assetImportMetaUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,122 +49,124 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
return environment.config.consumer === 'client'
},

async transform(code, id) {
if (
id !== preloadHelperId &&
id !== CLIENT_ENTRY &&
code.includes('new URL') &&
code.includes(`import.meta.url`)
) {
let s: MagicString | undefined
const assetImportMetaUrlRE =
/\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\)/dg
const cleanString = stripLiteral(code)
transform: {
async handler(code, id) {
if (
id !== preloadHelperId &&
id !== CLIENT_ENTRY &&
code.includes('new URL') &&
code.includes(`import.meta.url`)
) {
let s: MagicString | undefined
const assetImportMetaUrlRE =
/\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\)/dg
const cleanString = stripLiteral(code)

let match: RegExpExecArray | null
while ((match = assetImportMetaUrlRE.exec(cleanString))) {
const [[startIndex, endIndex], [urlStart, urlEnd]] = match.indices!
if (hasViteIgnoreRE.test(code.slice(startIndex, urlStart))) continue
let match: RegExpExecArray | null
while ((match = assetImportMetaUrlRE.exec(cleanString))) {
const [[startIndex, endIndex], [urlStart, urlEnd]] = match.indices!
if (hasViteIgnoreRE.test(code.slice(startIndex, urlStart))) continue

const rawUrl = code.slice(urlStart, urlEnd)
const rawUrl = code.slice(urlStart, urlEnd)

if (!s) s = new MagicString(code)
if (!s) s = new MagicString(code)

// potential dynamic template string
if (rawUrl[0] === '`' && rawUrl.includes('${')) {
const queryDelimiterIndex = getQueryDelimiterIndex(rawUrl)
const hasQueryDelimiter = queryDelimiterIndex !== -1
const pureUrl = hasQueryDelimiter
? rawUrl.slice(0, queryDelimiterIndex) + '`'
: rawUrl
const queryString = hasQueryDelimiter
? rawUrl.slice(queryDelimiterIndex, -1)
: ''
const ast = this.parse(pureUrl)
const templateLiteral = (ast as any).body[0].expression
if (templateLiteral.expressions.length) {
const pattern = buildGlobPattern(templateLiteral)
if (pattern.startsWith('*')) {
// don't transform for patterns like this
// because users won't intend to do that in most cases
// potential dynamic template string
if (rawUrl[0] === '`' && rawUrl.includes('${')) {
const queryDelimiterIndex = getQueryDelimiterIndex(rawUrl)
const hasQueryDelimiter = queryDelimiterIndex !== -1
const pureUrl = hasQueryDelimiter
? rawUrl.slice(0, queryDelimiterIndex) + '`'
: rawUrl
const queryString = hasQueryDelimiter
? rawUrl.slice(queryDelimiterIndex, -1)
: ''
const ast = this.parse(pureUrl)
const templateLiteral = (ast as any).body[0].expression
if (templateLiteral.expressions.length) {
const pattern = buildGlobPattern(templateLiteral)
if (pattern.startsWith('*')) {
// don't transform for patterns like this
// because users won't intend to do that in most cases
continue
}

const globOptions = {
eager: true,
import: 'default',
// A hack to allow 'as' & 'query' exist at the same time
query: injectQuery(queryString, 'url'),
}
s.update(
startIndex,
endIndex,
`new URL((import.meta.glob(${JSON.stringify(
pattern,
)}, ${JSON.stringify(
globOptions,
)}))[${pureUrl}], import.meta.url)`,
)
continue
}
}

const globOptions = {
eager: true,
import: 'default',
// A hack to allow 'as' & 'query' exist at the same time
query: injectQuery(queryString, 'url'),
}
s.update(
startIndex,
endIndex,
`new URL((import.meta.glob(${JSON.stringify(
pattern,
)}, ${JSON.stringify(
globOptions,
)}))[${pureUrl}], import.meta.url)`,
)
const url = rawUrl.slice(1, -1)
if (isDataUrl(url)) {
continue
}
}

const url = rawUrl.slice(1, -1)
if (isDataUrl(url)) {
continue
}
let file: string | undefined
if (url[0] === '.') {
file = slash(path.resolve(path.dirname(id), url))
file = tryFsResolve(file, fsResolveOptions) ?? file
} else {
assetResolver ??= createBackCompatIdResolver(config, {
extensions: [],
mainFields: [],
tryIndex: false,
preferRelative: true,
})
file = await assetResolver(this.environment, url, id)
file ??=
url[0] === '/'
? slash(path.join(publicDir, url))
: slash(path.resolve(path.dirname(id), url))
}
let file: string | undefined
if (url[0] === '.') {
file = slash(path.resolve(path.dirname(id), url))
file = tryFsResolve(file, fsResolveOptions) ?? file
} else {
assetResolver ??= createBackCompatIdResolver(config, {
extensions: [],
mainFields: [],
tryIndex: false,
preferRelative: true,
})
file = await assetResolver(this.environment, url, id)
file ??=
url[0] === '/'
? slash(path.join(publicDir, url))
: slash(path.resolve(path.dirname(id), url))
}

// Get final asset URL. If the file does not exist,
// we fall back to the initial URL and let it resolve in runtime
let builtUrl: string | undefined
if (file) {
try {
if (publicDir && isParentDirectory(publicDir, file)) {
const publicPath = '/' + path.posix.relative(publicDir, file)
builtUrl = await fileToUrl(this, publicPath)
} else {
builtUrl = await fileToUrl(this, file)
// Get final asset URL. If the file does not exist,
// we fall back to the initial URL and let it resolve in runtime
let builtUrl: string | undefined
if (file) {
try {
if (publicDir && isParentDirectory(publicDir, file)) {
const publicPath = '/' + path.posix.relative(publicDir, file)
builtUrl = await fileToUrl(this, publicPath)
} else {
builtUrl = await fileToUrl(this, file)
}
} catch {
// do nothing, we'll log a warning after this
}
} catch {
// do nothing, we'll log a warning after this
}
}
if (!builtUrl) {
const rawExp = code.slice(startIndex, endIndex)
config.logger.warnOnce(
`\n${rawExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime. ` +
`If this is intended, you can use the /* @vite-ignore */ comment to suppress this warning.`,
if (!builtUrl) {
const rawExp = code.slice(startIndex, endIndex)
config.logger.warnOnce(
`\n${rawExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime. ` +
`If this is intended, you can use the /* @vite-ignore */ comment to suppress this warning.`,
)
builtUrl = url
}
s.update(
startIndex,
endIndex,
`new URL(${JSON.stringify(builtUrl)}, import.meta.url)`,
)
builtUrl = url
}
s.update(
startIndex,
endIndex,
`new URL(${JSON.stringify(builtUrl)}, import.meta.url)`,
)
}
if (s) {
return transformStableResult(s, id, config)
if (s) {
return transformStableResult(s, id, config)
}
}
}
return null
return null
},
},
}
}
Expand Down
Loading