diff --git a/.changeset/dry-dragons-greet.md b/.changeset/dry-dragons-greet.md
new file mode 100644
index 000000000..024dd2cdd
--- /dev/null
+++ b/.changeset/dry-dragons-greet.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Fix `.css?raw` usage
diff --git a/.changeset/gentle-insects-run.md b/.changeset/gentle-insects-run.md
new file mode 100644
index 000000000..a2531c521
--- /dev/null
+++ b/.changeset/gentle-insects-run.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Update `@astrojs/compiler` and use the new `resolvePath` option. This allows removing much of the runtime code, which should improve rendering performance for Astro and MDX pages.
diff --git a/packages/astro/e2e/fixtures/errors/src/pages/import-not-found.astro b/packages/astro/e2e/fixtures/errors/src/pages/import-not-found.astro
index 948e4dba5..2205b62b8 100644
--- a/packages/astro/e2e/fixtures/errors/src/pages/import-not-found.astro
+++ b/packages/astro/e2e/fixtures/errors/src/pages/import-not-found.astro
@@ -2,3 +2,4 @@
import Fake from '../abc.astro';
---
Testing unresolved frontmatter import
+
diff --git a/packages/astro/package.json b/packages/astro/package.json
index e0c2ebbec..fdfe081e7 100644
--- a/packages/astro/package.json
+++ b/packages/astro/package.json
@@ -98,7 +98,7 @@
"test:e2e:match": "playwright test -g"
},
"dependencies": {
- "@astrojs/compiler": "^0.27.1",
+ "@astrojs/compiler": "^0.28.0",
"@astrojs/language-server": "^0.26.2",
"@astrojs/markdown-remark": "^1.1.3",
"@astrojs/telemetry": "^1.0.1",
diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts
index 6fd80b1d8..e0954afae 100644
--- a/packages/astro/src/@types/astro.ts
+++ b/packages/astro/src/@types/astro.ts
@@ -16,7 +16,7 @@ import type { SerializedSSRManifest } from '../core/app/types';
import type { PageBuildData } from '../core/build/types';
import type { AstroConfigSchema } from '../core/config';
import type { AstroCookies } from '../core/cookies';
-import type { AstroComponentFactory, Metadata } from '../runtime/server';
+import type { AstroComponentFactory } from '../runtime/server';
export type {
MarkdownHeading,
MarkdownMetadata,
@@ -968,7 +968,6 @@ export type AsyncRendererComponentFn = (
/** Generic interface for a component (Astro, Svelte, React, etc.) */
export interface ComponentInstance {
- $$metadata: Metadata;
default: AstroComponentFactory;
css?: string[];
getStaticPaths?: (options: GetStaticPathsOptions) => GetStaticPathsResult;
diff --git a/packages/astro/src/core/build/vite-plugin-css.ts b/packages/astro/src/core/build/vite-plugin-css.ts
index 1f4eba731..01ea9a8a2 100644
--- a/packages/astro/src/core/build/vite-plugin-css.ts
+++ b/packages/astro/src/core/build/vite-plugin-css.ts
@@ -223,38 +223,6 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
});
output.source = minifiedCSS;
}
- } else if (output.type === 'chunk') {
- // vite:css-post removes "pure CSS" JavaScript chunks, that is chunks that only contain a comment
- // about it being a CSS module. We need to keep these chunks around because Astro
- // re-imports all modules as their namespace `import * as module1 from 'some/path';
- // in order to determine if one of them is a side-effectual web component.
- // If we ever get rid of that feature, the code below can be removed.
- for (const [imp, bindings] of Object.entries(output.importedBindings)) {
- if (imp.startsWith('chunks/') && !bundle[imp] && output.code.includes(imp)) {
- // This just creates an empty chunk module so that the main entry module
- // that is importing it doesn't break.
- const depChunk: OutputChunk = {
- type: 'chunk',
- fileName: imp,
- name: imp,
- facadeModuleId: imp,
- code: `/* Pure CSS chunk ${imp} */ ${bindings
- .map((b) => `export const ${b} = {};`)
- .join('')}`,
- dynamicImports: [],
- implicitlyLoadedBefore: [],
- importedBindings: {},
- imports: [],
- referencedFiles: [],
- exports: Array.from(bindings),
- isDynamicEntry: false,
- isEntry: false,
- isImplicitEntry: false,
- modules: {},
- };
- bundle[imp] = depChunk;
- }
- }
}
}
}
diff --git a/packages/astro/src/core/compile/compile.ts b/packages/astro/src/core/compile/compile.ts
index 3376aa529..776e7088f 100644
--- a/packages/astro/src/core/compile/compile.ts
+++ b/packages/astro/src/core/compile/compile.ts
@@ -1,12 +1,11 @@
import type { TransformResult } from '@astrojs/compiler';
-import path from 'path';
import type { AstroConfig } from '../../@types/astro';
import type { TransformStyle } from './types';
import { transform } from '@astrojs/compiler';
import { AstroErrorCodes } from '../errors.js';
-import { prependForwardSlash, removeLeadingForwardSlashWindows } from '../path.js';
-import { AggregateError, resolveJsToTs, viteID } from '../util.js';
+import { prependForwardSlash } from '../path.js';
+import { AggregateError, resolvePath, viteID } from '../util.js';
import { createStylePreprocessor } from './style.js';
type CompilationCache = Map;
@@ -37,13 +36,7 @@ async function compile({
// use `sourcemap: "both"` so that sourcemap is included in the code
// result passed to esbuild, but also available in the catch handler.
const transformResult = await transform(source, {
- // For Windows compat, prepend filename with `/`.
- // Note this is required because the compiler uses URLs to parse and built paths.
- // TODO: Ideally the compiler could expose a `resolvePath` function so that we can
- // unify how we handle path in a bundler-agnostic way.
- // At the meantime workaround with a slash and remove them in `astro:postprocess`
- // when they are used in `client:component-path`.
- pathname: prependForwardSlash(filename),
+ pathname: filename,
projectRoot: config.root.toString(),
site: config.site?.toString(),
sourcefile: filename,
@@ -54,6 +47,9 @@ async function compile({
// TODO: baseline flag
experimentalStaticExtraction: true,
preprocessStyle: createStylePreprocessor(transformStyle, cssDeps, cssTransformErrors),
+ async resolvePath(specifier) {
+ return resolvePath(specifier, filename);
+ },
})
.catch((err) => {
// throw compiler errors here if encountered
@@ -88,32 +84,6 @@ async function compile({
},
});
- // Also fix path before returning. Example original resolvedPaths:
- // - @astrojs/preact/client.js
- // - @/components/Foo.vue
- // - /Users/macos/project/src/Foo.vue
- // - /C:/Windows/project/src/Foo.vue
- for (const c of compileResult.clientOnlyComponents) {
- c.resolvedPath = removeLeadingForwardSlashWindows(c.resolvedPath);
- // The compiler trims .jsx by default, prevent this
- if (c.specifier.endsWith('.jsx') && !c.resolvedPath.endsWith('.jsx')) {
- c.resolvedPath += '.jsx';
- }
- if (path.isAbsolute(c.resolvedPath)) {
- c.resolvedPath = resolveJsToTs(c.resolvedPath);
- }
- }
- for (const c of compileResult.hydratedComponents) {
- c.resolvedPath = removeLeadingForwardSlashWindows(c.resolvedPath);
- // The compiler trims .jsx by default, prevent this
- if (c.specifier.endsWith('.jsx') && !c.resolvedPath.endsWith('.jsx')) {
- c.resolvedPath += '.jsx';
- }
- if (path.isAbsolute(c.resolvedPath)) {
- c.resolvedPath = resolveJsToTs(c.resolvedPath);
- }
- }
-
return compileResult;
}
diff --git a/packages/astro/src/core/render/dev/index.ts b/packages/astro/src/core/render/dev/index.ts
index fc14bc3a4..72c55766d 100644
--- a/packages/astro/src/core/render/dev/index.ts
+++ b/packages/astro/src/core/render/dev/index.ts
@@ -14,7 +14,6 @@ import { isPage, resolveIdToUrl } from '../../util.js';
import { createRenderContext, renderPage as coreRenderPage } from '../index.js';
import { filterFoundRenderers, loadRenderer } from '../renderer.js';
import { RouteCache } from '../route-cache.js';
-import { collectMdMetadata } from '../util.js';
import { getStylesForURL } from './css.js';
import type { DevelopmentEnvironment } from './environment';
import { getScriptsForURL } from './scripts.js';
@@ -94,16 +93,6 @@ export async function preload({
const renderers = await loadRenderers(env.viteServer, env.settings);
// Load the module from the Vite SSR Runtime.
const mod = (await env.viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance;
- if (env.viteServer.config.mode === 'development' || !mod?.$$metadata) {
- return [renderers, mod];
- }
-
- // append all nested markdown metadata to mod.$$metadata
- const modGraph = await env.viteServer.moduleGraph.getModuleByUrl(fileURLToPath(filePath));
- if (modGraph) {
- await collectMdMetadata(mod.$$metadata, modGraph, env.viteServer);
- }
-
return [renderers, mod];
}
diff --git a/packages/astro/src/core/render/util.ts b/packages/astro/src/core/render/util.ts
index be1e693ef..2dddc49b0 100644
--- a/packages/astro/src/core/render/util.ts
+++ b/packages/astro/src/core/render/util.ts
@@ -1,15 +1,3 @@
-import type { ModuleNode, ViteDevServer } from 'vite';
-import type { Metadata } from '../../runtime/server/metadata.js';
-
-/** Check if a URL is already valid */
-export function isValidURL(url: string): boolean {
- try {
- new URL(url);
- return true;
- } catch (e) {}
- return false;
-}
-
// https://vitejs.dev/guide/features.html#css-pre-processors
export const STYLE_EXTENSIONS = new Set([
'.css',
@@ -22,59 +10,9 @@ export const STYLE_EXTENSIONS = new Set([
'.less',
]);
-// duplicate const from vite-plugin-markdown
-// can't import directly due to Deno bundling issue
-// (node fs import failing during prod builds)
-const MARKDOWN_IMPORT_FLAG = '?mdImport';
-
const cssRe = new RegExp(
`\\.(${Array.from(STYLE_EXTENSIONS)
.map((s) => s.slice(1))
.join('|')})($|\\?)`
);
export const isCSSRequest = (request: string): boolean => cssRe.test(request);
-
-// During prod builds, some modules have dependencies we should preload by hand
-// Ex. markdown files imported asynchronously or via Astro.glob(...)
-// This calls each md file's $$loadMetadata to discover those dependencies
-// and writes all results to the input `metadata` object
-const seenMdMetadata = new Set();
-export async function collectMdMetadata(
- metadata: Metadata,
- modGraph: ModuleNode,
- viteServer: ViteDevServer
-) {
- const importedModules = [...(modGraph?.importedModules ?? [])];
- await Promise.all(
- importedModules.map(async (importedModule) => {
- // recursively check for importedModules
- if (!importedModule.id || seenMdMetadata.has(importedModule.id)) return;
-
- seenMdMetadata.add(importedModule.id);
- await collectMdMetadata(metadata, importedModule, viteServer);
-
- if (!importedModule?.id?.endsWith(MARKDOWN_IMPORT_FLAG)) return;
-
- const mdSSRMod = await viteServer.ssrLoadModule(importedModule.id);
- const mdMetadata = (await mdSSRMod.$$loadMetadata?.()) as Metadata;
- if (!mdMetadata) return;
-
- for (let mdMod of mdMetadata.modules) {
- mdMod.specifier = mdMetadata.resolvePath(mdMod.specifier);
- metadata.modules.push(mdMod);
- }
- for (let mdHoisted of mdMetadata.hoisted) {
- metadata.hoisted.push(mdHoisted);
- }
- for (let mdHydrated of mdMetadata.hydratedComponents) {
- metadata.hydratedComponents.push(mdHydrated);
- }
- for (let mdClientOnly of mdMetadata.clientOnlyComponents) {
- metadata.clientOnlyComponents.push(mdClientOnly);
- }
- for (let mdHydrationDirective of mdMetadata.hydrationDirectives) {
- metadata.hydrationDirectives.add(mdHydrationDirective);
- }
- })
- );
-}
diff --git a/packages/astro/src/core/util.ts b/packages/astro/src/core/util.ts
index a1a5f9264..54681f3d0 100644
--- a/packages/astro/src/core/util.ts
+++ b/packages/astro/src/core/util.ts
@@ -4,7 +4,7 @@ import path from 'path';
import resolve from 'resolve';
import slash from 'slash';
import { fileURLToPath, pathToFileURL } from 'url';
-import type { ErrorPayload, ViteDevServer } from 'vite';
+import { ErrorPayload, normalizePath, ViteDevServer } from 'vite';
import type { AstroConfig, AstroSettings, RouteType } from '../@types/astro';
import { prependForwardSlash, removeTrailingForwardSlash } from './path.js';
@@ -227,6 +227,18 @@ export function resolveJsToTs(filePath: string) {
return filePath;
}
+/**
+ * Resolve the hydration paths so that it can be imported in the client
+ */
+export function resolvePath(specifier: string, importer: string) {
+ if (specifier.startsWith('.')) {
+ const absoluteSpecifier = path.resolve(path.dirname(importer), specifier);
+ return resolveJsToTs(normalizePath(absoluteSpecifier));
+ } else {
+ return specifier;
+ }
+}
+
export const AggregateError =
typeof (globalThis as any).AggregateError !== 'undefined'
? (globalThis as any).AggregateError
diff --git a/packages/astro/src/jsx/babel.ts b/packages/astro/src/jsx/babel.ts
index 19e4327d7..67cd3c098 100644
--- a/packages/astro/src/jsx/babel.ts
+++ b/packages/astro/src/jsx/babel.ts
@@ -1,8 +1,6 @@
import type { PluginObj } from '@babel/core';
import * as t from '@babel/types';
-import npath from 'path';
-import { normalizePath } from 'vite';
-import { resolveJsToTs } from '../core/util.js';
+import { resolvePath } from '../core/util.js';
import { HydrationDirectiveProps } from '../runtime/server/hydration.js';
import type { PluginMetadata } from '../vite-plugin-astro/types';
@@ -217,13 +215,7 @@ export default function astroJSX(): PluginObj {
const meta = path.getData('import');
if (meta) {
- let resolvedPath: string;
- if (meta.path.startsWith('.')) {
- resolvedPath = normalizePath(npath.resolve(npath.dirname(state.filename!), meta.path));
- resolvedPath = resolveJsToTs(resolvedPath);
- } else {
- resolvedPath = meta.path;
- }
+ const resolvedPath = resolvePath(meta.path, state.filename!);
if (isClientOnly) {
(state.file.metadata as PluginMetadata).astro.clientOnlyComponents.push({
@@ -297,13 +289,7 @@ export default function astroJSX(): PluginObj {
}
}
}
- let resolvedPath: string;
- if (meta.path.startsWith('.')) {
- resolvedPath = normalizePath(npath.resolve(npath.dirname(state.filename!), meta.path));
- resolvedPath = resolveJsToTs(resolvedPath);
- } else {
- resolvedPath = meta.path;
- }
+ const resolvedPath = resolvePath(meta.path, state.filename!);
if (isClientOnly) {
(state.file.metadata as PluginMetadata).astro.clientOnlyComponents.push({
exportName: meta.name,
diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts
index 74219f4c5..6ae149917 100644
--- a/packages/astro/src/runtime/server/index.ts
+++ b/packages/astro/src/runtime/server/index.ts
@@ -2,8 +2,6 @@ export { createAstro } from './astro-global.js';
export { renderEndpoint } from './endpoint.js';
export { escapeHTML, HTMLBytes, HTMLString, markHTMLString, unescapeHTML } from './escape.js';
export { renderJSX } from './jsx.js';
-export type { Metadata } from './metadata';
-export { createMetadata } from './metadata.js';
export {
addAttribute,
defineScriptVars,
diff --git a/packages/astro/src/runtime/server/metadata.ts b/packages/astro/src/runtime/server/metadata.ts
deleted file mode 100644
index 101bf1fdb..000000000
--- a/packages/astro/src/runtime/server/metadata.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-import { removeLeadingForwardSlashWindows } from '../../core/path.js';
-
-interface ModuleInfo {
- module: Record;
- specifier: string;
-}
-
-interface ComponentMetadata {
- componentExport: string;
- componentUrl: string;
-}
-
-interface CreateMetadataOptions {
- modules: ModuleInfo[];
- hydratedComponents: any[];
- clientOnlyComponents: any[];
- hydrationDirectives: Set;
- hoisted: any[];
-}
-
-export class Metadata {
- public filePath: string;
- public modules: ModuleInfo[];
- public hoisted: any[];
- public hydratedComponents: any[];
- public clientOnlyComponents: any[];
- public hydrationDirectives: Set;
-
- private mockURL: URL;
- private metadataCache: Map;
-
- constructor(filePathname: string, opts: CreateMetadataOptions) {
- this.modules = opts.modules;
- this.hoisted = opts.hoisted;
- this.hydratedComponents = opts.hydratedComponents;
- this.clientOnlyComponents = opts.clientOnlyComponents;
- this.hydrationDirectives = opts.hydrationDirectives;
- this.filePath = removeLeadingForwardSlashWindows(filePathname);
- this.mockURL = new URL(filePathname, 'http://example.com');
- this.metadataCache = new Map();
- }
-
- resolvePath(specifier: string): string {
- if (specifier.startsWith('.')) {
- // NOTE: ideally we should use `path.resolve` here, but this is part
- // of server output code, which needs to work on platform that doesn't
- // have the `path` module. Use `URL` here since we deal with posix only.
- const url = new URL(specifier, this.mockURL);
- return removeLeadingForwardSlashWindows(decodeURI(url.pathname));
- } else {
- return specifier;
- }
- }
-
- getPath(Component: any): string | null {
- const metadata = this.getComponentMetadata(Component);
- return metadata?.componentUrl || null;
- }
-
- getExport(Component: any): string | null {
- const metadata = this.getComponentMetadata(Component);
- return metadata?.componentExport || null;
- }
-
- private getComponentMetadata(Component: any): ComponentMetadata | null {
- if (this.metadataCache.has(Component)) {
- return this.metadataCache.get(Component)!;
- }
- const metadata = this.findComponentMetadata(Component);
- this.metadataCache.set(Component, metadata);
- return metadata;
- }
-
- private findComponentMetadata(Component: any): ComponentMetadata | null {
- const isCustomElement = typeof Component === 'string';
- for (const { module, specifier } of this.modules) {
- const id = this.resolvePath(specifier);
- for (const [key, value] of Object.entries(module)) {
- if (isCustomElement) {
- if (key === 'tagName' && Component === value) {
- return {
- componentExport: key,
- componentUrl: id,
- };
- }
- } else if (Component === value) {
- return {
- componentExport: key,
- componentUrl: id,
- };
- }
- }
- }
- return null;
- }
-}
-
-export function createMetadata(filePathname: string, options: CreateMetadataOptions) {
- return new Metadata(filePathname, options);
-}
diff --git a/packages/astro/src/vite-plugin-astro-postprocess/index.ts b/packages/astro/src/vite-plugin-astro-postprocess/index.ts
index e120db061..16eab8662 100644
--- a/packages/astro/src/vite-plugin-astro-postprocess/index.ts
+++ b/packages/astro/src/vite-plugin-astro-postprocess/index.ts
@@ -1,28 +1,17 @@
import { parse as babelParser } from '@babel/parser';
-import type {
- ArrowFunctionExpressionKind,
- CallExpressionKind,
- StringLiteralKind,
-} from 'ast-types/gen/kinds';
+import type { ArrowFunctionExpressionKind, CallExpressionKind } from 'ast-types/gen/kinds';
import type { NodePath } from 'ast-types/lib/node-path';
-import npath from 'path';
import { parse, print, types, visit } from 'recast';
import type { Plugin } from 'vite';
import type { AstroSettings } from '../@types/astro';
-import { removeLeadingForwardSlashWindows } from '../core/path.js';
-import { resolveJsToTs } from '../core/util.js';
// Check for `Astro.glob()`. Be very forgiving of whitespace. False positives are okay.
const ASTRO_GLOB_REGEX = /Astro2?\s*\.\s*glob\s*\(/;
-const CLIENT_COMPONENT_PATH_REGEX = /['"]client:component-path['"]:/;
interface AstroPluginOptions {
settings: AstroSettings;
}
-// esbuild transforms the component-scoped Astro into Astro2, so need to check both.
-const validAstroGlobalNames = new Set(['Astro', 'Astro2']);
-
export default function astro(_opts: AstroPluginOptions): Plugin {
return {
name: 'astro:postprocess',
@@ -34,7 +23,7 @@ export default function astro(_opts: AstroPluginOptions): Plugin {
// Optimization: Detect usage with a quick string match.
// Only perform the transform if this function is found
- if (!ASTRO_GLOB_REGEX.test(code) && !CLIENT_COMPONENT_PATH_REGEX.test(code)) {
+ if (!ASTRO_GLOB_REGEX.test(code)) {
return null;
}
@@ -85,33 +74,6 @@ export default function astro(_opts: AstroPluginOptions): Plugin {
);
return false;
},
- visitObjectProperty: function (path) {
- // Filter out none 'client:component-path' properties
- if (
- !types.namedTypes.StringLiteral.check(path.node.key) ||
- path.node.key.value !== 'client:component-path' ||
- !types.namedTypes.StringLiteral.check(path.node.value)
- ) {
- this.traverse(path);
- return;
- }
-
- // Patch up client:component-path value that has leading slash on Windows.
- // See `compile.ts` for more details, this will be fixed in the Astro compiler.
- const valuePath = path.get('value') as NodePath;
- let value = valuePath.value.value;
- value = removeLeadingForwardSlashWindows(value);
- // Add back `.jsx` stripped by the compiler by loosely checking the code
- if (code.includes(npath.basename(value) + '.jsx')) {
- value += '.jsx';
- }
- value = resolveJsToTs(value);
- valuePath.replace({
- type: 'StringLiteral',
- value,
- } as StringLiteralKind);
- return false;
- },
});
const result = print(ast);
diff --git a/packages/astro/src/vite-plugin-markdown-legacy/index.ts b/packages/astro/src/vite-plugin-markdown-legacy/index.ts
index 299725962..3e0ff307b 100644
--- a/packages/astro/src/vite-plugin-markdown-legacy/index.ts
+++ b/packages/astro/src/vite-plugin-markdown-legacy/index.ts
@@ -120,9 +120,6 @@ export default function markdown({ settings }: AstroPluginOptions): Plugin {
export async function compiledContent() {
return load().then((m) => m.compiledContent());
}
- export function $$loadMetadata() {
- return load().then((m) => m.$$metadata);
- }
// Deferred
export default async function load() {
diff --git a/packages/astro/test/0-css.test.js b/packages/astro/test/0-css.test.js
index f6da42f23..2f1e51a74 100644
--- a/packages/astro/test/0-css.test.js
+++ b/packages/astro/test/0-css.test.js
@@ -266,6 +266,13 @@ describe('CSS', function () {
);
});
});
+
+ describe('Vite features', () => {
+ it('.css?raw return a string', () => {
+ const el = $('#css-raw');
+ expect(el.text()).to.equal('.foo {color: red;}');
+ });
+ });
});
// with "build" handling CSS checking, the dev tests are mostly testing the paths resolve in dev
@@ -375,5 +382,10 @@ describe('CSS', function () {
'Should not have found a preload for the dynamic CSS'
);
});
+
+ it('.css?raw return a string', () => {
+ const el = $('#css-raw');
+ expect(el.text()).to.equal('.foo {color: red;}');
+ });
});
});
diff --git a/packages/astro/test/fixtures/0-css/src/pages/index.astro b/packages/astro/test/fixtures/0-css/src/pages/index.astro
index 7da2dbcb1..073419a5e 100644
--- a/packages/astro/test/fixtures/0-css/src/pages/index.astro
+++ b/packages/astro/test/fixtures/0-css/src/pages/index.astro
@@ -23,6 +23,7 @@ import SvelteDynamic from '../components/SvelteDynamic.svelte';
import '../styles/imported-url.css';
import '../styles/imported.sass';
import '../styles/imported.scss';
+import raw from '../styles/raw.css?raw'
---
@@ -71,6 +72,7 @@ import '../styles/imported.scss';
+ {raw}