Fix issue

This commit is contained in:
Alex 2025-02-03 00:05:24 -05:00
parent 2c4b75aa6c
commit 1f5e7fa049
14 changed files with 135 additions and 22 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@aet/tailwind",
"version": "1.0.20",
"version": "1.0.23",
"license": "MIT",
"type": "module",
"scripts": {

View File

@ -0,0 +1,27 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`emit > supports emitting as CSS module 1`] = `
"// babel-tailwind:/Users/aet/Documents/Git/babel-tailwind/src/.temp-attr/index.module.css
var index_default = {
"tw-gqn2k6": "index_tw-gqn2k6",
"tw-1qtvvjy": "index_tw-1qtvvjy"
};
import { cx as _cx } from "@emotion/css";
import { jsx } from "react/jsx-runtime";
function Hello() {
return /* @__PURE__ */ jsx("div", { className: _cx([index_default["tw-gqn2k6"], index_default["tw-1qtvvjy"]]), children: "Hello, world!" });
}
export {
Hello
};"
`;
exports[`emit > supports emitting as CSS module 2`] = `
".index_tw-gqn2k6 {
text-align: center;
}
.index_tw-1qtvvjy:hover {
font-weight: 600;
}"
`;

View File

@ -1,4 +1,5 @@
import { describe, it } from "vitest";
import { getBuild, matchSnapshot } from "./utils";
describe("attr", () => {
@ -61,7 +62,7 @@ describe("attr", () => {
matchSnapshot(files);
});
it.only("fails", async () => {
it("fails", async () => {
const { files } = await compileESBuild({
clsx: "emotion",
expectFiles: 2,

View File

@ -1,5 +1,7 @@
import { describe, expect, it } from "vitest";
import { createPostCSS } from "../index";
import { getBuild, minCSS, name } from "./utils";
describe("babel-tailwind", () => {

View File

@ -0,0 +1,26 @@
import { describe, it } from "vitest";
import { getBuild, matchSnapshot } from "./utils";
describe("emit", () => {
const compileESBuild = getBuild("attr");
it("supports emitting as CSS module", async () => {
const { files } = await compileESBuild({
clsx: "emotion",
expectFiles: 2,
cssModules: true,
javascript: /* tsx */ `
export function Hello() {
return (
<div css={["text-center", { hover: "font-semibold" }]}>
Hello, world!
</div>
);
}
`,
});
matchSnapshot(files);
});
});

View File

@ -1,6 +1,8 @@
/* 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", () => {

View File

@ -1,4 +1,5 @@
import { describe, expect, it } from "vitest";
import { getBuild, matchSnapshot } from "./utils";
describe("options", () => {

View File

@ -1,4 +1,5 @@
import { describe, it } from "vitest";
import { getBuild, matchSnapshot } from "./utils";
describe("babel-tailwind", () => {

View File

@ -1,4 +1,5 @@
import { describe, it } from "vitest";
import { getBuild, matchSnapshot } from "./utils";
describe("babel-tailwind", () => {

View File

@ -1,8 +1,10 @@
import { promises as fs } from "node:fs";
import { join, resolve } from "node:path";
import { afterEach, beforeEach, expect } from "vitest";
import * as esbuild from "esbuild";
import dedent from "dedent";
import * as esbuild from "esbuild";
import { afterEach, beforeEach, expect } from "vitest";
import { type TailwindPluginOptions, babelPlugin, getTailwindPlugins } from "../index";
export { name } from "../../package.json" with { type: "json" };

View File

@ -35,12 +35,14 @@ function getUtils({
clsx,
getClassName: getClass = getClassName,
vite: bustCache,
cssModules,
} = options;
let cx: t.Identifier;
let tslibImport: t.Identifier;
let styleImport: t.Identifier;
let classedImport: t.Identifier;
let cssModuleImport: t.Identifier;
const cssMap = new Map<string, StyleMapEntry>();
const jsMap = new Map<string, StyleMapEntry>();
@ -50,6 +52,13 @@ function getUtils({
return t.cloneNode(styleImport);
}
const getCssModuleImport = () => {
if (cssModuleImport == null) {
cssModuleImport = path.scope.generateUidIdentifier("cssModule");
}
return t.cloneNode(cssModuleImport);
};
return {
getClass(type: Type, value: string) {
return type === "css" ? getClass(value) : "tw_" + hash(value);
@ -65,7 +74,7 @@ function getUtils({
.join("\n"),
}),
recordIfAbsent(type: Type, entry: StyleMapEntry) {
recordIfAbsent(type: "css", entry: StyleMapEntry) {
const map = type === "css" ? cssMap : jsMap;
if (!map.has(entry.key)) {
map.set(entry.key, entry);
@ -125,18 +134,44 @@ function getUtils({
return t.cloneNode(classedImport);
},
getCssModuleImport,
getClassNameValue: (className: string) => {
const validId = t.isValidIdentifier(className);
return cssModules
? t.memberExpression(
getCssModuleImport(),
validId ? t.identifier(className) : t.stringLiteral(className),
!validId
)
: t.stringLiteral(className);
},
finish(node: t.Program) {
const { filename } = state;
if (!cssMap.size && !jsMap.size) return;
invariant(filename, "babel: missing state.filename");
if (cssMap.size) {
const cssName = basename(filename, extname(filename)) + ".css";
const cssName =
basename(filename, extname(filename)) +
(cssModuleImport ? ".module" : "") +
".css";
const path = join(dirname(filename), cssName);
const value = Array.from(cssMap.values());
const importee = `tailwind:./${cssName}` + getSuffix(bustCache, value);
node.body.unshift(t.importDeclaration([], t.stringLiteral(importee)));
if (cssModuleImport) {
const importee = `tailwind:./${cssName}` + getSuffix(bustCache, value);
node.body.unshift(
t.importDeclaration(
[t.importDefaultSpecifier(cssModuleImport)],
t.stringLiteral(importee)
)
);
} else {
const importee = `tailwind:./${cssName}` + getSuffix(bustCache, value);
node.body.unshift(t.importDeclaration([], t.stringLiteral(importee)));
}
styleMap.set(path, value);
onCollect?.(path, value);
@ -212,7 +247,7 @@ export function babelTailwind(
classNames: trimmed,
location: _.sliceText(node),
});
path.replaceWith(t.stringLiteral(className));
path.replaceWith(_.getClassNameValue(className));
}
},
ArrayExpression(path) {
@ -228,7 +263,7 @@ export function babelTailwind(
classNames: trimmed,
location: _.sliceText(path.node),
});
path.replaceWith(t.stringLiteral(className));
path.replaceWith(_.getClassNameValue(className));
},
JSXExpressionContainer(path) {
go(path.get("expression"));

View File

@ -1,9 +1,10 @@
import { readFileSync } from "node:fs";
import { extname } from "node:path";
import { once } from "lodash-es";
import type babel from "@babel/core";
import type * as esbuild from "esbuild";
import { transformSync } from "@babel/core";
import type * as esbuild from "esbuild";
import { once } from "lodash-es";
/**
* An esbuild plugin that processes files with Babel if `plugins` is not empty.

View File

@ -19,7 +19,7 @@ export { createPostCSS } from "./shared";
type GetClassName = (className: string) => string;
export type BuildStyleFile = (
path: string
) => Promise<readonly ["css", string] | readonly ["js", string]>;
) => Promise<readonly ["css" | "local-css", string] | readonly ["js", string]>;
export interface TailwindPluginOptions {
/**
@ -86,6 +86,17 @@ export interface TailwindPluginOptions {
* Keep the original classnames in the CSS output
*/
addSourceAsComment?: boolean;
/**
* Emit as CSS modules
*/
cssModules?: boolean;
/**
* Emit type. `css-import` for plain CSS import,
* `css-module` for CSS modules, `css-in-js` for JS.
*/
// emitType: "css-import" | "css-module" | "css-in-js";
}
export type ResolveTailwindOptions = SetRequired<
@ -121,7 +132,7 @@ export const getClassName: GetClassName = cls => "tw-" + hash(cls);
* });
*/
export function getTailwindPlugins(options: TailwindPluginOptions) {
const { addSourceAsComment, compile: _compile } = options;
const { addSourceAsComment, compile: _compile, cssModules } = options;
const resolvedOptions: ResolveTailwindOptions = {
getClassName,
jsxAttributeAction: "delete",
@ -155,7 +166,10 @@ export function getTailwindPlugins(options: TailwindPluginOptions) {
.join("\n")
);
if (path.endsWith(".css")) {
return ["css", transformSync(compiled, { loader: "css" }).code] as const;
return [
cssModules ? "local-css" : "css",
transformSync(compiled, { loader: "css" }).code,
] as const;
} else if (path.endsWith(".js")) {
const js = toJSCode(compiled, x => x.slice(1));
return ["js", js] as const;
@ -176,6 +190,7 @@ export function getTailwindPlugins(options: TailwindPluginOptions) {
styleMap,
options,
getCompiler,
buildStyleFile,
[Symbol.dispose]() {
styleMap.clear();
},

13
src/vendor/animate.ts vendored
View File

@ -1,4 +1,5 @@
// https://github.com/jamiebuilds/tailwindcss-animate/commit/ac0dd3a3c81681b78f1d8ea5e7478044213995e1
// https://github.com/tailwindlabs/tailwindcss/discussions/11164#discussioncomment-5819097
import plugin from "tailwindcss/plugin.js";
import type { PluginAPI } from "tailwindcss/types/config";
@ -11,11 +12,7 @@ function filterDefault<T extends object>(values: T) {
export default plugin(
({ addUtilities, matchUtilities, theme }) => {
addUtilities({
"@keyframes enter": theme("keyframes.enter"),
"@keyframes exit": theme("keyframes.exit"),
".animate-in": {
animationName: "enter",
animationDuration: theme("animationDuration.DEFAULT"),
"--tw-enter-opacity": "initial",
"--tw-enter-scale": "initial",
"--tw-enter-rotate": "initial",
@ -23,8 +20,6 @@ export default plugin(
"--tw-enter-translate-y": "initial",
},
".animate-out": {
animationName: "exit",
animationDuration: theme("animationDuration.DEFAULT"),
"--tw-exit-opacity": "initial",
"--tw-exit-scale": "initial",
"--tw-exit-rotate": "initial",
@ -168,6 +163,10 @@ export default plugin(
1: "1",
infinite: "infinite",
},
animation: ({ theme }) => ({
out: `leave ${theme("animationDuration.DEFAULT")}`,
in: `enter ${theme("animationDuration.DEFAULT")}`,
}),
keyframes: {
enter: {
from: {
@ -176,7 +175,7 @@ export default plugin(
"translate3d(var(--tw-enter-translate-x, 0), var(--tw-enter-translate-y, 0), 0) scale3d(var(--tw-enter-scale, 1), var(--tw-enter-scale, 1), var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))",
},
},
exit: {
leave: {
to: {
opacity: "var(--tw-exit-opacity, 1)",
transform: