Files
crates/crates/less.plugin.ts
T
2025-04-25 19:01:47 +10:00

116 lines
3.2 KiB
TypeScript

import { Plugin } from "$fresh/server.ts";
import { ensureDir } from "$std/fs/ensure_dir.ts";
import { join } from "$std/path/join.ts";
import { parse } from "$std/path/parse.ts";
import { dirname } from "$std/path/dirname.ts";
// External Less compiler for Deno
const LESS_CDN = "https://esm.sh/less@4.2.0";
export interface LessPluginOptions {
path: string;
outDir: string;
}
export default function lessPlugin(options: LessPluginOptions): Plugin {
let watcher: Deno.FsWatcher | null = null;
const combinedCssPath = join(options.outDir, "styles.combined.css");
let less: any;
// Compile a Less file and return its CSS
async function compileLessFile(path: string): Promise<string> {
try {
const lessContent = await Deno.readTextFile(path);
const { css } = await less.render(lessContent);
return css;
} catch (err) {
console.error(`Error compiling ${path}:`, err);
return "";
}
}
// Combine all compiled CSS into a single file
async function combineAndWriteCss(paths) {
try {
const compiledCssArray: string[] = [];
for (const path of paths) {
const css = await compileLessFile(path);
compiledCssArray.push(css);
}
const combinedCss = compiledCssArray.join("\n");
await Deno.writeTextFile(combinedCssPath, combinedCss);
console.log(`Combined CSS written to ${combinedCssPath}`);
} catch (err) {
console.error("Error combining CSS:", err);
}
}
// Start watching the style directory
async function startWatcher() {
try {
// Determine directory to watch (assuming first path is representative)
const watchDir = dirname(options.path);
console.log(`Starting watcher on ${watchDir}`);
watcher = Deno.watchFs(watchDir);
(async () => {
for await (const event of watcher!) {
if (event.kind === "modify" || event.kind === "create") {
const lessFiles = event.paths.filter(p => p.endsWith(".less"));
if (lessFiles.length > 0) {
console.log(`Less files changed: ${lessFiles.join(", ")}`);
await combineAndWriteCss(lessFiles);
}
}
}
})();
} catch (err) {
console.error("Error starting watcher:", err);
}
}
return {
name: "less",
async buildStart() {
try {
// Import the Less compiler
const lessModule = await import(LESS_CDN);
less = lessModule.default;
// Ensure the output directory exists
await ensureDir(options.outDir);
// Initial compilation
await combineAndWriteCss();
// Start the file watcher
await startWatcher();
} catch (err) {
console.error("Error initializing Less plugin:", err);
}
},
render(ctx) {
// Generate a link to the combined CSS file
const href = "/css/styles.combined.css"
ctx.render();
return {
links: [{ rel: "stylesheet", href }],
};
},
async buildEnd() {
// Clean up watcher when build ends
if (watcher) {
try {
watcher.close();
} catch (err) {
console.error("Error closing watcher:", err);
}
}
},
};
}