From d59f1b3e5d8c1489f0295e8b3ff18e7058ff2f8f Mon Sep 17 00:00:00 2001 From: philipp-spiess <458591+philipp-spiess@users.noreply.github.com> Date: Tue, 22 Oct 2024 16:03:07 +0000 Subject: [PATCH] Vite: Fix issues when loading files via static asset queries (#14716) Fixes: #14558 This PR fixes an issue where our Vite plugin would crash when trying to load stylesheets via certain static asset query parameters: ```ts import raw from './style.css?raw' import url from './style.css?url' ``` The proper behavior for our extension is to _not touch these file at all_. The `?raw` identifier should never transform anything and the `?url` one will emit a module which points to the asset URL. However, if that URL is loaded as a stylesheet, another transform hook is called and the file is properly transformed. I verified this in the Vite setup and have added an integration test ensuring these two features work as expected. I've also greatly reduced the complexity of the Vite playground to make it easier to set up examples like this in the future. --- CHANGELOG.md | 1 + integrations/vite/index.test.ts | 61 +++++++++++++++++++++ packages/@tailwindcss-vite/src/index.ts | 11 +++- playgrounds/vite/package.json | 3 +- playgrounds/vite/src/animate.js | 1 - playgrounds/vite/src/app.tsx | 4 -- playgrounds/vite/src/bar.tsx | 7 --- playgrounds/vite/src/foo.tsx | 10 ---- playgrounds/vite/src/forms.js | 1 - playgrounds/vite/src/{app.css => index.css} | 1 - playgrounds/vite/src/index.html | 1 - playgrounds/vite/src/main.tsx | 2 + playgrounds/vite/src/plugin.js | 4 -- playgrounds/vite/src/typography.js | 1 - pnpm-lock.yaml | 55 +------------------ 15 files changed, 75 insertions(+), 88 deletions(-) delete mode 100644 playgrounds/vite/src/animate.js delete mode 100644 playgrounds/vite/src/bar.tsx delete mode 100644 playgrounds/vite/src/foo.tsx delete mode 100644 playgrounds/vite/src/forms.js rename playgrounds/vite/src/{app.css => index.css} (50%) delete mode 100644 playgrounds/vite/src/plugin.js delete mode 100644 playgrounds/vite/src/typography.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 13acdc3183d1..43725914687e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ensure color opacity modifiers work with OKLCH colors ([#14741](https://github.com/tailwindlabs/tailwindcss/pull/14741)) - Ensure changes to the input CSS file result in a full rebuild ([#14744](https://github.com/tailwindlabs/tailwindcss/pull/14744)) - Add `postcss` as a dependency of `@tailwindcss/postcss` ([#14750](https://github.com/tailwindlabs/tailwindcss/pull/14750)) +- Ensure loading stylesheets via the `?raw` and `?url` static asset query works when using the Vite plugin ([#14716](https://github.com/tailwindlabs/tailwindcss/pull/14716)) - _Upgrade (experimental)_: Migrate `flex-grow` to `grow` and `flex-shrink` to `shrink` ([#14721](https://github.com/tailwindlabs/tailwindcss/pull/14721)) - _Upgrade (experimental)_: Minify arbitrary values when printing candidates ([#14720](https://github.com/tailwindlabs/tailwindcss/pull/14720)) - _Upgrade (experimental)_: Ensure legacy theme values ending in `1` (like `theme(spacing.1)`) are correctly migrated to custom properties ([#14724](https://github.com/tailwindlabs/tailwindcss/pull/14724)) diff --git a/integrations/vite/index.test.ts b/integrations/vite/index.test.ts index f56575c1a8ec..89814ccc6168 100644 --- a/integrations/vite/index.test.ts +++ b/integrations/vite/index.test.ts @@ -504,3 +504,64 @@ test( }) }, ) + +test( + `does not interfere with ?raw and ?url static asset handling`, + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^5.3.5" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss()], + }) + `, + 'index.html': html` +
+ + + `, + 'src/index.js': js` + import url from './index.css?url' + import raw from './index.css?raw' + `, + 'src/index.css': css`@import 'tailwindcss';`, + }, + }, + async ({ spawn, getFreePort }) => { + let port = await getFreePort() + await spawn(`pnpm vite dev --port ${port}`) + + await retryAssertion(async () => { + // We have to load the .js file first so that the static assets are + // resolved + await fetch(`http://localhost:${port}/src/index.js`).then((r) => r.text()) + + let [raw, url] = await Promise.all([ + fetch(`http://localhost:${port}/src/index.css?raw`).then((r) => r.text()), + fetch(`http://localhost:${port}/src/index.css?url`).then((r) => r.text()), + ]) + + expect(firstLine(raw)).toBe(`export default "@import 'tailwindcss';"`) + expect(firstLine(url)).toBe(`export default "/src/index.css"`) + }) + }, +) + +function firstLine(str: string) { + return str.split('\n')[0] +} diff --git a/packages/@tailwindcss-vite/src/index.ts b/packages/@tailwindcss-vite/src/index.ts index abb13a613f26..9571342cdbda 100644 --- a/packages/@tailwindcss-vite/src/index.ts +++ b/packages/@tailwindcss-vite/src/index.ts @@ -5,6 +5,8 @@ import { Features, transform } from 'lightningcss' import path from 'path' import type { Plugin, ResolvedConfig, Rollup, Update, ViteDevServer } from 'vite' +const SPECIAL_QUERY_RE = /[?&](raw|url)\b/ + export default function tailwindcss(): Plugin[] { let servers: ViteDevServer[] = [] let config: ResolvedConfig | null = null @@ -261,9 +263,12 @@ function getExtension(id: string) { function isPotentialCssRootFile(id: string) { let extension = getExtension(id) let isCssFile = - extension === 'css' || - (extension === 'vue' && id.includes('&lang.css')) || - (extension === 'astro' && id.includes('&lang.css')) + (extension === 'css' || + (extension === 'vue' && id.includes('&lang.css')) || + (extension === 'astro' && id.includes('&lang.css'))) && + // Don't intercept special static asset resources + !SPECIAL_QUERY_RE.test(id) + return isCssFile } diff --git a/playgrounds/vite/package.json b/playgrounds/vite/package.json index 846b96252efd..a5a953a14cc8 100644 --- a/playgrounds/vite/package.json +++ b/playgrounds/vite/package.json @@ -19,7 +19,6 @@ "@types/react": "^18.3.9", "@types/react-dom": "^18.3.1", "bun": "^1.1.29", - "vite": "catalog:", - "vite-plugin-handlebars": "^2.0.0" + "vite": "catalog:" } } diff --git a/playgrounds/vite/src/animate.js b/playgrounds/vite/src/animate.js deleted file mode 100644 index 0a5617399262..000000000000 --- a/playgrounds/vite/src/animate.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('tailwindcss-animate') diff --git a/playgrounds/vite/src/app.tsx b/playgrounds/vite/src/app.tsx index 285bd41439de..8ec50298951f 100644 --- a/playgrounds/vite/src/app.tsx +++ b/playgrounds/vite/src/app.tsx @@ -1,11 +1,7 @@ -import { Foo } from './foo' - export function App() { return (