@jesscss/less-parser 2.0.0-alpha.6 → 2.0.0-alpha.7

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 (62) hide show
  1. package/README.md +133 -5
  2. package/lib/builders.d.ts +381 -0
  3. package/lib/builders.d.ts.map +1 -0
  4. package/lib/cst.cjs +14 -0
  5. package/lib/cst.d.ts +6 -0
  6. package/lib/cst.d.ts.map +1 -0
  7. package/lib/cst.js +12 -0
  8. package/lib/functional-parser.cjs +2653 -0
  9. package/lib/functional-parser.d.ts +18 -0
  10. package/lib/functional-parser.d.ts.map +1 -0
  11. package/lib/functional-parser.js +2588 -0
  12. package/lib/grammar.cjs +30621 -0
  13. package/lib/grammar.d.ts +2 -0
  14. package/lib/grammar.d.ts.map +1 -0
  15. package/lib/grammar.js +30620 -0
  16. package/lib/index.cjs +17 -4096
  17. package/lib/index.d.ts +9 -149
  18. package/lib/index.d.ts.map +1 -1
  19. package/lib/index.js +6 -4091
  20. package/lib/jess.cjs +3968 -0
  21. package/lib/jess.d.ts +7 -0
  22. package/lib/jess.d.ts.map +1 -0
  23. package/lib/jess.js +3962 -0
  24. package/lib/lessParser.d.ts +38 -0
  25. package/lib/lessParser.d.ts.map +1 -0
  26. package/lib/lessRecursiveParser.d.ts +99 -0
  27. package/lib/lessRecursiveParser.d.ts.map +1 -0
  28. package/lib/lessTokens.d.ts +23 -0
  29. package/lib/lessTokens.d.ts.map +1 -0
  30. package/lib/productions/guards.d.ts +113 -0
  31. package/lib/productions/guards.d.ts.map +1 -0
  32. package/lib/productions/index.d.ts +5 -0
  33. package/lib/productions/index.d.ts.map +1 -0
  34. package/lib/productions/root.d.ts +38 -0
  35. package/lib/productions/root.d.ts.map +1 -0
  36. package/lib/productions/selectors.d.ts +41 -0
  37. package/lib/productions/selectors.d.ts.map +1 -0
  38. package/lib/productions/values.d.ts +35 -0
  39. package/lib/productions/values.d.ts.map +1 -0
  40. package/lib/utils.d.ts +9 -0
  41. package/lib/utils.d.ts.map +1 -0
  42. package/package.json +41 -10
  43. package/src/__tests__/debug-log.ts +35 -0
  44. package/src/__tests__/wall5-parse.test.ts +67 -0
  45. package/src/builders.ts +3183 -0
  46. package/src/cst.ts +25 -0
  47. package/src/functional-parser.ts +162 -0
  48. package/src/grammar.ts +869 -0
  49. package/src/index.ts +19 -0
  50. package/src/jess.ts +6 -0
  51. package/src/lessParser.ts +120 -0
  52. package/src/lessRecursiveParser.ts +279 -0
  53. package/src/lessTokens.ts +350 -0
  54. package/src/productions/guards.ts +1066 -0
  55. package/src/productions/index.ts +29 -0
  56. package/src/productions/root.ts +1613 -0
  57. package/src/productions/selectors.ts +1309 -0
  58. package/src/productions/values.ts +1449 -0
  59. package/src/utils.ts +178 -0
  60. package/lib/index.d.cts +0 -150
  61. package/lib/index.d.cts.map +0 -1
  62. package/lib/index.js.map +0 -1
