trickle-observe 0.2.87 → 0.2.89
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/dist/observe-register.js +74 -3
- package/dist/vite-plugin.js +58 -1
- package/dist-esm/vite-plugin.js +58 -1
- package/observe-esm-hooks.mjs +32 -0
- package/package.json +1 -1
- package/src/observe-register.ts +53 -3
- package/src/vite-plugin.ts +41 -1
package/dist/observe-register.js
CHANGED
|
@@ -327,6 +327,33 @@ function findClosingBrace(source, openBrace) {
|
|
|
327
327
|
pos++;
|
|
328
328
|
pos++; // skip past /
|
|
329
329
|
}
|
|
330
|
+
else if (ch === '/' && pos + 1 < source.length && source[pos + 1] !== '/' && source[pos + 1] !== '*') {
|
|
331
|
+
// Possible regex literal — check preceding context
|
|
332
|
+
let p = pos - 1;
|
|
333
|
+
while (p >= 0 && (source[p] === ' ' || source[p] === '\t'))
|
|
334
|
+
p--;
|
|
335
|
+
const prevCh = p >= 0 ? source[p] : '';
|
|
336
|
+
if ('=(!,;:?[{&|^~+-><%'.includes(prevCh) || source.slice(Math.max(0, p - 5), p + 1).match(/\b(return|typeof|instanceof|in|of|void|delete|throw|new|case)\s*$/)) {
|
|
337
|
+
pos++;
|
|
338
|
+
while (pos < source.length) {
|
|
339
|
+
if (source[pos] === '\\')
|
|
340
|
+
pos++;
|
|
341
|
+
else if (source[pos] === '[') {
|
|
342
|
+
pos++;
|
|
343
|
+
while (pos < source.length && source[pos] !== ']') {
|
|
344
|
+
if (source[pos] === '\\')
|
|
345
|
+
pos++;
|
|
346
|
+
pos++;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
else if (source[pos] === '/')
|
|
350
|
+
break;
|
|
351
|
+
pos++;
|
|
352
|
+
}
|
|
353
|
+
while (pos + 1 < source.length && /[gimsuy]/.test(source[pos + 1]))
|
|
354
|
+
pos++;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
330
357
|
pos++;
|
|
331
358
|
}
|
|
332
359
|
return -1; // not found
|
|
@@ -429,9 +456,21 @@ function findVarDeclarations(source, lineOffset = 0) {
|
|
|
429
456
|
// AND the previous non-whitespace isn't an operator expecting more (=, +, -, etc.)
|
|
430
457
|
const nextNonWs = source.slice(pos + 1).match(/^\s*(\S)/);
|
|
431
458
|
if (nextNonWs && !'.+=-|&?:,'.includes(nextNonWs[1])) {
|
|
432
|
-
// Also check if
|
|
433
|
-
|
|
434
|
-
|
|
459
|
+
// Also check if a recent line ends with an operator that expects a value on the next line
|
|
460
|
+
// Walk backwards through empty lines to find the last non-empty line
|
|
461
|
+
let checkPos = pos;
|
|
462
|
+
let lastChar = '';
|
|
463
|
+
for (let back = 0; back < 5; back++) {
|
|
464
|
+
const prevNL = source.lastIndexOf('\n', checkPos - 1);
|
|
465
|
+
const prevLine = source.slice(prevNL + 1, checkPos).trimEnd();
|
|
466
|
+
if (prevLine.length > 0) {
|
|
467
|
+
lastChar = prevLine[prevLine.length - 1];
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
checkPos = prevNL;
|
|
471
|
+
if (prevNL <= 0)
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
435
474
|
if (lastChar && '=+-*/%&|^~<>?:,({['.includes(lastChar)) {
|
|
436
475
|
// Line ends with operator — this is a continuation, don't end the statement
|
|
437
476
|
pos++;
|
|
@@ -454,6 +493,38 @@ function findVarDeclarations(source, lineOffset = 0) {
|
|
|
454
493
|
pos++;
|
|
455
494
|
}
|
|
456
495
|
}
|
|
496
|
+
else if (ch === '/' && pos + 1 < source.length && source[pos + 1] !== '/' && source[pos + 1] !== '*') {
|
|
497
|
+
// Possible regex literal — check if the preceding non-whitespace indicates regex context
|
|
498
|
+
// (after =, (, ,, ;, !, &, |, ^, ~, ?, :, [, {, return, typeof, etc.)
|
|
499
|
+
let p = pos - 1;
|
|
500
|
+
while (p >= 0 && (source[p] === ' ' || source[p] === '\t'))
|
|
501
|
+
p--;
|
|
502
|
+
const prevCh = p >= 0 ? source[p] : '';
|
|
503
|
+
if ('=(!,;:?[{&|^~+-><%'.includes(prevCh) || source.slice(Math.max(0, p - 5), p + 1).match(/\b(return|typeof|instanceof|in|of|void|delete|throw|new|case)\s*$/)) {
|
|
504
|
+
// This is a regex literal — skip to the closing /
|
|
505
|
+
pos++; // skip past opening /
|
|
506
|
+
while (pos < source.length) {
|
|
507
|
+
if (source[pos] === '\\') {
|
|
508
|
+
pos++;
|
|
509
|
+
} // skip escaped char
|
|
510
|
+
else if (source[pos] === '[') {
|
|
511
|
+
// Character class — skip to ]
|
|
512
|
+
pos++;
|
|
513
|
+
while (pos < source.length && source[pos] !== ']') {
|
|
514
|
+
if (source[pos] === '\\')
|
|
515
|
+
pos++;
|
|
516
|
+
pos++;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
else if (source[pos] === '/')
|
|
520
|
+
break;
|
|
521
|
+
pos++;
|
|
522
|
+
}
|
|
523
|
+
// Skip regex flags
|
|
524
|
+
while (pos + 1 < source.length && /[gimsuy]/.test(source[pos + 1]))
|
|
525
|
+
pos++;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
457
528
|
else if (ch === '/' && pos + 1 < source.length && source[pos + 1] === '/') {
|
|
458
529
|
// Skip line comment
|
|
459
530
|
while (pos < source.length && source[pos] !== '\n')
|
package/dist/vite-plugin.js
CHANGED
|
@@ -632,13 +632,25 @@ function findReassignments(source) {
|
|
|
632
632
|
const beforeOnLine = source.slice(lineStart, match.index).trim();
|
|
633
633
|
if (beforeOnLine.endsWith(':') || beforeOnLine.endsWith(','))
|
|
634
634
|
continue;
|
|
635
|
+
// Skip comma-separated multi-variable declaration continuations:
|
|
636
|
+
// var X = 'foo',
|
|
637
|
+
// Y = 'bar'; ← Y looks like a reassignment but is actually a declaration
|
|
638
|
+
// Detect by checking if the previous non-empty line ends with ','
|
|
639
|
+
if (beforeOnLine.length === 0) {
|
|
640
|
+
const prevLineEnd = source.lastIndexOf('\n', lineStart - 1);
|
|
641
|
+
if (prevLineEnd >= 0) {
|
|
642
|
+
const prevLine = source.slice(source.lastIndexOf('\n', prevLineEnd - 1) + 1, prevLineEnd).trimEnd();
|
|
643
|
+
if (prevLine.endsWith(','))
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
635
647
|
// Calculate line number
|
|
636
648
|
let lineNo = 1;
|
|
637
649
|
for (let i = 0; i < match.index; i++) {
|
|
638
650
|
if (source[i] === '\n')
|
|
639
651
|
lineNo++;
|
|
640
652
|
}
|
|
641
|
-
// Find end of statement
|
|
653
|
+
// Find end of statement
|
|
642
654
|
const startPos = match.index + match[0].length - 1;
|
|
643
655
|
let pos = startPos;
|
|
644
656
|
let depth = 0;
|
|
@@ -660,6 +672,24 @@ function findReassignments(source) {
|
|
|
660
672
|
else if (ch === '\n' && depth === 0) {
|
|
661
673
|
const nextNonWs = source.slice(pos + 1).match(/^\s*(\S)/);
|
|
662
674
|
if (nextNonWs && !'.+=-|&?:,'.includes(nextNonWs[1])) {
|
|
675
|
+
// Check if a recent non-empty line ends with an operator
|
|
676
|
+
let checkPos2 = pos;
|
|
677
|
+
let lastCh2 = '';
|
|
678
|
+
for (let back = 0; back < 5; back++) {
|
|
679
|
+
const prevNL2 = source.lastIndexOf('\n', checkPos2 - 1);
|
|
680
|
+
const prevLine2 = source.slice(prevNL2 + 1, checkPos2).trimEnd();
|
|
681
|
+
if (prevLine2.length > 0) {
|
|
682
|
+
lastCh2 = prevLine2[prevLine2.length - 1];
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
checkPos2 = prevNL2;
|
|
686
|
+
if (prevNL2 <= 0)
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
if (lastCh2 && '=+-*/%&|^~<>?:,({['.includes(lastCh2)) {
|
|
690
|
+
pos++;
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
663
693
|
foundEnd = pos;
|
|
664
694
|
break;
|
|
665
695
|
}
|
|
@@ -676,6 +706,33 @@ function findReassignments(source) {
|
|
|
676
706
|
pos++;
|
|
677
707
|
}
|
|
678
708
|
}
|
|
709
|
+
else if (ch === '/' && pos + 1 < source.length && source[pos + 1] !== '/' && source[pos + 1] !== '*') {
|
|
710
|
+
// Possible regex literal
|
|
711
|
+
let rp = pos - 1;
|
|
712
|
+
while (rp >= 0 && (source[rp] === ' ' || source[rp] === '\t'))
|
|
713
|
+
rp--;
|
|
714
|
+
const rpCh = rp >= 0 ? source[rp] : '';
|
|
715
|
+
if ('=(!,;:?[{&|^~+-><%'.includes(rpCh) || source.slice(Math.max(0, rp - 5), rp + 1).match(/\b(return|typeof|instanceof|in|of|void|delete|throw|new|case)\s*$/)) {
|
|
716
|
+
pos++;
|
|
717
|
+
while (pos < source.length) {
|
|
718
|
+
if (source[pos] === '\\')
|
|
719
|
+
pos++;
|
|
720
|
+
else if (source[pos] === '[') {
|
|
721
|
+
pos++;
|
|
722
|
+
while (pos < source.length && source[pos] !== ']') {
|
|
723
|
+
if (source[pos] === '\\')
|
|
724
|
+
pos++;
|
|
725
|
+
pos++;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
else if (source[pos] === '/')
|
|
729
|
+
break;
|
|
730
|
+
pos++;
|
|
731
|
+
}
|
|
732
|
+
while (pos + 1 < source.length && /[gimsuy]/.test(source[pos + 1]))
|
|
733
|
+
pos++;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
679
736
|
else if (ch === '/' && pos + 1 < source.length && source[pos + 1] === '/') {
|
|
680
737
|
while (pos < source.length && source[pos] !== '\n')
|
|
681
738
|
pos++;
|
package/dist-esm/vite-plugin.js
CHANGED
|
@@ -619,13 +619,25 @@ export function findReassignments(source) {
|
|
|
619
619
|
const beforeOnLine = source.slice(lineStart, match.index).trim();
|
|
620
620
|
if (beforeOnLine.endsWith(':') || beforeOnLine.endsWith(','))
|
|
621
621
|
continue;
|
|
622
|
+
// Skip comma-separated multi-variable declaration continuations:
|
|
623
|
+
// var X = 'foo',
|
|
624
|
+
// Y = 'bar'; ← Y looks like a reassignment but is actually a declaration
|
|
625
|
+
// Detect by checking if the previous non-empty line ends with ','
|
|
626
|
+
if (beforeOnLine.length === 0) {
|
|
627
|
+
const prevLineEnd = source.lastIndexOf('\n', lineStart - 1);
|
|
628
|
+
if (prevLineEnd >= 0) {
|
|
629
|
+
const prevLine = source.slice(source.lastIndexOf('\n', prevLineEnd - 1) + 1, prevLineEnd).trimEnd();
|
|
630
|
+
if (prevLine.endsWith(','))
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
622
634
|
// Calculate line number
|
|
623
635
|
let lineNo = 1;
|
|
624
636
|
for (let i = 0; i < match.index; i++) {
|
|
625
637
|
if (source[i] === '\n')
|
|
626
638
|
lineNo++;
|
|
627
639
|
}
|
|
628
|
-
// Find end of statement
|
|
640
|
+
// Find end of statement
|
|
629
641
|
const startPos = match.index + match[0].length - 1;
|
|
630
642
|
let pos = startPos;
|
|
631
643
|
let depth = 0;
|
|
@@ -647,6 +659,24 @@ export function findReassignments(source) {
|
|
|
647
659
|
else if (ch === '\n' && depth === 0) {
|
|
648
660
|
const nextNonWs = source.slice(pos + 1).match(/^\s*(\S)/);
|
|
649
661
|
if (nextNonWs && !'.+=-|&?:,'.includes(nextNonWs[1])) {
|
|
662
|
+
// Check if a recent non-empty line ends with an operator
|
|
663
|
+
let checkPos2 = pos;
|
|
664
|
+
let lastCh2 = '';
|
|
665
|
+
for (let back = 0; back < 5; back++) {
|
|
666
|
+
const prevNL2 = source.lastIndexOf('\n', checkPos2 - 1);
|
|
667
|
+
const prevLine2 = source.slice(prevNL2 + 1, checkPos2).trimEnd();
|
|
668
|
+
if (prevLine2.length > 0) {
|
|
669
|
+
lastCh2 = prevLine2[prevLine2.length - 1];
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
checkPos2 = prevNL2;
|
|
673
|
+
if (prevNL2 <= 0)
|
|
674
|
+
break;
|
|
675
|
+
}
|
|
676
|
+
if (lastCh2 && '=+-*/%&|^~<>?:,({['.includes(lastCh2)) {
|
|
677
|
+
pos++;
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
650
680
|
foundEnd = pos;
|
|
651
681
|
break;
|
|
652
682
|
}
|
|
@@ -663,6 +693,33 @@ export function findReassignments(source) {
|
|
|
663
693
|
pos++;
|
|
664
694
|
}
|
|
665
695
|
}
|
|
696
|
+
else if (ch === '/' && pos + 1 < source.length && source[pos + 1] !== '/' && source[pos + 1] !== '*') {
|
|
697
|
+
// Possible regex literal
|
|
698
|
+
let rp = pos - 1;
|
|
699
|
+
while (rp >= 0 && (source[rp] === ' ' || source[rp] === '\t'))
|
|
700
|
+
rp--;
|
|
701
|
+
const rpCh = rp >= 0 ? source[rp] : '';
|
|
702
|
+
if ('=(!,;:?[{&|^~+-><%'.includes(rpCh) || source.slice(Math.max(0, rp - 5), rp + 1).match(/\b(return|typeof|instanceof|in|of|void|delete|throw|new|case)\s*$/)) {
|
|
703
|
+
pos++;
|
|
704
|
+
while (pos < source.length) {
|
|
705
|
+
if (source[pos] === '\\')
|
|
706
|
+
pos++;
|
|
707
|
+
else if (source[pos] === '[') {
|
|
708
|
+
pos++;
|
|
709
|
+
while (pos < source.length && source[pos] !== ']') {
|
|
710
|
+
if (source[pos] === '\\')
|
|
711
|
+
pos++;
|
|
712
|
+
pos++;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
else if (source[pos] === '/')
|
|
716
|
+
break;
|
|
717
|
+
pos++;
|
|
718
|
+
}
|
|
719
|
+
while (pos + 1 < source.length && /[gimsuy]/.test(source[pos + 1]))
|
|
720
|
+
pos++;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
666
723
|
else if (ch === '/' && pos + 1 < source.length && source[pos + 1] === '/') {
|
|
667
724
|
while (pos < source.length && source[pos] !== '\n')
|
|
668
725
|
pos++;
|
package/observe-esm-hooks.mjs
CHANGED
|
@@ -236,6 +236,10 @@ function findVarDeclarationsESM(source) {
|
|
|
236
236
|
const restOfLine = source.slice(match.index + match[0].length - 1, match.index + match[0].length + 200);
|
|
237
237
|
if (/^\s*require\s*\(/.test(restOfLine)) continue;
|
|
238
238
|
|
|
239
|
+
// Skip variable declarations inside for-loop headers
|
|
240
|
+
const beforeDecl = source.slice(Math.max(0, match.index - 50), match.index);
|
|
241
|
+
if (/\bfor\s*\(\s*$/.test(beforeDecl)) continue;
|
|
242
|
+
|
|
239
243
|
// Calculate line number where the declaration starts
|
|
240
244
|
let lineNo = 1;
|
|
241
245
|
for (let i = 0; i < match.index; i++) {
|
|
@@ -260,6 +264,19 @@ function findVarDeclarationsESM(source) {
|
|
|
260
264
|
} else if (ch === '\n' && depth === 0) {
|
|
261
265
|
const nextNonWs = source.slice(pos + 1).match(/^\s*(\S)/);
|
|
262
266
|
if (nextNonWs && !'.+=-|&?:,'.includes(nextNonWs[1])) {
|
|
267
|
+
// Check if a recent non-empty line ends with an operator (continuation across empty lines)
|
|
268
|
+
let checkPos = pos;
|
|
269
|
+
let lastCh = '';
|
|
270
|
+
for (let back = 0; back < 5; back++) {
|
|
271
|
+
const prevNL = source.lastIndexOf('\n', checkPos - 1);
|
|
272
|
+
const prevLine = source.slice(prevNL + 1, checkPos).trimEnd();
|
|
273
|
+
if (prevLine.length > 0) { lastCh = prevLine[prevLine.length - 1]; break; }
|
|
274
|
+
checkPos = prevNL;
|
|
275
|
+
if (prevNL <= 0) break;
|
|
276
|
+
}
|
|
277
|
+
if (lastCh && '=+-*/%&|^~<>?:,({['.includes(lastCh)) {
|
|
278
|
+
pos++; continue;
|
|
279
|
+
}
|
|
263
280
|
foundEnd = pos;
|
|
264
281
|
break;
|
|
265
282
|
}
|
|
@@ -270,6 +287,21 @@ function findVarDeclarationsESM(source) {
|
|
|
270
287
|
else if (source[pos] === quote) break;
|
|
271
288
|
pos++;
|
|
272
289
|
}
|
|
290
|
+
} else if (ch === '/' && pos + 1 < source.length && source[pos + 1] !== '/' && source[pos + 1] !== '*') {
|
|
291
|
+
// Possible regex literal
|
|
292
|
+
let rp = pos - 1;
|
|
293
|
+
while (rp >= 0 && (source[rp] === ' ' || source[rp] === '\t')) rp--;
|
|
294
|
+
const rpCh = rp >= 0 ? source[rp] : '';
|
|
295
|
+
if ('=(!,;:?[{&|^~+-><%'.includes(rpCh) || source.slice(Math.max(0, rp - 5), rp + 1).match(/\b(return|typeof|instanceof|in|of|void|delete|throw|new|case)\s*$/)) {
|
|
296
|
+
pos++;
|
|
297
|
+
while (pos < source.length) {
|
|
298
|
+
if (source[pos] === '\\') pos++;
|
|
299
|
+
else if (source[pos] === '[') { pos++; while (pos < source.length && source[pos] !== ']') { if (source[pos] === '\\') pos++; pos++; } }
|
|
300
|
+
else if (source[pos] === '/') break;
|
|
301
|
+
pos++;
|
|
302
|
+
}
|
|
303
|
+
while (pos + 1 < source.length && /[gimsuy]/.test(source[pos + 1])) pos++;
|
|
304
|
+
}
|
|
273
305
|
} else if (ch === '/' && pos + 1 < source.length && source[pos + 1] === '/') {
|
|
274
306
|
while (pos < source.length && source[pos] !== '\n') pos++;
|
|
275
307
|
continue;
|
package/package.json
CHANGED
package/src/observe-register.ts
CHANGED
|
@@ -334,6 +334,21 @@ function findClosingBrace(source: string, openBrace: number): number {
|
|
|
334
334
|
pos += 2;
|
|
335
335
|
while (pos < source.length - 1 && !(source[pos] === '*' && source[pos + 1] === '/')) pos++;
|
|
336
336
|
pos++; // skip past /
|
|
337
|
+
} else if (ch === '/' && pos + 1 < source.length && source[pos + 1] !== '/' && source[pos + 1] !== '*') {
|
|
338
|
+
// Possible regex literal — check preceding context
|
|
339
|
+
let p = pos - 1;
|
|
340
|
+
while (p >= 0 && (source[p] === ' ' || source[p] === '\t')) p--;
|
|
341
|
+
const prevCh = p >= 0 ? source[p] : '';
|
|
342
|
+
if ('=(!,;:?[{&|^~+-><%'.includes(prevCh) || source.slice(Math.max(0, p - 5), p + 1).match(/\b(return|typeof|instanceof|in|of|void|delete|throw|new|case)\s*$/)) {
|
|
343
|
+
pos++;
|
|
344
|
+
while (pos < source.length) {
|
|
345
|
+
if (source[pos] === '\\') pos++;
|
|
346
|
+
else if (source[pos] === '[') { pos++; while (pos < source.length && source[pos] !== ']') { if (source[pos] === '\\') pos++; pos++; } }
|
|
347
|
+
else if (source[pos] === '/') break;
|
|
348
|
+
pos++;
|
|
349
|
+
}
|
|
350
|
+
while (pos + 1 < source.length && /[gimsuy]/.test(source[pos + 1])) pos++;
|
|
351
|
+
}
|
|
337
352
|
}
|
|
338
353
|
pos++;
|
|
339
354
|
}
|
|
@@ -427,9 +442,20 @@ function findVarDeclarations(source: string, lineOffset: number = 0): Array<{ li
|
|
|
427
442
|
// AND the previous non-whitespace isn't an operator expecting more (=, +, -, etc.)
|
|
428
443
|
const nextNonWs = source.slice(pos + 1).match(/^\s*(\S)/);
|
|
429
444
|
if (nextNonWs && !'.+=-|&?:,'.includes(nextNonWs[1])) {
|
|
430
|
-
// Also check if
|
|
431
|
-
|
|
432
|
-
|
|
445
|
+
// Also check if a recent line ends with an operator that expects a value on the next line
|
|
446
|
+
// Walk backwards through empty lines to find the last non-empty line
|
|
447
|
+
let checkPos = pos;
|
|
448
|
+
let lastChar = '';
|
|
449
|
+
for (let back = 0; back < 5; back++) {
|
|
450
|
+
const prevNL = source.lastIndexOf('\n', checkPos - 1);
|
|
451
|
+
const prevLine = source.slice(prevNL + 1, checkPos).trimEnd();
|
|
452
|
+
if (prevLine.length > 0) {
|
|
453
|
+
lastChar = prevLine[prevLine.length - 1];
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
checkPos = prevNL;
|
|
457
|
+
if (prevNL <= 0) break;
|
|
458
|
+
}
|
|
433
459
|
if (lastChar && '=+-*/%&|^~<>?:,({['.includes(lastChar)) {
|
|
434
460
|
// Line ends with operator — this is a continuation, don't end the statement
|
|
435
461
|
pos++;
|
|
@@ -447,6 +473,30 @@ function findVarDeclarations(source: string, lineOffset: number = 0): Array<{ li
|
|
|
447
473
|
else if (source[pos] === quote) break;
|
|
448
474
|
pos++;
|
|
449
475
|
}
|
|
476
|
+
} else if (ch === '/' && pos + 1 < source.length && source[pos + 1] !== '/' && source[pos + 1] !== '*') {
|
|
477
|
+
// Possible regex literal — check if the preceding non-whitespace indicates regex context
|
|
478
|
+
// (after =, (, ,, ;, !, &, |, ^, ~, ?, :, [, {, return, typeof, etc.)
|
|
479
|
+
let p = pos - 1;
|
|
480
|
+
while (p >= 0 && (source[p] === ' ' || source[p] === '\t')) p--;
|
|
481
|
+
const prevCh = p >= 0 ? source[p] : '';
|
|
482
|
+
if ('=(!,;:?[{&|^~+-><%'.includes(prevCh) || source.slice(Math.max(0, p - 5), p + 1).match(/\b(return|typeof|instanceof|in|of|void|delete|throw|new|case)\s*$/)) {
|
|
483
|
+
// This is a regex literal — skip to the closing /
|
|
484
|
+
pos++; // skip past opening /
|
|
485
|
+
while (pos < source.length) {
|
|
486
|
+
if (source[pos] === '\\') { pos++; } // skip escaped char
|
|
487
|
+
else if (source[pos] === '[') {
|
|
488
|
+
// Character class — skip to ]
|
|
489
|
+
pos++;
|
|
490
|
+
while (pos < source.length && source[pos] !== ']') {
|
|
491
|
+
if (source[pos] === '\\') pos++;
|
|
492
|
+
pos++;
|
|
493
|
+
}
|
|
494
|
+
} else if (source[pos] === '/') break;
|
|
495
|
+
pos++;
|
|
496
|
+
}
|
|
497
|
+
// Skip regex flags
|
|
498
|
+
while (pos + 1 < source.length && /[gimsuy]/.test(source[pos + 1])) pos++;
|
|
499
|
+
}
|
|
450
500
|
} else if (ch === '/' && pos + 1 < source.length && source[pos + 1] === '/') {
|
|
451
501
|
// Skip line comment
|
|
452
502
|
while (pos < source.length && source[pos] !== '\n') pos++;
|
package/src/vite-plugin.ts
CHANGED
|
@@ -578,13 +578,25 @@ export function findReassignments(source: string): Array<{ lineEnd: number; varN
|
|
|
578
578
|
const beforeOnLine = source.slice(lineStart, match.index).trim();
|
|
579
579
|
if (beforeOnLine.endsWith(':') || beforeOnLine.endsWith(',')) continue;
|
|
580
580
|
|
|
581
|
+
// Skip comma-separated multi-variable declaration continuations:
|
|
582
|
+
// var X = 'foo',
|
|
583
|
+
// Y = 'bar'; ← Y looks like a reassignment but is actually a declaration
|
|
584
|
+
// Detect by checking if the previous non-empty line ends with ','
|
|
585
|
+
if (beforeOnLine.length === 0) {
|
|
586
|
+
const prevLineEnd = source.lastIndexOf('\n', lineStart - 1);
|
|
587
|
+
if (prevLineEnd >= 0) {
|
|
588
|
+
const prevLine = source.slice(source.lastIndexOf('\n', prevLineEnd - 1) + 1, prevLineEnd).trimEnd();
|
|
589
|
+
if (prevLine.endsWith(',')) continue;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
581
593
|
// Calculate line number
|
|
582
594
|
let lineNo = 1;
|
|
583
595
|
for (let i = 0; i < match.index; i++) {
|
|
584
596
|
if (source[i] === '\n') lineNo++;
|
|
585
597
|
}
|
|
586
598
|
|
|
587
|
-
// Find end of statement
|
|
599
|
+
// Find end of statement
|
|
588
600
|
const startPos = match.index + match[0].length - 1;
|
|
589
601
|
let pos = startPos;
|
|
590
602
|
let depth = 0;
|
|
@@ -603,6 +615,19 @@ export function findReassignments(source: string): Array<{ lineEnd: number; varN
|
|
|
603
615
|
} else if (ch === '\n' && depth === 0) {
|
|
604
616
|
const nextNonWs = source.slice(pos + 1).match(/^\s*(\S)/);
|
|
605
617
|
if (nextNonWs && !'.+=-|&?:,'.includes(nextNonWs[1])) {
|
|
618
|
+
// Check if a recent non-empty line ends with an operator
|
|
619
|
+
let checkPos2 = pos;
|
|
620
|
+
let lastCh2 = '';
|
|
621
|
+
for (let back = 0; back < 5; back++) {
|
|
622
|
+
const prevNL2 = source.lastIndexOf('\n', checkPos2 - 1);
|
|
623
|
+
const prevLine2 = source.slice(prevNL2 + 1, checkPos2).trimEnd();
|
|
624
|
+
if (prevLine2.length > 0) { lastCh2 = prevLine2[prevLine2.length - 1]; break; }
|
|
625
|
+
checkPos2 = prevNL2;
|
|
626
|
+
if (prevNL2 <= 0) break;
|
|
627
|
+
}
|
|
628
|
+
if (lastCh2 && '=+-*/%&|^~<>?:,({['.includes(lastCh2)) {
|
|
629
|
+
pos++; continue;
|
|
630
|
+
}
|
|
606
631
|
foundEnd = pos;
|
|
607
632
|
break;
|
|
608
633
|
}
|
|
@@ -614,6 +639,21 @@ export function findReassignments(source: string): Array<{ lineEnd: number; varN
|
|
|
614
639
|
else if (source[pos] === quote) break;
|
|
615
640
|
pos++;
|
|
616
641
|
}
|
|
642
|
+
} else if (ch === '/' && pos + 1 < source.length && source[pos + 1] !== '/' && source[pos + 1] !== '*') {
|
|
643
|
+
// Possible regex literal
|
|
644
|
+
let rp = pos - 1;
|
|
645
|
+
while (rp >= 0 && (source[rp] === ' ' || source[rp] === '\t')) rp--;
|
|
646
|
+
const rpCh = rp >= 0 ? source[rp] : '';
|
|
647
|
+
if ('=(!,;:?[{&|^~+-><%'.includes(rpCh) || source.slice(Math.max(0, rp - 5), rp + 1).match(/\b(return|typeof|instanceof|in|of|void|delete|throw|new|case)\s*$/)) {
|
|
648
|
+
pos++;
|
|
649
|
+
while (pos < source.length) {
|
|
650
|
+
if (source[pos] === '\\') pos++;
|
|
651
|
+
else if (source[pos] === '[') { pos++; while (pos < source.length && source[pos] !== ']') { if (source[pos] === '\\') pos++; pos++; } }
|
|
652
|
+
else if (source[pos] === '/') break;
|
|
653
|
+
pos++;
|
|
654
|
+
}
|
|
655
|
+
while (pos + 1 < source.length && /[gimsuy]/.test(source[pos + 1])) pos++;
|
|
656
|
+
}
|
|
617
657
|
} else if (ch === '/' && pos + 1 < source.length && source[pos + 1] === '/') {
|
|
618
658
|
while (pos < source.length && source[pos] !== '\n') pos++;
|
|
619
659
|
continue;
|