diff options
-rw-r--r-- | .changeset/cyan-pigs-ring.md | 5 | ||||
-rw-r--r-- | examples/with-tailwindcss/src/pages/index.astro | 5 | ||||
-rw-r--r-- | examples/with-tailwindcss/src/pages/markdown-page.md | 10 | ||||
-rw-r--r-- | packages/astro/src/@types/astro.ts | 2 | ||||
-rw-r--r-- | packages/astro/src/core/build/generate.ts | 20 | ||||
-rw-r--r-- | packages/astro/src/core/build/vite-plugin-ssr.ts | 3 | ||||
-rw-r--r-- | packages/astro/src/runtime/server/hydration.ts | 3 | ||||
-rw-r--r-- | packages/astro/src/vite-plugin-astro/index.ts | 7 | ||||
-rw-r--r-- | packages/astro/src/vite-plugin-markdown/index.ts | 16 | ||||
-rw-r--r-- | packages/astro/src/vite-plugin-scripts/index.ts | 8 | ||||
-rw-r--r-- | packages/astro/test/fixtures/tailwindcss/src/pages/index.astro | 5 | ||||
-rw-r--r-- | packages/astro/test/fixtures/tailwindcss/src/pages/markdown-page.md | 11 | ||||
-rw-r--r-- | packages/astro/test/fixtures/tailwindcss/src/styles/global.css | 3 | ||||
-rw-r--r-- | packages/astro/test/tailwindcss.test.js | 12 |
14 files changed, 71 insertions, 39 deletions
diff --git a/.changeset/cyan-pigs-ring.md b/.changeset/cyan-pigs-ring.md new file mode 100644 index 000000000..9e03aa3c9 --- /dev/null +++ b/.changeset/cyan-pigs-ring.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fix a bug where tailwind integration wouldn't apply to markdown pages diff --git a/examples/with-tailwindcss/src/pages/index.astro b/examples/with-tailwindcss/src/pages/index.astro index 743653a95..ebccafa34 100644 --- a/examples/with-tailwindcss/src/pages/index.astro +++ b/examples/with-tailwindcss/src/pages/index.astro @@ -14,8 +14,9 @@ import Button from '../components/Button.astro'; </head> <body> - <div class="grid place-items-center h-screen"> - <Button>Click Me!</Button> + <div class="grid place-items-center h-screen content-center"> + <Button>Tailwind Button in Astro!</Button> + <a href="/markdown-page" class="p-4 underline">Markdown is also supported...</a> </div> </body> </html> diff --git a/examples/with-tailwindcss/src/pages/markdown-page.md b/examples/with-tailwindcss/src/pages/markdown-page.md new file mode 100644 index 000000000..9aa98ffa9 --- /dev/null +++ b/examples/with-tailwindcss/src/pages/markdown-page.md @@ -0,0 +1,10 @@ +--- +title: "Markdown + Tailwind" +setup: | + import Button from '../components/Button.astro'; +--- + +<div class="grid place-items-center h-screen content-center"> + <Button>Tailwind Button in Markdown!</Button> + <a href="/" class="p-4 underline">Go home...</a> +</div>
\ No newline at end of file diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 52c06dff1..9e83af22d 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -465,7 +465,7 @@ export interface AstroUserConfig { * - "page": Injected into the JavaScript bundle of every page. Processed & resolved by Vite. * - "page-ssr": Injected into the frontmatter of every Astro page. Processed & resolved by Vite. */ -type InjectedScriptStage = 'before-hydration' | 'head-inline' | 'page' | 'page-ssr'; +export type InjectedScriptStage = 'before-hydration' | 'head-inline' | 'page' | 'page-ssr'; /** * Resolved Astro Config diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 306187ffd..166c82cdf 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -1,23 +1,23 @@ -import type { OutputAsset, OutputChunk, RollupOutput } from 'rollup'; -import type { PageBuildData } from './types'; -import type { AstroConfig, AstroRenderer, ComponentInstance, EndpointHandler, SSRLoadedRenderer } from '../../@types/astro'; -import type { StaticBuildOptions } from './types'; -import type { BuildInternals } from '../../core/build/internal.js'; -import type { RenderOptions } from '../../core/render/core'; - import fs from 'fs'; +import { bgMagenta, black, cyan, dim, magenta } from 'kleur/colors'; import npath from 'path'; +import type { OutputAsset, OutputChunk, RollupOutput } from 'rollup'; import { fileURLToPath } from 'url'; +import type { AstroConfig, AstroRenderer, ComponentInstance, EndpointHandler, SSRLoadedRenderer } from '../../@types/astro'; +import type { BuildInternals } from '../../core/build/internal.js'; import { debug, error, info } from '../../core/logger.js'; import { prependForwardSlash } from '../../core/path.js'; +import type { RenderOptions } from '../../core/render/core'; import { resolveDependency } from '../../core/util.js'; +import { BEFORE_HYDRATION_SCRIPT_ID } from '../../vite-plugin-scripts/index.js'; import { call as callEndpoint } from '../endpoint/index.js'; import { render } from '../render/core.js'; import { createLinkStylesheetElementSet, createModuleScriptElementWithSrcSet } from '../render/ssr-element.js'; -import { getOutRoot, getOutFolder, getOutFile } from './common.js'; -import { bgMagenta, black, cyan, dim, magenta } from 'kleur/colors'; +import { getOutFile, getOutFolder, getOutRoot } from './common.js'; +import type { PageBuildData, StaticBuildOptions } from './types'; import { getTimeStat } from './util.js'; + // Render is usually compute, which Node.js can't parallelize well. // In real world testing, dropping from 10->1 showed a notiable perf // improvement. In the future, we can revisit a smarter parallel @@ -214,7 +214,7 @@ async function generatePath(pathname: string, opts: StaticBuildOptions, gopts: G // Return this as placeholder, which will be ignored by the browser. // TODO: In the future, we hope to run this entire script through Vite, // removing the need to maintain our own custom Vite-mimic resolve logic. - if (specifier === 'astro:scripts/before-hydration.js') { + if (specifier === BEFORE_HYDRATION_SCRIPT_ID) { return 'data:text/javascript;charset=utf-8,//[no before-hydration script]'; } throw new Error(`Cannot find the built path for ${specifier}`); diff --git a/packages/astro/src/core/build/vite-plugin-ssr.ts b/packages/astro/src/core/build/vite-plugin-ssr.ts index 6ba82a2b7..0ea9f1d84 100644 --- a/packages/astro/src/core/build/vite-plugin-ssr.ts +++ b/packages/astro/src/core/build/vite-plugin-ssr.ts @@ -7,6 +7,7 @@ import type { SerializedRouteInfo, SerializedSSRManifest } from '../app/types'; import { chunkIsPage, rootRelativeFacadeId, getByFacadeId } from './generate.js'; import { serializeRouteData } from '../routing/index.js'; +import { BEFORE_HYDRATION_SCRIPT_ID } from '../../vite-plugin-scripts/index.js'; const virtualModuleId = '@astrojs-ssr-virtual-entry'; const resolvedVirtualModuleId = '\0' + virtualModuleId; @@ -107,7 +108,7 @@ function buildManifest(bundle: OutputBundle, opts: StaticBuildOptions, internals // HACK! Patch this special one. const entryModules = Object.fromEntries(internals.entrySpecifierToBundleMap.entries()); - entryModules['astro:scripts/before-hydration.js'] = 'data:text/javascript;charset=utf-8,//[no before-hydration script]'; + entryModules[BEFORE_HYDRATION_SCRIPT_ID] = 'data:text/javascript;charset=utf-8,//[no before-hydration script]'; const ssrManifest: SerializedSSRManifest = { routes, diff --git a/packages/astro/src/runtime/server/hydration.ts b/packages/astro/src/runtime/server/hydration.ts index 5f106d942..c963e42c8 100644 --- a/packages/astro/src/runtime/server/hydration.ts +++ b/packages/astro/src/runtime/server/hydration.ts @@ -107,7 +107,8 @@ export async function generateHydrateScript(scriptOptions: HydrateScriptOptions, : `await import("${await result.resolve(componentUrl)}"); return () => {}; `; - + // TODO: If we can figure out tree-shaking in the final SSR build, we could safely + // use BEFORE_HYDRATION_SCRIPT_ID instead of 'astro:scripts/before-hydration.js'. const hydrationScript = { props: { type: 'module', 'data-astro-component-hydration': true }, children: `import setup from '${await result.resolve(hydrationSpecifier(hydrate))}'; diff --git a/packages/astro/src/vite-plugin-astro/index.ts b/packages/astro/src/vite-plugin-astro/index.ts index 829076c6c..8fcf7aafb 100644 --- a/packages/astro/src/vite-plugin-astro/index.ts +++ b/packages/astro/src/vite-plugin-astro/index.ts @@ -13,6 +13,7 @@ import { cachedCompilation } from './compile.js'; import ancestor from 'common-ancestor-path'; import { trackCSSDependencies, handleHotUpdate } from './hmr.js'; import { isRelativePath, startsWithForwardSlash } from '../core/path.js'; +import { PAGE_SCRIPT_ID, PAGE_SSR_SCRIPT_ID } from '../vite-plugin-scripts/index.js'; const FRONTMATTER_PARSE_REGEXP = /^\-\-\-(.*)^\-\-\-/ms; interface AstroPluginOptions { @@ -93,9 +94,9 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu const filename = normalizeFilename(parsedId.filename); const fileUrl = new URL(`file://${filename}`); let source = await fs.promises.readFile(fileUrl, 'utf-8'); - const isPage = filename.startsWith(config.pages.pathname); + const isPage = fileUrl.pathname.startsWith(config.pages.pathname); if (isPage && config._ctx.scripts.some((s) => s.stage === 'page')) { - source += `\n<script hoist src="astro:scripts/page.js" />`; + source += `\n<script hoist src="${PAGE_SCRIPT_ID}" />`; } if (query.astro) { if (query.type === 'style') { @@ -152,7 +153,7 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu } // Add handling to inject scripts into each page JS bundle, if needed. if (isPage) { - SUFFIX += `\nimport "astro:scripts/page-ssr.js";`; + SUFFIX += `\nimport "${PAGE_SSR_SCRIPT_ID}";`; } return { code: `${code}${SUFFIX}`, diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts index 9ea41a842..b74c3f173 100644 --- a/packages/astro/src/vite-plugin-markdown/index.ts +++ b/packages/astro/src/vite-plugin-markdown/index.ts @@ -4,6 +4,7 @@ import esbuild from 'esbuild'; import fs from 'fs'; import type { Plugin } from 'vite'; import type { AstroConfig } from '../@types/astro'; +import { PAGE_SSR_SCRIPT_ID } from '../vite-plugin-scripts/index.js'; interface AstroPluginOptions { config: AstroConfig; @@ -27,7 +28,7 @@ export default function markdown({ config }: AstroPluginOptions): Plugin { enforce: 'pre', // run transforms before other plugins can async load(id) { if (id.endsWith('.md')) { - let source = await fs.promises.readFile(id, 'utf8'); + const source = await fs.promises.readFile(id, 'utf8'); // Transform from `.md` to valid `.astro` let render = config.markdownOptions.render; @@ -42,13 +43,20 @@ export default function markdown({ config }: AstroPluginOptions): Plugin { let renderResult = await render(source, renderOpts); let { frontmatter, metadata, code: astroResult } = renderResult; + const filename = normalizeFilename(id); + const fileUrl = new URL(`file://${filename}`); + const isPage = fileUrl.pathname.startsWith(config.pages.pathname); + const hasInjectedScript = (isPage && config._ctx.scripts.some((s) => s.stage === 'page-ssr')); + // Extract special frontmatter keys const { layout = '', components = '', setup = '', ...content } = frontmatter; content.astro = metadata; const prelude = `--- ${layout ? `import Layout from '${layout}';` : ''} ${components ? `import * from '${components}';` : ''} +${hasInjectedScript ? `import '${PAGE_SSR_SCRIPT_ID}';` : ''} ${setup} + const $$content = ${JSON.stringify(content)} ---`; const imports = `${layout ? `import Layout from '${layout}';` : ''} @@ -60,12 +68,6 @@ ${setup}`.trim(); astroResult = `${prelude}\n${astroResult}`; } - const filename = normalizeFilename(id); - const fileUrl = new URL(`file://${filename}`); - const isPage = filename.startsWith(config.pages.pathname); - if (isPage && config._ctx.scripts.some((s) => s.stage === 'page')) { - source += `\n<script hoist src="astro:scripts/page.js" />`; - } // Transform from `.astro` to valid `.ts` let { code: tsResult } = await transform(astroResult, { diff --git a/packages/astro/src/vite-plugin-scripts/index.ts b/packages/astro/src/vite-plugin-scripts/index.ts index 5e3d6c78d..0d1601bba 100644 --- a/packages/astro/src/vite-plugin-scripts/index.ts +++ b/packages/astro/src/vite-plugin-scripts/index.ts @@ -1,13 +1,13 @@ import { Plugin as VitePlugin } from 'vite'; -import { AstroConfig } from '../@types/astro.js'; +import { AstroConfig, InjectedScriptStage } from '../@types/astro.js'; // NOTE: We can't use the virtual "\0" ID convention because we need to // inject these as ESM imports into actual code, where they would not // resolve correctly. const SCRIPT_ID_PREFIX = `astro:scripts/`; -const BEFORE_HYDRATION_SCRIPT_ID = `${SCRIPT_ID_PREFIX}before-hydration.js`; -const PAGE_SCRIPT_ID = `${SCRIPT_ID_PREFIX}page.js`; -const PAGE_SSR_SCRIPT_ID = `${SCRIPT_ID_PREFIX}page-ssr.js`; +export const BEFORE_HYDRATION_SCRIPT_ID = `${SCRIPT_ID_PREFIX}${'before-hydration' as InjectedScriptStage}.js`; +export const PAGE_SCRIPT_ID = `${SCRIPT_ID_PREFIX}${'page' as InjectedScriptStage}.js`; +export const PAGE_SSR_SCRIPT_ID = `${SCRIPT_ID_PREFIX}${'page-ssr' as InjectedScriptStage}.js`; export default function astroScriptsPlugin({ config }: { config: AstroConfig }): VitePlugin { return { diff --git a/packages/astro/test/fixtures/tailwindcss/src/pages/index.astro b/packages/astro/test/fixtures/tailwindcss/src/pages/index.astro index 30e666384..592f7a47d 100644 --- a/packages/astro/test/fixtures/tailwindcss/src/pages/index.astro +++ b/packages/astro/test/fixtures/tailwindcss/src/pages/index.astro @@ -2,11 +2,6 @@ // Component Imports import Button from '../components/Button.astro'; import Complex from '../components/Complex.astro'; - -import "../styles/global.css"; - -// Full Astro Component Syntax: -// https://docs.astro.build/core-concepts/astro-components/ --- <html lang="en"> diff --git a/packages/astro/test/fixtures/tailwindcss/src/pages/markdown-page.md b/packages/astro/test/fixtures/tailwindcss/src/pages/markdown-page.md new file mode 100644 index 000000000..e4c6b6bc9 --- /dev/null +++ b/packages/astro/test/fixtures/tailwindcss/src/pages/markdown-page.md @@ -0,0 +1,11 @@ +--- +title: "Markdown + Tailwind" +setup: | + import Button from '../components/Button.astro'; + import Complex from '../components/Complex.astro'; +--- + +<div class="grid place-items-center h-screen content-center"> + <Button>Tailwind Button in Markdown!</Button> + <Complex /> +</div>
\ No newline at end of file diff --git a/packages/astro/test/fixtures/tailwindcss/src/styles/global.css b/packages/astro/test/fixtures/tailwindcss/src/styles/global.css deleted file mode 100644 index b5c61c956..000000000 --- a/packages/astro/test/fixtures/tailwindcss/src/styles/global.css +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; diff --git a/packages/astro/test/tailwindcss.test.js b/packages/astro/test/tailwindcss.test.js index 2b5d202d4..957b3f1a2 100644 --- a/packages/astro/test/tailwindcss.test.js +++ b/packages/astro/test/tailwindcss.test.js @@ -55,6 +55,14 @@ describe('Tailwind', () => { expect(button.hasClass('w-10/12'), 'solidus').to.be.true; expect(button.hasClass('2xl:w-[80%]'), 'complex class').to.be.true; }); + + it('handles Markdown pages', async () => { + const html = await fixture.readFile('/markdown-page/index.html'); + const $ = cheerio.load(html); + const bundledCSSHREF = $('link[rel=stylesheet][href^=/assets/]').attr('href'); + const bundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/')); + expect(bundledCSS, 'includes used component classes').to.match(/\.bg-purple-600{/); + }); }); // with "build" handling CSS checking, the dev tests are mostly testing the paths resolve in dev @@ -73,8 +81,8 @@ describe('Tailwind', () => { }); it('resolves CSS in src/styles', async () => { - const href = $(`link[href$="/src/styles/global.css"]`).attr('href'); - const res = await fixture.fetch(href); + const bundledCSSHREF = $('link[rel=stylesheet]').attr('href'); + const res = await fixture.fetch(bundledCSSHREF); expect(res.status).to.equal(200); const text = await res.text(); |