@jesscss/less-parser 1.0.8-alpha.6 → 2.0.0-alpha.10
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 +165 -2
- package/lib/cst.cjs +14 -0
- package/lib/cst.d.ts +6 -0
- package/lib/cst.d.ts.map +1 -0
- package/lib/cst.js +12 -0
- package/lib/grammar.cjs +5 -0
- package/lib/grammar.d.ts +5 -0
- package/lib/grammar.d.ts.map +1 -0
- package/lib/grammar.js +2 -0
- package/lib/grammar2.cjs +141007 -0
- package/lib/grammar2.js +140948 -0
- package/lib/index.cjs +72 -0
- package/lib/index.d.ts +12 -11
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +62 -37
- package/lib/parse-error.d.ts +68 -0
- package/lib/parse-error.d.ts.map +1 -0
- package/package.json +62 -19
- package/lib/lessParser.d.ts +0 -36
- package/lib/lessParser.js +0 -49
- package/lib/lessTokens.d.ts +0 -3
- package/lib/lessTokens.js +0 -155
- package/lib/productions/atRules.d.ts +0 -2
- package/lib/productions/atRules.js +0 -160
- package/lib/productions/blocks.d.ts +0 -2
- package/lib/productions/blocks.js +0 -60
- package/lib/productions/declarations.d.ts +0 -2
- package/lib/productions/declarations.js +0 -36
- package/lib/productions/interpolation.d.ts +0 -2
- package/lib/productions/interpolation.js +0 -18
- package/lib/productions/mixin.d.ts +0 -2
- package/lib/productions/mixin.js +0 -373
- package/lib/productions/root.d.ts +0 -2
- package/lib/productions/root.js +0 -33
- package/lib/productions/selectors.d.ts +0 -2
- package/lib/productions/selectors.js +0 -91
- package/lib/productions/values.d.ts +0 -2
- package/lib/productions/values.js +0 -305
package/README.md
CHANGED
|
@@ -1,3 +1,166 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @jesscss/less-parser
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The Less grammar, layered on the CSS base parser, with core-free CST entry points.
|
|
4
|
+
|
|
5
|
+
> **Status: alpha.** Part of [Jess](https://github.com/jesscss/jess). The broader
|
|
6
|
+
> language/tooling picture is still early. Expect gaps and
|
|
7
|
+
> [report bugs](https://github.com/jesscss/jess/issues). Docs live at
|
|
8
|
+
> [jesscss.github.io](https://jesscss.github.io/).
|
|
9
|
+
|
|
10
|
+
## What it is
|
|
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
|
|
17
|
+
general-purpose JavaScript parser** in its
|
|
18
|
+
[published benchmarks](https://matthew-dean.github.io/parseman/guide/benchmarks)
|
|
19
|
+
(see `@jesscss/css-parser` for figures and engineering details). It is the parser
|
|
20
|
+
Jess uses when it compiles `.less` — the "Now" tier of the language roadmap, and
|
|
21
|
+
the one dialect shipping in the alpha.
|
|
22
|
+
|
|
23
|
+
The default `parse()` operation constructs canonical AST v2 `Stylesheet` directly
|
|
24
|
+
through parser-local Parseman reductions. Use the explicit `./cst` entry when a
|
|
25
|
+
language-service or document consumer needs a CST. The package has no
|
|
26
|
+
core-owned parser driver or AST construction host.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
npm install @jesscss/less-parser
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`@jesscss/core` is an optional peer for consumers using the default AST v2
|
|
35
|
+
`parse()` result. The explicit CST and grammar subpaths remain core-free.
|
|
36
|
+
Those explicit entries expose Parseman types and grammar values, so consumers
|
|
37
|
+
of them must also provide the package's `parseman` peer.
|
|
38
|
+
|
|
39
|
+
## Canonical AST parsing
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
import { parse } from '@jesscss/less-parser'
|
|
43
|
+
|
|
44
|
+
const stylesheet = parse('@c: red;\n.foo { color: @c; }')
|
|
45
|
+
|
|
46
|
+
stylesheet.type // 'Stylesheet'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Standalone usage (core-free)
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
import { parseLessCst } from '@jesscss/less-parser/cst'
|
|
53
|
+
|
|
54
|
+
const result = parseLessCst('@c: red;\n.foo { color: @c; }')
|
|
55
|
+
|
|
56
|
+
result.ok // true
|
|
57
|
+
result.errors // ParseError[] (empty when ok)
|
|
58
|
+
result.unconsumedFrom // index of first unparsed char, or null
|
|
59
|
+
result.tree // the CST root (a StyleSheet node)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Signature:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
parseLessCst(input: string, startRule = 'Stylesheet', options?: { collapse?: boolean }): LessCstParseResult
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Pass a different `startRule` (any capitalized grammar rule, e.g. `'SelectorList'`, `'Declaration'`) to parse a fragment.
|
|
69
|
+
|
|
70
|
+
## Public API
|
|
71
|
+
|
|
72
|
+
| Entry | Export | Purpose |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| `@jesscss/less-parser/cst` | `parseLessCst` | Core-free parse of a Less string to a CST. |
|
|
75
|
+
| `@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`. |
|
|
77
|
+
| `@jesscss/less-parser` (`.`) | `parse` | Parse Less directly to canonical AST v2 `Stylesheet`. It does not load the CST grammar. |
|
|
78
|
+
|
|
79
|
+
## Default CST shape
|
|
80
|
+
|
|
81
|
+
The CST is parseman's, produced by the shared `cssCstBuildHost`. Three kinds of node:
|
|
82
|
+
|
|
83
|
+
- **node** — `{ _tag: 'node', type, grammarType, span: { start, end }, state, children }` (`grammarType` = raw rule name; `type` = friendly public name).
|
|
84
|
+
- **leaf** — `{ _tag: 'leaf', value, span }` for terminals.
|
|
85
|
+
- **error** — `{ _tag: 'error', type, span, expected, children, state }` where recovery happened.
|
|
86
|
+
|
|
87
|
+
Spans are `[start, end)` offsets; whitespace, block comments, **and Less line comments (`//`)** are trivia and do not appear as children.
|
|
88
|
+
|
|
89
|
+
Parsing `@c: red;\n.foo { color: @c; }` yields (abridged):
|
|
90
|
+
|
|
91
|
+
```jsonc
|
|
92
|
+
{
|
|
93
|
+
"_tag": "node", "type": "StyleSheet", "grammarType": "Stylesheet",
|
|
94
|
+
"children": [
|
|
95
|
+
{ "_tag": "node", "type": "VarDeclaration", "grammarType": "VarDeclaration", "span": { "start": 0, "end": 8 },
|
|
96
|
+
"children": [
|
|
97
|
+
{ "_tag": "leaf", "value": "@c" }, { "_tag": "leaf", "value": ":" },
|
|
98
|
+
{ "_tag": "node", "type": "NamedColor", "grammarType": "NamedColor",
|
|
99
|
+
"children": [ { "_tag": "leaf", "value": "red" } ] },
|
|
100
|
+
{ "_tag": "leaf", "value": ";" }
|
|
101
|
+
] },
|
|
102
|
+
{ "_tag": "node", "type": "QualifiedRule", "grammarType": "Ruleset", "span": { "start": 9, "end": 28 },
|
|
103
|
+
"children": [
|
|
104
|
+
{ "_tag": "leaf", "value": ".foo" }, { "_tag": "leaf", "value": "{" },
|
|
105
|
+
{ "_tag": "node", "type": "Declaration", "grammarType": "Declaration",
|
|
106
|
+
"children": [
|
|
107
|
+
{ "_tag": "leaf", "value": "color" }, { "_tag": "leaf", "value": ":" },
|
|
108
|
+
{ "_tag": "node", "type": "Reference", "grammarType": "Reference",
|
|
109
|
+
"children": [ { "_tag": "leaf", "value": "@c" } ] },
|
|
110
|
+
{ "_tag": "leaf", "value": ";" }
|
|
111
|
+
] },
|
|
112
|
+
{ "_tag": "leaf", "value": "}" }
|
|
113
|
+
] }
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
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`).
|
|
119
|
+
|
|
120
|
+
Pass `{ collapse: true }` to unwrap single-child wrapper types (`Reference`, `NamedColor`, `InterpolatedSelector`) into their child.
|
|
121
|
+
|
|
122
|
+
### Name-independent condition arguments
|
|
123
|
+
|
|
124
|
+
A top-level condition operator (`> < >= <= = and or not`) inside **any** call's argument parses as a `Condition` node — there is **no** parse-time name-dispatch on `if`/`boolean`. `if(@a > 5, 1, 2)`, `boolean(not(2 < 1))`, `#ns.if(@a > 5)`, and `foo(@a > 5 and @b < 2)` all route through the ordinary function/mixin `Call` production; the shared call-arg rule (`ArgCondition` → `CondArgOr`/`CondArgAnd`/`CondArgTerm`) layers the condition-operator precedence chain on top of the normal value production. The layer is structurally gated: it only matches when a real operator is present, so a plain value / space-list argument (and mixin-definition params) fall through to the unchanged `valueSequence` byte-identically. Eval treats `if`/`boolean` as ordinary registered functions that consume the parsed `Condition`, so this is a parse-only unification (a deliberate v5 loosening vs Less 4.x, which name-dispatched and errored on the namespaced/generic forms).
|
|
125
|
+
|
|
126
|
+
One known gap: a namespace/accessor call in **value** position (`b: #ns.if(@a > 5)`, `b: .if(@a > 5)`) is reassembled from a raw permissive-paren capture (`_buildRefCallArgs`), a separate shallow path that does not run the condition layer — its args stay a value list. Statement-position (`#ns.if(@a > 5) { }` / bare `#ns.if(@a > 5)`) and all function-call forms are covered.
|
|
127
|
+
|
|
128
|
+
## Extending with your own builders
|
|
129
|
+
|
|
130
|
+
The grammar is decoupled from the tree it builds. Every capitalized rule is a parseman `node()`; when you drive a grammar with a `build` host, each `node()` calls your host instead of constructing the default CST. Use parseman's `run` with your own host and the grammar's trivia rule:
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
import { run } from 'parseman'
|
|
134
|
+
import { lessGrammar } from '@jesscss/less-parser/grammar'
|
|
135
|
+
|
|
136
|
+
const myHost = (type, children, fields, span) => ({ type, span, children: children.filter(Boolean) })
|
|
137
|
+
|
|
138
|
+
const result = run(lessGrammar.Stylesheet, '@c: red; .foo { color: @c; }', {
|
|
139
|
+
build: myHost,
|
|
140
|
+
trivia: lessGrammar.rw // Less trivia = whitespace + block + line comments
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
result.value // the root node your host returned
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The `BuildHost` signature (from parseman):
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
type BuildHost = (
|
|
150
|
+
type: string,
|
|
151
|
+
children: readonly unknown[],
|
|
152
|
+
fields: FieldMap | undefined,
|
|
153
|
+
span: { start: number; end: number },
|
|
154
|
+
rawChildren: readonly unknown[],
|
|
155
|
+
triviaLog: readonly number[],
|
|
156
|
+
state: unknown
|
|
157
|
+
) => unknown
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`parseLessCst(...)` is this pattern with the shared `cssCstBuildHost` (see `@jesscss/css-parser`, `src/cst.ts`) as a reference host.
|
|
161
|
+
|
|
162
|
+
## Part of Jess
|
|
163
|
+
|
|
164
|
+
This package is developed as part of [Jess](https://github.com/jesscss/jess).
|
|
165
|
+
Jess translates a Less string into the core Jess AST, which the compiler then
|
|
166
|
+
evaluates and renders to CSS. Licensed MIT.
|
package/lib/cst.cjs
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
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");
|
|
4
|
+
//#region src/cst.ts
|
|
5
|
+
function parseLessCst(input, startRule = "Stylesheet", options) {
|
|
6
|
+
return (0, _jesscss_css_parser_cst.parseCst)(require_grammar.lessCstGrammar, input, startRule, options);
|
|
7
|
+
}
|
|
8
|
+
/** Incremental (`.edit()`-able) Less document — see `parseDocCst`. */
|
|
9
|
+
function parseLessDoc(input, startRule = "Stylesheet") {
|
|
10
|
+
return (0, _jesscss_css_parser_cst.parseDocCst)(require_grammar.lessCstGrammar, input, startRule);
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
exports.parseLessCst = parseLessCst;
|
|
14
|
+
exports.parseLessDoc = parseLessDoc;
|
package/lib/cst.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type CssCstNode, type CssCstParseOptions, type CssCstParseResult, type ParseDoc } from '@jesscss/css-parser/cst';
|
|
2
|
+
export declare function parseLessCst(input: string, startRule?: string, options?: CssCstParseOptions): CssCstParseResult;
|
|
3
|
+
/** Incremental (`.edit()`-able) Less document — see `parseDocCst`. */
|
|
4
|
+
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';
|
|
6
|
+
//# sourceMappingURL=cst.d.ts.map
|
package/lib/cst.d.ts.map
ADDED
|
@@ -0,0 +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"}
|
package/lib/cst.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { n as lessCstGrammar } from "./grammar2.js";
|
|
2
|
+
import { parseCst, parseDocCst } from "@jesscss/css-parser/cst";
|
|
3
|
+
//#region src/cst.ts
|
|
4
|
+
function parseLessCst(input, startRule = "Stylesheet", options) {
|
|
5
|
+
return parseCst(lessCstGrammar, input, startRule, options);
|
|
6
|
+
}
|
|
7
|
+
/** Incremental (`.edit()`-able) Less document — see `parseDocCst`. */
|
|
8
|
+
function parseLessDoc(input, startRule = "Stylesheet") {
|
|
9
|
+
return parseDocCst(lessCstGrammar, input, startRule);
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { parseLessCst, parseLessDoc };
|
package/lib/grammar.cjs
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_grammar = require("./grammar2.cjs");
|
|
3
|
+
exports.lessAstGrammar = require_grammar.lessAstGrammar;
|
|
4
|
+
exports.lessCstGrammar = require_grammar.lessCstGrammar;
|
|
5
|
+
exports.lessGrammar = require_grammar.lessGrammar;
|
package/lib/grammar.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const lessGrammar: Record<string, import("parseman").FusedRule>;
|
|
2
|
+
export declare const lessAstGrammar: Record<string, import("parseman").FusedRule>;
|
|
3
|
+
/** Public Less CST artifact: the same grammar factory compiled in CST mode. */
|
|
4
|
+
export declare const lessCstGrammar: Record<string, import("parseman").FusedRule>;
|
|
5
|
+
//# sourceMappingURL=grammar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grammar.d.ts","sourceRoot":"","sources":["../src/grammar.ts"],"names":[],"mappings":"AAixLA,eAAO,MAAM,WAAW,8CAAwL,CAAC;AACjN,eAAO,MAAM,cAAc,8CAAc,CAAC;AAE1C,+EAA+E;AAC/E,eAAO,MAAM,cAAc,8CAAyM,CAAC"}
|
package/lib/grammar.js
ADDED