@jesscss/scss-parser 2.0.0-alpha.7 → 2.0.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -12
- package/lib/ast/grammar.cjs +36610 -0
- package/lib/ast/grammar.d.ts +1 -0
- package/lib/ast/grammar.js +36609 -0
- package/lib/ast/lower-user-function-calls.d.ts +6 -0
- package/lib/cst.cjs +1 -1
- package/lib/cst.js +1 -1
- package/lib/grammar.cjs +2 -55568
- package/lib/grammar.js +1 -55567
- package/lib/grammar2.cjs +88858 -0
- package/lib/grammar2.js +88853 -0
- package/lib/index.cjs +108 -11
- package/lib/index.d.ts +10 -7
- package/lib/index.js +107 -5
- package/package.json +10 -20
- package/lib/builders.d.ts +0 -154
- package/lib/functional-parser.cjs +0 -2872
- package/lib/functional-parser.d.ts +0 -46
- package/lib/functional-parser.js +0 -2855
- package/lib/interp.d.ts +0 -26
- package/lib/jess.cjs +0 -6
- package/lib/jess.d.ts +0 -2
- package/lib/jess.js +0 -2
- package/lib/scss-atroot-helpers.d.ts +0 -4
- package/lib/scss-atrule-helpers.d.ts +0 -36
- package/lib/scss-value-helpers.d.ts +0 -11
- package/src/builders.ts +0 -1408
- package/src/cst.ts +0 -25
- package/src/functional-parser.ts +0 -135
- package/src/grammar.ts +0 -621
- package/src/index.ts +0 -14
- package/src/interp.ts +0 -158
- package/src/jess.ts +0 -11
- package/src/scss-atroot-helpers.ts +0 -105
- package/src/scss-atrule-helpers.ts +0 -191
- package/src/scss-value-helpers.ts +0 -105
package/src/grammar.ts
DELETED
|
@@ -1,621 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Functional SCSS grammar — the macro-compiled counterpart to the class-based
|
|
3
|
-
* ScssGrammar. This file is JUST the grammar: `scssGrammar = compose([lessGrammar,
|
|
4
|
-
* <SCSS delta>])`. Most returned rules are structural `node(parser)` entries that build via
|
|
5
|
-
* the injected `ctx.build` host. The host + parse entry (`parseScssFn`,
|
|
6
|
-
* `ScssParser`) live in ./functional-parser.ts; the shared driver in
|
|
7
|
-
* @jesscss/css-parser.
|
|
8
|
-
*/
|
|
9
|
-
import {
|
|
10
|
-
rules, compose,
|
|
11
|
-
node, regex, literal, sequence, choice, optional, trivia,
|
|
12
|
-
many, expect, sepBy, oneOrMore, scanTo, balanced, label, not
|
|
13
|
-
} from 'parseman' with { type: 'macro' };
|
|
14
|
-
import { lessGrammar } from '@jesscss/less-parser/grammar';
|
|
15
|
-
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
// Grammar — SCSS = Less + the SCSS delta. `compose` fuses the imported compiled
|
|
18
|
-
// `lessGrammar` (pieces travel on the value — no source) with the inline SCSS
|
|
19
|
-
// delta; the delta's rules win by name (its `Stylesheet` etc. override Less's),
|
|
20
|
-
// and its references to Less/CSS rules resolve into the fused set. One grammar =
|
|
21
|
-
// one `rules()`; no fragment spreads.
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
|
|
24
|
-
// Trivia (`rw`) is declared ONCE on the grammar via `rules({ trivia: rw }, …)`,
|
|
25
|
-
// honored through `compose()`, making it ambient in every rule — no per-rule
|
|
26
|
-
// trivia-establisher wrappers are needed. Hoisted to module scope (mirroring
|
|
27
|
-
// css-parser) so the options-first `rules({ trivia: rw }, …)` call below can
|
|
28
|
-
// reference it. Same shape as Less/CSS (whitespace + block + `//` line comments).
|
|
29
|
-
const ws = regex(/[ \t\n\r\f]+/);
|
|
30
|
-
const comment = regex(/\/\*(?:[^*]|\*(?!\/))*\*\//);
|
|
31
|
-
const lineComment = regex(/\/\/[^\n\r]*/);
|
|
32
|
-
const rw = trivia(oneOrMore(choice(label('whitespace', ws), label('blockComment', comment), label('lineComment', lineComment))));
|
|
33
|
-
|
|
34
|
-
export const scssGrammar = compose([lessGrammar, rules({ trivia: rw }, (g: any) => {
|
|
35
|
-
// SCSS `$variable` token — first char may be a letter or `-` after `$`.
|
|
36
|
-
const scssVar = regex(/\$-?[_a-zA-Z\u0080-\uffff][-_a-zA-Z0-9\u0080-\uffff]*/);
|
|
37
|
-
const plainIdent = regex(/-?[_a-zA-Z\u0080-\uffff][-_a-zA-Z0-9\u0080-\uffff]*/);
|
|
38
|
-
|
|
39
|
-
const VarDeclaration = node(
|
|
40
|
-
sequence(
|
|
41
|
-
scssVar,
|
|
42
|
-
literal(':'),
|
|
43
|
-
g.valueList,
|
|
44
|
-
optional(choice(literal('!default'), literal('!global'))),
|
|
45
|
-
optional(literal(';'))
|
|
46
|
-
));
|
|
47
|
-
|
|
48
|
-
// SCSS references are bare `$var` (no Less accessor-chain syntax) — a single
|
|
49
|
-
// token, so no trivia handling is needed.
|
|
50
|
-
const Reference = node(scssVar);
|
|
51
|
-
|
|
52
|
-
// Namespaced variable ASSIGNMENT — `ns.$var: value [!default|!global];`. Writes
|
|
53
|
-
// into another module's variable (distinct from the member READ `ns.get-x()`).
|
|
54
|
-
// The `plainIdent '.' scssVar` head can't be confused with a Declaration
|
|
55
|
-
// (`scssDeclPropName` stops before `.`) or a class-selector ruleset (`.` there
|
|
56
|
-
// is followed by an ident, not `$`), so this is safe at statement head.
|
|
57
|
-
const NsVarDeclaration = node(
|
|
58
|
-
sequence(
|
|
59
|
-
plainIdent,
|
|
60
|
-
literal('.'),
|
|
61
|
-
scssVar,
|
|
62
|
-
literal(':'),
|
|
63
|
-
g.valueList,
|
|
64
|
-
optional(choice(literal('!default'), literal('!global'))),
|
|
65
|
-
optional(literal(';'))
|
|
66
|
-
));
|
|
67
|
-
|
|
68
|
-
// ── Interpolation (#{…}) ───────────────────────────────────────────────────
|
|
69
|
-
// SCSS uses `#{expr}` (not Less `@{var}`). Override the Less interpolation
|
|
70
|
-
// hooks: bare `#{…}` values, interpolated idents in names/selectors/strings.
|
|
71
|
-
const scssInterpKey = regex(/(?:-?[_a-zA-Z\u0080-\uffff][-_a-zA-Z0-9\u0080-\uffff]*|-)?#\{-?[_a-zA-Z\u0080-\uffff][-_a-zA-Z0-9\u0080-\uffff]*\}(?:#\{-?[_a-zA-Z\u0080-\uffff][-_a-zA-Z0-9\u0080-\uffff]*\}|[-_a-zA-Z0-9\u0080-\uffff])*/);
|
|
72
|
-
const scssCustomPropInterp = regex(/--(?:[-_a-zA-Z0-9\u0080-\uffff]|#\{[^}]*\})+/);
|
|
73
|
-
const customProp = regex(/--[-_a-zA-Z0-9\u0080-\uffff]*/);
|
|
74
|
-
const scssDeclPropName = regex(/\*?-?(?:[_a-zA-Z\u0080-\uffff]|\\(?:[0-9a-fA-F]{1,6}[ \t\n\r\f]?|[^\n])|#\{[^}]*\})(?:[-_a-zA-Z0-9\u0080-\uffff]|\\(?:[0-9a-fA-F]{1,6}[ \t\n\r\f]?|[^\n])|#\{[^}]*\})*/);
|
|
75
|
-
const important = sequence(literal('!'), literal('important'));
|
|
76
|
-
|
|
77
|
-
const ScssInterpBare = node(
|
|
78
|
-
sequence(literal('#'), literal('{'), g.valueSequence, expect(literal('}'), '}')));
|
|
79
|
-
|
|
80
|
-
const InterpValue = node(
|
|
81
|
-
scssInterpKey);
|
|
82
|
-
|
|
83
|
-
// ── Sass map literals + module-qualified idents ────────────────────────────
|
|
84
|
-
const dotName = regex(/\.-?[_a-zA-Z\u0080-\uffff][-_a-zA-Z0-9\u0080-\uffff]*/);
|
|
85
|
-
const ScssMapPair = node('ScssMapPair',
|
|
86
|
-
sequence(g.value, literal(':'), g.valueSequence));
|
|
87
|
-
// A Sass map literal REQUIRES at least one `key: value` pair. `expect(')')`
|
|
88
|
-
// recovers in place (zero-width success), so if this rule matched an empty or
|
|
89
|
-
// pairless `(…)` it would swallow every parenthesized value before the value
|
|
90
|
-
// paren rule is tried. Requiring a real pair (the `:` is a soft `literal`) lets a
|
|
91
|
-
// non-map paren like `(15px/30px)` or `(1 + 2)` fail here and fall through.
|
|
92
|
-
const ScssMapLiteral = node(
|
|
93
|
-
sequence(
|
|
94
|
-
literal('('),
|
|
95
|
-
ScssMapPair,
|
|
96
|
-
many(sequence(literal(','), ScssMapPair)),
|
|
97
|
-
optional(literal(',')),
|
|
98
|
-
expect(literal(')'))
|
|
99
|
-
));
|
|
100
|
-
const scssHashName = regex(/#-?[_a-zA-Z\u0080-\uffff][-_a-zA-Z0-9\u0080-\uffff]*/);
|
|
101
|
-
const ScssIdentValue = node(
|
|
102
|
-
sequence(
|
|
103
|
-
plainIdent,
|
|
104
|
-
optional(choice(
|
|
105
|
-
sequence(
|
|
106
|
-
literal('.'), literal('\\'), choice(scssHashName, dotName),
|
|
107
|
-
literal('('), optional(g.ScssCallArgsInner), expect(literal(')'))
|
|
108
|
-
),
|
|
109
|
-
sequence(literal('.'), scssVar),
|
|
110
|
-
sequence(dotName, literal('('), optional(g.ScssCallArgsInner), expect(literal(')')))
|
|
111
|
-
))
|
|
112
|
-
));
|
|
113
|
-
|
|
114
|
-
// Value-position paren. Unlike Less's strict single-expression `Paren`, SCSS
|
|
115
|
-
// allows space/comma-separated value lists inside parens (e.g.
|
|
116
|
-
// `(bold 15px/30px sans-serif)`). We parse permissively and let `_buildScssParen`
|
|
117
|
-
// decide: an isolated arithmetic form (`(15px/30px)`, `(1 + 2)`) becomes an
|
|
118
|
-
// `Expression(Operation)`; anything else stays a grouped `Paren`.
|
|
119
|
-
const ScssValueParen = node('Paren',
|
|
120
|
-
sequence(literal('('), g.permissiveParenBody));
|
|
121
|
-
|
|
122
|
-
// Sass allows trailing commas in comma-separated lists (Less v5 rejects them).
|
|
123
|
-
const valueList = sequence(
|
|
124
|
-
g.valueSequence,
|
|
125
|
-
many(sequence(literal(','), g.valueSequence)),
|
|
126
|
-
optional(literal(','))
|
|
127
|
-
);
|
|
128
|
-
const callArgSeq = choice(g.AnonymousMixinDefinition, g.DetachedRuleset, g.valueSequence);
|
|
129
|
-
const callArgList = choice(g.AnonymousMixinDefinition, g.DetachedRuleset, valueList);
|
|
130
|
-
const functionCallArgs = sequence(
|
|
131
|
-
optional(sequence(
|
|
132
|
-
callArgSeq,
|
|
133
|
-
many(sequence(literal(','), callArgSeq)),
|
|
134
|
-
optional(literal(',')),
|
|
135
|
-
many(sequence(literal(';'), optional(callArgList)))
|
|
136
|
-
)),
|
|
137
|
-
literal(')')
|
|
138
|
-
);
|
|
139
|
-
const fnIdent = regex(/-?(?:[_a-zA-Z\u0080-\uffff]|\\(?:[0-9a-fA-F]{1,6}[ \t\n\r\f]?|[^\n]))(?:[-_a-zA-Z0-9\u0080-\uffff]|\\(?:[0-9a-fA-F]{1,6}[ \t\n\r\f]?|[^\n]))*/);
|
|
140
|
-
const Call = node(
|
|
141
|
-
sequence(fnIdent, literal('('), functionCallArgs));
|
|
142
|
-
|
|
143
|
-
const value = choice(
|
|
144
|
-
ScssInterpBare, InterpValue, g.Reference, g.Dimension, g.Num, g.Color, g.NamedColor,
|
|
145
|
-
g.Url, g.CalcCall, g.Call, ScssIdentValue, g.EscapedValue, g.GluedParen, ScssMapLiteral,
|
|
146
|
-
ScssValueParen, g.SquareParen, g.Quoted, g.anyValue
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
const staticSeg = regex(/[-_a-zA-Z0-9]+/);
|
|
150
|
-
const nameSegment = choice(staticSeg, ScssInterpBare);
|
|
151
|
-
const ScssInterpolatedName = node(
|
|
152
|
-
oneOrMore(nameSegment));
|
|
153
|
-
|
|
154
|
-
const InterpolatedSelector = node(
|
|
155
|
-
sequence(
|
|
156
|
-
optional(regex(/[.#]/)),
|
|
157
|
-
oneOrMore(nameSegment)
|
|
158
|
-
));
|
|
159
|
-
|
|
160
|
-
const CustomDeclaration = node(
|
|
161
|
-
sequence(
|
|
162
|
-
choice(scssCustomPropInterp, customProp),
|
|
163
|
-
literal(':'),
|
|
164
|
-
choice(g.customCurlyBlock, g.customValue, g.cpValue),
|
|
165
|
-
optional(literal(';'))
|
|
166
|
-
));
|
|
167
|
-
|
|
168
|
-
// A nested prop (`size: 1rem`) is built AS a `Declaration` (structural node →
|
|
169
|
-
// ctx.build('Declaration')); `_buildScssNestedProps` filters children for
|
|
170
|
-
// Declaration nodes. The rule's own name stays local (`many(ScssNestedDecl)`).
|
|
171
|
-
const ScssNestedDecl = node('Declaration',
|
|
172
|
-
sequence(
|
|
173
|
-
scssDeclPropName,
|
|
174
|
-
literal(':'),
|
|
175
|
-
g.valueList,
|
|
176
|
-
optional(literal(';'))
|
|
177
|
-
));
|
|
178
|
-
|
|
179
|
-
// A nested-properties block (`font: { … }`) normally holds inner
|
|
180
|
-
// sub-declarations, but Sass also allows control flow (`@for`, `@if`, …) and
|
|
181
|
-
// namespaced variable ASSIGNMENTS inside it. Try those before the plain
|
|
182
|
-
// sub-declaration.
|
|
183
|
-
const ScssNestedProps = node(
|
|
184
|
-
sequence(
|
|
185
|
-
literal('{'),
|
|
186
|
-
many(choice(
|
|
187
|
-
g.ScssIf, g.ScssEach, g.ScssFor, g.ScssWhile,
|
|
188
|
-
g.NsVarDeclaration, g.VarDeclaration,
|
|
189
|
-
ScssNestedDecl
|
|
190
|
-
)),
|
|
191
|
-
expect(literal('}'))
|
|
192
|
-
));
|
|
193
|
-
|
|
194
|
-
const Declaration = node(
|
|
195
|
-
sequence(
|
|
196
|
-
scssDeclPropName,
|
|
197
|
-
optional(choice(literal('+_'), literal('+'))),
|
|
198
|
-
literal(':'),
|
|
199
|
-
choice(
|
|
200
|
-
ScssNestedProps,
|
|
201
|
-
sequence(
|
|
202
|
-
optional(g.valueList),
|
|
203
|
-
optional(ScssNestedProps)
|
|
204
|
-
)
|
|
205
|
-
),
|
|
206
|
-
optional(important),
|
|
207
|
-
optional(literal(';'))
|
|
208
|
-
));
|
|
209
|
-
|
|
210
|
-
// ── Control flow: @if / @else if / @else ───────────────────────────────────
|
|
211
|
-
// Faithful port of the Chevrotain scssCondition* / scssIfAtRule productions
|
|
212
|
-
// (productions/conditions.ts, productions/atRules.ts). The condition sub-
|
|
213
|
-
// grammar is structurally the Less guard grammar (or → and → term → parens /
|
|
214
|
-
// comparison), so the SCSS builders mirror LessGrammar's guard builders.
|
|
215
|
-
const scssCompareOp = regex(/==|!=|>=|<=|=|>|</);
|
|
216
|
-
const kwOr = regex(/or(?![-\w])/i);
|
|
217
|
-
const kwAnd = regex(/and(?![-\w])/i);
|
|
218
|
-
const kwNot = regex(/not(?![-\w])/i);
|
|
219
|
-
|
|
220
|
-
// A single comparison operand. Specific value rules first; `anyValue` last —
|
|
221
|
-
// it stops at whitespace so it cannot swallow a spaced ` == ` / `and` / `{`.
|
|
222
|
-
const condOperand = choice(g.Reference, g.Dimension, g.Num, g.Color, g.NamedColor, g.Quoted, g.Call, g.Paren, g.anyValue);
|
|
223
|
-
const ScssComparison = node(
|
|
224
|
-
sequence(condOperand, optional(sequence(scssCompareOp, condOperand))));
|
|
225
|
-
// `(` condOr `)` (Paren-wrapped) OR a bare comparison.
|
|
226
|
-
const ScssCondInParens = node(
|
|
227
|
-
choice(
|
|
228
|
-
sequence(literal('('), g.ScssCondOr, literal(')')),
|
|
229
|
-
g.ScssComparison
|
|
230
|
-
));
|
|
231
|
-
// A term: optional `not`, then a paren-group or a comparison.
|
|
232
|
-
const ScssCondTerm = node(
|
|
233
|
-
sequence(optional(kwNot), g.ScssCondInParens));
|
|
234
|
-
// 'and' chain (left-associative).
|
|
235
|
-
const ScssCondAnd = node(
|
|
236
|
-
sequence(g.ScssCondTerm, many(sequence(kwAnd, g.ScssCondTerm))));
|
|
237
|
-
// 'or' / ',' chain (left-associative). `,` is allowed in @if (legacy syntax).
|
|
238
|
-
const ScssCondOr = node(
|
|
239
|
-
sequence(g.ScssCondAnd, many(sequence(choice(kwOr, literal(',')), g.ScssCondAnd))));
|
|
240
|
-
|
|
241
|
-
// A `{ … }` block body → Rules (statements come from atRuleBody).
|
|
242
|
-
const ScssRules = node(
|
|
243
|
-
sequence(literal('{'), g.atRuleBody, expect(literal('}'), '}')));
|
|
244
|
-
|
|
245
|
-
const ifKw = regex(/@if(?![-\w])/i);
|
|
246
|
-
const elseKw = regex(/@else(?![-\w])/i);
|
|
247
|
-
const ifWord = regex(/if(?![-\w])/i);
|
|
248
|
-
const ScssIf = node(
|
|
249
|
-
sequence(
|
|
250
|
-
ifKw, g.ScssCondOr, g.ScssRules,
|
|
251
|
-
many(sequence(elseKw, choice(
|
|
252
|
-
sequence(ifWord, g.ScssCondOr, g.ScssRules),
|
|
253
|
-
g.ScssRules
|
|
254
|
-
)))
|
|
255
|
-
));
|
|
256
|
-
|
|
257
|
-
// ── Control flow: @each / @for / @while ────────────────────────────────────
|
|
258
|
-
// Faithful ports of scssEachAtRule / scssForAtRule / scssWhileAtRule
|
|
259
|
-
// (productions/atRules.ts). All normalize to Jess `For` / `While` nodes.
|
|
260
|
-
const inKw = regex(/\bin\b/);
|
|
261
|
-
const fromKw = regex(/\bfrom\b/);
|
|
262
|
-
const forThrough = regex(/\bthrough\b/);
|
|
263
|
-
const forTo = regex(/\bto\b/);
|
|
264
|
-
const eachKw = regex(/@each(?![-\w])/i);
|
|
265
|
-
const forKw = regex(/@for(?![-\w])/i);
|
|
266
|
-
const whileKw = regex(/@while(?![-\w])/i);
|
|
267
|
-
|
|
268
|
-
const ScssEach = node(
|
|
269
|
-
sequence(
|
|
270
|
-
eachKw,
|
|
271
|
-
sepBy(scssVar, literal(',')),
|
|
272
|
-
inKw,
|
|
273
|
-
g.valueSequence,
|
|
274
|
-
g.ScssRules
|
|
275
|
-
));
|
|
276
|
-
|
|
277
|
-
const ScssFor = node(
|
|
278
|
-
sequence(
|
|
279
|
-
forKw,
|
|
280
|
-
scssVar,
|
|
281
|
-
fromKw,
|
|
282
|
-
g.topSum,
|
|
283
|
-
choice(forThrough, forTo),
|
|
284
|
-
g.topSum,
|
|
285
|
-
g.ScssRules
|
|
286
|
-
));
|
|
287
|
-
|
|
288
|
-
const ScssWhile = node(
|
|
289
|
-
sequence(whileKw, g.ScssCondOr, g.ScssRules));
|
|
290
|
-
|
|
291
|
-
// ── Mixins: @mixin / @include / @content ───────────────────────────────────
|
|
292
|
-
// Faithful ports of scssMixinAtRule / scssIncludeAtRule / scssContentAtRule.
|
|
293
|
-
const mixinKw = regex(/@mixin(?![-\w])/i);
|
|
294
|
-
const includeKw = regex(/@include(?![-\w])/i);
|
|
295
|
-
const contentKw = regex(/@content(?![-\w])/i);
|
|
296
|
-
const usingKw = regex(/\busing\b/);
|
|
297
|
-
|
|
298
|
-
// SCSS call/mixin argument: `$x: val`, `val...`, or a plain value.
|
|
299
|
-
const ScssCallArg = node(
|
|
300
|
-
choice(
|
|
301
|
-
sequence(scssVar, literal(':'), g.valueSequence),
|
|
302
|
-
sequence(g.value, literal('...')),
|
|
303
|
-
sequence(g.valueSequence, literal('...')),
|
|
304
|
-
g.valueSequence
|
|
305
|
-
));
|
|
306
|
-
const ScssCallArgsInner = node(
|
|
307
|
-
optional(sequence(
|
|
308
|
-
g.ScssCallArg,
|
|
309
|
-
many(sequence(literal(','), optional(g.ScssCallArg)))
|
|
310
|
-
)));
|
|
311
|
-
const optionalCallParens = optional(sequence(
|
|
312
|
-
literal('('), g.ScssCallArgsInner, expect(literal(')'))
|
|
313
|
-
));
|
|
314
|
-
|
|
315
|
-
// Mixin parameter: `...$rest`, `$rest...`, `$a: default`, or bare `$a`.
|
|
316
|
-
const ScssMixinParam = node(
|
|
317
|
-
choice(
|
|
318
|
-
sequence(literal('...'), scssVar),
|
|
319
|
-
sequence(scssVar, literal('...')),
|
|
320
|
-
sequence(scssVar, optional(sequence(literal(':'), g.valueSequence)))
|
|
321
|
-
));
|
|
322
|
-
const ScssMixinParams = node(
|
|
323
|
-
sequence(
|
|
324
|
-
literal('('),
|
|
325
|
-
optional(sequence(
|
|
326
|
-
g.ScssMixinParam,
|
|
327
|
-
many(sequence(literal(','), optional(g.ScssMixinParam)))
|
|
328
|
-
)),
|
|
329
|
-
expect(literal(')'))
|
|
330
|
-
));
|
|
331
|
-
|
|
332
|
-
// Mixin/include name: `foo`, module-qualified `ns.foo`, or `foo-#{$bar}`.
|
|
333
|
-
const scssMixinIdent = choice(ScssInterpolatedName, plainIdent);
|
|
334
|
-
const ScssMixinName = node(
|
|
335
|
-
choice(
|
|
336
|
-
sequence(plainIdent, literal('.'), plainIdent),
|
|
337
|
-
ScssInterpolatedName,
|
|
338
|
-
plainIdent
|
|
339
|
-
));
|
|
340
|
-
|
|
341
|
-
const ScssDeclBody = node(
|
|
342
|
-
sequence(literal('{'), g.declarationList, expect(literal('}'), '}')));
|
|
343
|
-
|
|
344
|
-
const ScssMixin = node(
|
|
345
|
-
sequence(
|
|
346
|
-
mixinKw, scssMixinIdent, optional(g.ScssMixinParams), g.ScssDeclBody
|
|
347
|
-
));
|
|
348
|
-
|
|
349
|
-
const ScssIncludeUsing = node(
|
|
350
|
-
sequence(
|
|
351
|
-
usingKw, literal('('), sepBy(scssVar, literal(',')), expect(literal(')'))
|
|
352
|
-
));
|
|
353
|
-
|
|
354
|
-
const ScssInclude = node(
|
|
355
|
-
sequence(
|
|
356
|
-
includeKw, g.ScssMixinName, optionalCallParens,
|
|
357
|
-
optional(g.ScssIncludeUsing), optional(g.ScssRules), optional(literal(';'))
|
|
358
|
-
));
|
|
359
|
-
|
|
360
|
-
const ScssContent = node(
|
|
361
|
-
sequence(contentKw, optionalCallParens, optional(literal(';'))));
|
|
362
|
-
|
|
363
|
-
// ── @function / @return ─────────────────────────────────────────────────────
|
|
364
|
-
const functionKw = regex(/@function(?![-\w])/i);
|
|
365
|
-
const returnKw = regex(/@return(?![-\w])/i);
|
|
366
|
-
|
|
367
|
-
const ScssFunction = node(
|
|
368
|
-
sequence(
|
|
369
|
-
functionKw, scssMixinIdent, optional(g.ScssMixinParams), g.ScssDeclBody
|
|
370
|
-
));
|
|
371
|
-
|
|
372
|
-
const ScssReturn = node(
|
|
373
|
-
sequence(returnKw, g.valueList, optional(literal(';'))));
|
|
374
|
-
|
|
375
|
-
// ── @use / @forward / @import / @extend ───────────────────────────────────
|
|
376
|
-
// Faithful ports of scssUseAtRule / scssForwardAtRule / importAtRule /
|
|
377
|
-
// scssExtendAtRule (productions/atRules.ts).
|
|
378
|
-
const singleStr = regex(/'(?:[^'\\]|\\[\s\S])*'/);
|
|
379
|
-
const doubleStr = regex(/"(?:[^"\\]|\\[\s\S])*"/);
|
|
380
|
-
const strHole = [singleStr, doubleStr];
|
|
381
|
-
const bParen = balanced('(', ')', { skip: strHole });
|
|
382
|
-
const bSquare = balanced('[', ']', { skip: strHole });
|
|
383
|
-
const bCurly = balanced('{', '}', { skip: strHole });
|
|
384
|
-
const scanSkip = [bParen, bSquare, bCurly, singleStr, doubleStr];
|
|
385
|
-
|
|
386
|
-
const kwAs = regex(/\bas\b/);
|
|
387
|
-
const kwWith = regex(/\bwith\b/);
|
|
388
|
-
const useKw = regex(/@use(?![-\w])/i);
|
|
389
|
-
const forwardKw = regex(/@forward(?![-\w])/i);
|
|
390
|
-
const extendKw = regex(/@extend(?![-\w])/i);
|
|
391
|
-
const extendOptional = regex(/!optional\b/);
|
|
392
|
-
const importKw = regex(/@import(?![-\w])/i);
|
|
393
|
-
|
|
394
|
-
const ScssWithConfigEntry = node(
|
|
395
|
-
sequence(
|
|
396
|
-
scssVar, literal(':'), g.valueSequence,
|
|
397
|
-
optional(choice(literal('!default'), literal('!global')))
|
|
398
|
-
));
|
|
399
|
-
const ScssWithConfig = node(
|
|
400
|
-
sequence(
|
|
401
|
-
literal('('),
|
|
402
|
-
optional(sequence(
|
|
403
|
-
sepBy(ScssWithConfigEntry, literal(',')),
|
|
404
|
-
optional(literal(','))
|
|
405
|
-
)),
|
|
406
|
-
expect(literal(')'))
|
|
407
|
-
));
|
|
408
|
-
|
|
409
|
-
const ScssUseAs = node(
|
|
410
|
-
sequence(kwAs, choice(literal('*'), plainIdent)));
|
|
411
|
-
|
|
412
|
-
const ScssUse = node(
|
|
413
|
-
sequence(
|
|
414
|
-
useKw, g.Quoted,
|
|
415
|
-
optional(ScssUseAs),
|
|
416
|
-
optional(sequence(kwWith, ScssWithConfig)),
|
|
417
|
-
optional(literal(';'))
|
|
418
|
-
));
|
|
419
|
-
|
|
420
|
-
// Capture the post-path prelude (`as *`, `show …`, `hide …`, `as prefix-*`) up
|
|
421
|
-
// to `with (`, `;`, `}`, or EOF — so an unterminated `@forward "x" as a-*` (the
|
|
422
|
-
// owner-rejected prefix form, which the sass-spec corpus writes without a `;`,
|
|
423
|
-
// sometimes with comments/newlines around `as`) still reaches the builder's
|
|
424
|
-
// "will never be" check rather than dangling as unparsed input. `{` bounds the
|
|
425
|
-
// scan so a following ruleset is never swallowed.
|
|
426
|
-
const forwardExtra = optional(scanTo(
|
|
427
|
-
choice(sequence(kwWith, literal('(')), literal(';'), literal('{'), literal('}')),
|
|
428
|
-
{ skip: scanSkip, orEOF: true }
|
|
429
|
-
));
|
|
430
|
-
const ScssForward = node(
|
|
431
|
-
sequence(
|
|
432
|
-
forwardKw, g.Quoted,
|
|
433
|
-
forwardExtra,
|
|
434
|
-
optional(sequence(kwWith, ScssWithConfig)),
|
|
435
|
-
optional(literal(';'))
|
|
436
|
-
));
|
|
437
|
-
|
|
438
|
-
const scssPlaceholder = regex(/%-?[_a-zA-Z\u0080-\uffff][-_a-zA-Z0-9\u0080-\uffff]*/);
|
|
439
|
-
const ScssPlaceholderSelector = node(
|
|
440
|
-
scssPlaceholder);
|
|
441
|
-
const scssExtendComplex = choice(ScssPlaceholderSelector, g.ComplexSelector);
|
|
442
|
-
const ScssExtendTarget = node(
|
|
443
|
-
sequence(
|
|
444
|
-
scssExtendComplex,
|
|
445
|
-
many(sequence(literal(','), scssExtendComplex))
|
|
446
|
-
));
|
|
447
|
-
const ScssExtend = node(
|
|
448
|
-
sequence(
|
|
449
|
-
extendKw, ScssExtendTarget,
|
|
450
|
-
optional(extendOptional),
|
|
451
|
-
optional(literal(';'))
|
|
452
|
-
));
|
|
453
|
-
|
|
454
|
-
const importOptionsParen = sequence(
|
|
455
|
-
literal('('),
|
|
456
|
-
scanTo(literal(')'), { skip: scanSkip }),
|
|
457
|
-
literal(')')
|
|
458
|
-
);
|
|
459
|
-
// An `@import` prelude is a comma-separated list of items, each `<path>
|
|
460
|
-
// <modifiers>?`. The modifiers are a CSS media-query list / `supports(...)`
|
|
461
|
-
// that MAY itself contain commas (`@import "a" b, (c: d), e;` is ONE import
|
|
462
|
-
// with a three-part media list). So a comma only begins a NEW import when the
|
|
463
|
-
// token after it is another path (a string / url) — `not(not(...))` is the
|
|
464
|
-
// positive lookahead. The modifier scan skips balanced groups, strings, and
|
|
465
|
-
// comments, and terminates at `;`, `}`, EOF, or such a new-import comma.
|
|
466
|
-
const importPathStart = choice(g.Url, g.Quoted);
|
|
467
|
-
// `#{ … }` interpolation hole (handles `#{$a}` and `#{"(a: b)"}` with a string
|
|
468
|
-
// that may itself carry braces). Kept ahead of the generic brace skip so the
|
|
469
|
-
// leading `#` is consumed together with the group.
|
|
470
|
-
const scssInterpHole = regex(/#\{(?:[^{}'"]|'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*")*\}/);
|
|
471
|
-
const importSkip = [scssInterpHole, bParen, bSquare, bCurly, singleStr, doubleStr, comment, lineComment];
|
|
472
|
-
const newImportComma = sequence(literal(','), not(not(importPathStart)));
|
|
473
|
-
const importModifier = scanTo(
|
|
474
|
-
choice(literal(';'), literal('}'), newImportComma),
|
|
475
|
-
{ skip: importSkip, orEOF: true }
|
|
476
|
-
);
|
|
477
|
-
const ScssImportItem = node(
|
|
478
|
-
sequence(
|
|
479
|
-
expect(importPathStart, 'import path'),
|
|
480
|
-
optional(importModifier)
|
|
481
|
-
));
|
|
482
|
-
const ImportAtRuleStatement = node('ScssImportAtRule',
|
|
483
|
-
sequence(
|
|
484
|
-
importKw,
|
|
485
|
-
optional(importOptionsParen),
|
|
486
|
-
ScssImportItem,
|
|
487
|
-
many(sequence(literal(','), ScssImportItem)),
|
|
488
|
-
optional(literal(';'))
|
|
489
|
-
));
|
|
490
|
-
|
|
491
|
-
const atRootKw = regex(/@at-root(?![-\w])/i);
|
|
492
|
-
const debugKw = regex(/@debug(?![-\w])/i);
|
|
493
|
-
const warnKw = regex(/@warn(?![-\w])/i);
|
|
494
|
-
const errorKw = regex(/@error(?![-\w])/i);
|
|
495
|
-
|
|
496
|
-
const ScssDiagnostic = node(
|
|
497
|
-
sequence(
|
|
498
|
-
choice(debugKw, warnKw, errorKw),
|
|
499
|
-
g.valueSequence,
|
|
500
|
-
optional(literal(';'))
|
|
501
|
-
));
|
|
502
|
-
|
|
503
|
-
const ScssAtRootFilter = node(
|
|
504
|
-
sequence(
|
|
505
|
-
atRootKw,
|
|
506
|
-
literal('('),
|
|
507
|
-
g.valueSequence,
|
|
508
|
-
literal(')'),
|
|
509
|
-
ScssRules
|
|
510
|
-
));
|
|
511
|
-
|
|
512
|
-
const ScssAtRootSelector = node(
|
|
513
|
-
sequence(
|
|
514
|
-
atRootKw,
|
|
515
|
-
g.SelectorList,
|
|
516
|
-
ScssDeclBody
|
|
517
|
-
));
|
|
518
|
-
|
|
519
|
-
const ScssAtRootPlain = node(
|
|
520
|
-
sequence(atRootKw, ScssRules));
|
|
521
|
-
|
|
522
|
-
// ── SCSS at-rule prelude interpolation (segments) ────────────────────────
|
|
523
|
-
const scssPreludeText = regex(/(?:[^{#]|#(?!\{))+/);
|
|
524
|
-
const scssPreludeSegment = choice(ScssInterpBare, scssPreludeText);
|
|
525
|
-
const scssPermissivePrelude = oneOrMore(scssPreludeSegment);
|
|
526
|
-
|
|
527
|
-
// Generic unknown at-rule statement (`@charset "x";`, or a bare `@c` used as a
|
|
528
|
-
// content placeholder in the sass-spec corpus). Overrides Less's
|
|
529
|
-
// `AtRuleStatement`, whose `;` is mandatory and whose prelude scan stops only
|
|
530
|
-
// at `{`/`;`: Sass allows omitting the terminator before `}`/EOF, so the
|
|
531
|
-
// prelude also stops at `}`/EOF and the trailing `;` is optional.
|
|
532
|
-
const scssAtKeyword = regex(/@-?[_a-zA-Z-][-_a-zA-Z0-9-]*/);
|
|
533
|
-
const scssAtPrelude = optional(scanTo(
|
|
534
|
-
choice(literal('{'), literal(';'), literal('}')),
|
|
535
|
-
{ skip: scanSkip, orEOF: true }
|
|
536
|
-
));
|
|
537
|
-
const AtRuleStatement = node('AtRuleStatement',
|
|
538
|
-
sequence(scssAtKeyword, scssAtPrelude, optional(literal(';'))));
|
|
539
|
-
|
|
540
|
-
// ── Statement injection ─────────────────────────────────────────────────
|
|
541
|
-
// Override Less's containers to try the SCSS control statements first, then
|
|
542
|
-
// fall back to Less's full statement set (`g.stylesheetItem` / `g.blockItem`).
|
|
543
|
-
const scssStatement = choice(
|
|
544
|
-
g.ScssIf, g.ScssEach, g.ScssFor, g.ScssWhile,
|
|
545
|
-
g.ScssMixin, g.ScssInclude, g.ScssContent,
|
|
546
|
-
g.ScssFunction, g.ScssReturn,
|
|
547
|
-
g.ScssUse, g.ScssForward,
|
|
548
|
-
// Tried ahead of Less's `blockItem`, so an `@import` whose modifier carries
|
|
549
|
-
// `#{ … }` interpolation is handled by the SCSS import rule rather than being
|
|
550
|
-
// misread by Less's generic `AtRuleBlock` (which would treat the `{` in `#{`
|
|
551
|
-
// as a block opener).
|
|
552
|
-
g.ImportAtRuleStatement,
|
|
553
|
-
g.NsVarDeclaration,
|
|
554
|
-
ScssDiagnostic,
|
|
555
|
-
ScssAtRootFilter, ScssAtRootSelector, ScssAtRootPlain
|
|
556
|
-
);
|
|
557
|
-
const declarationList = many(choice(
|
|
558
|
-
scssStatement, g.ScssExtend, g.ScssPlaceholderRuleset, Declaration, CustomDeclaration, g.blockItem
|
|
559
|
-
));
|
|
560
|
-
const atRuleBody = many(choice(scssStatement, g.ScssPlaceholderRuleset, g.blockItem));
|
|
561
|
-
|
|
562
|
-
const ScssPlaceholderRuleset = node(
|
|
563
|
-
sequence(
|
|
564
|
-
ScssPlaceholderSelector,
|
|
565
|
-
optional(g.Guard),
|
|
566
|
-
literal('{'),
|
|
567
|
-
declarationList,
|
|
568
|
-
expect(literal('}'))
|
|
569
|
-
));
|
|
570
|
-
const queryAtKeyword = regex(/@(?:media|container|supports)(?![-\w])/i);
|
|
571
|
-
const QueryAtRuleBlock = node(
|
|
572
|
-
sequence(
|
|
573
|
-
queryAtKeyword,
|
|
574
|
-
scssPermissivePrelude,
|
|
575
|
-
expect(literal('{')),
|
|
576
|
-
atRuleBody,
|
|
577
|
-
expect(literal('}'))
|
|
578
|
-
));
|
|
579
|
-
const scopeKw = regex(/@scope(?![-\w])/i);
|
|
580
|
-
const ScssScopeBlock = node(
|
|
581
|
-
sequence(
|
|
582
|
-
scopeKw,
|
|
583
|
-
scssPermissivePrelude,
|
|
584
|
-
literal('{'),
|
|
585
|
-
atRuleBody,
|
|
586
|
-
expect(literal('}'))
|
|
587
|
-
));
|
|
588
|
-
const layerKw = regex(/@layer(?![-\w])/i);
|
|
589
|
-
const ScssLayerBlock = node(
|
|
590
|
-
sequence(
|
|
591
|
-
layerKw,
|
|
592
|
-
optional(ScssInterpolatedName),
|
|
593
|
-
literal('{'),
|
|
594
|
-
atRuleBody,
|
|
595
|
-
expect(literal('}'))
|
|
596
|
-
));
|
|
597
|
-
const Stylesheet = node(
|
|
598
|
-
many(choice(
|
|
599
|
-
scssStatement, ScssPlaceholderRuleset, ScssScopeBlock, ScssLayerBlock, g.stylesheetItem
|
|
600
|
-
)));
|
|
601
|
-
|
|
602
|
-
return {
|
|
603
|
-
VarDeclaration, Reference, NsVarDeclaration, AtRuleStatement,
|
|
604
|
-
ScssInterpBare, InterpValue, value, valueList, functionCallArgs, Call,
|
|
605
|
-
ScssMapLiteral, ScssIdentValue,
|
|
606
|
-
ScssInterpolatedName, InterpolatedSelector,
|
|
607
|
-
Declaration, CustomDeclaration,
|
|
608
|
-
ScssComparison, ScssCondInParens, ScssCondTerm, ScssCondAnd, ScssCondOr, ScssRules, ScssIf,
|
|
609
|
-
ScssEach, ScssFor, ScssWhile,
|
|
610
|
-
ScssCallArg, ScssCallArgsInner, ScssMixinParam, ScssMixinParams, ScssMixinName,
|
|
611
|
-
ScssDeclBody, ScssMixin, ScssIncludeUsing, ScssInclude, ScssContent,
|
|
612
|
-
ScssFunction, ScssReturn,
|
|
613
|
-
ScssWithConfigEntry, ScssWithConfig, ScssUseAs, ScssUse, ScssForward,
|
|
614
|
-
ScssPlaceholderSelector, ScssPlaceholderRuleset, ScssExtendTarget, ScssExtend,
|
|
615
|
-
ScssImportItem, ImportAtRuleStatement,
|
|
616
|
-
ScssNestedProps,
|
|
617
|
-
ScssDiagnostic, ScssAtRootFilter, ScssAtRootSelector, ScssAtRootPlain,
|
|
618
|
-
QueryAtRuleBlock, ScssScopeBlock, ScssLayerBlock,
|
|
619
|
-
Stylesheet, declarationList, atRuleBody
|
|
620
|
-
};
|
|
621
|
-
})]);
|
package/src/index.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
// The legacy Chevrotain parser has been removed — the functional macro parser
|
|
2
|
-
// (ScssParser / scssGrammar) IS the parser now.
|
|
3
|
-
|
|
4
|
-
export { ScssGrammar } from './builders.js';
|
|
5
|
-
export { scssGrammar } from './grammar.js';
|
|
6
|
-
|
|
7
|
-
import { ScssParser } from './functional-parser.js';
|
|
8
|
-
export { ScssParser, parseScssFn, type ScssFnParseResult, type ScssFnParseOptions } from './functional-parser.js';
|
|
9
|
-
|
|
10
|
-
export const Parser = ScssParser;
|
|
11
|
-
export { parseScssCst, parseScssDoc } from './cst.js';
|
|
12
|
-
export type {
|
|
13
|
-
ScssCstChild, ScssCstError, ScssCstLeaf, ScssCstNode, ScssCstParseResult, ScssCstType
|
|
14
|
-
} from './cst.js';
|