157 lines
3.8 KiB
TypeScript
157 lines
3.8 KiB
TypeScript
import hash from "@emotion/hash";
|
|
import type { Config } from "tailwindcss";
|
|
import type { SetRequired } from "type-fest";
|
|
import type postcss from "postcss";
|
|
import { memoize } from "lodash";
|
|
import { type ClassNameCollector, babelTailwind } from "./babel-tailwind";
|
|
import { esbuildPlugin } from "./esbuild-postcss";
|
|
import { vitePlugin } from "./vite-plugin";
|
|
import { type StyleMap, createPostCSS } from "./shared";
|
|
|
|
export { babelPlugin } from "./esbuild-babel";
|
|
export { createPostCSS } from "./shared";
|
|
|
|
type GetClassName = (className: string) => string;
|
|
|
|
/**
|
|
* Tagged template macro function for Tailwind classes
|
|
* @example "tw" => tw`p-2 text-center`
|
|
*/
|
|
export type TaggedTailwindFunction = (strings: TemplateStringsArray) => string;
|
|
|
|
export interface TailwindPluginOptions {
|
|
/**
|
|
* Tailwind CSS configuration
|
|
*/
|
|
tailwindConfig?: Omit<Config, "content">;
|
|
|
|
/**
|
|
* Prefix to all Tailwind stylesheets
|
|
*
|
|
* @example
|
|
* tailwind base;
|
|
* tailwind components;
|
|
* tailwind utilities;
|
|
*/
|
|
prefix?: string;
|
|
|
|
/**
|
|
* Additional PostCSS plugins (optional)
|
|
*/
|
|
postCSSPlugins?: postcss.AcceptedPlugin[];
|
|
|
|
/**
|
|
* Attribute to use for tailwind classes in JSX
|
|
* @default "css"
|
|
*/
|
|
jsxAttributeName?: string;
|
|
|
|
/**
|
|
* What to do with the original attribute after processing
|
|
* @default "delete"
|
|
*/
|
|
jsxAttributeAction?: "delete" | "preserve" | ["rename", string];
|
|
|
|
/**
|
|
* Tagged template macro function to use for Tailwind classes
|
|
* @default "tw"
|
|
* @example
|
|
* declare const tw: TaggedTailwindFunction;
|
|
* "tw" => tw`p-2 text-center`
|
|
*/
|
|
taggedTemplateName?: string | undefined;
|
|
|
|
/**
|
|
* The prefix to use for the generated class names.
|
|
* @default className => `tw-${hash(className)}`
|
|
*/
|
|
getClassName?: GetClassName;
|
|
|
|
/**
|
|
* Preferred library for classnames
|
|
*/
|
|
clsx: "clsx" | "classnames" | "emotion";
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
styleMap?: StyleMap;
|
|
|
|
/**
|
|
* Custom CSS compile function
|
|
* @example
|
|
* async css => (await postcss.process(css, { plugins: [tailwindcss()] })).css
|
|
*/
|
|
compile?(css: string): Promise<string>;
|
|
|
|
/**
|
|
* Using the vite plugin?
|
|
*/
|
|
vite?: boolean;
|
|
}
|
|
|
|
export type ResolveTailwindOptions = SetRequired<
|
|
TailwindPluginOptions,
|
|
| "clsx"
|
|
| "jsxAttributeAction"
|
|
| "jsxAttributeName"
|
|
| "postCSSPlugins"
|
|
| "styleMap"
|
|
| "tailwindConfig"
|
|
| "taggedTemplateName"
|
|
>;
|
|
|
|
/**
|
|
* Hashes and prefixes a string of Tailwind class names.
|
|
* @example getClassName("p-2 text-center") // "tw-1r6fxxz"
|
|
*/
|
|
export const getClassName: GetClassName = cls => "tw-" + hash(cls);
|
|
|
|
/**
|
|
* Main entry. Returns the plugins and utilities for processing Tailwind
|
|
* classNames in JS.
|
|
*
|
|
* @example
|
|
* import { build } from "esbuild";
|
|
* import getTailwindPlugins, { babelPlugin } from "babel-tailwind";
|
|
*
|
|
* const tailwind = getTailwindPlugins(options);
|
|
* const result = await build({
|
|
* plugins: [
|
|
* babelPlugin({ getPlugins: () => [tailwind.babel] }),
|
|
* tailwind.esbuild,
|
|
* ],
|
|
* });
|
|
*/
|
|
export function getTailwindPlugins(options: TailwindPluginOptions) {
|
|
const resolvedOptions: ResolveTailwindOptions = {
|
|
getClassName,
|
|
jsxAttributeAction: "delete",
|
|
jsxAttributeName: "css",
|
|
postCSSPlugins: [],
|
|
styleMap: new Map(),
|
|
taggedTemplateName: undefined,
|
|
tailwindConfig: {},
|
|
...options,
|
|
};
|
|
|
|
const getCompiler = () => createPostCSS(resolvedOptions);
|
|
|
|
const { styleMap } = resolvedOptions;
|
|
const compile = options.compile ?? memoize(getCompiler());
|
|
|
|
return {
|
|
compile,
|
|
babel: (onCollect?: ClassNameCollector) => babelTailwind(resolvedOptions, onCollect),
|
|
esbuild: () => esbuildPlugin(styleMap, compile),
|
|
/** Requires `options.vite` to be `true`. */
|
|
vite: () => vitePlugin(styleMap, compile),
|
|
styleMap,
|
|
options,
|
|
getCompiler,
|
|
[Symbol.dispose]() {
|
|
styleMap.clear();
|
|
},
|
|
};
|
|
}
|