Repository navigation
Fix Vite and Rolldown build warnings in Astro 7 #17090
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
Changes from 1 commit
f2511ef
4034c8e
6b5506d
3232aab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| --- | ||
| 'astro': patch | ||
| --- | ||
|
|
||
| Fixes Vite deprecation and build warnings in Astro 7 | ||
|
|
||
| - Removes the deprecated `resolve.alias` `customResolver` option that produced warnings in dev and build. CSS `@import` alias resolution is now handled via a `transform` hook instead. | ||
| - Disables Rolldown's `checks.pluginTimings` warning that fired by default during builds. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -67,106 +67,105 @@ const getConfigAlias = (settings: AstroSettings): Alias[] | null => { | |
| return aliases; | ||
| }; | ||
|
|
||
| /** Generate vite.resolve.alias entries from tsconfig paths */ | ||
| const getViteResolveAlias = (settings: AstroSettings) => { | ||
| const { tsConfig, tsConfigPath } = settings; | ||
| if (!tsConfig || !tsConfigPath || !tsConfig.compilerOptions) return []; | ||
|
|
||
| const { baseUrl, paths } = tsConfig.compilerOptions as CompilerOptions; | ||
| const effectiveBaseUrl = baseUrl ?? (paths ? '.' : undefined); | ||
| if (!effectiveBaseUrl) return []; | ||
|
|
||
| const resolvedBaseUrl = path.resolve(path.dirname(tsConfigPath), effectiveBaseUrl); | ||
| const aliases: Array<{ find: string | RegExp; replacement: string; customResolver?: any }> = []; | ||
|
|
||
| // Build aliases with custom resolver that tries multiple paths | ||
| if (paths) { | ||
| for (const [aliasPattern, values] of Object.entries(paths)) { | ||
| const resolvedValues = values.map((v) => path.resolve(resolvedBaseUrl, v)); | ||
|
|
||
| const customResolver = (id: string) => { | ||
| // Try each path in order | ||
| // id is already the wildcard part (e.g., 'extra.css' for '@styles/*') | ||
| // resolvedValues still have the * in them, so replace * with id | ||
| for (const resolvedValue of resolvedValues) { | ||
| // nosemgrep: javascript.lang.security.audit.incomplete-sanitization.incomplete-sanitization | ||
| // `id` is the wildcard capture from the alias match and only substitutes the tsconfig `*`. | ||
| const resolved = resolvedValue.replace('*', id); | ||
| const stats = fs.statSync(resolved, { throwIfNoEntry: false }); | ||
| if (stats && stats.isFile()) { | ||
| return normalizePath(resolved); | ||
| } | ||
| } | ||
| return null; | ||
| }; | ||
|
|
||
| aliases.push({ | ||
| // Build regex from alias pattern (e.g., '@styles/*' -> /^@styles\/(.+)$/) | ||
| // First, escape special regex chars. Then replace * with a capture group (.+) | ||
| find: new RegExp( | ||
| `^${aliasPattern.replace(/[\\^$+?.()|[\]{}]/g, '\\$&').replace(/\*/g, '(.+)')}$`, | ||
| ), | ||
| replacement: aliasPattern.includes('*') ? '$1' : aliasPattern, | ||
| customResolver, | ||
| }); | ||
| /** | ||
| * Resolve an import id against tsconfig path aliases. | ||
| * Tries each alias replacement in order, returning the first that maps to an existing file. | ||
| */ | ||
| function resolveWithAlias(id: string, configAlias: Alias[]): string | null { | ||
| for (const alias of configAlias) { | ||
| if (alias.find.test(id)) { | ||
| const updatedId = id.replace(alias.find, alias.replacement); | ||
| const stats = fs.statSync(updatedId, { throwIfNoEntry: false }); | ||
| if (stats && stats.isFile()) { | ||
| return normalizePath(updatedId); | ||
| } | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| return aliases; | ||
| }; | ||
| /** Regex matching CSS @import statements with the specifier in capture group 1. */ | ||
| const cssImportRE = /@import\s+(?:url\(\s*)?['"]([^'"]+)['"]\s*\)?/g; | ||
|
matthewp marked this conversation as resolved.
Resolved
|
||
|
|
||
| /** Returns a Vite plugin used to alias paths from tsconfig.json and jsconfig.json. */ | ||
| /** Returns Vite plugins used to alias paths from tsconfig.json and jsconfig.json. */ | ||
| export default function configAliasVitePlugin({ | ||
| settings, | ||
| }: { | ||
| settings: AstroSettings; | ||
| }): VitePlugin | null { | ||
| }): VitePlugin[] | null { | ||
| const configAlias = getConfigAlias(settings); | ||
| if (!configAlias) return null; | ||
|
|
||
| const plugin: VitePlugin = { | ||
| name: 'astro:tsconfig-alias', | ||
| // use post to only resolve ids that all other plugins before it can't | ||
| enforce: 'post', | ||
| config() { | ||
| // Return vite.resolve.alias config with custom resolvers | ||
| return { | ||
| resolve: { | ||
| alias: getViteResolveAlias(settings), | ||
| return [ | ||
| // Pre-plugin: rewrite CSS @import aliases to absolute paths before Vite's CSS plugin. | ||
| // Vite's internal CSS @import resolver (postcss-import) uses a mini plugin container | ||
| // that doesn't include user resolveId hooks, so we must rewrite aliases in a transform | ||
| // hook that runs before Vite's CSS processing. | ||
| { | ||
| name: 'astro:tsconfig-alias-css', | ||
| enforce: 'pre', | ||
| transform: { | ||
| filter: { | ||
| id: { | ||
| include: /\.css$/, | ||
| }, | ||
| }, | ||
| }; | ||
| }, | ||
| resolveId: { | ||
| filter: { | ||
| id: { | ||
| include: configAlias.map((alias) => alias.find), | ||
| exclude: /(?:\0|^virtual:|^astro:)/, | ||
| handler(code) { | ||
| if (!code.includes('@import')) return; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why don't we use the regex here? Also, using this check puts us under a lot of false positives e.g.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a fast-path early exit for when the code doesn't contain any imports, skipping the more expensive regex. I added a comment explaining that. |
||
|
|
||
| let hasReplacement = false; | ||
| const result = code.replace(cssImportRE, (match, importId) => { | ||
| if (!importId) return match; | ||
|
|
||
| const resolved = resolveWithAlias(importId, configAlias); | ||
| if (resolved) { | ||
| hasReplacement = true; | ||
| return match.replace(importId, resolved); | ||
| } | ||
| return match; | ||
| }); | ||
|
|
||
| if (hasReplacement) { | ||
| return { code: result, map: null }; | ||
| } | ||
| }, | ||
| }, | ||
| async handler(id, importer, options) { | ||
| // Handle aliases found from `compilerOptions.paths`. Unlike Vite aliases, tsconfig aliases | ||
| // are best effort only, so we have to manually replace them here, instead of using `vite.resolve.alias` | ||
| for (const alias of configAlias) { | ||
| if (alias.find.test(id)) { | ||
| const updatedId = id.replace(alias.find, alias.replacement); | ||
|
|
||
| // Vite may pass an id with "*" when resolving glob import paths | ||
| // Returning early allows Vite to handle the final resolution | ||
| // See https://github.com/withastro/astro/issues/9258#issuecomment-1838806157 | ||
| if (updatedId.includes('*')) { | ||
| return updatedId; | ||
| }, | ||
| // Post-plugin: resolve JS/TS imports using tsconfig path aliases via resolveId. | ||
| { | ||
| name: 'astro:tsconfig-alias', | ||
| // use post to only resolve ids that all other plugins before it can't | ||
| enforce: 'post', | ||
| resolveId: { | ||
| filter: { | ||
| id: { | ||
| include: configAlias.map((alias) => alias.find), | ||
| exclude: /(?:\0|^virtual:|^astro:)/, | ||
| }, | ||
| }, | ||
| async handler(id, importer, options) { | ||
| // Handle aliases found from `compilerOptions.paths`. Unlike Vite aliases, tsconfig aliases | ||
| // are best effort only, so we have to manually replace them here, instead of using `vite.resolve.alias` | ||
| for (const alias of configAlias) { | ||
| if (alias.find.test(id)) { | ||
| const updatedId = id.replace(alias.find, alias.replacement); | ||
|
|
||
| // Vite may pass an id with "*" when resolving glob import paths | ||
| // Returning early allows Vite to handle the final resolution | ||
| // See https://github.com/withastro/astro/issues/9258#issuecomment-1838806157 | ||
| if (updatedId.includes('*')) { | ||
| return updatedId; | ||
| } | ||
|
|
||
| const resolved = await this.resolve(updatedId, importer, { | ||
| skipSelf: true, | ||
| ...options, | ||
| }); | ||
| if (resolved) return resolved; | ||
| } | ||
|
|
||
| const resolved = await this.resolve(updatedId, importer, { | ||
| skipSelf: true, | ||
| ...options, | ||
| }); | ||
| if (resolved) return resolved; | ||
| } | ||
| } | ||
| }, | ||
| }, | ||
| }, | ||
| }; | ||
|
|
||
| return plugin; | ||
| ]; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changeset is highly technical. It could just say that we fixed vite warnings