@jesscss/scss-parser 2.0.0-alpha.7 → 2.0.0-alpha.8

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/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "description": "",
7
- "version": "2.0.0-alpha.7",
7
+ "version": "2.0.0-alpha.8",
8
8
  "main": "lib/index.cjs",
9
9
  "types": "lib/index.d.ts",
10
10
  "exports": {
@@ -40,11 +40,11 @@
40
40
  ],
41
41
  "dependencies": {
42
42
  "chevrotain": "^12.0.0",
43
- "@jesscss/css-parser": "2.0.0-alpha.7",
44
- "@jesscss/less-parser": "2.0.0-alpha.7"
43
+ "@jesscss/css-parser": "2.0.0-alpha.8",
44
+ "@jesscss/less-parser": "2.0.0-alpha.8"
45
45
  },
46
46
  "peerDependencies": {
47
- "@jesscss/core": "2.0.0-alpha.7"
47
+ "@jesscss/core": "2.0.0-alpha.8"
48
48
  },
49
49
  "peerDependenciesMeta": {
50
50
  "@jesscss/core": {
@@ -52,9 +52,9 @@
52
52
  }
53
53
  },
54
54
  "devDependencies": {
55
- "parseman": "0.25.0",
55
+ "parseman": "link:/Users/matthew/git/oss/parser-thing",
56
56
  "sass-spec": "github:sass/sass-spec",
57
- "@jesscss/core": "2.0.0-alpha.7"
57
+ "@jesscss/core": "2.0.0-alpha.8"
58
58
  },
59
59
  "author": "Matthew Dean <matthew-dean@users.noreply.github.com>",
60
60
  "license": "MIT",
