Skip to content

Commit

Permalink
feat: use filter for plugins (#49) (#50) (#51) (#52) (#53)
Browse files Browse the repository at this point in the history
Co-authored-by: IWANABETHATGUY <iwanabethatguy@qq.com>
  • Loading branch information
sapphi-red and IWANABETHATGUY committed Sep 18, 2024
1 parent be90e9c commit 7ae3b0a
Show file tree
Hide file tree
Showing 5 changed files with 956 additions and 865 deletions.
215 changes: 112 additions & 103 deletions packages/vite/src/node/plugins/assetImportMetaUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import path from 'node:path'
import MagicString from 'magic-string'
import { stripLiteral } from 'strip-literal'
import { parseAst } from 'rollup/parseAst'
import type { Plugin } from '../plugin'
import type { RolldownPlugin } from 'rolldown'
import type { ResolvedConfig } from '../config'
import { injectQuery, isParentDirectory, transformStableResult } from '../utils'
import { CLIENT_ENTRY } from '../constants'
Expand All @@ -25,7 +25,9 @@ import { hasViteIgnoreRE } from './importAnalysis'
* import.meta.glob('./dir/**.png', { eager: true, import: 'default' })[`./dir/${name}.png`]
* ```
*/
export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
export function assetImportMetaUrlPlugin(
config: ResolvedConfig,
): RolldownPlugin {
const { publicDir } = config
let assetResolver: ResolveIdFn

Expand All @@ -40,121 +42,128 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin {

return {
name: 'vite:asset-import-meta-url',
async transform(code, id) {
const { environment } = this
if (
environment.config.consumer === 'client' &&
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: {
filter: {
code: {
include: ['new URL', 'import.meta.url'],
},
},
async handler(code, id) {
const { environment } = this
if (
environment.config.consumer === 'client' &&
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 = parseAst(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
}
// 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 = parseAst(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'),
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
}
s.update(
startIndex,
endIndex,
`new URL((import.meta.glob(${JSON.stringify(
pattern,
)}, ${JSON.stringify(
globOptions,
)}))[${pureUrl}], import.meta.url)`,
)
continue
}
}

const url = rawUrl.slice(1, -1)
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(environment, url, id)
file ??=
url[0] === '/'
? slash(path.join(publicDir, url))
: slash(path.resolve(path.dirname(id), url))
}
const url = rawUrl.slice(1, -1)
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(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

0 comments on commit 7ae3b0a

Please sign in to comment.