astro-eslint-parser 2.0.0 → 3.0.0
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/lib/index.d.mts +113 -18
- package/lib/index.mjs +880 -1182
- package/package.json +10 -13
package/lib/index.mjs
CHANGED
|
@@ -8,12 +8,10 @@ import path from "path";
|
|
|
8
8
|
import * as fs$1 from "fs";
|
|
9
9
|
import fs from "fs";
|
|
10
10
|
import semver, { satisfies } from "semver";
|
|
11
|
-
import
|
|
12
|
-
import isGlob from "is-glob";
|
|
11
|
+
import { globSync, isDynamicPattern } from "tinyglobby";
|
|
13
12
|
import { Reference, Variable, analyze } from "@typescript-eslint/scope-manager";
|
|
14
13
|
import * as eslintScope$1 from "eslint-scope";
|
|
15
|
-
import {
|
|
16
|
-
import * as service from "astrojs-compiler-sync";
|
|
14
|
+
import { parse } from "@astrojs/compiler-rs";
|
|
17
15
|
const KEYS = unionWith({
|
|
18
16
|
Program: ["body"],
|
|
19
17
|
AstroFragment: ["children"],
|
|
@@ -63,13 +61,15 @@ function getTSProgram(code, options, ts) {
|
|
|
63
61
|
return service.getProgram(code, options.filePath);
|
|
64
62
|
}
|
|
65
63
|
var TSService = class {
|
|
64
|
+
watch;
|
|
65
|
+
patchedHostSet = /* @__PURE__ */ new WeakSet();
|
|
66
|
+
currTarget = {
|
|
67
|
+
code: "",
|
|
68
|
+
filePath: ""
|
|
69
|
+
};
|
|
70
|
+
fileWatchCallbacks = /* @__PURE__ */ new Map();
|
|
71
|
+
ts;
|
|
66
72
|
constructor(tsconfigPath, ts) {
|
|
67
|
-
this.patchedHostSet = /* @__PURE__ */ new WeakSet();
|
|
68
|
-
this.currTarget = {
|
|
69
|
-
code: "",
|
|
70
|
-
filePath: ""
|
|
71
|
-
};
|
|
72
|
-
this.fileWatchCallbacks = /* @__PURE__ */ new Map();
|
|
73
73
|
this.ts = ts;
|
|
74
74
|
this.watch = this.createWatch(tsconfigPath);
|
|
75
75
|
}
|
|
@@ -177,7 +177,6 @@ function toAbsolutePath(filePath, baseDir) {
|
|
|
177
177
|
* Code from: https://github.com/typescript-eslint/typescript-eslint/blob/v5.62.0/packages/typescript-estree/src/parseSettings/resolveProjectList.ts
|
|
178
178
|
* Code updated to exclude caching.
|
|
179
179
|
*/
|
|
180
|
-
const { sync: globSync } = fastGlob;
|
|
181
180
|
/**
|
|
182
181
|
* Normalizes, sanitizes, resolves and filters the provided project paths
|
|
183
182
|
*/
|
|
@@ -188,13 +187,14 @@ function resolveProjectList(options) {
|
|
|
188
187
|
for (const project of options.project) if (typeof project === "string") sanitizedProjects.push(project);
|
|
189
188
|
}
|
|
190
189
|
if (sanitizedProjects.length === 0) return [];
|
|
191
|
-
const projectFolderIgnoreList = (options.projectFolderIgnoreList ?? ["**/node_modules/**"]).
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
190
|
+
const projectFolderIgnoreList = (options.projectFolderIgnoreList ?? ["**/node_modules/**"]).filter((folder) => typeof folder === "string");
|
|
191
|
+
const nonGlobProjects = sanitizedProjects.filter((project) => !isDynamicPattern(project));
|
|
192
|
+
const globProjects = sanitizedProjects.filter((project) => isDynamicPattern(project));
|
|
193
|
+
const uniqueCanonicalProjectPaths = new Set(nonGlobProjects.concat(globProjects.length === 0 ? [] : globSync(globProjects, {
|
|
194
|
+
cwd: options.tsconfigRootDir,
|
|
195
|
+
expandDirectories: false,
|
|
196
|
+
ignore: projectFolderIgnoreList
|
|
197
|
+
})).map((project) => getCanonicalFileName(ensureAbsolutePath(project, options.tsconfigRootDir))));
|
|
198
198
|
return Array.from(uniqueCanonicalProjectPaths);
|
|
199
199
|
}
|
|
200
200
|
/**
|
|
@@ -395,15 +395,15 @@ function getKeys(node, visitorKeys) {
|
|
|
395
395
|
function* getNodes(node, key) {
|
|
396
396
|
const child = node[key];
|
|
397
397
|
if (Array.isArray(child)) {
|
|
398
|
-
for (const c of child) if (isNode(c)) yield c;
|
|
399
|
-
} else if (isNode(child)) yield child;
|
|
398
|
+
for (const c of child) if (isNode$1(c)) yield c;
|
|
399
|
+
} else if (isNode$1(child)) yield child;
|
|
400
400
|
}
|
|
401
401
|
/**
|
|
402
402
|
* Check whether a given value is a node.
|
|
403
403
|
* @param x The value to check.
|
|
404
404
|
* @returns `true` if the value is a node.
|
|
405
405
|
*/
|
|
406
|
-
function isNode(x) {
|
|
406
|
+
function isNode$1(x) {
|
|
407
407
|
return x !== null && typeof x === "object" && typeof x.type === "string";
|
|
408
408
|
}
|
|
409
409
|
/**
|
|
@@ -427,248 +427,6 @@ function traverseNodes(node, visitor) {
|
|
|
427
427
|
traverse(node, null, visitor);
|
|
428
428
|
}
|
|
429
429
|
//#endregion
|
|
430
|
-
//#region src/util/index.ts
|
|
431
|
-
/**
|
|
432
|
-
* Uses a binary search to determine the highest index at which value should be inserted into array in order to maintain its sort order.
|
|
433
|
-
*/
|
|
434
|
-
function sortedLastIndex(array, compare) {
|
|
435
|
-
let lower = 0;
|
|
436
|
-
let upper = array.length;
|
|
437
|
-
while (lower < upper) {
|
|
438
|
-
const mid = Math.floor(lower + (upper - lower) / 2);
|
|
439
|
-
const target = compare(array[mid]);
|
|
440
|
-
if (target < 0) lower = mid + 1;
|
|
441
|
-
else if (target > 0) upper = mid;
|
|
442
|
-
else return mid + 1;
|
|
443
|
-
}
|
|
444
|
-
return upper;
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* Add element to a sorted array
|
|
448
|
-
*/
|
|
449
|
-
function addElementToSortedArray(array, element, compare) {
|
|
450
|
-
const index = sortedLastIndex(array, (target) => compare(target, element));
|
|
451
|
-
array.splice(index, 0, element);
|
|
452
|
-
}
|
|
453
|
-
/**
|
|
454
|
-
* Add element to a sorted array
|
|
455
|
-
*/
|
|
456
|
-
function addElementsToSortedArray(array, elements, compare) {
|
|
457
|
-
if (!elements.length) return;
|
|
458
|
-
let last = elements[0];
|
|
459
|
-
let index = sortedLastIndex(array, (target) => compare(target, last));
|
|
460
|
-
for (const element of elements) {
|
|
461
|
-
if (compare(last, element) > 0) index = sortedLastIndex(array, (target) => compare(target, element));
|
|
462
|
-
let e = array[index];
|
|
463
|
-
while (e && compare(e, element) <= 0) e = array[++index];
|
|
464
|
-
array.splice(index, 0, element);
|
|
465
|
-
last = element;
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
const WRITE_FLAG = 2;
|
|
469
|
-
const READ_WRITE_FLAG = 3;
|
|
470
|
-
const REFERENCE_TYPE_TYPE_FLAG = 2;
|
|
471
|
-
/**
|
|
472
|
-
* Gets the scope for the Program node
|
|
473
|
-
*/
|
|
474
|
-
function getProgramScope(scopeManager) {
|
|
475
|
-
const globalScope = scopeManager.globalScope;
|
|
476
|
-
return globalScope.childScopes.find((s) => s.type === "module") || globalScope;
|
|
477
|
-
}
|
|
478
|
-
/** Remove all scope, variable, and reference */
|
|
479
|
-
function removeAllScopeAndVariableAndReference(target, info) {
|
|
480
|
-
const removeTargetScopes = /* @__PURE__ */ new Set();
|
|
481
|
-
traverseNodes(target, {
|
|
482
|
-
visitorKeys: info.visitorKeys,
|
|
483
|
-
enterNode(node) {
|
|
484
|
-
const scope = info.scopeManager.acquire(node);
|
|
485
|
-
if (scope) {
|
|
486
|
-
removeTargetScopes.add(scope);
|
|
487
|
-
return;
|
|
488
|
-
}
|
|
489
|
-
if (node.type === "Identifier" || node.type === "JSXIdentifier") {
|
|
490
|
-
let targetScope = getInnermostScopeFromNode(info.scopeManager, node);
|
|
491
|
-
while (targetScope && targetScope.block.type !== "Program" && target.range[0] <= targetScope.block.range[0] && targetScope.block.range[1] <= target.range[1]) targetScope = targetScope.upper;
|
|
492
|
-
if (removeTargetScopes.has(targetScope)) return;
|
|
493
|
-
removeIdentifierVariable(node, targetScope);
|
|
494
|
-
removeIdentifierReference(node, targetScope);
|
|
495
|
-
}
|
|
496
|
-
},
|
|
497
|
-
leaveNode() {}
|
|
498
|
-
});
|
|
499
|
-
for (const scope of removeTargetScopes) removeScope(info.scopeManager, scope);
|
|
500
|
-
}
|
|
501
|
-
/**
|
|
502
|
-
* Add the virtual reference.
|
|
503
|
-
*/
|
|
504
|
-
function addVirtualReference(node, variable, scope, status) {
|
|
505
|
-
const reference = new Reference(node, scope, status.write && status.read ? READ_WRITE_FLAG : status.write ? WRITE_FLAG : 1, void 0, void 0, void 0, status.typeRef ? REFERENCE_TYPE_TYPE_FLAG : 1);
|
|
506
|
-
reference.astroVirtualReference = true;
|
|
507
|
-
addReference(variable.references, reference);
|
|
508
|
-
reference.resolved = variable;
|
|
509
|
-
if (status.forceUsed) variable.eslintUsed = true;
|
|
510
|
-
return reference;
|
|
511
|
-
}
|
|
512
|
-
/**
|
|
513
|
-
* Add global variable
|
|
514
|
-
*/
|
|
515
|
-
function addGlobalVariable(reference, scopeManager) {
|
|
516
|
-
const globalScope = scopeManager.globalScope;
|
|
517
|
-
const name = reference.identifier.name;
|
|
518
|
-
let variable = globalScope.set.get(name);
|
|
519
|
-
if (!variable) {
|
|
520
|
-
variable = new Variable(name, globalScope);
|
|
521
|
-
globalScope.variables.push(variable);
|
|
522
|
-
globalScope.set.set(name, variable);
|
|
523
|
-
}
|
|
524
|
-
reference.resolved = variable;
|
|
525
|
-
variable.references.push(reference);
|
|
526
|
-
return variable;
|
|
527
|
-
}
|
|
528
|
-
/** Remove reference from through */
|
|
529
|
-
function removeReferenceFromThrough(reference, baseScope) {
|
|
530
|
-
const variable = reference.resolved;
|
|
531
|
-
const name = reference.identifier.name;
|
|
532
|
-
let scope = baseScope;
|
|
533
|
-
while (scope) {
|
|
534
|
-
for (const ref of [...scope.through]) if (reference === ref) scope.through.splice(scope.through.indexOf(ref), 1);
|
|
535
|
-
else if (ref.identifier.name === name) {
|
|
536
|
-
ref.resolved = variable;
|
|
537
|
-
if (!variable.references.includes(ref)) addReference(variable.references, ref);
|
|
538
|
-
scope.through.splice(scope.through.indexOf(ref), 1);
|
|
539
|
-
}
|
|
540
|
-
scope = scope.upper;
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
/** Remove scope */
|
|
544
|
-
function removeScope(scopeManager, scope) {
|
|
545
|
-
for (const childScope of scope.childScopes) removeScope(scopeManager, childScope);
|
|
546
|
-
while (scope.references[0]) removeReference(scope.references[0], scope);
|
|
547
|
-
const upper = scope.upper;
|
|
548
|
-
if (upper) {
|
|
549
|
-
const index = upper.childScopes.indexOf(scope);
|
|
550
|
-
if (index >= 0) upper.childScopes.splice(index, 1);
|
|
551
|
-
}
|
|
552
|
-
const index = scopeManager.scopes.indexOf(scope);
|
|
553
|
-
if (index >= 0) scopeManager.scopes.splice(index, 1);
|
|
554
|
-
}
|
|
555
|
-
/** Remove reference */
|
|
556
|
-
function removeReference(reference, baseScope) {
|
|
557
|
-
if (reference.resolved) if (reference.resolved.defs.some((d) => d.name === reference.identifier)) {
|
|
558
|
-
const varIndex = baseScope.variables.indexOf(reference.resolved);
|
|
559
|
-
if (varIndex >= 0) baseScope.variables.splice(varIndex, 1);
|
|
560
|
-
const name = reference.identifier.name;
|
|
561
|
-
if (reference.resolved === baseScope.set.get(name)) baseScope.set.delete(name);
|
|
562
|
-
} else {
|
|
563
|
-
const refIndex = reference.resolved.references.indexOf(reference);
|
|
564
|
-
if (refIndex >= 0) reference.resolved.references.splice(refIndex, 1);
|
|
565
|
-
}
|
|
566
|
-
let scope = baseScope;
|
|
567
|
-
while (scope) {
|
|
568
|
-
const refIndex = scope.references.indexOf(reference);
|
|
569
|
-
if (refIndex >= 0) scope.references.splice(refIndex, 1);
|
|
570
|
-
const throughIndex = scope.through.indexOf(reference);
|
|
571
|
-
if (throughIndex >= 0) scope.through.splice(throughIndex, 1);
|
|
572
|
-
scope = scope.upper;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
/** Remove variable */
|
|
576
|
-
function removeIdentifierVariable(node, scope) {
|
|
577
|
-
for (let varIndex = 0; varIndex < scope.variables.length; varIndex++) {
|
|
578
|
-
const variable = scope.variables[varIndex];
|
|
579
|
-
const defIndex = variable.defs.findIndex((def) => def.name === node);
|
|
580
|
-
if (defIndex < 0) continue;
|
|
581
|
-
variable.defs.splice(defIndex, 1);
|
|
582
|
-
if (variable.defs.length === 0) {
|
|
583
|
-
referencesToThrough(variable.references, scope);
|
|
584
|
-
variable.references.forEach((r) => {
|
|
585
|
-
if (r.init) r.init = false;
|
|
586
|
-
r.resolved = null;
|
|
587
|
-
});
|
|
588
|
-
scope.variables.splice(varIndex, 1);
|
|
589
|
-
const name = node.name;
|
|
590
|
-
if (variable === scope.set.get(name)) scope.set.delete(name);
|
|
591
|
-
} else {
|
|
592
|
-
const idIndex = variable.identifiers.indexOf(node);
|
|
593
|
-
if (idIndex >= 0) variable.identifiers.splice(idIndex, 1);
|
|
594
|
-
}
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
/** Remove reference */
|
|
599
|
-
function removeIdentifierReference(node, scope) {
|
|
600
|
-
const reference = scope.references.find((ref) => ref.identifier === node);
|
|
601
|
-
if (reference) {
|
|
602
|
-
removeReference(reference, scope);
|
|
603
|
-
return true;
|
|
604
|
-
}
|
|
605
|
-
const location = node.range[0];
|
|
606
|
-
const pendingScopes = [];
|
|
607
|
-
for (const childScope of scope.childScopes) {
|
|
608
|
-
const range = childScope.block.range;
|
|
609
|
-
if (range[0] <= location && location < range[1]) {
|
|
610
|
-
if (removeIdentifierReference(node, childScope)) return true;
|
|
611
|
-
} else pendingScopes.push(childScope);
|
|
612
|
-
}
|
|
613
|
-
for (const childScope of pendingScopes) if (removeIdentifierReference(node, childScope)) return true;
|
|
614
|
-
return false;
|
|
615
|
-
}
|
|
616
|
-
/**
|
|
617
|
-
* Get the innermost scope which contains a given node.
|
|
618
|
-
* @returns The innermost scope.
|
|
619
|
-
*/
|
|
620
|
-
function getInnermostScopeFromNode(scopeManager, currentNode) {
|
|
621
|
-
return getInnermostScope(getScopeFromNode(scopeManager, currentNode), currentNode);
|
|
622
|
-
}
|
|
623
|
-
/**
|
|
624
|
-
* Gets the scope for the current node
|
|
625
|
-
*/
|
|
626
|
-
function getScopeFromNode(scopeManager, currentNode) {
|
|
627
|
-
let node = currentNode;
|
|
628
|
-
for (; node; node = node.parent || null) {
|
|
629
|
-
const scope = scopeManager.acquire(node, false);
|
|
630
|
-
if (scope) {
|
|
631
|
-
if (scope.type === "function-expression-name") return scope.childScopes[0];
|
|
632
|
-
if (scope.type === "global" && node.type === "Program" && node.sourceType === "module") return scope.childScopes.find((s) => s.type === "module") || scope;
|
|
633
|
-
return scope;
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
return scopeManager.globalScope;
|
|
637
|
-
}
|
|
638
|
-
/**
|
|
639
|
-
* Get the innermost scope which contains a given location.
|
|
640
|
-
* @param initialScope The initial scope to search.
|
|
641
|
-
* @param node The location to search.
|
|
642
|
-
* @returns The innermost scope.
|
|
643
|
-
*/
|
|
644
|
-
function getInnermostScope(initialScope, node) {
|
|
645
|
-
for (const childScope of initialScope.childScopes) {
|
|
646
|
-
const range = childScope.block.range;
|
|
647
|
-
if (range[0] <= node.range[0] && node.range[1] <= range[1]) return getInnermostScope(childScope, node);
|
|
648
|
-
}
|
|
649
|
-
return initialScope;
|
|
650
|
-
}
|
|
651
|
-
/** Move reference to through */
|
|
652
|
-
function referencesToThrough(references, baseScope) {
|
|
653
|
-
let scope = baseScope;
|
|
654
|
-
while (scope) {
|
|
655
|
-
addAllReferences(scope.through, references);
|
|
656
|
-
scope = scope.upper;
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
/**
|
|
660
|
-
* Add all references to array
|
|
661
|
-
*/
|
|
662
|
-
function addAllReferences(list, elements) {
|
|
663
|
-
addElementsToSortedArray(list, elements, (a, b) => a.identifier.range[0] - b.identifier.range[0]);
|
|
664
|
-
}
|
|
665
|
-
/**
|
|
666
|
-
* Add reference to array
|
|
667
|
-
*/
|
|
668
|
-
function addReference(list, reference) {
|
|
669
|
-
addElementToSortedArray(list, reference, (a, b) => a.identifier.range[0] - b.identifier.range[0]);
|
|
670
|
-
}
|
|
671
|
-
//#endregion
|
|
672
430
|
//#region src/parser/eslint-scope.ts
|
|
673
431
|
let eslintScopeCache = null;
|
|
674
432
|
/**
|
|
@@ -775,31 +533,6 @@ ${code}`);
|
|
|
775
533
|
patchResult?.terminate();
|
|
776
534
|
}
|
|
777
535
|
}
|
|
778
|
-
var Referencer = class extends eslintScope.Referencer {
|
|
779
|
-
JSXAttribute(node) {
|
|
780
|
-
this.visit(node.value);
|
|
781
|
-
}
|
|
782
|
-
JSXClosingElement() {}
|
|
783
|
-
JSXFragment(node) {
|
|
784
|
-
this.visitChildren(node);
|
|
785
|
-
}
|
|
786
|
-
JSXIdentifier(node) {
|
|
787
|
-
const scope = this.currentScope();
|
|
788
|
-
const ref = new Reference(node, scope, 1, void 0, void 0, false, 1);
|
|
789
|
-
scope.references.push(ref);
|
|
790
|
-
scope.__left.push(ref);
|
|
791
|
-
}
|
|
792
|
-
JSXMemberExpression(node) {
|
|
793
|
-
if (node.object.type !== AST_NODE_TYPES.JSXIdentifier) this.visit(node.object);
|
|
794
|
-
else if (node.object.name !== "this") this.visit(node.object);
|
|
795
|
-
}
|
|
796
|
-
JSXOpeningElement(node) {
|
|
797
|
-
if (node.name.type === AST_NODE_TYPES.JSXIdentifier) {
|
|
798
|
-
if (node.name.name[0].toUpperCase() === node.name.name[0] || node.name.name === "this") this.visit(node.name);
|
|
799
|
-
} else this.visit(node.name);
|
|
800
|
-
for (const attr of node.attributes) this.visit(attr);
|
|
801
|
-
}
|
|
802
|
-
};
|
|
803
536
|
/**
|
|
804
537
|
* Analyzed scopes for JavaScript.
|
|
805
538
|
*/
|
|
@@ -811,11 +544,10 @@ function analyzeForEcmaScript(tree, providedOptions) {
|
|
|
811
544
|
sourceType: "script",
|
|
812
545
|
ecmaVersion: 5,
|
|
813
546
|
childVisitorKeys: null,
|
|
814
|
-
fallback: "iteration"
|
|
547
|
+
fallback: "iteration",
|
|
548
|
+
jsx: true
|
|
815
549
|
}, providedOptions);
|
|
816
|
-
|
|
817
|
-
new Referencer(options, scopeManager).visit(tree);
|
|
818
|
-
return scopeManager;
|
|
550
|
+
return eslintScope.analyze(tree, options);
|
|
819
551
|
}
|
|
820
552
|
//#endregion
|
|
821
553
|
//#region src/parser/sort.ts
|
|
@@ -829,403 +561,12 @@ function sort(tokens) {
|
|
|
829
561
|
});
|
|
830
562
|
}
|
|
831
563
|
//#endregion
|
|
832
|
-
//#region src/errors.ts
|
|
833
|
-
/**
|
|
834
|
-
* Astro parse errors.
|
|
835
|
-
*/
|
|
836
|
-
var ParseError = class extends SyntaxError {
|
|
837
|
-
/**
|
|
838
|
-
* Initialize this ParseError instance.
|
|
839
|
-
*/
|
|
840
|
-
constructor(message, offset, ctx) {
|
|
841
|
-
super(message);
|
|
842
|
-
if (typeof offset === "number") {
|
|
843
|
-
this.index = offset;
|
|
844
|
-
const loc = ctx.getLocFromIndex(offset);
|
|
845
|
-
this.lineNumber = loc.line;
|
|
846
|
-
this.column = loc.column;
|
|
847
|
-
} else {
|
|
848
|
-
this.index = ctx.getIndexFromLoc(offset);
|
|
849
|
-
this.lineNumber = offset.line;
|
|
850
|
-
this.column = offset.column;
|
|
851
|
-
}
|
|
852
|
-
this.originalAST = ctx.originalAST;
|
|
853
|
-
}
|
|
854
|
-
};
|
|
855
|
-
//#endregion
|
|
856
|
-
//#region src/astro/index.ts
|
|
857
|
-
/**
|
|
858
|
-
* Checks if the given node is TagLikeNode
|
|
859
|
-
*/
|
|
860
|
-
function isTag(node) {
|
|
861
|
-
return node.type === "element" || node.type === "custom-element" || node.type === "component" || node.type === "fragment";
|
|
862
|
-
}
|
|
863
|
-
/**
|
|
864
|
-
* Checks if the given node is ParentNode
|
|
865
|
-
*/
|
|
866
|
-
function isParent(node) {
|
|
867
|
-
return Array.isArray(node.children);
|
|
868
|
-
}
|
|
869
|
-
/** walk element nodes */
|
|
870
|
-
function walkElements(parent, code, enter, leave, parents = []) {
|
|
871
|
-
const children = getSortedChildren(parent, code);
|
|
872
|
-
const currParents = [parent, ...parents];
|
|
873
|
-
for (const node of children) {
|
|
874
|
-
enter(node, currParents);
|
|
875
|
-
if (isParent(node)) walkElements(node, code, enter, leave, currParents);
|
|
876
|
-
leave(node, currParents);
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
/** walk nodes */
|
|
880
|
-
function walk(parent, code, enter, leave) {
|
|
881
|
-
walkElements(parent, code, (node, parents) => {
|
|
882
|
-
enter(node, parents);
|
|
883
|
-
if (isTag(node)) {
|
|
884
|
-
const attrParents = [node, ...parents];
|
|
885
|
-
for (const attr of node.attributes) {
|
|
886
|
-
enter(attr, attrParents);
|
|
887
|
-
leave(attr, attrParents);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
}, leave);
|
|
891
|
-
}
|
|
892
|
-
/**
|
|
893
|
-
* Get end offset of start tag
|
|
894
|
-
*/
|
|
895
|
-
function calcStartTagEndOffset(node, ctx) {
|
|
896
|
-
const lastAttr = node.attributes[node.attributes.length - 1];
|
|
897
|
-
let beforeCloseIndex;
|
|
898
|
-
if (lastAttr) beforeCloseIndex = calcAttributeEndOffset(lastAttr, ctx);
|
|
899
|
-
else {
|
|
900
|
-
const info = getTokenInfo(ctx, [`<${node.name}`], node.position.start.offset);
|
|
901
|
-
beforeCloseIndex = info.index + info.match.length;
|
|
902
|
-
}
|
|
903
|
-
const info = getTokenInfo(ctx, [[">", "/>"]], beforeCloseIndex);
|
|
904
|
-
return info.index + info.match.length;
|
|
905
|
-
}
|
|
906
|
-
/**
|
|
907
|
-
* Get end offset of attribute
|
|
908
|
-
*/
|
|
909
|
-
function calcAttributeEndOffset(node, ctx) {
|
|
910
|
-
let info;
|
|
911
|
-
if (node.kind === "empty") info = getTokenInfo(ctx, [node.name], node.position.start.offset);
|
|
912
|
-
else if (node.kind === "quoted") info = getTokenInfo(ctx, [[
|
|
913
|
-
{
|
|
914
|
-
token: `"${node.value}"`,
|
|
915
|
-
htmlEntityDecode: true
|
|
916
|
-
},
|
|
917
|
-
{
|
|
918
|
-
token: `'${node.value}'`,
|
|
919
|
-
htmlEntityDecode: true
|
|
920
|
-
},
|
|
921
|
-
{
|
|
922
|
-
token: node.value,
|
|
923
|
-
htmlEntityDecode: true
|
|
924
|
-
}
|
|
925
|
-
]], calcAttributeValueStartOffset(node, ctx));
|
|
926
|
-
else if (node.kind === "expression") info = getTokenInfo(ctx, [
|
|
927
|
-
"{",
|
|
928
|
-
node.value,
|
|
929
|
-
"}"
|
|
930
|
-
], calcAttributeValueStartOffset(node, ctx));
|
|
931
|
-
else if (node.kind === "shorthand") info = getTokenInfo(ctx, [
|
|
932
|
-
"{",
|
|
933
|
-
node.name,
|
|
934
|
-
"}"
|
|
935
|
-
], node.position.start.offset);
|
|
936
|
-
else if (node.kind === "spread") info = getTokenInfo(ctx, [
|
|
937
|
-
"{",
|
|
938
|
-
"...",
|
|
939
|
-
node.name,
|
|
940
|
-
"}"
|
|
941
|
-
], node.position.start.offset);
|
|
942
|
-
else if (node.kind === "template-literal") info = getTokenInfo(ctx, [`\`${node.value}\``], calcAttributeValueStartOffset(node, ctx));
|
|
943
|
-
else throw new ParseError(`Unknown attr kind: ${node.kind}`, node.position.start.offset, ctx);
|
|
944
|
-
return info.index + info.match.length;
|
|
945
|
-
}
|
|
946
|
-
/**
|
|
947
|
-
* Get start offset of attribute value
|
|
948
|
-
*/
|
|
949
|
-
function calcAttributeValueStartOffset(node, ctx) {
|
|
950
|
-
let info;
|
|
951
|
-
if (node.kind === "quoted") info = getTokenInfo(ctx, [
|
|
952
|
-
node.name,
|
|
953
|
-
"=",
|
|
954
|
-
[
|
|
955
|
-
`"`,
|
|
956
|
-
`'`,
|
|
957
|
-
{
|
|
958
|
-
token: node.value,
|
|
959
|
-
htmlEntityDecode: true
|
|
960
|
-
}
|
|
961
|
-
]
|
|
962
|
-
], node.position.start.offset);
|
|
963
|
-
else if (node.kind === "expression") info = getTokenInfo(ctx, [
|
|
964
|
-
node.name,
|
|
965
|
-
"=",
|
|
966
|
-
"{"
|
|
967
|
-
], node.position.start.offset);
|
|
968
|
-
else if (node.kind === "template-literal") info = getTokenInfo(ctx, [
|
|
969
|
-
node.name,
|
|
970
|
-
"=",
|
|
971
|
-
"`"
|
|
972
|
-
], node.position.start.offset);
|
|
973
|
-
else throw new ParseError(`Unknown attr kind: ${node.kind}`, node.position.start.offset, ctx);
|
|
974
|
-
return info.index;
|
|
975
|
-
}
|
|
976
|
-
/**
|
|
977
|
-
* Get end offset of tag
|
|
978
|
-
*/
|
|
979
|
-
function getEndOffset(node, ctx) {
|
|
980
|
-
if (node.position.end?.offset != null) return node.position.end.offset;
|
|
981
|
-
if (isTag(node)) return calcTagEndOffset(node, ctx);
|
|
982
|
-
if (node.type === "expression") return calcExpressionEndOffset(node, ctx);
|
|
983
|
-
if (node.type === "comment") return calcCommentEndOffset(node, ctx);
|
|
984
|
-
if (node.type === "frontmatter") {
|
|
985
|
-
const start = node.position.start.offset;
|
|
986
|
-
return ctx.code.indexOf("---", start + 3) + 3;
|
|
987
|
-
}
|
|
988
|
-
if (node.type === "doctype") {
|
|
989
|
-
const start = node.position.start.offset;
|
|
990
|
-
return ctx.code.indexOf(">", start) + 1;
|
|
991
|
-
}
|
|
992
|
-
if (node.type === "text") return node.position.start.offset + node.value.length;
|
|
993
|
-
if (node.type === "root") return ctx.code.length;
|
|
994
|
-
throw new Error(`unknown type: ${node.type}`);
|
|
995
|
-
}
|
|
996
|
-
/**
|
|
997
|
-
* Get content end offset
|
|
998
|
-
*/
|
|
999
|
-
function calcContentEndOffset(parent, ctx) {
|
|
1000
|
-
const code = ctx.code;
|
|
1001
|
-
if (isTag(parent)) {
|
|
1002
|
-
const end = getEndOffset(parent, ctx);
|
|
1003
|
-
if (code[end - 1] !== ">") return end;
|
|
1004
|
-
const index = code.lastIndexOf("</", end - 1);
|
|
1005
|
-
if (index >= 0 && code.slice(index + 2, end - 1).trim() === parent.name) return index;
|
|
1006
|
-
return end;
|
|
1007
|
-
} else if (parent.type === "expression") {
|
|
1008
|
-
const end = getEndOffset(parent, ctx);
|
|
1009
|
-
return code.lastIndexOf("}", end);
|
|
1010
|
-
} else if (parent.type === "root") return code.length;
|
|
1011
|
-
throw new Error(`unknown type: ${parent.type}`);
|
|
1012
|
-
}
|
|
1013
|
-
/**
|
|
1014
|
-
* If the given tag is a self-close tag, get the self-closing tag.
|
|
1015
|
-
*/
|
|
1016
|
-
function getSelfClosingTag(node, ctx) {
|
|
1017
|
-
if (node.children.length > 0) return null;
|
|
1018
|
-
const code = ctx.code;
|
|
1019
|
-
const startTagEndOffset = calcStartTagEndOffset(node, ctx);
|
|
1020
|
-
if (code.startsWith("/>", startTagEndOffset - 2)) return {
|
|
1021
|
-
offset: startTagEndOffset,
|
|
1022
|
-
end: "/>"
|
|
1023
|
-
};
|
|
1024
|
-
if (code.startsWith(`</${node.name}`, startTagEndOffset)) return null;
|
|
1025
|
-
return {
|
|
1026
|
-
offset: startTagEndOffset,
|
|
1027
|
-
end: ">"
|
|
1028
|
-
};
|
|
1029
|
-
}
|
|
1030
|
-
/**
|
|
1031
|
-
* If the given tag has a end tag, get the end tag.
|
|
1032
|
-
*/
|
|
1033
|
-
function getEndTag(node, ctx) {
|
|
1034
|
-
let beforeIndex;
|
|
1035
|
-
if (node.children.length) {
|
|
1036
|
-
const lastChild = node.children[node.children.length - 1];
|
|
1037
|
-
beforeIndex = getEndOffset(lastChild, ctx);
|
|
1038
|
-
} else beforeIndex = calcStartTagEndOffset(node, ctx);
|
|
1039
|
-
beforeIndex = skipSpaces(ctx.code, beforeIndex);
|
|
1040
|
-
if (ctx.code.startsWith(`</${node.name}`, beforeIndex)) {
|
|
1041
|
-
const offset = beforeIndex;
|
|
1042
|
-
beforeIndex = beforeIndex + 2 + node.name.length;
|
|
1043
|
-
const info = getTokenInfo(ctx, [">"], beforeIndex);
|
|
1044
|
-
const end = info.index + info.match.length;
|
|
1045
|
-
return {
|
|
1046
|
-
offset,
|
|
1047
|
-
tag: ctx.code.slice(offset, end)
|
|
1048
|
-
};
|
|
1049
|
-
}
|
|
1050
|
-
return null;
|
|
1051
|
-
}
|
|
1052
|
-
/**
|
|
1053
|
-
* Get end offset of comment
|
|
1054
|
-
*/
|
|
1055
|
-
function calcCommentEndOffset(node, ctx) {
|
|
1056
|
-
const info = getTokenInfo(ctx, [
|
|
1057
|
-
"<!--",
|
|
1058
|
-
node.value,
|
|
1059
|
-
"-->"
|
|
1060
|
-
], node.position.start.offset);
|
|
1061
|
-
return info.index + info.match.length;
|
|
1062
|
-
}
|
|
1063
|
-
/**
|
|
1064
|
-
* Get end offset of tag
|
|
1065
|
-
*/
|
|
1066
|
-
function calcTagEndOffset(node, ctx) {
|
|
1067
|
-
let beforeIndex;
|
|
1068
|
-
if (node.children.length) {
|
|
1069
|
-
const lastChild = node.children[node.children.length - 1];
|
|
1070
|
-
beforeIndex = getEndOffset(lastChild, ctx);
|
|
1071
|
-
} else beforeIndex = calcStartTagEndOffset(node, ctx);
|
|
1072
|
-
beforeIndex = skipSpaces(ctx.code, beforeIndex);
|
|
1073
|
-
if (ctx.code.startsWith(`</${node.name}`, beforeIndex)) {
|
|
1074
|
-
beforeIndex = beforeIndex + 2 + node.name.length;
|
|
1075
|
-
const info = getTokenInfo(ctx, [">"], beforeIndex);
|
|
1076
|
-
return info.index + info.match.length;
|
|
1077
|
-
}
|
|
1078
|
-
return beforeIndex;
|
|
1079
|
-
}
|
|
1080
|
-
/**
|
|
1081
|
-
* Get end offset of Expression
|
|
1082
|
-
*/
|
|
1083
|
-
function calcExpressionEndOffset(node, ctx) {
|
|
1084
|
-
if (node.children.length) {
|
|
1085
|
-
const lastChild = node.children[node.children.length - 1];
|
|
1086
|
-
const info = getTokenInfo(ctx, ["}"], getEndOffset(lastChild, ctx));
|
|
1087
|
-
return info.index + info.match.length;
|
|
1088
|
-
}
|
|
1089
|
-
const info = getTokenInfo(ctx, ["{", "}"], node.position.start.offset);
|
|
1090
|
-
return info.index + info.match.length;
|
|
1091
|
-
}
|
|
1092
|
-
/**
|
|
1093
|
-
* Get token info
|
|
1094
|
-
*/
|
|
1095
|
-
function getTokenInfo(ctx, tokens, position) {
|
|
1096
|
-
let lastMatch;
|
|
1097
|
-
for (const t of tokens) {
|
|
1098
|
-
const index = lastMatch ? lastMatch.index + lastMatch.match.length : position;
|
|
1099
|
-
const m = Array.isArray(t) ? matchOfForMulti(t, index) : match(t, index);
|
|
1100
|
-
if (m == null) throw new ParseError(`Unknown token at ${index}, expected: ${JSON.stringify(t)}, actual: ${JSON.stringify(ctx.code.slice(index, index + 10))}`, index, ctx);
|
|
1101
|
-
lastMatch = m;
|
|
1102
|
-
}
|
|
1103
|
-
return lastMatch;
|
|
1104
|
-
/**
|
|
1105
|
-
* For Single Token
|
|
1106
|
-
*/
|
|
1107
|
-
function match(token, position) {
|
|
1108
|
-
const search = typeof token === "string" ? token : token.token;
|
|
1109
|
-
const index = search.trim() === search ? skipSpaces(ctx.code, position) : position;
|
|
1110
|
-
if (ctx.code.startsWith(search, index)) return {
|
|
1111
|
-
match: search,
|
|
1112
|
-
index
|
|
1113
|
-
};
|
|
1114
|
-
if (typeof token !== "string") return matchWithHTMLEntity(token, index);
|
|
1115
|
-
return null;
|
|
1116
|
-
}
|
|
1117
|
-
/**
|
|
1118
|
-
* For Multiple Token
|
|
1119
|
-
*/
|
|
1120
|
-
function matchOfForMulti(search, position) {
|
|
1121
|
-
for (const s of search) {
|
|
1122
|
-
const m = match(s, position);
|
|
1123
|
-
if (m) return m;
|
|
1124
|
-
}
|
|
1125
|
-
return null;
|
|
1126
|
-
}
|
|
1127
|
-
/**
|
|
1128
|
-
* With HTML entity
|
|
1129
|
-
*/
|
|
1130
|
-
function matchWithHTMLEntity(token, position) {
|
|
1131
|
-
const search = token.token;
|
|
1132
|
-
let codeOffset = position;
|
|
1133
|
-
let searchOffset = 0;
|
|
1134
|
-
while (searchOffset < search.length) {
|
|
1135
|
-
const searchChar = search[searchOffset];
|
|
1136
|
-
if (ctx.code[codeOffset] === searchChar) {
|
|
1137
|
-
if (searchChar === "&") {
|
|
1138
|
-
const entityCandidate = ctx.code.slice(codeOffset, codeOffset + 5);
|
|
1139
|
-
if (entityCandidate === "&" || entityCandidate === "&") {
|
|
1140
|
-
codeOffset += 5;
|
|
1141
|
-
searchOffset++;
|
|
1142
|
-
continue;
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
codeOffset++;
|
|
1146
|
-
searchOffset++;
|
|
1147
|
-
continue;
|
|
1148
|
-
}
|
|
1149
|
-
const entity = getHTMLEntity(codeOffset);
|
|
1150
|
-
if (entity?.entity === searchChar) {
|
|
1151
|
-
codeOffset += entity.length;
|
|
1152
|
-
searchOffset++;
|
|
1153
|
-
continue;
|
|
1154
|
-
}
|
|
1155
|
-
return null;
|
|
1156
|
-
}
|
|
1157
|
-
return {
|
|
1158
|
-
match: ctx.code.slice(position, codeOffset),
|
|
1159
|
-
index: position
|
|
1160
|
-
};
|
|
1161
|
-
/**
|
|
1162
|
-
* Get HTML entity from the given position
|
|
1163
|
-
*/
|
|
1164
|
-
function getHTMLEntity(position) {
|
|
1165
|
-
let codeOffset = position;
|
|
1166
|
-
if (ctx.code[codeOffset++] !== "&") return null;
|
|
1167
|
-
let entity = "";
|
|
1168
|
-
const entityDecoder = new EntityDecoder(htmlDecodeTree, (cp) => entity += String.fromCodePoint(cp));
|
|
1169
|
-
entityDecoder.startEntity(DecodingMode.Attribute);
|
|
1170
|
-
const length = entityDecoder.write(ctx.code, codeOffset);
|
|
1171
|
-
if (length < 0) return null;
|
|
1172
|
-
if (length === 0) return null;
|
|
1173
|
-
return {
|
|
1174
|
-
entity,
|
|
1175
|
-
length
|
|
1176
|
-
};
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
/**
|
|
1181
|
-
* Skip spaces
|
|
1182
|
-
*/
|
|
1183
|
-
function skipSpaces(string, position) {
|
|
1184
|
-
const re = /\s*/g;
|
|
1185
|
-
re.lastIndex = position;
|
|
1186
|
-
const match = re.exec(string);
|
|
1187
|
-
if (match) return match.index + match[0].length;
|
|
1188
|
-
return position;
|
|
1189
|
-
}
|
|
1190
|
-
/**
|
|
1191
|
-
* Get children
|
|
1192
|
-
*/
|
|
1193
|
-
function getSortedChildren(parent, code) {
|
|
1194
|
-
if (parent.type === "root" && parent.children[0]?.type === "frontmatter") {
|
|
1195
|
-
const children = [...parent.children];
|
|
1196
|
-
if (children.every((n) => n.position)) return children.sort((a, b) => a.position.start.offset - b.position.start.offset);
|
|
1197
|
-
let start = skipSpaces(code, 0);
|
|
1198
|
-
if (code.startsWith("<!", start)) {
|
|
1199
|
-
const frontmatter = children.shift();
|
|
1200
|
-
const before = [];
|
|
1201
|
-
let first;
|
|
1202
|
-
while (first = children.shift()) {
|
|
1203
|
-
start = skipSpaces(code, start);
|
|
1204
|
-
if (first.type === "comment" && code.startsWith("<!--", start)) {
|
|
1205
|
-
start = code.indexOf("-->", start + 4) + 3;
|
|
1206
|
-
before.push(first);
|
|
1207
|
-
} else if (first.type === "doctype" && code.startsWith("<!", start)) {
|
|
1208
|
-
start = code.indexOf(">", start + 2) + 1;
|
|
1209
|
-
before.push(first);
|
|
1210
|
-
} else {
|
|
1211
|
-
children.unshift(first);
|
|
1212
|
-
break;
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
return [
|
|
1216
|
-
...before,
|
|
1217
|
-
frontmatter,
|
|
1218
|
-
...children
|
|
1219
|
-
];
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
return parent.children;
|
|
1223
|
-
}
|
|
1224
|
-
//#endregion
|
|
1225
564
|
//#region src/context/restore.ts
|
|
1226
565
|
var RestoreNodeProcessContext = class {
|
|
566
|
+
result;
|
|
567
|
+
removeTokens = /* @__PURE__ */ new Set();
|
|
568
|
+
nodeMap;
|
|
1227
569
|
constructor(result, nodeMap) {
|
|
1228
|
-
this.removeTokens = /* @__PURE__ */ new Set();
|
|
1229
570
|
this.result = result;
|
|
1230
571
|
this.nodeMap = nodeMap;
|
|
1231
572
|
}
|
|
@@ -1240,11 +581,12 @@ var RestoreNodeProcessContext = class {
|
|
|
1240
581
|
}
|
|
1241
582
|
};
|
|
1242
583
|
var RestoreContext = class {
|
|
584
|
+
ctx;
|
|
585
|
+
offsets = [];
|
|
586
|
+
virtualFragments = [];
|
|
587
|
+
restoreNodeProcesses = [];
|
|
588
|
+
tokens = [];
|
|
1243
589
|
constructor(ctx) {
|
|
1244
|
-
this.offsets = [];
|
|
1245
|
-
this.virtualFragments = [];
|
|
1246
|
-
this.restoreNodeProcesses = [];
|
|
1247
|
-
this.tokens = [];
|
|
1248
590
|
this.ctx = ctx;
|
|
1249
591
|
}
|
|
1250
592
|
addRestoreNodeProcess(process) {
|
|
@@ -1370,9 +712,11 @@ function restoreNodes(result, nodeMap, restoreNodeProcesses) {
|
|
|
1370
712
|
//#endregion
|
|
1371
713
|
//#region src/context/script.ts
|
|
1372
714
|
var VirtualScriptContext = class {
|
|
715
|
+
originalCode;
|
|
716
|
+
restoreContext;
|
|
717
|
+
script = "";
|
|
718
|
+
consumedIndex = 0;
|
|
1373
719
|
constructor(ctx) {
|
|
1374
|
-
this.script = "";
|
|
1375
|
-
this.consumedIndex = 0;
|
|
1376
720
|
this.originalCode = ctx.code;
|
|
1377
721
|
this.restoreContext = new RestoreContext(ctx);
|
|
1378
722
|
}
|
|
@@ -1382,21 +726,393 @@ var VirtualScriptContext = class {
|
|
|
1382
726
|
skipUntilOriginalOffset(offset) {
|
|
1383
727
|
this.consumedIndex = Math.max(offset, this.consumedIndex);
|
|
1384
728
|
}
|
|
1385
|
-
appendOriginal(index) {
|
|
1386
|
-
if (this.consumedIndex >= index) return;
|
|
1387
|
-
this.restoreContext.addOffset({
|
|
1388
|
-
original: this.consumedIndex,
|
|
1389
|
-
dist: this.script.length
|
|
1390
|
-
});
|
|
1391
|
-
this.script += this.originalCode.slice(this.consumedIndex, index);
|
|
1392
|
-
this.consumedIndex = index;
|
|
729
|
+
appendOriginal(index) {
|
|
730
|
+
if (this.consumedIndex >= index) return;
|
|
731
|
+
this.restoreContext.addOffset({
|
|
732
|
+
original: this.consumedIndex,
|
|
733
|
+
dist: this.script.length
|
|
734
|
+
});
|
|
735
|
+
this.script += this.originalCode.slice(this.consumedIndex, index);
|
|
736
|
+
this.consumedIndex = index;
|
|
737
|
+
}
|
|
738
|
+
appendVirtualScript(virtualFragment) {
|
|
739
|
+
const start = this.script.length;
|
|
740
|
+
this.script += virtualFragment;
|
|
741
|
+
this.restoreContext.addVirtualFragmentRange(start, this.script.length);
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
//#endregion
|
|
745
|
+
//#region src/util/index.ts
|
|
746
|
+
/**
|
|
747
|
+
* Uses a binary search to determine the highest index at which value should be inserted into array in order to maintain its sort order.
|
|
748
|
+
*/
|
|
749
|
+
function sortedLastIndex(array, compare) {
|
|
750
|
+
let lower = 0;
|
|
751
|
+
let upper = array.length;
|
|
752
|
+
while (lower < upper) {
|
|
753
|
+
const mid = Math.floor(lower + (upper - lower) / 2);
|
|
754
|
+
const target = compare(array[mid]);
|
|
755
|
+
if (target < 0) lower = mid + 1;
|
|
756
|
+
else if (target > 0) upper = mid;
|
|
757
|
+
else return mid + 1;
|
|
758
|
+
}
|
|
759
|
+
return upper;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Add element to a sorted array
|
|
763
|
+
*/
|
|
764
|
+
function addElementToSortedArray(array, element, compare) {
|
|
765
|
+
const index = sortedLastIndex(array, (target) => compare(target, element));
|
|
766
|
+
array.splice(index, 0, element);
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Add element to a sorted array
|
|
770
|
+
*/
|
|
771
|
+
function addElementsToSortedArray(array, elements, compare) {
|
|
772
|
+
if (!elements.length) return;
|
|
773
|
+
let last = elements[0];
|
|
774
|
+
let index = sortedLastIndex(array, (target) => compare(target, last));
|
|
775
|
+
for (const element of elements) {
|
|
776
|
+
if (compare(last, element) > 0) index = sortedLastIndex(array, (target) => compare(target, element));
|
|
777
|
+
let e = array[index];
|
|
778
|
+
while (e && compare(e, element) <= 0) e = array[++index];
|
|
779
|
+
array.splice(index, 0, element);
|
|
780
|
+
last = element;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
//#endregion
|
|
784
|
+
//#region src/parser/scope/index.ts
|
|
785
|
+
const REFERENCE_TYPE_VALUE_FLAG = 1;
|
|
786
|
+
const REFERENCE_TYPE_TYPE_FLAG = 2;
|
|
787
|
+
/**
|
|
788
|
+
* Gets the scope for the Program node
|
|
789
|
+
*/
|
|
790
|
+
function getProgramScope(scopeManager) {
|
|
791
|
+
const globalScope = scopeManager.globalScope;
|
|
792
|
+
return globalScope.childScopes.find((s) => s.type === "module") || globalScope;
|
|
793
|
+
}
|
|
794
|
+
/** Remove all scope, variable, and reference */
|
|
795
|
+
function removeAllScopeAndVariableAndReference(target, info) {
|
|
796
|
+
const removeTargetScopes = /* @__PURE__ */ new Set();
|
|
797
|
+
traverseNodes(target, {
|
|
798
|
+
visitorKeys: info.visitorKeys,
|
|
799
|
+
enterNode(node) {
|
|
800
|
+
const scope = info.scopeManager.acquire(node);
|
|
801
|
+
if (scope) {
|
|
802
|
+
removeTargetScopes.add(scope);
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
if (node.type === "Identifier" || node.type === "JSXIdentifier") {
|
|
806
|
+
let targetScope = getInnermostScopeFromNode(info.scopeManager, node);
|
|
807
|
+
while (targetScope && targetScope.block.type !== "Program" && target.range[0] <= targetScope.block.range[0] && targetScope.block.range[1] <= target.range[1]) targetScope = targetScope.upper;
|
|
808
|
+
if (removeTargetScopes.has(targetScope)) return;
|
|
809
|
+
removeIdentifierVariable(node, targetScope);
|
|
810
|
+
removeIdentifierReference(node, targetScope);
|
|
811
|
+
}
|
|
812
|
+
},
|
|
813
|
+
leaveNode() {}
|
|
814
|
+
});
|
|
815
|
+
for (const scope of removeTargetScopes) removeScope(info.scopeManager, scope);
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Add the virtual reference.
|
|
819
|
+
*/
|
|
820
|
+
function addVirtualReference(node, variable, scope, status) {
|
|
821
|
+
const reference = new Reference(node, scope, status.write && status.read ? eslintScope$1.Reference.RW : status.write ? eslintScope$1.Reference.WRITE : eslintScope$1.Reference.READ, void 0, void 0, void 0, status.typeRef ? REFERENCE_TYPE_TYPE_FLAG : REFERENCE_TYPE_VALUE_FLAG);
|
|
822
|
+
reference.astroVirtualReference = true;
|
|
823
|
+
addReference(variable.references, reference);
|
|
824
|
+
reference.resolved = variable;
|
|
825
|
+
if (status.forceUsed) variable.eslintUsed = true;
|
|
826
|
+
return reference;
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Add global variable
|
|
830
|
+
*/
|
|
831
|
+
function addGlobalVariable(reference, scopeManager) {
|
|
832
|
+
const globalScope = scopeManager.globalScope;
|
|
833
|
+
const name = reference.identifier.name;
|
|
834
|
+
let variable = globalScope.set.get(name);
|
|
835
|
+
if (!variable) {
|
|
836
|
+
variable = new Variable(name, globalScope);
|
|
837
|
+
globalScope.variables.push(variable);
|
|
838
|
+
globalScope.set.set(name, variable);
|
|
839
|
+
}
|
|
840
|
+
reference.resolved = variable;
|
|
841
|
+
variable.references.push(reference);
|
|
842
|
+
return variable;
|
|
843
|
+
}
|
|
844
|
+
/** Remove reference from through */
|
|
845
|
+
function removeReferenceFromThrough(reference, baseScope) {
|
|
846
|
+
const variable = reference.resolved;
|
|
847
|
+
const name = reference.identifier.name;
|
|
848
|
+
let scope = baseScope;
|
|
849
|
+
while (scope) {
|
|
850
|
+
for (const ref of [...scope.through]) if (reference === ref) scope.through.splice(scope.through.indexOf(ref), 1);
|
|
851
|
+
else if (ref.identifier.name === name) {
|
|
852
|
+
ref.resolved = variable;
|
|
853
|
+
if (!variable.references.includes(ref)) addReference(variable.references, ref);
|
|
854
|
+
scope.through.splice(scope.through.indexOf(ref), 1);
|
|
855
|
+
}
|
|
856
|
+
scope = scope.upper;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
/** Remove scope */
|
|
860
|
+
function removeScope(scopeManager, scope) {
|
|
861
|
+
for (const childScope of scope.childScopes) removeScope(scopeManager, childScope);
|
|
862
|
+
while (scope.references[0]) removeReference(scope.references[0], scope);
|
|
863
|
+
const upper = scope.upper;
|
|
864
|
+
if (upper) {
|
|
865
|
+
const index = upper.childScopes.indexOf(scope);
|
|
866
|
+
if (index >= 0) upper.childScopes.splice(index, 1);
|
|
867
|
+
}
|
|
868
|
+
const index = scopeManager.scopes.indexOf(scope);
|
|
869
|
+
if (index >= 0) scopeManager.scopes.splice(index, 1);
|
|
870
|
+
}
|
|
871
|
+
/** Remove reference */
|
|
872
|
+
function removeReference(reference, baseScope) {
|
|
873
|
+
if (reference.resolved) if (reference.resolved.defs.some((d) => d.name === reference.identifier)) {
|
|
874
|
+
const varIndex = baseScope.variables.indexOf(reference.resolved);
|
|
875
|
+
if (varIndex >= 0) baseScope.variables.splice(varIndex, 1);
|
|
876
|
+
const name = reference.identifier.name;
|
|
877
|
+
if (reference.resolved === baseScope.set.get(name)) baseScope.set.delete(name);
|
|
878
|
+
} else {
|
|
879
|
+
const refIndex = reference.resolved.references.indexOf(reference);
|
|
880
|
+
if (refIndex >= 0) reference.resolved.references.splice(refIndex, 1);
|
|
881
|
+
}
|
|
882
|
+
let scope = baseScope;
|
|
883
|
+
while (scope) {
|
|
884
|
+
const refIndex = scope.references.indexOf(reference);
|
|
885
|
+
if (refIndex >= 0) scope.references.splice(refIndex, 1);
|
|
886
|
+
const throughIndex = scope.through.indexOf(reference);
|
|
887
|
+
if (throughIndex >= 0) scope.through.splice(throughIndex, 1);
|
|
888
|
+
scope = scope.upper;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
/** Remove variable */
|
|
892
|
+
function removeIdentifierVariable(node, scope) {
|
|
893
|
+
for (let varIndex = 0; varIndex < scope.variables.length; varIndex++) {
|
|
894
|
+
const variable = scope.variables[varIndex];
|
|
895
|
+
const defIndex = variable.defs.findIndex((def) => def.name === node);
|
|
896
|
+
if (defIndex < 0) continue;
|
|
897
|
+
variable.defs.splice(defIndex, 1);
|
|
898
|
+
if (variable.defs.length === 0) {
|
|
899
|
+
referencesToThrough(variable.references, scope);
|
|
900
|
+
variable.references.forEach((r) => {
|
|
901
|
+
if (r.init) r.init = false;
|
|
902
|
+
r.resolved = null;
|
|
903
|
+
});
|
|
904
|
+
scope.variables.splice(varIndex, 1);
|
|
905
|
+
const name = node.name;
|
|
906
|
+
if (variable === scope.set.get(name)) scope.set.delete(name);
|
|
907
|
+
} else {
|
|
908
|
+
const idIndex = variable.identifiers.indexOf(node);
|
|
909
|
+
if (idIndex >= 0) variable.identifiers.splice(idIndex, 1);
|
|
910
|
+
}
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
/** Remove reference */
|
|
915
|
+
function removeIdentifierReference(node, scope) {
|
|
916
|
+
const reference = scope.references.find((ref) => ref.identifier === node);
|
|
917
|
+
if (reference) {
|
|
918
|
+
removeReference(reference, scope);
|
|
919
|
+
return true;
|
|
920
|
+
}
|
|
921
|
+
const location = node.range[0];
|
|
922
|
+
const pendingScopes = [];
|
|
923
|
+
for (const childScope of scope.childScopes) {
|
|
924
|
+
const range = childScope.block.range;
|
|
925
|
+
if (range[0] <= location && location < range[1]) {
|
|
926
|
+
if (removeIdentifierReference(node, childScope)) return true;
|
|
927
|
+
} else pendingScopes.push(childScope);
|
|
928
|
+
}
|
|
929
|
+
for (const childScope of pendingScopes) if (removeIdentifierReference(node, childScope)) return true;
|
|
930
|
+
return false;
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Get the innermost scope which contains a given node.
|
|
934
|
+
* @returns The innermost scope.
|
|
935
|
+
*/
|
|
936
|
+
function getInnermostScopeFromNode(scopeManager, currentNode) {
|
|
937
|
+
return getInnermostScope(getScopeFromNode(scopeManager, currentNode), currentNode);
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Gets the scope for the current node
|
|
941
|
+
*/
|
|
942
|
+
function getScopeFromNode(scopeManager, currentNode) {
|
|
943
|
+
let node = currentNode;
|
|
944
|
+
for (; node; node = node.parent || null) {
|
|
945
|
+
const scope = scopeManager.acquire(node, false);
|
|
946
|
+
if (scope) {
|
|
947
|
+
if (scope.type === "function-expression-name") return scope.childScopes[0];
|
|
948
|
+
if (scope.type === "global" && node.type === "Program" && node.sourceType === "module") return scope.childScopes.find((s) => s.type === "module") || scope;
|
|
949
|
+
return scope;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
return scopeManager.globalScope;
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Get the innermost scope which contains a given location.
|
|
956
|
+
* @param initialScope The initial scope to search.
|
|
957
|
+
* @param node The location to search.
|
|
958
|
+
* @returns The innermost scope.
|
|
959
|
+
*/
|
|
960
|
+
function getInnermostScope(initialScope, node) {
|
|
961
|
+
for (const childScope of initialScope.childScopes) {
|
|
962
|
+
const range = childScope.block.range;
|
|
963
|
+
if (range[0] <= node.range[0] && node.range[1] <= range[1]) return getInnermostScope(childScope, node);
|
|
1393
964
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
965
|
+
return initialScope;
|
|
966
|
+
}
|
|
967
|
+
/** Move reference to through */
|
|
968
|
+
function referencesToThrough(references, baseScope) {
|
|
969
|
+
let scope = baseScope;
|
|
970
|
+
while (scope) {
|
|
971
|
+
addAllReferences(scope.through, references);
|
|
972
|
+
scope = scope.upper;
|
|
1398
973
|
}
|
|
1399
|
-
}
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Add all references to array
|
|
977
|
+
*/
|
|
978
|
+
function addAllReferences(list, elements) {
|
|
979
|
+
addElementsToSortedArray(list, elements, (a, b) => a.identifier.range[0] - b.identifier.range[0]);
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Add reference to array
|
|
983
|
+
*/
|
|
984
|
+
function addReference(list, reference) {
|
|
985
|
+
addElementToSortedArray(list, reference, (a, b) => a.identifier.range[0] - b.identifier.range[0]);
|
|
986
|
+
}
|
|
987
|
+
//#endregion
|
|
988
|
+
//#region src/astro/node.ts
|
|
989
|
+
/** Check whether the given node is a JSX element or fragment. */
|
|
990
|
+
function isJSXElementOrFragment(node) {
|
|
991
|
+
return node.type === "JSXElement" || node.type === "JSXFragment";
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Check whether a JSX fragment is only a compiler wrapper.
|
|
995
|
+
*
|
|
996
|
+
* e.g `<div></div><div></div>`
|
|
997
|
+
*
|
|
998
|
+
* In this case, the compiler wraps the two divs in a fragment,
|
|
999
|
+
* but the fragment itself has no location in the source code.
|
|
1000
|
+
* We can identify such fragments by checking if their opening and closing tags have zero length.
|
|
1001
|
+
*/
|
|
1002
|
+
function isSyntheticFragment(node) {
|
|
1003
|
+
return node.openingFragment.start === node.openingFragment.end && (node.closingFragment == null || node.closingFragment.start === node.closingFragment.end);
|
|
1004
|
+
}
|
|
1005
|
+
/** Check whether an attribute uses Astro shorthand syntax. */
|
|
1006
|
+
function isShorthandAttribute(attr, code) {
|
|
1007
|
+
if (attr.type !== "JSXAttribute" || attr.name.type !== "JSXIdentifier" || attr.value?.type !== "JSXExpressionContainer") return false;
|
|
1008
|
+
return (isIdentifier(attr.value.expression) ? attr.value.expression.name : code.slice(attr.value.expression.start, attr.value.expression.end)) === attr.name.name && attr.value.expression.start === attr.name.start && attr.value.expression.end === attr.name.end;
|
|
1009
|
+
}
|
|
1010
|
+
/** Check whether an unknown value has the node location shape. */
|
|
1011
|
+
function isNode(node) {
|
|
1012
|
+
return Boolean(node) && typeof node === "object" && typeof node.type === "string" && typeof node.start === "number" && typeof node.end === "number";
|
|
1013
|
+
}
|
|
1014
|
+
/** Check whether the given node is an identifier. */
|
|
1015
|
+
function isIdentifier(node) {
|
|
1016
|
+
return isNode(node) && node.type === "Identifier";
|
|
1017
|
+
}
|
|
1018
|
+
//#endregion
|
|
1019
|
+
//#region src/astro/walker.ts
|
|
1020
|
+
/**
|
|
1021
|
+
* Walk the compiler AST.
|
|
1022
|
+
* The `enter` callback is called when entering a node, and the `leave` callback is called when leaving a node.
|
|
1023
|
+
* The `ctx` object passed to the callbacks has two methods: `skipChildren()` to skip walking the children of the current node, and `break()` to stop walking entirely.
|
|
1024
|
+
* The callbacks are called in depth-first order.
|
|
1025
|
+
*/
|
|
1026
|
+
function walk(parent, enter, leave) {
|
|
1027
|
+
const childNodes = getChildNodes(parent);
|
|
1028
|
+
const parents = [parent];
|
|
1029
|
+
for (const child of childNodes) if (walkNode(child, enter, leave || (() => {}), parents).break) break;
|
|
1030
|
+
}
|
|
1031
|
+
/** Walk one compiler child node. */
|
|
1032
|
+
function walkNode(node, enter, leave, parents) {
|
|
1033
|
+
const buffer = [{
|
|
1034
|
+
node,
|
|
1035
|
+
parents
|
|
1036
|
+
}];
|
|
1037
|
+
let shouldBreak = false;
|
|
1038
|
+
let shouldSkipChildren = false;
|
|
1039
|
+
const currentCtx = {
|
|
1040
|
+
skipChildren() {
|
|
1041
|
+
shouldSkipChildren = true;
|
|
1042
|
+
},
|
|
1043
|
+
break() {
|
|
1044
|
+
shouldBreak = true;
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
while (buffer.length > 0) {
|
|
1048
|
+
const current = buffer.pop();
|
|
1049
|
+
enter(current.node, current.parents, currentCtx);
|
|
1050
|
+
if (shouldBreak) return { break: true };
|
|
1051
|
+
if (shouldSkipChildren) shouldSkipChildren = false;
|
|
1052
|
+
else {
|
|
1053
|
+
const childNodes = getChildNodes(current.node);
|
|
1054
|
+
const parents = [current.node, ...current.parents];
|
|
1055
|
+
for (let i = childNodes.length - 1; i >= 0; i--) buffer.push({
|
|
1056
|
+
node: childNodes[i],
|
|
1057
|
+
parents
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
leave(current.node, current.parents, currentCtx);
|
|
1061
|
+
if (shouldBreak) return { break: true };
|
|
1062
|
+
}
|
|
1063
|
+
return {};
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Get child nodes of a compiler node.
|
|
1067
|
+
*/
|
|
1068
|
+
function getChildNodes(node) {
|
|
1069
|
+
if (isAstroRoot(node)) return [...node.frontmatter ? [node.frontmatter] : [], ...node.body].sort((a, b) => a.start - b.start);
|
|
1070
|
+
if (isAstroFrontmatter(node)) return [node.program];
|
|
1071
|
+
if (isAstroComment(node) || isAstroDoctype(node)) return [];
|
|
1072
|
+
if (isJSXFragment(node)) return [
|
|
1073
|
+
node.openingFragment,
|
|
1074
|
+
...node.children,
|
|
1075
|
+
...node.closingFragment ? [node.closingFragment] : []
|
|
1076
|
+
];
|
|
1077
|
+
if (isJSXElement(node)) return [
|
|
1078
|
+
node.openingElement,
|
|
1079
|
+
...node.children,
|
|
1080
|
+
...node.closingElement ? [node.closingElement] : []
|
|
1081
|
+
];
|
|
1082
|
+
const keys = getKeys(node);
|
|
1083
|
+
const children = [];
|
|
1084
|
+
for (const key of keys) {
|
|
1085
|
+
const value = node[key];
|
|
1086
|
+
if (Array.isArray(value)) {
|
|
1087
|
+
for (const element of value) if (isNode(element)) children.push(element);
|
|
1088
|
+
} else if (isNode(value)) children.push(value);
|
|
1089
|
+
}
|
|
1090
|
+
return children.sort((a, b) => a.start - b.start);
|
|
1091
|
+
}
|
|
1092
|
+
/** Check whether the given node is a JSX element */
|
|
1093
|
+
function isJSXElement(node) {
|
|
1094
|
+
return node.type === "JSXElement";
|
|
1095
|
+
}
|
|
1096
|
+
/** Check whether the given node is a JSX fragment */
|
|
1097
|
+
function isJSXFragment(node) {
|
|
1098
|
+
return node.type === "JSXFragment";
|
|
1099
|
+
}
|
|
1100
|
+
/** Check whether the given node is the root node of the Astro AST. */
|
|
1101
|
+
function isAstroRoot(node) {
|
|
1102
|
+
return node.type === "AstroRoot";
|
|
1103
|
+
}
|
|
1104
|
+
/** Check whether the given node is a walkable template node. */
|
|
1105
|
+
function isAstroFrontmatter(node) {
|
|
1106
|
+
return node.type === "AstroFrontmatter";
|
|
1107
|
+
}
|
|
1108
|
+
/** Check whether the given node is a walkable template node. */
|
|
1109
|
+
function isAstroComment(node) {
|
|
1110
|
+
return node.type === "AstroComment";
|
|
1111
|
+
}
|
|
1112
|
+
/** Check whether the given node is a walkable template node. */
|
|
1113
|
+
function isAstroDoctype(node) {
|
|
1114
|
+
return node.type === "AstroDoctype";
|
|
1115
|
+
}
|
|
1400
1116
|
//#endregion
|
|
1401
1117
|
//#region src/parser/process-template.ts
|
|
1402
1118
|
/**
|
|
@@ -1406,6 +1122,7 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
1406
1122
|
let uniqueIdSeq = 0;
|
|
1407
1123
|
const usedUniqueIds = /* @__PURE__ */ new Set();
|
|
1408
1124
|
const script = new VirtualScriptContext(ctx);
|
|
1125
|
+
const code = ctx.code;
|
|
1409
1126
|
let fragmentOpened = false;
|
|
1410
1127
|
/** Open astro root fragment */
|
|
1411
1128
|
function openRootFragment(startOffset) {
|
|
@@ -1423,20 +1140,21 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
1423
1140
|
return false;
|
|
1424
1141
|
});
|
|
1425
1142
|
}
|
|
1426
|
-
walkElements(resultTemplate.ast,
|
|
1427
|
-
if (node.type === "
|
|
1428
|
-
const start = node.position.start.offset;
|
|
1143
|
+
walkElements(resultTemplate.ast, (node) => {
|
|
1144
|
+
if (node.type === "AstroFrontmatter") {
|
|
1429
1145
|
if (fragmentOpened) {
|
|
1430
1146
|
script.appendVirtualScript("</>;");
|
|
1431
1147
|
fragmentOpened = false;
|
|
1432
1148
|
}
|
|
1149
|
+
let start = node.start;
|
|
1150
|
+
while (code[start] !== "-") start++;
|
|
1433
1151
|
script.appendOriginal(start);
|
|
1434
1152
|
script.skipOriginalOffset(3);
|
|
1435
|
-
const end =
|
|
1153
|
+
const end = node.end;
|
|
1436
1154
|
const scriptStart = start + 3;
|
|
1437
1155
|
let scriptEnd = end - 3;
|
|
1438
1156
|
let endChar;
|
|
1439
|
-
while (scriptStart < scriptEnd - 1 && (endChar =
|
|
1157
|
+
while (scriptStart < scriptEnd - 1 && (endChar = code[scriptEnd - 1]) && !endChar.trim()) scriptEnd--;
|
|
1440
1158
|
script.appendOriginal(scriptEnd);
|
|
1441
1159
|
script.appendVirtualScript("\n;");
|
|
1442
1160
|
script.skipOriginalOffset(end - scriptEnd);
|
|
@@ -1452,132 +1170,136 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
1452
1170
|
}
|
|
1453
1171
|
return true;
|
|
1454
1172
|
});
|
|
1455
|
-
script.restoreContext.addToken(AST_TOKEN_TYPES.Punctuator, [
|
|
1173
|
+
script.restoreContext.addToken(AST_TOKEN_TYPES.Punctuator, [start, start + 3]);
|
|
1456
1174
|
script.restoreContext.addToken(AST_TOKEN_TYPES.Punctuator, [end - 3, end]);
|
|
1457
|
-
} else if (
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
if (
|
|
1463
|
-
const
|
|
1464
|
-
|
|
1465
|
-
|
|
1175
|
+
} else if (isJSXElementOrFragment(node)) {
|
|
1176
|
+
const start = node.start;
|
|
1177
|
+
script.appendOriginal(start);
|
|
1178
|
+
if (!fragmentOpened) openRootFragment(start);
|
|
1179
|
+
if (node.type === "JSXFragment") {
|
|
1180
|
+
if (isSyntheticFragment(node)) {
|
|
1181
|
+
const start = node.start;
|
|
1182
|
+
script.appendOriginal(start);
|
|
1183
|
+
script.appendVirtualScript("<>");
|
|
1184
|
+
script.restoreContext.addRestoreNodeProcess((scriptNode) => {
|
|
1185
|
+
if (scriptNode.range[0] === start && scriptNode.type === AST_NODE_TYPES.JSXFragment) {
|
|
1186
|
+
delete scriptNode.openingFragment;
|
|
1187
|
+
delete scriptNode.closingFragment;
|
|
1188
|
+
const fragmentNode = scriptNode;
|
|
1189
|
+
fragmentNode.type = "AstroFragment";
|
|
1190
|
+
const last = fragmentNode.children[fragmentNode.children.length - 1];
|
|
1191
|
+
if (last && fragmentNode.range[1] < last.range[1]) {
|
|
1192
|
+
fragmentNode.range[1] = last.range[1];
|
|
1193
|
+
fragmentNode.loc.end = ctx.getLocFromIndex(fragmentNode.range[1]);
|
|
1194
|
+
}
|
|
1195
|
+
return true;
|
|
1196
|
+
}
|
|
1197
|
+
return false;
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
} else {
|
|
1201
|
+
const tagType = getTagType(node);
|
|
1202
|
+
for (const attr of node.openingElement.attributes) {
|
|
1203
|
+
const analyzed = analyzeAttribute(attr);
|
|
1204
|
+
if (analyzed.kind === "literal-value" || analyzed.kind === "empty" || analyzed.kind === "expression" || analyzed.kind === "template-literal") {
|
|
1205
|
+
const attrName = getJsxName(analyzed.node.name);
|
|
1206
|
+
if (tagType === "component" ? /[.:@]/u.test(attrName) : /[.@]/u.test(attrName) || attrName.startsWith(":")) processAttributePunctuators(analyzed.node);
|
|
1207
|
+
}
|
|
1208
|
+
if (analyzed.kind === "literal-value") {
|
|
1209
|
+
const raw = code.slice(analyzed.node.value.start, analyzed.node.value.end);
|
|
1210
|
+
if (raw && !raw.startsWith("\"") && !raw.startsWith("'")) {
|
|
1211
|
+
const attrStart = analyzed.node.start;
|
|
1212
|
+
const valueStart = analyzed.node.value.start;
|
|
1213
|
+
const attrEnd = analyzed.node.end;
|
|
1214
|
+
script.appendOriginal(valueStart);
|
|
1215
|
+
script.appendVirtualScript("\"");
|
|
1216
|
+
script.appendOriginal(attrEnd);
|
|
1217
|
+
script.appendVirtualScript("\"");
|
|
1218
|
+
script.restoreContext.addRestoreNodeProcess((scriptNode, context) => {
|
|
1219
|
+
if (scriptNode.type === AST_NODE_TYPES.JSXAttribute && scriptNode.range[0] === attrStart) {
|
|
1220
|
+
const attrNode = scriptNode;
|
|
1221
|
+
if (attrNode.value?.type === "Literal" && typeof attrNode.value.value === "string") {
|
|
1222
|
+
const raw = code.slice(valueStart, attrEnd);
|
|
1223
|
+
attrNode.value.raw = raw;
|
|
1224
|
+
context.findToken(valueStart).value = raw;
|
|
1225
|
+
return true;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
return false;
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
} else if (analyzed.kind === "shorthand") {
|
|
1232
|
+
const attrName = getJsxName(analyzed.node.name);
|
|
1233
|
+
const start = getShorthandAttributeOpeningBraceOffset(analyzed.node);
|
|
1466
1234
|
script.appendOriginal(start);
|
|
1467
|
-
|
|
1235
|
+
const jsxName = /[\s"'[\]{}]/u.test(attrName) ? generateUniqueId(attrName) : attrName;
|
|
1236
|
+
script.appendVirtualScript(`${jsxName}=`);
|
|
1468
1237
|
script.restoreContext.addRestoreNodeProcess((scriptNode) => {
|
|
1469
|
-
if (scriptNode.
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
const
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
fragmentNode.range[1] = last.range[1];
|
|
1477
|
-
fragmentNode.loc.end = ctx.getLocFromIndex(fragmentNode.range[1]);
|
|
1478
|
-
}
|
|
1238
|
+
if (scriptNode.type === AST_NODE_TYPES.JSXAttribute && scriptNode.range[0] === start) {
|
|
1239
|
+
const attrNode = scriptNode;
|
|
1240
|
+
attrNode.type = "AstroShorthandAttribute";
|
|
1241
|
+
const locs = ctx.getLocations(...attrNode.value.expression.range);
|
|
1242
|
+
if (jsxName !== attrName) attrNode.name.name = attrName;
|
|
1243
|
+
attrNode.name.range = locs.range;
|
|
1244
|
+
attrNode.name.loc = locs.loc;
|
|
1479
1245
|
return true;
|
|
1480
1246
|
}
|
|
1481
1247
|
return false;
|
|
1482
1248
|
});
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
}
|
|
1493
|
-
if (attr.kind === "quoted") {
|
|
1494
|
-
if (attr.raw && !attr.raw.startsWith("\"") && !attr.raw.startsWith("'")) {
|
|
1495
|
-
const attrStart = attr.position.start.offset;
|
|
1496
|
-
const start = calcAttributeValueStartOffset(attr, ctx);
|
|
1497
|
-
const end = calcAttributeEndOffset(attr, ctx);
|
|
1498
|
-
script.appendOriginal(start);
|
|
1499
|
-
script.appendVirtualScript("\"");
|
|
1500
|
-
script.appendOriginal(end);
|
|
1501
|
-
script.appendVirtualScript("\"");
|
|
1502
|
-
script.restoreContext.addRestoreNodeProcess((scriptNode, context) => {
|
|
1249
|
+
} else if (analyzed.kind === "template-literal") {
|
|
1250
|
+
const attrStart = analyzed.node.start;
|
|
1251
|
+
const valueStart = analyzed.node.value.start;
|
|
1252
|
+
const attrEnd = analyzed.node.end;
|
|
1253
|
+
script.appendOriginal(valueStart);
|
|
1254
|
+
script.appendVirtualScript("{");
|
|
1255
|
+
script.appendOriginal(attrEnd);
|
|
1256
|
+
script.appendVirtualScript("}");
|
|
1257
|
+
script.restoreContext.addRestoreNodeProcess((scriptNode) => {
|
|
1503
1258
|
if (scriptNode.type === AST_NODE_TYPES.JSXAttribute && scriptNode.range[0] === attrStart) {
|
|
1504
1259
|
const attrNode = scriptNode;
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
attrNode.value.raw = raw;
|
|
1508
|
-
context.findToken(start).value = raw;
|
|
1509
|
-
return true;
|
|
1510
|
-
}
|
|
1260
|
+
attrNode.type = "AstroTemplateLiteralAttribute";
|
|
1261
|
+
return true;
|
|
1511
1262
|
}
|
|
1512
1263
|
return false;
|
|
1513
1264
|
});
|
|
1514
1265
|
}
|
|
1515
|
-
} else if (attr.kind === "shorthand") {
|
|
1516
|
-
const start = attr.position.start.offset;
|
|
1517
|
-
script.appendOriginal(start);
|
|
1518
|
-
const jsxName = /[\s"'[\]{}]/u.test(attr.name) ? generateUniqueId(attr.name) : attr.name;
|
|
1519
|
-
script.appendVirtualScript(`${jsxName}=`);
|
|
1520
|
-
script.restoreContext.addRestoreNodeProcess((scriptNode) => {
|
|
1521
|
-
if (scriptNode.type === AST_NODE_TYPES.JSXAttribute && scriptNode.range[0] === start) {
|
|
1522
|
-
const attrNode = scriptNode;
|
|
1523
|
-
attrNode.type = "AstroShorthandAttribute";
|
|
1524
|
-
const locs = ctx.getLocations(...attrNode.value.expression.range);
|
|
1525
|
-
if (jsxName !== attr.name) attrNode.name.name = attr.name;
|
|
1526
|
-
attrNode.name.range = locs.range;
|
|
1527
|
-
attrNode.name.loc = locs.loc;
|
|
1528
|
-
return true;
|
|
1529
|
-
}
|
|
1530
|
-
return false;
|
|
1531
|
-
});
|
|
1532
|
-
} else if (attr.kind === "template-literal") {
|
|
1533
|
-
const attrStart = attr.position.start.offset;
|
|
1534
|
-
const start = calcAttributeValueStartOffset(attr, ctx);
|
|
1535
|
-
const end = calcAttributeEndOffset(attr, ctx);
|
|
1536
|
-
script.appendOriginal(start);
|
|
1537
|
-
script.appendVirtualScript("{");
|
|
1538
|
-
script.appendOriginal(end);
|
|
1539
|
-
script.appendVirtualScript("}");
|
|
1540
|
-
script.restoreContext.addRestoreNodeProcess((scriptNode) => {
|
|
1541
|
-
if (scriptNode.type === AST_NODE_TYPES.JSXAttribute && scriptNode.range[0] === attrStart) {
|
|
1542
|
-
const attrNode = scriptNode;
|
|
1543
|
-
attrNode.type = "AstroTemplateLiteralAttribute";
|
|
1544
|
-
return true;
|
|
1545
|
-
}
|
|
1546
|
-
return false;
|
|
1547
|
-
});
|
|
1548
1266
|
}
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1267
|
+
const closing = getSelfClosingTag(node);
|
|
1268
|
+
if (closing && closing.end === ">") {
|
|
1269
|
+
script.appendOriginal(closing.offset - 1);
|
|
1270
|
+
script.appendVirtualScript("/");
|
|
1271
|
+
}
|
|
1272
|
+
const tagName = getJsxName(node.openingElement.name);
|
|
1273
|
+
if (tagName === "script" || tagName === "style" || node.openingElement.attributes.some((attr) => {
|
|
1274
|
+
const analyzed = analyzeAttribute(attr);
|
|
1275
|
+
if (analyzed.kind === "spread") return false;
|
|
1276
|
+
return getJsxName(analyzed.node.name) === "is:raw";
|
|
1277
|
+
})) {
|
|
1278
|
+
const text = getRawTextContent(node);
|
|
1279
|
+
if (text && text.value) {
|
|
1280
|
+
const styleNodeStart = node.start;
|
|
1281
|
+
script.appendOriginal(text.start);
|
|
1282
|
+
script.skipOriginalOffset(text.value.length);
|
|
1283
|
+
script.restoreContext.addRestoreNodeProcess((scriptNode) => {
|
|
1284
|
+
if (scriptNode.type === AST_NODE_TYPES.JSXElement && scriptNode.range[0] === styleNodeStart) {
|
|
1285
|
+
scriptNode.children = [{
|
|
1286
|
+
type: "AstroRawText",
|
|
1287
|
+
value: text.value,
|
|
1288
|
+
raw: text.value,
|
|
1289
|
+
parent: scriptNode,
|
|
1290
|
+
...ctx.getLocations(text.start, text.end)
|
|
1291
|
+
}];
|
|
1292
|
+
return true;
|
|
1293
|
+
}
|
|
1294
|
+
return false;
|
|
1295
|
+
});
|
|
1296
|
+
script.restoreContext.addToken(AST_TOKEN_TYPES.JSXText, [text.start, text.end]);
|
|
1297
|
+
}
|
|
1576
1298
|
}
|
|
1577
1299
|
}
|
|
1578
|
-
} else if (node.type === "
|
|
1579
|
-
const start = node.
|
|
1580
|
-
const end =
|
|
1300
|
+
} else if (node.type === "AstroComment") {
|
|
1301
|
+
const start = node.start;
|
|
1302
|
+
const end = node.end;
|
|
1581
1303
|
const length = end - start;
|
|
1582
1304
|
script.appendOriginal(start);
|
|
1583
1305
|
if (!fragmentOpened) openRootFragment(start);
|
|
@@ -1601,9 +1323,9 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
1601
1323
|
return false;
|
|
1602
1324
|
});
|
|
1603
1325
|
script.restoreContext.addToken("HTMLComment", [start, start + length]);
|
|
1604
|
-
} else if (node.type === "
|
|
1605
|
-
const start = node.
|
|
1606
|
-
const end =
|
|
1326
|
+
} else if (node.type === "AstroDoctype") {
|
|
1327
|
+
const start = node.start;
|
|
1328
|
+
const end = node.end;
|
|
1607
1329
|
const length = end - start;
|
|
1608
1330
|
script.appendOriginal(start);
|
|
1609
1331
|
if (!fragmentOpened) openRootFragment(start);
|
|
@@ -1627,63 +1349,139 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
1627
1349
|
});
|
|
1628
1350
|
script.restoreContext.addToken("HTMLDocType", [start, end]);
|
|
1629
1351
|
} else {
|
|
1630
|
-
const start = node.
|
|
1352
|
+
const start = node.start;
|
|
1631
1353
|
script.appendOriginal(start);
|
|
1632
1354
|
if (!fragmentOpened) openRootFragment(start);
|
|
1633
1355
|
}
|
|
1634
|
-
}, (node
|
|
1635
|
-
if (
|
|
1636
|
-
if (!getSelfClosingTag(node
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
});
|
|
1653
|
-
}
|
|
1356
|
+
}, (node) => {
|
|
1357
|
+
if (node.type === "JSXElement") {
|
|
1358
|
+
if (!getSelfClosingTag(node) && node.closingElement == null) {
|
|
1359
|
+
const offset = calcContentEndOffset(node);
|
|
1360
|
+
script.appendOriginal(offset);
|
|
1361
|
+
script.appendVirtualScript(`</${getJsxName(node.openingElement.name)}>`);
|
|
1362
|
+
script.restoreContext.addRestoreNodeProcess((scriptNode, context) => {
|
|
1363
|
+
const parent = context.getParent(scriptNode);
|
|
1364
|
+
if (scriptNode.range[0] === offset && scriptNode.type === AST_NODE_TYPES.JSXClosingElement && parent.type === AST_NODE_TYPES.JSXElement) {
|
|
1365
|
+
removeAllScopeAndVariableAndReference(scriptNode, {
|
|
1366
|
+
visitorKeys: context.result.visitorKeys,
|
|
1367
|
+
scopeManager: context.result.scopeManager
|
|
1368
|
+
});
|
|
1369
|
+
parent.closingElement = null;
|
|
1370
|
+
return true;
|
|
1371
|
+
}
|
|
1372
|
+
return false;
|
|
1373
|
+
});
|
|
1654
1374
|
}
|
|
1655
1375
|
}
|
|
1656
|
-
if (
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
const after = siblings[index + 1];
|
|
1660
|
-
if (!after || !isTag(after) && after.type !== "comment") {
|
|
1661
|
-
const before = siblings[index - 1];
|
|
1662
|
-
if (before && (isTag(before) || before.type === "comment")) {
|
|
1663
|
-
const end = getEndOffset(node, ctx);
|
|
1664
|
-
script.appendOriginal(end);
|
|
1665
|
-
script.appendVirtualScript("</>");
|
|
1666
|
-
}
|
|
1667
|
-
}
|
|
1376
|
+
if (node.type === "JSXFragment" && isSyntheticFragment(node)) {
|
|
1377
|
+
script.appendOriginal(node.end);
|
|
1378
|
+
script.appendVirtualScript("</>");
|
|
1668
1379
|
}
|
|
1669
1380
|
});
|
|
1670
1381
|
if (fragmentOpened) {
|
|
1671
|
-
const last = resultTemplate.ast
|
|
1672
|
-
|
|
1673
|
-
script.appendOriginal(end);
|
|
1382
|
+
const last = findLastJSXNode(resultTemplate.ast);
|
|
1383
|
+
if (last) script.appendOriginal(last.end);
|
|
1674
1384
|
script.appendVirtualScript("</>;");
|
|
1675
1385
|
}
|
|
1676
|
-
script.appendOriginal(
|
|
1386
|
+
script.appendOriginal(code.length);
|
|
1677
1387
|
return script;
|
|
1678
1388
|
/**
|
|
1389
|
+
* Walk template nodes in source order from the compiler AST.
|
|
1390
|
+
*
|
|
1391
|
+
* The traversal starts with `AstroRoot`. Root-only source ranges such as
|
|
1392
|
+
* frontmatter fences are handled in the root branch so they do not become
|
|
1393
|
+
* node-shaped intermediate children.
|
|
1394
|
+
*/
|
|
1395
|
+
function walkElements(parent, enter, leave) {
|
|
1396
|
+
const nodes = [...parent.body];
|
|
1397
|
+
const frontmatter = parent.frontmatter;
|
|
1398
|
+
if (frontmatter && !isEmptyFrontmatter(frontmatter)) {
|
|
1399
|
+
let insertIndex = nodes.findIndex((child) => frontmatter.start <= child.start);
|
|
1400
|
+
if (insertIndex < 0) insertIndex = nodes.length;
|
|
1401
|
+
while (insertIndex > 0 && isWhitespaceJSXText(nodes[insertIndex - 1])) {
|
|
1402
|
+
nodes.splice(insertIndex - 1, 1);
|
|
1403
|
+
insertIndex--;
|
|
1404
|
+
}
|
|
1405
|
+
while (insertIndex < nodes.length && isWhitespaceJSXText(nodes[insertIndex])) nodes.splice(insertIndex, 1);
|
|
1406
|
+
nodes.splice(insertIndex, 0, frontmatter);
|
|
1407
|
+
}
|
|
1408
|
+
while (nodes.length > 0 && isWhitespaceJSXText(nodes[0])) nodes.shift();
|
|
1409
|
+
while (nodes.length > 0 && isWhitespaceJSXText(nodes[nodes.length - 1])) nodes.pop();
|
|
1410
|
+
for (const child of nodes) walkChild(child, enter, leave);
|
|
1411
|
+
}
|
|
1412
|
+
/** Get raw text content for script, style, and raw nodes. */
|
|
1413
|
+
function getRawTextContent(node) {
|
|
1414
|
+
if (node.closingElement == null) return null;
|
|
1415
|
+
const start = node.openingElement.end;
|
|
1416
|
+
const end = node.closingElement.start;
|
|
1417
|
+
if (start >= end) return null;
|
|
1418
|
+
return {
|
|
1419
|
+
start,
|
|
1420
|
+
end,
|
|
1421
|
+
value: code.slice(start, end)
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
/** Get self-closing tag metadata. */
|
|
1425
|
+
function getSelfClosingTag(node) {
|
|
1426
|
+
if (!node.openingElement?.selfClosing || node.closingElement) return null;
|
|
1427
|
+
const offset = node.openingElement.end;
|
|
1428
|
+
return {
|
|
1429
|
+
offset,
|
|
1430
|
+
end: code.startsWith("/>", offset - 2) ? "/>" : ">"
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
/** Get the Astro attribute kind represented by a compiler node. */
|
|
1434
|
+
function analyzeAttribute(attr) {
|
|
1435
|
+
if (attr.type === "JSXSpreadAttribute") return {
|
|
1436
|
+
kind: "spread",
|
|
1437
|
+
node: attr
|
|
1438
|
+
};
|
|
1439
|
+
if (!attr.value) return {
|
|
1440
|
+
kind: "empty",
|
|
1441
|
+
node: attr
|
|
1442
|
+
};
|
|
1443
|
+
if (isShorthandAttribute(attr, code)) return {
|
|
1444
|
+
kind: "shorthand",
|
|
1445
|
+
node: attr
|
|
1446
|
+
};
|
|
1447
|
+
if (attr.value.type === "Literal") return {
|
|
1448
|
+
kind: "literal-value",
|
|
1449
|
+
node: attr
|
|
1450
|
+
};
|
|
1451
|
+
if (attr.value.type === "JSXExpressionContainer" && code[attr.value.start] === "`") return {
|
|
1452
|
+
kind: "template-literal",
|
|
1453
|
+
node: attr
|
|
1454
|
+
};
|
|
1455
|
+
return {
|
|
1456
|
+
kind: "expression",
|
|
1457
|
+
node: attr
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
/**
|
|
1461
|
+
* Get the source offset of the opening `{` for an Astro shorthand attribute.
|
|
1462
|
+
*
|
|
1463
|
+
* The compiler represents `<img {src}>` as a JSXAttribute that starts at
|
|
1464
|
+
* `src`, while the end offset still includes `}`. The virtual JSX needs to
|
|
1465
|
+
* insert `src=` before the original `{src}`, so shorthand processing must use
|
|
1466
|
+
* the brace offset instead of `attr.start`.
|
|
1467
|
+
*/
|
|
1468
|
+
function getShorthandAttributeOpeningBraceOffset(attr) {
|
|
1469
|
+
return code[attr.start - 1] === "{" ? attr.start - 1 : attr.start;
|
|
1470
|
+
}
|
|
1471
|
+
/** Check whether a node is whitespace-only text. */
|
|
1472
|
+
function isWhitespaceJSXText(node) {
|
|
1473
|
+
return node.type === "JSXText" && code.slice(node.start, node.end).trim() === "";
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1679
1476
|
* Process for attribute punctuators
|
|
1680
1477
|
*/
|
|
1681
1478
|
function processAttributePunctuators(attr) {
|
|
1682
|
-
const
|
|
1479
|
+
const name = getJsxName(attr.name);
|
|
1480
|
+
const start = attr.name.start;
|
|
1683
1481
|
let targetIndex = start;
|
|
1684
1482
|
let colonOffset;
|
|
1685
|
-
for (let index = 0; index <
|
|
1686
|
-
const char =
|
|
1483
|
+
for (let index = 0; index < name.length; index++) {
|
|
1484
|
+
const char = name[index];
|
|
1687
1485
|
if (char !== ":" && char !== "." && char !== "@") continue;
|
|
1688
1486
|
if (index === 0) targetIndex++;
|
|
1689
1487
|
const punctuatorIndex = start + index;
|
|
@@ -1696,8 +1494,8 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
1696
1494
|
const punctuatorIndex = start + colonOffset;
|
|
1697
1495
|
script.restoreContext.addToken(AST_TOKEN_TYPES.JSXIdentifier, [start, punctuatorIndex]);
|
|
1698
1496
|
script.restoreContext.addToken(AST_TOKEN_TYPES.Punctuator, [punctuatorIndex, punctuatorIndex + 1]);
|
|
1699
|
-
script.restoreContext.addToken(AST_TOKEN_TYPES.JSXIdentifier, [punctuatorIndex + 1, start +
|
|
1700
|
-
} else script.restoreContext.addToken(AST_TOKEN_TYPES.JSXIdentifier, [start, start +
|
|
1497
|
+
script.restoreContext.addToken(AST_TOKEN_TYPES.JSXIdentifier, [punctuatorIndex + 1, start + name.length]);
|
|
1498
|
+
} else script.restoreContext.addToken(AST_TOKEN_TYPES.JSXIdentifier, [start, start + name.length]);
|
|
1701
1499
|
script.restoreContext.addRestoreNodeProcess((scriptNode, context) => {
|
|
1702
1500
|
if (scriptNode.type === AST_NODE_TYPES.JSXAttribute && scriptNode.range[0] === targetIndex) {
|
|
1703
1501
|
const baseNameNode = scriptNode.name;
|
|
@@ -1706,25 +1504,27 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
1706
1504
|
nameNode.type = AST_NODE_TYPES.JSXNamespacedName;
|
|
1707
1505
|
nameNode.namespace = {
|
|
1708
1506
|
type: AST_NODE_TYPES.JSXIdentifier,
|
|
1709
|
-
name:
|
|
1710
|
-
...ctx.getLocations(baseNameNode.range[0], baseNameNode.range[0] + colonOffset)
|
|
1507
|
+
name: name.slice(0, colonOffset),
|
|
1508
|
+
...ctx.getLocations(baseNameNode.range[0], baseNameNode.range[0] + colonOffset),
|
|
1509
|
+
parent: void 0
|
|
1711
1510
|
};
|
|
1712
1511
|
nameNode.name = {
|
|
1713
1512
|
type: AST_NODE_TYPES.JSXIdentifier,
|
|
1714
|
-
name:
|
|
1715
|
-
...ctx.getLocations(baseNameNode.range[0] + colonOffset + 1, baseNameNode.range[1])
|
|
1513
|
+
name: name.slice(colonOffset + 1),
|
|
1514
|
+
...ctx.getLocations(baseNameNode.range[0] + colonOffset + 1, baseNameNode.range[1]),
|
|
1515
|
+
parent: void 0
|
|
1716
1516
|
};
|
|
1717
1517
|
scriptNode.name = nameNode;
|
|
1718
1518
|
nameNode.namespace.parent = nameNode;
|
|
1719
1519
|
nameNode.name.parent = nameNode;
|
|
1720
1520
|
} else if (baseNameNode.type === AST_NODE_TYPES.JSXIdentifier) {
|
|
1721
1521
|
const nameNode = baseNameNode;
|
|
1722
|
-
nameNode.name =
|
|
1522
|
+
nameNode.name = name;
|
|
1723
1523
|
scriptNode.name = nameNode;
|
|
1724
1524
|
} else {
|
|
1725
1525
|
const nameNode = baseNameNode;
|
|
1726
|
-
nameNode.namespace.name =
|
|
1727
|
-
nameNode.name.name =
|
|
1526
|
+
nameNode.namespace.name = name.slice(baseNameNode.namespace.range[0] - start, baseNameNode.namespace.range[1] - start);
|
|
1527
|
+
nameNode.name.name = name.slice(baseNameNode.name.range[0] - start, baseNameNode.name.range[1] - start);
|
|
1728
1528
|
scriptNode.name = nameNode;
|
|
1729
1529
|
nameNode.namespace.parent = nameNode;
|
|
1730
1530
|
nameNode.name.parent = nameNode;
|
|
@@ -1740,17 +1540,90 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
1740
1540
|
*/
|
|
1741
1541
|
function generateUniqueId(base) {
|
|
1742
1542
|
let candidate = `$_${base.replace(/\W/g, "_")}${uniqueIdSeq++}`;
|
|
1743
|
-
while (usedUniqueIds.has(candidate) ||
|
|
1543
|
+
while (usedUniqueIds.has(candidate) || code.includes(candidate)) candidate = `$_${base.replace(/\W/g, "_")}${uniqueIdSeq++}`;
|
|
1744
1544
|
usedUniqueIds.add(candidate);
|
|
1745
1545
|
return candidate;
|
|
1746
1546
|
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Find the last JSXNode.
|
|
1549
|
+
* Basically, it returns the last element of AstroRootNode.body.
|
|
1550
|
+
* However, if the last element is a JSXTextNode, and its text is whitespace,
|
|
1551
|
+
* and all preceding elements are AstroCommentNode, it returns the last AstroCommentNode.
|
|
1552
|
+
*/
|
|
1553
|
+
function findLastJSXNode(ast) {
|
|
1554
|
+
const body = ast.body;
|
|
1555
|
+
if (body.length === 0) return null;
|
|
1556
|
+
const lastNode = body[body.length - 1];
|
|
1557
|
+
if (isWhitespaceJSXText(lastNode) && body.length >= 2 && body.slice(0, -1).every((node) => node.type === "AstroComment")) return body[body.length - 2];
|
|
1558
|
+
return lastNode;
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
/** Walk one compiler child node. */
|
|
1562
|
+
function walkChild(node, enter, leave) {
|
|
1563
|
+
enter(node);
|
|
1564
|
+
if (isJSXElementOrFragment(node)) for (const child of node.children) {
|
|
1565
|
+
if (child.type === "AstroScript") continue;
|
|
1566
|
+
walkChild(child, enter, leave);
|
|
1567
|
+
}
|
|
1568
|
+
else if (node.type === "JSXExpressionContainer") walkExpression(node.expression, enter, leave);
|
|
1569
|
+
leave(node);
|
|
1570
|
+
}
|
|
1571
|
+
/** Walk one compiler expression node. */
|
|
1572
|
+
function walkExpression(node, enter, leave) {
|
|
1573
|
+
const walked = /* @__PURE__ */ new Set();
|
|
1574
|
+
walk(node, (child, _parents, ctx) => {
|
|
1575
|
+
if (walked.has(child)) return;
|
|
1576
|
+
walked.add(child);
|
|
1577
|
+
if (isWalkableNode(child)) {
|
|
1578
|
+
walkChild(child, enter, leave);
|
|
1579
|
+
ctx.skipChildren();
|
|
1580
|
+
}
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
/**
|
|
1584
|
+
* Check whether the given node is a walkable template node that should be passed through the enter/leave hooks.
|
|
1585
|
+
*/
|
|
1586
|
+
function isWalkableNode(node) {
|
|
1587
|
+
return isJSXElementOrFragment(node) || node.type === "AstroComment" || node.type === "AstroDoctype" || node.type === "JSXExpressionContainer" || node.type === "JSXText";
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1590
|
+
* Check whether the frontmatter is empty (i.e. contains no characters).
|
|
1591
|
+
* In this case, the frontmatter node is still generated by the compiler,
|
|
1592
|
+
* but it has no source range. We can identify such empty frontmatter by checking if the start and end offsets are the same.
|
|
1593
|
+
*/
|
|
1594
|
+
function isEmptyFrontmatter(node) {
|
|
1595
|
+
return node.start === node.end;
|
|
1596
|
+
}
|
|
1597
|
+
/** Convert a compiler JSX name node to source text. */
|
|
1598
|
+
function getJsxName(nameNode) {
|
|
1599
|
+
if (nameNode.type === "JSXIdentifier") return nameNode.name;
|
|
1600
|
+
if (nameNode.type === "JSXMemberExpression") return `${getJsxName(nameNode.object)}.${getJsxName(nameNode.property)}`;
|
|
1601
|
+
if (nameNode.type === "JSXNamespacedName") return `${getJsxName(nameNode.namespace)}:${getJsxName(nameNode.name)}`;
|
|
1602
|
+
return "";
|
|
1603
|
+
}
|
|
1604
|
+
/** Get the Astro tag category for a traversal node. */
|
|
1605
|
+
function getTagType(node) {
|
|
1606
|
+
const name = getJsxName(node.openingElement.name);
|
|
1607
|
+
if (/^[A-Z]/u.test(name) || name.includes(".")) return "component";
|
|
1608
|
+
if (name.includes("-")) return "custom-element";
|
|
1609
|
+
return "element";
|
|
1610
|
+
}
|
|
1611
|
+
/** Calculate where an element without a closing tag should end. */
|
|
1612
|
+
function calcContentEndOffset(node) {
|
|
1613
|
+
const children = node.children;
|
|
1614
|
+
const lastChild = children[children.length - 1];
|
|
1615
|
+
if (lastChild) return lastChild.end;
|
|
1616
|
+
return node.openingElement.end;
|
|
1747
1617
|
}
|
|
1748
1618
|
//#endregion
|
|
1749
1619
|
//#region src/context/index.ts
|
|
1750
1620
|
var Context = class {
|
|
1621
|
+
code;
|
|
1622
|
+
filePath;
|
|
1623
|
+
locs;
|
|
1624
|
+
locsMap = /* @__PURE__ */ new Map();
|
|
1625
|
+
state = {};
|
|
1751
1626
|
constructor(code, filePath) {
|
|
1752
|
-
this.locsMap = /* @__PURE__ */ new Map();
|
|
1753
|
-
this.state = {};
|
|
1754
1627
|
this.locs = new LinesAndColumns(code);
|
|
1755
1628
|
this.code = code;
|
|
1756
1629
|
this.filePath = filePath;
|
|
@@ -1805,31 +1678,19 @@ var Context = class {
|
|
|
1805
1678
|
}
|
|
1806
1679
|
};
|
|
1807
1680
|
var LinesAndColumns = class {
|
|
1681
|
+
lineStartIndices;
|
|
1682
|
+
code;
|
|
1808
1683
|
constructor(origCode) {
|
|
1809
1684
|
const len = origCode.length;
|
|
1810
1685
|
const lineStartIndices = [0];
|
|
1811
|
-
const crs = [];
|
|
1812
|
-
let normalizedCode = "";
|
|
1813
1686
|
for (let index = 0; index < len;) {
|
|
1814
1687
|
const c = origCode[index++];
|
|
1815
|
-
if (c === "\r")
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
normalizedCode += next;
|
|
1819
|
-
crs.push(index - 2);
|
|
1820
|
-
lineStartIndices.push(index);
|
|
1821
|
-
} else {
|
|
1822
|
-
normalizedCode += `\n${next}`;
|
|
1823
|
-
lineStartIndices.push(index - 1);
|
|
1824
|
-
}
|
|
1825
|
-
} else {
|
|
1826
|
-
normalizedCode += c;
|
|
1827
|
-
if (c === "\n") lineStartIndices.push(index);
|
|
1828
|
-
}
|
|
1688
|
+
if (c === "\r") if ((origCode[index++] || "") === "\n") lineStartIndices.push(index);
|
|
1689
|
+
else lineStartIndices.push(index - 1);
|
|
1690
|
+
else if (c === "\n") lineStartIndices.push(index);
|
|
1829
1691
|
}
|
|
1830
1692
|
this.lineStartIndices = lineStartIndices;
|
|
1831
1693
|
this.code = origCode;
|
|
1832
|
-
this.normalizedLineFeed = new NormalizedLineFeed(normalizedCode, crs);
|
|
1833
1694
|
}
|
|
1834
1695
|
getLocFromIndex(index) {
|
|
1835
1696
|
const lineNumber = sortedLastIndex(this.lineStartIndices, (target) => target - index);
|
|
@@ -1844,264 +1705,145 @@ var LinesAndColumns = class {
|
|
|
1844
1705
|
else if (this.lineStartIndices.length === lineIndex) return this.code.length + loc.column;
|
|
1845
1706
|
return this.code.length + loc.column;
|
|
1846
1707
|
}
|
|
1847
|
-
getNormalizedLineFeed() {
|
|
1848
|
-
return this.normalizedLineFeed;
|
|
1849
|
-
}
|
|
1850
|
-
};
|
|
1851
|
-
var NormalizedLineFeed = class {
|
|
1852
|
-
get needRemap() {
|
|
1853
|
-
return this.offsets.length > 0;
|
|
1854
|
-
}
|
|
1855
|
-
constructor(code, offsets) {
|
|
1856
|
-
this.code = code;
|
|
1857
|
-
this.offsets = offsets;
|
|
1858
|
-
if (offsets.length) {
|
|
1859
|
-
const cache = {};
|
|
1860
|
-
this.remapIndex = (index) => {
|
|
1861
|
-
let result = cache[index];
|
|
1862
|
-
if (result != null) return result;
|
|
1863
|
-
result = index;
|
|
1864
|
-
for (const offset of offsets) if (offset < result) result++;
|
|
1865
|
-
else break;
|
|
1866
|
-
return cache[index] = result;
|
|
1867
|
-
};
|
|
1868
|
-
} else this.remapIndex = (i) => i;
|
|
1869
|
-
}
|
|
1870
1708
|
};
|
|
1871
1709
|
//#endregion
|
|
1872
|
-
//#region src/
|
|
1873
|
-
/**
|
|
1874
|
-
* Parse code by `@astrojs/compiler`
|
|
1875
|
-
*/
|
|
1876
|
-
function parse(code, ctx) {
|
|
1877
|
-
const result = service.parse(code, { position: true });
|
|
1878
|
-
for (const { code, text, location, severity } of result.diagnostics || []) if (severity === 1) {
|
|
1879
|
-
ctx.originalAST = result.ast;
|
|
1880
|
-
throw new ParseError(`${text} [${code}]`, location, ctx);
|
|
1881
|
-
}
|
|
1882
|
-
if (!result.ast.children) result.ast.children = [];
|
|
1883
|
-
const htmlElement = result.ast.children.find((n) => n.type === "element" && n.name === "html");
|
|
1884
|
-
if (!result._adjusted) {
|
|
1885
|
-
if (htmlElement) adjustHTML(result.ast, htmlElement, ctx);
|
|
1886
|
-
fixLocations(result.ast, ctx);
|
|
1887
|
-
result._adjusted = true;
|
|
1888
|
-
}
|
|
1889
|
-
return result;
|
|
1890
|
-
}
|
|
1710
|
+
//#region src/errors.ts
|
|
1891
1711
|
/**
|
|
1892
|
-
*
|
|
1712
|
+
* Astro parse errors.
|
|
1893
1713
|
*/
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
const children = [...htmlElement.children];
|
|
1900
|
-
for (const child of children) {
|
|
1901
|
-
const offset = child.position?.start.offset;
|
|
1902
|
-
if (hasTokenAfter && offset != null) {
|
|
1903
|
-
if (isOffsetAfter(offset, htmlEnd)) {
|
|
1904
|
-
htmlElement.children.splice(htmlElement.children.indexOf(child), 1);
|
|
1905
|
-
ast.children.push(child);
|
|
1906
|
-
}
|
|
1907
|
-
}
|
|
1908
|
-
if (child.type === "element" && child.name === "body") adjustHTMLBody(ast, htmlElement, htmlEnd, hasTokenAfter, child, ctx, isOffsetAfter);
|
|
1909
|
-
}
|
|
1910
|
-
/**
|
|
1911
|
-
* Build a comparator used only for matching compiler offsets against
|
|
1912
|
-
* positions derived from `ctx.code`.
|
|
1913
|
-
*
|
|
1914
|
-
* The raw offset check is important for laziness: if the compiler offset is
|
|
1915
|
-
* already before the threshold, remapping cannot make it jump forward past
|
|
1916
|
-
* that threshold, so we can reject it without any byte/code-unit work.
|
|
1917
|
-
*/
|
|
1918
|
-
function buildComparableOffsetComparator(code) {
|
|
1919
|
-
let remapOffset;
|
|
1920
|
-
const comparableOffsetCache = /* @__PURE__ */ new Map();
|
|
1921
|
-
return (offset, threshold) => {
|
|
1922
|
-
if (threshold > offset) return false;
|
|
1923
|
-
let comparableOffset = comparableOffsetCache.get(offset);
|
|
1924
|
-
if (comparableOffset == null) {
|
|
1925
|
-
remapOffset ||= buildComparableOffsetRemapper(code);
|
|
1926
|
-
comparableOffset = remapOffset(offset);
|
|
1927
|
-
comparableOffsetCache.set(offset, comparableOffset);
|
|
1928
|
-
}
|
|
1929
|
-
return threshold <= comparableOffset;
|
|
1930
|
-
};
|
|
1931
|
-
}
|
|
1714
|
+
var ParseError = class extends SyntaxError {
|
|
1715
|
+
index;
|
|
1716
|
+
lineNumber;
|
|
1717
|
+
column;
|
|
1718
|
+
originalAST;
|
|
1932
1719
|
/**
|
|
1933
|
-
*
|
|
1720
|
+
* Initialize this ParseError instance.
|
|
1934
1721
|
*/
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
const
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
codeUnitOffsets = [0];
|
|
1947
|
-
for (let asciiOffset = 1; asciiOffset <= index; asciiOffset++) {
|
|
1948
|
-
byteOffsets.push(asciiOffset);
|
|
1949
|
-
codeUnitOffsets.push(asciiOffset);
|
|
1950
|
-
}
|
|
1951
|
-
byteOffsets.push(nextByteOffset);
|
|
1952
|
-
codeUnitOffsets.push(nextIndex);
|
|
1953
|
-
}
|
|
1954
|
-
index = nextIndex;
|
|
1955
|
-
byteOffset = nextByteOffset;
|
|
1722
|
+
constructor(message, offset, ctx) {
|
|
1723
|
+
super(message);
|
|
1724
|
+
if (typeof offset === "number") {
|
|
1725
|
+
this.index = offset;
|
|
1726
|
+
const loc = ctx.getLocFromIndex(offset);
|
|
1727
|
+
this.lineNumber = loc.line;
|
|
1728
|
+
this.column = loc.column;
|
|
1729
|
+
} else {
|
|
1730
|
+
this.index = ctx.getIndexFromLoc(offset);
|
|
1731
|
+
this.lineNumber = offset.line;
|
|
1732
|
+
this.column = offset.column;
|
|
1956
1733
|
}
|
|
1957
|
-
|
|
1958
|
-
return (offset) => {
|
|
1959
|
-
const index = sortedLastIndex(byteOffsets, (target) => target - offset) - 1;
|
|
1960
|
-
return codeUnitOffsets[Math.max(index, 0)];
|
|
1961
|
-
};
|
|
1962
|
-
}
|
|
1963
|
-
/**
|
|
1964
|
-
* Get UTF-8 byte length for code point.
|
|
1965
|
-
*/
|
|
1966
|
-
function getUTF8ByteLength(codePoint) {
|
|
1967
|
-
if (codePoint <= 127) return 1;
|
|
1968
|
-
if (codePoint <= 2047) return 2;
|
|
1969
|
-
if (codePoint <= 65535) return 3;
|
|
1970
|
-
return 4;
|
|
1734
|
+
this.originalAST = ctx.originalAST;
|
|
1971
1735
|
}
|
|
1972
|
-
}
|
|
1736
|
+
};
|
|
1737
|
+
//#endregion
|
|
1738
|
+
//#region src/parser/astro-parser/parse.ts
|
|
1973
1739
|
/**
|
|
1974
|
-
*
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
bodyElement.children.splice(bodyElement.children.indexOf(child), 1);
|
|
1990
|
-
htmlElement.children.push(child);
|
|
1991
|
-
}
|
|
1992
|
-
}
|
|
1740
|
+
* Parse code by `@astrojs/compiler-rs`.
|
|
1741
|
+
*
|
|
1742
|
+
* The compiler returns locations in its own offset format. Normalize them here
|
|
1743
|
+
* before any later parser phase reads the AST, so the rest of the parser can
|
|
1744
|
+
* treat every `start`/`end` and diagnostic label as ESLint-compatible source
|
|
1745
|
+
* indexes.
|
|
1746
|
+
*/
|
|
1747
|
+
function parse$1(code, ctx) {
|
|
1748
|
+
const result = parse(code);
|
|
1749
|
+
normalizeLocations(result, code, ctx);
|
|
1750
|
+
for (const diagnostic of result.diagnostics || []) {
|
|
1751
|
+
if (diagnostic.severity !== "error") continue;
|
|
1752
|
+
ctx.originalAST = result.ast;
|
|
1753
|
+
const location = diagnostic.labels?.[0]?.start ?? 0;
|
|
1754
|
+
throw new ParseError(diagnostic.text, location, ctx);
|
|
1993
1755
|
}
|
|
1756
|
+
return result;
|
|
1994
1757
|
}
|
|
1995
1758
|
/**
|
|
1996
|
-
*
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
node.position.start.offset = start;
|
|
2034
|
-
const value = node.value.replace(/\s+/gu, "");
|
|
2035
|
-
for (const char of value) start = tokenIndex(ctx, char, start) + 1;
|
|
2036
|
-
start = skipSpaces(ctx.code, start);
|
|
2037
|
-
node.value = ctx.code.slice(node.position.start.offset, start);
|
|
2038
|
-
}
|
|
2039
|
-
}
|
|
2040
|
-
if (node.position.end) node.position.end.offset = start;
|
|
2041
|
-
} else if (node.type === "expression") {
|
|
2042
|
-
start = node.position.start.offset = tokenIndex(ctx, "{", start);
|
|
2043
|
-
start += 1;
|
|
2044
|
-
} else if (node.type === "doctype") {
|
|
2045
|
-
if (!node.position) node.position = {
|
|
2046
|
-
start: {},
|
|
2047
|
-
end: {}
|
|
2048
|
-
};
|
|
2049
|
-
if (!node.position.end) node.position.end = {};
|
|
2050
|
-
start = node.position.start.offset = tokenIndex(ctx, "<!", start);
|
|
2051
|
-
start += 2;
|
|
2052
|
-
start = node.position.end.offset = ctx.code.indexOf(">", start) + 1;
|
|
2053
|
-
} else if (node.type === "root") {}
|
|
2054
|
-
}, (node, [parent]) => {
|
|
2055
|
-
if (node.type === "attribute") {
|
|
2056
|
-
const attributes = parent.attributes;
|
|
2057
|
-
if (attributes[attributes.length - 1] === node) start = calcStartTagEndOffset(parent, ctx);
|
|
2058
|
-
} else if (node.type === "expression") start = tokenIndex(ctx, "}", start) + 1;
|
|
2059
|
-
else if (node.type === "fragment" || node.type === "element" || node.type === "component" || node.type === "custom-element") {
|
|
2060
|
-
if (!getSelfClosingTag(node, ctx)) {
|
|
2061
|
-
const closeTagStart = tokenIndexSafe(ctx.code, `</${node.name}`, start);
|
|
2062
|
-
if (closeTagStart != null) {
|
|
2063
|
-
start = closeTagStart + 2 + node.name.length;
|
|
2064
|
-
start = tokenIndex(ctx, ">", start) + 1;
|
|
2065
|
-
}
|
|
2066
|
-
}
|
|
2067
|
-
} else return;
|
|
2068
|
-
if (node.position.end) node.position.end.offset = start;
|
|
2069
|
-
});
|
|
2070
|
-
}
|
|
2071
|
-
/**
|
|
2072
|
-
* Fix locations
|
|
2073
|
-
*/
|
|
2074
|
-
function fixLocationForAttr(node, ctx, start) {
|
|
2075
|
-
if (node.kind === "empty") node.position.start.offset = tokenIndex(ctx, node.name, start);
|
|
2076
|
-
else if (node.kind === "quoted") node.position.start.offset = tokenIndex(ctx, node.name, start);
|
|
2077
|
-
else if (node.kind === "expression") node.position.start.offset = tokenIndex(ctx, node.name, start);
|
|
2078
|
-
else if (node.kind === "shorthand") node.position.start.offset = tokenIndex(ctx, "{", start);
|
|
2079
|
-
else if (node.kind === "spread") node.position.start.offset = tokenIndex(ctx, "{", start);
|
|
2080
|
-
else if (node.kind === "template-literal") node.position.start.offset = tokenIndex(ctx, node.name, start);
|
|
2081
|
-
else throw new ParseError(`Unknown attr kind: ${node.kind}`, node.position.start.offset, ctx);
|
|
2082
|
-
}
|
|
2083
|
-
/**
|
|
2084
|
-
* Get token index
|
|
2085
|
-
*/
|
|
2086
|
-
function tokenIndex(ctx, token, position) {
|
|
2087
|
-
const index = tokenIndexSafe(ctx.code, token, position);
|
|
2088
|
-
if (index == null) {
|
|
2089
|
-
const start = token.trim() === token ? skipSpaces(ctx.code, position) : position;
|
|
2090
|
-
throw new ParseError(`Unknown token at ${start}, expected: ${JSON.stringify(token)}, actual: ${JSON.stringify(ctx.code.slice(start, start + 10))}`, start, ctx);
|
|
1759
|
+
* Normalize compiler byte offsets to JavaScript string indices.
|
|
1760
|
+
*
|
|
1761
|
+
* `@astrojs/compiler-rs` reports `start`/`end` as UTF-8 byte offsets. ESLint
|
|
1762
|
+
* `range`, `Context`, and JavaScript string slicing all use UTF-16 code-unit
|
|
1763
|
+
* indexes. Those values are the same for ASCII, but diverge as soon as a
|
|
1764
|
+
* multibyte character appears before or inside a node. For example, the raw
|
|
1765
|
+
* compiler offset after `"あ"` is 3, while the JavaScript index is 1.
|
|
1766
|
+
*
|
|
1767
|
+
* Keep this conversion in the parse phase so downstream code does not need to
|
|
1768
|
+
* know which compiler produced the AST or remember to remap every lookup.
|
|
1769
|
+
*/
|
|
1770
|
+
function normalizeLocations(result, code, ctx) {
|
|
1771
|
+
const byteOffsetToIndex = buildByteOffsetToIndexMap(code);
|
|
1772
|
+
remapNodeLocations(result.ast, byteOffsetToIndex);
|
|
1773
|
+
for (const diagnostic of result.diagnostics || []) for (const label of diagnostic.labels || []) {
|
|
1774
|
+
label.start = byteOffsetToIndex(label.start);
|
|
1775
|
+
label.end = byteOffsetToIndex(label.end);
|
|
1776
|
+
const loc = ctx.getLocFromIndex(label.start);
|
|
1777
|
+
label.line = loc.line;
|
|
1778
|
+
label.column = loc.column;
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* Remap start/end properties on a compiler AST subtree.
|
|
1783
|
+
*
|
|
1784
|
+
* The compiler AST contains ESTree-compatible JavaScript nodes nested inside
|
|
1785
|
+
* Astro nodes. Walking generically keeps the location fix independent from the
|
|
1786
|
+
* exact node shape and prevents future compiler node additions from bypassing
|
|
1787
|
+
* the normalization.
|
|
1788
|
+
*/
|
|
1789
|
+
function remapNodeLocations(value, byteOffsetToIndex, seen = /* @__PURE__ */ new Set()) {
|
|
1790
|
+
if (!value || typeof value !== "object") return;
|
|
1791
|
+
if (seen.has(value)) return;
|
|
1792
|
+
seen.add(value);
|
|
1793
|
+
if (Array.isArray(value)) {
|
|
1794
|
+
for (const child of value) remapNodeLocations(child, byteOffsetToIndex, seen);
|
|
1795
|
+
return;
|
|
2091
1796
|
}
|
|
2092
|
-
|
|
1797
|
+
const node = value;
|
|
1798
|
+
if (typeof node.start === "number") node.start = byteOffsetToIndex(node.start);
|
|
1799
|
+
if (typeof node.end === "number") node.end = byteOffsetToIndex(node.end);
|
|
1800
|
+
for (const child of Object.values(node)) remapNodeLocations(child, byteOffsetToIndex, seen);
|
|
1801
|
+
}
|
|
1802
|
+
/**
|
|
1803
|
+
* Build a mapper from compiler byte offsets to JavaScript string indices.
|
|
1804
|
+
*
|
|
1805
|
+
* The table records both coordinates at each source character boundary. The
|
|
1806
|
+
* returned function can then translate any compiler offset with a binary
|
|
1807
|
+
* search. If the offset falls between boundaries, it returns the previous
|
|
1808
|
+
* JavaScript index; this keeps error locations stable even if the compiler
|
|
1809
|
+
* points into a multibyte character boundary.
|
|
1810
|
+
*/
|
|
1811
|
+
function buildByteOffsetToIndexMap(source) {
|
|
1812
|
+
const byteOffsets = [0];
|
|
1813
|
+
const codeUnitOffsets = [0];
|
|
1814
|
+
let byteOffset = 0;
|
|
1815
|
+
for (let index = 0; index < source.length;) {
|
|
1816
|
+
const codePoint = source.codePointAt(index);
|
|
1817
|
+
const nextIndex = index + (codePoint > 65535 ? 2 : 1);
|
|
1818
|
+
byteOffset += getUTF8ByteLength(codePoint);
|
|
1819
|
+
byteOffsets.push(byteOffset);
|
|
1820
|
+
codeUnitOffsets.push(nextIndex);
|
|
1821
|
+
index = nextIndex;
|
|
1822
|
+
}
|
|
1823
|
+
return (offset) => {
|
|
1824
|
+
let low = 0;
|
|
1825
|
+
let high = byteOffsets.length - 1;
|
|
1826
|
+
while (low <= high) {
|
|
1827
|
+
const mid = Math.floor((low + high) / 2);
|
|
1828
|
+
const value = byteOffsets[mid];
|
|
1829
|
+
if (value === offset) return codeUnitOffsets[mid];
|
|
1830
|
+
if (value < offset) low = mid + 1;
|
|
1831
|
+
else high = mid - 1;
|
|
1832
|
+
}
|
|
1833
|
+
return codeUnitOffsets[Math.max(high, 0)] ?? 0;
|
|
1834
|
+
};
|
|
2093
1835
|
}
|
|
2094
|
-
/**
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
return null;
|
|
1836
|
+
/** Get the UTF-8 byte length for a Unicode code point. */
|
|
1837
|
+
function getUTF8ByteLength(codePoint) {
|
|
1838
|
+
if (codePoint <= 127) return 1;
|
|
1839
|
+
if (codePoint <= 2047) return 2;
|
|
1840
|
+
if (codePoint <= 65535) return 3;
|
|
1841
|
+
return 4;
|
|
2101
1842
|
}
|
|
2102
1843
|
//#endregion
|
|
2103
1844
|
//#region src/parser/lru-cache.ts
|
|
2104
1845
|
var LruCache = class extends Map {
|
|
1846
|
+
capacity;
|
|
2105
1847
|
constructor(capacity) {
|
|
2106
1848
|
super();
|
|
2107
1849
|
this.capacity = capacity;
|
|
@@ -2135,64 +1877,22 @@ function parseTemplate$1(code, filePath) {
|
|
|
2135
1877
|
const cache = lruCache.get(code);
|
|
2136
1878
|
if (cache) return cache;
|
|
2137
1879
|
const ctx = new Context(code, filePath);
|
|
2138
|
-
const normalized = ctx.locs.getNormalizedLineFeed();
|
|
2139
|
-
const ctxForAstro = normalized.needRemap ? new Context(normalized.code, filePath) : ctx;
|
|
2140
1880
|
try {
|
|
2141
|
-
const result = parse(normalized?.code ?? code, ctxForAstro);
|
|
2142
|
-
if (normalized.needRemap) {
|
|
2143
|
-
remap(result, normalized, code, ctxForAstro);
|
|
2144
|
-
ctx.originalAST = ctxForAstro.originalAST;
|
|
2145
|
-
}
|
|
2146
1881
|
const templateResult = {
|
|
2147
|
-
result,
|
|
1882
|
+
result: parse$1(code, ctx),
|
|
2148
1883
|
context: ctx
|
|
2149
1884
|
};
|
|
2150
1885
|
lruCache.set(code, templateResult);
|
|
2151
1886
|
return templateResult;
|
|
2152
1887
|
} catch (e) {
|
|
2153
1888
|
if (typeof e.pos === "number") {
|
|
2154
|
-
const err = new ParseError(e.message,
|
|
1889
|
+
const err = new ParseError(e.message, e.pos, ctx);
|
|
2155
1890
|
err.astroCompilerError = e;
|
|
2156
1891
|
throw err;
|
|
2157
1892
|
}
|
|
2158
1893
|
throw e;
|
|
2159
1894
|
}
|
|
2160
1895
|
}
|
|
2161
|
-
/** Remap */
|
|
2162
|
-
function remap(result, normalized, originalCode, ctxForAstro) {
|
|
2163
|
-
const remapDataMap = /* @__PURE__ */ new Map();
|
|
2164
|
-
walk(result.ast, normalized.code, (node) => {
|
|
2165
|
-
const start = normalized.remapIndex(node.position.start.offset);
|
|
2166
|
-
let end, value;
|
|
2167
|
-
if (node.position.end) {
|
|
2168
|
-
end = normalized.remapIndex(node.position.end.offset);
|
|
2169
|
-
if (node.position.start.offset === start && node.position.end.offset === end) return;
|
|
2170
|
-
}
|
|
2171
|
-
if (node.type === "text") value = originalCode.slice(start, normalized.remapIndex(getEndOffset(node, ctxForAstro)));
|
|
2172
|
-
else if (node.type === "comment") value = originalCode.slice(start + 4, normalized.remapIndex(getEndOffset(node, ctxForAstro)) - 3);
|
|
2173
|
-
else if (node.type === "attribute") {
|
|
2174
|
-
if (node.kind !== "empty" && node.kind !== "shorthand" && node.kind !== "spread") {
|
|
2175
|
-
let valueStart = normalized.remapIndex(calcAttributeValueStartOffset(node, ctxForAstro));
|
|
2176
|
-
let valueEnd = normalized.remapIndex(calcAttributeEndOffset(node, ctxForAstro));
|
|
2177
|
-
if (node.kind !== "quoted" || originalCode[valueStart] === "\"" || originalCode[valueStart] === "'") {
|
|
2178
|
-
valueStart++;
|
|
2179
|
-
valueEnd--;
|
|
2180
|
-
}
|
|
2181
|
-
value = originalCode.slice(valueStart, valueEnd);
|
|
2182
|
-
}
|
|
2183
|
-
}
|
|
2184
|
-
remapDataMap.set(node, {
|
|
2185
|
-
start,
|
|
2186
|
-
end,
|
|
2187
|
-
value
|
|
2188
|
-
});
|
|
2189
|
-
}, (_node) => {});
|
|
2190
|
-
for (const [node, remapData] of remapDataMap) {
|
|
2191
|
-
node.position.start.offset = remapData.start;
|
|
2192
|
-
if (node.position.end) node.position.end.offset = remapData.end;
|
|
2193
|
-
if (node.type === "text" || node.type === "comment" || node.type === "attribute" && node.kind !== "empty" && node.kind !== "shorthand" && node.kind !== "spread") node.value = remapData.value;
|
|
2194
|
-
}
|
|
2195
|
-
}
|
|
2196
1896
|
//#endregion
|
|
2197
1897
|
//#region src/context/resolve-parser/espree.ts
|
|
2198
1898
|
let espreeCache = null;
|
|
@@ -2240,8 +1940,9 @@ function getParser(attrs, parser) {
|
|
|
2240
1940
|
//#region src/context/parser-options.ts
|
|
2241
1941
|
const TS_PARSER_NAMES = ["@typescript-eslint/parser", "typescript-eslint-parser-for-extra-files"];
|
|
2242
1942
|
var ParserOptionsContext = class {
|
|
1943
|
+
parserOptions;
|
|
1944
|
+
state = {};
|
|
2243
1945
|
constructor(options) {
|
|
2244
|
-
this.state = {};
|
|
2245
1946
|
const parserOptions = {
|
|
2246
1947
|
ecmaVersion: 2020,
|
|
2247
1948
|
sourceType: "module",
|
|
@@ -2398,11 +2099,8 @@ function parseTemplate(code) {
|
|
|
2398
2099
|
const parsed = parseTemplate$1(code);
|
|
2399
2100
|
return {
|
|
2400
2101
|
result: parsed.result,
|
|
2401
|
-
getEndOffset: (node) => getEndOffset(node, parsed.context),
|
|
2402
|
-
calcAttributeValueStartOffset: (node) => calcAttributeValueStartOffset(node, parsed.context),
|
|
2403
|
-
calcAttributeEndOffset: (node) => calcAttributeEndOffset(node, parsed.context),
|
|
2404
2102
|
walk(parent, enter, leave) {
|
|
2405
|
-
walk(parent,
|
|
2103
|
+
walk(parent, enter, leave);
|
|
2406
2104
|
},
|
|
2407
2105
|
getLocFromIndex: (index) => parsed.context.getLocFromIndex(index),
|
|
2408
2106
|
getIndexFromLoc: (loc) => parsed.context.locs.getIndexFromLoc(loc)
|
|
@@ -2414,7 +2112,7 @@ var ast_exports = /* @__PURE__ */ __exportAll({});
|
|
|
2414
2112
|
//#endregion
|
|
2415
2113
|
//#region package.json
|
|
2416
2114
|
var name = "astro-eslint-parser";
|
|
2417
|
-
var version = "
|
|
2115
|
+
var version = "3.0.0";
|
|
2418
2116
|
//#endregion
|
|
2419
2117
|
//#region src/meta.ts
|
|
2420
2118
|
var meta_exports = /* @__PURE__ */ __exportAll({
|