package/src/grammar.ts CHANGED
@@ -119,7 +119,7 @@ export const scssGrammar = compose([lessGrammar, rules({ trivia: rw }, (g: any)
119
119
  const ScssValueParen = node('Paren',
120
120
  sequence(literal('('), g.permissiveParenBody));
121
121
 
122
- // Sass allows trailing commas in comma-separated lists (Less v5 rejects them).
122
+ // Sass allows trailing commas in comma-separated lists (as does Less v5, matching Less 4.x).
123
123
  const valueList = sequence(
124
124
  g.valueSequence,
125
125
  many(sequence(literal(','), g.valueSequence)),
@@ -245,9 +245,16 @@ export const scssGrammar = compose([lessGrammar, rules({ trivia: rw }, (g: any)
245
245
  const ifKw = regex(/@if(?![-\w])/i);
246
246
  const elseKw = regex(/@else(?![-\w])/i);
247
247
  const ifWord = regex(/if(?![-\w])/i);
248
+ // A REQUIRED condition. `@if { … }` (no condition) is a real error. `not('{')`
249
+ // asserts we are not sitting directly on the block opener; `expect` reports the
250
+ // missing condition and RECOVERS IN PLACE (zero-width) so the `{ … }` block still
251
+ // parses (as an `@if` with a recovered error) rather than the whole rule failing
252
+ // and `@if` falling through to the opaque unknown-at-rule handler. Structural,
253
+ // context-free — no `withCtx`/`guard`, so the grammar stays macro-compiled.
254
+ const reqIfCond = expect(sequence(not(literal('{')), g.ScssCondOr), 'condition');
248
255
  const ScssIf = node(
249
256
  sequence(
250
- ifKw, g.ScssCondOr, g.ScssRules,
257
+ ifKw, reqIfCond, g.ScssRules,
251
258
  many(sequence(elseKw, choice(
252
259
  sequence(ifWord, g.ScssCondOr, g.ScssRules),
253
260
  g.ScssRules
@@ -265,23 +272,32 @@ export const scssGrammar = compose([lessGrammar, rules({ trivia: rw }, (g: any)
265
272
  const forKw = regex(/@for(?![-\w])/i);
266
273
  const whileKw = regex(/@while(?![-\w])/i);
267
274
 
275
+ // A REQUIRED loop variable. `@each in $list { … }` (no variable) is a real error.
276
+ // `not(inKw | '{')` asserts a variable is actually present (missing → we are at
277
+ // `in` or the block); `expect` reports it and recovers zero-width so `in $list
278
+ // { … }` still parses as an `@each` with a recovered error.
279
+ const reqEachVars = expect(
280
+ sequence(not(choice(inKw, literal('{'))), sepBy(scssVar, literal(','))), 'variable');
268
281
  const ScssEach = node(
269
282
  sequence(
270
283
  eachKw,
271
- sepBy(scssVar, literal(',')),
284
+ reqEachVars,
272
285
  inKw,
273
286
  g.valueSequence,
274
287
  g.ScssRules
275
288
  ));
276
289
 
290
+ // A REQUIRED `from … through/to …` range. `@for $i { … }` (no range) is a real
291
+ // error. The whole range tail is wrapped in one `expect`: on failure it recovers
292
+ // zero-width (as a unit — `fromKw` is a hard token that fails at `{` without
293
+ // consuming) so the trailing `{ … }` block still parses as a `@for` with a
294
+ // recovered error rather than the rule failing and falling through.
295
+ const forRangeTail = sequence(fromKw, g.topSum, choice(forThrough, forTo), g.topSum);
277
296
  const ScssFor = node(
278
297
  sequence(
279
298
  forKw,
280
- scssVar,
281
- fromKw,
282
- g.topSum,
283
- choice(forThrough, forTo),
284
- g.topSum,
299
+ expect(scssVar, 'variable'),
300
+ expect(forRangeTail, '"from"'),
285
301
  g.ScssRules
286
302
  ));
287
303
 
@@ -341,9 +357,12 @@ export const scssGrammar = compose([lessGrammar, rules({ trivia: rw }, (g: any)
341
357
  const ScssDeclBody = node(
342
358
  sequence(literal('{'), g.declarationList, expect(literal('}'), '}')));
343
359
 
360
+ // A REQUIRED mixin name. `@mixin { … }` (no name) is a real error. `expect` reports
361
+ // the missing name and recovers zero-width so the `{ … }` body still parses (as a
362
+ // `@mixin` with a recovered error) rather than the rule falling through.
344
363
  const ScssMixin = node(
345
364
  sequence(
346
- mixinKw, scssMixinIdent, optional(g.ScssMixinParams), g.ScssDeclBody
365
+ mixinKw, expect(scssMixinIdent, 'name'), optional(g.ScssMixinParams), g.ScssDeclBody
347
366
  ));
348
367
 
349
368
  const ScssIncludeUsing = node(
@@ -351,9 +370,12 @@ export const scssGrammar = compose([lessGrammar, rules({ trivia: rw }, (g: any)
351
370
  usingKw, literal('('), sepBy(scssVar, literal(',')), expect(literal(')'))
352
371
  ));
353
372
 
373
+ // A REQUIRED mixin name. `@include ;` and `.a { @include }` (no name) are real
374
+ // errors. `expect` reports the missing name and recovers zero-width so the trailing
375
+ // `;`/`}` still closes the statement rather than the rule falling through.
354
376
  const ScssInclude = node(
355
377
  sequence(
356
- includeKw, g.ScssMixinName, optionalCallParens,
378
+ includeKw, expect(g.ScssMixinName, 'name'), optionalCallParens,
357
379
  optional(g.ScssIncludeUsing), optional(g.ScssRules), optional(literal(';'))
358
380
  ));
359
381
 
@@ -369,8 +391,16 @@ export const scssGrammar = compose([lessGrammar, rules({ trivia: rw }, (g: any)
369
391
  functionKw, scssMixinIdent, optional(g.ScssMixinParams), g.ScssDeclBody
370
392
  ));
371
393
 
394
+ // A REQUIRED return value. `@return }` / `@return ;` (no expression) is a real
395
+ // error. `not('}' | ';')` asserts a value is actually present (valueList can match
396
+ // zero-width); `expect` reports it and recovers zero-width so the enclosing block's
397
+ // `}` still closes.
372
398
  const ScssReturn = node(
373
- sequence(returnKw, g.valueList, optional(literal(';'))));
399
+ sequence(
400
+ returnKw,
401
+ expect(sequence(not(choice(literal('}'), literal(';'))), g.valueList), 'expression'),
402
+ optional(literal(';'))
403
+ ));
374
404
 
375
405
  // ── @use / @forward / @import / @extend ───────────────────────────────────
376
406
  // Faithful ports of scssUseAtRule / scssForwardAtRule / importAtRule /