Vite: Add support for <style> tags in Astro files (#14340)

This works similar to the Vue setup. The styles that Astro will receive
might still contain Tailwind CSS APIs but since it's not picky, we can
pass that through to the regular Vite `transform` handlers for now.

This, however, will have issues like
https://github.com/tailwindlabs/tailwindcss/issues/14205. We have to fix
this together with Vue and other similar extensions later. For now, it
will break when syntax is used that lightningcss rewrites (like `@apply
text-3xl/tight;`)

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
This commit is contained in:
Philipp Spiess 2024-09-04 19:14:05 +02:00 committed by GitHub
parent 805c8a0201
commit 2dd52f5a0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 89 additions and 14 deletions

View File

@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support TypeScript for `@plugin` and `@config` files ([#14317](https://github.com/tailwindlabs/tailwindcss/pull/14317))
- Add `default` option to `@theme` to support overriding default theme values from plugins/JS config files ([#14327](https://github.com/tailwindlabs/tailwindcss/pull/14327))
- Add support for `<style>` tags in Astro files to the Vite plugin ([#14340](https://github.com/tailwindlabs/tailwindcss/pull/14340))
### Fixed

View File

@ -536,32 +536,40 @@ export async function fetchStyles(port: number, path = '/'): Promise<string> {
let index = await fetch(`http://localhost:${port}${path}`)
let html = await index.text()
let regex = /<link rel="stylesheet" href="([a-zA-Z0-9\/_\.\?=%-]+)"/g
let linkRegex = /<link rel="stylesheet" href="([a-zA-Z0-9\/_\.\?=%-]+)"/gi
let styleRegex = /<style\b[^>]*>([\s\S]*?)<\/style>/gi
let stylesheets: string[] = []
let paths: string[] = []
let match
while ((match = regex.exec(html)) !== null) {
for (let match of html.matchAll(linkRegex)) {
let path: string = match[1]
if (path.startsWith('./')) {
path = path.slice(1)
}
paths.push(path)
}
let stylesheets = await Promise.all(
paths.map(async (path) => {
let css = await fetch(`http://localhost:${port}${path}`, {
headers: {
Accept: 'text/css',
},
})
return await css.text()
}),
stylesheets.push(
...(await Promise.all(
paths.map(async (path) => {
let css = await fetch(`http://localhost:${port}${path}`, {
headers: {
Accept: 'text/css',
},
})
return await css.text()
}),
)),
)
for (let match of html.matchAll(styleRegex)) {
stylesheets.push(match[1])
}
return stylesheets.reduce((acc, css) => {
return acc + '\n' + css
})
}, '')
}
async function gracefullyRemove(dir: string) {

View File

@ -0,0 +1,63 @@
import { expect } from 'vitest'
import { candidate, fetchStyles, html, json, retryAssertion, test, ts } from '../utils'
test.debug(
'dev mode',
{
fs: {
'package.json': json`
{
"type": "module",
"dependencies": {
"astro": "^4.15.2",
"@tailwindcss/vite": "workspace:^",
"tailwindcss": "workspace:^"
}
}
`,
'astro.config.mjs': ts`
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'astro/config'
// https://astro.build/config
export default defineConfig({
vite: {
plugins: [tailwindcss()],
},
})
`,
'src/pages/index.astro': html`
<div class="underline">Hello, world!</div>
<style is:global>
@import 'tailwindcss';
</style>
`,
},
},
async ({ fs, spawn, getFreePort }) => {
let port = await getFreePort()
await spawn(`pnpm astro dev --port ${port}`)
await retryAssertion(async () => {
let css = await fetchStyles(port)
expect(css).toContain(candidate`underline`)
})
await fs.write(
'src/pages/index.astro',
html`
<div class="underline font-bold">Hello, world!</div>
<style is:global>
@import 'tailwindcss';
</style>
`,
)
await retryAssertion(async () => {
let css = await fetchStyles(port)
expect(css).toContain(candidate`underline`)
expect(css).toContain(candidate`font-bold`)
})
},
)

View File

@ -262,7 +262,10 @@ function getExtension(id: string) {
function isPotentialCssRootFile(id: string) {
let extension = getExtension(id)
let isCssFile = extension === 'css' || (extension === 'vue' && id.includes('&lang.css'))
let isCssFile =
extension === 'css' ||
(extension === 'vue' && id.includes('&lang.css')) ||
(extension === 'astro' && id.includes('&lang.css'))
return isCssFile
}