@jesscss/less-parser 2.0.0-alpha.11 → 2.0.0-alpha.13

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.
Files changed (56) hide show
  1. package/README.md +55 -11
  2. package/lib/chunks/parse-with.cjs +172 -0
  3. package/lib/chunks/parse-with.js +161 -0
  4. package/lib/chunks/trivia-labels.cjs +18 -0
  5. package/lib/chunks/trivia-labels.js +13 -0
  6. package/lib/cst/positions.cjs +24 -0
  7. package/lib/cst/positions.d.ts +8 -0
  8. package/lib/cst/positions.d.ts.map +1 -0
  9. package/lib/cst/positions.js +21 -0
  10. package/lib/cst.cjs +11 -4
  11. package/lib/cst.d.ts +8 -2
  12. package/lib/cst.d.ts.map +1 -1
  13. package/lib/cst.js +10 -4
  14. package/lib/grammar/ast/positions.cjs +28802 -0
  15. package/lib/grammar/ast/positions.d.ts +3 -0
  16. package/lib/grammar/ast/positions.d.ts.map +1 -0
  17. package/lib/grammar/ast/positions.js +28801 -0
  18. package/lib/grammar/ast.cjs +29126 -0
  19. package/lib/grammar/ast.d.ts +11 -0
  20. package/lib/grammar/ast.d.ts.map +1 -0
  21. package/lib/grammar/ast.js +29125 -0
  22. package/lib/grammar/cst/positions.cjs +28802 -0
  23. package/lib/grammar/cst/positions.d.ts +3 -0
  24. package/lib/grammar/cst/positions.d.ts.map +1 -0
  25. package/lib/grammar/cst/positions.js +28801 -0
  26. package/lib/grammar/cst.cjs +29127 -0
  27. package/lib/grammar/cst.d.ts +3 -0
  28. package/lib/grammar/cst.d.ts.map +1 -0
  29. package/lib/grammar/cst.js +29126 -0
  30. package/lib/grammar-helpers.d.ts +478 -0
  31. package/lib/grammar-helpers.d.ts.map +1 -0
  32. package/lib/grammar.d.ts +4 -1
  33. package/lib/grammar.d.ts.map +1 -1
  34. package/lib/index.cjs +25 -63
  35. package/lib/index.d.ts +17 -9
  36. package/lib/index.d.ts.map +1 -1
  37. package/lib/index.js +18 -57
  38. package/lib/parse-error.cjs +160 -0
  39. package/lib/parse-error.d.ts +26 -0
  40. package/lib/parse-error.d.ts.map +1 -1
  41. package/lib/parse-error.js +152 -0
  42. package/lib/parse-state.d.ts +39 -0
  43. package/lib/parse-state.d.ts.map +1 -0
  44. package/lib/parse-with.d.ts +25 -0
  45. package/lib/parse-with.d.ts.map +1 -0
  46. package/lib/positions.cjs +24 -0
  47. package/lib/positions.d.ts +20 -0
  48. package/lib/positions.d.ts.map +1 -0
  49. package/lib/positions.js +14 -0
  50. package/lib/trivia-labels.d.ts +11 -0
  51. package/lib/trivia-labels.d.ts.map +1 -0
  52. package/package.json +42 -12
  53. package/lib/grammar.cjs +0 -5
  54. package/lib/grammar.js +0 -2
  55. package/lib/grammar2.cjs +0 -141792
  56. package/lib/grammar2.js +0 -141733
package/README.md CHANGED
@@ -9,11 +9,15 @@ The Less grammar, layered on the CSS base parser, with core-free CST entry point
9
9
 
10
10
  ## What it is
11
11
 
