@jesscss/less-parser 2.0.0-alpha.8 → 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.
Files changed (58) hide show
  1. package/README.md +27 -10
  2. package/lib/ast/grammar.d.ts +2 -0
  3. package/lib/ast/grammar.d.ts.map +1 -0
  4. package/lib/grammar.cjs +39855 -31054
  5. package/lib/grammar.d.ts.map +1 -1
  6. package/lib/grammar.js +39855 -31054
  7. package/lib/index.cjs +49609 -15
  8. package/lib/index.d.ts +4 -9
  9. package/lib/index.d.ts.map +1 -1
  10. package/lib/index.js +49607 -5
  11. package/lib/parse-error.d.ts +15 -0
  12. package/lib/parse-error.d.ts.map +1 -0
  13. package/package.json +9 -24
  14. package/lib/builders.d.ts +0 -381
  15. package/lib/builders.d.ts.map +0 -1
  16. package/lib/functional-parser.cjs +0 -2654
  17. package/lib/functional-parser.d.ts +0 -18
  18. package/lib/functional-parser.d.ts.map +0 -1
  19. package/lib/functional-parser.js +0 -2589
  20. package/lib/jess.cjs +0 -3968
  21. package/lib/jess.d.ts +0 -7
  22. package/lib/jess.d.ts.map +0 -1
  23. package/lib/jess.js +0 -3962
  24. package/lib/lessParser.d.ts +0 -38
  25. package/lib/lessParser.d.ts.map +0 -1
  26. package/lib/lessRecursiveParser.d.ts +0 -99
  27. package/lib/lessRecursiveParser.d.ts.map +0 -1
  28. package/lib/lessTokens.d.ts +0 -23
  29. package/lib/lessTokens.d.ts.map +0 -1
  30. package/lib/productions/guards.d.ts +0 -113
  31. package/lib/productions/guards.d.ts.map +0 -1
  32. package/lib/productions/index.d.ts +0 -5
  33. package/lib/productions/index.d.ts.map +0 -1
  34. package/lib/productions/root.d.ts +0 -38
  35. package/lib/productions/root.d.ts.map +0 -1
  36. package/lib/productions/selectors.d.ts +0 -41
  37. package/lib/productions/selectors.d.ts.map +0 -1
  38. package/lib/productions/values.d.ts +0 -35
  39. package/lib/productions/values.d.ts.map +0 -1
  40. package/lib/utils.d.ts +0 -9
  41. package/lib/utils.d.ts.map +0 -1
  42. package/src/__tests__/debug-log.ts +0 -35
  43. package/src/__tests__/wall5-parse.test.ts +0 -67
  44. package/src/builders.ts +0 -3190
  45. package/src/cst.ts +0 -25
  46. package/src/functional-parser.ts +0 -162
  47. package/src/grammar.ts +0 -870
  48. package/src/index.ts +0 -19
  49. package/src/jess.ts +0 -6
  50. package/src/lessParser.ts +0 -120
  51. package/src/lessRecursiveParser.ts +0 -279
  52. package/src/lessTokens.ts +0 -350
  53. package/src/productions/guards.ts +0 -1066
  54. package/src/productions/index.ts +0 -29
  55. package/src/productions/root.ts +0 -1613
  56. package/src/productions/selectors.ts +0 -1309
  57. package/src/productions/values.ts +0 -1449
  58. package/src/utils.ts +0 -178
