From 1befb90bba5ca32b62b54ac76bd5a83e375ced87 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 14:44:03 +0100 Subject: [PATCH 01/30] feat: plugin hook filters --- .../astro/src/actions/vite-plugin-actions.ts | 88 +++++++++++-------- packages/astro/src/assets/consts.ts | 1 + .../src/assets/fonts/vite-plugin-fonts.ts | 18 ++-- .../astro/src/assets/vite-plugin-assets.ts | 41 +++++---- .../src/container/vite-plugin-container.ts | 13 +-- .../src/content/vite-plugin-content-assets.ts | 61 +++++++------ .../vite-plugin-content-virtual-mod.ts | 82 +++++++++-------- 7 files changed, 174 insertions(+), 130 deletions(-) diff --git a/packages/astro/src/actions/vite-plugin-actions.ts b/packages/astro/src/actions/vite-plugin-actions.ts index c2a90c00ad27..e77e69b93c1f 100644 --- a/packages/astro/src/actions/vite-plugin-actions.ts +++ b/packages/astro/src/actions/vite-plugin-actions.ts @@ -59,27 +59,30 @@ export function vitePluginActions({ return { name: VIRTUAL_MODULE_ID, enforce: 'pre', - async resolveId(id) { - if (id === VIRTUAL_MODULE_ID) { - return RESOLVED_VIRTUAL_MODULE_ID; - } - - if (id === OPTIONS_VIRTUAL_MODULE_ID) { - return RESOLVED_OPTIONS_VIRTUAL_MODULE_ID; - } - - if (id === ACTIONS_ENTRYPOINT_VIRTUAL_MODULE_ID) { - const resolvedModule = await this.resolve( - `${decodeURI(new URL('actions', settings.config.srcDir).pathname)}`, - ); - - if (!resolvedModule) { - return RESOLVED_NOOP_ENTRYPOINT_VIRTUAL_MODULE_ID; + resolveId: { + filter: { + id: new RegExp( + `^(${VIRTUAL_MODULE_ID}|${OPTIONS_VIRTUAL_MODULE_ID}|${ACTIONS_ENTRYPOINT_VIRTUAL_MODULE_ID})$`, + ), + }, + async handler(id) { + if (id === VIRTUAL_MODULE_ID) { + return RESOLVED_VIRTUAL_MODULE_ID; } - - resolvedActionsId = resolvedModule.id; - return ACTIONS_RESOLVED_ENTRYPOINT_VIRTUAL_MODULE_ID; - } + if (id === OPTIONS_VIRTUAL_MODULE_ID) { + return RESOLVED_OPTIONS_VIRTUAL_MODULE_ID; + } + if (id === ACTIONS_ENTRYPOINT_VIRTUAL_MODULE_ID) { + const resolvedModule = await this.resolve( + `${decodeURI(new URL('actions', settings.config.srcDir).pathname)}`, + ); + if (!resolvedModule) { + return RESOLVED_NOOP_ENTRYPOINT_VIRTUAL_MODULE_ID; + } + resolvedActionsId = resolvedModule.id; + return ACTIONS_RESOLVED_ENTRYPOINT_VIRTUAL_MODULE_ID; + } + }, }, async configureServer(server) { const filePresentOnStartup = await isActionsFilePresent(fs, settings.config.srcDir); @@ -93,35 +96,42 @@ export function vitePluginActions({ server.watcher.on('add', watcherCallback); server.watcher.on('change', watcherCallback); }, - async load(id) { - if (id === RESOLVED_VIRTUAL_MODULE_ID) { - if (this.environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.client) { + load: { + filter: { + id: new RegExp( + `^(${RESOLVED_VIRTUAL_MODULE_ID}|${RESOLVED_NOOP_ENTRYPOINT_VIRTUAL_MODULE_ID}|${ACTIONS_RESOLVED_ENTRYPOINT_VIRTUAL_MODULE_ID}|${RESOLVED_OPTIONS_VIRTUAL_MODULE_ID})$`, + ), + }, + async handler(id) { + if (id === RESOLVED_VIRTUAL_MODULE_ID) { + if (this.environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.client) { + return { + code: `export * from 'astro/actions/runtime/entrypoints/client.js';`, + }; + } return { - code: `export * from 'astro/actions/runtime/entrypoints/client.js';`, + code: `export * from 'astro/actions/runtime/entrypoints/server.js';`, }; } - return { - code: `export * from 'astro/actions/runtime/entrypoints/server.js';`, - }; - } - if (id === RESOLVED_NOOP_ENTRYPOINT_VIRTUAL_MODULE_ID) { - return { code: 'export const server = {}' }; - } + if (id === RESOLVED_NOOP_ENTRYPOINT_VIRTUAL_MODULE_ID) { + return { code: 'export const server = {}' }; + } - if (id === ACTIONS_RESOLVED_ENTRYPOINT_VIRTUAL_MODULE_ID) { - return { code: `export { server } from ${JSON.stringify(resolvedActionsId)};` }; - } + if (id === ACTIONS_RESOLVED_ENTRYPOINT_VIRTUAL_MODULE_ID) { + return { code: `export { server } from ${JSON.stringify(resolvedActionsId)};` }; + } - if (id === RESOLVED_OPTIONS_VIRTUAL_MODULE_ID) { - return { - code: ` + if (id === RESOLVED_OPTIONS_VIRTUAL_MODULE_ID) { + return { + code: ` export const shouldAppendTrailingSlash = ${JSON.stringify( shouldAppendForwardSlash(settings.config.trailingSlash, settings.config.build.format), )}; `, - }; - } + }; + } + }, }, }; } diff --git a/packages/astro/src/assets/consts.ts b/packages/astro/src/assets/consts.ts index 5fae641ae462..b1c522a9e58b 100644 --- a/packages/astro/src/assets/consts.ts +++ b/packages/astro/src/assets/consts.ts @@ -1,4 +1,5 @@ export const VIRTUAL_MODULE_ID = 'astro:assets'; +export const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID; export const VIRTUAL_SERVICE_ID = 'virtual:image-service'; export const VALID_INPUT_FORMATS = [ 'jpeg', diff --git a/packages/astro/src/assets/fonts/vite-plugin-fonts.ts b/packages/astro/src/assets/fonts/vite-plugin-fonts.ts index a673aab34bdd..21be8b243aa1 100644 --- a/packages/astro/src/assets/fonts/vite-plugin-fonts.ts +++ b/packages/astro/src/assets/fonts/vite-plugin-fonts.ts @@ -308,20 +308,26 @@ export function fontsPlugin({ settings, sync, logger }: Options): Plugin { } }); }, - resolveId(id) { - if (id === VIRTUAL_MODULE_ID) { + resolveId: { + filter: { + id: new RegExp(`^(${VIRTUAL_MODULE_ID})$`), + }, + handler() { return RESOLVED_VIRTUAL_MODULE_ID; - } + }, }, - load(id) { - if (id === RESOLVED_VIRTUAL_MODULE_ID) { + load: { + filter: { + id: new RegExp(`^(${RESOLVED_VIRTUAL_MODULE_ID})$`), + }, + handler() { return { code: ` export const internalConsumableMap = new Map(${JSON.stringify(Array.from(internalConsumableMap?.entries() ?? []))}); export const consumableMap = new Map(${JSON.stringify(Array.from(consumableMap?.entries() ?? []))}); `, }; - } + }, }, async buildEnd() { if (settings.config.experimental.fonts!.length === 0) { diff --git a/packages/astro/src/assets/vite-plugin-assets.ts b/packages/astro/src/assets/vite-plugin-assets.ts index 9626abd483b4..1cf6307adeee 100644 --- a/packages/astro/src/assets/vite-plugin-assets.ts +++ b/packages/astro/src/assets/vite-plugin-assets.ts @@ -13,7 +13,12 @@ import { } from '../core/path.js'; import { normalizePath } from '../core/viteUtils.js'; import type { AstroSettings } from '../types/astro.js'; -import { VALID_INPUT_FORMATS, VIRTUAL_MODULE_ID, VIRTUAL_SERVICE_ID } from './consts.js'; +import { + RESOLVED_VIRTUAL_MODULE_ID, + VALID_INPUT_FORMATS, + VIRTUAL_MODULE_ID, + VIRTUAL_SERVICE_ID, +} from './consts.js'; import { fontsPlugin } from './fonts/vite-plugin-fonts.js'; import type { ImageTransform } from './types.js'; import { getAssetsPrefix } from './utils/getAssetsPrefix.js'; @@ -23,8 +28,6 @@ import { getProxyCode } from './utils/proxy.js'; import { makeSvgComponent } from './utils/svg.js'; import { createPlaceholderURL, stringifyPlaceholderURL } from './utils/url.js'; -const resolvedVirtualModuleId = '\0' + VIRTUAL_MODULE_ID; - const assetRegex = new RegExp(`\\.(${VALID_INPUT_FORMATS.join('|')})`, 'i'); const assetRegexEnds = new RegExp(`\\.(${VALID_INPUT_FORMATS.join('|')})$`, 'i'); const addStaticImageFactory = ( @@ -126,19 +129,27 @@ export default function assets({ fs, settings, sync, logger }: Options): vite.Pl config(_, env) { isBuild = env.command === 'build'; }, - async resolveId(id, _importer, options) { - if (id === VIRTUAL_SERVICE_ID) { - if (options?.ssr) { - return await this.resolve(settings.config.image.service.entrypoint); + resolveId: { + filter: { + id: new RegExp(`^(${VIRTUAL_SERVICE_ID}|${VIRTUAL_MODULE_ID})$`), + }, + async handler(id, _importer, options) { + if (id === VIRTUAL_SERVICE_ID) { + if (options?.ssr) { + return await this.resolve(settings.config.image.service.entrypoint); + } + return await this.resolve('astro/assets/services/noop'); } - return await this.resolve('astro/assets/services/noop'); - } - if (id === VIRTUAL_MODULE_ID) { - return resolvedVirtualModuleId; - } + if (id === VIRTUAL_MODULE_ID) { + return RESOLVED_VIRTUAL_MODULE_ID; + } + }, }, - load(id) { - if (id === resolvedVirtualModuleId) { + load: { + filter: { + id: new RegExp(`^(${RESOLVED_VIRTUAL_MODULE_ID})$`), + }, + handler() { return { code: ` export { getConfiguredImageService, isLocalService } from "astro/assets"; @@ -185,7 +196,7 @@ export default function assets({ fs, settings, sync, logger }: Options): vite.Pl export const getFontData = createGetFontData(fontsMod); `, }; - } + }, }, buildStart() { if (!isBuild) return; diff --git a/packages/astro/src/container/vite-plugin-container.ts b/packages/astro/src/container/vite-plugin-container.ts index 2098ff0866e2..77d19534e3dd 100644 --- a/packages/astro/src/container/vite-plugin-container.ts +++ b/packages/astro/src/container/vite-plugin-container.ts @@ -1,15 +1,18 @@ import type * as vite from 'vite'; -const virtualModuleId = 'astro:container'; +const VIRTUAL_MODULE_ID = 'astro:container'; export default function astroContainer(): vite.Plugin { return { - name: 'astro:container', + name: VIRTUAL_MODULE_ID, enforce: 'pre', - resolveId(id) { - if (id === virtualModuleId) { + resolveId: { + filter: { + id: new RegExp(`^(${VIRTUAL_MODULE_ID})$`), + }, + handler() { return this.resolve('astro/virtual-modules/container.js'); - } + }, }, }; } diff --git a/packages/astro/src/content/vite-plugin-content-assets.ts b/packages/astro/src/content/vite-plugin-content-assets.ts index 7424011f230e..215e032434fa 100644 --- a/packages/astro/src/content/vite-plugin-content-assets.ts +++ b/packages/astro/src/content/vite-plugin-content-assets.ts @@ -30,40 +30,45 @@ export function astroContentAssetPropagationPlugin({ return { name: 'astro:content-asset-propagation', enforce: 'pre', - async resolveId(id, importer, opts) { - if (hasContentFlag(id, CONTENT_IMAGE_FLAG)) { - const [base, query] = id.split('?'); - const params = new URLSearchParams(query); - const importerParam = params.get('importer'); + resolveId: { + filter: { + id: new RegExp(`[?&](${CONTENT_IMAGE_FLAG}|${CONTENT_RENDER_FLAG})`), + }, + async handler(id, importer, opts) { + if (hasContentFlag(id, CONTENT_IMAGE_FLAG)) { + const [base, query] = id.split('?'); + const params = new URLSearchParams(query); + const importerParam = params.get('importer'); - const importerPath = importerParam - ? fileURLToPath(new URL(importerParam, settings.config.root)) - : importer; + const importerPath = importerParam + ? fileURLToPath(new URL(importerParam, settings.config.root)) + : importer; - const resolved = await this.resolve(base, importerPath, { skipSelf: true, ...opts }); - if (!resolved) { - throw new AstroError({ - ...AstroErrorData.ImageNotFound, - message: AstroErrorData.ImageNotFound.message(base), - }); + const resolved = await this.resolve(base, importerPath, { skipSelf: true, ...opts }); + if (!resolved) { + throw new AstroError({ + ...AstroErrorData.ImageNotFound, + message: AstroErrorData.ImageNotFound.message(base), + }); + } + return resolved; } - return resolved; - } - if (hasContentFlag(id, CONTENT_RENDER_FLAG)) { - const base = id.split('?')[0]; + if (hasContentFlag(id, CONTENT_RENDER_FLAG)) { + const base = id.split('?')[0]; - for (const { extensions, handlePropagation = true } of settings.contentEntryTypes) { - if (handlePropagation && extensions.includes(extname(base))) { - return this.resolve(`${base}?${PROPAGATED_ASSET_FLAG}`, importer, { - skipSelf: true, - ...opts, - }); + for (const { extensions, handlePropagation = true } of settings.contentEntryTypes) { + if (handlePropagation && extensions.includes(extname(base))) { + return this.resolve(`${base}?${PROPAGATED_ASSET_FLAG}`, importer, { + skipSelf: true, + ...opts, + }); + } } + // Resolve to the base id (no content flags) + // if Astro doesn't need to handle propagation. + return this.resolve(base, importer, { skipSelf: true, ...opts }); } - // Resolve to the base id (no content flags) - // if Astro doesn't need to handle propagation. - return this.resolve(base, importer, { skipSelf: true, ...opts }); - } + }, }, configureServer(server) { if (!isRunnableDevEnvironment(server.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr])) { diff --git a/packages/astro/src/content/vite-plugin-content-virtual-mod.ts b/packages/astro/src/content/vite-plugin-content-virtual-mod.ts index 86509ad6a510..e2b0a212258a 100644 --- a/packages/astro/src/content/vite-plugin-content-virtual-mod.ts +++ b/packages/astro/src/content/vite-plugin-content-virtual-mod.ts @@ -11,6 +11,7 @@ import { ASSET_IMPORTS_FILE, ASSET_IMPORTS_RESOLVED_STUB_ID, ASSET_IMPORTS_VIRTUAL_ID, + CONTENT_MODULE_FLAG, CONTENT_RENDER_FLAG, DATA_STORE_VIRTUAL_ID, MODULES_IMPORTS_FILE, @@ -66,50 +67,57 @@ export function astroContentVirtualModPlugin({ invalidateDataStore(devServer); } }, - async resolveId(id, importer) { - if (id === VIRTUAL_MODULE_ID) { - // Live content config can't import the virtual module directly, - // because it would create a circular dependency from the collection exports. - // Instead, we resolve the config util module, because that's all that it should use anyway. - if (liveConfig && importer && liveConfig === normalizePath(importer)) { - return this.resolve('astro/virtual-modules/live-config', importer, { - skipSelf: true, - }); + resolveId: { + filter: { + id: new RegExp( + `^(${VIRTUAL_MODULE_ID}|${DATA_STORE_VIRTUAL_ID}|${MODULES_MJS_ID}|${ASSET_IMPORTS_VIRTUAL_ID})$|[?&]${CONTENT_MODULE_FLAG}`, + ), + }, + async handler(id, importer) { + if (id === VIRTUAL_MODULE_ID) { + // Live content config can't import the virtual module directly, + // because it would create a circular dependency from the collection exports. + // Instead, we resolve the config util module, because that's all that it should use anyway. + if (liveConfig && importer && liveConfig === normalizePath(importer)) { + return this.resolve('astro/virtual-modules/live-config', importer, { + skipSelf: true, + }); + } + return RESOLVED_VIRTUAL_MODULE_ID; } - return RESOLVED_VIRTUAL_MODULE_ID; - } - if (id === DATA_STORE_VIRTUAL_ID) { - return RESOLVED_DATA_STORE_VIRTUAL_ID; - } - - if (isDeferredModule(id)) { - const [, query] = id.split('?'); - const params = new URLSearchParams(query); - const fileName = params.get('fileName'); - let importPath = undefined; - if (fileName && URL.canParse(fileName, settings.config.root.toString())) { - importPath = fileURLToPath(new URL(fileName, settings.config.root)); + if (id === DATA_STORE_VIRTUAL_ID) { + return RESOLVED_DATA_STORE_VIRTUAL_ID; } - if (importPath) { - return await this.resolve(`${importPath}?${CONTENT_RENDER_FLAG}`); + + if (isDeferredModule(id)) { + const [, query] = id.split('?'); + const params = new URLSearchParams(query); + const fileName = params.get('fileName'); + let importPath = undefined; + if (fileName && URL.canParse(fileName, settings.config.root.toString())) { + importPath = fileURLToPath(new URL(fileName, settings.config.root)); + } + if (importPath) { + return await this.resolve(`${importPath}?${CONTENT_RENDER_FLAG}`); + } } - } - if (id === MODULES_MJS_ID) { - const modules = new URL(MODULES_IMPORTS_FILE, settings.dotAstroDir); - if (fs.existsSync(modules)) { - return fileURLToPath(modules); + if (id === MODULES_MJS_ID) { + const modules = new URL(MODULES_IMPORTS_FILE, settings.dotAstroDir); + if (fs.existsSync(modules)) { + return fileURLToPath(modules); + } + return MODULES_MJS_VIRTUAL_ID; } - return MODULES_MJS_VIRTUAL_ID; - } - if (id === ASSET_IMPORTS_VIRTUAL_ID) { - const assetImportsFile = new URL(ASSET_IMPORTS_FILE, settings.dotAstroDir); - if (fs.existsSync(assetImportsFile)) { - return fileURLToPath(assetImportsFile); + if (id === ASSET_IMPORTS_VIRTUAL_ID) { + const assetImportsFile = new URL(ASSET_IMPORTS_FILE, settings.dotAstroDir); + if (fs.existsSync(assetImportsFile)) { + return fileURLToPath(assetImportsFile); + } + return ASSET_IMPORTS_RESOLVED_STUB_ID; } - return ASSET_IMPORTS_RESOLVED_STUB_ID; - } + }, }, async load(id, args) { if (id === RESOLVED_VIRTUAL_MODULE_ID) { From c383f54b8c2235021e6395256b74d4d3a32ece9f Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 15:02:44 +0100 Subject: [PATCH 02/30] wip --- .../build/plugins/plugin-component-entry.ts | 14 ++-- .../src/core/build/plugins/plugin-noop.ts | 28 ++++---- .../src/core/build/plugins/plugin-ssr.ts | 65 +++++++++++-------- .../astro/src/core/middleware/vite-plugin.ts | 46 +++++++------ 4 files changed, 87 insertions(+), 66 deletions(-) diff --git a/packages/astro/src/core/build/plugins/plugin-component-entry.ts b/packages/astro/src/core/build/plugins/plugin-component-entry.ts index c06670a4b98e..0d867661017b 100644 --- a/packages/astro/src/core/build/plugins/plugin-component-entry.ts +++ b/packages/astro/src/core/build/plugins/plugin-component-entry.ts @@ -57,13 +57,11 @@ export function pluginComponentEntry(internals: BuildInternals): VitePlugin { }); } }, - async resolveId(id) { - if (id.startsWith(astroEntryPrefix)) { - return id; - } - }, - async load(id) { - if (id.startsWith(astroEntryPrefix)) { + load: { + filter: { + id: new RegExp(`^${astroEntryPrefix}`), + }, + async handler(id) { const componentId = id.slice(astroEntryPrefix.length); const exportNames = componentToExportNames.get(componentId); if (exportNames) { @@ -71,7 +69,7 @@ export function pluginComponentEntry(internals: BuildInternals): VitePlugin { code: `export { ${exportNames.join(', ')} } from ${JSON.stringify(componentId)}`, }; } - } + }, }, }; } diff --git a/packages/astro/src/core/build/plugins/plugin-noop.ts b/packages/astro/src/core/build/plugins/plugin-noop.ts index 5478979a0160..544ba1417e2a 100644 --- a/packages/astro/src/core/build/plugins/plugin-noop.ts +++ b/packages/astro/src/core/build/plugins/plugin-noop.ts @@ -10,24 +10,30 @@ const RESOLVED_NOOP_MODULE_ID = '\0' + NOOP_MODULE_ID; export function pluginNoop(): vite.Plugin { return { name: 'plugin-noop', - resolveId(id) { - if(id === NOOP_MODULE_ID) { + resolveId: { + filter: { + id: new RegExp(`^${NOOP_MODULE_ID}$`), + }, + handler() { return RESOLVED_NOOP_MODULE_ID; - } + }, }, - load(id) { - if(id === RESOLVED_NOOP_MODULE_ID) { + load: { + filter: { + id: new RegExp(`^${RESOLVED_NOOP_MODULE_ID}$`), + }, + handler() { return 'export const noop = {};'; - } + }, }, generateBundle(_options, bundle) { // Delete this bundle so that its not written out to disk. - for(const [name, chunk] of Object.entries(bundle)) { - if(chunk.type === 'asset') continue; - if(chunk.facadeModuleId === RESOLVED_NOOP_MODULE_ID) { + for (const [name, chunk] of Object.entries(bundle)) { + if (chunk.type === 'asset') continue; + if (chunk.facadeModuleId === RESOLVED_NOOP_MODULE_ID) { delete bundle[name]; } } - } - } + }, + }; } diff --git a/packages/astro/src/core/build/plugins/plugin-ssr.ts b/packages/astro/src/core/build/plugins/plugin-ssr.ts index 4e20c63eb2f5..e5e0d0d1af7a 100644 --- a/packages/astro/src/core/build/plugins/plugin-ssr.ts +++ b/packages/astro/src/core/build/plugins/plugin-ssr.ts @@ -20,20 +20,26 @@ function vitePluginAdapter(adapter: AstroAdapter): VitePlugin { applyToEnvironment(environment) { return environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr; }, - resolveId(id) { - if (id === ADAPTER_VIRTUAL_MODULE_ID) { + resolveId: { + filter: { + id: new RegExp(`^${ADAPTER_VIRTUAL_MODULE_ID}$`), + }, + handler() { return RESOLVED_ADAPTER_VIRTUAL_MODULE_ID; - } + }, }, - async load(id) { - if (id === RESOLVED_ADAPTER_VIRTUAL_MODULE_ID) { + load: { + filter: { + id: new RegExp(`^${RESOLVED_ADAPTER_VIRTUAL_MODULE_ID}$`), + }, + async handler() { const adapterEntrypointStr = JSON.stringify(adapter.serverEntrypoint); return { code: `export * from ${adapterEntrypointStr}; import * as _serverEntrypoint from ${adapterEntrypointStr}; export default _serverEntrypoint.default;`, }; - } + }, }, }; } @@ -50,41 +56,50 @@ function vitePluginAdapterConfig(adapter: AstroAdapter): VitePlugin { applyToEnvironment(environment) { return environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr; }, - resolveId(id) { - if (id === ADAPTER_CONFIG_VIRTUAL_MODULE_ID) { + resolveId: { + filter: { + id: new RegExp(`^${ADAPTER_CONFIG_VIRTUAL_MODULE_ID}$`), + }, + handler() { return RESOLVED_ADAPTER_CONFIG_VIRTUAL_MODULE_ID; - } + }, }, - load(id) { - if (id === RESOLVED_ADAPTER_CONFIG_VIRTUAL_MODULE_ID) { + load: { + filter: { + id: new RegExp(`^${RESOLVED_ADAPTER_CONFIG_VIRTUAL_MODULE_ID}$`), + }, + handler() { return { code: `export const args = ${adapter.args ? JSON.stringify(adapter.args, null, 2) : 'undefined'}; export const exports = ${adapter.exports ? JSON.stringify(adapter.exports) : 'undefined'}; export const adapterFeatures = ${adapter.adapterFeatures ? JSON.stringify(adapter.adapterFeatures, null, 2) : 'undefined'}; export const serverEntrypoint = ${JSON.stringify(adapter.serverEntrypoint)};`, }; - } + }, }, }; } -function vitePluginSSR( - internals: BuildInternals, - adapter: AstroAdapter, -): VitePlugin { +function vitePluginSSR(internals: BuildInternals, adapter: AstroAdapter): VitePlugin { return { name: '@astrojs/vite-plugin-astro-ssr-server', enforce: 'post', applyToEnvironment(environment) { return environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr; }, - resolveId(id) { - if (id === SSR_VIRTUAL_MODULE_ID) { + resolveId: { + filter: { + id: new RegExp(`^${SSR_VIRTUAL_MODULE_ID}$`), + }, + handler() { return RESOLVED_SSR_VIRTUAL_MODULE_ID; - } + }, }, - load(id) { - if (id === RESOLVED_SSR_VIRTUAL_MODULE_ID) { + load: { + filter: { + id: new RegExp(`^${RESOLVED_SSR_VIRTUAL_MODULE_ID}$`), + }, + handler() { const exports: string[] = []; if (adapter.exports) { @@ -102,7 +117,7 @@ function vitePluginSSR( return { code: `import _exports from 'astro/entrypoints/legacy';\n${exports.join('\n')}`, }; - } + }, }, async generateBundle(_opts, bundle) { // Add assets from this SSR chunk as well. @@ -115,10 +130,7 @@ function vitePluginSSR( }; } -export function pluginSSR( - options: StaticBuildOptions, - internals: BuildInternals, -): VitePlugin[] { +export function pluginSSR(options: StaticBuildOptions, internals: BuildInternals): VitePlugin[] { // We check before this point if there's an adapter, so we can safely assume it exists here. const adapter = options.settings.adapter!; const ssr = options.settings.buildOutput === 'server'; @@ -131,4 +143,3 @@ export function pluginSSR( return plugins; } - diff --git a/packages/astro/src/core/middleware/vite-plugin.ts b/packages/astro/src/core/middleware/vite-plugin.ts index cab3082c7bcb..efb588803074 100644 --- a/packages/astro/src/core/middleware/vite-plugin.ts +++ b/packages/astro/src/core/middleware/vite-plugin.ts @@ -30,8 +30,11 @@ export function vitePluginMiddleware({ settings }: { settings: AstroSettings }): environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender ); }, - async resolveId(id) { - if (id === MIDDLEWARE_MODULE_ID) { + resolveId: { + filter: { + id: new RegExp(`^${MIDDLEWARE_MODULE_ID}$`), + }, + async handler() { const middlewareId = await this.resolve( `${decodeURI(settings.config.srcDir.pathname)}${MIDDLEWARE_PATH_SEGMENT_NAME}`, ); @@ -44,26 +47,28 @@ export function vitePluginMiddleware({ settings }: { settings: AstroSettings }): } else { return NOOP_MIDDLEWARE; } - } - if (id === NOOP_MIDDLEWARE) { - return NOOP_MIDDLEWARE; - } + }, }, - async load(id) { - if (id === NOOP_MIDDLEWARE) { - if (!userMiddlewareIsPresent && settings.config.i18n?.routing === 'manual') { - throw new AstroError(MissingMiddlewareForInternationalization); - } - return { code: 'export const onRequest = (_, next) => next()' }; - } else if (id === MIDDLEWARE_RESOLVED_MODULE_ID) { - if (!userMiddlewareIsPresent && settings.config.i18n?.routing === 'manual') { - throw new AstroError(MissingMiddlewareForInternationalization); + load: { + filter: { + id: new RegExp(`^(${NOOP_MIDDLEWARE}|${MIDDLEWARE_RESOLVED_MODULE_ID})$`), + }, + async handler(id) { + if (id === NOOP_MIDDLEWARE) { + if (!userMiddlewareIsPresent && settings.config.i18n?.routing === 'manual') { + throw new AstroError(MissingMiddlewareForInternationalization); + } + return { code: 'export const onRequest = (_, next) => next()' }; } + if (id === MIDDLEWARE_RESOLVED_MODULE_ID) { + if (!userMiddlewareIsPresent && settings.config.i18n?.routing === 'manual') { + throw new AstroError(MissingMiddlewareForInternationalization); + } - const preMiddleware = createMiddlewareImports(settings.middlewares.pre, 'pre'); - const postMiddleware = createMiddlewareImports(settings.middlewares.post, 'post'); + const preMiddleware = createMiddlewareImports(settings.middlewares.pre, 'pre'); + const postMiddleware = createMiddlewareImports(settings.middlewares.post, 'post'); - const code = ` + const code = ` ${ userMiddlewareIsPresent ? `import { onRequest as userOnRequest } from '${resolvedMiddlewareId}';` @@ -79,8 +84,9 @@ export const onRequest = sequence( ); `.trim(); - return { code }; - } + return { code }; + } + }, }, }; } From 3feb29ff4a7c7aa5025845c7eb2f688eb395db10 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 15:07:38 +0100 Subject: [PATCH 03/30] wip --- .../vite-plugin-server-islands.ts | 22 +++-- .../astro/src/core/session/vite-plugin.ts | 18 ++-- packages/astro/src/env/vite-plugin-env.ts | 98 +++++++++++-------- packages/astro/src/i18n/vite-plugin-i18n.ts | 13 ++- 4 files changed, 89 insertions(+), 62 deletions(-) diff --git a/packages/astro/src/core/server-islands/vite-plugin-server-islands.ts b/packages/astro/src/core/server-islands/vite-plugin-server-islands.ts index b4317082063c..090ced0ec356 100644 --- a/packages/astro/src/core/server-islands/vite-plugin-server-islands.ts +++ b/packages/astro/src/core/server-islands/vite-plugin-server-islands.ts @@ -26,19 +26,23 @@ export function vitePluginServerIslands({ settings }: AstroPluginOptions): ViteP configureServer(server) { ssrEnvironment = server.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr]; }, - resolveId(name) { - if (name === SERVER_ISLAND_MANIFEST) { + resolveId: { + filter: { + id: new RegExp(`^${SERVER_ISLAND_MANIFEST}$`), + }, + handler() { return RESOLVED_SERVER_ISLAND_MANIFEST; - } + }, }, - load(id) { - if (id === RESOLVED_SERVER_ISLAND_MANIFEST) { + load: { + filter: { + id: new RegExp(`^${RESOLVED_SERVER_ISLAND_MANIFEST}$`), + }, + handler() { return { - code: ` - export const serverIslandMap = ${serverIslandPlaceholderMap};\n\nexport const serverIslandNameMap = ${serverIslandPlaceholderNameMap}; - `, + code: `export const serverIslandMap = ${serverIslandPlaceholderMap};\n\nexport const serverIslandNameMap = ${serverIslandPlaceholderNameMap};`, }; - } + }, }, async transform(_code, id) { diff --git a/packages/astro/src/core/session/vite-plugin.ts b/packages/astro/src/core/session/vite-plugin.ts index bb76357c708e..e8f26c96680c 100644 --- a/packages/astro/src/core/session/vite-plugin.ts +++ b/packages/astro/src/core/session/vite-plugin.ts @@ -14,14 +14,20 @@ export function vitePluginSessionDriver({ settings }: { settings: AstroSettings name: VIRTUAL_SESSION_DRIVER_ID, enforce: 'pre', - async resolveId(id) { - if (id === VIRTUAL_SESSION_DRIVER_ID) { + resolveId: { + filter: { + id: new RegExp(`^${VIRTUAL_SESSION_DRIVER_ID}$`), + }, + handler() { return RESOLVED_VIRTUAL_SESSION_DRIVER_ID; - } + }, }, - async load(id) { - if (id === RESOLVED_VIRTUAL_SESSION_DRIVER_ID) { + load: { + filter: { + id: new RegExp(`^${RESOLVED_VIRTUAL_SESSION_DRIVER_ID}$`), + }, + async handler() { if (settings.config.session) { let sessionDriver: string; if (settings.config.session.driver === 'fs') { @@ -51,7 +57,7 @@ export function vitePluginSessionDriver({ settings }: { settings: AstroSettings } else { return { code: 'export default null;' }; } - } + }, }, }; } diff --git a/packages/astro/src/env/vite-plugin-env.ts b/packages/astro/src/env/vite-plugin-env.ts index bc9020461ea4..0505b24d4331 100644 --- a/packages/astro/src/env/vite-plugin-env.ts +++ b/packages/astro/src/env/vite-plugin-env.ts @@ -49,53 +49,67 @@ export function astroEnv({ settings, sync, envLoader }: AstroEnvPluginParams): P populated = true; }, - resolveId(id) { - if (id === CLIENT_VIRTUAL_MODULE_ID) { - return RESOLVED_CLIENT_VIRTUAL_MODULE_ID; - } - if (id === SERVER_VIRTUAL_MODULE_ID) { - return RESOLVED_SERVER_VIRTUAL_MODULE_ID; - } - if (id === INTERNAL_VIRTUAL_MODULE_ID) { - return RESOLVED_INTERNAL_VIRTUAL_MODULE_ID; - } + resolveId: { + filter: { + id: new RegExp( + `^(${CLIENT_VIRTUAL_MODULE_ID}|${SERVER_VIRTUAL_MODULE_ID}|${INTERNAL_VIRTUAL_MODULE_ID})$`, + ), + }, + handler(id) { + if (id === CLIENT_VIRTUAL_MODULE_ID) { + return RESOLVED_CLIENT_VIRTUAL_MODULE_ID; + } + if (id === SERVER_VIRTUAL_MODULE_ID) { + return RESOLVED_SERVER_VIRTUAL_MODULE_ID; + } + if (id === INTERNAL_VIRTUAL_MODULE_ID) { + return RESOLVED_INTERNAL_VIRTUAL_MODULE_ID; + } + }, }, - load(id, options) { - if (id === RESOLVED_INTERNAL_VIRTUAL_MODULE_ID) { - return { code: `export const schema = ${JSON.stringify(schema)};` }; - } - - if (id === RESOLVED_SERVER_VIRTUAL_MODULE_ID && !options?.ssr) { - throw new AstroError({ - ...AstroErrorData.ServerOnlyModule, - message: AstroErrorData.ServerOnlyModule.message(SERVER_VIRTUAL_MODULE_ID), - }); - } + load: { + filter: { + id: new RegExp( + `^(${RESOLVED_CLIENT_VIRTUAL_MODULE_ID}|${RESOLVED_SERVER_VIRTUAL_MODULE_ID}|${RESOLVED_INTERNAL_VIRTUAL_MODULE_ID})$`, + ), + }, + handler(id, options) { + if (id === RESOLVED_INTERNAL_VIRTUAL_MODULE_ID) { + return { code: `export const schema = ${JSON.stringify(schema)};` }; + } - if (id === RESOLVED_CLIENT_VIRTUAL_MODULE_ID || id === RESOLVED_SERVER_VIRTUAL_MODULE_ID) { - const loadedEnv = envLoader.get(); - - const validatedVariables = validatePublicVariables({ - schema, - loadedEnv, - validateSecrets, - sync, - }); - const { client, server } = getTemplates({ - schema, - validatedVariables, - // In dev, we inline process.env to avoid freezing it - loadedEnv: isBuild ? null : loadedEnv, - }); - - if (id === RESOLVED_CLIENT_VIRTUAL_MODULE_ID) { - return { code: client }; + if (id === RESOLVED_SERVER_VIRTUAL_MODULE_ID && !options?.ssr) { + throw new AstroError({ + ...AstroErrorData.ServerOnlyModule, + message: AstroErrorData.ServerOnlyModule.message(SERVER_VIRTUAL_MODULE_ID), + }); } - if (id === RESOLVED_SERVER_VIRTUAL_MODULE_ID) { - return { code: server }; + if (id === RESOLVED_CLIENT_VIRTUAL_MODULE_ID || id === RESOLVED_SERVER_VIRTUAL_MODULE_ID) { + const loadedEnv = envLoader.get(); + + const validatedVariables = validatePublicVariables({ + schema, + loadedEnv, + validateSecrets, + sync, + }); + const { client, server } = getTemplates({ + schema, + validatedVariables, + // In dev, we inline process.env to avoid freezing it + loadedEnv: isBuild ? null : loadedEnv, + }); + + if (id === RESOLVED_CLIENT_VIRTUAL_MODULE_ID) { + return { code: client }; + } + + if (id === RESOLVED_SERVER_VIRTUAL_MODULE_ID) { + return { code: server }; + } } - } + }, }, }; } diff --git a/packages/astro/src/i18n/vite-plugin-i18n.ts b/packages/astro/src/i18n/vite-plugin-i18n.ts index 791ed9d75187..5df1a740269d 100644 --- a/packages/astro/src/i18n/vite-plugin-i18n.ts +++ b/packages/astro/src/i18n/vite-plugin-i18n.ts @@ -3,7 +3,7 @@ import { AstroError } from '../core/errors/errors.js'; import { AstroErrorData } from '../core/errors/index.js'; import type { AstroSettings } from '../types/astro.js'; -const virtualModuleId = 'astro:i18n'; +const VIRTUAL_MODULE_ID = 'astro:i18n'; type AstroInternationalization = { settings: AstroSettings; @@ -14,13 +14,16 @@ export default function astroInternationalization({ }: AstroInternationalization): vite.Plugin { const { i18n } = settings.config; return { - name: 'astro:i18n', + name: VIRTUAL_MODULE_ID, enforce: 'pre', - resolveId(id) { - if (id === virtualModuleId) { + resolveId: { + filter: { + id: new RegExp(`^${VIRTUAL_MODULE_ID}$`), + }, + handler() { if (i18n === undefined) throw new AstroError(AstroErrorData.i18nNotEnabled); return this.resolve('astro/virtual-modules/i18n.js'); - } + }, }, }; } From 5a5acbb9f2404fbd8530c3fd58f8734eb4fc87aa Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 15:14:37 +0100 Subject: [PATCH 04/30] wip --- packages/astro/src/manifest/serialized.ts | 20 ++++--- packages/astro/src/manifest/virtual-module.ts | 55 +++++++++++-------- .../src/prefetch/vite-plugin-prefetch.ts | 24 +++++--- .../src/toolbar/vite-plugin-dev-toolbar.ts | 24 +++++--- 4 files changed, 75 insertions(+), 48 deletions(-) diff --git a/packages/astro/src/manifest/serialized.ts b/packages/astro/src/manifest/serialized.ts index f1455aa5254e..15a533e9e2c2 100644 --- a/packages/astro/src/manifest/serialized.ts +++ b/packages/astro/src/manifest/serialized.ts @@ -29,7 +29,7 @@ export const SERIALIZED_MANIFEST_RESOLVED_ID = '\0' + SERIALIZED_MANIFEST_ID; export function serializedManifestPlugin({ settings, command, - sync + sync, }: { settings: AstroSettings; command: 'dev' | 'build'; @@ -39,14 +39,20 @@ export function serializedManifestPlugin({ name: SERIALIZED_MANIFEST_ID, enforce: 'pre', - resolveId(id) { - if (id === SERIALIZED_MANIFEST_ID) { + resolveId: { + filter: { + id: new RegExp(`^${SERIALIZED_MANIFEST_ID}$`), + }, + handler() { return SERIALIZED_MANIFEST_RESOLVED_ID; - } + }, }, - async load(id) { - if (id === SERIALIZED_MANIFEST_RESOLVED_ID) { + load: { + filter: { + id: new RegExp(`^${SERIALIZED_MANIFEST_RESOLVED_ID}$`), + }, + async handler() { let manifestData: string; if (command === 'build' && !sync) { // Emit placeholder token that will be replaced by plugin-manifest.ts in build:post @@ -82,7 +88,7 @@ export function serializedManifestPlugin({ export { manifest }; `; return { code }; - } + }, }, }; } diff --git a/packages/astro/src/manifest/virtual-module.ts b/packages/astro/src/manifest/virtual-module.ts index cb6145ca7c20..f0c488425d13 100644 --- a/packages/astro/src/manifest/virtual-module.ts +++ b/packages/astro/src/manifest/virtual-module.ts @@ -11,18 +11,27 @@ const RESOLVED_VIRTUAL_CLIENT_ID = '\0' + VIRTUAL_CLIENT_ID; export default function virtualModulePlugin(): Plugin { return { name: 'astro-manifest-plugin', - resolveId(id) { - // Resolve the virtual module - if (VIRTUAL_SERVER_ID === id) { - return RESOLVED_VIRTUAL_SERVER_ID; - } else if (VIRTUAL_CLIENT_ID === id) { - return RESOLVED_VIRTUAL_CLIENT_ID; - } + resolveId: { + filter: { + id: new RegExp(`^(${VIRTUAL_SERVER_ID}|${VIRTUAL_CLIENT_ID})$`), + }, + handler(id) { + if (id === VIRTUAL_SERVER_ID) { + return RESOLVED_VIRTUAL_SERVER_ID; + } + if (id === VIRTUAL_CLIENT_ID) { + return RESOLVED_VIRTUAL_CLIENT_ID; + } + }, }, - async load(id) { - if (id === RESOLVED_VIRTUAL_CLIENT_ID) { - // There's nothing wrong about using `/client` on the server - const code = ` + load: { + filter: { + id: new RegExp(`^(${RESOLVED_VIRTUAL_SERVER_ID}|${RESOLVED_VIRTUAL_CLIENT_ID})$`), + }, + handler(id) { + if (id === RESOLVED_VIRTUAL_CLIENT_ID) { + // There's nothing wrong about using `/client` on the server + const code = ` import { manifest } from '${SERIALIZED_MANIFEST_ID}' import { fromRoutingStrategy } from 'astro/app'; @@ -46,17 +55,16 @@ const build = { export { base, i18n, trailingSlash, site, compressHTML, build }; `; - return { code }; - } - // server - else if (id == RESOLVED_VIRTUAL_SERVER_ID) { - if (this.environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.client) { - throw new AstroError({ - ...AstroErrorData.ServerOnlyModule, - message: AstroErrorData.ServerOnlyModule.message(VIRTUAL_SERVER_ID), - }); + return { code }; } - const code = ` + if (id == RESOLVED_VIRTUAL_SERVER_ID) { + if (this.environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.client) { + throw new AstroError({ + ...AstroErrorData.ServerOnlyModule, + message: AstroErrorData.ServerOnlyModule.message(VIRTUAL_SERVER_ID), + }); + } + const code = ` import { manifest } from '${SERIALIZED_MANIFEST_ID}' import { fromRoutingStrategy } from "astro/app"; @@ -102,8 +110,9 @@ export { }; `; - return { code }; - } + return { code }; + } + }, }, }; } diff --git a/packages/astro/src/prefetch/vite-plugin-prefetch.ts b/packages/astro/src/prefetch/vite-plugin-prefetch.ts index 8ab23503cc61..49c5c884b55c 100644 --- a/packages/astro/src/prefetch/vite-plugin-prefetch.ts +++ b/packages/astro/src/prefetch/vite-plugin-prefetch.ts @@ -1,8 +1,8 @@ import type * as vite from 'vite'; import type { AstroSettings } from '../types/astro.js'; -const virtualModuleId = 'astro:prefetch'; -const resolvedVirtualModuleId = '\0' + virtualModuleId; +const VIRTUAL_MODULE_ID = 'astro:prefetch'; +const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID; const prefetchInternalModuleFsSubpath = 'astro/dist/prefetch/index.js'; const prefetchCode = `import { init } from 'astro/virtual-modules/prefetch.js';init()`; @@ -31,17 +31,23 @@ export default function astroPrefetch({ settings }: { settings: AstroSettings }) return { name: 'astro:prefetch', - async resolveId(id) { - if (id === virtualModuleId) { + resolveId: { + filter: { + id: new RegExp(`^${VIRTUAL_MODULE_ID}$`), + }, + handler() { if (!prefetch) throwPrefetchNotEnabledError(); - return resolvedVirtualModuleId; - } + return RESOLVED_VIRTUAL_MODULE_ID; + }, }, - load(id) { - if (id === resolvedVirtualModuleId) { + load: { + filter: { + id: new RegExp(`^${RESOLVED_VIRTUAL_MODULE_ID}$`), + }, + handler() { if (!prefetch) throwPrefetchNotEnabledError(); return { code: `export { prefetch } from "astro/virtual-modules/prefetch.js";` }; - } + }, }, transform(code, id) { // NOTE: Handle replacing the specifiers even if prefetch is disabled so View Transitions diff --git a/packages/astro/src/toolbar/vite-plugin-dev-toolbar.ts b/packages/astro/src/toolbar/vite-plugin-dev-toolbar.ts index 66989a4e3a5e..e0c205d9be5c 100644 --- a/packages/astro/src/toolbar/vite-plugin-dev-toolbar.ts +++ b/packages/astro/src/toolbar/vite-plugin-dev-toolbar.ts @@ -3,8 +3,8 @@ import { telemetry } from '../events/index.js'; import { eventAppToggled } from '../events/toolbar.js'; import type { AstroPluginOptions } from '../types/astro.js'; -const PRIVATE_VIRTUAL_MODULE_ID = 'astro:toolbar:internal'; -const resolvedPrivateVirtualModuleId = '\0' + PRIVATE_VIRTUAL_MODULE_ID; +const VIRTUAL_MODULE_ID = 'astro:toolbar:internal'; +const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID; export default function astroDevToolbar({ settings, logger }: AstroPluginOptions): vite.Plugin { let telemetryTimeout: ReturnType; @@ -19,10 +19,13 @@ export default function astroDevToolbar({ settings, logger }: AstroPluginOptions }, }; }, - resolveId(id) { - if (id === PRIVATE_VIRTUAL_MODULE_ID) { - return resolvedPrivateVirtualModuleId; - } + resolveId: { + filter: { + id: new RegExp(`^${VIRTUAL_MODULE_ID}$`), + }, + handler() { + return RESOLVED_VIRTUAL_MODULE_ID; + }, }, configureServer(server) { server.hot.on('astro:devtoolbar:error:load', (args) => { @@ -56,8 +59,11 @@ export default function astroDevToolbar({ settings, logger }: AstroPluginOptions }, 200); }); }, - async load(id) { - if (id === resolvedPrivateVirtualModuleId) { + load: { + filter: { + id: new RegExp(`^${RESOLVED_VIRTUAL_MODULE_ID}$`), + }, + handler() { return { code: ` export const loadDevToolbarApps = async () => { @@ -115,7 +121,7 @@ export default function astroDevToolbar({ settings, logger }: AstroPluginOptions } `, }; - } + }, }, }; } From c37813ae7b9cc75737ea261c21c95f90a48a2ed8 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 15:21:56 +0100 Subject: [PATCH 05/30] wip --- .../transitions/vite-plugin-transitions.ts | 54 +++++++++++-------- .../src/vite-plugin-adapter-config/index.ts | 18 ++++--- packages/astro/src/vite-plugin-app/index.ts | 11 ++-- 3 files changed, 51 insertions(+), 32 deletions(-) diff --git a/packages/astro/src/transitions/vite-plugin-transitions.ts b/packages/astro/src/transitions/vite-plugin-transitions.ts index c2ec27e77ba4..3d38d7683f17 100644 --- a/packages/astro/src/transitions/vite-plugin-transitions.ts +++ b/packages/astro/src/transitions/vite-plugin-transitions.ts @@ -1,10 +1,10 @@ import type * as vite from 'vite'; import type { AstroSettings } from '../types/astro.js'; -const virtualModuleId = 'astro:transitions'; -const resolvedVirtualModuleId = '\0' + virtualModuleId; -const virtualClientModuleId = 'astro:transitions/client'; -const resolvedVirtualClientModuleId = '\0' + virtualClientModuleId; +const VIRTUAL_MODULE_ID = 'astro:transitions'; +const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID; +const VIRTUAL_CLIENT_MODULE_ID = 'astro:transitions/client'; +const RESOLVED_VIRTUAL_CLIENT_MODULE_ID = '\0' + VIRTUAL_CLIENT_MODULE_ID; // The virtual module for the astro:transitions namespace export default function astroTransitions({ settings }: { settings: AstroSettings }): vite.Plugin { @@ -17,26 +17,35 @@ export default function astroTransitions({ settings }: { settings: AstroSettings }, }; }, - async resolveId(id) { - if (id === virtualModuleId) { - return resolvedVirtualModuleId; - } - if (id === virtualClientModuleId) { - return resolvedVirtualClientModuleId; - } + resolveId: { + filter: { + id: new RegExp(`^(${VIRTUAL_MODULE_ID}|${VIRTUAL_CLIENT_MODULE_ID})$`), + }, + handler(id) { + if (id === VIRTUAL_MODULE_ID) { + return RESOLVED_VIRTUAL_MODULE_ID; + } + if (id === VIRTUAL_CLIENT_MODULE_ID) { + return RESOLVED_VIRTUAL_CLIENT_MODULE_ID; + } + }, }, - load(id) { - if (id === resolvedVirtualModuleId) { - return { - code: ` + load: { + filter: { + id: new RegExp(`^(${RESOLVED_VIRTUAL_MODULE_ID}|${RESOLVED_VIRTUAL_CLIENT_MODULE_ID})$`), + }, + handler(id) { + if (id === RESOLVED_VIRTUAL_MODULE_ID) { + return { + code: ` export * from "astro/virtual-modules/transitions.js"; export { default as ClientRouter } from "astro/components/ClientRouter.astro"; `, - }; - } - if (id === resolvedVirtualClientModuleId) { - return { - code: ` + }; + } + if (id === RESOLVED_VIRTUAL_CLIENT_MODULE_ID) { + return { + code: ` export { navigate, supportsViewTransitions, transitionEnabledOnThisPage } from "astro/virtual-modules/transitions-router.js"; export * from "astro/virtual-modules/transitions-types.js"; export { @@ -47,8 +56,9 @@ export default function astroTransitions({ settings }: { settings: AstroSettings } from "astro/virtual-modules/transitions-events.js"; export { swapFunctions } from "astro/virtual-modules/transitions-swap-functions.js"; `, - }; - } + }; + } + }, }, transform(code, id) { if (id.includes('ClientRouter.astro') && id.endsWith('.ts')) { diff --git a/packages/astro/src/vite-plugin-adapter-config/index.ts b/packages/astro/src/vite-plugin-adapter-config/index.ts index 41bad297f962..b9fa45dd49cb 100644 --- a/packages/astro/src/vite-plugin-adapter-config/index.ts +++ b/packages/astro/src/vite-plugin-adapter-config/index.ts @@ -7,13 +7,19 @@ const RESOLVED_VIRTUAL_CLIENT_ID = '\0' + VIRTUAL_CLIENT_ID; export function vitePluginAdapterConfig(settings: AstroSettings): VitePlugin { return { name: 'astro:adapter-config', - resolveId(id) { - if (id === VIRTUAL_CLIENT_ID) { + resolveId: { + filter: { + id: new RegExp(`^${VIRTUAL_CLIENT_ID}$`), + }, + handler() { return RESOLVED_VIRTUAL_CLIENT_ID; - } + }, }, - load(id, options) { - if (id === RESOLVED_VIRTUAL_CLIENT_ID) { + load: { + filter: { + id: new RegExp(`^${RESOLVED_VIRTUAL_CLIENT_ID}$`), + }, + handler(_id, options) { // During SSR, return empty headers to avoid any runtime issues if (options?.ssr) { return { @@ -35,7 +41,7 @@ export function vitePluginAdapterConfig(settings: AstroSettings): VitePlugin { return { code: `export const internalFetchHeaders = ${JSON.stringify(internalFetchHeaders)};`, }; - } + }, }, }; } diff --git a/packages/astro/src/vite-plugin-app/index.ts b/packages/astro/src/vite-plugin-app/index.ts index 5c1a4bb2e169..a635da81dfb1 100644 --- a/packages/astro/src/vite-plugin-app/index.ts +++ b/packages/astro/src/vite-plugin-app/index.ts @@ -6,11 +6,14 @@ export function vitePluginApp(): vite.Plugin { return { name: 'astro:app', - async resolveId(id) { - if (id === ASTRO_DEV_APP_ID) { + resolveId: { + filter: { + id: new RegExp(`^${ASTRO_DEV_APP_ID}$`), + }, + handler() { const url = new URL('./createAstroServerApp.js', import.meta.url); - return await this.resolve(url.toString()); - } + return this.resolve(url.toString()); + }, }, }; } From 3901a73697b30830a34f21f223babd6d3b0a4907 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 15:32:21 +0100 Subject: [PATCH 06/30] wip --- packages/astro/src/vite-plugin-astro/index.ts | 15 +- .../src/vite-plugin-config-alias/index.ts | 48 ++-- packages/astro/src/vite-plugin-css/index.ts | 223 ++++++++++-------- 3 files changed, 157 insertions(+), 129 deletions(-) diff --git a/packages/astro/src/vite-plugin-astro/index.ts b/packages/astro/src/vite-plugin-astro/index.ts index 0e4a089b5906..971aba373e0d 100644 --- a/packages/astro/src/vite-plugin-astro/index.ts +++ b/packages/astro/src/vite-plugin-astro/index.ts @@ -280,13 +280,14 @@ export default function astro({ settings, logger }: AstroPluginOptions): vite.Pl const normalPlugin: vite.Plugin = { name: 'astro:build:normal', - resolveId(id) { - // If Vite resolver can't resolve the Astro request, it's likely a virtual Astro file, fallback here instead - const parsedId = parseAstroRequest(id); - if (parsedId.query.astro) { - return id; - } - }, + // TODO: check if this is useful. This just passes the id through AFAIK + // resolveId(id) { + // // If Vite resolver can't resolve the Astro request, it's likely a virtual Astro file, fallback here instead + // const parsedId = parseAstroRequest(id); + // if (parsedId.query.astro) { + // return id; + // } + // }, }; return [prePlugin, normalPlugin]; diff --git a/packages/astro/src/vite-plugin-config-alias/index.ts b/packages/astro/src/vite-plugin-config-alias/index.ts index 1ebd279b2bf0..13186909f24f 100644 --- a/packages/astro/src/vite-plugin-config-alias/index.ts +++ b/packages/astro/src/vite-plugin-config-alias/index.ts @@ -136,26 +136,34 @@ export default function configAliasVitePlugin({ configResolved(config) { patchCreateResolver(config, plugin); }, - async resolveId(id, importer, options) { - if (isVirtualId(id)) return; - - // 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; + resolveId: { + filter: { + // Everything but ids that start with virtual: or astro: OR contains \0 + // TODO: check it works for null bytes + id: /^(?!virtual:|astro:)[^\0]*$/, + }, + 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; } - } + }, }, }; @@ -208,7 +216,3 @@ function patchCreateResolver(config: ResolvedConfig, postPlugin: VitePlugin) { }; }; } - -function isVirtualId(id: string) { - return id.includes('\0') || id.startsWith('virtual:') || id.startsWith('astro:'); -} diff --git a/packages/astro/src/vite-plugin-css/index.ts b/packages/astro/src/vite-plugin-css/index.ts index f1e8f7730051..fa723784ada0 100644 --- a/packages/astro/src/vite-plugin-css/index.ts +++ b/packages/astro/src/vite-plugin-css/index.ts @@ -56,7 +56,7 @@ function* collectCSSWithOrder( return; } // ?raw imports the underlying css but is handled as a string in the JS. - else if(id.endsWith('?raw')) { + else if (id.endsWith('?raw')) { return; } @@ -83,110 +83,133 @@ export function astroDevCssPlugin({ routesList, command }: AstroVitePluginOption // Cache CSS content by module ID to avoid re-reading const cssContentCache = new Map(); - return [{ - name: MODULE_DEV_CSS, - - async configureServer(server) { - environment = server.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr] as RunnableDevEnvironment; - }, - - resolveId(id) { - if (id === MODULE_DEV_CSS) { - return RESOLVED_MODULE_DEV_CSS; - } - if (id.startsWith(MODULE_DEV_CSS_PREFIX)) { - return RESOLVED_MODULE_DEV_CSS_PREFIX + id.slice(MODULE_DEV_CSS_PREFIX.length); - } - }, - - async load(id) { - if (id === RESOLVED_MODULE_DEV_CSS) { - return { - code: `export const css = new Set()`, - }; - } - if (id.startsWith(RESOLVED_MODULE_DEV_CSS_PREFIX)) { - const componentPath = getComponentFromVirtualModuleCssName( - RESOLVED_MODULE_DEV_CSS_PREFIX, - id, - ); - - // Collect CSS by walking the dependency tree from the component - const cssWithOrder = new Map(); - - // The virtual module name for this page, like virtual:astro:dev-css:index@_@astro - const componentPageId = getVirtualModulePageNameForComponent(componentPath); - - // Ensure the page module is loaded. This will populate the graph and allow us to walk through. - await environment?.runner?.import(componentPageId); - const resolved = await environment?.pluginContainer.resolveId(componentPageId); - - if(!resolved?.id) { - return { - code: 'export const css = new Set()' - }; - } - - // the vite.EnvironmentModuleNode has all of the info we need - const mod = environment?.moduleGraph.getModuleById(resolved.id); + return [ + { + name: MODULE_DEV_CSS, + + async configureServer(server) { + environment = server.environments[ + ASTRO_VITE_ENVIRONMENT_NAMES.ssr + ] as RunnableDevEnvironment; + }, + + resolveId: { + filter: { + id: new RegExp(`^(${MODULE_DEV_CSS}|${MODULE_DEV_CSS_PREFIX}.*)$`), + }, + handler(id) { + if (id === MODULE_DEV_CSS) { + return RESOLVED_MODULE_DEV_CSS; + } + return RESOLVED_MODULE_DEV_CSS_PREFIX + id.slice(MODULE_DEV_CSS_PREFIX.length); + }, + }, + + load: { + filter: { + id: new RegExp(`^(${RESOLVED_MODULE_DEV_CSS}|${RESOLVED_MODULE_DEV_CSS_PREFIX}.*)$`), + }, + async handler(id) { + if (id === RESOLVED_MODULE_DEV_CSS) { + return { + code: `export const css = new Set()`, + }; + } + if (id.startsWith(RESOLVED_MODULE_DEV_CSS_PREFIX)) { + const componentPath = getComponentFromVirtualModuleCssName( + RESOLVED_MODULE_DEV_CSS_PREFIX, + id, + ); + + // Collect CSS by walking the dependency tree from the component + const cssWithOrder = new Map(); + + // The virtual module name for this page, like virtual:astro:dev-css:index@_@astro + const componentPageId = getVirtualModulePageNameForComponent(componentPath); + + // Ensure the page module is loaded. This will populate the graph and allow us to walk through. + await environment?.runner?.import(componentPageId); + const resolved = await environment?.pluginContainer.resolveId(componentPageId); + + if (!resolved?.id) { + return { + code: 'export const css = new Set()', + }; + } + + // the vite.EnvironmentModuleNode has all of the info we need + const mod = environment?.moduleGraph.getModuleById(resolved.id); + + if (!mod) { + return { + code: 'export const css = new Set()', + }; + } + + // Walk through the graph depth-first + for (const collected of collectCSSWithOrder(componentPageId, mod!)) { + // Use the CSS file ID as the key to deduplicate while keeping best ordering + if (!cssWithOrder.has(collected.idKey)) { + // Look up actual content from cache if available + const content = cssContentCache.get(collected.id) || collected.content; + cssWithOrder.set(collected.idKey, { ...collected, content }); + } + } + + const cssArray = Array.from(cssWithOrder.values()); + // Remove the temporary fields added during collection + const cleanedCss = cssArray.map(({ content, id: cssId, url }) => ({ + content, + id: cssId, + url, + })); + return { + code: `export const css = new Set(${JSON.stringify(cleanedCss)})`, + }; + } + }, + }, - if(!mod) { - return { - code: 'export const css = new Set()' - }; + async transform(code, id) { + if (command === 'build') { + return; } - // Walk through the graph depth-first - for (const collected of collectCSSWithOrder(componentPageId, mod!)) { - // Use the CSS file ID as the key to deduplicate while keeping best ordering - if (!cssWithOrder.has(collected.idKey)) { - // Look up actual content from cache if available - const content = cssContentCache.get(collected.id) || collected.content; - cssWithOrder.set(collected.idKey, { ...collected, content }); + // Cache CSS content as we see it + if (isBuildableCSSRequest(id)) { + const mod = environment?.moduleGraph.getModuleById(id); + if (mod) { + cssContentCache.set(id, code); } } - - const cssArray = Array.from(cssWithOrder.values()); - // Remove the temporary fields added during collection - const cleanedCss = cssArray.map(({ content, id: cssId, url }) => ({ content, id: cssId, url })); - return { - code: `export const css = new Set(${JSON.stringify(cleanedCss)})`, - }; - } - }, - - async transform(code, id) { - if (command === 'build') { - return; - } - - // Cache CSS content as we see it - if (isBuildableCSSRequest(id)) { - const mod = environment?.moduleGraph.getModuleById(id); - if (mod) { - cssContentCache.set(id, code); - } - } + }, }, - }, { - name: MODULE_DEV_CSS_ALL, - resolveId(id) { - if(id === MODULE_DEV_CSS_ALL) { - return RESOLVED_MODULE_DEV_CSS_ALL - } + { + name: MODULE_DEV_CSS_ALL, + resolveId: { + filter: { + id: new RegExp(`^${MODULE_DEV_CSS_ALL}$`), + }, + handler() { + return RESOLVED_MODULE_DEV_CSS_ALL; + }, + }, + load: { + filter: { + id: new RegExp(`^${RESOLVED_MODULE_DEV_CSS_ALL}$`), + }, + handler() { + // Creates a map of the component name to a function to import it + let code = `export const devCSSMap = new Map([`; + for (const route of routesList.routes) { + code += `\n\t[${JSON.stringify(route.component)}, () => import(${JSON.stringify(getDevCSSModuleName(route.component))})],`; + } + code += ']);'; + return { + code, + }; + }, + }, }, - load(id) { - if(id === RESOLVED_MODULE_DEV_CSS_ALL) { - // Creates a map of the component name to a function to import it - let code = `export const devCSSMap = new Map([`; - for(const route of routesList.routes) { - code += `\n\t[${JSON.stringify(route.component)}, () => import(${JSON.stringify(getDevCSSModuleName(route.component))})],` - } - code += ']);' - return { - code - }; - } - } - }]; + ]; } From fb6e01d482321150685a273907f2e580ee226bad Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 15:37:05 +0100 Subject: [PATCH 07/30] wip --- .../astro/src/vite-plugin-fileurl/index.ts | 11 +++++++---- .../astro/src/vite-plugin-markdown/index.ts | 18 ++++++++++++------ packages/astro/src/vite-plugin-pages/page.ts | 18 ++++++++++++------ 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/packages/astro/src/vite-plugin-fileurl/index.ts b/packages/astro/src/vite-plugin-fileurl/index.ts index 73132f3af979..000ca8666aa0 100644 --- a/packages/astro/src/vite-plugin-fileurl/index.ts +++ b/packages/astro/src/vite-plugin-fileurl/index.ts @@ -4,11 +4,14 @@ export default function vitePluginFileURL(): VitePlugin { return { name: 'astro:vite-plugin-file-url', enforce: 'pre', - resolveId(source, importer) { - if (source.startsWith('file://')) { - const rest = source.slice(7); + resolveId: { + filter: { + id: /^file:\/\//, + }, + handler(id, importer) { + const rest = id.slice(7); return this.resolve(rest, importer); - } + }, }, }; } diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts index 0e4bb7c2a5ed..9e9945d8c2af 100644 --- a/packages/astro/src/vite-plugin-markdown/index.ts +++ b/packages/astro/src/vite-plugin-markdown/index.ts @@ -39,12 +39,18 @@ export default function markdown({ settings, logger }: AstroPluginOptions): Plug buildEnd() { processor = undefined; }, - async resolveId(source, importer, options) { - if (importer?.endsWith('.md') && source[0] !== '/') { - let resolved = await this.resolve(source, importer, options); - if (!resolved) resolved = await this.resolve('./' + source, importer, options); - return resolved; - } + resolveId: { + filter: { + // Do not match sources that start with / + id: /^[^/]/, + }, + async handler(source, importer, options) { + if (importer?.endsWith('.md')) { + let resolved = await this.resolve(source, importer, options); + if (!resolved) resolved = await this.resolve('./' + source, importer, options); + return resolved; + } + }, }, // Why not the "transform" hook instead of "load" + readFile? // A: Vite transforms all "import.meta.env" references to their values before diff --git a/packages/astro/src/vite-plugin-pages/page.ts b/packages/astro/src/vite-plugin-pages/page.ts index 238b5c538ba1..c538d713f00f 100644 --- a/packages/astro/src/vite-plugin-pages/page.ts +++ b/packages/astro/src/vite-plugin-pages/page.ts @@ -19,13 +19,19 @@ export function pluginPage({ routesList }: PagePluginOptions): VitePlugin { environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender ); }, - resolveId(id) { - if (id.startsWith(VIRTUAL_PAGE_MODULE_ID)) { + resolveId: { + filter: { + id: new RegExp(`^${VIRTUAL_PAGE_MODULE_ID}`), + }, + handler(id) { return VIRTUAL_PAGE_RESOLVED_MODULE_ID + id.slice(VIRTUAL_PAGE_MODULE_ID.length); - } + }, }, - async load(id) { - if (id.startsWith(VIRTUAL_PAGE_RESOLVED_MODULE_ID)) { + load: { + filter: { + id: new RegExp(`^${VIRTUAL_PAGE_RESOLVED_MODULE_ID}`), + }, + handler(id) { const componentPath = getComponentFromVirtualModulePageName( VIRTUAL_PAGE_RESOLVED_MODULE_ID, id, @@ -53,7 +59,7 @@ export function pluginPage({ routesList }: PagePluginOptions): VitePlugin { return { code: `${imports.join('\n')}\n${exports.join('\n')}` }; } - } + }, }, }; } From e87df8d2b8c7a1a509e45ac6eaa97056eff5c355 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 15:40:26 +0100 Subject: [PATCH 08/30] wip --- packages/astro/src/vite-plugin-pages/pages.ts | 18 ++++++++---- .../astro/src/vite-plugin-renderers/index.ts | 21 ++++++++------ .../astro/src/vite-plugin-routes/index.ts | 28 +++++++++++-------- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/packages/astro/src/vite-plugin-pages/pages.ts b/packages/astro/src/vite-plugin-pages/pages.ts index 76588233d485..3e0716a03698 100644 --- a/packages/astro/src/vite-plugin-pages/pages.ts +++ b/packages/astro/src/vite-plugin-pages/pages.ts @@ -23,13 +23,19 @@ export function pluginPages({ routesList }: PagesPluginOptions): VitePlugin { environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender ); }, - resolveId(id) { - if (id === VIRTUAL_PAGES_MODULE_ID) { + resolveId: { + filter: { + id: new RegExp(`^${VIRTUAL_PAGES_MODULE_ID}$`), + }, + handler() { return VIRTUAL_PAGES_RESOLVED_MODULE_ID; - } + }, }, - async load(id) { - if (id === VIRTUAL_PAGES_RESOLVED_MODULE_ID) { + load: { + filter: { + id: new RegExp(`^${VIRTUAL_PAGES_RESOLVED_MODULE_ID}$`), + }, + async handler() { const imports: string[] = []; const pageMap: string[] = []; let i = 0; @@ -60,7 +66,7 @@ export function pluginPages({ routesList }: PagesPluginOptions): VitePlugin { const pageMapCode = `const pageMap = new Map([\n ${pageMap.join(',\n ')}\n]);\n\nexport { pageMap };`; return { code: [...imports, pageMapCode].join('\n') }; - } + }, }, }; } diff --git a/packages/astro/src/vite-plugin-renderers/index.ts b/packages/astro/src/vite-plugin-renderers/index.ts index 53bae8759b3a..f318e96f555d 100644 --- a/packages/astro/src/vite-plugin-renderers/index.ts +++ b/packages/astro/src/vite-plugin-renderers/index.ts @@ -15,14 +15,20 @@ export default function vitePluginRenderers(options: PluginOptions): VitePlugin name: 'astro:plugin-renderers', enforce: 'pre', - resolveId(id) { - if (id === ASTRO_RENDERERS_MODULE_ID) { + resolveId: { + filter: { + id: new RegExp(`^${ASTRO_RENDERERS_MODULE_ID}$`), + }, + handler() { return RESOLVED_ASTRO_RENDERERS_MODULE_ID; - } + }, }, - async load(id) { - if (id === RESOLVED_ASTRO_RENDERERS_MODULE_ID) { + load: { + filter: { + id: new RegExp(`^${RESOLVED_ASTRO_RENDERERS_MODULE_ID}$`), + }, + handler() { if (renderers.length > 0) { const imports: string[] = []; const exports: string[] = []; @@ -39,10 +45,9 @@ export default function vitePluginRenderers(options: PluginOptions): VitePlugin exports.push(`export const renderers = [${rendererItems}];`); return { code: `${imports.join('\n')}\n${exports.join('\n')}` }; - } else { - return { code: `export const renderers = [];` }; } - } + return { code: `export const renderers = [];` }; + }, }, }; } diff --git a/packages/astro/src/vite-plugin-routes/index.ts b/packages/astro/src/vite-plugin-routes/index.ts index 7f8287e58feb..380757ea1f30 100644 --- a/packages/astro/src/vite-plugin-routes/index.ts +++ b/packages/astro/src/vite-plugin-routes/index.ts @@ -98,8 +98,20 @@ export default async function astroPluginRoutes({ ); }, - load(id) { - if (id === ASTRO_ROUTES_MODULE_ID_RESOLVED) { + resolveId: { + filter: { + id: new RegExp(`^${ASTRO_ROUTES_MODULE_ID}$`), + }, + handler() { + return ASTRO_ROUTES_MODULE_ID_RESOLVED; + }, + }, + + load: { + filter: { + id: new RegExp(`^${ASTRO_ROUTES_MODULE_ID_RESOLVED}$`), + }, + handler() { const environmentName = this.environment.name; const filteredRoutes = serializedRouteInfo.filter((routeInfo) => { // In prerender, filter to only the routes that need prerendering. @@ -118,16 +130,8 @@ export default async function astroPluginRoutes({ export { routes }; `; - return { - code, - }; - } - }, - - resolveId(id) { - if (id === ASTRO_ROUTES_MODULE_ID) { - return ASTRO_ROUTES_MODULE_ID_RESOLVED; - } + return { code }; + }, }, async transform(this, code, id, options) { From 93c8cb23963b950b14635e9514105a1654bf75cb Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 15:45:16 +0100 Subject: [PATCH 09/30] wip --- .../astro/src/vite-plugin-scripts/index.ts | 69 +++++++------- .../core/integration/vite-plugin-db-client.ts | 35 ++++--- .../db/src/core/integration/vite-plugin-db.ts | 93 ++++++++++--------- 3 files changed, 108 insertions(+), 89 deletions(-) diff --git a/packages/astro/src/vite-plugin-scripts/index.ts b/packages/astro/src/vite-plugin-scripts/index.ts index 70082f8c0ec6..8339dbfae81c 100644 --- a/packages/astro/src/vite-plugin-scripts/index.ts +++ b/packages/astro/src/vite-plugin-scripts/index.ts @@ -17,39 +17,44 @@ export default function astroScriptsPlugin({ settings }: { settings: AstroSettin return { name: 'astro:scripts', - async resolveId(id) { - if (id.startsWith(SCRIPT_ID_PREFIX)) { - return id; - } - return undefined; - }, + // TODO: check if actually useful + // async resolveId(id) { + // if (id.startsWith(SCRIPT_ID_PREFIX)) { + // return id; + // } + // return undefined; + // }, - async load(id) { - if (id === BEFORE_HYDRATION_SCRIPT_ID) { - return { - code: settings.scripts - .filter((s) => s.stage === 'before-hydration') - .map((s) => s.content) - .join('\n'), - }; - } - if (id === PAGE_SCRIPT_ID) { - return { - code: settings.scripts - .filter((s) => s.stage === 'page') - .map((s) => s.content) - .join('\n'), - }; - } - if (id === PAGE_SSR_SCRIPT_ID) { - return { - code: settings.scripts - .filter((s) => s.stage === 'page-ssr') - .map((s) => s.content) - .join('\n'), - }; - } - return null; + load: { + filter: { + id: new RegExp(`^(${BEFORE_HYDRATION_SCRIPT_ID}|${PAGE_SCRIPT_ID}|${PAGE_SSR_SCRIPT_ID})$`), + }, + handler(id) { + if (id === BEFORE_HYDRATION_SCRIPT_ID) { + return { + code: settings.scripts + .filter((s) => s.stage === 'before-hydration') + .map((s) => s.content) + .join('\n'), + }; + } + if (id === PAGE_SCRIPT_ID) { + return { + code: settings.scripts + .filter((s) => s.stage === 'page') + .map((s) => s.content) + .join('\n'), + }; + } + if (id === PAGE_SSR_SCRIPT_ID) { + return { + code: settings.scripts + .filter((s) => s.stage === 'page-ssr') + .map((s) => s.content) + .join('\n'), + }; + } + }, }, buildStart() { const hasHydrationScripts = settings.scripts.some((s) => s.stage === 'before-hydration'); diff --git a/packages/db/src/core/integration/vite-plugin-db-client.ts b/packages/db/src/core/integration/vite-plugin-db-client.ts index 25138653a5d5..928346fd3f44 100644 --- a/packages/db/src/core/integration/vite-plugin-db-client.ts +++ b/packages/db/src/core/integration/vite-plugin-db-client.ts @@ -31,21 +31,28 @@ export function vitePluginDbClient(params: VitePluginDBClientParams): VitePlugin return { name: 'virtual:astro:db-client', enforce: 'pre', - async resolveId(id) { - if (id !== VIRTUAL_CLIENT_MODULE_ID) return; - return resolved; + resolveId: { + filter: { + id: new RegExp(`^${VIRTUAL_CLIENT_MODULE_ID}$`), + }, + handler() { + return resolved; + }, }, - async load(id) { - if (id !== resolved) return; - - switch (params.connectToRemote) { - case true: - return getRemoteClientModule(params.mode); - case false: - default: - // Local client is always available, even if not used. - return getLocalClientModule(params.mode); - } + load: { + filter: { + id: new RegExp(`^${resolved}$`), + }, + handler() { + switch (params.connectToRemote) { + case true: + return getRemoteClientModule(params.mode); + case false: + default: + // Local client is always available, even if not used. + return getLocalClientModule(params.mode); + } + }, }, }; } diff --git a/packages/db/src/core/integration/vite-plugin-db.ts b/packages/db/src/core/integration/vite-plugin-db.ts index 1e6bc96088b0..03c2d0ee4f55 100644 --- a/packages/db/src/core/integration/vite-plugin-db.ts +++ b/packages/db/src/core/integration/vite-plugin-db.ts @@ -68,57 +68,64 @@ export function vitePluginDb(params: VitePluginDBParams): VitePlugin { configResolved(resolvedConfig) { command = resolvedConfig.command; }, - async resolveId(id) { - if (id !== VIRTUAL_MODULE_ID) return; - if (params.seedHandler.inProgress) { - return resolved.importedFromSeedFile; - } - return resolved.module; + resolveId: { + filter: { + id: new RegExp(`^${VIRTUAL_MODULE_ID}$`), + }, + handler() { + if (params.seedHandler.inProgress) { + return resolved.importedFromSeedFile; + } + return resolved.module; + }, }, - async load(id) { - if (id !== resolved.module && id !== resolved.importedFromSeedFile) return; + load: { + filter: { + id: new RegExp(`^(${resolved.module}|${resolved.importedFromSeedFile})$`), + }, + async handler(id) { + if (params.connectToRemote) { + return getRemoteVirtualModContents({ + appToken: params.appToken, + tables: params.tables.get(), + isBuild: command === 'build', + output: params.output, + localExecution: false, + }); + } - if (params.connectToRemote) { - return getRemoteVirtualModContents({ - appToken: params.appToken, - tables: params.tables.get(), - isBuild: command === 'build', - output: params.output, - localExecution: false, - }); - } + // When seeding, we resolved to a different virtual module. + // this prevents an infinite loop attempting to rerun seed files. + // Short circuit with the module contents in this case. + if (id === resolved.importedFromSeedFile) { + return getLocalVirtualModContents({ + root: params.root, + tables: params.tables.get(), + localExecution: false, + }); + } - // When seeding, we resolved to a different virtual module. - // this prevents an infinite loop attempting to rerun seed files. - // Short circuit with the module contents in this case. - if (id === resolved.importedFromSeedFile) { + await recreateTables(params); + const seedFiles = getResolvedSeedFiles(params); + for await (const seedFile of seedFiles) { + // Use `addWatchFile()` to invalidate the `astro:db` module + // when a seed file changes. + this.addWatchFile(fileURLToPath(seedFile)); + if (existsSync(seedFile)) { + params.seedHandler.inProgress = true; + await params.seedHandler.execute(seedFile); + } + } + if (params.seedHandler.inProgress) { + (params.logger ?? console).info('Seeded database.'); + params.seedHandler.inProgress = false; + } return getLocalVirtualModContents({ root: params.root, tables: params.tables.get(), localExecution: false, }); - } - - await recreateTables(params); - const seedFiles = getResolvedSeedFiles(params); - for await (const seedFile of seedFiles) { - // Use `addWatchFile()` to invalidate the `astro:db` module - // when a seed file changes. - this.addWatchFile(fileURLToPath(seedFile)); - if (existsSync(seedFile)) { - params.seedHandler.inProgress = true; - await params.seedHandler.execute(seedFile); - } - } - if (params.seedHandler.inProgress) { - (params.logger ?? console).info('Seeded database.'); - params.seedHandler.inProgress = false; - } - return getLocalVirtualModContents({ - root: params.root, - tables: params.tables.get(), - localExecution: false, - }); + }, }, }; } From a3da79c48f474974f54890cbd0d52c6cd77fae51 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 15:51:44 +0100 Subject: [PATCH 10/30] wip --- .../src/plugins/virtual.ts | 32 +++++---- .../astro/test/astro-get-static-paths.test.js | 38 ++++++----- .../extension-matching/astro.config.mjs | 68 ++++++++++--------- .../src/plugin/my-plugin.mjs | 30 ++++---- 4 files changed, 94 insertions(+), 74 deletions(-) diff --git a/packages/astro/e2e/fixtures/vite-virtual-modules/src/plugins/virtual.ts b/packages/astro/e2e/fixtures/vite-virtual-modules/src/plugins/virtual.ts index c0b359bcb334..a3217eba1d1b 100644 --- a/packages/astro/e2e/fixtures/vite-virtual-modules/src/plugins/virtual.ts +++ b/packages/astro/e2e/fixtures/vite-virtual-modules/src/plugins/virtual.ts @@ -1,18 +1,24 @@ -import type { Plugin } from "vite"; +import type { Plugin } from 'vite'; -const VIRTUAL_MODULE_ID = "virtual:dynamic.css"; +const VIRTUAL_MODULE_ID = 'virtual:dynamic.css'; const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`; export default { - name: VIRTUAL_MODULE_ID, - resolveId(source) { - if (!source.startsWith(VIRTUAL_MODULE_ID)) return; - - return RESOLVED_VIRTUAL_MODULE_ID; - }, - load(id) { - if (!id.startsWith(RESOLVED_VIRTUAL_MODULE_ID)) return; - - return "body { background: red; }"; - }, + name: VIRTUAL_MODULE_ID, + resolveId: { + filter: { + id: new RegExp(`^${VIRTUAL_MODULE_ID}$`), + }, + handler() { + return RESOLVED_VIRTUAL_MODULE_ID; + }, + }, + load: { + filter: { + id: new RegExp(`^${RESOLVED_VIRTUAL_MODULE_ID}$`), + }, + handler() { + return 'body { background: red; }'; + }, + }, } satisfies Plugin; diff --git a/packages/astro/test/astro-get-static-paths.test.js b/packages/astro/test/astro-get-static-paths.test.js index c37ccfe861ea..8477d0636be5 100644 --- a/packages/astro/test/astro-get-static-paths.test.js +++ b/packages/astro/test/astro-get-static-paths.test.js @@ -6,18 +6,24 @@ import { loadFixture } from './test-utils.js'; const root = new URL('./fixtures/astro-get-static-paths/', import.meta.url); -const paramsTypePlugin = (type = 'string') => ({ +const paramsTypePlugin = (type = 'string') => ({ name: 'vite-plugin-param-type', - resolveId(name) { - if(name ==='virtual:my:param:type') { + resolveId: { + filter: { + id: /^virtual:my:param:type$/, + }, + handler() { return '\0virtual:my:param:type'; - } + }, }, - load(id) { - if(id === '\0virtual:my:param:type') { + load: { + filter: { + id: /^\0virtual:my:param:type$/, + }, + handler() { return `export const paramType = ${JSON.stringify(type)}`; - } - } + }, + }, }); function resetFlags() { @@ -39,8 +45,8 @@ describe('getStaticPaths - build calls', () => { trailingSlash: 'never', base: '/blog', vite: { - plugins: [paramsTypePlugin()] - } + plugins: [paramsTypePlugin()], + }, }); await fixture.build({}); }); @@ -73,8 +79,8 @@ describe('getStaticPaths - dev calls', () => { root, site: 'https://mysite.dev/', vite: { - plugins: [paramsTypePlugin()] - } + plugins: [paramsTypePlugin()], + }, }); devServer = await fixture.startDevServer(); }); @@ -201,8 +207,8 @@ describe('throws if an invalid Astro property is accessed', () => { root, site: 'https://mysite.dev/', vite: { - plugins: [paramsTypePlugin()] - } + plugins: [paramsTypePlugin()], + }, }); await fixture.editFile( '/src/pages/food/[name].astro', @@ -238,8 +244,8 @@ describe('throws if an invalid params type is returned', () => { root, site: 'https://mysite.dev/', vite: { - plugins: [paramsTypePlugin(type)] - } + plugins: [paramsTypePlugin(type)], + }, }); await fixture.build({}); } catch (err) { diff --git a/packages/astro/test/fixtures/extension-matching/astro.config.mjs b/packages/astro/test/fixtures/extension-matching/astro.config.mjs index 6083ccdcd051..b91bf82eb413 100644 --- a/packages/astro/test/fixtures/extension-matching/astro.config.mjs +++ b/packages/astro/test/fixtures/extension-matching/astro.config.mjs @@ -4,35 +4,41 @@ const MODULE_ID = 'virtual:test'; const RESOLVED_MODULE_ID = '\0virtual:test'; export default defineConfig({ - integrations: [ - { - name: 'astro-test-invalid-transform', - hooks: { - 'astro:config:setup': ({ updateConfig }) => { - updateConfig({ - vite: { - plugins: [ - // ----------------------------------- - { - name: 'vite-test-invalid-transform', - resolveId(id) { - if (id === MODULE_ID) { - // Astro tries to transform this import because the query params can end with '.astro' - return `${RESOLVED_MODULE_ID}?importer=index.astro`; - } - }, - load(id) { - if (id.startsWith(RESOLVED_MODULE_ID)) { - return `export default 'true';`; - } - }, - }, - // ----------------------------------- - ], - }, - }); - }, - }, - }, - ], + integrations: [ + { + name: 'astro-test-invalid-transform', + hooks: { + 'astro:config:setup': ({ updateConfig }) => { + updateConfig({ + vite: { + plugins: [ + // ----------------------------------- + { + name: 'vite-test-invalid-transform', + resolveId: { + filter: { + id: new RegExp(`^${MODULE_ID}$`), + }, + handler() { + // Astro tries to transform this import because the query params can end with '.astro' + return `${RESOLVED_MODULE_ID}?importer=index.astro`; + }, + }, + load: { + filter: { + id: new RegExp(`^${RESOLVED_MODULE_ID}`), + }, + handler() { + return `export default 'true';`; + }, + }, + }, + // ----------------------------------- + ], + }, + }); + }, + }, + }, + ], }); diff --git a/packages/astro/test/fixtures/virtual-astro-file/src/plugin/my-plugin.mjs b/packages/astro/test/fixtures/virtual-astro-file/src/plugin/my-plugin.mjs index e01f6a0fd802..07a5ae54414b 100644 --- a/packages/astro/test/fixtures/virtual-astro-file/src/plugin/my-plugin.mjs +++ b/packages/astro/test/fixtures/virtual-astro-file/src/plugin/my-plugin.mjs @@ -1,16 +1,18 @@ - - export default function myPlugin() { const pluginId = `@my-plugin/virtual.astro`; - return { - enforce: 'pre', - name: 'virtual-astro-plugin', - resolveId(id) { - if (id === pluginId) return id; - }, - load(id) { - if (id === pluginId) { - return `--- + return { + enforce: 'pre', + name: 'virtual-astro-plugin', + // TODO: check if useful + // resolveId(id) { + // if (id === pluginId) return id; + // }, + load: { + filter: { + id: new RegExp(`^${pluginId}$`), + }, + handler() { + return `--- const works = true; ---

This is a virtual module id

@@ -21,7 +23,7 @@ const works = true; } `; - } - }, - }; + }, + }, + }; } From e95e37e59104a6bc5493418ca0827d6a301947ad Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 16:00:19 +0100 Subject: [PATCH 11/30] wip --- .../test/ssr-adapter-build-config.test.js | 26 ++++++++++--------- packages/astro/test/test-adapter.js | 24 ++++++++++------- packages/integrations/alpinejs/src/index.ts | 18 ++++++++----- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/packages/astro/test/ssr-adapter-build-config.test.js b/packages/astro/test/ssr-adapter-build-config.test.js index a08a126a3e7d..77fd70efceb9 100644 --- a/packages/astro/test/ssr-adapter-build-config.test.js +++ b/packages/astro/test/ssr-adapter-build-config.test.js @@ -23,20 +23,22 @@ describe('Integration buildConfig hook', () => { vite: { plugins: [ { - resolveId(id) { - if (id === '@my-ssr') { - return id; - } else if (id === 'astro/app') { - const viteId = viteID( - new URL('../dist/core/app/index.js', import.meta.url), - ); - return viteId; - } + // TODO: check if @my-ssr check is useful + resolveId: { + filter: { + id: /^astro\/app$/, + }, + handler() { + return viteID(new URL('../dist/core/app/index.js', import.meta.url)); + }, }, - load(id) { - if (id === '@my-ssr') { + load: { + filter: { + id: /^@my-ssr$/, + }, + handler() { return `import { App } from 'astro/app';export function createExports(manifest) { return { manifest, createApp: () => new App(manifest) }; }`; - } + }, }, }, ], diff --git a/packages/astro/test/test-adapter.js b/packages/astro/test/test-adapter.js index 9e6915330879..546e090788d1 100644 --- a/packages/astro/test/test-adapter.js +++ b/packages/astro/test/test-adapter.js @@ -34,16 +34,20 @@ export default function ({ vite: { plugins: [ { - resolveId(id) { - if (id === '@my-ssr') { - return id; - } else if (id === 'astro/app') { - const viteId = viteID(new URL('../dist/core/app/index.js', import.meta.url)); - return viteId; - } + // TODO: check if @my-ssr check is useful + resolveId: { + filter: { + id: /^astro\/app$/, + }, + handler() { + return viteID(new URL('../dist/core/app/index.js', import.meta.url)); + }, }, - load(id) { - if (id === '@my-ssr') { + load: { + filter: { + id: /^@my-ssr$/, + }, + handler() { return { code: ` import { App, AppPipeline } from 'astro/app'; @@ -102,7 +106,7 @@ export default function ({ } `, }; - } + }, }, }, ], diff --git a/packages/integrations/alpinejs/src/index.ts b/packages/integrations/alpinejs/src/index.ts index 61a50d705099..92bee60af8a9 100644 --- a/packages/integrations/alpinejs/src/index.ts +++ b/packages/integrations/alpinejs/src/index.ts @@ -54,13 +54,19 @@ function virtualEntrypoint(options?: Options): Plugin { : options.entrypoint; } }, - resolveId(id) { - if (id === virtualModuleId) { + resolveId: { + filter: { + id: new RegExp(`^${virtualModuleId}$`), + }, + handler() { return resolvedVirtualModuleId; - } + }, }, - load(id) { - if (id === resolvedVirtualModuleId) { + load: { + filter: { + id: new RegExp(`^${resolvedVirtualModuleId}$`), + }, + handler() { if (entrypoint) { return `\ import * as mod from ${JSON.stringify(entrypoint)}; @@ -80,7 +86,7 @@ export const setup = (Alpine) => { }`; } return `export const setup = () => {};`; - } + }, }, }; } From 8f72ee4e35ba2718f012bfef0b01d0d5bcb44c91 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 16:52:05 +0100 Subject: [PATCH 12/30] wip --- packages/integrations/cloudflare/src/index.ts | 12 +++++----- .../cloudflare/src/vite-plugin-config.ts | 18 ++++++++++----- packages/integrations/react/src/index.ts | 18 ++++++++++----- packages/integrations/vue/src/index.ts | 22 +++++++++++-------- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/packages/integrations/cloudflare/src/index.ts b/packages/integrations/cloudflare/src/index.ts index fc3bc28e1b5d..8863ddf79a47 100644 --- a/packages/integrations/cloudflare/src/index.ts +++ b/packages/integrations/cloudflare/src/index.ts @@ -216,11 +216,13 @@ export default function createIntegration(args?: Options): AstroIntegration { { name: '@astrojs/cloudflare:cf-imports', enforce: 'pre', - resolveId(source) { - if (source.startsWith('cloudflare:')) { - return { id: source, external: true }; - } - return null; + resolveId: { + filter: { + id: /^cloudflare:/, + }, + handler(id) { + return { id, external: true }; + }, }, }, { diff --git a/packages/integrations/cloudflare/src/vite-plugin-config.ts b/packages/integrations/cloudflare/src/vite-plugin-config.ts index b2d20b99599b..bf4f0afff4c9 100644 --- a/packages/integrations/cloudflare/src/vite-plugin-config.ts +++ b/packages/integrations/cloudflare/src/vite-plugin-config.ts @@ -10,15 +10,21 @@ interface CloudflareConfig { export function createConfigPlugin(config: CloudflareConfig): PluginOption { return { name: 'vite:astro-cloudflare-config', - resolveId(id) { - if (id === VIRTUAL_CONFIG_ID) { + resolveId: { + filter: { + id: new RegExp(`^${VIRTUAL_CONFIG_ID}$`), + }, + handler() { return RESOLVED_VIRTUAL_CONFIG_ID; - } + }, }, - load(id) { - if (id === RESOLVED_VIRTUAL_CONFIG_ID) { + load: { + filter: { + id: new RegExp(`^${RESOLVED_VIRTUAL_CONFIG_ID}$`), + }, + handler() { return `export const sessionKVBindingName = ${JSON.stringify(config.sessionKVBindingName)};`; - } + }, }, }; } diff --git a/packages/integrations/react/src/index.ts b/packages/integrations/react/src/index.ts index f25202899d9b..9e258a153ea3 100644 --- a/packages/integrations/react/src/index.ts +++ b/packages/integrations/react/src/index.ts @@ -41,20 +41,26 @@ function optionsPlugin({ const virtualModuleId = '\0' + virtualModule; return { name: '@astrojs/react:opts', - resolveId(id) { - if (id === virtualModule) { + resolveId: { + filter: { + id: new RegExp(`^${virtualModule}$`), + }, + handler() { return virtualModuleId; - } + }, }, - load(id) { - if (id === virtualModuleId) { + load: { + filter: { + id: new RegExp(`^${virtualModuleId}$`), + }, + handler() { return { code: `export default { experimentalReactChildren: ${JSON.stringify(experimentalReactChildren)}, experimentalDisableStreaming: ${JSON.stringify(experimentalDisableStreaming)} }`, }; - } + }, }, }; } diff --git a/packages/integrations/vue/src/index.ts b/packages/integrations/vue/src/index.ts index bd3cda5867d0..69b5f327d0f2 100644 --- a/packages/integrations/vue/src/index.ts +++ b/packages/integrations/vue/src/index.ts @@ -52,13 +52,19 @@ function virtualAppEntrypoint(options?: Options): Plugin { : options.appEntrypoint; } }, - resolveId(id: string) { - if (id == VIRTUAL_MODULE_ID) { + resolveId: { + filter: { + id: new RegExp(`^${VIRTUAL_MODULE_ID}$`), + }, + handler() { return RESOLVED_VIRTUAL_MODULE_ID; - } + }, }, - load(id: string) { - if (id === RESOLVED_VIRTUAL_MODULE_ID) { + load: { + filter: { + id: new RegExp(`^${RESOLVED_VIRTUAL_MODULE_ID}$`), + }, + handler() { if (appEntrypoint) { return `\ export const setup = async (app) => { @@ -78,7 +84,7 @@ export const setup = async (app) => { }`; } return `export const setup = () => {};`; - } + }, }, // Ensure that Vue components reference appEntrypoint directly // This allows Astro to associate global styles imported in this file @@ -150,9 +156,7 @@ function configEnvironmentPlugin(): Plugin { ((environmentName === 'ssr' || environmentName === 'prerender') && _options.optimizeDeps?.noDiscovery === false) ) { - environmentOptions.optimizeDeps!.include = [ - 'vue' - ]; + environmentOptions.optimizeDeps!.include = ['vue']; environmentOptions.optimizeDeps!.exclude = [ '@astrojs/vue/server.js', 'vue/server-renderer', From db456d0f690ee409c15fc7a87422c1e8a7738e61 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 16:52:39 +0100 Subject: [PATCH 13/30] wip --- .../integrations/mdx/src/vite-plugin-mdx.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/integrations/mdx/src/vite-plugin-mdx.ts b/packages/integrations/mdx/src/vite-plugin-mdx.ts index 910701afcd98..12ac4668c286 100644 --- a/packages/integrations/mdx/src/vite-plugin-mdx.ts +++ b/packages/integrations/mdx/src/vite-plugin-mdx.ts @@ -32,12 +32,18 @@ export function vitePluginMdx(opts: VitePluginMdxOptions): Plugin { resolved.plugins.splice(jsxPluginIndex, 1); } }, - async resolveId(source, importer, options) { - if (importer?.endsWith('.mdx') && source[0] !== '/') { - let resolved = await this.resolve(source, importer, options); - if (!resolved) resolved = await this.resolve('./' + source, importer, options); - return resolved; - } + resolveId: { + filter: { + // Do not match sources that start with / + id: /^[^/]/, + }, + async handler(source, importer, options) { + if (importer?.endsWith('.mdx')) { + let resolved = await this.resolve(source, importer, options); + if (!resolved) resolved = await this.resolve('./' + source, importer, options); + return resolved; + } + }, }, // Override transform to alter code before MDX compilation // ex. inject layouts From c76daa35eccfa603edc8047a173e3a3810c81d60 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 17:02:02 +0100 Subject: [PATCH 14/30] todos --- packages/astro/src/vite-plugin-astro/index.ts | 17 +++++++++-------- .../astro/src/vite-plugin-config-alias/index.ts | 1 - packages/astro/src/vite-plugin-scripts/index.ts | 15 ++++++++------- .../virtual-astro-file/src/plugin/my-plugin.mjs | 12 ++++++++---- .../astro/test/ssr-adapter-build-config.test.js | 8 +++++--- packages/astro/test/test-adapter.js | 6 ++++-- 6 files changed, 34 insertions(+), 25 deletions(-) diff --git a/packages/astro/src/vite-plugin-astro/index.ts b/packages/astro/src/vite-plugin-astro/index.ts index 971aba373e0d..dc45352572b7 100644 --- a/packages/astro/src/vite-plugin-astro/index.ts +++ b/packages/astro/src/vite-plugin-astro/index.ts @@ -280,14 +280,15 @@ export default function astro({ settings, logger }: AstroPluginOptions): vite.Pl const normalPlugin: vite.Plugin = { name: 'astro:build:normal', - // TODO: check if this is useful. This just passes the id through AFAIK - // resolveId(id) { - // // If Vite resolver can't resolve the Astro request, it's likely a virtual Astro file, fallback here instead - // const parsedId = parseAstroRequest(id); - // if (parsedId.query.astro) { - // return id; - // } - // }, + // If Vite resolver can't resolve the Astro request, it's likely a virtual Astro file, fallback here instead + resolveId: { + filter: { + id: /[?&]astro/, + }, + handler(id) { + return id; + }, + }, }; return [prePlugin, normalPlugin]; diff --git a/packages/astro/src/vite-plugin-config-alias/index.ts b/packages/astro/src/vite-plugin-config-alias/index.ts index 13186909f24f..3d8150732f3e 100644 --- a/packages/astro/src/vite-plugin-config-alias/index.ts +++ b/packages/astro/src/vite-plugin-config-alias/index.ts @@ -139,7 +139,6 @@ export default function configAliasVitePlugin({ resolveId: { filter: { // Everything but ids that start with virtual: or astro: OR contains \0 - // TODO: check it works for null bytes id: /^(?!virtual:|astro:)[^\0]*$/, }, async handler(id, importer, options) { diff --git a/packages/astro/src/vite-plugin-scripts/index.ts b/packages/astro/src/vite-plugin-scripts/index.ts index 8339dbfae81c..a2dfe3c69d93 100644 --- a/packages/astro/src/vite-plugin-scripts/index.ts +++ b/packages/astro/src/vite-plugin-scripts/index.ts @@ -17,13 +17,14 @@ export default function astroScriptsPlugin({ settings }: { settings: AstroSettin return { name: 'astro:scripts', - // TODO: check if actually useful - // async resolveId(id) { - // if (id.startsWith(SCRIPT_ID_PREFIX)) { - // return id; - // } - // return undefined; - // }, + resolveId: { + filter: { + id: new RegExp(`^${SCRIPT_ID_PREFIX}`), + }, + handler(id) { + return id; + }, + }, load: { filter: { diff --git a/packages/astro/test/fixtures/virtual-astro-file/src/plugin/my-plugin.mjs b/packages/astro/test/fixtures/virtual-astro-file/src/plugin/my-plugin.mjs index 07a5ae54414b..eb59c67ff953 100644 --- a/packages/astro/test/fixtures/virtual-astro-file/src/plugin/my-plugin.mjs +++ b/packages/astro/test/fixtures/virtual-astro-file/src/plugin/my-plugin.mjs @@ -3,10 +3,14 @@ export default function myPlugin() { return { enforce: 'pre', name: 'virtual-astro-plugin', - // TODO: check if useful - // resolveId(id) { - // if (id === pluginId) return id; - // }, + resolveId: { + filter: { + id: new RegExp(`^${pluginId}$`), + }, + handler(id) { + return id; + }, + }, load: { filter: { id: new RegExp(`^${pluginId}$`), diff --git a/packages/astro/test/ssr-adapter-build-config.test.js b/packages/astro/test/ssr-adapter-build-config.test.js index 77fd70efceb9..c37adb43e778 100644 --- a/packages/astro/test/ssr-adapter-build-config.test.js +++ b/packages/astro/test/ssr-adapter-build-config.test.js @@ -23,12 +23,14 @@ describe('Integration buildConfig hook', () => { vite: { plugins: [ { - // TODO: check if @my-ssr check is useful resolveId: { filter: { - id: /^astro\/app$/, + id: /^(astro\/app|@my-ssr)$/, }, - handler() { + handler(id) { + if (id === '@my-ssr') { + return id; + } return viteID(new URL('../dist/core/app/index.js', import.meta.url)); }, }, diff --git a/packages/astro/test/test-adapter.js b/packages/astro/test/test-adapter.js index 546e090788d1..62f35dabcc95 100644 --- a/packages/astro/test/test-adapter.js +++ b/packages/astro/test/test-adapter.js @@ -34,12 +34,14 @@ export default function ({ vite: { plugins: [ { - // TODO: check if @my-ssr check is useful resolveId: { filter: { - id: /^astro\/app$/, + id: /^(astro\/app|@my-ssr)$/, }, handler() { + if (id === '@my-ssr') { + return id; + } return viteID(new URL('../dist/core/app/index.js', import.meta.url)); }, }, From bf3a9ed6127b672467a8b1b887ea0dd02d0e1011 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 17:09:44 +0100 Subject: [PATCH 15/30] fix --- .../src/core/build/plugins/plugin-component-entry.ts | 8 ++++++++ packages/astro/test/test-adapter.js | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/astro/src/core/build/plugins/plugin-component-entry.ts b/packages/astro/src/core/build/plugins/plugin-component-entry.ts index 0d867661017b..f992404ad7d8 100644 --- a/packages/astro/src/core/build/plugins/plugin-component-entry.ts +++ b/packages/astro/src/core/build/plugins/plugin-component-entry.ts @@ -57,6 +57,14 @@ export function pluginComponentEntry(internals: BuildInternals): VitePlugin { }); } }, + resolveId: { + filter: { + id: new RegExp(`^${astroEntryPrefix}`), + }, + handler(id) { + return id; + }, + }, load: { filter: { id: new RegExp(`^${astroEntryPrefix}`), diff --git a/packages/astro/test/test-adapter.js b/packages/astro/test/test-adapter.js index 62f35dabcc95..7616ff0fb587 100644 --- a/packages/astro/test/test-adapter.js +++ b/packages/astro/test/test-adapter.js @@ -38,7 +38,7 @@ export default function ({ filter: { id: /^(astro\/app|@my-ssr)$/, }, - handler() { + handler(id) { if (id === '@my-ssr') { return id; } From 8750f02517da40a0b0557d2b06229bd297bcd03b Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 18:04:36 +0100 Subject: [PATCH 16/30] fix: vite plugin config alias --- packages/astro/src/vite-plugin-config-alias/index.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/astro/src/vite-plugin-config-alias/index.ts b/packages/astro/src/vite-plugin-config-alias/index.ts index 3d8150732f3e..00ad07270ab2 100644 --- a/packages/astro/src/vite-plugin-config-alias/index.ts +++ b/packages/astro/src/vite-plugin-config-alias/index.ts @@ -206,11 +206,10 @@ function patchCreateResolver(config: ResolvedConfig, postPlugin: VitePlugin) { if (result) return result; // @ts-expect-error resolveId exists - const resolved = await postPlugin.resolveId.apply(fakePluginContext, [ - id, - importer, - fakeResolveIdOpts, - ]); + const resolved = await (typeof postPlugin.resolveId === 'function' + ? postPlugin.resolveId + : postPlugin.resolveId?.handler + ).apply(fakePluginContext, [id, importer, fakeResolveIdOpts]); if (resolved) return resolved; }; }; From 8b23c66c5e66cf54605e906a6a75d9054f0e2690 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 10 Dec 2025 18:04:49 +0100 Subject: [PATCH 17/30] feat: load --- .../astro/src/assets/vite-plugin-assets.ts | 9 +- .../vite-plugin-content-virtual-mod.ts | 105 +++++----- packages/astro/src/core/util.ts | 17 +- packages/astro/src/vite-plugin-astro/index.ts | 197 +++++++++--------- .../src/vite-plugin-load-fallback/index.ts | 20 +- .../astro/src/vite-plugin-markdown/index.ts | 11 +- packages/db/src/core/integration/file-url.ts | 9 +- .../src/utils/cloudflare-module-loader.ts | 91 ++++---- 8 files changed, 240 insertions(+), 219 deletions(-) diff --git a/packages/astro/src/assets/vite-plugin-assets.ts b/packages/astro/src/assets/vite-plugin-assets.ts index 1cf6307adeee..a2f4535eb8d0 100644 --- a/packages/astro/src/assets/vite-plugin-assets.ts +++ b/packages/astro/src/assets/vite-plugin-assets.ts @@ -241,8 +241,11 @@ export default function assets({ fs, settings, sync, logger }: Options): vite.Pl configResolved(viteConfig) { resolvedConfig = viteConfig; }, - async load(id, options) { - if (assetRegex.test(id)) { + load: { + filter: { + id: assetRegex, + }, + async handler(id, options) { if (!globalThis.astroAsset.referencedImages) globalThis.astroAsset.referencedImages = new Set(); @@ -293,7 +296,7 @@ export default function assets({ fs, settings, sync, logger }: Options): vite.Pl code: `export default ${JSON.stringify(imageMetadata)}`, }; } - } + }, }, }, fontsPlugin({ settings, sync, logger }), diff --git a/packages/astro/src/content/vite-plugin-content-virtual-mod.ts b/packages/astro/src/content/vite-plugin-content-virtual-mod.ts index e2b0a212258a..cefc0ded90ee 100644 --- a/packages/astro/src/content/vite-plugin-content-virtual-mod.ts +++ b/packages/astro/src/content/vite-plugin-content-virtual-mod.ts @@ -119,61 +119,68 @@ export function astroContentVirtualModPlugin({ } }, }, - async load(id, args) { - if (id === RESOLVED_VIRTUAL_MODULE_ID) { - const isClient = !args?.ssr; - const code = await generateContentEntryFile({ - settings, - fs, - isClient, - }); - - const astro = createDefaultAstroMetadata(); - astro.propagation = 'in-tree'; - return { - code, - meta: { - astro, - } satisfies AstroPluginMetadata, - }; - } - if (id === RESOLVED_DATA_STORE_VIRTUAL_ID) { - if (!fs.existsSync(dataStoreFile)) { - return { code: 'export default new Map()' }; + load: { + filter: { + id: new RegExp( + `^(${RESOLVED_VIRTUAL_MODULE_ID}|${RESOLVED_DATA_STORE_VIRTUAL_ID}|${ASSET_IMPORTS_RESOLVED_STUB_ID}|${MODULES_MJS_VIRTUAL_ID})$`, + ), + }, + async handler(id, opts) { + if (id === RESOLVED_VIRTUAL_MODULE_ID) { + const isClient = !opts?.ssr; + const code = await generateContentEntryFile({ + settings, + fs, + isClient, + }); + + const astro = createDefaultAstroMetadata(); + astro.propagation = 'in-tree'; + return { + code, + meta: { + astro, + } satisfies AstroPluginMetadata, + }; + } + if (id === RESOLVED_DATA_STORE_VIRTUAL_ID) { + if (!fs.existsSync(dataStoreFile)) { + return { code: 'export default new Map()' }; + } + const jsonData = await fs.promises.readFile(dataStoreFile, 'utf-8'); + + try { + const parsed = JSON.parse(jsonData); + return { + code: dataToEsm(parsed, { + compact: true, + }), + map: { mappings: '' }, + }; + } catch (err) { + const message = 'Could not parse JSON file'; + this.error({ message, id, cause: err }); + } } - const jsonData = await fs.promises.readFile(dataStoreFile, 'utf-8'); - try { - const parsed = JSON.parse(jsonData); + if (id === ASSET_IMPORTS_RESOLVED_STUB_ID) { + const assetImportsFile = new URL(ASSET_IMPORTS_FILE, settings.dotAstroDir); return { - code: dataToEsm(parsed, { - compact: true, - }), - map: { mappings: '' }, + code: fs.existsSync(assetImportsFile) + ? fs.readFileSync(assetImportsFile, 'utf-8') + : 'export default new Map()', }; - } catch (err) { - const message = 'Could not parse JSON file'; - this.error({ message, id, cause: err }); } - } - if (id === ASSET_IMPORTS_RESOLVED_STUB_ID) { - const assetImportsFile = new URL(ASSET_IMPORTS_FILE, settings.dotAstroDir); - return { - code: fs.existsSync(assetImportsFile) - ? fs.readFileSync(assetImportsFile, 'utf-8') - : 'export default new Map()', - }; - } - - if (id === MODULES_MJS_VIRTUAL_ID) { - const modules = new URL(MODULES_IMPORTS_FILE, settings.dotAstroDir); - return { - code: fs.existsSync(modules) - ? fs.readFileSync(modules, 'utf-8') - : 'export default new Map()', - }; - } + if (id === MODULES_MJS_VIRTUAL_ID) { + const modules = new URL(MODULES_IMPORTS_FILE, settings.dotAstroDir); + return { + code: fs.existsSync(modules) + ? fs.readFileSync(modules, 'utf-8') + : 'export default new Map()', + }; + } + }, }, configureServer(server) { diff --git a/packages/astro/src/core/util.ts b/packages/astro/src/core/util.ts index 54f8d57abc52..da12bad8a520 100644 --- a/packages/astro/src/core/util.ts +++ b/packages/astro/src/core/util.ts @@ -4,7 +4,6 @@ import { fileURLToPath } from 'node:url'; import type { AstroSettings } from '../types/astro.js'; import type { AstroConfig } from '../types/public/config.js'; import type { RouteData } from '../types/public/internal.js'; -import { hasSpecialQueries } from '../vite-plugin-utils/index.js'; import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './constants.js'; import { removeQueryString, removeTrailingForwardSlash, slash } from './path.js'; @@ -17,18 +16,10 @@ export function isObject(value: unknown): value is Record { export function isURL(value: unknown): value is URL { return Object.prototype.toString.call(value) === '[object URL]'; } -/** Check if a file is a markdown file based on its extension */ -export function isMarkdownFile(fileId: string, option?: { suffix?: string }): boolean { - if (hasSpecialQueries(fileId)) { - return false; - } - const id = removeQueryString(fileId); - const _suffix = option?.suffix ?? ''; - for (let markdownFileExtension of SUPPORTED_MARKDOWN_FILE_EXTENSIONS) { - if (id.endsWith(`${markdownFileExtension}${_suffix}`)) return true; - } - return false; -} + +export const markdownFileIdRegex = new RegExp( + `(?!.*(?:\\?|&)(?:url|raw|direct)(?:&|$)).*\\.(${SUPPORTED_MARKDOWN_FILE_EXTENSIONS.map((ext) => ext.slice(1)).join('|')})([^?]*)$`, +); /** Wraps an object in an array. If an array is passed, ignore it. */ export function arraify(target: T | T[]): T[] { diff --git a/packages/astro/src/vite-plugin-astro/index.ts b/packages/astro/src/vite-plugin-astro/index.ts index dc45352572b7..81883d191400 100644 --- a/packages/astro/src/vite-plugin-astro/index.ts +++ b/packages/astro/src/vite-plugin-astro/index.ts @@ -91,121 +91,124 @@ export default function astro({ settings, logger }: AstroPluginOptions): vite.Pl astroFileToCompileMetadataWeakMap.set(config, astroFileToCompileMetadata); } }, - async load(id, opts) { - const parsedId = parseAstroRequest(id); - const query = parsedId.query; - if (!query.astro) { - return null; - } + load: { + filter: { + id: /[?&]astro/, + }, + async handler(id, opts) { + const parsedId = parseAstroRequest(id); + const query = parsedId.query; - // Astro scripts and styles virtual module code comes from the main Astro compilation - // through the metadata from `astroFileToCompileMetadata`. It should always exist as Astro - // modules are compiled first, then its virtual modules. - const filename = normalizePath(normalizeFilename(parsedId.filename, config.root)); - let compileMetadata = astroFileToCompileMetadata.get(filename); - if (!compileMetadata) { - // If `compileMetadata` doesn't exist in dev, that means the virtual module may have been invalidated. - // We try to re-compile the main Astro module (`filename`) first before retrieving the metadata again. - if (server) { - const code = await loadId(server.pluginContainer, filename); - // `compile` should re-set `filename` in `astroFileToCompileMetadata` - if (code != null) await compile(code, filename); - } + // Astro scripts and styles virtual module code comes from the main Astro compilation + // through the metadata from `astroFileToCompileMetadata`. It should always exist as Astro + // modules are compiled first, then its virtual modules. + const filename = normalizePath(normalizeFilename(parsedId.filename, config.root)); + let compileMetadata = astroFileToCompileMetadata.get(filename); + if (!compileMetadata) { + // If `compileMetadata` doesn't exist in dev, that means the virtual module may have been invalidated. + // We try to re-compile the main Astro module (`filename`) first before retrieving the metadata again. + if (server) { + const code = await loadId(server.pluginContainer, filename); + // `compile` should re-set `filename` in `astroFileToCompileMetadata` + if (code != null) await compile(code, filename); + } - compileMetadata = astroFileToCompileMetadata.get(filename); - } - // If the metadata still doesn't exist, that means the virtual modules are somehow compiled first, - // throw an error and we should investigate it. - if (!compileMetadata) { - throw new Error( - `No cached compile metadata found for "${id}". The main Astro module "${filename}" should have ` + - `compiled and filled the metadata first, before its virtual modules can be requested.`, - ); - } + compileMetadata = astroFileToCompileMetadata.get(filename); + } + // If the metadata still doesn't exist, that means the virtual modules are somehow compiled first, + // throw an error and we should investigate it. + if (!compileMetadata) { + throw new Error( + `No cached compile metadata found for "${id}". The main Astro module "${filename}" should have ` + + `compiled and filled the metadata first, before its virtual modules can be requested.`, + ); + } - switch (query.type) { - case 'style': { - if (typeof query.index === 'undefined') { - throw new Error(`Requests for Astro CSS must include an index.`); - } + switch (query.type) { + case 'style': { + if (typeof query.index === 'undefined') { + throw new Error(`Requests for Astro CSS must include an index.`); + } - const result = compileMetadata.css[query.index]; - if (!result) { - throw new Error(`No Astro CSS at index ${query.index}`); - } + const result = compileMetadata.css[query.index]; + if (!result) { + throw new Error(`No Astro CSS at index ${query.index}`); + } - // Register dependencies from preprocessing this style - result.dependencies?.forEach((dep) => this.addWatchFile(dep)); + // Register dependencies from preprocessing this style + result.dependencies?.forEach((dep) => this.addWatchFile(dep)); - return { - code: result.code, - // `vite.cssScopeTo` is a Vite feature that allows this CSS to be treeshaken - // if the Astro component's default export is not used - meta: result.isGlobal - ? undefined - : { - vite: { - cssScopeTo: [filename, 'default'], - }, - }, - }; - } - case 'script': { - if (typeof query.index === 'undefined') { - throw new Error(`Requests for scripts must include an index`); - } - // SSR script only exists to make them appear in the module graph. - if (opts?.ssr) { return { - code: `/* client script, empty in SSR: ${id} */`, + code: result.code, + // `vite.cssScopeTo` is a Vite feature that allows this CSS to be treeshaken + // if the Astro component's default export is not used + meta: result.isGlobal + ? undefined + : { + vite: { + cssScopeTo: [filename, 'default'], + }, + }, }; } + case 'script': { + if (typeof query.index === 'undefined') { + throw new Error(`Requests for scripts must include an index`); + } + // SSR script only exists to make them appear in the module graph. + if (opts?.ssr) { + return { + code: `/* client script, empty in SSR: ${id} */`, + }; + } - const script = compileMetadata.scripts[query.index]; - if (!script) { - throw new Error(`No script at index ${query.index}`); - } + const script = compileMetadata.scripts[query.index]; + if (!script) { + throw new Error(`No script at index ${query.index}`); + } - if (script.type === 'external') { - const src = script.src; - if (src.startsWith('/') && !isBrowserPath(src)) { - const publicDir = config.publicDir.pathname.replace(/\/$/, '').split('/').pop() + '/'; - throw new Error( - `\n\n