diff --git a/.changeset/four-terms-bathe.md b/.changeset/four-terms-bathe.md new file mode 100644 index 00000000..1e0267db --- /dev/null +++ b/.changeset/four-terms-bathe.md @@ -0,0 +1,6 @@ +--- +"vinxi": patch +"solid-ssr-basic": patch +--- + +(feat): css modules support diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index e0313588..03063a21 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -12,12 +12,19 @@ jobs: autofix: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + uses: actions/checkout@v2 + + - uses: pnpm/action-setup@v2.2.2 + with: + version: 8 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 with: - node-version: 18 + node-version: ${{ matrix.node-version }} cache: "pnpm" - - run: pnpm install + - name: Install dependencies + run: pnpm install - uses: autofix-ci/action@bee19d72e71787c12ca0f29de72f2833e437e4c9 with: commit-message: "chore: apply automated fixes" diff --git a/examples/solid/ssr/basic/app/Counter.module.css b/examples/solid/ssr/basic/app/Counter.module.css new file mode 100644 index 00000000..54b8d113 --- /dev/null +++ b/examples/solid/ssr/basic/app/Counter.module.css @@ -0,0 +1,3 @@ +.button { + @apply bg-red-500; +} diff --git a/examples/solid/ssr/basic/app/Counter.tsx b/examples/solid/ssr/basic/app/Counter.tsx index c16c52c6..8e1ca751 100644 --- a/examples/solid/ssr/basic/app/Counter.tsx +++ b/examples/solid/ssr/basic/app/Counter.tsx @@ -1,10 +1,15 @@ import { createSignal } from "solid-js"; +import styles from "./Counter.module.css"; + export function Counter() { const [count, setCount] = createSignal(0); return ( <> - diff --git a/packages/vinxi/lib/manifest/collect-styles.js b/packages/vinxi/lib/manifest/collect-styles.js index f234fb8f..270984d9 100644 --- a/packages/vinxi/lib/manifest/collect-styles.js +++ b/packages/vinxi/lib/manifest/collect-styles.js @@ -49,7 +49,6 @@ async function getViteModuleNode(vite, file, ssr) { node = await vite.moduleGraph.getModuleById(nodePath); } - try { if (!node.transformResult && !ssr) { await vite.transformRequest(nodePath); @@ -113,12 +112,6 @@ async function findDeps(vite, node, deps, ssr) { await Promise.all(branches); } -// Vite doesn't expose this so we just copy the list for now -// https://github.com/vitejs/vite/blob/3edd1af56e980aef56641a5a51cf2932bb580d41/packages/vite/src/node/plugins/css.ts#L96 -const STYLE_ASSET_REGEX = /\.(css|less|sass|scss|styl|stylus|pcss|postcss)$/; -const MODULE_STYLE_ASSET_REGEX = - /\.module\.(css|less|sass|scss|styl|stylus|pcss|postcss)$/; - /** * * @param {import('vite').ViteDevServer} vite @@ -141,6 +134,17 @@ async function findDependencies(vite, match, ssr) { return deps; } +// Vite doesn't expose these so we just copy the list for now +// https://github.com/vitejs/vite/blob/d6bde8b03d433778aaed62afc2be0630c8131908/packages/vite/src/node/constants.ts#L49C23-L50 +const cssFileRegExp = + /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/; +// https://github.com/vitejs/vite/blob/d6bde8b03d433778aaed62afc2be0630c8131908/packages/vite/src/node/plugins/css.ts#L160 +const cssModulesRegExp = new RegExp(`\\.module${cssFileRegExp.source}`); + +const isCssFile = (/** @type {string} */ file) => cssFileRegExp.test(file); +export const isCssModulesFile = (/** @type {string} */ file) => + cssModulesRegExp.test(file); + /** * * @param {import('vite').ViteDevServer} vite @@ -155,14 +159,14 @@ async function findStylesInModuleGraph(vite, match, ssr) { const parsed = new URL(dep.url, "http://localhost/"); const query = parsed.searchParams; - if (STYLE_ASSET_REGEX.test(dep.file ?? "")) { + if (isCssFile(dep.url ?? "")) { try { const mod = await vite.ssrLoadModule(dep.url); - // if (module_STYLE_ASSET_REGEX.test(dep.file)) { - // styles[dep.url] = env.cssModules?.[dep.file]; - // } else { - styles[join(vite.config.root, dep.url)] = mod.default; - // } + if (isCssModulesFile(dep.file)) { + styles[join(vite.config.root, dep.url)] = vite.cssModules?.[dep.file]; + } else { + styles[join(vite.config.root, dep.url)] = mod.default; + } } catch { // this can happen with dynamically imported modules, I think // because the Vite module graph doesn't distinguish between diff --git a/packages/vinxi/lib/manifest/dev-server-manifest.js b/packages/vinxi/lib/manifest/dev-server-manifest.js index 8f47f53a..145a9c4a 100644 --- a/packages/vinxi/lib/manifest/dev-server-manifest.js +++ b/packages/vinxi/lib/manifest/dev-server-manifest.js @@ -37,6 +37,25 @@ export function createDevManifest(app) { } const viteServer = router.internals.devServer; + + async function viteAssets(paths, ssr) { + invariant(viteServer, "Vite server expected"); + return Object.entries( + await findStylesInModuleGraph( + viteServer, + paths.filter(Boolean), + ssr, + ), + ).map(([key, value]) => ({ + tag: "style", + attrs: { + type: "text/css", + key, + "data-vite-dev-id": key, + }, + children: value, + })); + } return { json() { return {}; @@ -192,26 +211,20 @@ export function createDevManifest(app) { async assets() { return [ ...(viteServer - ? Object.entries( - await findStylesInModuleGraph( - viteServer, + ? ( + await viteAssets( [ absolutePath.endsWith(".ts") && router.mode === "spa" ? undefined : absolutePath, - ].filter(Boolean), + ], false, - ), - ).map(([key, value]) => ({ - tag: "style", - attrs: { - type: "text/css", - key, - "data-vite-dev-id": key, - }, - children: value, - })) + ) + ).filter( + (asset) => + !asset.attrs.key.includes("vinxi-devtools"), + ) : []), ...(isHandler ? [ @@ -242,21 +255,10 @@ export function createDevManifest(app) { async assets() { return [ ...(viteServer - ? Object.entries( - await findStylesInModuleGraph( - viteServer, - [input], - true, - ), - ).map(([key, value]) => ({ - tag: "style", - attrs: { - type: "text/css", - key, - "data-vite-dev-id": key, - }, - children: value, - })) + ? (await viteAssets([input], true)).filter( + (asset) => + !asset.attrs.key.includes("vinxi-devtools"), + ) : []), ]; }, diff --git a/packages/vinxi/lib/plugins/css.js b/packages/vinxi/lib/plugins/css.js index 4fb117fc..05e766f3 100644 --- a/packages/vinxi/lib/plugins/css.js +++ b/packages/vinxi/lib/plugins/css.js @@ -1,11 +1,15 @@ +import { isCssModulesFile } from "../manifest/collect-styles.js"; + export function css() { /** @type {import('vite').ViteDevServer} */ let viteServer; + let cssModules = {}; /** @type {import('../vite-dev.d.ts').Plugin} */ const plugin = { name: "vinxi:css-hmr", configureServer(dev) { viteServer = dev; + viteServer.cssModules = cssModules; }, async handleHotUpdate({ file, read, server, modules }) { if (file.endsWith(".css")) { @@ -26,6 +30,11 @@ export function css() { return []; } }, + transform(code, id) { + if (isCssModulesFile(id)) { + cssModules[id] = code; + } + }, }; return plugin;