babel-tailwind/src/esbuild-postcss.ts
2024-04-07 03:13:05 -04:00

91 lines
2.5 KiB
TypeScript

import { dirname, join } from "node:path";
import type * as esbuild from "esbuild";
import { CssSyntaxError } from "postcss";
import { type Compile, type StyleMap, pkgName, toCSSText } from "./shared";
const PLUGIN_NAME = "tailwind";
const ESBUILD_NAMESPACE = "babel-tailwind";
export const esbuildPlugin = (styleMap: StyleMap, compile: Compile): esbuild.Plugin => ({
name: PLUGIN_NAME,
setup(build) {
build.onResolve({ filter: /^tailwind:.+\.css$/ }, ({ path, importer }) => {
const resolved = join(dirname(importer), path.replace(/^tailwind:/, ""));
if (styleMap.has(resolved)) {
return {
path: resolved,
namespace: ESBUILD_NAMESPACE,
};
}
});
build.onResolve({ filter: RegExp(`^${pkgName}/base$`) }, () => ({
path: "directive:base",
namespace: ESBUILD_NAMESPACE,
}));
build.onLoad({ filter: /.*/, namespace: ESBUILD_NAMESPACE }, async ({ path }) => {
if (path === "directive:base") {
return {
contents: (await compile("@tailwind base")).css,
loader: "css",
};
}
if (!styleMap.has(path)) return;
const styles = styleMap.get(path)!;
try {
const result = await compile(toCSSText(styles));
return {
contents: result.css,
loader: "css",
};
} 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) {
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,
},
],
};
}
throw e;
}
});
},
});