1import * as NodeFSP from "node:fs/promises";
2import * as NodeModule from "node:module";
3import * as NodePath from "node:path";
4import * as NodeURL from "node:url";
5
6import { compile } from "tailwindcss";
7
8const directory = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url));
9const appRoot = NodePath.join(directory, "..");
10const sourcePath = NodePath.join(appRoot, "src", "preview", "Annotation.css");
11const preloadPath = NodePath.join(appRoot, "src", "preview", "PickPreload.ts");
12const outputPath = NodePath.join(appRoot, "src", "preview", "AnnotationStyles.generated.ts");
13const require = NodeModule.createRequire(import.meta.url);
14const tailwindRoot = NodePath.dirname(require.resolve("tailwindcss/package.json"));
15
16const [annotationSource, preloadSource, themeSource, preflightSource] = await Promise.all([
17 NodeFSP.readFile(sourcePath, "utf8"),
18 NodeFSP.readFile(preloadPath, "utf8"),
19 NodeFSP.readFile(NodePath.join(tailwindRoot, "theme.css"), "utf8"),
20 NodeFSP.readFile(NodePath.join(tailwindRoot, "preflight.css"), "utf8"),
21]);
22
23const candidates = new Set(
24 Array.from(preloadSource.matchAll(/!?-?[A-Za-z0-9_:@/.[\]()%,-]+/g), (match) => match[0]),
25);
26const compilerInput = [
27 themeSource,
28 preflightSource,
29 annotationSource.replace('@import "tailwindcss";', "@tailwind utilities;"),
30].join("\n");
31const compiler = await compile(compilerInput, { base: appRoot });
32// Shadow-root styles cannot register @property defaults. Make Tailwind's
33// fallback unconditional so border, shadow, and ring utilities have defaults.
34const compiledCss = compiler.build([...candidates]);
35const css = compiledCss.replace(
36 /(@layer properties \{\n) @supports [^\n]*\{\n([\s\S]*?)\n \}\n(\})/,
37 "$1$2\n$3",
38);
39if (css === compiledCss || /@layer properties\s*\{\s*@supports\b/.test(css)) {
40 throw new Error("Tailwind @property fallback was not unwrapped; update the pattern above.");
41}
42const encodedCss = `'${css
43 .replaceAll("\\", "\\\\")
44 .replaceAll("'", "\\'")
45 .replaceAll("\r", "\\r")
46 .replaceAll("\n", "\\n")}'`;
47const moduleSource = `// Generated by scripts/build-preview-annotation-css.mjs. Do not edit.\nexport const previewAnnotationStyles =\n ${encodedCss};\n`;
48
49await NodeFSP.writeFile(outputPath, moduleSource);