diff --git a/.changeset/big-rocks-do.md b/.changeset/big-rocks-do.md new file mode 100644 index 000000000000..52b8a1e5dbc4 --- /dev/null +++ b/.changeset/big-rocks-do.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Prevent ?inline and ?raw CSS from being bundled as CSS diff --git a/packages/astro/src/core/build/plugins/plugin-css.ts b/packages/astro/src/core/build/plugins/plugin-css.ts index 0d57cfb5e668..9873ad8898be 100644 --- a/packages/astro/src/core/build/plugins/plugin-css.ts +++ b/packages/astro/src/core/build/plugins/plugin-css.ts @@ -2,7 +2,7 @@ import * as crypto from 'node:crypto'; import * as npath from 'node:path'; import type { GetModuleInfo } from 'rollup'; import { Plugin as VitePlugin, ResolvedConfig, transformWithEsbuild } from 'vite'; -import { isCSSRequest } from '../../render/util.js'; +import { isBuildableCSSRequest } from '../../render/dev/util.js'; import type { BuildInternals } from '../internal'; import type { AstroBuildPlugin } from '../plugin'; import type { PageBuildData, StaticBuildOptions } from '../types'; @@ -66,7 +66,7 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] after(id, meta) { // For CSS, create a hash of all of the pages that use it. // This causes CSS to be built into shared chunks when used by multiple pages. - if (isCSSRequest(id)) { + if (isBuildableCSSRequest(id)) { for (const [pageInfo] of walkParentInfos(id, { getModuleInfo: meta.getModuleInfo, })) { diff --git a/packages/astro/src/core/render/dev/css.ts b/packages/astro/src/core/render/dev/css.ts index 5667f705c818..9338c531c540 100644 --- a/packages/astro/src/core/render/dev/css.ts +++ b/packages/astro/src/core/render/dev/css.ts @@ -3,7 +3,7 @@ import type { ModuleLoader } from '../../module-loader/index'; import path from 'path'; import { RuntimeMode } from '../../../@types/astro.js'; import { viteID } from '../../util.js'; -import { STYLE_EXTENSIONS } from '../util.js'; +import { isCSSRequest } from './util.js'; import { crawlGraph } from './vite.js'; /** Given a filePath URL, crawl Vite’s module graph to find all style imports. */ @@ -16,8 +16,7 @@ export async function getStylesForURL( const importedStylesMap = new Map(); for await (const importedModule of crawlGraph(loader, viteID(filePath), true)) { - const ext = path.extname(importedModule.url).toLowerCase(); - if (STYLE_EXTENSIONS.has(ext)) { + if (isCSSRequest(importedModule.url)) { let ssrModule: Record; try { // The SSR module is possibly not loaded. Load it if it's null. diff --git a/packages/astro/src/core/render/dev/util.ts b/packages/astro/src/core/render/dev/util.ts new file mode 100644 index 000000000000..85bf3dec28f4 --- /dev/null +++ b/packages/astro/src/core/render/dev/util.ts @@ -0,0 +1,9 @@ +import { isCSSRequest } from 'vite'; + +const rawRE = /(?:\?|&)raw(?:&|$)/; +const inlineRE = /(?:\?|&)inline\b/; + +export { isCSSRequest }; + +export const isBuildableCSSRequest = (request: string): boolean => isCSSRequest(request) && + !rawRE.test(request) && !inlineRE.test(request); diff --git a/packages/astro/src/core/render/dev/vite.ts b/packages/astro/src/core/render/dev/vite.ts index f927966b636f..a3f0b6437821 100644 --- a/packages/astro/src/core/render/dev/vite.ts +++ b/packages/astro/src/core/render/dev/vite.ts @@ -4,7 +4,7 @@ import npath from 'path'; import { PROPAGATED_ASSET_FLAG } from '../../../content/consts.js'; import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from '../../constants.js'; import { unwrapId } from '../../util.js'; -import { STYLE_EXTENSIONS } from '../util.js'; +import { isCSSRequest } from './util.js'; /** * List of file extensions signalling we can (and should) SSR ahead-of-time @@ -43,7 +43,7 @@ export async function* crawlGraph( } if (id === entry.id) { scanned.add(id); - const entryIsStyle = STYLE_EXTENSIONS.has(npath.extname(id)); + const entryIsStyle = isCSSRequest(id); for (const importedModule of entry.importedModules) { // some dynamically imported modules are *not* server rendered in time // to only SSR modules that we can safely transform, we check against @@ -57,7 +57,7 @@ export async function* crawlGraph( // Tools like Tailwind might add HMR dependencies as `importedModules` // but we should skip them--they aren't really imported. Without this, // every hoisted script in the project is added to every page! - if (entryIsStyle && !STYLE_EXTENSIONS.has(npath.extname(importedModulePathname))) { + if (entryIsStyle && !isCSSRequest(importedModulePathname)) { continue; } if (fileExtensionsToSSR.has(npath.extname(importedModulePathname))) { diff --git a/packages/astro/src/core/render/util.ts b/packages/astro/src/core/render/util.ts deleted file mode 100644 index 2dddc49b0b97..000000000000 --- a/packages/astro/src/core/render/util.ts +++ /dev/null @@ -1,18 +0,0 @@ -// https://vitejs.dev/guide/features.html#css-pre-processors -export const STYLE_EXTENSIONS = new Set([ - '.css', - '.pcss', - '.postcss', - '.scss', - '.sass', - '.styl', - '.stylus', - '.less', -]); - -const cssRe = new RegExp( - `\\.(${Array.from(STYLE_EXTENSIONS) - .map((s) => s.slice(1)) - .join('|')})($|\\?)` -); -export const isCSSRequest = (request: string): boolean => cssRe.test(request); diff --git a/packages/astro/test/css-inline.test.js b/packages/astro/test/css-inline.test.js new file mode 100644 index 000000000000..01d3133b8be2 --- /dev/null +++ b/packages/astro/test/css-inline.test.js @@ -0,0 +1,28 @@ +import { expect } from 'chai'; +import * as cheerio from 'cheerio'; +import { loadFixture } from './test-utils.js'; + +describe('Importing raw/inlined CSS', () => { + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/css-inline/', + }); + await fixture.build(); + }); + + it('?inline is imported as a string', async () => { + const html = await fixture.readFile('/index.html'); + const $ = cheerio.load(html); + + expect($('#inline').text()).to.contain('tomato'); + }); + + it('?raw is imported as a string', async () => { + const html = await fixture.readFile('/index.html'); + const $ = cheerio.load(html); + + expect($('#raw').text()).to.contain('plum'); + }); +}); diff --git a/packages/astro/test/fixtures/css-inline/package.json b/packages/astro/test/fixtures/css-inline/package.json new file mode 100644 index 000000000000..5fce139475cd --- /dev/null +++ b/packages/astro/test/fixtures/css-inline/package.json @@ -0,0 +1,8 @@ +{ + "name": "@test/css-inline", + "version": "0.0.0", + "private": true, + "dependencies": { + "astro": "workspace:*" + } +} diff --git a/packages/astro/test/fixtures/css-inline/src/inline.css b/packages/astro/test/fixtures/css-inline/src/inline.css new file mode 100644 index 000000000000..ea8200bd9861 --- /dev/null +++ b/packages/astro/test/fixtures/css-inline/src/inline.css @@ -0,0 +1,3 @@ +body { + background: tomato; +} diff --git a/packages/astro/test/fixtures/css-inline/src/layouts/Layout.astro b/packages/astro/test/fixtures/css-inline/src/layouts/Layout.astro new file mode 100644 index 000000000000..f1a62a537b7d --- /dev/null +++ b/packages/astro/test/fixtures/css-inline/src/layouts/Layout.astro @@ -0,0 +1,35 @@ +--- +export interface Props { + title: string; +} + +const { title } = Astro.props; +--- + + + + + + + + + {title} + + + + + + diff --git a/packages/astro/test/fixtures/css-inline/src/pages/index.astro b/packages/astro/test/fixtures/css-inline/src/pages/index.astro new file mode 100644 index 000000000000..89a7288ae12a --- /dev/null +++ b/packages/astro/test/fixtures/css-inline/src/pages/index.astro @@ -0,0 +1,19 @@ +--- +import Layout from '../layouts/Layout.astro'; +import inline from '../inline.css?inline'; +import raw from '../raw.css?raw'; +--- + +
+

Welcome to Astro

+

+ This are some `?inline` styles to show as object: +

+

+ {inline} +

+

+ {raw} +

+
+
diff --git a/packages/astro/test/fixtures/css-inline/src/raw.css b/packages/astro/test/fixtures/css-inline/src/raw.css new file mode 100644 index 000000000000..916081876507 --- /dev/null +++ b/packages/astro/test/fixtures/css-inline/src/raw.css @@ -0,0 +1,3 @@ +main { + background: plum; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fb87e5f4242a..7577d37e1d5c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1690,6 +1690,12 @@ importers: packages/astro/test/fixtures/css-assets/packages/font-awesome: specifiers: {} + packages/astro/test/fixtures/css-inline: + specifiers: + astro: workspace:* + dependencies: + astro: link:../../.. + packages/astro/test/fixtures/css-no-code-split: specifiers: astro: workspace:*