mirror of
https://github.com/def-peter/obsidian-aera-theme.git
synced 2026-07-22 05:00:27 +00:00
103 lines
3 KiB
JavaScript
103 lines
3 KiB
JavaScript
import postcss from "postcss";
|
|
|
|
const allowedThemeSelectors = new Set([
|
|
".theme-light",
|
|
".theme-dark",
|
|
"body",
|
|
".callout",
|
|
".callout-title",
|
|
".callout-content",
|
|
".callout-icon",
|
|
".callout-icon svg",
|
|
".callout-fold",
|
|
".callout.is-collapsed .callout-icon",
|
|
":where(.markdown-rendered pre:not(.frontmatter))",
|
|
":where(.markdown-rendered pre:not(.frontmatter)) code[class*=language-]",
|
|
":where(.markdown-source-view.mod-cm6 .HyperMD-codeblock)",
|
|
".markdown-source-view.mod-cm6 :where(.HyperMD-codeblock)",
|
|
".markdown-rendered blockquote",
|
|
".markdown-rendered blockquote:not(blockquote blockquote)::before",
|
|
".markdown-rendered blockquote:not(blockquote blockquote)::after",
|
|
".markdown-source-view.mod-cm6.is-live-preview .HyperMD-quote-1:not(.HyperMD-quote + .HyperMD-quote)",
|
|
".markdown-source-view.mod-cm6.is-live-preview .HyperMD-quote-1:not(.HyperMD-quote + .HyperMD-quote)::before",
|
|
".markdown-source-view.mod-cm6.is-live-preview .HyperMD-quote-1:not(.HyperMD-quote + .HyperMD-quote)::after",
|
|
".markdown-source-view.mod-cm6:not(.is-live-preview) .HyperMD-quote",
|
|
]);
|
|
const officialCalloutTypeVariables = new Set([
|
|
"--callout-bug",
|
|
"--callout-default",
|
|
"--callout-error",
|
|
"--callout-example",
|
|
"--callout-fail",
|
|
"--callout-important",
|
|
"--callout-info",
|
|
"--callout-question",
|
|
"--callout-success",
|
|
"--callout-summary",
|
|
"--callout-tip",
|
|
"--callout-todo",
|
|
"--callout-warning",
|
|
"--callout-quote",
|
|
]);
|
|
|
|
export function declarationsFor(css, selector) {
|
|
const declarations = new Map();
|
|
const root = postcss.parse(css);
|
|
|
|
root.walkRules((rule) => {
|
|
if (rule.selector !== selector) return;
|
|
|
|
for (const node of rule.nodes ?? []) {
|
|
if (node.type === "decl") declarations.set(node.prop, node.value);
|
|
}
|
|
});
|
|
|
|
return declarations;
|
|
}
|
|
|
|
export function selectorsFor(css) {
|
|
const selectors = new Set();
|
|
|
|
postcss.parse(css).walkRules((rule) => selectors.add(rule.selector));
|
|
|
|
return selectors;
|
|
}
|
|
|
|
export function assertThemeStructure(css) {
|
|
const root = postcss.parse(css);
|
|
|
|
root.walkDecls((declaration) => {
|
|
if (officialCalloutTypeVariables.has(declaration.prop)) {
|
|
throw new Error(
|
|
`${declaration.prop} is a forbidden semantic type variable`,
|
|
);
|
|
}
|
|
|
|
if (!declaration.prop.startsWith("--callout-")) return;
|
|
|
|
const selector =
|
|
declaration.parent?.type === "rule"
|
|
? declaration.parent.selector
|
|
: "a non-rule context";
|
|
if (selector !== "body") {
|
|
throw new Error(
|
|
`${declaration.prop} must be declared only on body; found ${selector}`,
|
|
);
|
|
}
|
|
});
|
|
|
|
const selectors = new Set();
|
|
root.walkRules((rule) => selectors.add(rule.selector));
|
|
const unexpected = [...selectors].filter(
|
|
(selector) => !allowedThemeSelectors.has(selector),
|
|
);
|
|
const missing = [...allowedThemeSelectors].filter(
|
|
(selector) => !selectors.has(selector),
|
|
);
|
|
|
|
if (unexpected.length > 0 || missing.length > 0) {
|
|
throw new Error(
|
|
`unexpected theme selector set: unexpected [${unexpected.join(", ")}], missing [${missing.join(", ")}]`,
|
|
);
|
|
}
|
|
}
|