next-yak
Version:
next-yak is a CSS-in-JS solution tailored for Next.js that seamlessly combines the expressive power of styled-components syntax with efficient build-time extraction of CSS using Next.js's built-in CSS configuration
93 lines (87 loc) • 3.23 kB
text/typescript
import { fileURLToPath } from "node:url";
import type { RsbuildPlugin } from "@rsbuild/core";
import {
buildYakPluginOptions,
resolveYakContext,
type YakConfigOptions,
} from "../withYak/index.js";
// Rspack's loader context is webpack-compatible but does NOT implement
// `this.loadModule` (the webpack API the webpack-loader relies on). It does
// provide `this.fs.readFile`, `this.getResolve` and `this.addDependency` — which
// is exactly what the turbopack loader uses — so the turbopack loader runs as-is
// on Rspack. The compiled loader lives next to this file under dist/
// (dist/rsbuild/index.js and dist/loaders/turbo-loader.cjs are siblings).
const rspackLoaderPath = fileURLToPath(new URL("../loaders/turbo-loader.cjs", import.meta.url));
/**
* Rsbuild plugin for next-yak.
*
* Wires the yak-swc transform as an Rspack `pre` loader that runs the
* compile-time CSS extraction in-loader and inlines the result as a
* `data:text/css;base64,` import (the same strategy as the Turbopack loader).
* Running it as a `pre` loader keeps yak-swc out of Rsbuild's own
* `builtin:swc-loader`, so it composes cleanly with that pass (TS/JSX, React
* Fast Refresh, and the React Compiler).
*
* @example
* ```ts
* // rsbuild.config.ts
* import { defineConfig } from "@rsbuild/core";
* import { pluginReact } from "@rsbuild/plugin-react";
* import { pluginYak } from "next-yak/rsbuild";
*
* export default defineConfig({
* plugins: [pluginReact(), pluginYak()],
* });
* ```
*/
export function pluginYak(yakOptions: YakConfigOptions = {}): RsbuildPlugin {
return {
name: "next-yak",
setup(api) {
api.modifyRspackConfig((config) => {
const rootContext = api.context.rootPath;
const yakPluginOptions = {
...buildYakPluginOptions(yakOptions, rootContext),
importMode: {
value: "data:text/css;base64,",
transpilation: "Css",
encoding: "Base64",
},
};
config.module ??= {};
config.module.rules ??= [];
config.module.rules.push(
{
test: /\.(c|m)?[jt]sx?$/,
exclude: /[\\/]node_modules[\\/]/,
enforce: "pre",
use: [
{
loader: rspackLoaderPath,
options: { yakOptions, yakPluginOptions },
},
],
},
// The extracted CSS is inlined as a `data:text/css;base64,` import.
// Rspack parses unknown data URIs as JavaScript, so tell it these are
// CSS modules. `css/auto` keeps Rspack's native CSS handling for them.
{
mimetype: "text/css",
type: "css/auto",
},
);
// Custom context file (`yak.context.{ts,tsx,js,jsx}`) for server-aware
// theming, mirroring the webpack/turbopack integrations.
const yakContext = resolveYakContext(yakOptions.contextPath, rootContext);
if (yakContext) {
config.resolve ??= {};
config.resolve.alias = {
...config.resolve.alias,
"next-yak/context/baseContext": yakContext,
};
}
});
},
};
}
export type { YakConfigOptions };