diff --git a/.changeset/hungry-cougars-yell.md b/.changeset/hungry-cougars-yell.md new file mode 100644 index 0000000000..658e0bb91f --- /dev/null +++ b/.changeset/hungry-cougars-yell.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Allow defining Astro components in Vite plugins diff --git a/packages/astro/src/vite-plugin-astro/compile.ts b/packages/astro/src/vite-plugin-astro/compile.ts index 7b8c6c7fd0..5a1d718689 100644 --- a/packages/astro/src/vite-plugin-astro/compile.ts +++ b/packages/astro/src/vite-plugin-astro/compile.ts @@ -11,7 +11,10 @@ import { viteID } from '../core/util.js'; import { transformWithVite } from './styles.js'; type CompilationCache = Map; -type CompileResult = TransformResult & { rawCSSDeps: Set }; +type CompileResult = TransformResult & { + rawCSSDeps: Set; + source: string; +}; /** * Note: this is currently needed because Astro is directly using a Vite internal CSS transform. This gives us @@ -44,6 +47,16 @@ export interface CompileProps { pluginContext: PluginContext; } +function getNormalizedID(filename: string): string { + try { + const filenameURL = new URL(`file://${filename}`); + return fileURLToPath(filenameURL); + } catch(err) { + // Not a real file, so just use the provided filename as the normalized id + return filename; + } +} + async function compile({ config, filename, @@ -53,9 +66,7 @@ async function compile({ viteTransform, pluginContext, }: CompileProps): Promise { - const filenameURL = new URL(`file://${filename}`); - const normalizedID = fileURLToPath(filenameURL); - + const normalizedID = getNormalizedID(filename); let rawCSSDeps = new Set(); let cssTransformError: Error | undefined; @@ -141,6 +152,9 @@ async function compile({ rawCSSDeps: { value: rawCSSDeps, }, + source: { + value: source, + }, }); return compileResult; @@ -150,6 +164,13 @@ export function isCached(config: AstroConfig, filename: string) { return configCache.has(config) && configCache.get(config)!.has(filename); } +export function getCachedSource(config: AstroConfig, filename: string): string | null { + if(!isCached(config, filename)) return null; + let src = configCache.get(config)!.get(filename); + if(!src) return null; + return src.source; +} + export function invalidateCompilation(config: AstroConfig, filename: string) { if (configCache.has(config)) { const cache = configCache.get(config)!; diff --git a/packages/astro/src/vite-plugin-astro/index.ts b/packages/astro/src/vite-plugin-astro/index.ts index 1318d74586..195bb149dc 100644 --- a/packages/astro/src/vite-plugin-astro/index.ts +++ b/packages/astro/src/vite-plugin-astro/index.ts @@ -13,7 +13,7 @@ import { isRelativePath, startsWithForwardSlash } from '../core/path.js'; import { resolvePages } from '../core/util.js'; import { PAGE_SCRIPT_ID, PAGE_SSR_SCRIPT_ID } from '../vite-plugin-scripts/index.js'; import { getFileInfo } from '../vite-plugin-utils/index.js'; -import { cachedCompilation, CompileProps } from './compile.js'; +import { cachedCompilation, CompileProps, getCachedSource } from './compile.js'; import { handleHotUpdate, trackCSSDependencies } from './hmr.js'; import { parseAstroRequest, ParsedRequestResult } from './query.js'; import { getViteTransform, TransformHook } from './styles.js'; @@ -96,24 +96,25 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu return id; } }, - async load(this: PluginContext, id, opts) { + async load(id, opts) { const parsedId = parseAstroRequest(id); const query = parsedId.query; - if (!id.endsWith('.astro') && !query.astro) { + if (!query.astro) { return null; } - // if we still get a relative path here, vite couldn't resolve the import - if (isRelativePath(parsedId.filename)) { + let filename = parsedId.filename; + // For CSS / hoisted scripts we need to load the source ourselves. + // It should be in the compilation cache at this point. + let raw = await this.resolve(filename, undefined); + if(!raw) { return null; } - const filename = normalizeFilename(parsedId.filename); - const fileUrl = new URL(`file://${filename}`); - let source = await fs.promises.readFile(fileUrl, 'utf-8'); - const isPage = fileUrl.pathname.startsWith(resolvePages(config).pathname); - if (isPage && config._ctx.scripts.some((s) => s.stage === 'page')) { - source += `\n