|
2 | 2 | // that works in Deno and on the web |
3 | 3 |
|
4 | 4 | import { walk } from "./ast-utils.ts"; |
5 | | - |
| 5 | +let counter = 1; |
6 | 6 | export const makeParserModule = ( |
7 | 7 | parse: any, |
8 | | - prettierFormat: any |
9 | 8 | ) => { |
10 | 9 | return { |
11 | | - getSassAst: async (contents: string) => { |
| 10 | + getSassAst: (contents: string) => { |
12 | 11 | // scss-parser doesn't support the `...` operator and it breaks their parser oO, so we remove it. |
13 | 12 | // our analysis doesn't need to know about it. |
14 | 13 | contents = contents.replaceAll("...", "_dot_dot_dot"); |
15 | 14 | // it also doesn't like some valid ways to do '@import url' |
16 | 15 | contents = contents.replaceAll("@import url", "//@import url"); |
17 | 16 |
|
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. |
21 | 43 |
|
22 | 44 | // Create an AST from a string of SCSS |
23 | 45 | // and convert it to a plain JSON object |
24 | 46 | const ast = JSON.parse(JSON.stringify(parse(contents))); |
25 | 47 |
|
26 | 48 | if (!(ast.type === "stylesheet")) { |
27 | 49 | throw new Error("Expected AST to have type 'stylesheet'"); |
28 | | - }; |
| 50 | + } |
29 | 51 | if (!Array.isArray(ast.value)) { |
30 | 52 | throw new Error("Expected AST to have an array value"); |
31 | 53 | } |
@@ -53,6 +75,6 @@ export const makeParserModule = ( |
53 | 75 | }); |
54 | 76 |
|
55 | 77 | return ast; |
56 | | - } |
57 | | - } |
58 | | -} |
| 78 | + }, |
| 79 | + }; |
| 80 | +}; |
0 commit comments