Skip to content

Commit 7e7b73c

Browse files
authored
Fix support for latest GitHub styles (#28)
1 parent 4b791ea commit 7e7b73c

File tree

3 files changed

+94
-29
lines changed

3 files changed

+94
-29
lines changed

cli.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const cli = meow(
2323
and dark themes match or if type is not 'auto'
2424
--only-style Only output the styles, forces --preserve-variables on
2525
--only-variables Only output the variables for the specified themes
26-
--rootSelector Specify the root selector when outputting styles, default '.markdown-body'
26+
--root-selector Specify the root selector when outputting styles, default '.markdown-body'
2727
2828
Examples
2929
$ github-markdown-css --list
@@ -40,7 +40,7 @@ const cli = meow(
4040
$ github-markdown-css --theme=dark_dimmed --only-variables
4141
[CSS with single variable block for 'dark_dimmed' theme with no element styles]
4242
43-
$ github-markdown-css --onlyStyles
43+
$ github-markdown-css --only-style
4444
[CSS with only element styles using variables but no variables set.
4545
Use in combination with output from setting --only-variables]
4646
`,

index.js

Lines changed: 90 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,43 @@ function extractColors(colors, name, ast) {
1818
colors[name] = Object.assign([], {name});
1919
colors.push(colors[name]);
2020

21+
function pushOrReplace(declaration) {
22+
const {property, value} = declaration;
23+
if (property in colors[name]) {
24+
if (colors[name][property] !== value) {
25+
colors[name][property] = value;
26+
const index = colors[name].findIndex(declaration => declaration.property === property);
27+
colors[name][index] = declaration;
28+
}
29+
} else {
30+
colors[name][property] = value;
31+
colors[name].push(declaration);
32+
}
33+
}
34+
2135
for (const rule of walkRules(ast)) {
2236
for (const declaration of rule.declarations) {
2337
if (declaration.type === 'declaration') {
24-
const {property, value} = declaration;
25-
colors[name][property] = value;
26-
colors[name].push(declaration);
38+
pushOrReplace(declaration);
2739
}
2840
}
2941
}
3042

43+
colors[name]['--base-size-8'] = '8px';
44+
colors[name]['--base-size-16'] = '16px';
3145
colors[name]['--base-text-weight-normal'] = '400';
46+
colors[name]['--base-text-weight-medium'] = '500';
3247
colors[name]['--base-text-weight-semibold'] = '600';
3348
}
3449

35-
// https://github.com/gjtorikian/html-pipeline/blob/main/lib/html/pipeline/sanitization_filter.rb
50+
// https://github.com/gjtorikian/html-pipeline/blob/main/lib/html_pipeline/sanitization_filter.rb
3651
const ALLOW_TAGS = new Set([
3752
'h1',
3853
'h2',
3954
'h3',
4055
'h4',
4156
'h5',
4257
'h6',
43-
'h7',
44-
'h8',
4558
'br',
4659
'b',
4760
'i',
@@ -58,6 +71,7 @@ const ALLOW_TAGS = new Set([
5871
'sup',
5972
'sub',
6073
'p',
74+
'picture',
6175
'ol',
6276
'ul',
6377
'table',
@@ -93,6 +107,7 @@ const ALLOW_TAGS = new Set([
93107
'dfn',
94108
'mark',
95109
'small',
110+
'source',
96111
'span',
97112
'time',
98113
'wbr',
@@ -112,14 +127,19 @@ const ALLOW_CLASS = new Set([
112127
'.task-list-item',
113128
'.task-list-item-checkbox',
114129
// For Markdown alerts.
130+
'.octicon-info',
131+
'.octicon-light-bulb',
132+
'.octicon-report',
133+
'.octicon-alert',
134+
'.octicon-stop',
115135
'.markdown-alert',
116-
'.color-fg-accent',
117-
'.color-fg-attention',
118-
'.color-fg-done',
119-
'.text-semibold',
120-
'.d-inline-flex',
121-
'.flex-items-center',
122-
'.mb-1',
136+
'.markdown-alert-title',
137+
'.markdown-alert-note',
138+
'.markdown-alert-tip',
139+
'.markdown-alert-important',
140+
'.markdown-alert-warning',
141+
'.markdown-alert-caution',
142+
'.mr-2',
123143
]);
124144

125145
function extractStyles(rules, ast) {
@@ -182,14 +202,23 @@ function extractStyles(rules, ast) {
182202
}
183203

184204
for (const rule of walkRules(ast)) {
185-
if (rule.declarations.some(({value}) => value.includes('prettylights'))) {
205+
if (!rule.selectors.some(selector => selector.includes('QueryBuilder'))
206+
&& rule.declarations.some(({value}) => value.includes('prettylights'))) {
186207
rules.push(rule);
187208
} else {
188209
rule.selectors = rule.selectors
189210
.filter(selector => select(selector))
190211
.map(selector => fixSelector(selector));
191212
if (rule.selectors.length > 0) {
192213
rule.declarations.map(declaration => fixDeclaration(declaration));
214+
215+
// '-webkit-appearance: x' << 'appearance: x'
216+
const index = rule.declarations.findIndex(declaration => declaration.property === '-webkit-appearance');
217+
if (index >= 0) {
218+
const {value} = rule.declarations[index];
219+
rule.declarations.splice(index + 1, 0, {type: 'declaration', property: 'appearance', value});
220+
}
221+
193222
rules.push(rule);
194223
}
195224
}
@@ -282,22 +311,57 @@ const manuallyAddedStyle = `
282311
function applyColors(colors, rules) {
283312
for (const rule of rules) {
284313
for (const declaration of rule.declarations) {
285-
const match = /var\((?<name>.+?)\)/.exec(declaration.value);
286-
if (match) {
287-
let {name} = match.groups;
288-
name = name.split(',')[0];
289-
if (name === '--color-text-primary') {
290-
name = '--color-fg-default';
291-
}
314+
if (declaration.value.includes('var(')) {
315+
declaration.value = declaration.value.replaceAll(/var\((.+?)\)/g, (match, name) => {
316+
name = name.split(',')[0];
317+
if (name === '--color-text-primary') {
318+
name = '--color-fg-default';
319+
}
320+
321+
if (name in colors) {
322+
return colors[name];
323+
}
324+
325+
return match[0];
326+
});
327+
}
328+
}
329+
}
330+
331+
return rules;
332+
}
292333

293-
if (name in colors) {
294-
declaration.value = declaration.value.replace(match[0], colors[name]);
334+
// Workaround for module 'css' does not understand new CSS syntaxes (@container, etc.)
335+
// Strip them as they are not used in the output anyway.
336+
function patchCSSText(cssText) {
337+
function strip(mark, left = '{', right = '}') {
338+
const ranges = [];
339+
340+
let i = -1;
341+
while ((i = cssText.indexOf(mark, i + 1)) >= 0) {
342+
let j = cssText.indexOf(left, i) + 1;
343+
let depth = 1;
344+
while (depth > 0) {
345+
if (cssText[j] === left) {
346+
depth++;
347+
} else if (cssText[j] === right) {
348+
depth--;
295349
}
350+
351+
j++;
296352
}
353+
354+
ranges.push([i, j]);
355+
}
356+
357+
for (const [i, j] of ranges.reverse()) {
358+
cssText = cssText.slice(0, i) + cssText.slice(j);
297359
}
298360
}
299361

300-
return rules;
362+
strip('@container');
363+
364+
return cssText;
301365
}
302366

303367
/**
@@ -356,7 +420,8 @@ export default async function getCSS({
356420
}
357421

358422
const [name] = match;
359-
const ast = css.parse(cssText);
423+
const patched = patchCSSText(cssText);
424+
const ast = css.parse(patched);
360425

361426
// If it's a theme variable file extract colors, otherwise extract style
362427
if (/^(light|dark)/.test(name)) {

readme.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ $ github-markdown-css --help
7676
and dark themes match or if type is not 'auto'
7777
--only-style Only output the styles, forces --preserve-variables on
7878
--only-variables Only output the variables for the specified themes
79-
--rootSelector Specify the root selector when outputting styles, default '.markdown-body'
79+
--root-selector Specify the root selector when outputting styles, default '.markdown-body'
8080
8181
Examples
8282
$ github-markdown-css --list
@@ -93,7 +93,7 @@ $ github-markdown-css --help
9393
$ github-markdown-css --theme=dark_dimmed --only-variables
9494
[CSS with single variable block for 'dark_dimmed' theme with no element styles]
9595
96-
$ github-markdown-css --onlyStyles
96+
$ github-markdown-css --only-style
9797
[CSS with only element styles using variables but no variables set.
9898
Use in combination with output from setting --only-variables]
9999
```

0 commit comments

Comments
 (0)