Skip to content

Commit 91628f8

Browse files
authored
Merge pull request #11148 from quarto-dev/bugfix/issue-11121
SCSS - add additional parser workaround
2 parents 41369cc + 839482b commit 91628f8

File tree

5 files changed

+55
-11
lines changed

5 files changed

+55
-11
lines changed

src/core/sass.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,6 @@ export async function compileWithCache(
371371
console.warn(
372372
"This is likely a Quarto bug.\nPlease consider reporting it at https://github.com/quarto-dev/quarto-cli,\nalong with the _quarto_internal_scss_error.scss file that can be found in the current working directory.",
373373
);
374-
throw e;
375374
}
376375
return input;
377376
};

src/core/sass/analyzer/parse.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ export const makeParserModule = (
1414
// it also doesn't like some valid ways to do '@import url'
1515
contents = contents.replaceAll("@import url", "//@import url");
1616

17+
// https://github.com/quarto-dev/quarto-cli/issues/11121
18+
// It also doesn't like empty rules
19+
20+
// that long character class rule matches everything in \s except for \n
21+
// using the explanation from regex101.com as a reference
22+
contents = contents.replaceAll(
23+
/^[\t\f\v \u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]*([^\n{]+)([{])\s*([}])$/mg,
24+
"$1$2 /* empty rule */ $3",
25+
);
26+
1727
// it also really doesn't like statements that don't end in a semicolon
1828
// so, in case you are reading this code to understand why the parser is failing,
1929
// ensure that your SCSS has semicolons at the end of every statement.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
format:
3+
html:
4+
theme:
5+
- cerulean
6+
- test.scss
7+
---
8+
9+
## This will crash
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/*-- scss:rules --*/
2+
3+
.agenda {
4+
}

tools/sass-variable-explainer/parse.ts

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,52 @@
22
// that works in Deno and on the web
33

44
import { walk } from "./ast-utils.ts";
5-
5+
let counter = 1;
66
export const makeParserModule = (
77
parse: any,
8-
prettierFormat: any
98
) => {
109
return {
11-
getSassAst: async (contents: string) => {
10+
getSassAst: (contents: string) => {
1211
// scss-parser doesn't support the `...` operator and it breaks their parser oO, so we remove it.
1312
// our analysis doesn't need to know about it.
1413
contents = contents.replaceAll("...", "_dot_dot_dot");
1514
// it also doesn't like some valid ways to do '@import url'
1615
contents = contents.replaceAll("@import url", "//@import url");
1716

18-
// the scss-parser also apparently breaks on Quarto's SCSS unless it's
19-
// been prettified first :shrug:
20-
contents = await prettierFormat(contents, { parser: "scss" });
17+
// https://github.com/quarto-dev/quarto-cli/issues/11121
18+
// It also doesn't like empty rules
19+
20+
// that long character class rule matches everything in \s except for \n
21+
// using the explanation from regex101.com as a reference
22+
contents = contents.replaceAll(
23+
/^[\t\f\v \u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]*([^\n{]+)([{])\s*([}])$/mg,
24+
"$1$2 /* empty rule */ $3",
25+
);
26+
27+
// it also really doesn't like statements that don't end in a semicolon
28+
// so, in case you are reading this code to understand why the parser is failing,
29+
// ensure that your SCSS has semicolons at the end of every statement.
30+
// we try to work around this by adding semicolons at the end of declarations that don't have them
31+
contents = contents.replaceAll(
32+
/^(?!(?=\/\/)|(?=\s*[@#$]))(.*[^}/\s\n;])([\s\n]*)}(\n|$)/mg,
33+
"$1;$2}$3",
34+
);
35+
// It also doesn't like values that follow a colon directly without a space
36+
contents = contents.replaceAll(
37+
/(^\s*[A-Za-z0-9-]+):([^ \n])/mg,
38+
"$1: $2",
39+
);
40+
41+
// This is relatively painful, because unfortunately the error message of scss-parser
42+
// is not helpful.
2143

2244
// Create an AST from a string of SCSS
2345
// and convert it to a plain JSON object
2446
const ast = JSON.parse(JSON.stringify(parse(contents)));
2547

2648
if (!(ast.type === "stylesheet")) {
2749
throw new Error("Expected AST to have type 'stylesheet'");
28-
};
50+
}
2951
if (!Array.isArray(ast.value)) {
3052
throw new Error("Expected AST to have an array value");
3153
}
@@ -53,6 +75,6 @@ export const makeParserModule = (
5375
});
5476

5577
return ast;
56-
}
57-
}
58-
}
78+
},
79+
};
80+
};

0 commit comments

Comments
 (0)