@styn/tree
Version:
127 lines (104 loc) • 3.5 kB
JavaScript
// Hey, hi! You can see an example of tree in the test file.
const i = (content, count) => `${" ".repeat(count)}${content}`; // indentation
const linewrap = content => content[content.length - 1] === "\n" ? content : `${content}\n`;
const block = (content, _i = 0) => {
const open = `{\n`;
const close = i("}", _i);
return `${open}${linewrap(content)}${close}\n`;
};
const normalizeProp = prop => [...prop].map(l => l.toLowerCase() === l ? l : `-${l.toLowerCase()}`).join("");
const join = (arr, fn = x => x, ...args) => {
return arr.map(item => fn(item, ...args)).map(linewrap).join("");
};
const stringify = tree => {
const stringifyDeclarations = (declarations, _i = 0) => {
return block(Object.keys(declarations).map(property => {
const prop = normalizeProp(property);
const value = declarations[property];
if (typeof value === "object") return undefined;
return linewrap(i(`${prop}: ${value};`, _i + 2));
}).filter(Boolean).join(""), _i);
};
const stringifyRule = (rule, _i = 0) => {
const declarations = stringifyDeclarations(rule.declarations, _i);
return linewrap(i(`${rule.selector} ${declarations}`, _i));
};
const stringifyAtRule = (atRule, _i = 0) => {
const keyword = atRule.keyword;
const values = atRule.values ? ` ${atRule.values.join(" ")}` : "";
if (atRule.declarations) {
const declarations = stringifyDeclarations(atRule.declarations, _i);
return linewrap(i(`${keyword}${values} ${declarations}`, _i));
} else if (atRule.rules) {
const rules = atRule.rules.map(rule => stringifyRule(rule, _i + 2)).join("");
return linewrap(i(`${keyword}${values} ${block(rules, _i)}`, _i));
} else {
return linewrap(i(`${keyword}${values};`, _i));
}
};
const stringifyStynRule = rule => {
if (rule.type === "at-rule") {
return stringifyAtRule(rule);
} else if (rule.type === "rule") {
return stringifyRule(rule);
}
return "";
};
return join(tree.rules.map(stringifyStynRule).filter(Boolean));
};
const parse = object => {
const rules = [];
for (const prop in object) {
const value = object[prop];
if (prop.startsWith("@")) {
// Handle at-rules
const atRule = {
type: "at-rule",
keyword: prop
};
if (prop.startsWith("@font-face")) {
// Handle at-rules with declarations
atRule.declarations = value;
} else if (typeof value === "object") {
// Handle at-rules with selectors (rules)
atRule.rules = [];
for (const selector in value) {
atRule.rules.push({
type: "rule",
selector,
declarations: value[selector]
});
}
} else if (typeof value === "string") {
atRule.values = [value];
}
rules.push(atRule);
} else {
// Handle normal rules
const rule = {
type: "rule",
selector: prop,
declarations: value
};
rules.push(rule);
}
}
return {
rules,
meta: {}
};
};
const walk = (tree, cb) => {
const treeCopy = { ...tree
};
for (const rule of treeCopy.rules) {
cb(rule, treeCopy.rules, treeCopy.rules.indexOf(rule));
if (rule.type === "at-rule" && typeof rule.rules !== "undefined") {
for (const childRule of rule.rules) {
cb(childRule, rule.rules, rule.rules.indexOf(childRule));
}
}
}
return treeCopy;
};
export { parse, stringify, walk };