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";
export function style(options) {
if (options.inline || options.css === "none") return "";
const rules = load(options);
const schemes = rules.find(({ media }) => media === "preferes-color-scheme")
? `\n`
: "";
return ``;
}
export const inline = (options) => {
const rules = load(options);
return (tag) => {
if (!options.inline || options.css === "none") 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) {
options.css =
options.css ||
(!options.body || !options.inline ? "gmi-web.css" : "gmi.css");
console.log("load:", options.css);
if (
["gmi", "web", "gmi.css", "gmi-web.css"].includes(options.css) ||
existsSync(options.css)
) {
const packageRoot = (file) =>
path.resolve(path.dirname(new URL(import.meta.url).pathname), file);
return parse(
readFileSync(
path.resolve(
["gmi-web.css", "web"].includes(options.css)
? packageRoot("gmi-web.css")
: ["gmi.css", "gmi"].includes(options.css)
? packageRoot("gmi.css")
: resolve(options.css)
),
"utf-8"
)
).stylesheet.rules
} else {
throw new Error(`Cannot find file ${options.css}.`);
}
}
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]: value }),
{}
);
}
function reduceVariables(rules, options) {
const defaultVariables = rootVariables(rules);
const CSS_VAR = /(^var\((?.+)\)|(?.+))/;
return rules
.filter(({ selectors }) => selectors && !selectors.includes(":root"))
.map((rule) => {
return Object.assign(rule, {
declarations: rule.declarations.map((declaration) => {
let { key, val } = CSS_VAR.exec(declaration.value).groups;
// 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,
});
}),
});
});
}