@tsrx/core 0.1.7 → 0.1.8

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 (2) hide show
  1. package/package.json +6 -6
  2. package/src/plugin.js +185 -104
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Core compiler infrastructure for TSRX syntax",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.1.7",
6
+ "version": "0.1.8",
7
7
  "type": "module",
8
8
  "repository": {
9
9
  "type": "git",
@@ -64,7 +64,7 @@
64
64
  "@types/estree-jsx": "^1.0.5",
65
65
  "@types/estree": "^1.0.8",
66
66
  "acorn": "^8.15.0",
67
- "esrap": "^2.2.7",
67
+ "esrap": "^2.2.8",
68
68
  "is-reference": "^3.0.3",
69
69
  "magic-string": "^0.30.18",
70
70
  "zimmerframe": "^1.1.2"
@@ -82,10 +82,10 @@
82
82
  "vscode-languageserver-types": "^3.17.5",
83
83
  "vue": "3.6.0-beta.10",
84
84
  "vue-jsx-vapor": "^3.2.12",
85
- "@tsrx/preact": "0.1.7",
86
- "@tsrx/react": "0.2.7",
87
- "@tsrx/solid": "0.1.7",
88
- "@tsrx/vue": "0.1.7"
85
+ "@tsrx/preact": "0.1.8",
86
+ "@tsrx/react": "0.2.8",
87
+ "@tsrx/solid": "0.1.8",
88
+ "@tsrx/vue": "0.1.8"
89
89
  },
