Support vite cache invalidation
This commit is contained in:
parent
2a3e7390cf
commit
2a6df0dd4a
30
package.json
30
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@aet/tailwind",
|
"name": "@aet/tailwind",
|
||||||
"version": "0.0.1-beta.6",
|
"version": "0.0.1-beta.8",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"private": true,
|
"private": true,
|
||||||
@ -12,38 +12,40 @@
|
|||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@aet/eslint-rules": "^0.0.25",
|
"@aet/eslint-rules": "^0.0.33",
|
||||||
"@types/babel__core": "^7.20.5",
|
"@types/babel__core": "^7.20.5",
|
||||||
"@types/bun": "^1.1.0",
|
"@types/bun": "^1.1.6",
|
||||||
"@types/dedent": "^0.7.2",
|
"@types/dedent": "^0.7.2",
|
||||||
"@types/lodash": "^4.17.0",
|
"@types/lodash": "^4.17.6",
|
||||||
"@types/node": "^20.12.7",
|
"@types/node": "^20.14.9",
|
||||||
"@types/postcss-safe-parser": "^5.0.4",
|
"@types/postcss-safe-parser": "^5.0.4",
|
||||||
|
"@types/react": "^18.3.3",
|
||||||
"cli-highlight": "^2.1.11",
|
"cli-highlight": "^2.1.11",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
"colord": "^2.9.3",
|
"colord": "^2.9.3",
|
||||||
"css-what": "^6.1.0",
|
"css-what": "^6.1.0",
|
||||||
"dedent": "^1.5.3",
|
"dedent": "^1.5.3",
|
||||||
"esbuild": "^0.20.2",
|
"esbuild": "^0.21.5",
|
||||||
"esbuild-register": "^3.5.0",
|
"esbuild-register": "^3.5.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"postcss-nested": "^6.0.1",
|
"postcss-nested": "^6.0.1",
|
||||||
"postcss-safe-parser": "^7.0.0",
|
"postcss-safe-parser": "^7.0.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.3.2",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.4",
|
||||||
"tsup": "^8.0.2",
|
"tsup": "^8.1.0",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.5.2",
|
||||||
"vite": "^5.2.10",
|
"vite": "^5.3.2",
|
||||||
"vitest": "^1.5.2"
|
"vitest": "^1.6.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"tailwindcss": "^3.4.3"
|
"tailwindcss": "^3.4.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.24.4",
|
"@babel/core": "^7.24.7",
|
||||||
"@emotion/hash": "^0.9.1",
|
"@emotion/hash": "^0.9.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
"type-fest": "^4.17.0"
|
"type-fest": "^4.20.1"
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"arrowParens": "avoid",
|
"arrowParens": "avoid",
|
||||||
|
5109
pnpm-lock.yaml
generated
5109
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,25 +1,51 @@
|
|||||||
#!/usr/bin/env bun
|
#!/usr/bin/env bun
|
||||||
import { promises as fs } from "node:fs";
|
import { promises as fs } from "node:fs";
|
||||||
import { type Options, build } from "tsup";
|
import { build, defineConfig } from "tsup";
|
||||||
import { pick } from "lodash";
|
import { pick } from "lodash";
|
||||||
import tsupConfig from "../tsup.config";
|
|
||||||
import pkg from "../package.json" with { type: "json" };
|
import pkg from "../package.json" with { type: "json" };
|
||||||
|
|
||||||
await build(tsupConfig as Options);
|
const tsupConfig = defineConfig({
|
||||||
await Bun.write(
|
outDir: "dist",
|
||||||
|
splitting: false,
|
||||||
|
sourcemap: false,
|
||||||
|
dts: true,
|
||||||
|
treeshake: true,
|
||||||
|
platform: "node",
|
||||||
|
define: {
|
||||||
|
"process.env.BABEL_TAILWIND_BUILD": "true",
|
||||||
|
},
|
||||||
|
banner: {
|
||||||
|
js: "/* eslint-disable */",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
build({
|
||||||
|
...tsupConfig,
|
||||||
|
entry: ["src/classed.tsx"],
|
||||||
|
outDir: "dist",
|
||||||
|
external: ["react", "react/jsx-runtime", "clsx"],
|
||||||
|
format: "esm",
|
||||||
|
clean: true,
|
||||||
|
}),
|
||||||
|
build({
|
||||||
|
...tsupConfig,
|
||||||
|
entry: ["src/index.ts"],
|
||||||
|
}),
|
||||||
|
Bun.write(
|
||||||
"dist/package.json",
|
"dist/package.json",
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
pick(pkg, ["name", "version", "license", "dependencies", "author"]),
|
pick(pkg, ["name", "version", "license", "dependencies", "author"]),
|
||||||
null,
|
null,
|
||||||
2
|
2
|
||||||
)
|
)
|
||||||
);
|
),
|
||||||
|
Bun.write(`dist/base.d.ts`, `/**\n * \`@tailwind base\` component.\n */\nexport {};`),
|
||||||
|
]);
|
||||||
|
|
||||||
await Bun.write(
|
await Promise.all([
|
||||||
`dist/base.d.ts`,
|
fs.copyFile("README.md", "dist/README.md"),
|
||||||
`/**\n * \`@tailwind base\` component.\n */\nexport {};`
|
fs.copyFile("LICENSE.md", "dist/LICENSE.md"),
|
||||||
);
|
]);
|
||||||
await fs.copyFile("README.md", "dist/README.md");
|
|
||||||
await fs.copyFile("LICENSE.md", "dist/LICENSE.md");
|
|
||||||
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { basename, dirname, extname, join } from "node:path";
|
import { basename, dirname, extname, join } from "node:path";
|
||||||
import type babel from "@babel/core";
|
import type babel from "@babel/core";
|
||||||
|
import hash from "@emotion/hash";
|
||||||
import { type NodePath, type types as t } from "@babel/core";
|
import { type NodePath, type types as t } from "@babel/core";
|
||||||
import type { SourceLocation, StyleMapEntry } from "./shared";
|
import type { SourceLocation, StyleMapEntry } from "./shared";
|
||||||
import { type ResolveTailwindOptions, getClassName } from "./index";
|
import { type ResolveTailwindOptions, getClassName } from "./index";
|
||||||
@ -18,7 +19,7 @@ const extractJSXContainer = (attr: NonNullable<t.JSXAttribute["value"]>): t.Expr
|
|||||||
|
|
||||||
function matchPath(
|
function matchPath(
|
||||||
nodePath: NodePath<t.Node | null | undefined>,
|
nodePath: NodePath<t.Node | null | undefined>,
|
||||||
fns: (dig: (nodePath: NodePath) => void) => babel.Visitor
|
fns: (dig: (nodePath: NodePath<t.Node | null | undefined>) => void) => babel.Visitor
|
||||||
) {
|
) {
|
||||||
if (!nodePath.node) return;
|
if (!nodePath.node) return;
|
||||||
const fn = fns(path => matchPath(path, fns))[nodePath.node.type] as any;
|
const fn = fns(path => matchPath(path, fns))[nodePath.node.type] as any;
|
||||||
@ -42,6 +43,7 @@ export function babelTailwind(
|
|||||||
taggedTemplateName,
|
taggedTemplateName,
|
||||||
jsxAttributeAction = "delete",
|
jsxAttributeAction = "delete",
|
||||||
jsxAttributeName = "css",
|
jsxAttributeName = "css",
|
||||||
|
vite,
|
||||||
}: ResolveTailwindOptions,
|
}: ResolveTailwindOptions,
|
||||||
onCollect: ClassNameCollector | undefined
|
onCollect: ClassNameCollector | undefined
|
||||||
) {
|
) {
|
||||||
@ -102,12 +104,18 @@ export function babelTailwind(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cssName = basename(filename, extname(filename)) + ".css";
|
const cssName = basename(filename, extname(filename)) + ".css";
|
||||||
node.body.unshift(
|
|
||||||
t.importDeclaration([], t.stringLiteral(`tailwind:./${cssName}`))
|
|
||||||
);
|
|
||||||
|
|
||||||
const path = join(dirname(filename), cssName);
|
const path = join(dirname(filename), cssName);
|
||||||
const value = Array.from(tailwindMap.values());
|
const value = Array.from(tailwindMap.values());
|
||||||
|
|
||||||
|
let importee = `tailwind:./${cssName}`;
|
||||||
|
if (vite) {
|
||||||
|
const cacheKey = hash(value.map(x => x.className).join(","));
|
||||||
|
importee += `?${cacheKey}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.body.unshift(t.importDeclaration([], t.stringLiteral(importee)));
|
||||||
|
|
||||||
styleMap.set(path, value);
|
styleMap.set(path, value);
|
||||||
onCollect?.(path, value);
|
onCollect?.(path, value);
|
||||||
},
|
},
|
||||||
@ -172,6 +180,11 @@ export function babelTailwind(
|
|||||||
path.replaceWith(t.stringLiteral(className));
|
path.replaceWith(t.stringLiteral(className));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
ArrayExpression(path) {
|
||||||
|
for (const element of path.get("elements")) {
|
||||||
|
go(element);
|
||||||
|
}
|
||||||
|
},
|
||||||
JSXExpressionContainer(path) {
|
JSXExpressionContainer(path) {
|
||||||
go(path.get("expression"));
|
go(path.get("expression"));
|
||||||
},
|
},
|
||||||
|
32
src/classed.tsx
Normal file
32
src/classed.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import cx from "clsx";
|
||||||
|
import { type FunctionComponent, forwardRef } from "react";
|
||||||
|
|
||||||
|
interface WithClassName<P = object> extends FunctionComponent<P> {
|
||||||
|
className: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const classed: {
|
||||||
|
(
|
||||||
|
type: "input",
|
||||||
|
className: string | string[]
|
||||||
|
): WithClassName<
|
||||||
|
React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>
|
||||||
|
>;
|
||||||
|
<K extends keyof JSX.IntrinsicElements>(
|
||||||
|
type: K,
|
||||||
|
className: string | string[]
|
||||||
|
): WithClassName<JSX.IntrinsicElements[K]>;
|
||||||
|
(
|
||||||
|
type: string,
|
||||||
|
className: string | string[]
|
||||||
|
): WithClassName<React.ClassAttributes<any> & React.DOMAttributes<any>>;
|
||||||
|
<P>(type: FunctionComponent<P>, className: string | string[]): WithClassName<P>;
|
||||||
|
<P>(type: React.ComponentClass<P>, className: string | string[]): WithClassName<P>;
|
||||||
|
} = (Component: any, classNameInput: string | (string | false)[]) => {
|
||||||
|
const className = cx(classNameInput);
|
||||||
|
const component: any = forwardRef<any, any>(({ className: cls, ...props }, ref) => (
|
||||||
|
<Component {...props} ref={ref} className={cx(className, cls)} />
|
||||||
|
));
|
||||||
|
component.className = className;
|
||||||
|
return component;
|
||||||
|
};
|
@ -83,6 +83,11 @@ export interface TailwindPluginOptions {
|
|||||||
* async css => (await postcss.process(css, { plugins: [tailwindcss()] })).css
|
* async css => (await postcss.process(css, { plugins: [tailwindcss()] })).css
|
||||||
*/
|
*/
|
||||||
compile?(css: string): Promise<string>;
|
compile?(css: string): Promise<string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Using the vite plugin?
|
||||||
|
*/
|
||||||
|
vite?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ResolveTailwindOptions = SetRequired<
|
export type ResolveTailwindOptions = SetRequired<
|
||||||
@ -139,6 +144,7 @@ export function getTailwindPlugins(options: TailwindPluginOptions) {
|
|||||||
compile,
|
compile,
|
||||||
babel: (onCollect?: ClassNameCollector) => babelTailwind(resolvedOptions, onCollect),
|
babel: (onCollect?: ClassNameCollector) => babelTailwind(resolvedOptions, onCollect),
|
||||||
esbuild: () => esbuildPlugin(styleMap, compile),
|
esbuild: () => esbuildPlugin(styleMap, compile),
|
||||||
|
/** Requires `options.vite` to be `true`. */
|
||||||
vite: () => vitePlugin(styleMap, compile),
|
vite: () => vitePlugin(styleMap, compile),
|
||||||
styleMap,
|
styleMap,
|
||||||
options,
|
options,
|
||||||
|
@ -15,15 +15,17 @@ export const vitePlugin = (styleMap: StyleMap, compile: Compile): vite.Plugin =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (id.startsWith("tailwind:")) {
|
if (id.startsWith("tailwind:")) {
|
||||||
const resolved = join(dirname(importer!), id.slice("tailwind:".length));
|
const [name, cacheKey] = id.slice("tailwind:".length).split("?");
|
||||||
|
const resolved = join(dirname(importer!), name);
|
||||||
if (styleMap.has(resolved)) {
|
if (styleMap.has(resolved)) {
|
||||||
return {
|
return {
|
||||||
id: ROLLUP_PREFIX + resolved,
|
id: ROLLUP_PREFIX + resolved + "?" + cacheKey,
|
||||||
moduleSideEffects: true,
|
moduleSideEffects: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
if (id.startsWith(ROLLUP_PREFIX)) {
|
if (id.startsWith(ROLLUP_PREFIX)) {
|
||||||
const resolved = id.slice(ROLLUP_PREFIX.length);
|
const resolved = id.slice(ROLLUP_PREFIX.length);
|
||||||
@ -31,8 +33,9 @@ export const vitePlugin = (styleMap: StyleMap, compile: Compile): vite.Plugin =>
|
|||||||
return await compile("@tailwind base");
|
return await compile("@tailwind base");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (styleMap.has(resolved)) {
|
const name = resolved.split("?")[0];
|
||||||
return await compile(toCSSText(styleMap.get(resolved)!));
|
if (styleMap.has(name)) {
|
||||||
|
return await compile(toCSSText(styleMap.get(name)!));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
import { defineConfig } from "tsup";
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
entry: ["src/index.ts"],
|
|
||||||
outDir: "dist",
|
|
||||||
splitting: false,
|
|
||||||
sourcemap: false,
|
|
||||||
clean: true,
|
|
||||||
dts: true,
|
|
||||||
treeshake: true,
|
|
||||||
platform: "node",
|
|
||||||
define: {
|
|
||||||
"process.env.BABEL_TAILWIND_BUILD": "true",
|
|
||||||
},
|
|
||||||
banner: {
|
|
||||||
js: "/* eslint-disable */",
|
|
||||||
},
|
|
||||||
});
|
|
Loading…
x
Reference in New Issue
Block a user