@plumeria/unplugin

View as Markdown

@plumeria/unplugin is a general-purpose bundler plugin for Plumeria.

Installation

Terminal
npm i -D @plumeria/unplugin
Terminal
yarn add -D @plumeria/unplugin
Terminal
pnpm i -D @plumeria/unplugin

Configuration

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 is false.
  • 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 is false.
  • withoutPhysicalProperties: Rejects the physical name of a property that also has a logical name. The default is false.
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 };
}

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. @plumeria/core ships no prop declaration of its own, so add one file to your project:

plumeria.d.ts
import type { Style } from '@plumeria/core';

declare global {
  namespace React {
    interface HTMLAttributes<T> {
      sx?: Style;
    }
    interface SVGAttributes<T> {
      sx?: Style;
    }
  }
}

For the default name, reference the declaration that ships with the package instead:

plumeria.d.ts
/// <reference types="@plumeria/core/class-style" />

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.

Driving the transform

@plumeria/unplugin/factory exposes the plugin behind every bundler entry. transform takes a source string and hands back the rewritten code; load hands back the stylesheet it emitted:

const { unpluginFactory } = require('@plumeria/unplugin/factory');

const plugin = unpluginFactory();
const { code } = await plugin.transform(source, id);
const css = plugin.load(code.match(/import "(.+\.zero\.css)"/)[1]);

No bundler, no DOM and no runtime resolution of @plumeria/core is involved, so it runs anywhere plain JavaScript does. The hooks are part of the published surface. See Testing.

On this page