ripple 0.3.13 → 0.3.14

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 (66) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/package.json +5 -30
  3. package/src/runtime/array.js +38 -38
  4. package/src/runtime/create-subscriber.js +2 -2
  5. package/src/runtime/internal/client/bindings.js +4 -6
  6. package/src/runtime/internal/client/events.js +8 -3
  7. package/src/runtime/internal/client/hmr.js +5 -17
  8. package/src/runtime/internal/client/runtime.js +1 -0
  9. package/src/runtime/internal/server/blocks.js +7 -9
  10. package/src/runtime/internal/server/index.js +14 -22
  11. package/src/runtime/media-query.js +34 -33
  12. package/src/runtime/object.js +7 -10
  13. package/src/runtime/proxy.js +2 -3
  14. package/src/runtime/reactive-value.js +23 -21
  15. package/src/utils/ast.js +1 -1
  16. package/src/utils/attributes.js +43 -0
  17. package/src/utils/builders.js +2 -2
  18. package/tests/client/basic/basic.errors.test.rsrx +1 -1
  19. package/tests/client/basic/basic.styling.test.rsrx +1 -1
  20. package/tests/client/compiler/compiler.assignments.test.rsrx +1 -1
  21. package/tests/client/compiler/compiler.attributes.test.rsrx +1 -1
  22. package/tests/client/compiler/compiler.basic.test.rsrx +13 -13
  23. package/tests/client/compiler/compiler.tracked-access.test.rsrx +1 -1
  24. package/tests/client/compiler/compiler.try-in-function.test.rsrx +1 -1
  25. package/tests/client/compiler/compiler.typescript.test.rsrx +1 -1
  26. package/tests/client/css/global-additional-cases.test.rsrx +1 -1
  27. package/tests/client/css/global-advanced-selectors.test.rsrx +1 -1
  28. package/tests/client/css/global-at-rules.test.rsrx +1 -1
  29. package/tests/client/css/global-basic.test.rsrx +1 -1
  30. package/tests/client/css/global-classes-ids.test.rsrx +1 -1
  31. package/tests/client/css/global-combinators.test.rsrx +1 -1
  32. package/tests/client/css/global-complex-nesting.test.rsrx +1 -1
  33. package/tests/client/css/global-edge-cases.test.rsrx +1 -1
  34. package/tests/client/css/global-keyframes.test.rsrx +1 -1
  35. package/tests/client/css/global-nested.test.rsrx +1 -1
  36. package/tests/client/css/global-pseudo.test.rsrx +1 -1
  37. package/tests/client/css/global-scoping.test.rsrx +1 -1
  38. package/tests/client/css/style-identifier.test.rsrx +1 -1
  39. package/tests/client/return.test.rsrx +1 -1
  40. package/tests/hydration/build-components.js +1 -1
  41. package/tests/server/style-identifier.test.rsrx +1 -1
  42. package/tests/setup-server.js +1 -1
  43. package/tests/utils/compiler-compat-config.test.js +1 -1
  44. package/src/compiler/comment-utils.js +0 -91
  45. package/src/compiler/errors.js +0 -77
  46. package/src/compiler/identifier-utils.js +0 -80
  47. package/src/compiler/index.d.ts +0 -127
  48. package/src/compiler/index.js +0 -89
  49. package/src/compiler/phases/1-parse/index.js +0 -3007
  50. package/src/compiler/phases/1-parse/style.js +0 -704
  51. package/src/compiler/phases/2-analyze/css-analyze.js +0 -160
  52. package/src/compiler/phases/2-analyze/index.js +0 -2208
  53. package/src/compiler/phases/2-analyze/prune.js +0 -1131
  54. package/src/compiler/phases/2-analyze/validation.js +0 -168
  55. package/src/compiler/phases/3-transform/client/index.js +0 -5264
  56. package/src/compiler/phases/3-transform/segments.js +0 -2125
  57. package/src/compiler/phases/3-transform/server/index.js +0 -1749
  58. package/src/compiler/phases/3-transform/stylesheet.js +0 -545
  59. package/src/compiler/scope.js +0 -476
  60. package/src/compiler/source-map-utils.js +0 -358
  61. package/src/compiler/types/acorn.d.ts +0 -11
  62. package/src/compiler/types/estree-jsx.d.ts +0 -11
  63. package/src/compiler/types/estree.d.ts +0 -11
  64. package/src/compiler/types/index.d.ts +0 -1411
  65. package/src/compiler/types/parse.d.ts +0 -1723
  66. package/src/compiler/utils.js +0 -1258
