API
@plumeria/unplugin
Install and configure the bundler plugin behind every integration except Next.js.
@plumeria/unplugin is a general-purpose bundler plugin for Plumeria.
Installation
npm i -D @plumeria/unpluginyarn add -D @plumeria/unpluginpnpm i -D @plumeria/unpluginConfiguration
import plumeria from '@plumeria/unplugin';
export default {
plugins: [
plumeria.vite({
include: ['**/*.{ts,tsx}'],
exclude: ['**/node_modules/**'],
devEmitToDisk: false,
styleProp: 'sx',
}),
],
};Options
include: The path to the file to be converted.exclude: The path to the file to be excluded from conversion.devEmitToDisk: Whether to write styles as actual files (zero-virtual.css) instead of virtual modules in development mode. The default isfalse.styleProp: The JSX prop that carries styles. The default is'classStyle'.withoutLogicalProperties: Rejects the logical name of a property that also has a physical name. The default isfalse.withoutPhysicalProperties: Rejects the physical name of a property that also has a logical name. The default isfalse.lint: Lints the project with@plumeria/eslint-pluginduring a production build, on every bundler except esbuild and Bun. The default istrue.
interface PluginOptions {
include?: string | RegExp | Array<string | RegExp>;
exclude?: string | RegExp | Array<string | RegExp>;
devEmitToDisk?: boolean;
styleProp?: string;
withoutLogicalProperties?: boolean | { sizes?: boolean };
withoutPhysicalProperties?: boolean | { sizes?: boolean };
lint?: boolean;
}styleProp
Renaming the prop takes two steps, and they have to agree. Tell the plugin:
plumeria.vite({ styleProp: 'sx' });and declare the same name for TypeScript; see Declaring the styling prop.
If the two disagree, the prop type-checks but is never compiled away. @plumeria/eslint-plugin reads the same name from settings.plumeria.styleProp.
withoutLogicalProperties and withoutPhysicalProperties
Two names for the same box edge cannot be ranked by specificity, so a file that uses both leaves the result to source order. These options settle it by rejecting one spelling:
plumeria.vite({ withoutPhysicalProperties: true });The build fails on the first rejected property, and the message names the file
and the property to write instead. Nothing is rewritten. { sizes: true }
extends the check from the edges to the twelve axis pairs — width, height,
their min and max forms, and overflow, overscroll-behavior and
contain-intrinsic-size on both axes:
plumeria.vite({ withoutPhysicalProperties: { sizes: true } });Enabling both is a configuration error, and the direction has to match the lint
rule you run: withoutPhysicalProperties with
no-physical-properties and withoutLogicalProperties with
no-logical-properties, { sizes } included. Set opposite
directions and the rule renames a property the build then rejects.
Specificity explains why the pair cannot be ranked, and why neither spelling is rewritten for you.
lint
The lint is integrated into the build by default, so it needs no setup. A production build runs @plumeria/eslint-plugin through oxlint alongside the build and stops on any error or warning. The build waits for the lint before it exits, so a build that finishes first still fails when the lint does.
| Bundler | Lints on | Does not lint on |
|---|---|---|
| Vite | vite build | the dev server, Vitest |
| webpack, Rspack | a single build | --watch, the dev server |
| Rollup, Rolldown | a single build | --watch |
| Farm | farm build | farm start |
esbuild and Bun do not say whether a build is a one-off or a watch, so they do not lint. Run plumeria-lint there instead.
A build started from a script or a test lints as well, and a lint error ends that process. Pass lint: false where that is not wanted.
The lint runs the recommended rules, and withoutLogicalProperties or withoutPhysicalProperties adds the spelling rule that matches, { sizes } included. styleProp reaches the rules that read the styling prop. It does not read eslint.config.*.
oxlint skips the files .gitignore ignores, and runs only the Plumeria rules, not oxlint's own.
plumeria-lint -- vite build still works, and the lint runs once. To build without it:
plumeria.vite({ lint: false });