Page MenuHomeSealhub

CSSBuilder.ts
No OneTemporary

CSSBuilder.ts

import { build } from "esbuild";
import { resolve, relative, basename, dirname } from "node:path";
import glob from "tiny-glob";
import { promises as fs } from "fs";
import { walkDir } from "./utils/walk.js";
import { Builder } from "./builder.js";
export class CSSClump {
public files: string[] = [];
constructor(public project_dir: string, public name: string) {}
addFile(full_path: string) {
this.files.push(full_path);
}
async build() {
await this.makeEntrypoint();
const loader = Object.fromEntries(
(<const>[
"png",
"svg",
"jpg",
"gif",
"jpeg",
"otf",
"ttf",
"woff",
"woff2",
]).map((ext) => <const>["." + ext, "file"])
);
const result = await build({
entryPoints: [relative(this.project_dir, this.getEntrypointPath())],
sourcemap: true,
bundle: true,
outdir: "./public/dist",
logLevel: "info",
loader,
minify: true,
target: ["safari16.5"], // to make css nesting work without manually adding `&`
metafile: true,
});
await fs.writeFile(
relative(this.project_dir, "public/dist") +
"/" +
this.name +
".meta.json",
JSON.stringify(result.metafile)
);
}
getEntrypointPath(): string {
return resolve(
this.project_dir,
`src/style-entrypoints/${this.name}.entrypoint.css`
);
}
async makeEntrypoint() {
const entrypoint_path = this.getEntrypointPath();
await fs.mkdir(dirname(entrypoint_path), { recursive: true });
await fs.writeFile(
entrypoint_path,
[
`/* DO NOT EDIT! Automatically generated by sealgen */`,
...this.files
.sort()
.map(
(fullpath) =>
`@import "${relative(
dirname(entrypoint_path),
fullpath
)}";`
),
].join("\n")
);
}
static assignFileToClumps(file_path: string): string[] {
const segments = basename(file_path).split(".");
segments.shift(); // filename
segments.pop(); // extension
if (!segments.length) {
if (file_path.includes("/jdd-components/")) {
const component_name = file_path.match(
/jdd-components\/([^/]+)\//
)?.[1];
if (!component_name) {
return [];
}
return ["jdd-components", "jdd-component__" + component_name];
} else {
// no segments, default clump
return ["default"];
}
}
if (segments.length == 1 && segments[0] == "entrypoint") {
return [];
}
return segments;
}
static async findCSSClumps(
project_dir: string,
style_dirs: string[]
): Promise<CSSClump[]> {
const clumps: Record<string, CSSClump> = {};
const dirs_to_scan = [resolve(project_dir, "src"), ...style_dirs];
const all_files = (await Promise.all(dirs_to_scan.map(walkDir))).flat();
for (const file of all_files) {
if (!file.endsWith(".css")) continue;
const clump_names = CSSClump.assignFileToClumps(file);
for (const clump_name of clump_names) {
if (!clump_name) continue;
if (!clumps[clump_name]) {
clumps[clump_name] = new CSSClump(project_dir, clump_name);
}
clumps[clump_name]?.addFile(file);
}
}
return Object.values(clumps);
}
}
export class CSSBuilder extends Builder {
getName(): string {
return "css";
}
ownsFile(file_path: string) {
return (
file_path.endsWith(".css") && !file_path.endsWith("/includes.css")
);
}
async dispose(): Promise<void> {
//noop
}
async _build() {
try {
const entrypoints = await glob(`./src/*.entrypoint.css`);
await Promise.allSettled(
entrypoints.map((rel_path) =>
fs.unlink(resolve(this.project_dir, rel_path))
)
);
const clumps = await CSSClump.findCSSClumps(
this.project_dir,
this.style_dirs
);
await Promise.all(clumps.map((clump) => clump.build()));
} catch (e) {
console.error(e);
}
}
}

File Metadata

Mime Type
text/x-java
Expires
Tue, Dec 24, 14:05 (12 h, 28 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
557303
Default Alt Text
CSSBuilder.ts (3 KB)

Event Timeline