90
90
  "files": [
91
91
  "src",
package/src/plugin.js CHANGED
@@ -20,6 +20,39 @@ import { DIAGNOSTIC_CODES } from './diagnostics.js';
20
20
  const JSX_EXPRESSION_VALUE_ERROR =
21
21
  'JSX elements cannot be used as expressions. Wrap JSX with `<>...</>` or `<tsx>...</tsx>`, wrap TSRX templates with `<tsrx>...</tsrx>`, or use elements as statements within a component.';
22
22
 
23
+ const CharCode = Object.freeze({
24
+ tab: 9,
25
+ lineFeed: 10,
26
+ carriageReturn: 13,
27
+ space: 32,
28
+ doubleQuote: 34,
29
+ dollar: 36,
30
+ ampersand: 38,
31
+ singleQuote: 39,
32
+ openParen: 40,
33
+ closeParen: 41,
34
+ asterisk: 42,
35
+ slash: 47,
36
+ colon: 58,
37
+ semicolon: 59,
38
+ lessThan: 60,
39
+ equals: 61,
40
+ greaterThan: 62,
41
+ at: 64,
42
+ digit0: 48,
43
+ digit9: 57,
44
+ uppercaseA: 65,
45
+ uppercaseZ: 90,
46
+ openBracket: 91,
47
+ backslash: 92,
48
+ underscore: 95,
49
+ backtick: 96,
50
+ lowercaseA: 97,
51
+ lowercaseZ: 122,
52
+ openBrace: 123,
53
+ closeBrace: 125,
54
+ });
55
+
23
56
  /** @type {WeakMap<Record<string, boolean>, Map<string, number>>} */
24
57
  const argument_clash_first_positions = new WeakMap();
25
58
  /** @type {WeakMap<Record<string, boolean>, Set<string>>} */
@@ -58,7 +91,13 @@ function get_argument_clash_reported_names(check_clashes) {
58
91
  function skip_whitespace_from(input, i) {
59
92
  while (i < input.length) {
60
93
  const ch = input.charCodeAt(i);
61
- if (ch !== 32 && ch !== 9 && ch !== 10 && ch !== 13) break;
94
+ if (
95
+ ch !== CharCode.space &&
96
+ ch !== CharCode.tab &&
97
+ ch !== CharCode.lineFeed &&
98
+ ch !== CharCode.carriageReturn
99
+ )
100
+ break;
62
101
  i++;
63
102
  }
64
103
  return i;
@@ -75,7 +114,7 @@ function skip_string_from(input, i, quote) {
75
114
  while (i < input.length) {
76
115
  const ch = input.charCodeAt(i);
77
116
  i++;
78
- if (ch === 92)
117
+ if (ch === CharCode.backslash)
79
118
  i++; // backslash escape
80
119
  else if (ch === quote) return i;
81
120
  }
@@ -95,7 +134,7 @@ function scan_balanced_from(input, i, open, close) {
95
134
  i++;
96
135
  while (i < input.length) {
97
136
  const ch = input.charCodeAt(i);
98
- if (ch === 34 || ch === 39 || ch === 96) {
137
+ if (ch === CharCode.doubleQuote || ch === CharCode.singleQuote || ch === CharCode.backtick) {
99
138
  i = skip_string_from(input, i, ch);
100
139
  continue;
101
140
  }
@@ -114,47 +153,50 @@ function scan_balanced_from(input, i, open, close) {
114
153
  * @param {number} pos
115
154
  */
116
155
  function looks_like_generic_arrow(input, pos) {
117
- if (input.charCodeAt(pos) !== 60) return false;
156
+ if (input.charCodeAt(pos) !== CharCode.lessThan) return false;
118
157
 
119
158
  // Match the angle brackets, skipping over string literals.
120
159
  let i = pos + 1;
121
160
  let depth = 1;
122
161
  while (i < input.length) {
123
162
  const ch = input.charCodeAt(i);
124
- if (ch === 34 || ch === 39 || ch === 96) {
163
+ if (ch === CharCode.doubleQuote || ch === CharCode.singleQuote || ch === CharCode.backtick) {
125
164
  i = skip_string_from(input, i, ch);
126
165
  continue;
127
166
  }
128
- if (ch === 60) depth++;
129
- else if (ch === 62 && --depth === 0) break;
167
+ if (ch === CharCode.lessThan) depth++;
168
+ else if (ch === CharCode.greaterThan && --depth === 0) break;
130
169
  i++;
131
170
  }
132
171
  if (depth !== 0) return false;
133
172
 
134
173
  // `>` must be followed by `(...)`.
135
174
  i = skip_whitespace_from(input, i + 1);
136
- if (input.charCodeAt(i) !== 40) return false;
137
- i = scan_balanced_from(input, i, 40, 41);
175
+ if (input.charCodeAt(i) !== CharCode.openParen) return false;
176
+ i = scan_balanced_from(input, i, CharCode.openParen, CharCode.closeParen);
138
177
  if (i === -1) return false;
139
178
 
140
179
  // Optional `: ReturnType` before `=>`.
141
180
  i = skip_whitespace_from(input, i);
142
- if (input.charCodeAt(i) === 58) {
181
+ if (input.charCodeAt(i) === CharCode.colon) {
143
182
  i++;
144
183
  while (i < input.length) {
145
184
  const ch = input.charCodeAt(i);
146
- if (ch === 34 || ch === 39 || ch === 96) {
185
+ if (ch === CharCode.doubleQuote || ch === CharCode.singleQuote || ch === CharCode.backtick) {
147
186
  i = skip_string_from(input, i, ch);
148
187
  continue;
149
188
  }
150
- if (ch === 61 && input.charCodeAt(i + 1) === 62) return true;
151
- if (ch === 59 || ch === 123 || ch === 125) return false;
189
+ if (ch === CharCode.equals && input.charCodeAt(i + 1) === CharCode.greaterThan) return true;
190
+ if (ch === CharCode.semicolon || ch === CharCode.openBrace || ch === CharCode.closeBrace)
191
+ return false;
152
192
  i++;
153
193
  }
154
194
  return false;
155
195
  }
156
196
 
157
- return input.charCodeAt(i) === 61 && input.charCodeAt(i + 1) === 62;
197
+ return (
198
+ input.charCodeAt(i) === CharCode.equals && input.charCodeAt(i + 1) === CharCode.greaterThan
199
+ );
158
200
  }
159
201
 
160
202
  /**
@@ -176,7 +218,13 @@ function previous_word_before(input, pos) {
176
218
  let i = pos - 1;
177
219
  while (i >= 0) {
178
220
  const ch = input.charCodeAt(i);
179
- if (ch !== 32 && ch !== 9 && ch !== 10 && ch !== 13) break;
221
+ if (
222
+ ch !== CharCode.space &&
223
+ ch !== CharCode.tab &&
224
+ ch !== CharCode.lineFeed &&
225
+ ch !== CharCode.carriageReturn
226
+ )
227
+ break;
180
228
  i--;
181
229
  }
182
230
  const end = i + 1;
@@ -266,7 +314,12 @@ export function TSRXPlugin(config) {
266
314
  let index = this.pos - 1;
267
315
  while (index >= 0) {
268
316
  const ch = this.input.charCodeAt(index);
269
- if (ch !== 32 && ch !== 9 && ch !== 10 && ch !== 13) {
317
+ if (
318
+ ch !== CharCode.space &&
319
+ ch !== CharCode.tab &&
320
+ ch !== CharCode.lineFeed &&
321
+ ch !== CharCode.carriageReturn
322
+ ) {
270
323
  return ch;
271
324
  }
272
325
  index--;
@@ -454,7 +507,7 @@ export function TSRXPlugin(config) {
454
507
  }
455
508
 
456
509
  const after = this.input.charCodeAt(this.pos + 4);
457
- return after === 62 /* > */;
510
+ return after === CharCode.greaterThan;
458
511
  }
459
512
 
460
513
  #parseTsxIslandText() {
@@ -466,7 +519,7 @@ export function TSRXPlugin(config) {
466
519
  const ch = this.input.charCodeAt(this.pos);
467
520
 
468
521
  // Stop at opening tag, expression, or the component-closing brace
469
- if (ch === 60 || ch === 123 || ch === 125) {
522
+ if (ch === CharCode.lessThan || ch === CharCode.openBrace || ch === CharCode.closeBrace) {
470
523
  break;
471
524
  }
472
525
 
@@ -496,24 +549,27 @@ export function TSRXPlugin(config) {
496
549
  // fragment props like `content={<></>}` still need the JSX context.
497
550
  while (index < this.input.length) {
498
551
  const ch = this.input.charCodeAt(index);
499
- if (ch === 32 || ch === 9) {
552
+ if (ch === CharCode.space || ch === CharCode.tab) {
500
553
  index++;
501
- } else if (ch === 10 || ch === 13) {
554
+ } else if (ch === CharCode.lineFeed || ch === CharCode.carriageReturn) {
502
555
  has_newline = true;
503
556
  index++;
504
- } else if (ch === 47 && this.input.charCodeAt(index + 1) === 42) {
557
+ } else if (
558
+ ch === CharCode.slash &&
559
+ this.input.charCodeAt(index + 1) === CharCode.asterisk
560
+ ) {
505
561
  const end = this.input.indexOf('*/', index + 2);
506
562
  const comment_end = end === -1 ? this.input.length : end + 2;
507
563
  if (this.input.slice(index, comment_end).match(regex_newline_characters)) {
508
564
  has_newline = true;
509
565
  }
510
566
  index = comment_end;
511
- } else if (ch === 47 && this.input.charCodeAt(index + 1) === 47) {
567
+ } else if (ch === CharCode.slash && this.input.charCodeAt(index + 1) === CharCode.slash) {
512
568
  has_newline = true;
513
569
  index += 2;
514
570
  while (index < this.input.length) {
515
571
  const comment_ch = this.input.charCodeAt(index);
516
- if (comment_ch === 10 || comment_ch === 13) break;
572
+ if (comment_ch === CharCode.lineFeed || comment_ch === CharCode.carriageReturn) break;
517
573
  index++;
518
574
  }
519
575
  } else {
@@ -521,7 +577,7 @@ export function TSRXPlugin(config) {
521
577
  }
522
578
  }
523
579
 
524
- if (!has_newline || this.input.charCodeAt(index) !== 123) {
580
+ if (!has_newline || this.input.charCodeAt(index) !== CharCode.openBrace) {
525
581
  return;
526
582
  }
527
583
 
@@ -544,16 +600,24 @@ export function TSRXPlugin(config) {
544
600
  #skipWhitespaceAndComments(index) {
545
601
  while (index < this.input.length) {
546
602
  const ch = this.input.charCodeAt(index);
547
- if (ch === 32 || ch === 9 || ch === 10 || ch === 13) {
603
+ if (
604
+ ch === CharCode.space ||
605
+ ch === CharCode.tab ||
606
+ ch === CharCode.lineFeed ||
607
+ ch === CharCode.carriageReturn
608
+ ) {
548
609
  index++;
549
- } else if (ch === 47 && this.input.charCodeAt(index + 1) === 42) {
610
+ } else if (
611
+ ch === CharCode.slash &&
612
+ this.input.charCodeAt(index + 1) === CharCode.asterisk
613
+ ) {
550
614
  const end = this.input.indexOf('*/', index + 2);
551
615
  index = end === -1 ? this.input.length : end + 2;
552
- } else if (ch === 47 && this.input.charCodeAt(index + 1) === 47) {
616
+ } else if (ch === CharCode.slash && this.input.charCodeAt(index + 1) === CharCode.slash) {
553
617
  index += 2;
554
618
  while (index < this.input.length) {
555
619
  const comment_ch = this.input.charCodeAt(index);
556
- if (comment_ch === 10 || comment_ch === 13) break;
620
+ if (comment_ch === CharCode.lineFeed || comment_ch === CharCode.carriageReturn) break;
557
621
  index++;
558
622
  }
559
623
  } else {
@@ -569,7 +633,7 @@ export function TSRXPlugin(config) {
569
633
  let count = 0;
570
634
  while (index < this.input.length) {
571
635
  index = this.#skipWhitespaceAndComments(index);
572
- if (this.input.charCodeAt(index) !== 125) break;
636
+ if (this.input.charCodeAt(index) !== CharCode.closeBrace) break;
573
637
  count++;
574
638
  index++;
575
639
  }
@@ -701,11 +765,11 @@ export function TSRXPlugin(config) {
701
765
  const prev = this.#previousNonWhitespaceChar();
702
766
  return (
703
767
  prev === null ||
704
- prev === 34 || // "
705
- prev === 59 || // ;
706
- prev === 62 || // >
707
- (prev === 123 && this.#allowDoubleQuotedTextChildAfterBrace) || // {
708
- prev === 125 // }
768
+ prev === CharCode.doubleQuote ||
769
+ prev === CharCode.semicolon ||
770
+ prev === CharCode.greaterThan ||
771
+ (prev === CharCode.openBrace && this.#allowDoubleQuotedTextChildAfterBrace) ||
772
+ prev === CharCode.closeBrace
709
773
  );
710
774
  }
711
775
 
@@ -718,13 +782,13 @@ export function TSRXPlugin(config) {
718
782
  while (this.pos < this.input.length) {
719
783
  const ch = this.input.charCodeAt(this.pos);
720
784
 
721
- if (ch === 34 /* " */) {
785
+ if (ch === CharCode.doubleQuote) {
722
786
  out += this.input.slice(chunkStart, this.pos);
723
787
  this.pos++;
724
788
  return this.finishToken(tt.string, out);
725
789
  }
726
790
 
727
- if (ch === 38 /* & */) {
791
+ if (ch === CharCode.ampersand) {
728
792
  out += this.input.slice(chunkStart, this.pos);
729
793
  out += this.jsx_readEntity();
730
794
  chunkStart = this.pos;
@@ -1050,7 +1114,7 @@ export function TSRXPlugin(config) {
1050
1114
  * @type {Parse.Parser['readToken']}
1051
1115
  */
1052
1116
  readToken(code) {
1053
- if (code === 60 && looks_like_generic_arrow(this.input, this.pos)) {
1117
+ if (code === CharCode.lessThan && looks_like_generic_arrow(this.input, this.pos)) {
1054
1118
  ++this.pos;
1055
1119
  return this.finishToken(tt.relational, '<');
1056
1120
  }
@@ -1062,7 +1126,19 @@ export function TSRXPlugin(config) {
1062
1126
  * @type {Parse.Parser['getTokenFromCode']}
1063
1127
  */
1064
1128
  getTokenFromCode(code) {
1065
- if (code === 34) {
1129
+ // Callback props that return `<tsrx>...</tsrx>` without a semicolon can
1130
+ // leave the attribute expression context above the still-open tag. Drop
1131
+ // it before tokenizing `/>`, otherwise Acorn treats `/` as a regexp.
1132
+ if (
1133
+ code === CharCode.slash &&
1134
+ this.input.charCodeAt(this.pos + 1) === CharCode.greaterThan &&
1135
+ this.curContext() === b_expr &&
1136
+ this.context[this.context.length - 2] === tstc.tc_oTag
1137
+ ) {
1138
+ this.context.pop();
1139
+ this.exprAllowed = false;
1140
+ }
1141
+ if (code === CharCode.doubleQuote) {
1066
1142
  const is_double_quoted_text_child = this.#isDoubleQuotedTextChildStart();
1067
1143
  this.#allowDoubleQuotedTextChildAfterBrace = false;
1068
1144
  if (is_double_quoted_text_child) {
@@ -1072,11 +1148,11 @@ export function TSRXPlugin(config) {
1072
1148
  this.#allowDoubleQuotedTextChildAfterBrace = false;
1073
1149
  }
1074
1150
 
1075
- if (code !== 60) {
1151
+ if (code !== CharCode.lessThan) {
1076
1152
  this.#allowTagStartAfterDoubleQuotedText = false;
1077
1153
  }
1078
1154
 
1079
- if (code === 60) {
1155
+ if (code === CharCode.lessThan) {
1080
1156
  // < character
1081
1157
  const inComponent = this.#isInsideComponentTemplate();
1082
1158
  /** @type {number | null} */
@@ -1093,7 +1169,7 @@ export function TSRXPlugin(config) {
1093
1169
  // Skip whitespace backwards
1094
1170
  while (lookback >= 0) {
1095
1171
  const ch = this.input.charCodeAt(lookback);
1096
- if (ch !== 32 && ch !== 9) break; // not space or tab
1172
+ if (ch !== CharCode.space && ch !== CharCode.tab) break; // not space or tab
1097
1173
  lookback--;
1098
1174
  }
1099
1175
 
@@ -1105,12 +1181,12 @@ export function TSRXPlugin(config) {
1105
1181
  // If preceded by identifier character (letter, digit, _, $) or closing paren,
1106
1182
  // this is likely TypeScript generics, not JSX
1107
1183
  const isIdentifierChar =
1108
- (prevChar >= 65 && prevChar <= 90) || // A-Z
1109
- (prevChar >= 97 && prevChar <= 122) || // a-z
1110
- (prevChar >= 48 && prevChar <= 57) || // 0-9
1111
- prevChar === 95 || // _
1112
- prevChar === 36 || // $
1113
- prevChar === 41; // )
1184
+ (prevChar >= CharCode.uppercaseA && prevChar <= CharCode.uppercaseZ) ||
1185
+ (prevChar >= CharCode.lowercaseA && prevChar <= CharCode.lowercaseZ) ||
1186
+ (prevChar >= CharCode.digit0 && prevChar <= CharCode.digit9) ||
1187
+ prevChar === CharCode.underscore ||
1188
+ prevChar === CharCode.dollar ||
1189
+ prevChar === CharCode.closeParen;
1114
1190
 
1115
1191
  if (isIdentifierChar) {
1116
1192
  return super.getTokenFromCode(code);
@@ -1125,23 +1201,26 @@ export function TSRXPlugin(config) {
1125
1201
  const nextChar =
1126
1202
  this.pos + 1 < this.input.length ? this.input.charCodeAt(this.pos + 1) : -1;
1127
1203
  const isWhitespaceAfterLt =
1128
- nextChar === 32 || nextChar === 9 || nextChar === 10 || nextChar === 13;
1204
+ nextChar === CharCode.space ||
1205
+ nextChar === CharCode.tab ||
1206
+ nextChar === CharCode.lineFeed ||
1207
+ nextChar === CharCode.carriageReturn;
1129
1208
  const isTagLikeAfterLt =
1130
1209
  !isWhitespaceAfterLt &&
1131
- (nextChar === 47 || // '/'
1132
- nextChar === 62 || // '>' (fragments: <>)
1133
- nextChar === 64 || // '@'
1134
- nextChar === 36 || // '$'
1135
- nextChar === 95 || // '_'
1136
- (nextChar >= 65 && nextChar <= 90) || // A-Z
1137
- (nextChar >= 97 && nextChar <= 122)); // a-z
1210
+ (nextChar === CharCode.slash ||
1211
+ nextChar === CharCode.greaterThan ||
1212
+ nextChar === CharCode.at ||
1213
+ nextChar === CharCode.dollar ||
1214
+ nextChar === CharCode.underscore ||
1215
+ (nextChar >= CharCode.uppercaseA && nextChar <= CharCode.uppercaseZ) ||
1216
+ (nextChar >= CharCode.lowercaseA && nextChar <= CharCode.lowercaseZ));
1138
1217
  const prevAllowsTagStart =
1139
1218
  prevNonWhitespaceChar === null ||
1140
- prevNonWhitespaceChar === 10 || // '\n'
1141
- prevNonWhitespaceChar === 13 || // '\r'
1142
- prevNonWhitespaceChar === 123 || // '{'
1143
- prevNonWhitespaceChar === 125 || // '}'
1144
- prevNonWhitespaceChar === 62; // '>'
1219
+ prevNonWhitespaceChar === CharCode.lineFeed || // '\n'
1220
+ prevNonWhitespaceChar === CharCode.carriageReturn || // '\r'
1221
+ prevNonWhitespaceChar === CharCode.openBrace ||
1222
+ prevNonWhitespaceChar === CharCode.closeBrace ||
1223
+ prevNonWhitespaceChar === CharCode.greaterThan;
1145
1224
 
1146
1225
  if (!inComponent && prevAllowsTagStart && isTagLikeAfterLt) {
1147
1226
  ++this.pos;
@@ -1153,10 +1232,10 @@ export function TSRXPlugin(config) {
1153
1232
  // a newline/indentation before the next '<'. This is important for inputs
1154
1233
  // like `<div />` and `</div><style>...</style>` which Prettier formats.
1155
1234
  if (
1156
- (prevNonWhitespaceChar === 34 /* '"' */ &&
1235
+ (prevNonWhitespaceChar === CharCode.doubleQuote &&
1157
1236
  this.#allowTagStartAfterDoubleQuotedText) ||
1158
- prevNonWhitespaceChar === 123 /* '{' */ ||
1159
- prevNonWhitespaceChar === 62 /* '>' */
1237
+ prevNonWhitespaceChar === CharCode.openBrace ||
1238
+ prevNonWhitespaceChar === CharCode.greaterThan
1160
1239
  ) {
1161
1240
  if (!isWhitespaceAfterLt) {
1162
1241
  this.#allowTagStartAfterDoubleQuotedText = false;
@@ -1172,8 +1251,8 @@ export function TSRXPlugin(config) {
1172
1251
  let lineStart = this.pos - 1;
1173
1252
  while (
1174
1253
  lineStart >= 0 &&
1175
- this.input.charCodeAt(lineStart) !== 10 &&
1176
- this.input.charCodeAt(lineStart) !== 13
1254
+ this.input.charCodeAt(lineStart) !== CharCode.lineFeed &&
1255
+ this.input.charCodeAt(lineStart) !== CharCode.carriageReturn
1177
1256
  ) {
1178
1257
  lineStart--;
1179
1258
  }
@@ -1183,7 +1262,7 @@ export function TSRXPlugin(config) {
1183
1262
  let allWhitespace = true;
1184
1263
  for (let i = lineStart; i < this.pos; i++) {
1185
1264
  const ch = this.input.charCodeAt(i);
1186
- if (ch !== 32 && ch !== 9) {
1265
+ if (ch !== CharCode.space && ch !== CharCode.tab) {
1187
1266
  allWhitespace = false;
1188
1267
  break;
1189
1268
  }
@@ -1205,7 +1284,7 @@ export function TSRXPlugin(config) {
1205
1284
  /**
1206
1285
  * Override isLet to recognize `let &{` and `let &[` as variable declarations.
1207
1286
  * Acorn's isLet checks the char after `let` and only recognizes `{`, `[`, or identifiers.
1208
- * The `&` char (38) is not in that set, so `let &{...}` would not be parsed as a declaration.
1287
+ * The `&` character is not in that set, so `let &{...}` would not be parsed as a declaration.
1209
1288
  * @type {Parse.Parser['isLet']}
1210
1289
  */
1211
1290
  isLet(context) {
@@ -1217,9 +1296,9 @@ export function TSRXPlugin(config) {
1217
1296
  const next = this.pos + match[0].length;
1218
1297
  const nextCh = this.input.charCodeAt(next);
1219
1298
  // If next char is &, check if char after & is { or [
1220
- if (nextCh === 38) {
1299
+ if (nextCh === CharCode.ampersand) {
1221
1300
  const afterAmp = this.input.charCodeAt(next + 1);
1222
- if (afterAmp === 123 || afterAmp === 91) return true;
1301
+ if (afterAmp === CharCode.openBrace || afterAmp === CharCode.openBracket) return true;
1223
1302
  }
1224
1303
  return super.isLet(context);
1225
1304
  }
@@ -1233,7 +1312,7 @@ export function TSRXPlugin(config) {
1233
1312
  if (this.type === tt.bitwiseAND) {
1234
1313
  // Check that the char immediately after & is { or [ (no whitespace)
1235
1314
  const charAfterAmp = this.input.charCodeAt(this.end);
1236
- if (charAfterAmp === 123 || charAfterAmp === 91) {
1315
+ if (charAfterAmp === CharCode.openBrace || charAfterAmp === CharCode.openBracket) {
1237
1316
  // & directly followed by { or [ — lazy destructuring
1238
1317
  this.next(); // consume &, now current token is { or [
1239
1318
  const pattern = super.parseBindingAtom();
@@ -2121,20 +2200,20 @@ export function TSRXPlugin(config) {
2121
2200
  let ch = this.input.charCodeAt(this.pos);
2122
2201
 
2123
2202
  switch (ch) {
2124
- case 60: // '<'
2125
- case 123: // '{'
2203
+ case CharCode.lessThan:
2204
+ case CharCode.openBrace:
2126
2205
  // In JSX text mode, '<' and '{' always start a tag/expression container.
2127
2206
  // `exprAllowed` can be false here due to surrounding parser state, but
2128
2207
  // throwing breaks valid templates (e.g. sibling tags after a close).
2129
- if (ch === 60) {
2208
+ if (ch === CharCode.lessThan) {
2130
2209
  ++this.pos;
2131
2210
  return this.finishToken(tstt.jsxTagStart);
2132
2211
  }
2133
2212
  return this.getTokenFromCode(ch);
2134
2213
 
2135
- case 47: // '/'
2214
+ case CharCode.slash:
2136
2215
  // Check if this is a comment (// or /*)
2137
- if (this.input.charCodeAt(this.pos + 1) === 47) {
2216
+ if (this.input.charCodeAt(this.pos + 1) === CharCode.slash) {
2138
2217
  // '//'
2139
2218
  // Line comment - handle it properly
2140
2219
  const commentStart = this.pos;
@@ -2168,7 +2247,7 @@ export function TSRXPlugin(config) {
2168
2247
 
2169
2248
  // Continue processing from current position
2170
2249
  break;
2171
- } else if (this.input.charCodeAt(this.pos + 1) === 42) {
2250
+ } else if (this.input.charCodeAt(this.pos + 1) === CharCode.asterisk) {
2172
2251
  // '/*'
2173
2252
  // Block comment - handle it properly
2174
2253
  const commentStart = this.pos;
@@ -2178,8 +2257,8 @@ export function TSRXPlugin(config) {
2178
2257
  let commentText = '';
2179
2258
  while (this.pos < this.input.length - 1) {
2180
2259
  if (
2181
- this.input.charCodeAt(this.pos) === 42 &&
2182
- this.input.charCodeAt(this.pos + 1) === 47
2260
+ this.input.charCodeAt(this.pos) === CharCode.asterisk &&
2261
+ this.input.charCodeAt(this.pos + 1) === CharCode.slash
2183
2262
  ) {
2184
2263
  this.pos += 2;
2185
2264
  break;
@@ -2214,17 +2293,16 @@ export function TSRXPlugin(config) {
2214
2293
  this.exprAllowed = true;
2215
2294
  return original.readToken.call(this, ch);
2216
2295
 
2217
- case 38: // '&'
2296
+ case CharCode.ampersand:
2218
2297
  out += this.input.slice(chunkStart, this.pos);
2219
2298
  out += this.jsx_readEntity();
2220
2299
  chunkStart = this.pos;
2221
2300
  break;
2222
2301
 
2223
- case 62: // '>'
2224
- case 125: {
2225
- // '}'
2302
+ case CharCode.greaterThan:
2303
+ case CharCode.closeBrace: {
2226
2304
  if (
2227
- ch === 125 &&
2305
+ ch === CharCode.closeBrace &&
2228
2306
  (this.#path.length === 0 ||
2229
2307
  this.#path.at(-1)?.type === 'Component' ||
2230
2308
  this.#path.at(-1)?.type === 'Element' ||
@@ -2238,7 +2316,7 @@ export function TSRXPlugin(config) {
2238
2316
  'Unexpected token `' +
2239
2317
  this.input[this.pos] +
2240
2318
  '`. Did you mean `' +
2241
- (ch === 62 ? '&gt;' : '&rbrace;') +
2319
+ (ch === CharCode.greaterThan ? '&gt;' : '&rbrace;') +
2242
2320
  '` or ' +
2243
2321
  '`{"' +
2244
2322
  this.input[this.pos] +
@@ -2252,7 +2330,7 @@ export function TSRXPlugin(config) {
2252
2330
  out += this.input.slice(chunkStart, this.pos);
2253
2331
  out += this.jsx_readNewLine(true);
2254
2332
  chunkStart = this.pos;
2255
- } else if (ch === 32 || ch === 9) {
2333
+ } else if (ch === CharCode.space || ch === CharCode.tab) {
2256
2334
  ++this.pos;
2257
2335
  } else {
2258
2336
  this.#resetTokenStartToCurrentPosition();
@@ -2283,28 +2361,28 @@ export function TSRXPlugin(config) {
2283
2361
  // Check if the element being parsed IS a <tsx>, <tsrx>, or <tsx:*> tag
2284
2362
  // Current token is jsxTagStart, this.end is position after '<'
2285
2363
  const tag_name_start = this.end;
2286
- const is_fragment_tag = this.input.charCodeAt(tag_name_start) === 62;
2364
+ const is_fragment_tag = this.input.charCodeAt(tag_name_start) === CharCode.greaterThan;
2287
2365
  const char_after_tsx = this.input.charCodeAt(tag_name_start + 3);
2288
2366
  const char_after_tsrx = this.input.charCodeAt(tag_name_start + 4);
2289
2367
  const is_tsx_tag =
2290
2368
  this.input.startsWith('tsx', tag_name_start) &&
2291
2369
  (tag_name_start + 3 >= this.input.length ||
2292
- char_after_tsx === 62 || // >
2293
- char_after_tsx === 47 || // / (self-closing)
2294
- char_after_tsx === 32 || // space
2295
- char_after_tsx === 9 || // tab
2296
- char_after_tsx === 10 || // newline
2297
- char_after_tsx === 13 || // carriage return
2298
- char_after_tsx === 58); // : (tsx:react)
2370
+ char_after_tsx === CharCode.greaterThan ||
2371
+ char_after_tsx === CharCode.slash ||
2372
+ char_after_tsx === CharCode.space ||
2373
+ char_after_tsx === CharCode.tab ||
2374
+ char_after_tsx === CharCode.lineFeed ||
2375
+ char_after_tsx === CharCode.carriageReturn ||
2376
+ char_after_tsx === CharCode.colon);
2299
2377
  const is_tsrx_tag =
2300
2378
  this.input.startsWith('tsrx', tag_name_start) &&
2301
2379
  (tag_name_start + 4 >= this.input.length ||
2302
- char_after_tsrx === 62 || // >
2303
- char_after_tsrx === 47 || // / (self-closing)
2304
- char_after_tsrx === 32 || // space
2305
- char_after_tsrx === 9 || // tab
2306
- char_after_tsrx === 10 || // newline
2307
- char_after_tsrx === 13); // carriage return
2380
+ char_after_tsrx === CharCode.greaterThan ||
2381
+ char_after_tsrx === CharCode.slash ||
2382
+ char_after_tsrx === CharCode.space ||
2383
+ char_after_tsrx === CharCode.tab ||
2384
+ char_after_tsrx === CharCode.lineFeed ||
2385
+ char_after_tsrx === CharCode.carriageReturn);
2308
2386
 
2309
2387
  if (is_fragment_tag || is_tsx_tag || is_tsrx_tag) {
2310
2388
  // Use Ripple's parseElement to create a Tsx/Tsrx/TsxCompat node.
@@ -2787,7 +2865,10 @@ export function TSRXPlugin(config) {
2787
2865
  }
2788
2866
  if (this.type === tt.braceL) {
2789
2867
  body.push(this.#parseNativeTemplateExpressionContainer());
2790
- } else if (this.type === tt.string && this.input.charCodeAt(this.start) === 34) {
2868
+ } else if (
2869
+ this.type === tt.string &&
2870
+ this.input.charCodeAt(this.start) === CharCode.doubleQuote
2871
+ ) {
2791
2872
  body.push(this.parseDoubleQuotedTextChild());
2792
2873
  } else if (this.type === tt.braceR) {
2793
2874
  // Leaving a component/template body. We may still be in TSX/JSX tokenization
@@ -2800,8 +2881,8 @@ export function TSRXPlugin(config) {
2800
2881
  return;
2801
2882
  } else if (
2802
2883
  this.type === tstt.jsxTagStart ||
2803
- (this.input.charCodeAt(this.start) === 60 /* < */ &&
2804
- this.input.charCodeAt(this.start + 1) === 47) /* / */
2884
+ (this.input.charCodeAt(this.start) === CharCode.lessThan &&
2885
+ this.input.charCodeAt(this.start + 1) === CharCode.slash)
2805
2886
  ) {
2806
2887
  const startPos = this.start;
2807
2888
  const startLoc = this.startLoc;
@@ -3082,7 +3163,7 @@ export function TSRXPlugin(config) {
3082
3163
  if (
3083
3164
  this.#functionBodyDepth === 0 &&
3084
3165
  this.type === tt.string &&
3085
- this.input.charCodeAt(this.start) === 34 &&
3166
+ this.input.charCodeAt(this.start) === CharCode.doubleQuote &&
3086
3167
  (this.#path.at(-1)?.type === 'Component' || this.#path.at(-1)?.type === 'Element')
3087
3168
  ) {
3088
3169
  this.pos = this.start;
@@ -3096,7 +3177,7 @@ export function TSRXPlugin(config) {
3096
3177
  // e.g., &[data] = track(0); or &{x, y} = obj;
3097
3178
  if (this.type === tt.bitwiseAND) {
3098
3179
  const charAfterAmp = this.input.charCodeAt(this.end);
3099
- if (charAfterAmp === 123 || charAfterAmp === 91) {
3180
+ if (charAfterAmp === CharCode.openBrace || charAfterAmp === CharCode.openBracket) {
3100
3181
  const node = /** @type {AST.ExpressionStatement} */ (this.startNode());
3101
3182
  const assign_node = /** @type {AST.AssignmentExpression} */ (this.startNode());
3102
3183
  this.next(); // consume &