import { readFileSync, existsSync } from "fs"; // TODO import.meta.resolve is supposed to accomplish this without "path" import path from "path"; import { stringify, parse } from "css"; const internal = (file) => path.resolve(path.dirname(new URL(import.meta.url).pathname), file); export function style(options) { if (options.inline || options.css === "none") return ""; const rules = load(options); const schemes = rules.find(({ media }) => media) ? `` : ""; return `\n${schemes}\n`; } export function inline(options) { const rules = load(options); return function inlineCSS(tag) { if (options.css === "none" || !(options.inline || options.body)) return ""; const styles = reduceVariables(rules, options) .filter(({ selectors }) => selectors && selectors.includes(tag)) .reduce((style, { declarations }) => { declarations.forEach(({ property, value }) => { style = `${style}${property}:${value};`; }); return style; }, ""); return styles !== "" ? ` style="${styles}"` : ""; }; } export function load(options) { if (options.css === "none") return []; options.css = options.css ? options.css : options.body ? "gmi.css" : "gmi-web.css"; if ( !["gmi.css", "gmi-web.css"].includes(options.css) && !existsSync(options.css) ) throw new Error(`Cannot find file ${options.css}.`); return parse( readFileSync( path.resolve( ["gmi-web.css", "gmi.css"].includes(options.css) ? internal(options.css) : path.resolve(options.css) ), "utf-8" ) ).stylesheet.rules; } export function rootVariables(rules) { const root = rules.find( ({ selectors }) => selectors && selectors.includes(":root") ); if (!root) return {}; return root.declarations.reduce( (obj, { property, value }) => !/^--/.test(property) ? obj : Object.assign(obj, { [property.replace("--", "")]: value }), {} ); } function reduceVariables(rules, options = {}) { const defaultVariables = rootVariables(rules); const CSS_VAR = /(^var\(--(?.+)\)|(?.+))/; const reduce = (rule) => Object.assign( rule, rule.declarations ? { declarations: rule.declarations.map((declaration) => { let key = CSS_VAR.exec(declaration.value).groups.key; // only one level of variable referencing is supported key = CSS_VAR.exec(options[key] || defaultVariables[key]).groups .key || key; return Object.assign(declaration, { value: key ? options[key] || defaultVariables[key] : declaration.value, }); }), } : {} ); return rules .map(reduce) .map((rule) => { if (!rule.media) return rule; return Object.assign(rule, { rules: rule.rules.map(reduce), }); }) .filter( ({ selectors, media }) => media || (selectors && !selectors.includes(":root")) ); }