forked from darkreader/darkreader
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathformat-css.ts
More file actions
74 lines (66 loc) · 2.61 KB
/
Copy pathformat-css.ts
File metadata and controls
74 lines (66 loc) · 2.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import {isParsedStyleRule, parseCSS} from '../css-text/parse-css';
import type {ParsedAtRule, ParsedCSS, ParsedDeclaration, ParsedStyleRule} from '../css-text/parse-css';
export function formatCSS(cssText: string): string {
const parsed = parseCSS(cssText);
return formatParsedCSS(parsed);
}
export function formatParsedCSS(parsed: ParsedCSS): string {
const lines: string[] = [];
const tab = ' ';
function formatRule(rule: ParsedAtRule | ParsedStyleRule, indent: string) {
if (isParsedStyleRule(rule)) {
formatStyleRule(rule as ParsedStyleRule, indent);
} else {
formatAtRule(rule, indent);
}
}
function formatAtRule({type, query, rules}: ParsedAtRule, indent: string) {
lines.push(`${indent}${type} ${query} {`);
rules.forEach((child) => formatRule(child, `${indent}${tab}`));
lines.push(`${indent}}`);
}
function formatStyleRule({selectors, declarations}: ParsedStyleRule, indent: string) {
const lastSelectorIndex = selectors.length - 1;
selectors.forEach((selector, i) => {
lines.push(`${indent}${selector}${i < lastSelectorIndex ? ',' : ' {'}`);
});
const sorted = sortDeclarations(declarations);
sorted.forEach(({property, value, important}) => {
lines.push(`${indent}${tab}${property}: ${value}${important ? ' !important' : ''};`);
});
lines.push(`${indent}}`);
}
clearEmptyRules(parsed);
parsed.forEach((rule) => formatRule(rule, ''));
return lines.join('\n');
}
function sortDeclarations(declarations: ParsedDeclaration[]) {
const prefixRegex = /^-[a-z]-/;
return [...declarations].sort((a, b) => {
const aProp = a.property;
const bProp = b.property;
const aPrefix = aProp.match(prefixRegex)?.[0] ?? '';
const bPrefix = bProp.match(prefixRegex)?.[0] ?? '';
const aNorm = aPrefix ? aProp.replace(prefixRegex, '') : aProp;
const bNorm = bPrefix ? bProp.replace(prefixRegex, '') : bProp;
if (aNorm === bNorm) {
return aPrefix.localeCompare(bPrefix);
}
return aNorm.localeCompare(bNorm);
});
}
function clearEmptyRules(rules: Array<ParsedAtRule | ParsedStyleRule>) {
for (let i = rules.length - 1; i >= 0; i--) {
const rule = rules[i];
if (isParsedStyleRule(rule)) {
if (rule.declarations.length === 0) {
rules.splice(i, 1);
}
} else {
clearEmptyRules(rule.rules);
if (rule.rules.length === 0) {
rules.splice(i, 1);
}
}
}
}