Add style object support
This commit is contained in:
25
src/__tests__/base.test.ts
Normal file
25
src/__tests__/base.test.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createPostCSS } from "../index";
|
||||
import { getBuild, minCSS, name } from "./utils";
|
||||
|
||||
describe("babel-tailwind", () => {
|
||||
const compileESBuild = getBuild("base");
|
||||
|
||||
it("supports importing tailwind/base", async () => {
|
||||
const postcss = createPostCSS({
|
||||
tailwindConfig: {},
|
||||
postCSSPlugins: [],
|
||||
});
|
||||
const base = await postcss("@tailwind base;");
|
||||
const { files } = await compileESBuild({
|
||||
clsx: "emotion",
|
||||
expectFiles: 2,
|
||||
javascript: /* tsx */ `
|
||||
import "${name}/base";
|
||||
`,
|
||||
});
|
||||
|
||||
expect(files.js.text).toBe("");
|
||||
expect(minCSS(files.css.text)).toContain(minCSS(base));
|
||||
});
|
||||
});
|
36
src/__tests__/error.test.ts
Normal file
36
src/__tests__/error.test.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getBuild } from "./utils";
|
||||
|
||||
describe("babel-tailwind", () => {
|
||||
const compileESBuild = getBuild("error");
|
||||
it("reports errors with correct position", async () => {
|
||||
try {
|
||||
await compileESBuild({
|
||||
clsx: "emotion",
|
||||
jsxAttributeAction: "preserve",
|
||||
esbuild: {
|
||||
logLevel: "silent",
|
||||
},
|
||||
javascript: /* tsx */ `
|
||||
export function Hello() {
|
||||
return (
|
||||
<div css="text-center2 m-0">
|
||||
Hello, world!
|
||||
</div>
|
||||
);
|
||||
}
|
||||
`,
|
||||
});
|
||||
throw new Error("Expected an error");
|
||||
} catch (e) {
|
||||
expect(e.errors).toHaveLength(1);
|
||||
const [error] = e.errors;
|
||||
expect(error.location).toMatchObject({
|
||||
column: 14,
|
||||
length: 12,
|
||||
line: 3,
|
||||
lineText: ' <div css="text-center2 m-0">',
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
29
src/__tests__/styleObject.test.ts
Normal file
29
src/__tests__/styleObject.test.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getBuild } from "./utils";
|
||||
import { getClassName } from "../index";
|
||||
|
||||
describe.only("babel-tailwind", () => {
|
||||
const compileESBuild = getBuild("styleObject");
|
||||
|
||||
it("supports conversion into CSSProperties", async () => {
|
||||
const { files } = await compileESBuild({
|
||||
clsx: "emotion",
|
||||
expectFiles: 1,
|
||||
javascript: `
|
||||
export function Hello() {
|
||||
return (
|
||||
<div style={tws\`p-2 text-center\`}>
|
||||
Hello, world!
|
||||
</div>
|
||||
);
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
const clsName = getClassName("p-2 text-center").replace(/^tw-/, "tw_");
|
||||
expect(files.js.text).toContain(
|
||||
`var ${clsName} = {\n "padding": "0.5rem",\n "textAlign": "center"\n}`
|
||||
);
|
||||
expect(files.js.text).toContain(`style: ${clsName}`);
|
||||
});
|
||||
});
|
51
src/__tests__/tw.test.ts
Normal file
51
src/__tests__/tw.test.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getClassName } from "../index";
|
||||
import { getBuild } from "./utils";
|
||||
|
||||
describe("babel-tailwind", () => {
|
||||
const compileESBuild = getBuild("tw");
|
||||
it("supports grouped tw", async () => {
|
||||
const { files } = await compileESBuild({
|
||||
clsx: "emotion",
|
||||
expectFiles: 2,
|
||||
javascript: `
|
||||
export default tw("text-sm", \`flex\`, {
|
||||
"group-hover": "text-center",
|
||||
"[&>div]": \`font-semibold\`,
|
||||
data: {
|
||||
"name='hello'": "text-right",
|
||||
nested: {
|
||||
true: "border",
|
||||
}
|
||||
},
|
||||
})
|
||||
`,
|
||||
});
|
||||
|
||||
const clsName = getClassName(
|
||||
"text-sm flex group-hover:text-center [&>div]:font-semibold data-[name='hello']:text-right data-[nested=true]:border"
|
||||
);
|
||||
expect(files.js.text).toContain(`= "${clsName}"`);
|
||||
expect(files.css.text).toMatch(
|
||||
[
|
||||
`.${clsName} {`,
|
||||
" display: flex;",
|
||||
" font-size: 0.875rem;",
|
||||
" line-height: 1.25rem;",
|
||||
"}",
|
||||
`.group:hover .${clsName} {`,
|
||||
" text-align: center;",
|
||||
"}",
|
||||
`.${clsName}[data-nested=true] {`,
|
||||
" border-width: 1px;",
|
||||
"}",
|
||||
`.${clsName}[data-name=hello] {`,
|
||||
" text-align: right;",
|
||||
"}",
|
||||
`.${clsName} > div {`,
|
||||
" font-weight: 600;",
|
||||
"}",
|
||||
].join("\n")
|
||||
);
|
||||
});
|
||||
});
|
75
src/__tests__/utils.ts
Normal file
75
src/__tests__/utils.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import { promises as fs } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { afterEach, beforeEach, expect } from "vitest";
|
||||
import * as esbuild from "esbuild";
|
||||
import dedent from "dedent";
|
||||
import { type TailwindPluginOptions, babelPlugin, getTailwindPlugins } from "../index";
|
||||
|
||||
export { name } from "../../package.json" with { type: "json" };
|
||||
|
||||
export const minCSS = (text: string) =>
|
||||
esbuild.transformSync(text, { minify: true, loader: "css" }).code;
|
||||
|
||||
const findByExt = (outputFiles: esbuild.OutputFile[], ext: string) =>
|
||||
outputFiles.find(file => file.path.endsWith(ext))!;
|
||||
|
||||
export function getBuild(name: string) {
|
||||
const folder = resolve(import.meta.dirname, "..", ".temp-" + name);
|
||||
|
||||
async function write(path: string, content: string) {
|
||||
const resolved = resolve(folder, path);
|
||||
await fs.writeFile(resolved, content);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await fs.mkdir(folder, { recursive: true });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await fs.rm(folder, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
return async function compileESBuild({
|
||||
javascript,
|
||||
esbuild: esbuildOptions,
|
||||
expectFiles,
|
||||
...options
|
||||
}: Omit<TailwindPluginOptions, "compile"> & {
|
||||
esbuild?: esbuild.BuildOptions;
|
||||
javascript: string;
|
||||
expectFiles?: number;
|
||||
}) {
|
||||
const tailwind = getTailwindPlugins({
|
||||
tailwindConfig: {},
|
||||
macroFunction: "tw",
|
||||
macroStyleFunction: "tws",
|
||||
...options,
|
||||
});
|
||||
const result = await esbuild.build({
|
||||
bundle: true,
|
||||
write: false,
|
||||
external: ["react/jsx-runtime", "@emotion/css", "clsx"],
|
||||
outdir: "dist",
|
||||
format: "esm",
|
||||
entryPoints: [await write("index.tsx", dedent(javascript))],
|
||||
plugins: [babelPlugin({ plugins: [tailwind.babel()] }), tailwind.esbuild()],
|
||||
...esbuildOptions,
|
||||
});
|
||||
|
||||
const { errors, warnings, outputFiles } = result;
|
||||
expect(errors).toHaveLength(0);
|
||||
expect(warnings).toHaveLength(0);
|
||||
|
||||
if (expectFiles != null) {
|
||||
expect(outputFiles).toHaveLength(expectFiles);
|
||||
}
|
||||
|
||||
return {
|
||||
outputFiles: outputFiles!,
|
||||
files: new Proxy({} as Record<string, esbuild.OutputFile>, {
|
||||
get: (_, ext: string) => findByExt(outputFiles!, ext),
|
||||
}),
|
||||
};
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user