@@ -0,0 +1,350 @@
1
+ import {
2
+ rawCssFragments,
3
+ rawCssTokens,
4
+ LexerType,
5
+ groupCapture,
6
+ type RawModeConfig,
7
+ type RawTokenConfig,
8
+ type RawToken,
9
+ type TokenNames,
10
+ type CssTokenType,
11
+ SKIPPED_LABEL
12
+ } from '@jesscss/css-parser';
13
+
14
+ const AMPERSAND_TEMPLATE_CONTENTS_REGEX = /(?:[.#-]|\d)(?:[.#\w\u0080-\uffff-]|&)*|(?:[.#\w\u0080-\uffff-]|&)*&(?:[.#\w\u0080-\uffff-]|&)*/;
15
+
16
+ type IMerges = Partial<Record<CssTokenType, RawTokenConfig>>;
17
+
18
+ /**
19
+ * Less-specific token names introduced by merges below
20
+ *
21
+ * @todo - Can't we infer this from the tokens?
22
+ */
23
+ export type LessExtraTokenType =
24
+ | 'Ellipsis'
25
+ | 'AtKeywordLessExtension'
26
+ | 'Interpolated'
27
+ | 'LineComment'
28
+ | 'PlusAssign'
29
+ | 'UnderscoreAssign'
30
+ | 'AnonMixinStart'
31
+ | 'GtEqAlias'
32
+ | 'LtEqAlias'
33
+ | 'Extend'
34
+ | 'AmpersandLParen'
35
+ | 'AmpersandTemplateContents'
36
+ | 'AmpersandTemplateEnd'
37
+ | 'AllFlag'
38
+ | 'When'
39
+ | 'WhenFunctionStart'
40
+ | 'VarOrProp'
41
+ | 'NestedReference'
42
+ | 'PropertyReference'
43
+ | 'Percent'
44
+ | 'FormatFunction'
45
+ | 'IfFunction'
46
+ | 'BooleanFunction'
47
+ | 'DefaultGuardIdent'
48
+ | 'DefaultGuardFunc'
49
+ | 'JavaScript'
50
+ | 'InterpolatedIdent'
51
+ | 'InterpolatedCustomProperty'
52
+ | 'InterpolatedSelector';
53
+
54
+ function $preBuildFragments() {
55
+ const fragments: string[][] = rawCssFragments().map(f => [...f]);
56
+ fragments.unshift(['lineComment', '\\/\\/[^\\n\\r]*']);
57
+ fragments.push(['interpolated', '[@$]\\{(?:{{nmchar}}*)\\}']);
58
+
59
+ return fragments;
60
+ }
61
+
62
+ function $preBuildTokens() {
63
+ /**
64
+ * Creates a type from the CSS mode and adds Less tokens
65
+ */
66
+ type Modes<T> = any;
67
+
68
+ type InferMergeTypes = {
69
+ modes: any;
70
+ defaultMode: 'Default';
71
+ };
72
+
73
+ const tokens = rawCssTokens() as unknown as InferMergeTypes;
74
+
75
+ /**
76
+ * Keyed by what to insert after
77
+ *
78
+ * @todo - Move merge utility to css-parser
79
+ */
80
+ const merges = {
81
+ Assign: [
82
+ {
83
+ name: 'Ellipsis',
84
+ pattern: /\.\.\./,
85
+ categories: ['BlockMarker']
86
+ },
87
+ /**
88
+ * Less's historical parser unfortunately allows
89
+ * at-keywords that are not valid in CSS. One is
90
+ * that Less allows at-keywords to begin with numbers.
91
+ * Another is that it allows an at-rule that only
92
+ * contains a single dash. So we capture this as
93
+ * a separate token.
94
+ *
95
+ * We also do this later in the token stack so that we
96
+ * don't accidentally grab something like
97
+ * @-webkit-keyframes while looking for @-.
98
+ */
99
+ {
100
+ name: 'AtKeywordLessExtension',
101
+ pattern: '@(?:-|\\d(?:{{nmchar}})*)',
102
+ categories: ['BlockMarker', 'AtName']
103
+ }
104
+ ],
105
+ PlainIdent: [
106
+ { name: 'Interpolated', pattern: LexerType.NA },
107
+ {
108
+ name: 'LineComment',
109
+ pattern: '{{lineComment}}',
110
+ label: SKIPPED_LABEL
111
+ },
112
+ { name: 'PlusAssign', pattern: '\\+{{whitespace}}*:', categories: ['BlockMarker', 'Assign'] },
113
+ {
114
+ name: 'UnderscoreAssign',
115
+ pattern: '\\+{{whitespace}}*_{{whitespace}}*:',
116
+ categories: ['BlockMarker', 'Assign']
117
+ },
118
+ { name: 'AnonMixinStart', pattern: /[.#]\(/, categories: ['BlockMarker'] },
119
+ { name: 'GtEqAlias', pattern: /=>/, categories: ['CompareOperator'] },
120
+ { name: 'LtEqAlias', pattern: /=</, categories: ['CompareOperator'] },
121
+ {
122
+ name: 'Extend',
123
+ pattern: /:extend\(/,
124
+ categories: ['BlockMarker']
125
+ },
126
+ {
127
+ name: 'VarOrProp',
128
+ pattern: LexerType.NA
129
+ },
130
+ {
131
+ name: 'NestedReference',
132
+ pattern: ['([@$]+{{ident}}?){2,}', groupCapture],
133
+ start_chars_hint: ['@', '$'],
134
+ categories: ['VarOrProp'],
135
+ line_breaks: true
136
+ },
137
+ {
138
+ name: 'PropertyReference',
139
+ pattern: '\\${{ident}}',
140
+ categories: ['VarOrProp']
141
+ },
142
+ /** Can be used in unit function or mod operation */
143
+ {
144
+ name: 'Percent',
145
+ pattern: /%/
146
+ },
147
+ {
148
+ name: 'DefaultGuardIdent',
149
+ pattern: /default/,
150
+ longer_alt: 'PlainIdent',
151
+ categories: ['Ident']
152
+ },
153
+ {
154
+ name: 'DefaultGuardFunc',
155
+ pattern: /default(?:\(\))/
156
+ }
157
+ ],
158
+ Ampersand: [
159
+ {
160
+ name: 'AmpersandLParen',
161
+ pattern: /&\(/,
162
+ push_mode: 'AmpersandTemplate',
163
+ categories: ['Selector', 'NestedRuleStart', 'BlockMarker']
164
+ },
165
+ {
166
+ name: 'AllFlag',
167
+ pattern: /!all/,
168
+ categories: ['BlockMarker']
169
+ }
170
+ ],
171
+ UrlStart: [
172
+ /**
173
+ * Keywords that we don't identify as idents
174
+ * should be manually added to other places where an ident is valid.
175
+ */
176
+ {
177
+ name: 'When',
178
+ pattern: /when/i,
179
+ longer_alt: 'PlainIdent',
180
+ categories: ['BlockMarker']
181
+ },
182
+ {
183
+ name: 'FormatFunction',
184
+ pattern: /%\(/,
185
+ categories: ['BlockMarker', 'FunctionStart']
186
+ },
187
+ {
188
+ name: 'IfFunction',
189
+ pattern: /if\(/,
190
+ categories: ['BlockMarker', 'FunctionStart']
191
+ },
192
+ {
193
+ name: 'BooleanFunction',
194
+ pattern: /boolean\(/,
195
+ categories: ['BlockMarker', 'FunctionStart']
196
+ },
197
+ {
198
+ name: 'JavaScript',
199
+ pattern: /~?`[^`]*`/,
200
+ line_breaks: true
201
+ }
202
+ ],
203
+ /**
204
+ * These need to be after any keywords, so that
205
+ * keywords with a longer_alt of `PlainIdent`
206
+ * aren't captured first.
207
+ */
208
+ Signed: [
209
+ {
210
+ name: 'InterpolatedIdent',
211
+ /**
212
+ * Must contain one `@{}`
213
+ * It's too expensive for Chevrotain to capture groups here,
214
+ * so we'll extract the interpolated values later.
215
+ */
216
+ pattern: '(?:{{ident}}|-)?{{interpolated}}(?:{{interpolated}}|{{nmchar}})*',
217
+ categories: ['Interpolated', 'Selector', 'Ident']
218
+ },
219
+ {
220
+ name: 'InterpolatedCustomProperty',
221
+ /**
222
+ * Must contain one `@{}`
223
+ * It's too expensive for Chevrotain to capture groups here,
224
+ * so we'll extract the interpolated values later.
225
+ */
226
+ pattern: '--{{ident}}?{{interpolated}}(?:{{interpolated}}|{{nmchar}})*',
227
+ categories: ['Interpolated']
228
+ },
229
+ /**
230
+ * Unfortunately, there's grammatical ambiguity between
231
+ * interpolated props and a naked interpolated selector name,
232
+ * making this awkward token necessary.
233
+ */
234
+ {
235
+ name: 'InterpolatedSelector',
236
+ pattern: ['[.#]{{ident}}?{{interpolated}}(?:{{interpolated}}|{{nmchar}})*', groupCapture],
237
+ categories: ['Interpolated', 'Selector'],
238
+ start_chars_hint: ['.', '#'],
239
+ line_breaks: true
240
+ }
241
+ ]
242
+ } as const satisfies IMerges;
243
+
244
+ let defaultTokens = (tokens.modes.Default as unknown as Readonly<RawToken[]>).slice() as RawToken[];
245
+
246
+ let tokenLength = defaultTokens.length;
247
+ for (let i = 0; i < tokenLength; i++) {
248
+ let token: RawToken = defaultTokens[i]!;
249
+
250
+ const { name } = token;
251
+ const copyToken = () => {
252
+ token = structuredClone(token) as RawToken;
253
+ };
254
+
255
+ let alterations = true;
256
+
257
+ switch (name) {
258
+ /**
259
+ * Less / Sass Ampersand is slightly different
260
+ * from CSS Nesting in that it concatenates, so we
261
+ * need to gobble up the rest of the identifier
262
+ * if present.
263
+ */
264
+ case 'Ampersand':
265
+ copyToken();
266
+ /**
267
+ * Captures not just ampersands, but "ampersand merges", where
268
+ * the intent of the author was to merge the parent selector with a token
269
+ * suffix or prefix.
270
+ *
271
+ * e.g.
272
+ * 1. &-foo
273
+ * 2. &(foo)
274
+ * 3. &1
275
+ * 4. .foo-&
276
+ */
277
+ token.pattern = '(?:[.#](?:{{ident}}-)?&|&){{nmchar}}*';
278
+ token.start_chars_hint = ['&', '.', '#'];
279
+ break;
280
+ case 'DotName':
281
+ case 'HashName':
282
+ copyToken();
283
+ token.longer_alt = 'Ampersand';
284
+ break;
285
+ case 'Divide':
286
+ copyToken();
287
+ token.pattern = /\.?\//;
288
+ break;
289
+ case 'SingleQuoteStart':
290
+ copyToken();
291
+ token.pattern = /~?'/;
292
+ break;
293
+ case 'DoubleQuoteStart':
294
+ copyToken();
295
+ token.pattern = /~?"/;
296
+ break;
297
+ default:
298
+ alterations = false;
299
+ }
300
+ if (alterations) {
301
+ defaultTokens[i] = token;
302
+ }
303
+ // @ts-expect-error - Suppress index warning
304
+ const merge = merges[name];
305
+ if (merge) {
306
+ /** Insert after current token */
307
+ defaultTokens = defaultTokens.slice(0, i + 1).concat(merge, defaultTokens.slice(i + 1));
308
+ (tokens.modes.Default as unknown as RawToken[]) = defaultTokens;
309
+ const mergeLength = merge.length;
310
+ tokenLength += mergeLength;
311
+ i += mergeLength;
312
+ }
313
+ }
314
+ tokens.modes.AmpersandTemplate = [
315
+ {
316
+ name: 'AmpersandTemplateEnd',
317
+ pattern: /\)/,
318
+ pop_mode: true,
319
+ categories: ['FunctionLikeEnd']
320
+ },
321
+ {
322
+ name: 'AmpersandTemplateContents',
323
+ /**
324
+ * Template-ish raw text until `)` or a quote. Quotes are tokenized
325
+ * separately so `&('')` can be handled like `url(...)`.
326
+ *
327
+ * We intentionally do not accept arbitrary plain identifiers here:
328
+ * Less templates are selector-ish fragments or explicit `&` insertion
329
+ * patterns, not free-form identifiers like `nil`.
330
+ */
331
+ pattern: AMPERSAND_TEMPLATE_CONTENTS_REGEX,
332
+ categories: ['Selector']
333
+ },
334
+ 'SingleQuoteStart',
335
+ 'DoubleQuoteStart',
336
+ 'WS'
337
+ ] as Array<RawTokenConfig | string>;
338
+ return tokens;
339
+ }
340
+
341
+ export const Fragments = $preBuildFragments!();
342
+ export const Tokens = $preBuildTokens!();
343
+
344
+ type ReturnTokens = ReturnType<typeof $preBuildTokens>;
345
+ type TokenModes = ReturnTokens['modes'];
346
+
347
+ export type LessTokenType = TokenNames<TokenModes[keyof TokenModes]>;
348
+
349
+ export const lessFragments = () => Fragments as unknown as ReadonlyArray<Readonly<[string, string]>>;
350
+ export const lessTokens = () => Tokens as unknown as RawModeConfig;