@@ -1,704 +0,0 @@
1
- /** @import * as AST from 'estree' */
2
-
3
- import { hash } from '../../utils.js';
4
-
5
- const REGEX_MATCHER = /^[~^$*|]?=/;
6
- const REGEX_ATTRIBUTE_FLAGS = /^[a-zA-Z]+/;
7
- const REGEX_COMMENT_CLOSE = /\*\//;
8
- const REGEX_HTML_COMMENT_CLOSE = /-->/;
9
- const REGEX_PERCENTAGE = /^\d+(\.\d+)?%/;
10
- const REGEX_COMBINATOR = /^(\+|~|>|\|\|)/;
11
- const REGEX_VALID_IDENTIFIER_CHAR = /[a-zA-Z0-9_-]/;
12
- const REGEX_LEADING_HYPHEN_OR_DIGIT = /-?\d/;
13
- const REGEX_WHITESPACE_OR_COLON = /[\s:]/;
14
- const REGEX_NTH_OF =
15
- /^(even|odd|\+?(\d+|\d*n(\s*[+-]\s*\d+)?)|-\d*n(\s*\+\s*\d+))((?=\s*[,)])|\s+of\s+)/;
16
-
17
- const regex_whitespace = /\s/;
18
-
19
- class Parser {
20
- index = 0;
21
-
22
- /**
23
- * @param {string} template
24
- * @param {boolean} loose
25
- */
26
- constructor(template, loose) {
27
- if (typeof template !== 'string') {
28
- throw new TypeError('Template must be a string');
29
- }
30
-
31
- this.loose = loose;
32
- this.template_untrimmed = template;
33
- this.template = template.trimEnd();
34
- }
35
-
36
- /** @param {string} str */
37
- match(str) {
38
- const length = str.length;
39
- if (length === 1) {
40
- // more performant than slicing
41
- return this.template[this.index] === str;
42
- }
43
-
44
- return this.template.slice(this.index, this.index + length) === str;
45
- }
46
-
47
- /**
48
- * @param {string} str
49
- * @param {boolean} required
50
- * @param {boolean} required_in_loose
51
- */
52
- eat(str, required = false, required_in_loose = true) {
53
- if (this.match(str)) {
54
- this.index += str.length;
55
- return true;
56
- }
57
-
58
- if (required && (!this.loose || required_in_loose)) {
59
- throw new Error(`Expected ${str}`);
60
- }
61
-
62
- return false;
63
- }
64
-
65
- /**
66
- * Match a regex at the current index
67
- * @param {RegExp} pattern Should have a ^ anchor at the start so the regex doesn't search past the beginning, resulting in worse performance
68
- */
69
- match_regex(pattern) {
70
- const match = pattern.exec(this.template.slice(this.index));
71
- if (!match || match.index !== 0) return null;
72
-
73
- return match[0];
74
- }
75
-
76
- /**
77
- * Search for a regex starting at the current index and return the result if it matches
78
- * @param {RegExp} pattern Should have a ^ anchor at the start so the regex doesn't search past the beginning, resulting in worse performance
79
- */
80
- read(pattern) {
81
- const result = this.match_regex(pattern);
82
- if (result) this.index += result.length;
83
- return result;
84
- }
85
-
86
- allow_whitespace() {
87
- while (this.index < this.template.length && regex_whitespace.test(this.template[this.index])) {
88
- this.index++;
89
- }
90
- }
91
-
92
- /** @param {RegExp} pattern */
93
- read_until(pattern) {
94
- if (this.index >= this.template.length) {
95
- if (this.loose) return '';
96
- throw new Error('Unexpected end of input');
97
- }
98
-
99
- const start = this.index;
100
- const match = pattern.exec(this.template.slice(start));
101
-
102
- if (match) {
103
- this.index = start + match.index;
104
- return this.template.slice(start, this.index);
105
- }
106
-
107
- this.index = this.template.length;
108
- return this.template.slice(start);
109
- }
110
- }
111
-
112
- /**
113
- * @param {string} content
114
- * @param {{ loose?: boolean }} options
115
- * @returns {AST.CSS.StyleSheet}
116
- */
117
- export function parse_style(content, options) {
118
- const parser = new Parser(content, options.loose || false);
119
-
120
- return {
121
- source: content,
122
- hash: `ripple-${hash(content)}`,
123
- type: 'StyleSheet',
124
- children: read_body(parser),
125
- start: 0,
126
- end: content.length,
127
- };
128
- }
129
-
130
- /** @param {Parser} parser */
131
- function allow_comment_or_whitespace(parser) {
132
- parser.allow_whitespace();
133
- while (parser.match('/*') || parser.match('<!--')) {
134
- if (parser.eat('/*')) {
135
- parser.read_until(REGEX_COMMENT_CLOSE);
136
- parser.eat('*/', true);
137
- }
138
-
139
- if (parser.eat('<!--')) {
140
- parser.read_until(REGEX_HTML_COMMENT_CLOSE);
141
- parser.eat('-->', true);
142
- }
143
-
144
- parser.allow_whitespace();
145
- }
146
- }
147
-
148
- /**
149
- * @param {Parser} parser
150
- * @returns {Array<AST.CSS.Rule | AST.CSS.Atrule>}
151
- */
152
- function read_body(parser) {
153
- /** @type {Array<AST.CSS.Rule | AST.CSS.Atrule>} */
154
- const children = [];
155
-
156
- while (parser.index < parser.template.length) {
157
- allow_comment_or_whitespace(parser);
158
-
159
- if (parser.match('@')) {
160
- children.push(read_at_rule(parser));
161
- } else {
162
- children.push(read_rule(parser));
163
- }
164
- }
165
-
166
- return children;
167
- }
168
-
169
- /**
170
- * @param {Parser} parser
171
- * @returns {AST.CSS.Atrule}
172
- */
173
- function read_at_rule(parser) {
174
- const start = parser.index;
175
- parser.eat('@', true);
176
-
177
- const name = read_identifier(parser);
178
-
179
- const prelude = read_value(parser);
180
-
181
- /** @type {AST.CSS.Block | null} */
182
- let block = null;
183
-
184
- if (parser.match('{')) {
185
- // e.g. `@media (...) {...}`
186
- block = read_block(parser);
187
- } else {
188
- // e.g. `@import '...'`
189
- parser.eat(';', true);
190
- }
191
-
192
- return {
193
- type: 'Atrule',
194
- start,
195
- end: parser.index,
196
- name,
197
- prelude,
198
- block,
199
- };
200
- }
201
-
202
- /**
203
- * @param {Parser} parser
204
- * @returns {AST.CSS.Rule}
205
- */
206
- function read_rule(parser) {
207
- const start = parser.index;
208
-
209
- return {
210
- type: 'Rule',
211
- prelude: read_selector_list(parser),
212
- block: read_block(parser),
213
- start,
214
- end: parser.index,
215
- metadata: {
216
- parent_rule: null,
217
- has_local_selectors: false,
218
- is_global_block: false,
219
- },
220
- };
221
- }
222
-
223
- /**
224
- * @param {Parser} parser
225
- * @returns {AST.CSS.Block}
226
- */
227
- function read_block(parser) {
228
- const start = parser.index;
229
-
230
- parser.eat('{', true);
231
-
232
- /** @type {Array<AST.CSS.Declaration | AST.CSS.Rule | AST.CSS.Atrule>} */
233
- const children = [];
234
-
235
- while (parser.index < parser.template.length) {
236
- allow_comment_or_whitespace(parser);
237
-
238
- if (parser.match('}')) {
239
- break;
240
- } else {
241
- children.push(read_block_item(parser));
242
- }
243
- }
244
-
245
- parser.eat('}', true);
246
-
247
- return {
248
- type: 'Block',
249
- start,
250
- end: parser.index,
251
- children,
252
- };
253
- }
254
-
255
- /**
256
- * Reads a declaration, rule or at-rule
257
- *
258
- * @param {Parser} parser
259
- * @returns {AST.CSS.Declaration | AST.CSS.Rule | AST.CSS.Atrule}
260
- */
261
- function read_block_item(parser) {
262
- if (parser.match('@')) {
263
- return read_at_rule(parser);
264
- }
265
-
266
- // read ahead to understand whether we're dealing with a declaration or a nested rule.
267
- // this involves some duplicated work, but avoids a try-catch that would disguise errors
268
- const start = parser.index;
269
- read_value(parser);
270
- const char = parser.template[parser.index];
271
- parser.index = start;
272
-
273
- return char === '{' ? read_rule(parser) : read_declaration(parser);
274
- }
275
-
276
- /**
277
- * @param {Parser} parser
278
- * @returns {AST.CSS.Declaration}
279
- */
280
- function read_declaration(parser) {
281
- const start = parser.index;
282
-
283
- const property = parser.read_until(REGEX_WHITESPACE_OR_COLON);
284
- parser.allow_whitespace();
285
- parser.eat(':');
286
- let index = parser.index;
287
- parser.allow_whitespace();
288
-
289
- const value = read_value(parser);
290
-
291
- if (!value && !property.startsWith('--') && !parser.loose) {
292
- throw new Error('CSS Declaration cannot be empty');
293
- }
294
-
295
- const end = parser.index;
296
-
297
- if (!parser.match('}')) {
298
- parser.eat(';', true);
299
- }
300
-
301
- return {
302
- type: 'Declaration',
303
- start,
304
- end,
305
- property,
306
- value,
307
- };
308
- }
309
-
310
- /**
311
- * @param {Parser} parser
312
- * @returns {string}
313
- */
314
- function read_value(parser) {
315
- let value = '';
316
- let escaped = false;
317
- let in_url = false;
318
-
319
- /** @type {null | '"' | "'"} */
320
- let quote_mark = null;
321
-
322
- while (parser.index < parser.template.length) {
323
- const char = parser.template[parser.index];
324
-
325
- if (escaped) {
326
- value += '\\' + char;
327
- escaped = false;
328
- } else if (char === '\\') {
329
- escaped = true;
330
- } else if (char === quote_mark) {
331
- quote_mark = null;
332
- } else if (char === ')') {
333
- in_url = false;
334
- } else if (quote_mark === null && (char === '"' || char === "'")) {
335
- quote_mark = char;
336
- } else if (char === '(' && value.slice(-3) === 'url') {
337
- in_url = true;
338
- } else if ((char === ';' || char === '{' || char === '}') && !in_url && !quote_mark) {
339
- return value.trim();
340
- }
341
-
342
- value += char;
343
-
344
- parser.index++;
345
- }
346
-
347
- throw new Error('Unexpected end of input');
348
- }
349
-
350
- /**
351
- * @param {Parser} parser
352
- * @param {boolean} [inside_pseudo_class]
353
- * @returns {AST.CSS.SelectorList}
354
- */
355
- function read_selector_list(parser, inside_pseudo_class = false) {
356
- /** @type {AST.CSS.ComplexSelector[]} */
357
- const children = [];
358
-
359
- allow_comment_or_whitespace(parser);
360
-
361
- const start = parser.index;
362
-
363
- while (parser.index < parser.template.length) {
364
- children.push(read_selector(parser, inside_pseudo_class));
365
-
366
- const end = parser.index;
367
-
368
- allow_comment_or_whitespace(parser);
369
-
370
- if (inside_pseudo_class ? parser.match(')') : parser.match('{')) {
371
- return {
372
- type: 'SelectorList',
373
- start,
374
- end,
375
- children,
376
- };
377
- } else {
378
- parser.eat(',', true);
379
- allow_comment_or_whitespace(parser);
380
- }
381
- }
382
-
383
- throw new Error('Unexpected end of input');
384
- }
385
-
386
- /**
387
- * @param {Parser} parser
388
- * @returns {AST.CSS.Combinator | null}
389
- */
390
- function read_combinator(parser) {
391
- const start = parser.index;
392
- parser.allow_whitespace();
393
-
394
- const index = parser.index;
395
- const name = parser.read(REGEX_COMBINATOR);
396
-
397
- if (name) {
398
- const end = parser.index;
399
- parser.allow_whitespace();
400
-
401
- return {
402
- type: 'Combinator',
403
- name,
404
- start: index,
405
- end,
406
- };
407
- }
408
-
409
- if (parser.index !== start) {
410
- return {
411
- type: 'Combinator',
412
- name: ' ',
413
- start,
414
- end: parser.index,
415
- };
416
- }
417
-
418
- return null;
419
- }
420
-
421
- /**
422
- * @param {Parser} parser
423
- * @param {boolean} [inside_pseudo_class]
424
- * @returns {AST.CSS.ComplexSelector}
425
- */
426
- function read_selector(parser, inside_pseudo_class = false) {
427
- const list_start = parser.index;
428
-
429
- /** @type {AST.CSS.RelativeSelector[]} */
430
- const children = [];
431
-
432
- /**
433
- * @param {AST.CSS.Combinator | null} combinator
434
- * @param {number} start
435
- * @returns {AST.CSS.RelativeSelector}
436
- */
437
- function create_selector(combinator, start) {
438
- return {
439
- type: 'RelativeSelector',
440
- combinator,
441
- selectors: [],
442
- start,
443
- end: -1,
444
- metadata: {
445
- is_global: false,
446
- is_global_like: false,
447
- scoped: false,
448
- },
449
- };
450
- }
451
-
452
- /** @type {AST.CSS.RelativeSelector} */
453
- let relative_selector = create_selector(null, parser.index);
454
-
455
- while (parser.index < parser.template.length) {
456
- let start = parser.index;
457
-
458
- if (parser.eat('&')) {
459
- relative_selector.selectors.push({
460
- type: 'NestingSelector',
461
- name: '&',
462
- start,
463
- end: parser.index,
464
- });
465
- } else if (parser.eat('*')) {
466
- let name = '*';
467
-
468
- if (parser.eat('|')) {
469
- // * is the namespace (which we ignore)
470
- name = read_identifier(parser);
471
- }
472
-
473
- relative_selector.selectors.push({
474
- type: 'TypeSelector',
475
- name,
476
- start,
477
- end: parser.index,
478
- });
479
- } else if (parser.eat('#')) {
480
- relative_selector.selectors.push({
481
- type: 'IdSelector',
482
- name: read_identifier(parser),
483
- start,
484
- end: parser.index,
485
- });
486
- } else if (parser.eat('.')) {
487
- relative_selector.selectors.push({
488
- type: 'ClassSelector',
489
- name: read_identifier(parser),
490
- start,
491
- end: parser.index,
492
- });
493
- } else if (parser.eat('::')) {
494
- relative_selector.selectors.push({
495
- type: 'PseudoElementSelector',
496
- name: read_identifier(parser),
497
- start,
498
- end: parser.index,
499
- });
500
- // We read the inner selectors of a pseudo element to ensure it parses correctly,
501
- // but we don't do anything with the result.
502
- if (parser.eat('(')) {
503
- read_selector_list(parser, true);
504
- parser.eat(')', true);
505
- }
506
- } else if (parser.eat(':')) {
507
- const name = read_identifier(parser);
508
-
509
- /** @type {null | AST.CSS.SelectorList} */
510
- let args = null;
511
-
512
- if (parser.eat('(')) {
513
- args = read_selector_list(parser, true);
514
- parser.eat(')', true);
515
- }
516
-
517
- relative_selector.selectors.push({
518
- type: 'PseudoClassSelector',
519
- name,
520
- args,
521
- start,
522
- end: parser.index,
523
- });
524
- } else if (parser.eat('[')) {
525
- parser.allow_whitespace();
526
- const name = read_identifier(parser);
527
- parser.allow_whitespace();
528
-
529
- /** @type {string | null} */
530
- let value = null;
531
-
532
- const matcher = parser.read(REGEX_MATCHER);
533
-
534
- if (matcher) {
535
- parser.allow_whitespace();
536
- value = read_attribute_value(parser);
537
- }
538
-
539
- parser.allow_whitespace();
540
-
541
- const flags = parser.read(REGEX_ATTRIBUTE_FLAGS);
542
-
543
- parser.allow_whitespace();
544
- parser.eat(']', true);
545
-
546
- relative_selector.selectors.push({
547
- type: 'AttributeSelector',
548
- start,
549
- end: parser.index,
550
- name,
551
- matcher,
552
- value,
553
- flags,
554
- });
555
- } else if (inside_pseudo_class && parser.match_regex(REGEX_NTH_OF)) {
556
- // nth of matcher must come before combinator matcher to prevent collision else the '+' in '+2n-1' would be parsed as a combinator
557
-
558
- relative_selector.selectors.push({
559
- type: 'Nth',
560
- value: /**@type {string} */ (parser.read(REGEX_NTH_OF)),
561
- start,
562
- end: parser.index,
563
- });
564
- } else if (parser.match_regex(REGEX_PERCENTAGE)) {
565
- relative_selector.selectors.push({
566
- type: 'Percentage',
567
- value: /** @type {string} */ (parser.read(REGEX_PERCENTAGE)),
568
- start,
569
- end: parser.index,
570
- });
571
- } else if (!parser.match_regex(REGEX_COMBINATOR)) {
572
- let name = read_identifier(parser);
573
-
574
- if (parser.eat('|')) {
575
- // we ignore the namespace when trying to find matching element classes
576
- name = read_identifier(parser);
577
- }
578
-
579
- relative_selector.selectors.push({
580
- type: 'TypeSelector',
581
- name,
582
- start,
583
- end: parser.index,
584
- });
585
- }
586
-
587
- const index = parser.index;
588
- allow_comment_or_whitespace(parser);
589
-
590
- if (parser.match(',') || (inside_pseudo_class ? parser.match(')') : parser.match('{'))) {
591
- // rewind, so we know whether to continue building the selector list
592
- parser.index = index;
593
-
594
- relative_selector.end = index;
595
- children.push(relative_selector);
596
-
597
- return {
598
- type: 'ComplexSelector',
599
- start: list_start,
600
- end: index,
601
- children,
602
- metadata: {
603
- rule: null,
604
- used: false,
605
- },
606
- };
607
- }
608
-
609
- parser.index = index;
610
- const combinator = read_combinator(parser);
611
-
612
- if (combinator) {
613
- if (relative_selector.selectors.length > 0) {
614
- relative_selector.end = index;
615
- children.push(relative_selector);
616
- }
617
-
618
- // ...and start a new one
619
- relative_selector = create_selector(combinator, combinator.start);
620
-
621
- parser.allow_whitespace();
622
-
623
- if (parser.match(',') || (inside_pseudo_class ? parser.match(')') : parser.match('{'))) {
624
- throw new Error(`Invalid selector at parser.index: ${parser.index}`);
625
- }
626
- }
627
- }
628
-
629
- throw new Error('Unexpected end of input');
630
- }
631
-
632
- /**
633
- * Read a property that may or may not be quoted, e.g.
634
- * `foo` or `'foo bar'` or `"foo bar"`
635
- * @param {Parser} parser
636
- */
637
- function read_attribute_value(parser) {
638
- let value = '';
639
- let escaped = false;
640
- const quote_mark = parser.eat('"') ? '"' : parser.eat("'") ? "'" : null;
641
-
642
- while (parser.index < parser.template.length) {
643
- const char = parser.template[parser.index];
644
- if (escaped) {
645
- value += '\\' + char;
646
- escaped = false;
647
- } else if (char === '\\') {
648
- escaped = true;
649
- } else if (quote_mark ? char === quote_mark : /[\s\]]/.test(char)) {
650
- if (quote_mark) {
651
- parser.eat(quote_mark, true);
652
- }
653
-
654
- return value.trim();
655
- } else {
656
- value += char;
657
- }
658
-
659
- parser.index++;
660
- }
661
-
662
- throw new Error('Unexpected end of input');
663
- }
664
-
665
- /**
666
- * https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
667
- * @param {Parser} parser
668
- */
669
- function read_identifier(parser) {
670
- const start = parser.index;
671
-
672
- let identifier = '';
673
-
674
- if (parser.match_regex(REGEX_LEADING_HYPHEN_OR_DIGIT)) {
675
- throw new Error('Unexpected CSS identifier');
676
- }
677
-
678
- let escaped = false;
679
-
680
- while (parser.index < parser.template.length) {
681
- const char = parser.template[parser.index];
682
- if (escaped) {
683
- identifier += '\\' + char;
684
- escaped = false;
685
- } else if (char === '\\') {
686
- escaped = true;
687
- } else if (
688
- /** @type {number} */ (char.codePointAt(0)) >= 160 ||
689
- REGEX_VALID_IDENTIFIER_CHAR.test(char)
690
- ) {
691
- identifier += char;
692
- } else {
693
- break;
694
- }
695
-
696
- parser.index++;
697
- }
698
-
699
- if (identifier === '') {
700
- throw new Error('Expected identifier');
701
- }
702
-
703
- return identifier;
704
- }