first commit
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Global stylesheet pipeline.
|
||||
*
|
||||
* Convention: `app/styles/global.css` is the entry. If it is absent but other
|
||||
* `app/styles/*.css` files exist, they are aggregated into one entry. The entry
|
||||
* is bundled by Bun's CSS bundler, which resolves `@import` — including from
|
||||
* node_modules — so any npm CSS framework (Bootstrap, etc.) works by importing
|
||||
* it. A custom `process` hook can replace the bundler for Tailwind/PostCSS/Sass.
|
||||
*/
|
||||
|
||||
import { existsSync, readdirSync, mkdirSync, writeFileSync } from "node:fs";
|
||||
import { isAbsolute, join, relative } from "node:path";
|
||||
import type { Mode } from "./config.ts";
|
||||
|
||||
const fwd = (p: string) => p.replace(/\\/g, "/");
|
||||
|
||||
/**
|
||||
* Resolve the CSS entry for an app.
|
||||
* - `override` (from config.styles.entry) is resolved relative to `appRoot`.
|
||||
* - otherwise prefer `app/styles/global.css`.
|
||||
* - otherwise aggregate all `app/styles/*.css` into a generated entry.
|
||||
* Returns null when the app has no styles.
|
||||
*/
|
||||
export function findStyleEntry(appDir: string, appRoot: string, override?: string): string | null {
|
||||
if (override) {
|
||||
const p = isAbsolute(override) ? override : join(appRoot, override);
|
||||
return existsSync(p) ? p : null;
|
||||
}
|
||||
|
||||
const stylesDir = join(appDir, "styles");
|
||||
const globalCss = join(stylesDir, "global.css");
|
||||
if (existsSync(globalCss)) return globalCss;
|
||||
|
||||
if (existsSync(stylesDir)) {
|
||||
const cssFiles = readdirSync(stylesDir)
|
||||
.filter((f) => f.endsWith(".css"))
|
||||
.sort();
|
||||
if (cssFiles.length > 0) {
|
||||
const cacheDir = join(appDir, ".wrnexus");
|
||||
mkdirSync(cacheDir, { recursive: true });
|
||||
const entry = join(cacheDir, "styles-entry.css");
|
||||
const imports = cssFiles
|
||||
.map((f) => `@import "${fwd(relative(cacheDir, join(stylesDir, f)))}";`)
|
||||
.join("\n");
|
||||
writeFileSync(entry, imports + "\n", "utf8");
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bundle a CSS entry into a single stylesheet string using Bun's CSS bundler.
|
||||
* Resolves `@import` (local and node_modules), handles nesting, minifies in prod.
|
||||
*/
|
||||
export async function bundleCss(entryPath: string, mode: Mode): Promise<string> {
|
||||
const result = await Bun.build({
|
||||
entrypoints: [entryPath],
|
||||
minify: mode === "production",
|
||||
});
|
||||
if (!result.success) {
|
||||
throw new Error("CSS bundle failed:\n" + result.logs.map(String).join("\n"));
|
||||
}
|
||||
const cssOutput = result.outputs.find((o) => o.path.endsWith(".css")) ?? result.outputs[0];
|
||||
return await cssOutput!.text();
|
||||
}
|
||||
Reference in New Issue
Block a user