Update
This commit is contained in:
parent
2b3812b2ba
commit
d4404f7ae2
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@aet/tailwind",
|
||||
"version": "0.0.1-beta.30",
|
||||
"version": "0.0.1-beta.33",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "./scripts/index.ts",
|
||||
|
@ -38,13 +38,13 @@ await Promise.all([
|
||||
2
|
||||
)
|
||||
),
|
||||
Bun.write(`dist/base.d.ts`, `/**\n * \`@tailwind base\` component.\n */\nexport {};`),
|
||||
]);
|
||||
|
||||
await Promise.all([
|
||||
fs.copyFile("README.md", "dist/README.md"),
|
||||
fs.copyFile("LICENSE.md", "dist/LICENSE.md"),
|
||||
fs.copyFile("src/macro.d.ts", "dist/macro.d.ts"),
|
||||
Bun.write(`dist/base.d.ts`, `/**\n * \`@tailwind base\` component.\n */\nexport {};`),
|
||||
]);
|
||||
|
||||
process.exit(0);
|
||||
|
@ -42,4 +42,51 @@ describe("babel-tailwind", () => {
|
||||
);
|
||||
expect(files.js.text).toContain(`style: ${clsName}`);
|
||||
});
|
||||
|
||||
it("supports .hover, .focus, .active, .group-hover, .group-focus, .group-active", async () => {
|
||||
const { files } = await compileESBuild({
|
||||
clsx: "emotion",
|
||||
expectFiles: 1,
|
||||
javascript: `
|
||||
import { tws } from "@aet/tailwind/macro";
|
||||
|
||||
export const style = [
|
||||
tws.hover\`font-semibold\`,
|
||||
tws.focus\`font-bold\`,
|
||||
tws.active\`font-light\`,
|
||||
tws.hover.active\`p-2\`,
|
||||
];
|
||||
`,
|
||||
});
|
||||
|
||||
const semibold = getClassName("hover:font-semibold").replace(/^tw-/, "tw_");
|
||||
const bold = getClassName("focus:font-bold").replace(/^tw-/, "tw_");
|
||||
const light = getClassName("active:font-light").replace(/^tw-/, "tw_");
|
||||
const p = getClassName("active:hover:p-2").replace(/^tw-/, "tw_");
|
||||
|
||||
expect(files.js.text).toContain(
|
||||
[
|
||||
`var ${bold} = {`,
|
||||
' "&:focus": {',
|
||||
' fontWeight: "700"',
|
||||
" }",
|
||||
"};",
|
||||
`var ${light} = {`,
|
||||
' "&:active": {',
|
||||
' fontWeight: "300"',
|
||||
" }",
|
||||
"};",
|
||||
`var ${p} = {`,
|
||||
' "&:hover:active": {',
|
||||
' padding: "0.5rem"',
|
||||
" }",
|
||||
"};",
|
||||
`var ${semibold} = {`,
|
||||
' "&:hover": {',
|
||||
' fontWeight: "600"',
|
||||
" }",
|
||||
"};",
|
||||
].join("\n")
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -51,4 +51,22 @@ describe("babel-tailwind", () => {
|
||||
].join("\n")
|
||||
);
|
||||
});
|
||||
|
||||
it("passes through `group` className", async () => {
|
||||
const { files } = await compileESBuild({
|
||||
clsx: "emotion",
|
||||
expectFiles: 2,
|
||||
javascript: `
|
||||
import { tw } from "@aet/tailwind/macro";
|
||||
|
||||
export default tw\`group hover:text-center\`;
|
||||
`,
|
||||
});
|
||||
|
||||
const clsName = getClassName("group hover:text-center");
|
||||
expect(files.js.text).toContain(`"${clsName} group"`);
|
||||
expect(files.css.text).toMatch(
|
||||
[`.${clsName}:hover {`, " text-align: center;", "}"].join("\n")
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -42,8 +42,6 @@ export function getBuild(name: string) {
|
||||
}) {
|
||||
const tailwind = getTailwindPlugins({
|
||||
tailwindConfig: {},
|
||||
macroFunction: "tw",
|
||||
macroStyleFunction: "tws",
|
||||
...options,
|
||||
});
|
||||
const result = await esbuild.build({
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { basename, dirname, extname, join } from "node:path";
|
||||
import type babel from "@babel/core";
|
||||
import type b from "@babel/core";
|
||||
import hash from "@emotion/hash";
|
||||
import { isPlainObject } from "lodash";
|
||||
import invariant from "tiny-invariant";
|
||||
@ -8,23 +8,25 @@ import { type SourceLocation, type StyleMapEntry, macroName } from "./shared";
|
||||
import { type ResolveTailwindOptions, getClassName } from "./index";
|
||||
|
||||
export type ClassNameCollector = (path: string, entries: StyleMapEntry[]) => void;
|
||||
type BabelTypes = typeof babel.types;
|
||||
type BabelTypes = typeof b.types;
|
||||
type Type = "css" | "js";
|
||||
|
||||
export function babelTailwind(
|
||||
{
|
||||
options: ResolveTailwindOptions,
|
||||
onCollect: ClassNameCollector | undefined
|
||||
) {
|
||||
const {
|
||||
styleMap,
|
||||
clsx,
|
||||
getClassName: getClass = getClassName,
|
||||
jsxAttributeAction = "delete",
|
||||
jsxAttributeName = "css",
|
||||
vite,
|
||||
}: ResolveTailwindOptions,
|
||||
onCollect: ClassNameCollector | undefined
|
||||
) {
|
||||
type BabelPluginState = ReturnType<typeof getState>;
|
||||
vite: bustCache,
|
||||
} = options;
|
||||
|
||||
function getState(path: NodePath<t.Program>, state: babel.PluginPass, t: BabelTypes) {
|
||||
type BabelPluginUtils = ReturnType<typeof getUtils>;
|
||||
|
||||
function getUtils(path: NodePath<t.Program>, state: b.PluginPass, t: BabelTypes) {
|
||||
let cx: t.Identifier;
|
||||
let styleImport: t.Identifier;
|
||||
|
||||
@ -58,7 +60,15 @@ export function babelTailwind(
|
||||
}
|
||||
},
|
||||
|
||||
replaceWithImport(type: Type, path: NodePath, className: string) {
|
||||
replaceWithImport({
|
||||
type,
|
||||
path,
|
||||
className,
|
||||
}: {
|
||||
type: Type;
|
||||
path: NodePath;
|
||||
className: string;
|
||||
}) {
|
||||
if (type === "css") {
|
||||
path.replaceWith(t.stringLiteral(className));
|
||||
} else {
|
||||
@ -86,7 +96,7 @@ export function babelTailwind(
|
||||
const cssName = basename(filename, extname(filename)) + ".css";
|
||||
const path = join(dirname(filename), cssName);
|
||||
const value = Array.from(cssMap.values());
|
||||
const importee = `tailwind:./${cssName}` + getSuffix(vite, value);
|
||||
const importee = `tailwind:./${cssName}` + getSuffix(bustCache, value);
|
||||
|
||||
node.body.unshift(t.importDeclaration([], t.stringLiteral(importee)));
|
||||
|
||||
@ -98,7 +108,7 @@ export function babelTailwind(
|
||||
const jsName = basename(filename, extname(filename)) + ".tailwindStyle.js";
|
||||
const path = join(dirname(filename), jsName);
|
||||
const value = Array.from(jsMap.values());
|
||||
const importee = `tailwind:./${jsName}` + getSuffix(vite, value);
|
||||
const importee = `tailwind:./${jsName}` + getSuffix(bustCache, value);
|
||||
|
||||
node.body.unshift(
|
||||
t.importDeclaration(
|
||||
@ -113,23 +123,21 @@ export function babelTailwind(
|
||||
};
|
||||
}
|
||||
|
||||
return definePlugin<BabelPluginState>(({ types: t }) => ({
|
||||
return definePlugin<BabelPluginUtils>(({ types: t }) => ({
|
||||
Program: {
|
||||
enter(path, state) {
|
||||
const _ = getState(path, state, t);
|
||||
const _ = getUtils(path, state, t);
|
||||
Object.assign(state, _);
|
||||
|
||||
for (const {
|
||||
local: { parentPath: local },
|
||||
imported,
|
||||
} of getMacros(t, path, macroName)) {
|
||||
for (const { callee, imported, prefix } of getMacros(t, path, macroName).map(
|
||||
macro => mapMacro(t, macro)
|
||||
)) {
|
||||
const type = imported === "tw" ? "css" : imported === "tws" ? "js" : undefined;
|
||||
if (!type) continue;
|
||||
|
||||
if (isNodePath(local, t.isTaggedTemplateExpression)) {
|
||||
const { node } = local;
|
||||
const { tag, quasi } = node;
|
||||
if (!t.isIdentifier(tag)) continue;
|
||||
if (isNodePath(callee, t.isTaggedTemplateExpression)) {
|
||||
const { node } = callee;
|
||||
const { quasi } = node;
|
||||
|
||||
invariant(
|
||||
!quasi.expressions.length,
|
||||
@ -138,28 +146,35 @@ export function babelTailwind(
|
||||
|
||||
const value = quasi.quasis[0].value.cooked;
|
||||
if (value) {
|
||||
const trimmed = trim(value);
|
||||
const className = _.getClass(type, trimmed);
|
||||
const list = trimPrefix(value, prefix ? prefix + ":" : undefined);
|
||||
const className = _.getClass(type, list.join(" "));
|
||||
_.recordIfAbsent(type, {
|
||||
key: className,
|
||||
className: trimmed,
|
||||
classNames: list,
|
||||
location: _.sliceText(node),
|
||||
});
|
||||
_.replaceWithImport(type, local, className);
|
||||
_.replaceWithImport({
|
||||
type,
|
||||
path: callee,
|
||||
className: addIf(className, list.includes("group") && " group"),
|
||||
});
|
||||
}
|
||||
} else if (isNodePath(local, t.isCallExpression)) {
|
||||
const { node } = local;
|
||||
const { callee } = node;
|
||||
if (!t.isIdentifier(callee)) continue;
|
||||
} else if (isNodePath(callee, t.isCallExpression)) {
|
||||
const { node } = callee;
|
||||
if (!t.isIdentifier(node.callee)) continue;
|
||||
|
||||
const trimmed = local.get("arguments").flatMap(evaluateArgs).join(" ");
|
||||
const className = getClass(trimmed);
|
||||
const list = callee.get("arguments").flatMap(evaluateArgs);
|
||||
const className = getClass(list.join(" "));
|
||||
_.recordIfAbsent(type, {
|
||||
key: className,
|
||||
className: trimmed,
|
||||
classNames: list,
|
||||
location: _.sliceText(node),
|
||||
});
|
||||
_.replaceWithImport(type, local, className);
|
||||
_.replaceWithImport({
|
||||
type,
|
||||
path: callee,
|
||||
className: addIf(className, list.includes("group") && " group"),
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -190,11 +205,11 @@ export function babelTailwind(
|
||||
const { node } = path;
|
||||
const { value } = node;
|
||||
const trimmed = trim(value);
|
||||
if (trimmed) {
|
||||
const className = getClass(trimmed);
|
||||
if (trimmed.length) {
|
||||
const className = getClass(trimmed.join(" "));
|
||||
_.recordIfAbsent("css", {
|
||||
key: className,
|
||||
className: trimmed,
|
||||
classNames: trimmed,
|
||||
location: _.sliceText(node),
|
||||
});
|
||||
path.replaceWith(t.stringLiteral(className));
|
||||
@ -206,11 +221,11 @@ export function babelTailwind(
|
||||
}
|
||||
},
|
||||
ObjectExpression(path) {
|
||||
const trimmed = evaluateArgs(path).join(" ");
|
||||
const className = getClass(trimmed);
|
||||
const trimmed = evaluateArgs(path);
|
||||
const className = getClass(trimmed.join(" "));
|
||||
_.recordIfAbsent("css", {
|
||||
key: className,
|
||||
className: trimmed,
|
||||
classNames: trimmed,
|
||||
location: _.sliceText(path.node),
|
||||
});
|
||||
path.replaceWith(t.stringLiteral(className));
|
||||
@ -245,7 +260,7 @@ export function babelTailwind(
|
||||
|
||||
if (classNameAttribute) {
|
||||
const attrValue = classNameAttribute.value!;
|
||||
const wrap = (originalValue: babel.types.Expression) =>
|
||||
const wrap = (originalValue: b.types.Expression) =>
|
||||
t.callExpression(_.getCx(), [originalValue, valuePathNode]);
|
||||
|
||||
// If both are string literals, we can merge them directly here
|
||||
@ -308,7 +323,7 @@ function evaluateArgs(path: NodePath) {
|
||||
invariant(confident, "Argument cannot be statically evaluated");
|
||||
|
||||
if (typeof value === "string") {
|
||||
return [trim(value)];
|
||||
return trim(value);
|
||||
}
|
||||
|
||||
if (isPlainObject(value)) {
|
||||
@ -384,13 +399,32 @@ function getMacros(
|
||||
return macros;
|
||||
}
|
||||
|
||||
function mapMacro(t: BabelTypes, macro: ReturnType<typeof getMacros>[number]) {
|
||||
let callee = macro.local.parentPath;
|
||||
const prefix: string[] = [];
|
||||
|
||||
while (isNodePath(callee, t.isMemberExpression)) {
|
||||
invariant(t.isIdentifier(callee.node.property), "Invalid member expression");
|
||||
prefix.unshift(
|
||||
callee.node.property.name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase()
|
||||
);
|
||||
callee = callee.parentPath;
|
||||
}
|
||||
|
||||
return {
|
||||
callee,
|
||||
imported: macro.imported,
|
||||
prefix: prefix.length ? prefix.join(":") : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const definePlugin =
|
||||
<T>(fn: (runtime: typeof babel) => babel.Visitor<babel.PluginPass & T>) =>
|
||||
(runtime: typeof babel) => {
|
||||
const plugin: babel.PluginObj<babel.PluginPass & T> = {
|
||||
<T>(fn: (runtime: typeof b) => b.Visitor<b.PluginPass & T>) =>
|
||||
(runtime: typeof b) => {
|
||||
const plugin: b.PluginObj<b.PluginPass & T> = {
|
||||
visitor: fn(runtime),
|
||||
};
|
||||
return plugin as babel.PluginObj;
|
||||
return plugin as b.PluginObj;
|
||||
};
|
||||
|
||||
const extractJSXContainer = (attr: NonNullable<t.JSXAttribute["value"]>): t.Expression =>
|
||||
@ -398,13 +432,17 @@ const extractJSXContainer = (attr: NonNullable<t.JSXAttribute["value"]>): t.Expr
|
||||
|
||||
function matchPath(
|
||||
nodePath: NodePath<t.Node | null | undefined>,
|
||||
fns: (dig: (nodePath: NodePath<t.Node | null | undefined>) => void) => babel.Visitor
|
||||
fns: (dig: (nodePath: NodePath<t.Node | null | undefined>) => void) => b.Visitor
|
||||
) {
|
||||
if (!nodePath.node) return;
|
||||
const fn = fns(path => matchPath(path, fns))[nodePath.node.type] as any;
|
||||
fn?.(nodePath);
|
||||
}
|
||||
|
||||
function addIf(text: string, suffix: string | false) {
|
||||
return suffix ? text + suffix : text;
|
||||
}
|
||||
|
||||
const isNodePath = <T extends t.Node>(
|
||||
nodePath: NodePath<t.Node | null | undefined> | null,
|
||||
predicate: (node: t.Node) => node is T
|
||||
@ -413,15 +451,12 @@ const isNodePath = <T extends t.Node>(
|
||||
function getSuffix(add: boolean | undefined, entries: StyleMapEntry[]) {
|
||||
if (!add) return "";
|
||||
|
||||
const cacheKey = hash(entries.map(x => x.className).join(","));
|
||||
const cacheKey = hash(entries.map(x => x.classNames).join(","));
|
||||
return `?${cacheKey}`;
|
||||
}
|
||||
|
||||
const trim = (value: string) => value.replace(/\s+/g, " ").trim();
|
||||
const trimPrefix = (cls: string, prefix: string) =>
|
||||
trim(cls)
|
||||
.split(" ")
|
||||
.map(value => prefix + value);
|
||||
const trim = (value: string) => value.replace(/\s+/g, " ").trim().split(" ");
|
||||
const trimPrefix = (cls: string, prefix = "") => trim(cls).map(value => prefix + value);
|
||||
|
||||
const flatMapEntries = <K extends string | number, V, R>(
|
||||
map: Record<K, V>,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import cx from "clsx";
|
||||
import { type FunctionComponent, forwardRef } from "react";
|
||||
import cx from "clsx";
|
||||
|
||||
interface WithClassName<Props = object> extends FunctionComponent<Props> {
|
||||
className: string;
|
||||
@ -22,12 +22,12 @@ export const classed: {
|
||||
type: "input",
|
||||
className: PresetClassName<InputProps>,
|
||||
defaultProps?: Partial<InputProps>
|
||||
): InputProps;
|
||||
): React.FunctionComponent<InputProps>;
|
||||
<K extends keyof JSX.IntrinsicElements>(
|
||||
type: K,
|
||||
className: PresetClassName<JSX.IntrinsicElements[K]>,
|
||||
defaultProps?: Partial<JSX.IntrinsicElements[K]>
|
||||
): JSX.IntrinsicElements[K];
|
||||
): React.FunctionComponent<JSX.IntrinsicElements[K]>;
|
||||
(
|
||||
type: string,
|
||||
className: PresetClassName,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { dirname, join } from "node:path";
|
||||
import type * as esbuild from "esbuild";
|
||||
import { CssSyntaxError } from "postcss";
|
||||
import { type Compile, type StyleMap, pkgName } from "./shared";
|
||||
import { type Compile, type StyleMap, type StyleMapEntry, pkgName } from "./shared";
|
||||
import type { BuildStyleFile } from "./index";
|
||||
|
||||
const PLUGIN_NAME = "tailwind";
|
||||
@ -43,7 +43,6 @@ export const esbuildPlugin = ({
|
||||
}
|
||||
|
||||
if (!styleMap.has(path)) return;
|
||||
|
||||
const styles = styleMap.get(path)!;
|
||||
|
||||
try {
|
||||
@ -51,47 +50,51 @@ export const esbuildPlugin = ({
|
||||
return { contents, loader };
|
||||
} catch (e) {
|
||||
if (e instanceof CssSyntaxError) {
|
||||
const lines = e.source!.split("\n");
|
||||
const cls = lines
|
||||
.at(e.line! - 2)!
|
||||
.slice(1, -1)
|
||||
.trim();
|
||||
|
||||
const entry = styles.find(s => s.key === cls)!;
|
||||
if (!entry) {
|
||||
console.error(e);
|
||||
throw new Error("Could not find entry for CSS");
|
||||
}
|
||||
|
||||
const { location: loc } = entry;
|
||||
const errLoc: Partial<esbuild.Location> = {
|
||||
file: loc.filename,
|
||||
line: loc.start.line,
|
||||
column: loc.start.column,
|
||||
length: loc.end.column - loc.start.column,
|
||||
lineText: loc.text,
|
||||
};
|
||||
|
||||
const doesNotExist = e.reason.match(/The `(.+)` class does not exist/)?.[1];
|
||||
if (doesNotExist) {
|
||||
const index = loc.text.indexOf(doesNotExist, loc.start.column);
|
||||
if (index !== -1) {
|
||||
errLoc.column = index;
|
||||
errLoc.length = doesNotExist.length;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
errors: [
|
||||
{
|
||||
text: e.reason,
|
||||
location: errLoc,
|
||||
},
|
||||
],
|
||||
};
|
||||
return buildError(e, styles);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function buildError(e: CssSyntaxError, styles: StyleMapEntry[]) {
|
||||
const lines = e.source!.split("\n");
|
||||
const cls = lines
|
||||
.at(e.line! - 2)!
|
||||
.slice(1, -1)
|
||||
.trim();
|
||||
|
||||
const entry = styles.find(s => s.key === cls)!;
|
||||
if (!entry) {
|
||||
console.error(e);
|
||||
throw new Error("Could not find entry for CSS");
|
||||
}
|
||||
|
||||
const { location: loc } = entry;
|
||||
const errLoc: Partial<esbuild.Location> = {
|
||||
file: loc.filename,
|
||||
line: loc.start.line,
|
||||
column: loc.start.column,
|
||||
length: loc.end.column - loc.start.column,
|
||||
lineText: loc.text,
|
||||
};
|
||||
|
||||
const doesNotExist = e.reason.match(/The `(.+)` class does not exist/)?.[1];
|
||||
if (doesNotExist) {
|
||||
const index = loc.text.indexOf(doesNotExist, loc.start.column);
|
||||
if (index !== -1) {
|
||||
errLoc.column = index;
|
||||
errLoc.length = doesNotExist.length;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
errors: [
|
||||
{
|
||||
text: e.reason,
|
||||
location: errLoc,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ 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 { memoize, without } from "lodash";
|
||||
import { type ClassNameCollector, babelTailwind } from "./babel-tailwind";
|
||||
import { esbuildPlugin } from "./esbuild-postcss";
|
||||
import { vitePlugin } from "./vite-plugin";
|
||||
@ -130,7 +130,12 @@ export function getTailwindPlugins(options: TailwindPluginOptions) {
|
||||
const buildStyleFile: BuildStyleFile = async path => {
|
||||
const styles = styleMap.get(path)!;
|
||||
const compiled = await compile(
|
||||
styles.map(({ className, key }) => `.${key} {\n @apply ${className}\n}`).join("\n")
|
||||
styles
|
||||
.map(
|
||||
({ classNames, key }) =>
|
||||
`.${key} {\n @apply ${without(classNames, "group").join(" ")}\n}`
|
||||
)
|
||||
.join("\n")
|
||||
);
|
||||
if (path.endsWith(".css")) {
|
||||
return ["css", compiled] as const;
|
||||
|
62
src/macro.d.ts
vendored
62
src/macro.d.ts
vendored
@ -4,23 +4,77 @@ interface RecursiveStringObject {
|
||||
|
||||
type CSSAttributeValue = string | (string | RecursiveStringObject)[];
|
||||
|
||||
type Modifier =
|
||||
| "2xl"
|
||||
| "active"
|
||||
| "after"
|
||||
| "backdrop"
|
||||
| "before"
|
||||
| "contrastMore"
|
||||
| "dark"
|
||||
| "default"
|
||||
| "disabled"
|
||||
| "even"
|
||||
| "file"
|
||||
| "first"
|
||||
| "firstLetter"
|
||||
| "firstLine"
|
||||
| "firstOfType"
|
||||
| "focus"
|
||||
| "focusVisible"
|
||||
| "focusWithin"
|
||||
| "forcedColors"
|
||||
| "hover"
|
||||
| "indeterminate"
|
||||
| "invalid"
|
||||
| "last"
|
||||
| "lastOfType"
|
||||
| "ltr"
|
||||
| "marker"
|
||||
| "max2xl"
|
||||
| "maxLg"
|
||||
| "maxMd"
|
||||
| "maxSm"
|
||||
| "maxXl"
|
||||
| "md"
|
||||
| "motionReduce"
|
||||
| "motionSafe"
|
||||
| "odd"
|
||||
| "only"
|
||||
| "open"
|
||||
| "placeholder"
|
||||
| "prefersContrast"
|
||||
| "print"
|
||||
| "readOnly"
|
||||
| "required"
|
||||
| "rtl"
|
||||
| "selection"
|
||||
| "sm"
|
||||
| "target"
|
||||
| "visited"
|
||||
| "xl";
|
||||
|
||||
/**
|
||||
* Tagged template macro function combining Tailwind classes
|
||||
* @example "tw" => tw`p-2 text-center`
|
||||
*/
|
||||
export interface TailwindFunction {
|
||||
export type TailwindFunction = {
|
||||
(strings: TemplateStringsArray): string;
|
||||
(...args: (string | RecursiveStringObject)[]): string;
|
||||
}
|
||||
} & {
|
||||
[key in Modifier]: TailwindFunction;
|
||||
};
|
||||
|
||||
/**
|
||||
* Tagged template macro function compiling Tailwind styles
|
||||
* @example "tws" => tws`p-2 text-center` // { padding: 2, textAlign: "center" }
|
||||
*/
|
||||
export interface TailwindStyleFunction {
|
||||
export type TailwindStyleFunction = {
|
||||
(strings: TemplateStringsArray): TailwindStyleFunctionReturn;
|
||||
(...args: (string | RecursiveStringObject)[]): TailwindStyleFunctionReturn;
|
||||
}
|
||||
} & {
|
||||
[key in Modifier]: TailwindStyleFunction;
|
||||
};
|
||||
|
||||
export const tw: TailwindFunction;
|
||||
export const tws: TailwindStyleFunction;
|
||||
|
@ -19,7 +19,7 @@ export interface SourceLocation {
|
||||
|
||||
export interface StyleMapEntry {
|
||||
key: string;
|
||||
className: string;
|
||||
classNames: string[];
|
||||
location: SourceLocation;
|
||||
}
|
||||
|
||||
@ -47,15 +47,3 @@ export function createPostCSS({
|
||||
}
|
||||
|
||||
export type Compile = ReturnType<typeof createPostCSS>;
|
||||
|
||||
export function toCSSText(tailwindMap: StyleMapEntry[]) {
|
||||
return tailwindMap
|
||||
.map(({ className, key }) => `.${key} {\n @apply ${className}\n}`)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
export function toJSText(tailwindMap: StyleMapEntry[]) {
|
||||
return tailwindMap
|
||||
.map(({ className, key }) => `"${key}": "${className}"`)
|
||||
.join(",\n ");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user