import { merge } from "../core/utils.ts";import { Page } from "../core/filesystem.ts";import { Config, CSSParser, HTMLParser, Processor, StyleSheet,} from "../deps/windi_css.ts";
import type { Element, HTMLDocument } from "../deps/dom.ts";import type { DeepPartial, Site } from "../core.ts";
export interface Options { minify: boolean;
mode: "interpret" | "compile";
cssFile: string | false;
config: Config;
preflight: boolean;}
const defaults: Options = { minify: false, cssFile: false, mode: "interpret", config: {}, preflight: true,};
export default function (userOptions: DeepPartial<Options> = {}) { const options = merge(defaults, userOptions) as Options;
return (site: Site) => { const processor = new Processor(); options.config = processor.loadConfig(options.config);
site.loadAssets([".windi.css"]); site.process([".windi.css"], (page) => { const parser = new CSSParser(page.content as string, processor);
page.content = parser.parse().build(options.minify); const url = page.data.url as string; page.data.url = url.replace(/\.windi\.css$/, ".css"); });
const { cssFile } = options;
if (cssFile === false) { site.process([".html"], (page) => { const scopedsheet = windi(page, processor, options).sort().combine(); const code = scopedsheet.build(options.minify);
if (code) { const style = page.document!.createElement("style"); style.innerText = scopedsheet.build(options.minify); page.document?.head?.appendChild(style); } }); } else { site.addEventListener("afterRender", () => { let stylesheet = new StyleSheet();
const pages = site.pages .filter((page) => page.outputPath?.endsWith(".html"));
stylesheet = pages .map((page) => windi(page, processor, options)) .reduce( (previous, current) => previous.extend(current), stylesheet, ).sort().combine();
const exists = site.pages.find((page) => page.data.url === cssFile);
if (exists) { exists.content = `${exists.content}\n${ stylesheet.build(options.minify) }`; } else { site.pages.push( Page.create(cssFile, stylesheet.build(options.minify)), ); } }); } };}
export function windi(page: Page, processor: Processor, options: Options) { const content = page.content as string; const parser = new HTMLParser(content);
let stylesheet = new StyleSheet(); let html = ""; let index = 0;
for (const className of parser.parseClasses()) { html += content.substring(index, className.start); index = className.end;
if (options.mode === "interpret") { const interpreted = processor.interpret(className.result); html += [...interpreted.success, ...interpreted.ignored].join(" "); stylesheet = stylesheet.extend(interpreted.styleSheet); } else if (options.mode === "compile") { const compiled = processor.compile( className.result, options.config.prefix, ); html += [compiled.className, ...compiled.ignored].join(" "); stylesheet = stylesheet.extend(compiled.styleSheet); } }
page.content = html + content.substring(index);
if (options.config.attributify) { const attrs: { [key: string]: string | string[] } = parser .parseAttrs() .reduceRight((a: { [key: string]: string | string[] }, b: any) => { if (b.key === "class" || b.key === "className") return a; if (b.key in a) { a[b.key] = Array.isArray(a[b.key]) ? Array.isArray(b.value) ? [...(a[b.key] as string[]), ...b.value] : [...(a[b.key] as string[]), b.value] : [ a[b.key] as string, ...(Array.isArray(b.value) ? b.value : [b.value]), ]; return a; } return Object.assign(a, { [b.key]: b.value }); }, {}); const attributified = processor.attributify(attrs); stylesheet = stylesheet.extend(attributified.styleSheet); }
(page.document as HTMLDocument).querySelectorAll('style[lang="windi"]') .forEach((node) => { const $style = node as Element, translatedSheet = new CSSParser($style.innerText, processor).parse(); $style.removeAttribute("lang"); $style.innerText = translatedSheet.build(options.minify); });
if (!options.preflight) { return stylesheet; }
const preflightSheet = processor.preflight(content); return stylesheet.extend(preflightSheet);}