package/src/index.ts DELETED
@@ -1,19 +0,0 @@
1
- export * from './lessTokens.js';
2
-
3
- // The legacy Chevrotain parser (lessRecursiveParser / lessParser / productions) is
4
- // no longer exported — the functional macro parser (LessParser / lessGrammar below)
5
- // IS the parser now. Dropping these re-exports lets the bundler tree-shake ~130 KB
6
- // of the old parser out of the shipped library. (The source files can be deleted
7
- // as a follow-up.)
8
-
9
- export { LessGrammar } from './builders.js';
10
-
11
- import { LessParser } from './functional-parser.js';
12
- export { LessParser };
13
- export { lessGrammar } from './grammar.js';
14
- export { parseLessFn, type LessFnParseResult } from './functional-parser.js';
15
- export { parseLessCst, parseLessDoc } from './cst.js';
16
- export type {
17
- LessCstChild, LessCstError, LessCstLeaf, LessCstNode, LessCstParseResult, LessCstType
18
- } from './cst.js';
19
- export const Parser = LessParser;
package/src/jess.ts DELETED
@@ -1,6 +0,0 @@
1
- export * from './lessRecursiveParser.js';
2
- export type { LessRules, SyntacticContentAssistSuggestion } from './lessParser.js';
3
- export { LessParser as LessParserChevrotain } from './lessParser.js';
4
- export { LessGrammar } from './builders.js';
5
- export { LessParser, parseLessFn, type LessFnParseResult } from './functional-parser.js';
6
- export { LessParser as Parser } from './functional-parser.js';
package/src/lessParser.ts DELETED
@@ -1,120 +0,0 @@
1
- import { Lexer, type IToken, type IRecognitionException } from 'chevrotain';
2
- import { lessTokens, lessFragments } from './lessTokens.js';
3
- import { createLexerDefinition } from '@jesscss/css-parser';
4
- import { LessRecursiveParser, type LessParserConfig, type TokenMap } from './lessRecursiveParser.js';
5
- import { nil, type Node, type Rules, type IParseResult, type TreeContext } from '@jesscss/core';
6
-
7
- export type LessRules = keyof {
8
- [K in keyof LessRecursiveParser as LessRecursiveParser[K] extends (...args: any[]) => Node ? K : never]: true;
9
- };
10
-
11
- export type SyntacticContentAssistSuggestion = {
12
- nextTokenType: string;
13
- nextTokenLabel?: string;
14
- ruleStack: string[];
15
- };
16
-
17
- /**
18
- * Cached lexer + parser singletons. Chevrotain initialization (~500ms)
19
- * only happens once — config changes are applied via instance properties
20
- * before each parse.
21
- */
22
- let cachedLexer: Lexer | undefined;
23
- let cachedParser: LessRecursiveParser | undefined;
24
- let cachedTokenMap: TokenMap | undefined;
25
-
26
- function getSharedLexerAndParser(config: LessParserConfig): { lexer: Lexer; parser: LessRecursiveParser } {
27
- if (!cachedLexer || !cachedParser) {
28
- const { lexer, T } = createLexerDefinition(
29
- lessFragments(),
30
- lessTokens()
31
- );
32
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
33
- cachedTokenMap = T as TokenMap;
34
- cachedLexer = new Lexer(lexer, {
35
- ensureOptimizations: true,
36
- skipValidations: process.env.TEST !== 'true'
37
- });
38
- cachedParser = new LessRecursiveParser(cachedTokenMap, config);
39
- }
40
- // Apply per-parse config to the cached parser instance
41
- const {
42
- looseMode = true,
43
- leakyScope = true,
44
- mathMode = 'parens-division',
45
- wrapOuterExpressions = true,
46
- legacyMode = looseMode
47
- } = config;
48
- cachedParser.looseMode = looseMode;
49
- cachedParser.leakyScope = leakyScope;
50
- cachedParser.mathMode = mathMode;
51
- cachedParser.wrapOuterExpressions = wrapOuterExpressions;
52
- cachedParser.legacyMode = legacyMode;
53
-
54
- return { lexer: cachedLexer, parser: cachedParser };
55
- }
56
-
57
- /**
58
- * @deprecated LEGACY — Chevrotain-lexer + recursive-descent Less parser. Superseded
59
- * by the functional macro grammar (`LessParser` in ./grammar.ts). Kept only for
60
- * benchmarking / migration reference; TO BE DELETED once the functional parser
61
- * fully lands.
62
- */
63
- export class LessParser {
64
- lexer: Lexer;
65
- parser: LessRecursiveParser;
66
-
67
- constructor(
68
- config: LessParserConfig = {}
69
- ) {
70
- config = {
71
- looseMode: true,
72
- ...config
73
- };
74
- const shared = getSharedLexerAndParser(config);
75
- this.lexer = shared.lexer;
76
- this.parser = shared.parser;
77
- this.parse = this.parse.bind(this);
78
- }
79
-
80
- parse(text: string): IParseResult<Rules>;
81
- parse(text: string, rule: 'stylesheet'): IParseResult<Rules>;
82
- parse(text: string, rule: 'stylesheet', options: { context?: TreeContext }): IParseResult<Rules>;
83
- parse(text: string, rule?: LessRules, options?: { context?: TreeContext }): IParseResult;
84
- parse(text: string, rule: LessRules = 'stylesheet', options?: { context?: TreeContext }): IParseResult {
85
- const parser = this.parser;
86
- const lexerResult = this.lexer.tokenize(text);
87
- parser.warnings = [];
88
- if (options?.context) {
89
- parser.context = options.context;
90
- }
91
- parser.context.opts.trivia = undefined;
92
- parser.input = lexerResult.tokens;
93
- const ruleMethod = parser[rule];
94
- if (typeof ruleMethod !== 'function') {
95
- throw new Error(`Unknown parser rule: ${rule}`);
96
- }
97
- const tree = ruleMethod.call(parser);
98
- const trivia = (parser as LessRecursiveParser & { trivia: IParseResult['trivia'] }).trivia;
99
- parser.context.opts.trivia = trivia;
100
- const resultTree = tree ?? nil();
101
- (resultTree.sourceRoot?._treeContext ?? parser.context).opts.trivia = trivia;
102
-
103
- const warnings = [...parser.warnings];
104
-
105
- return {
106
- tree: resultTree,
107
- lexerResult,
108
- errors: parser.errors,
109
- trivia,
110
- warnings
111
- };
112
- }
113
-
114
- /**
115
- * @todo Implement content assist for the new parser
116
- */
117
- suggest(text: string, init: { offset: number; rule?: LessRules }): SyntacticContentAssistSuggestion[] {
118
- return [];
119
- }
120
- }
@@ -1,279 +0,0 @@
1
- import {
2
- type IToken,
3
- type TokenType,
4
- tokenMatcher
5
- } from 'chevrotain';
6
-
7
- import {
8
- CssRecursiveParser,
9
- type CssRecursiveParserConfig,
10
- type RuleContext as CssRuleContext,
11
- type CssTokenType,
12
- productions as cssProductions
13
- } from '@jesscss/css-parser/jess';
14
-
15
- import {
16
- Reference,
17
- DefaultGuard,
18
- Interpolated,
19
- INTERPOLATION_PLACEHOLDER,
20
- Any,
21
- Bool,
22
- type MathMode,
23
- type Node,
24
- type Extend,
25
- type ComplexSelector,
26
- type Selector
27
- } from '@jesscss/core';
28
- import { createInterpolatedReference, getInterpolatedOrString } from './utils.js';
29
-
30
- import { type LessExtraTokenType } from './lessTokens.js';
31
-
32
- import * as productions from './productions/index.js';
33
-
34
- export type LessParserConfig = CssRecursiveParserConfig & {
35
- /**
36
- * Is less strict with certain CSS rules and Less syntax
37
- * that the old Less parser allowed.
38
- *
39
- * @note This will also enable CSS legacyMode unless
40
- * legacyMode is explicitly false.
41
- */
42
- looseMode?: boolean;
43
-
44
- /**
45
- * Controls whether mixins and detached rulesets "leak" their inner rules.
46
- * When true (default):
47
- * - Mixins: Mixin and VarDeclaration nodes are 'public' and 'optional' respectively
48
- * - Detached rulesets: Mixin and VarDeclaration nodes are 'public' and 'private' respectively
49
- * When false:
50
- * - Both mixins and detached rulesets: Mixin and VarDeclaration nodes are 'private'
51
- */
52
- leakyScope?: boolean;
53
-
54
- /**
55
- * Less math evaluation mode. Used during parsing to decide whether a given
56
- * `Operation` should be represented as an `Expression` for Less→Jess conversion.
57
- *
58
- * Mirrors runtime behavior in `Context.shouldOperate()`.
59
- *
60
- * @default 'parens-division'
61
- */
62
- mathMode?: MathMode;
63
-
64
- /**
65
- * When enabled (default), the parser will wrap the *outermost* Less math/value
66
- * expressions (math operations, variable references, and chained mixin/variable
67
- * calls) in an `Expression({ parens: true })`.
68
- *
69
- * This is purely a parse-time AST shape choice to support Less→Jess conversion.
70
- *
71
- * @default true
72
- */
73
- wrapOuterExpressions?: boolean;
74
- };
75
-
76
- // Concrete TokenMap: union of CSS and Less token names
77
- export type CombinedTokenMap = Record<CssTokenType, TokenType> & Record<LessExtraTokenType, TokenType>;
78
- export type TokenMap = CombinedTokenMap;
79
-
80
- export interface ExtendTarget {
81
- selector?: Selector;
82
- target: Selector;
83
- flag: IToken | undefined;
84
- }
85
-
86
- export type RuleContext = CssRuleContext & {
87
- selector?: Selector;
88
- hasDefault?: boolean;
89
- allExtended?: boolean;
90
- isDefinition?: boolean;
91
- allowAnonymousMixins?: boolean;
92
- requireAccessorsAfterMixinCall?: boolean;
93
- inValueList?: boolean;
94
- allowComma?: boolean;
95
- node?: Node;
96
- ruleIsFinished?: boolean;
97
- sequences?: Array<ComplexSelector | Extend>;
98
- asReference?: boolean;
99
- extendTargets?: ExtendTarget[];
100
- extendNodes?: Extend[];
101
- inExtend?: boolean;
102
- wrapInExpression?: boolean;
103
- parenFrames?: boolean[];
104
- calcFrames?: number;
105
- detachedRulesetUsage?: 'function-arg' | 'mixin-arg' | 'default-param';
106
- inFunctionArgs?: boolean;
107
- allowMixinCallWithoutAccessor?: boolean;
108
- startValue?: Node;
109
- atRulePreludeBareVariableAs?: 'index';
110
- };
111
-
112
- /**
113
- * @deprecated LEGACY — Chevrotain-based Less parser engine (extends the deprecated
114
- * CssRecursiveParser). Superseded by the functional macro grammar in ./grammar.ts.
115
- * Kept only for benchmarking; TO BE DELETED once the functional parser fully lands.
116
- */
117
- export class LessRecursiveParser extends CssRecursiveParser {
118
- declare T: TokenMap;
119
- looseMode: boolean;
120
- leakyScope: boolean;
121
- /** Warnings collected during parsing */
122
- warnings: Array<{ message: string; token?: IToken; deprecation?: string }> = [];
123
-
124
- /** See `LessParserConfig.mathMode` */
125
- mathMode: MathMode;
126
- /** See `LessParserConfig.wrapOuterExpressions` */
127
- wrapOuterExpressions: boolean;
128
-
129
- private processLegacyMSFilterToken(token: IToken): Node {
130
- const location = this.getLocationInfo(token);
131
- const source = token.image.replace(/\s*=\s*/g, '=');
132
- const matches = [...source.matchAll(/@([_a-zA-Z\xA0-\uFFFF][-_a-zA-Z0-9\xA0-\uFFFF]*)/g)];
133
-
134
- if (matches.length === 0) {
135
- return new Any(source, { type: token.tokenType?.name }, location);
136
- }
137
-
138
- const templatedSource = source.replace(
139
- /@([_a-zA-Z\xA0-\uFFFF][-_a-zA-Z0-9\xA0-\uFFFF]*)/g,
140
- (_full, _name, offset: number, fullSource: string) => {
141
- const prefix = fullSource.slice(0, offset);
142
- const key = prefix.match(/([A-Za-z]+)=$/)?.[1];
143
- if (key && /colorstr$/i.test(key)) {
144
- return `"${INTERPOLATION_PLACEHOLDER}"`;
145
- }
146
- return INTERPOLATION_PLACEHOLDER;
147
- }
148
- );
149
-
150
- return new Interpolated(
151
- {
152
- source: templatedSource,
153
- replacements: matches.map(match =>
154
- createInterpolatedReference('@', match[1]!, location, this.context)
155
- )
156
- },
157
- { role: 'any' },
158
- location
159
- );
160
- }
161
-
162
- constructor(
163
- T: TokenMap,
164
- config: LessParserConfig = {}
165
- ) {
166
- let {
167
- legacyMode,
168
- looseMode = true,
169
- leakyScope = true,
170
- mathMode = 'parens-division',
171
- wrapOuterExpressions = true,
172
- ...rest
173
- } = config;
174
- legacyMode = legacyMode ?? looseMode;
175
- super(T, { legacyMode, ...rest });
176
-
177
- this.T = T;
178
- this.looseMode = looseMode;
179
- this.leakyScope = leakyScope;
180
- this.mathMode = mathMode;
181
- this.wrapOuterExpressions = wrapOuterExpressions;
182
- this.warnings = [];
183
-
184
- for (const [key, factory] of Object.entries(productions)) {
185
- if (typeof factory !== 'function') {
186
- continue;
187
- }
188
- const rule = (factory as Function).call(this, this.T);
189
- if (key in cssProductions) {
190
- this.OVERRIDE_RULE(key, rule);
191
- } else {
192
- this.RULE(key, rule);
193
- }
194
- }
195
-
196
- if (this.constructor === LessRecursiveParser) {
197
- this.performSelfAnalysis();
198
- }
199
- }
200
-
201
- protected override processValueToken(token: IToken, ctx?: RuleContext): Node {
202
- let tokenType = token.tokenType;
203
- const T = this.T;
204
-
205
- if (tokenType.name === 'AtKeyword' || tokenMatcher(token, T.AtKeyword)) {
206
- if (ctx?.inCustomPropertyValue) {
207
- const atName = token.image;
208
- const ident = token.image.slice(1);
209
- this.warnDeprecation(
210
- `"${atName}" in custom property values is treated as literal text, not a variable reference. Use "\@{${ident}}" if you want it to be evaluated.`,
211
- token,
212
- 'variable-in-unknown-value'
213
- );
214
- return new Any(token.image, { role: 'any' }, this.getLocationInfo(token));
215
- }
216
- if (ctx?.atRulePreludeBareVariableAs === 'index') {
217
- const nextToken = this.LA(1).tokenType;
218
- const hasExplicitAccessorOrCall = this.noSep()
219
- && (nextToken === T.LSquare || nextToken === T.LParen);
220
- if (hasExplicitAccessorOrCall) {
221
- return new Reference(token.image.slice(1), { type: 'variable' }, this.getLocationInfo(token));
222
- }
223
- const atName = token.image;
224
- const ident = token.image.slice(1);
225
- this.warnDeprecation(
226
- `"${atName}" in at-rule preludes is deprecated. Use "@{${ident}}" in Less; outside declaration values this is normalized to indexed lookup syntax.`,
227
- token,
228
- 'at-rule-prelude-variable'
229
- );
230
- return new Reference(
231
- { key: ident },
232
- { type: 'index', role: 'ident' },
233
- this.getLocationInfo(token)
234
- );
235
- }
236
- return new Reference(token.image.slice(1), { type: 'variable' }, this.getLocationInfo(token));
237
- } else if (tokenType.name === 'PropertyReference') {
238
- if (ctx?.inCustomPropertyValue) {
239
- const atName = token.image;
240
- const ident = token.image.slice(1);
241
- this.warnDeprecation(
242
- `"${atName}" in custom property values is treated as literal text, not a property reference. Use "\${${ident}}" if you want it to be evaluated.`,
243
- token,
244
- 'property-in-unknown-value'
245
- );
246
- return new Any(token.image, { role: 'any' }, this.getLocationInfo(token));
247
- }
248
- return super.processValueToken(token, ctx);
249
- } else if (tokenType === T['DefaultGuardFunc']) {
250
- return new DefaultGuard(token.image, undefined, this.getLocationInfo(token));
251
- } else if (
252
- tokenType.name === 'JavaScript'
253
- || (T['JavaScript'] && tokenMatcher(token, T['JavaScript']))
254
- ) {
255
- throw new Error(
256
- 'Inline JavaScript using backticks is not supported. Use @use / @-use to import a script module instead. Script-module documentation is coming soon.'
257
- );
258
- } else if (tokenType === T['InterpolatedIdent']) {
259
- const result = getInterpolatedOrString(token.image, this.getLocationInfo(token), this.context);
260
- if (result instanceof Interpolated) {
261
- return result;
262
- } else {
263
- return new Any(result, { role: 'ident' }, this.getLocationInfo(token));
264
- }
265
- } else if (tokenType === T['LegacyMSFilter']) {
266
- return this.processLegacyMSFilterToken(token);
267
- } else if (tokenType === T['PlainIdent']) {
268
- const image = token.image;
269
- if (image === 'true' || image === 'false') {
270
- return new Bool(image === 'true', undefined, this.getLocationInfo(token));
271
- }
272
- }
273
- return super.processValueToken(token, ctx);
274
- }
275
-
276
- warnDeprecation(message: string, token?: IToken, deprecationId?: string): void {
277
- this.warnings.push({ message, token, deprecation: deprecationId });
278
- }
279
- }