12
- The Less grammar is the shared CSS grammar plus a Less delta:
13
- `lessGrammar = compose([cssGrammar, <Less delta>])`. It adds `@variable` /
14
- `@{interpolation}`, mixins, and the rest of Less on top of the spec-aligned CSS
15
- base in [`@jesscss/css-parser`](https://www.npmjs.com/package/@jesscss/css-parser),
16
- built on [parseman](https://www.npmjs.com/package/parseman) **the fastest
12
+ The Less grammar extends the spec-aligned CSS base in
13
+ [`@jesscss/css-parser`](https://www.npmjs.com/package/@jesscss/css-parser):
14
+ unchanged CSS structure remains CSS-owned, and Less changes only the smallest
15
+ child, value slot, or reference its syntax requires. It adds `@variable` /
16
+ `@{interpolation}`, mixins, and the rest of Less. Parseman currently compiles
17
+ the CSS and Less host factories from shared recognition artifacts rather than
18
+ literally composing a terminal `cssGrammar` artifact; that macro boundary does
19
+ not relax the ownership rule. It is built on
20
+ [parseman](https://www.npmjs.com/package/parseman) — **the fastest
17
21
  general-purpose JavaScript parser** in its
18
22
  [published benchmarks](https://matthew-dean.github.io/parseman/guide/benchmarks)
19
23
  (see `@jesscss/css-parser` for figures and engineering details). It is the parser
@@ -73,9 +77,49 @@ Pass a different `startRule` (any capitalized grammar rule, e.g. `'SelectorList'
73
77
  | --- | --- | --- |
74
78
  | `@jesscss/less-parser/cst` | `parseLessCst` | Core-free parse of a Less string to a CST. |
75
79
  | `@jesscss/less-parser/cst` | `LessCstNode`, `LessCstLeaf`, `LessCstError`, `LessCstChild`, `LessCstParseResult`, `LessCstType` (types) | CST type definitions (aliases of the shared `@jesscss/css-parser/cst` types). |
76
- | `@jesscss/less-parser/grammar` | `lessGrammar` | The compiled Less grammar (a rule map). Extend it with `compose()` or drive it directly with parseman's `run`. |
80
+ | `@jesscss/less-parser/grammar` | `lessGrammar` | The compiled Less AST grammar (a rule map). Extend it with `compose()` or drive it directly with parseman's `run`. See the variant table below. |
77
81
  | `@jesscss/less-parser` (`.`) | `parse` | Parse Less directly to canonical AST v2 `Stylesheet`. It does not load the CST grammar. |
78
82
 
83
+ ### Line-aware entries
84
+
85
+ `parse` and the CST parsers come in two bindings, one per compiled table, so an
86
+ entry never loads a table it does not parse with:
87
+
88
+ | Entry | Export | Tree | Positions |
89
+ | --- | --- | --- | --- |
90
+ | `@jesscss/less-parser` (`.`) | `parse` | AST | no |
91
+ | `@jesscss/less-parser/positions` | `parse` | AST | yes |
92
+ | `@jesscss/less-parser/cst` | `parseLessCst`, `parseLessDoc` | CST | no |
93
+ | `@jesscss/less-parser/cst/positions` | `parseLessCst`, `parseLessDoc` | CST | yes |
94
+
95
+ The `/positions` entries export the same names bound to the line-aware table:
96
+ switching is a change of import specifier, not of call site.
97
+
98
+ ### Choosing a grammar build
99
+
100
+ Each compiled grammar is a standalone multi-megabyte artifact, so the four
101
+ variants ship as four separate files. Importing one never loads the others.
102
+ Pick by the two questions the subpath name answers — which tree, and whether
103
+ source positions are tracked:
104
+
105
+ | Subpath | Export | Tree | Positions |
106
+ | --- | --- | --- | --- |
107
+ | `@jesscss/less-parser/grammar/ast` | `lessGrammar` | AST | no |
108
+ | `@jesscss/less-parser/grammar/ast/positions` | `lessPositionsGrammar` | AST | yes |
109
+ | `@jesscss/less-parser/grammar/cst` | `lessCstGrammar` | CST | no |
110
+ | `@jesscss/less-parser/grammar/cst/positions` | `lessCstPositionsGrammar` | CST | yes |
111
+
112
+ `@jesscss/less-parser/grammar` is an alias for `/grammar/ast`, the build the
113
+ shipping `parse()` route uses. It is not a barrel: it exposes the AST variant
114
+ only, so importing it cannot pull the other three in.
115
+
116
+ The positions variants set `startLine`/`startColumn` on every span. There is no
117
+ `trackLines` option: an option would force one module to name both tables, and
118
+ Node executes every module it statically imports, so the choice is which entry
119
+ you import. Error tolerance is not a property of a build — the CST runner
120
+ collects `result.errors` on either CST variant.
121
+
122
+
79
123
  ## Default CST shape
80
124
 
81
125
  The CST is parseman's, produced by the shared `cssCstBuildHost`. Three kinds of node:
@@ -95,7 +139,7 @@ Parsing `@c: red;\n.foo { color: @c; }` yields (abridged):
95
139
  { "_tag": "node", "type": "VarDeclaration", "grammarType": "VarDeclaration", "span": { "start": 0, "end": 8 },
96
140
  "children": [
97
141
  { "_tag": "leaf", "value": "@c" }, { "_tag": "leaf", "value": ":" },
98
- { "_tag": "node", "type": "NamedColor", "grammarType": "NamedColor",
142
+ { "_tag": "node", "type": "Keyword", "grammarType": "Keyword",
99
143
  "children": [ { "_tag": "leaf", "value": "red" } ] },
100
144
  { "_tag": "leaf", "value": ";" }
101
145
  ] },
@@ -115,9 +159,9 @@ Parsing `@c: red;\n.foo { color: @c; }` yields (abridged):
115
159
  }
116
160
  ```
117
161
 
118
- Note the Less-specific nodes: a top-level `@c: …` becomes a `VarDeclaration`, a `@c` value becomes a `Reference`, and the color keyword `red` parses as `NamedColor` (the CSS-only grammar has no such rule see `@jesscss/css-parser`).
162
+ Note the Less-specific nodes: a top-level `@c: …` becomes a `VarDeclaration`, and a `@c` value becomes a `Reference`. The color keyword `red` parses as a plain `Keyword` the same node every dialect uses (NamedColor→Keyword convergence); its colour-ness is resolved only when it is operated on.
119
163
 
120
- Pass `{ collapse: true }` to unwrap single-child wrapper types (`Reference`, `NamedColor`, `InterpolatedSelector`) into their child.
164
+ Pass `{ collapse: true }` to unwrap single-child wrapper types (`Reference`, `InterpolatedSelector`) into their child.
121
165
 
122
166
  ### Name-independent condition arguments
123
167
 
@@ -133,7 +177,7 @@ The grammar is decoupled from the tree it builds. Every capitalized rule is a pa
133
177
  import { run } from 'parseman'
134
178
  import { lessGrammar } from '@jesscss/less-parser/grammar'
135
179
 
136
- const myHost = (type, children, fields, span) => ({ type, span, children: children.filter(Boolean) })
180
+ const myHost = (type, children, fields, span) => ({ type, span, rules: children.filter(Boolean) })
137
181
 
138
182
  const result = run(lessGrammar.Stylesheet, '@c: red; .foo { color: @c; }', {
139
183
  build: myHost,
@@ -148,7 +192,7 @@ The `BuildHost` signature (from parseman):
148
192
  ```ts
149
193
  type BuildHost = (
150
194
  type: string,
151
- children: readonly unknown[],
195
+ rules: readonly unknown[],
152
196
  fields: FieldMap | undefined,
153
197
  span: { start: number; end: number },
154
198
  rawChildren: readonly unknown[],
@@ -0,0 +1,172 @@
1
+ const require_parse_error = require("../parse-error.cjs");
2
+ const require_trivia_labels = require("./trivia-labels.cjs");
3
+ let parseman = require("parseman");
4
+ let _jesscss_core_diagnostics = require("@jesscss/core/diagnostics");
5
+ let _jesscss_core_ast = require("@jesscss/core/ast");
6
+ Object.freeze({ mathMode: "parens-division" });
7
+ //#endregion
8
+ //#region src/parse-with.ts
9
+ /**
10
+ * The `parse()`/`safeParse()` bodies, shared by the two AST entries.
11
+ *
12
+ * The grammar table arrives as an argument rather than being chosen from a
13
+ * boolean inside this module: Node does not tree-shake, so a module that names
14
+ * both compiled tables executes both at load time. Each entry imports exactly
15
+ * the one table it parses with, and this module imports none.
16
+ */
17
+ const EMPTY_LAYOUT = Object.freeze([]);
18
+ const SPACE_LAYOUT = Object.freeze([" "]);
19
+ function isStylesheet(value) {
20
+ return typeof value === "object" && value !== null && "type" in value && value.type === "Stylesheet" && "rules" in value && Array.isArray(value.rules);
21
+ }
22
+ /**
23
+ * The one currently typed Less import-query feature is a `Block` containing a
24
+ * colon `Operation`. Its right operand already carries a parser source start,
25
+ * so an exact trivia lookup can retain the comment boundary without walking or
26
+ * classifying source bytes. Opaque tail forms keep their existing byte owner.
27
+ */
28
+ function retainTypedImportTailTrivia(statement, trivia) {
29
+ const prelude = statement.prelude;
30
+ if (prelude?.type !== "Sequence") return false;
31
+ const tail = prelude.parts[1];
32
+ if (tail?.type !== "Block" || (0, _jesscss_core_ast.isValueSlotArray)(tail.value) || tail.value.type !== "Operation" || tail.value.operator !== ":") return false;
33
+ const rightStart = (0, _jesscss_core_ast.sourceStartOf)(tail.value.right);
34
+ if (rightStart === _jesscss_core_ast.NO_SPAN) return false;
35
+ const between = trivia.lookup(rightStart, "before");
36
+ if (between === void 0) return false;
37
+ (0, _jesscss_core_ast.withValueBoundaryTrivia)(tail.value, EMPTY_LAYOUT, {
38
+ before: null,
39
+ between,
40
+ after: null
41
+ });
42
+ return true;
43
+ }
44
+ /**
45
+ * Project only root-hoisted CSS-import boundaries through the canonical trivia
46
+ * adapter. The grammar carries the statement edges and typed-tail start in
47
+ * fixed private Smi slots, so each lookup names an exact parser-owned boundary
48
+ * and public source provenance, AST/CST shape, and bytes remain unchanged.
49
+ */
50
+ function retainRootImportBoundaryTrivia(document, trivia) {
51
+ if (trivia === void 0) return;
52
+ for (const rule of document.rules) {
53
+ if (rule.type !== "AtRuleStatement") continue;
54
+ const start = (0, _jesscss_core_ast.importSourceStartOf)(rule);
55
+ if (start === _jesscss_core_ast.NO_SPAN) continue;
56
+ const tailStart = (0, _jesscss_core_ast.importTailStartOf)(rule);
57
+ const before = trivia.lookup(start + rule.name.length, "after") ?? null;
58
+ const between = tailStart === _jesscss_core_ast.NO_SPAN ? null : trivia.lookup(tailStart, "before") ?? null;
59
+ const after = trivia.lookup((0, _jesscss_core_ast.importSourceEndOf)(rule) - 1, "before") ?? null;
60
+ const hasInnerBoundary = retainTypedImportTailTrivia(rule, trivia);
61
+ if (before !== null || between !== null || after !== null || hasInnerBoundary) {
62
+ const prelude = rule.prelude;
63
+ (0, _jesscss_core_ast.withValueBoundaryTrivia)(prelude, (0, _jesscss_core_ast.valueLayoutOf)(prelude) ?? (prelude.type === "Sequence" ? SPACE_LAYOUT : EMPTY_LAYOUT), {
64
+ before,
65
+ between,
66
+ after
67
+ });
68
+ }
69
+ }
70
+ }
71
+ /**
72
+ * Line/column at a bare offset. The leftover-input offset is not the start of
73
+ * any span the run returned — `result.span` covers the text that *was*
74
+ * consumed — so the position has to be derived from the offset itself. Only
75
+ * ever reached on a throw path, so building the index here costs a parse
76
+ * nothing.
77
+ */
78
+ function positionAt(input, offset) {
79
+ const { line, col } = (0, parseman.offsetToLineCol)((0, parseman.buildLineIndex)(input), offset);
80
+ return {
81
+ line,
82
+ column: col
83
+ };
84
+ }
85
+ /**
86
+ * Line/column for a failure span. The compiled table without line tracking
87
+ * leaves a span's line fields unset, so fall back to deriving them; an error
88
+ * that reports an offset but no line is barely actionable in an editor.
89
+ */
90
+ function lineOptions(input, span) {
91
+ if (span.startLine === void 0) return positionAt(input, span.start);
92
+ return {
93
+ line: span.startLine,
94
+ column: span.startColumn,
95
+ endLine: span.endLine,
96
+ endColumn: span.endColumn
97
+ };
98
+ }
99
+ function parseWith(grammar, input, options = {}) {
100
+ const entry = grammar.Stylesheet;
101
+ const trivia = grammar.whitespace;
102
+ if (entry === void 0 || trivia === void 0) throw new TypeError("Less AST grammar is missing its public document entry.");
103
+ const result = (0, parseman.run)(entry, input, {
104
+ trivia,
105
+ state: {
106
+ source: input,
107
+ mathMode: options.mathMode ?? "parens-division"
108
+ },
109
+ rootTrivia: { select: require_trivia_labels.commentTriviaLabels }
110
+ });
111
+ if (!result.ok) throw new require_parse_error.LessParseError(result.span.start, result.expected, lineOptions(input, result.span));
112
+ if (result.unconsumedFrom !== null) {
113
+ if (isStylesheet(result.value) && result.value.rules.length > 0) throw new require_parse_error.LessParseError(result.unconsumedFrom, [], {
114
+ message: "Unexpected Less input after a complete stylesheet.",
115
+ reason: "The parser consumed a complete Less stylesheet before this token, so the remaining text is not part of any rule, declaration, or at-rule.",
116
+ fix: "Remove the extra input or wrap it in valid Less syntax.",
117
+ ...positionAt(input, result.unconsumedFrom)
118
+ });
119
+ throw new require_parse_error.LessParseError(result.unconsumedFrom, [], {
120
+ message: "Unexpected Less syntax.",
121
+ reason: "The parser could not match this token as the start of a Less rule, declaration, or at-rule.",
122
+ fix: "Remove the token or rewrite it as valid Less syntax.",
123
+ ...positionAt(input, result.unconsumedFrom)
124
+ });
125
+ }
126
+ if (!isStylesheet(result.value)) throw new require_parse_error.LessParseError(result.span.end, [], {
127
+ ...positionAt(input, result.span.end),
128
+ message: "Less parser did not produce a stylesheet.",
129
+ reason: "The Less parser matched the input but returned a value that is not a stylesheet document.",
130
+ fix: "Report this as a parser bug with the source that triggered it."
131
+ });
132
+ const triviaMap = (0, _jesscss_core_ast.createTriviaMapFromParseman)(input, result.rootTrivia?.index);
133
+ retainRootImportBoundaryTrivia(result.value, result.rootTrivia === void 0 ? void 0 : triviaMap);
134
+ return (0, _jesscss_core_ast.withTriviaMap)((0, _jesscss_core_ast.withSourceSpan)(result.value, result.span), triviaMap);
135
+ }
136
+ /**
137
+ * The `safeParse` body for the product plugin path. Parser packages own
138
+ * recognition facts; this boundary attaches file/source context once and
139
+ * returns normalized diagnostics for compiler and CLI consumers to render.
140
+ */
141
+ function safeParseWith(grammar, filePath, input, options = {}) {
142
+ try {
143
+ return {
144
+ document: parseWith(grammar, input, options),
145
+ errors: [],
146
+ warnings: []
147
+ };
148
+ } catch (error) {
149
+ return {
150
+ errors: [(0, _jesscss_core_diagnostics.parserDiagnostic)({
151
+ dialect: "Less",
152
+ error,
153
+ filePath,
154
+ source: input
155
+ })],
156
+ warnings: []
157
+ };
158
+ }
159
+ }
160
+ //#endregion
161
+ Object.defineProperty(exports, "parseWith", {
162
+ enumerable: true,
163
+ get: function() {
164
+ return parseWith;
165
+ }
166
+ });
167
+ Object.defineProperty(exports, "safeParseWith", {
168
+ enumerable: true,
169
+ get: function() {
170
+ return safeParseWith;
171
+ }
172
+ });
@@ -0,0 +1,161 @@
1
+ import { LessParseError } from "../parse-error.js";
2
+ import { t as commentTriviaLabels } from "./trivia-labels.js";
3
+ import { buildLineIndex, offsetToLineCol, run } from "parseman";
4
+ import { parserDiagnostic } from "@jesscss/core/diagnostics";
5
+ import { NO_SPAN, createTriviaMapFromParseman, importSourceEndOf, importSourceStartOf, importTailStartOf, isValueSlotArray, sourceStartOf, valueLayoutOf, withSourceSpan, withTriviaMap, withValueBoundaryTrivia } from "@jesscss/core/ast";
6
+ Object.freeze({ mathMode: "parens-division" });
7
+ //#endregion
8
+ //#region src/parse-with.ts
9
+ /**
10
+ * The `parse()`/`safeParse()` bodies, shared by the two AST entries.
11
+ *
12
+ * The grammar table arrives as an argument rather than being chosen from a
13
+ * boolean inside this module: Node does not tree-shake, so a module that names
14
+ * both compiled tables executes both at load time. Each entry imports exactly
15
+ * the one table it parses with, and this module imports none.
16
+ */
17
+ const EMPTY_LAYOUT = Object.freeze([]);
18
+ const SPACE_LAYOUT = Object.freeze([" "]);
19
+ function isStylesheet(value) {
20
+ return typeof value === "object" && value !== null && "type" in value && value.type === "Stylesheet" && "rules" in value && Array.isArray(value.rules);
21
+ }
22
+ /**
23
+ * The one currently typed Less import-query feature is a `Block` containing a
24
+ * colon `Operation`. Its right operand already carries a parser source start,
25
+ * so an exact trivia lookup can retain the comment boundary without walking or
26
+ * classifying source bytes. Opaque tail forms keep their existing byte owner.
27
+ */
28
+ function retainTypedImportTailTrivia(statement, trivia) {
29
+ const prelude = statement.prelude;
30
+ if (prelude?.type !== "Sequence") return false;
31
+ const tail = prelude.parts[1];
32
+ if (tail?.type !== "Block" || isValueSlotArray(tail.value) || tail.value.type !== "Operation" || tail.value.operator !== ":") return false;
33
+ const rightStart = sourceStartOf(tail.value.right);
34
+ if (rightStart === NO_SPAN) return false;
35
+ const between = trivia.lookup(rightStart, "before");
36
+ if (between === void 0) return false;
37
+ withValueBoundaryTrivia(tail.value, EMPTY_LAYOUT, {
38
+ before: null,
39
+ between,
40
+ after: null
41
+ });
42
+ return true;
43
+ }
44
+ /**
45
+ * Project only root-hoisted CSS-import boundaries through the canonical trivia
46
+ * adapter. The grammar carries the statement edges and typed-tail start in
47
+ * fixed private Smi slots, so each lookup names an exact parser-owned boundary
48
+ * and public source provenance, AST/CST shape, and bytes remain unchanged.
49
+ */
50
+ function retainRootImportBoundaryTrivia(document, trivia) {
51
+ if (trivia === void 0) return;
52
+ for (const rule of document.rules) {
53
+ if (rule.type !== "AtRuleStatement") continue;
54
+ const start = importSourceStartOf(rule);
55
+ if (start === NO_SPAN) continue;
56
+ const tailStart = importTailStartOf(rule);
57
+ const before = trivia.lookup(start + rule.name.length, "after") ?? null;
58
+ const between = tailStart === NO_SPAN ? null : trivia.lookup(tailStart, "before") ?? null;
59
+ const after = trivia.lookup(importSourceEndOf(rule) - 1, "before") ?? null;
60
+ const hasInnerBoundary = retainTypedImportTailTrivia(rule, trivia);
61
+ if (before !== null || between !== null || after !== null || hasInnerBoundary) {
62
+ const prelude = rule.prelude;
63
+ withValueBoundaryTrivia(prelude, valueLayoutOf(prelude) ?? (prelude.type === "Sequence" ? SPACE_LAYOUT : EMPTY_LAYOUT), {
64
+ before,
65
+ between,
66
+ after
67
+ });
68
+ }
69
+ }
70
+ }
71
+ /**
72
+ * Line/column at a bare offset. The leftover-input offset is not the start of
73
+ * any span the run returned — `result.span` covers the text that *was*
74
+ * consumed — so the position has to be derived from the offset itself. Only
75
+ * ever reached on a throw path, so building the index here costs a parse
76
+ * nothing.
77
+ */
78
+ function positionAt(input, offset) {
79
+ const { line, col } = offsetToLineCol(buildLineIndex(input), offset);
80
+ return {
81
+ line,
82
+ column: col
83
+ };
84
+ }
85
+ /**
86
+ * Line/column for a failure span. The compiled table without line tracking
87
+ * leaves a span's line fields unset, so fall back to deriving them; an error
88
+ * that reports an offset but no line is barely actionable in an editor.
89
+ */
90
+ function lineOptions(input, span) {
91
+ if (span.startLine === void 0) return positionAt(input, span.start);
92
+ return {
93
+ line: span.startLine,
94
+ column: span.startColumn,
95
+ endLine: span.endLine,
96
+ endColumn: span.endColumn
97
+ };
98
+ }
99
+ function parseWith(grammar, input, options = {}) {
100
+ const entry = grammar.Stylesheet;
101
+ const trivia = grammar.whitespace;
102
+ if (entry === void 0 || trivia === void 0) throw new TypeError("Less AST grammar is missing its public document entry.");
103
+ const result = run(entry, input, {
104
+ trivia,
105
+ state: {
106
+ source: input,
107
+ mathMode: options.mathMode ?? "parens-division"
108
+ },
109
+ rootTrivia: { select: commentTriviaLabels }
110
+ });
111
+ if (!result.ok) throw new LessParseError(result.span.start, result.expected, lineOptions(input, result.span));
112
+ if (result.unconsumedFrom !== null) {
113
+ if (isStylesheet(result.value) && result.value.rules.length > 0) throw new LessParseError(result.unconsumedFrom, [], {
114
+ message: "Unexpected Less input after a complete stylesheet.",
115
+ reason: "The parser consumed a complete Less stylesheet before this token, so the remaining text is not part of any rule, declaration, or at-rule.",
116
+ fix: "Remove the extra input or wrap it in valid Less syntax.",
117
+ ...positionAt(input, result.unconsumedFrom)
118
+ });
119
+ throw new LessParseError(result.unconsumedFrom, [], {
120
+ message: "Unexpected Less syntax.",
121
+ reason: "The parser could not match this token as the start of a Less rule, declaration, or at-rule.",
122
+ fix: "Remove the token or rewrite it as valid Less syntax.",
123
+ ...positionAt(input, result.unconsumedFrom)
124
+ });
125
+ }
126
+ if (!isStylesheet(result.value)) throw new LessParseError(result.span.end, [], {
127
+ ...positionAt(input, result.span.end),
128
+ message: "Less parser did not produce a stylesheet.",
129
+ reason: "The Less parser matched the input but returned a value that is not a stylesheet document.",
130
+ fix: "Report this as a parser bug with the source that triggered it."
131
+ });
132
+ const triviaMap = createTriviaMapFromParseman(input, result.rootTrivia?.index);
133
+ retainRootImportBoundaryTrivia(result.value, result.rootTrivia === void 0 ? void 0 : triviaMap);
134
+ return withTriviaMap(withSourceSpan(result.value, result.span), triviaMap);
135
+ }
136
+ /**
137
+ * The `safeParse` body for the product plugin path. Parser packages own
138
+ * recognition facts; this boundary attaches file/source context once and
139
+ * returns normalized diagnostics for compiler and CLI consumers to render.
140
+ */
141
+ function safeParseWith(grammar, filePath, input, options = {}) {
142
+ try {
143
+ return {
144
+ document: parseWith(grammar, input, options),
145
+ errors: [],
146
+ warnings: []
147
+ };
148
+ } catch (error) {
149
+ return {
150
+ errors: [parserDiagnostic({
151
+ dialect: "Less",
152
+ error,
153
+ filePath,
154
+ source: input
155
+ })],
156
+ warnings: []
157
+ };
158
+ }
159
+ }
160
+ //#endregion
161
+ export { safeParseWith as n, parseWith as t };
@@ -0,0 +1,18 @@
1
+ //#region src/trivia-labels.ts
2
+ /**
3
+ * Root-trivia label selection for the Less grammar.
4
+ *
5
+ * This is a plain fact about the grammar's trivia arm labels, so it lives in a
6
+ * leaf module with no imports. Reading it from the CST entry instead would make
7
+ * every consumer of the package entry load the compiled CST grammar tables:
8
+ * Node's ESM loader does not tree-shake, so an unused named import still
9
+ * executes the module it is taken from, and each table is multiple megabytes.
10
+ */
11
+ const commentTriviaLabels = ["lineComment", "blockComment"];
12
+ //#endregion
13
+ Object.defineProperty(exports, "commentTriviaLabels", {
14
+ enumerable: true,
15
+ get: function() {
16
+ return commentTriviaLabels;
17
+ }
18
+ });
@@ -0,0 +1,13 @@
1
+ //#region src/trivia-labels.ts
2
+ /**
3
+ * Root-trivia label selection for the Less grammar.
4
+ *
5
+ * This is a plain fact about the grammar's trivia arm labels, so it lives in a
6
+ * leaf module with no imports. Reading it from the CST entry instead would make
7
+ * every consumer of the package entry load the compiled CST grammar tables:
8
+ * Node's ESM loader does not tree-shake, so an unused named import still
9
+ * executes the module it is taken from, and each table is multiple megabytes.
10
+ */
11
+ const commentTriviaLabels = ["lineComment", "blockComment"];
12
+ //#endregion
13
+ export { commentTriviaLabels as t };
@@ -0,0 +1,24 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_trivia_labels = require("../chunks/trivia-labels.cjs");
3
+ let _jesscss_css_parser_cst_host = require("@jesscss/css-parser/cst-host");
4
+ let src_grammar_cst_positions_js = require("../grammar/cst/positions.cjs");
5
+ //#region src/cst/positions.ts
6
+ /**
7
+ * The line-aware Less CST entry: the `./cst` functions bound to the compiled
8
+ * table that tracks lines and columns. Tolerant error recovery is a property of
9
+ * the CST runner, not of the table, so `result.errors` is collected here
10
+ * exactly as it is on `./cst` — this entry adds line/column facts and nothing
11
+ * else. It never loads the offsets-only table.
12
+ */
13
+ /** Parse Less to a CST whose spans carry line and column facts. */
14
+ function parseLessCst(input, startRule = "Stylesheet", options) {
15
+ return (0, _jesscss_css_parser_cst_host.parseCst)(src_grammar_cst_positions_js.lessCstPositionsGrammar, input, startRule, options, require_trivia_labels.commentTriviaLabels);
16
+ }
17
+ /** Incremental (`.edit()`-able) Less document with line and column facts. */
18
+ function parseLessDoc(input, startRule = "Stylesheet") {
19
+ return (0, _jesscss_css_parser_cst_host.parseDocCst)(src_grammar_cst_positions_js.lessCstPositionsGrammar, input, startRule);
20
+ }
21
+ //#endregion
22
+ exports.commentTriviaLabels = require_trivia_labels.commentTriviaLabels;
23
+ exports.parseLessCst = parseLessCst;
24
+ exports.parseLessDoc = parseLessDoc;
@@ -0,0 +1,8 @@
1
+ import { type CssCstNode, type CssCstParseOptions, type CssCstParseResult, type ParseDoc } from '@jesscss/css-parser/cst-host';
2
+ export { commentTriviaLabels } from '../trivia-labels.js';
3
+ /** Parse Less to a CST whose spans carry line and column facts. */
4
+ export declare function parseLessCst(input: string, startRule?: string, options?: CssCstParseOptions): CssCstParseResult;
5
+ /** Incremental (`.edit()`-able) Less document with line and column facts. */
6
+ export declare function parseLessDoc(input: string, startRule?: string): ParseDoc<CssCstNode>;
7
+ export type { CssCstChild as LessCstChild, CssCstError as LessCstError, CssCstLeaf as LessCstLeaf, CssCstNode as LessCstNode, CssCstParseOptions as LessCstParseOptions, CssCstParseResult as LessCstParseResult, CssCstType as LessCstType } from '@jesscss/css-parser/cst-host';
8
+ //# sourceMappingURL=positions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"positions.d.ts","sourceRoot":"","sources":["../../src/cst/positions.ts"],"names":[],"mappings":"AAQA,OAAO,EAAyB,KAAK,UAAU,EAAE,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,KAAK,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAGtJ,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,mEAAmE;AACnE,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,SAAS,SAAe,EACxB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,iBAAiB,CAQnB;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,SAAS,SAAe,GACvB,QAAQ,CAAC,UAAU,CAAC,CAMtB;AAED,YAAY,EACV,WAAW,IAAI,YAAY,EAC3B,WAAW,IAAI,YAAY,EAC3B,UAAU,IAAI,WAAW,EACzB,UAAU,IAAI,WAAW,EACzB,kBAAkB,IAAI,mBAAmB,EACzC,iBAAiB,IAAI,kBAAkB,EACvC,UAAU,IAAI,WAAW,EAC1B,MAAM,8BAA8B,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { t as commentTriviaLabels } from "../chunks/trivia-labels.js";
2
+ import { parseCst, parseDocCst } from "@jesscss/css-parser/cst-host";
3
+ import { lessCstPositionsGrammar } from "../grammar/cst/positions.js";
4
+ //#region src/cst/positions.ts
5
+ /**
6
+ * The line-aware Less CST entry: the `./cst` functions bound to the compiled
7
+ * table that tracks lines and columns. Tolerant error recovery is a property of
8
+ * the CST runner, not of the table, so `result.errors` is collected here
9
+ * exactly as it is on `./cst` — this entry adds line/column facts and nothing
10
+ * else. It never loads the offsets-only table.
11
+ */
12
+ /** Parse Less to a CST whose spans carry line and column facts. */
13
+ function parseLessCst(input, startRule = "Stylesheet", options) {
14
+ return parseCst(lessCstPositionsGrammar, input, startRule, options, commentTriviaLabels);
15
+ }
16
+ /** Incremental (`.edit()`-able) Less document with line and column facts. */
17
+ function parseLessDoc(input, startRule = "Stylesheet") {
18
+ return parseDocCst(lessCstPositionsGrammar, input, startRule);
19
+ }
20
+ //#endregion
21
+ export { commentTriviaLabels, parseLessCst, parseLessDoc };
package/lib/cst.cjs CHANGED
@@ -1,14 +1,21 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_grammar = require("./grammar2.cjs");
3
- let _jesscss_css_parser_cst = require("@jesscss/css-parser/cst");
2
+ const require_trivia_labels = require("./chunks/trivia-labels.cjs");
3
+ let _jesscss_css_parser_cst_host = require("@jesscss/css-parser/cst-host");
4
+ let src_grammar_cst_js = require("./grammar/cst.cjs");
4
5
  //#region src/cst.ts
6
+ /**
7
+ * Parse Less to a CST. Spans carry offsets only; for line/column facts import
8
+ * the same functions from `@jesscss/less-parser/cst/positions`, which binds the
9
+ * line-aware compiled table. This entry never loads that table.
10
+ */
5
11
  function parseLessCst(input, startRule = "Stylesheet", options) {
6
- return (0, _jesscss_css_parser_cst.parseCst)(require_grammar.lessCstGrammar, input, startRule, options);
12
+ return (0, _jesscss_css_parser_cst_host.parseCst)(src_grammar_cst_js.lessCstGrammar, input, startRule, options, require_trivia_labels.commentTriviaLabels);
7
13
  }
8
14
  /** Incremental (`.edit()`-able) Less document — see `parseDocCst`. */
9
15
  function parseLessDoc(input, startRule = "Stylesheet") {
10
- return (0, _jesscss_css_parser_cst.parseDocCst)(require_grammar.lessCstGrammar, input, startRule);
16
+ return (0, _jesscss_css_parser_cst_host.parseDocCst)(src_grammar_cst_js.lessCstGrammar, input, startRule);
11
17
  }
12
18
  //#endregion
19
+ exports.commentTriviaLabels = require_trivia_labels.commentTriviaLabels;
13
20
  exports.parseLessCst = parseLessCst;
14
21
  exports.parseLessDoc = parseLessDoc;
package/lib/cst.d.ts CHANGED
@@ -1,6 +1,12 @@
1
- import { type CssCstNode, type CssCstParseOptions, type CssCstParseResult, type ParseDoc } from '@jesscss/css-parser/cst';
1
+ import { type CssCstNode, type CssCstParseOptions, type CssCstParseResult, type ParseDoc } from '@jesscss/css-parser/cst-host';
2
+ export { commentTriviaLabels } from './trivia-labels.js';
3
+ /**
4
+ * Parse Less to a CST. Spans carry offsets only; for line/column facts import
5
+ * the same functions from `@jesscss/less-parser/cst/positions`, which binds the
6
+ * line-aware compiled table. This entry never loads that table.
7
+ */
2
8
  export declare function parseLessCst(input: string, startRule?: string, options?: CssCstParseOptions): CssCstParseResult;
3
9
  /** Incremental (`.edit()`-able) Less document — see `parseDocCst`. */
4
10
  export declare function parseLessDoc(input: string, startRule?: string): ParseDoc<CssCstNode>;
5
- export type { CssCstChild as LessCstChild, CssCstError as LessCstError, CssCstLeaf as LessCstLeaf, CssCstNode as LessCstNode, CssCstParseOptions as LessCstParseOptions, CssCstParseResult as LessCstParseResult, CssCstType as LessCstType } from '@jesscss/css-parser/cst';
11
+ export type { CssCstChild as LessCstChild, CssCstError as LessCstError, CssCstLeaf as LessCstLeaf, CssCstNode as LessCstNode, CssCstParseOptions as LessCstParseOptions, CssCstParseResult as LessCstParseResult, CssCstType as LessCstType } from '@jesscss/css-parser/cst-host';
6
12
  //# sourceMappingURL=cst.d.ts.map
package/lib/cst.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cst.d.ts","sourceRoot":"","sources":["../src/cst.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,UAAU,EAAE,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAGjJ,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,SAAS,SAAe,EACxB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,iBAAiB,CAEnB;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,SAAe,GAAG,QAAQ,CAAC,UAAU,CAAC,CAE1F;AAED,YAAY,EACV,WAAW,IAAI,YAAY,EAC3B,WAAW,IAAI,YAAY,EAC3B,UAAU,IAAI,WAAW,EACzB,UAAU,IAAI,WAAW,EACzB,kBAAkB,IAAI,mBAAmB,EACzC,iBAAiB,IAAI,kBAAkB,EACvC,UAAU,IAAI,WAAW,EAC1B,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"cst.d.ts","sourceRoot":"","sources":["../src/cst.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,UAAU,EAAE,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,KAAK,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAGtJ,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,SAAS,SAAe,EACxB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,iBAAiB,CAQnB;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,SAAS,SAAe,GACvB,QAAQ,CAAC,UAAU,CAAC,CAMtB;AAED,YAAY,EACV,WAAW,IAAI,YAAY,EAC3B,WAAW,IAAI,YAAY,EAC3B,UAAU,IAAI,WAAW,EACzB,UAAU,IAAI,WAAW,EACzB,kBAAkB,IAAI,mBAAmB,EACzC,iBAAiB,IAAI,kBAAkB,EACvC,UAAU,IAAI,WAAW,EAC1B,MAAM,8BAA8B,CAAC"}
package/lib/cst.js CHANGED
@@ -1,12 +1,18 @@
1
- import { n as lessCstGrammar } from "./grammar2.js";
2
- import { parseCst, parseDocCst } from "@jesscss/css-parser/cst";
1
+ import { t as commentTriviaLabels } from "./chunks/trivia-labels.js";
2
+ import { parseCst, parseDocCst } from "@jesscss/css-parser/cst-host";
3
+ import { lessCstGrammar } from "./grammar/cst.js";
3
4
  //#region src/cst.ts
5
+ /**
6
+ * Parse Less to a CST. Spans carry offsets only; for line/column facts import
7
+ * the same functions from `@jesscss/less-parser/cst/positions`, which binds the
8
+ * line-aware compiled table. This entry never loads that table.
9
+ */
4
10
  function parseLessCst(input, startRule = "Stylesheet", options) {
5
- return parseCst(lessCstGrammar, input, startRule, options);
11
+ return parseCst(lessCstGrammar, input, startRule, options, commentTriviaLabels);
6
12
  }
7
13
  /** Incremental (`.edit()`-able) Less document — see `parseDocCst`. */
8
14
  function parseLessDoc(input, startRule = "Stylesheet") {
9
15
  return parseDocCst(lessCstGrammar, input, startRule);
10
16
  }
11
17
  //#endregion
12
- export { parseLessCst, parseLessDoc };
18
+ export { commentTriviaLabels, parseLessCst, parseLessDoc };