Add tests and optimize spread
This commit is contained in:
parent
d4404f7ae2
commit
a05b1222b5
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@aet/tailwind",
|
"name": "@aet/tailwind",
|
||||||
"version": "0.0.1-beta.33",
|
"version": "0.0.1-beta.34",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "./scripts/index.ts",
|
"build": "./scripts/index.ts",
|
||||||
@ -37,6 +37,7 @@
|
|||||||
"postcss-nested": "^6.0.1",
|
"postcss-nested": "^6.0.1",
|
||||||
"prettier": "^3.3.2",
|
"prettier": "^3.3.2",
|
||||||
"tailwindcss": "^3.4.4",
|
"tailwindcss": "^3.4.4",
|
||||||
|
"tslib": "^2.6.3",
|
||||||
"tsup": "^8.1.0",
|
"tsup": "^8.1.0",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.3",
|
||||||
"vite": "^5.3.2",
|
"vite": "^5.3.2",
|
||||||
|
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@ -96,6 +96,9 @@ importers:
|
|||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^3.4.4
|
specifier: ^3.4.4
|
||||||
version: 3.4.4
|
version: 3.4.4
|
||||||
|
tslib:
|
||||||
|
specifier: ^2.6.3
|
||||||
|
version: 2.6.3
|
||||||
tsup:
|
tsup:
|
||||||
specifier: ^8.1.0
|
specifier: ^8.1.0
|
||||||
version: 8.1.0(postcss@8.4.39)(typescript@5.5.3)
|
version: 8.1.0(postcss@8.4.39)(typescript@5.5.3)
|
||||||
|
46
src/__tests__/__snapshots__/attr.test.ts.snap
Normal file
46
src/__tests__/__snapshots__/attr.test.ts.snap
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`attr > supports conditional expression in "css" attribute 1`] = `
|
||||||
|
"// src/.temp-attr/index.tsx
|
||||||
|
import { jsx } from "react/jsx-runtime";
|
||||||
|
function Hello({
|
||||||
|
isCenter
|
||||||
|
}) {
|
||||||
|
return /* @__PURE__ */ jsx("div", { className: isCenter ? "tw-gqn2k6" : void 0, children: "Hello, world!" });
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
Hello
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`attr > supports conditional expression in "css" attribute 2`] = `
|
||||||
|
"/* babel-tailwind:/Users/aet/Documents/Git/babel-tailwind/src/.temp-attr/index.css */
|
||||||
|
.tw-gqn2k6 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`attr > supports grouped array css attribute 1`] = `
|
||||||
|
"// src/.temp-attr/index.tsx
|
||||||
|
import { jsx } from "react/jsx-runtime";
|
||||||
|
function Hello() {
|
||||||
|
return /* @__PURE__ */ jsx("div", { className: "tw-gqn2k6 tw-1qtvvjy", children: "Hello, world!" });
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
Hello
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`attr > supports grouped array css attribute 2`] = `
|
||||||
|
"/* babel-tailwind:/Users/aet/Documents/Git/babel-tailwind/src/.temp-attr/index.css */
|
||||||
|
.tw-gqn2k6 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.tw-1qtvvjy:hover {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
`;
|
21
src/__tests__/__snapshots__/options.test.ts.snap
Normal file
21
src/__tests__/__snapshots__/options.test.ts.snap
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`options > supports custom jsxAttributeName 1`] = `
|
||||||
|
"// src/.temp-options/index.tsx
|
||||||
|
import { jsx } from "react/jsx-runtime";
|
||||||
|
function Hello() {
|
||||||
|
return /* @__PURE__ */ jsx("div", { className: "tw-gqn2k6", children: "Hello, world!" });
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
Hello
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`options > supports custom jsxAttributeName 2`] = `
|
||||||
|
"/* babel-tailwind:/Users/aet/Documents/Git/babel-tailwind/src/.temp-options/index.css */
|
||||||
|
.tw-gqn2k6 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
`;
|
51
src/__tests__/__snapshots__/spread.test.ts.snap
Normal file
51
src/__tests__/__snapshots__/spread.test.ts.snap
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`spread > supports spread attribute in "css" attribute (2) 1`] = `
|
||||||
|
"// src/.temp-spread/index.tsx
|
||||||
|
import { cx as _cx } from "@emotion/css";
|
||||||
|
import * as _tslib from "tslib";
|
||||||
|
import { jsx } from "react/jsx-runtime";
|
||||||
|
function Hello(props) {
|
||||||
|
props = {
|
||||||
|
...props,
|
||||||
|
className: "text-center"
|
||||||
|
};
|
||||||
|
return /* @__PURE__ */ jsx("div", { ..._tslib.__rest(props, ["className"]), className: _cx("tw-gqn2k6", props.className), children: "Hello, world!" });
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
Hello
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`spread > supports spread attribute in "css" attribute (2) 2`] = `
|
||||||
|
"/* babel-tailwind:/Users/aet/Documents/Git/babel-tailwind/src/.temp-spread/index.css */
|
||||||
|
.tw-gqn2k6 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`spread > supports spread attribute in "css" attribute 1`] = `
|
||||||
|
"// src/.temp-spread/index.tsx
|
||||||
|
import { cx as _cx } from "@emotion/css";
|
||||||
|
import { jsx } from "react/jsx-runtime";
|
||||||
|
function Hello({
|
||||||
|
className: _className,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
return /* @__PURE__ */ jsx("div", { ...props, className: _cx("tw-gqn2k6", _className), children: "Hello, world!" });
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
Hello
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`spread > supports spread attribute in "css" attribute 2`] = `
|
||||||
|
"/* babel-tailwind:/Users/aet/Documents/Git/babel-tailwind/src/.temp-spread/index.css */
|
||||||
|
.tw-gqn2k6 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
`;
|
56
src/__tests__/__snapshots__/styleObject.test.ts.snap
Normal file
56
src/__tests__/__snapshots__/styleObject.test.ts.snap
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`babel-tailwind > supports .hover, .focus, .active, .group-hover, .group-focus, .group-active 1`] = `
|
||||||
|
"// babel-tailwind:/Users/aet/Documents/Git/babel-tailwind/src/.temp-styleObject/index.tailwindStyle.js
|
||||||
|
var tw_12x0ow0 = {
|
||||||
|
"&:focus": {
|
||||||
|
fontWeight: "700"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var tw_m4tg46 = {
|
||||||
|
"&:active": {
|
||||||
|
fontWeight: "300"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var tw_rqrjo3 = {
|
||||||
|
"&:hover:active": {
|
||||||
|
padding: "0.5rem"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var tw_1qtvvjy = {
|
||||||
|
"&:hover": {
|
||||||
|
fontWeight: "600"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// src/.temp-styleObject/index.tsx
|
||||||
|
var style = [tw_1qtvvjy, tw_12x0ow0, tw_m4tg46, tw_rqrjo3];
|
||||||
|
export {
|
||||||
|
style
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`babel-tailwind > supports conversion into CSSProperties 1`] = `
|
||||||
|
"// babel-tailwind:/Users/aet/Documents/Git/babel-tailwind/src/.temp-styleObject/index.tailwindStyle.js
|
||||||
|
var tw_kt12th = {
|
||||||
|
padding: "0.5rem",
|
||||||
|
textAlign: "center",
|
||||||
|
"&:hover": {
|
||||||
|
fontWeight: "600"
|
||||||
|
},
|
||||||
|
"@media (min-width: 640px)": {
|
||||||
|
padding: "0.25rem"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// src/.temp-styleObject/index.tsx
|
||||||
|
import { jsx } from "react/jsx-runtime";
|
||||||
|
function Hello() {
|
||||||
|
return /* @__PURE__ */ jsx("div", { style: tw_kt12th, children: "Hello, world!" });
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
Hello
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`;
|
44
src/__tests__/attr.test.ts
Normal file
44
src/__tests__/attr.test.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { getBuild } from "./utils";
|
||||||
|
|
||||||
|
describe("attr", () => {
|
||||||
|
const compileESBuild = getBuild("attr");
|
||||||
|
|
||||||
|
it("supports grouped array css attribute", async () => {
|
||||||
|
const { files } = await compileESBuild({
|
||||||
|
clsx: "emotion",
|
||||||
|
expectFiles: 2,
|
||||||
|
javascript: /* tsx */ `
|
||||||
|
export function Hello() {
|
||||||
|
return (
|
||||||
|
<div css={["text-center", { hover: "font-semibold" }]}>
|
||||||
|
Hello, world!
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(files.js.text).toMatchSnapshot();
|
||||||
|
expect(files.css.text).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports conditional expression in "css" attribute', async () => {
|
||||||
|
const { files } = await compileESBuild({
|
||||||
|
clsx: "emotion",
|
||||||
|
expectFiles: 2,
|
||||||
|
javascript: /* tsx */ `
|
||||||
|
export function Hello({ isCenter }) {
|
||||||
|
return (
|
||||||
|
<div css={isCenter ? "text-center" : undefined}>
|
||||||
|
Hello, world!
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(files.js.text).toMatchSnapshot();
|
||||||
|
expect(files.css.text).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
50
src/__tests__/merge.test.ts
Normal file
50
src/__tests__/merge.test.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/* eslint-disable unicorn/string-content */
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { getClassName } from "../index";
|
||||||
|
import { getBuild } from "./utils";
|
||||||
|
|
||||||
|
describe("merges with existing className attribute", () => {
|
||||||
|
const compileESBuild = getBuild("merge");
|
||||||
|
|
||||||
|
it("string literal", async () => {
|
||||||
|
const { files } = await compileESBuild({
|
||||||
|
clsx: "emotion",
|
||||||
|
expectFiles: 2,
|
||||||
|
javascript: /* tsx */ `
|
||||||
|
export function Hello() {
|
||||||
|
return (
|
||||||
|
<div className="text-center" css="text-center">
|
||||||
|
Hello, world!
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const clsName = getClassName("text-center");
|
||||||
|
expect(files.js.text).toContain(`className: "text-center ${clsName}"`);
|
||||||
|
expect(files.css.text).toMatch(`.${clsName} {\n text-align: center;\n}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("existing function", async () => {
|
||||||
|
const { files } = await compileESBuild({
|
||||||
|
clsx: "emotion",
|
||||||
|
expectFiles: 2,
|
||||||
|
javascript: /* tsx */ `
|
||||||
|
export function Hello() {
|
||||||
|
return (
|
||||||
|
<div className={({ isEntering }) => isEntering ? "enter" : "exit"} css="text-center">
|
||||||
|
Hello, world!
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const clsName = getClassName("text-center");
|
||||||
|
expect(files.js.text).toContain(
|
||||||
|
`className: ({\n isEntering\n }) => _cx(isEntering ? "enter" : "exit", "${clsName}")`
|
||||||
|
);
|
||||||
|
expect(files.css.text).toMatch(`.${clsName} {\n text-align: center;\n}`);
|
||||||
|
});
|
||||||
|
});
|
44
src/__tests__/options.test.ts
Normal file
44
src/__tests__/options.test.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { getBuild } from "./utils";
|
||||||
|
|
||||||
|
describe("options", () => {
|
||||||
|
const compileESBuild = getBuild("options");
|
||||||
|
it("supports custom jsxAttributeName", async () => {
|
||||||
|
const { files } = await compileESBuild({
|
||||||
|
clsx: "emotion",
|
||||||
|
jsxAttributeName: "tw",
|
||||||
|
expectFiles: 2,
|
||||||
|
javascript: /* tsx */ `
|
||||||
|
export function Hello() {
|
||||||
|
return (
|
||||||
|
<div tw="text-center">
|
||||||
|
Hello, world!
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(files.js.text).toMatchSnapshot();
|
||||||
|
expect(files.css.text).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not remove the attribute if `preserveAttribute` is true", async () => {
|
||||||
|
const { files } = await compileESBuild({
|
||||||
|
clsx: "emotion",
|
||||||
|
jsxAttributeAction: "preserve",
|
||||||
|
expectFiles: 2,
|
||||||
|
javascript: /* tsx */ `
|
||||||
|
export function Hello() {
|
||||||
|
return (
|
||||||
|
<div css="text-center">
|
||||||
|
Hello, world!
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(files.js.text).toContain(`css: "text-center"`);
|
||||||
|
});
|
||||||
|
});
|
46
src/__tests__/spread.test.ts
Normal file
46
src/__tests__/spread.test.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/* eslint-disable unicorn/string-content */
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { getBuild } from "./utils";
|
||||||
|
|
||||||
|
describe("spread", () => {
|
||||||
|
const compileESBuild = getBuild("spread");
|
||||||
|
|
||||||
|
it('supports spread attribute in "css" attribute', async () => {
|
||||||
|
const { files } = await compileESBuild({
|
||||||
|
clsx: "emotion",
|
||||||
|
expectFiles: 2,
|
||||||
|
javascript: /* tsx */ `
|
||||||
|
export function Hello(props) {
|
||||||
|
return (
|
||||||
|
<div css="text-center" {...props}>
|
||||||
|
Hello, world!
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(files.js.text).toMatchSnapshot();
|
||||||
|
expect(files.css.text).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports spread attribute in "css" attribute (2)', async () => {
|
||||||
|
const { files } = await compileESBuild({
|
||||||
|
clsx: "emotion",
|
||||||
|
expectFiles: 2,
|
||||||
|
javascript: /* tsx */ `
|
||||||
|
export function Hello(props) {
|
||||||
|
props = { ...props, className: "text-center" };
|
||||||
|
return (
|
||||||
|
<div css="text-center" {...props}>
|
||||||
|
Hello, world!
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(files.js.text).toMatchSnapshot();
|
||||||
|
expect(files.css.text).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
@ -1,6 +1,5 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { getBuild } from "./utils";
|
import { getBuild } from "./utils";
|
||||||
import { getClassName } from "../index";
|
|
||||||
|
|
||||||
describe("babel-tailwind", () => {
|
describe("babel-tailwind", () => {
|
||||||
const compileESBuild = getBuild("styleObject");
|
const compileESBuild = getBuild("styleObject");
|
||||||
@ -22,25 +21,7 @@ describe("babel-tailwind", () => {
|
|||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const clsName = getClassName("p-2 text-center hover:font-semibold sm:p-1").replace(
|
expect(files.js.text).toMatchSnapshot();
|
||||||
/^tw-/,
|
|
||||||
"tw_"
|
|
||||||
);
|
|
||||||
expect(files.js.text).toContain(
|
|
||||||
[
|
|
||||||
`var ${clsName} = {`,
|
|
||||||
' padding: "0.5rem",',
|
|
||||||
' textAlign: "center",',
|
|
||||||
' "&:hover": {',
|
|
||||||
' fontWeight: "600"',
|
|
||||||
" },",
|
|
||||||
' "@media (min-width: 640px)": {',
|
|
||||||
' padding: "0.25rem"',
|
|
||||||
" }",
|
|
||||||
"}",
|
|
||||||
].join("\n")
|
|
||||||
);
|
|
||||||
expect(files.js.text).toContain(`style: ${clsName}`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("supports .hover, .focus, .active, .group-hover, .group-focus, .group-active", async () => {
|
it("supports .hover, .focus, .active, .group-hover, .group-focus, .group-active", async () => {
|
||||||
@ -59,34 +40,6 @@ describe("babel-tailwind", () => {
|
|||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const semibold = getClassName("hover:font-semibold").replace(/^tw-/, "tw_");
|
expect(files.js.text).toMatchSnapshot();
|
||||||
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")
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -47,7 +47,7 @@ export function getBuild(name: string) {
|
|||||||
const result = await esbuild.build({
|
const result = await esbuild.build({
|
||||||
bundle: true,
|
bundle: true,
|
||||||
write: false,
|
write: false,
|
||||||
external: ["react/jsx-runtime", "@emotion/css", "clsx"],
|
external: ["react/jsx-runtime", "@emotion/css", "clsx", "tslib"],
|
||||||
outdir: "dist",
|
outdir: "dist",
|
||||||
format: "esm",
|
format: "esm",
|
||||||
entryPoints: [await write("index.tsx", dedent(javascript))],
|
entryPoints: [await write("index.tsx", dedent(javascript))],
|
||||||
|
@ -28,6 +28,7 @@ export function babelTailwind(
|
|||||||
|
|
||||||
function getUtils(path: NodePath<t.Program>, state: b.PluginPass, t: BabelTypes) {
|
function getUtils(path: NodePath<t.Program>, state: b.PluginPass, t: BabelTypes) {
|
||||||
let cx: t.Identifier;
|
let cx: t.Identifier;
|
||||||
|
let tslibImport: t.Identifier;
|
||||||
let styleImport: t.Identifier;
|
let styleImport: t.Identifier;
|
||||||
|
|
||||||
const cssMap = new Map<string, StyleMapEntry>();
|
const cssMap = new Map<string, StyleMapEntry>();
|
||||||
@ -87,6 +88,19 @@ export function babelTailwind(
|
|||||||
return t.cloneNode(cx);
|
return t.cloneNode(cx);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getTSlibImport: () => {
|
||||||
|
if (tslibImport == null) {
|
||||||
|
tslibImport = path.scope.generateUidIdentifier("tslib");
|
||||||
|
path.node.body.unshift(
|
||||||
|
t.importDeclaration(
|
||||||
|
[t.importNamespaceSpecifier(tslibImport)],
|
||||||
|
t.stringLiteral("tslib")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return t.cloneNode(tslibImport);
|
||||||
|
},
|
||||||
|
|
||||||
finish(node: t.Program) {
|
finish(node: t.Program) {
|
||||||
const { filename } = state;
|
const { filename } = state;
|
||||||
if (!cssMap.size && !jsMap.size) return;
|
if (!cssMap.size && !jsMap.size) return;
|
||||||
@ -279,12 +293,58 @@ export function babelTailwind(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
const wrap = (originalValue: b.types.Expression) =>
|
||||||
|
t.callExpression(_.getCx(), [valuePathNode, originalValue]);
|
||||||
|
|
||||||
|
const rest = parent.attributes.filter(attr => t.isJSXSpreadAttribute(attr));
|
||||||
|
let arg;
|
||||||
|
if (rest.length === 1 && (arg = rest[0].argument) && t.isIdentifier(arg)) {
|
||||||
|
// props from argument and not modified anywhere
|
||||||
|
const scope = path.scope.getBinding(arg.name);
|
||||||
|
let index: number;
|
||||||
|
if (
|
||||||
|
scope &&
|
||||||
|
!scope.constantViolations.length &&
|
||||||
|
t.isFunctionDeclaration(scope.path.parent) &&
|
||||||
|
(index = (scope.path.parent.params as t.Node[]).indexOf(scope.path.node)) !==
|
||||||
|
-1
|
||||||
|
) {
|
||||||
|
const clsVar = path.scope.generateUidIdentifier("className");
|
||||||
|
scope.path.parent.params[index] = t.objectPattern([
|
||||||
|
t.objectProperty(t.identifier("className"), clsVar),
|
||||||
|
t.restElement(scope.path.node as t.Identifier),
|
||||||
|
]);
|
||||||
|
|
||||||
parent.attributes.push(
|
parent.attributes.push(
|
||||||
t.jsxAttribute(
|
t.jsxAttribute(
|
||||||
t.jsxIdentifier("className"),
|
t.jsxIdentifier("className"),
|
||||||
t.jSXExpressionContainer(valuePathNode)
|
t.jsxExpressionContainer(wrap(clsVar))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
const tslibImport = _.getTSlibImport();
|
||||||
|
rest[0].argument = t.callExpression(
|
||||||
|
t.memberExpression(tslibImport, t.identifier("__rest")),
|
||||||
|
[arg, t.arrayExpression([t.stringLiteral("className")])]
|
||||||
|
);
|
||||||
|
|
||||||
|
parent.attributes.push(
|
||||||
|
t.jsxAttribute(
|
||||||
|
t.jsxIdentifier("className"),
|
||||||
|
t.jsxExpressionContainer(
|
||||||
|
wrap(t.memberExpression(arg, t.identifier("className")))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parent.attributes.push(
|
||||||
|
t.jsxAttribute(
|
||||||
|
t.jsxIdentifier("className"),
|
||||||
|
t.jsxExpressionContainer(valuePathNode)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jsxAttributeAction === "delete") {
|
if (jsxAttributeAction === "delete") {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable unicorn/string-content */
|
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { getClassName } from "./index";
|
import { getClassName } from "./index";
|
||||||
import { getBuild } from "./__tests__/utils";
|
import { getBuild } from "./__tests__/utils";
|
||||||
@ -25,137 +24,4 @@ describe("babel-tailwind", () => {
|
|||||||
expect(files.js.text).toContain(`className: "${clsName}"`);
|
expect(files.js.text).toContain(`className: "${clsName}"`);
|
||||||
expect(files.css.text).toMatch(`.${clsName} {\n text-align: center;\n}`);
|
expect(files.css.text).toMatch(`.${clsName} {\n text-align: center;\n}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not remove the attribute if `preserveAttribute` is true", async () => {
|
|
||||||
const { files } = await compileESBuild({
|
|
||||||
clsx: "emotion",
|
|
||||||
jsxAttributeAction: "preserve",
|
|
||||||
expectFiles: 2,
|
|
||||||
javascript: /* tsx */ `
|
|
||||||
export function Hello() {
|
|
||||||
return (
|
|
||||||
<div css="text-center">
|
|
||||||
Hello, world!
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(files.js.text).toContain(`css: "text-center"`);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('merges with existing "className" attribute', () => {
|
|
||||||
it("string literal", async () => {
|
|
||||||
const { files } = await compileESBuild({
|
|
||||||
clsx: "emotion",
|
|
||||||
expectFiles: 2,
|
|
||||||
javascript: /* tsx */ `
|
|
||||||
export function Hello() {
|
|
||||||
return (
|
|
||||||
<div className="text-center" css="text-center">
|
|
||||||
Hello, world!
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
|
|
||||||
const clsName = getClassName("text-center");
|
|
||||||
expect(files.js.text).toContain(`className: "text-center ${clsName}"`);
|
|
||||||
expect(files.css.text).toMatch(`.${clsName} {\n text-align: center;\n}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("existing function", async () => {
|
|
||||||
const { files } = await compileESBuild({
|
|
||||||
clsx: "emotion",
|
|
||||||
expectFiles: 2,
|
|
||||||
javascript: /* tsx */ `
|
|
||||||
export function Hello() {
|
|
||||||
return (
|
|
||||||
<div className={({ isEntering }) => isEntering ? "enter" : "exit"} css="text-center">
|
|
||||||
Hello, world!
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
|
|
||||||
const clsName = getClassName("text-center");
|
|
||||||
expect(files.js.text).toContain(
|
|
||||||
`className: ({\n isEntering\n }) => _cx(isEntering ? "enter" : "exit", "${clsName}")`
|
|
||||||
);
|
|
||||||
expect(files.css.text).toMatch(`.${clsName} {\n text-align: center;\n}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("supports custom jsxAttributeName", async () => {
|
|
||||||
const { files } = await compileESBuild({
|
|
||||||
clsx: "emotion",
|
|
||||||
jsxAttributeName: "tw",
|
|
||||||
expectFiles: 2,
|
|
||||||
javascript: /* tsx */ `
|
|
||||||
export function Hello() {
|
|
||||||
return (
|
|
||||||
<div tw="text-center">
|
|
||||||
Hello, world!
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
|
|
||||||
const clsName = getClassName("text-center");
|
|
||||||
expect(files.js.text).toContain(`className: "${clsName}"`);
|
|
||||||
expect(files.css.text).toMatch(`.${clsName} {\n text-align: center;\n}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("supports grouped array css attribute", async () => {
|
|
||||||
const { files } = await compileESBuild({
|
|
||||||
clsx: "emotion",
|
|
||||||
expectFiles: 2,
|
|
||||||
javascript: /* tsx */ `
|
|
||||||
export function Hello() {
|
|
||||||
return (
|
|
||||||
<div css={["text-center", { hover: "font-semibold" }]}>
|
|
||||||
Hello, world!
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
|
|
||||||
const clsName = ["text-center", "hover:font-semibold"].map(getClassName);
|
|
||||||
expect(files.js.text).toContain(`className: "${clsName.join(" ")}"`);
|
|
||||||
expect(files.css.text).toMatch(
|
|
||||||
[
|
|
||||||
`.${clsName[0]} {`,
|
|
||||||
" text-align: center;",
|
|
||||||
"}",
|
|
||||||
`.${clsName[1]}:hover {`,
|
|
||||||
" font-weight: 600;",
|
|
||||||
"}",
|
|
||||||
].join("\n")
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports conditional expression in "css" attribute', async () => {
|
|
||||||
const { files } = await compileESBuild({
|
|
||||||
clsx: "emotion",
|
|
||||||
expectFiles: 2,
|
|
||||||
javascript: /* tsx */ `
|
|
||||||
export function Hello({ isCenter }) {
|
|
||||||
return (
|
|
||||||
<div css={isCenter ? "text-center" : undefined}>
|
|
||||||
Hello, world!
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
|
|
||||||
const clsName = getClassName("text-center");
|
|
||||||
expect(files.js.text).toContain(`className: isCenter ? "${clsName}" : void 0`);
|
|
||||||
expect(files.css.text).toMatch(`.${clsName} {\n text-align: center;\n}`);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user