@happyvertical/smrt-scanner 0.40.27 → 0.40.29
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/AGENTS.md +18 -0
- package/dist/chunks/{scanner-Di4QAFXG.js → scanner-_R1muI_s.js} +311 -23
- package/dist/chunks/scanner-_R1muI_s.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunks/scanner-Di4QAFXG.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -67,3 +67,21 @@ executes the source.
|
|
|
67
67
|
unwrapped from their `UnaryExpression` before the check.
|
|
68
68
|
- **Static property capture**: captures `uiSlots` and `adminRoutes` for agent
|
|
69
69
|
manifest generation.
|
|
70
|
+
- **`@smrt()` config spreads resolve only against unescaped same-file `const`s** (issue
|
|
71
|
+
#2100). `@smrt({ ...INTERNAL_SURFACE })` works when `INTERNAL_SURFACE` is a
|
|
72
|
+
module-scope `const` object literal in the same file (`as const` and
|
|
73
|
+
`export const` included); a constant may spread an earlier constant. Anything
|
|
74
|
+
else — an imported constant, a `let`, a function call, a computed non-literal
|
|
75
|
+
key, shorthand or non-literal value, mutation/alias/escape, or a spread inside
|
|
76
|
+
an include array — is reported as a `severity: 'error'` scan diagnostic,
|
|
77
|
+
never silently dropped. `const` prevents rebinding but not property mutation,
|
|
78
|
+
and object spread is shallow, so only binding-aware spread references whose
|
|
79
|
+
shared nested values remain safe through the decorator use are trusted.
|
|
80
|
+
Silence would be unsafe: a dropped spread can remove `api`/`mcp`/`cli`, and an
|
|
81
|
+
ABSENT surface key means default-open full CRUD, so a quiet drop turns a
|
|
82
|
+
deliberate lockdown into a published surface.
|
|
83
|
+
A constant whose own initializer holds an unresolvable spread
|
|
84
|
+
(`const CFG = { ...IMPORTED }`) is tracked as **tainted**: it still resolves,
|
|
85
|
+
but its taint replays into the diagnostics of every decorator that spreads
|
|
86
|
+
it, transitively through constant chains. Without that the silent drop simply
|
|
87
|
+
moves one level up. An unused tainted constant reports nothing.
|
|
@@ -374,10 +374,15 @@ function parseFile(filePath) {
|
|
|
374
374
|
const importAliases = extractImportAliases(program.body);
|
|
375
375
|
typeAliases = extractTypeAliases(program.body);
|
|
376
376
|
smrtImports = extractSmrtImports(program.body);
|
|
377
|
+
const ctx = {
|
|
378
|
+
constants: extractModuleObjectConstants(program.body, sourceText),
|
|
379
|
+
unresolved: []
|
|
380
|
+
};
|
|
377
381
|
for (const node of program.body) {
|
|
378
|
-
const extracted = extractClassFromNode(node, filePath, sourceText, importAliases);
|
|
382
|
+
const extracted = extractClassFromNode(node, filePath, sourceText, importAliases, ctx);
|
|
379
383
|
if (extracted) classes.push(extracted);
|
|
380
384
|
}
|
|
385
|
+
reportUnresolvedSpreads(ctx.unresolved, filePath, sourceText, errors);
|
|
381
386
|
}
|
|
382
387
|
} catch (error) {
|
|
383
388
|
errors.push({
|
|
@@ -422,10 +427,15 @@ function parseSource(sourceText, filename = "test.ts") {
|
|
|
422
427
|
const importAliases = extractImportAliases(program.body);
|
|
423
428
|
typeAliases = extractTypeAliases(program.body);
|
|
424
429
|
smrtImports = extractSmrtImports(program.body);
|
|
430
|
+
const ctx = {
|
|
431
|
+
constants: extractModuleObjectConstants(program.body, sourceText),
|
|
432
|
+
unresolved: []
|
|
433
|
+
};
|
|
425
434
|
for (const node of program.body) {
|
|
426
|
-
const extracted = extractClassFromNode(node, filename, sourceText, importAliases);
|
|
435
|
+
const extracted = extractClassFromNode(node, filename, sourceText, importAliases, ctx);
|
|
427
436
|
if (extracted) classes.push(extracted);
|
|
428
437
|
}
|
|
438
|
+
reportUnresolvedSpreads(ctx.unresolved, filename, sourceText, errors);
|
|
429
439
|
}
|
|
430
440
|
} catch (error) {
|
|
431
441
|
errors.push({
|
|
@@ -526,24 +536,253 @@ function extractTypeAliases(body) {
|
|
|
526
536
|
}
|
|
527
537
|
return aliases;
|
|
528
538
|
}
|
|
529
|
-
function
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
539
|
+
function unwrapTypeAssertion(node) {
|
|
540
|
+
let current = node;
|
|
541
|
+
for (let depth = 0; current && depth < 8; depth++) {
|
|
542
|
+
const type = current.type;
|
|
543
|
+
if (type === "TSAsExpression" || type === "TSSatisfiesExpression" || type === "TSNonNullExpression" || type === "TSTypeAssertion" || type === "ParenthesizedExpression") {
|
|
544
|
+
current = current.expression;
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
return current;
|
|
548
|
+
}
|
|
549
|
+
return current;
|
|
550
|
+
}
|
|
551
|
+
function extractModuleObjectConstants(body, sourceText) {
|
|
552
|
+
const constants = /* @__PURE__ */ new Map();
|
|
553
|
+
for (const node of body) {
|
|
554
|
+
const decl = node.type === "VariableDeclaration" ? node : node.type === "ExportNamedDeclaration" && node.declaration?.type === "VariableDeclaration" ? node.declaration : null;
|
|
555
|
+
if (decl?.kind !== "const") continue;
|
|
556
|
+
for (const declarator of decl.declarations) {
|
|
557
|
+
if (declarator.id?.type !== "Identifier") continue;
|
|
558
|
+
const name = declarator.id.name;
|
|
559
|
+
if (!name || !isSafeObjectKey(name)) continue;
|
|
560
|
+
const init = unwrapTypeAssertion(declarator.init);
|
|
561
|
+
if (init?.type !== "ObjectExpression") continue;
|
|
562
|
+
const unresolved = [];
|
|
563
|
+
const dependencies = [];
|
|
564
|
+
const value = extractObjectLiteral(init, sourceText, {
|
|
565
|
+
constants,
|
|
566
|
+
unresolved,
|
|
567
|
+
dependencies,
|
|
568
|
+
requireLiteralValues: true
|
|
569
|
+
});
|
|
570
|
+
constants.set(name, {
|
|
571
|
+
value,
|
|
572
|
+
unresolved,
|
|
573
|
+
unsafeReferences: [],
|
|
574
|
+
dependencies
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
taintUnsafeModuleConstantReferences(body, constants, sourceText);
|
|
579
|
+
propagateModuleConstantTaint(constants);
|
|
580
|
+
return constants;
|
|
581
|
+
}
|
|
582
|
+
function taintUnsafeModuleConstantReferences(body, constants, sourceText) {
|
|
583
|
+
if (constants.size === 0) return;
|
|
584
|
+
const tainted = /* @__PURE__ */ new Set();
|
|
585
|
+
const visit = (value, ancestors, shadowed) => {
|
|
586
|
+
if (Array.isArray(value)) {
|
|
587
|
+
for (const entry of value) visit(entry, ancestors, shadowed);
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
if (!value || typeof value !== "object") return;
|
|
591
|
+
const node = value;
|
|
592
|
+
if (typeof node.type !== "string") {
|
|
593
|
+
for (const child of Object.values(node)) visit(child, ancestors, shadowed);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
if (node.type === "Identifier" && typeof node.name === "string" && constants.has(node.name) && !shadowed.has(node.name) && !isTypeOnlyReference(node, ancestors) && !isSafeModuleConstantReference(node, ancestors, constants)) {
|
|
597
|
+
const name = node.name;
|
|
598
|
+
if (!tainted.has(name)) {
|
|
599
|
+
tainted.add(name);
|
|
600
|
+
const unresolved = {
|
|
601
|
+
expression: sliceSource(node, sourceText) ?? name,
|
|
602
|
+
start: typeof node.start === "number" ? node.start : void 0
|
|
603
|
+
};
|
|
604
|
+
const constant = constants.get(name);
|
|
605
|
+
constant?.unsafeReferences.push(unresolved);
|
|
606
|
+
constant?.unresolved.push(unresolved);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
const nextShadowed = createsLexicalScope(node) ? /* @__PURE__ */ new Set([...shadowed, ...collectScopeBindings(node)]) : shadowed;
|
|
610
|
+
const nextAncestors = [...ancestors, node];
|
|
611
|
+
for (const [key, child] of Object.entries(node)) {
|
|
612
|
+
if (key === "loc" || key === "range" || key === "start" || key === "end") continue;
|
|
613
|
+
visit(child, nextAncestors, nextShadowed);
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
visit(body, [], /* @__PURE__ */ new Set());
|
|
617
|
+
}
|
|
618
|
+
function createsLexicalScope(node) {
|
|
619
|
+
return node.type === "BlockStatement" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression" || node.type === "CatchClause" || node.type === "ForStatement" || node.type === "ForInStatement" || node.type === "ForOfStatement" || node.type === "ClassDeclaration" || node.type === "ClassExpression";
|
|
620
|
+
}
|
|
621
|
+
function collectScopeBindings(node) {
|
|
622
|
+
const bindings = /* @__PURE__ */ new Set();
|
|
623
|
+
const addPattern = (value) => {
|
|
624
|
+
if (!value || typeof value !== "object") return;
|
|
625
|
+
const pattern = value;
|
|
626
|
+
if (pattern.type === "Identifier" && typeof pattern.name === "string") {
|
|
627
|
+
bindings.add(pattern.name);
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
if (pattern.type === "RestElement") {
|
|
631
|
+
addPattern(pattern.argument);
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
if (pattern.type === "AssignmentPattern") {
|
|
635
|
+
addPattern(pattern.left);
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
if (pattern.type === "ArrayPattern" && Array.isArray(pattern.elements)) {
|
|
639
|
+
for (const element of pattern.elements) addPattern(element);
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (pattern.type === "ObjectPattern" && Array.isArray(pattern.properties)) for (const property of pattern.properties) {
|
|
643
|
+
if (!property || typeof property !== "object") continue;
|
|
644
|
+
const propertyNode = property;
|
|
645
|
+
addPattern(propertyNode.type === "RestElement" ? propertyNode.argument : propertyNode.value);
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
|
|
649
|
+
if (node.type !== "ArrowFunctionExpression") addPattern(node.id);
|
|
650
|
+
if (Array.isArray(node.params)) for (const parameter of node.params) addPattern(parameter);
|
|
651
|
+
collectFunctionVarBindings(node.body, bindings);
|
|
652
|
+
} else if (node.type === "CatchClause") addPattern(node.param);
|
|
653
|
+
else if (node.type === "ForStatement" || node.type === "ForInStatement" || node.type === "ForOfStatement") {
|
|
654
|
+
const declaration = node.type === "ForStatement" ? node.init : node.left;
|
|
655
|
+
if (declaration && typeof declaration === "object" && declaration.type === "VariableDeclaration") for (const declarator of declaration.declarations) addPattern(declarator.id);
|
|
656
|
+
} else if ((node.type === "ClassDeclaration" || node.type === "ClassExpression") && node.id) addPattern(node.id);
|
|
657
|
+
if (node.type === "BlockStatement" && Array.isArray(node.body)) for (const statement of node.body) {
|
|
658
|
+
const declaration = statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
|
|
659
|
+
if (declaration?.type === "VariableDeclaration") for (const declarator of declaration.declarations) addPattern(declarator.id);
|
|
660
|
+
else if (declaration?.type === "FunctionDeclaration" || declaration?.type === "ClassDeclaration") addPattern(declaration.id);
|
|
661
|
+
}
|
|
662
|
+
return bindings;
|
|
663
|
+
}
|
|
664
|
+
function collectFunctionVarBindings(value, bindings) {
|
|
665
|
+
if (Array.isArray(value)) {
|
|
666
|
+
for (const entry of value) collectFunctionVarBindings(entry, bindings);
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
if (!value || typeof value !== "object") return;
|
|
670
|
+
const node = value;
|
|
671
|
+
if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") return;
|
|
672
|
+
if (node.type === "VariableDeclaration" && node.kind === "var") for (const declarator of node.declarations) {
|
|
673
|
+
const id = declarator.id;
|
|
674
|
+
if (id?.type === "Identifier" && typeof id.name === "string") bindings.add(id.name);
|
|
675
|
+
}
|
|
676
|
+
for (const [key, child] of Object.entries(node)) {
|
|
677
|
+
if (key === "loc" || key === "range" || key === "start" || key === "end") continue;
|
|
678
|
+
collectFunctionVarBindings(child, bindings);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
function isTypeOnlyReference(identifier, ancestors) {
|
|
682
|
+
let child = identifier;
|
|
683
|
+
for (let index = ancestors.length - 1; index >= 0; index--) {
|
|
684
|
+
const parent = ancestors[index];
|
|
685
|
+
if ((parent.type === "TSAsExpression" || parent.type === "TSSatisfiesExpression" || parent.type === "TSNonNullExpression" || parent.type === "TSTypeAssertion") && parent.expression === child) {
|
|
686
|
+
child = parent;
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
if (typeof parent.type === "string" && parent.type.startsWith("TS")) return true;
|
|
690
|
+
break;
|
|
691
|
+
}
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
function propagateModuleConstantTaint(constants) {
|
|
695
|
+
let changed = true;
|
|
696
|
+
while (changed) {
|
|
697
|
+
changed = false;
|
|
698
|
+
for (const constant of constants.values()) for (const dependency of constant.dependencies) {
|
|
699
|
+
const source = constants.get(dependency.name);
|
|
700
|
+
if (!source) continue;
|
|
701
|
+
for (const unresolved of source.unresolved) {
|
|
702
|
+
if (dependency.start !== void 0 && unresolved.start !== void 0 && unresolved.start > dependency.start && !hasNestedReferenceValue(source.value)) continue;
|
|
703
|
+
if (!constant.unresolved.some((existing) => existing.expression === unresolved.expression && existing.start === unresolved.start)) {
|
|
704
|
+
constant.unresolved.push(unresolved);
|
|
705
|
+
changed = true;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
if (hasNestedReferenceValue(source.value)) for (const unsafeReference of constant.unsafeReferences) {
|
|
709
|
+
const matchesUnsafeReference = (existing) => existing.expression === unsafeReference.expression && existing.start === unsafeReference.start;
|
|
710
|
+
if (!source.unresolved.some(matchesUnsafeReference)) {
|
|
711
|
+
source.unresolved.push(unsafeReference);
|
|
712
|
+
changed = true;
|
|
713
|
+
}
|
|
714
|
+
if (!source.unsafeReferences.some(matchesUnsafeReference)) {
|
|
715
|
+
source.unsafeReferences.push(unsafeReference);
|
|
716
|
+
changed = true;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
function hasNestedReferenceValue(value) {
|
|
723
|
+
return Object.values(value).some((entry) => entry !== null && typeof entry === "object");
|
|
724
|
+
}
|
|
725
|
+
function isSafeModuleConstantReference(identifier, ancestors, constants) {
|
|
726
|
+
let child = identifier;
|
|
727
|
+
for (let index = ancestors.length - 1; index >= 0; index--) {
|
|
728
|
+
const parent = ancestors[index];
|
|
729
|
+
if (parent.type === "VariableDeclarator" && parent.id === child) return true;
|
|
730
|
+
if (parent.type === "Property" && parent.key === child && parent.computed === false && parent.shorthand === false) return true;
|
|
731
|
+
if (parent.type === "MemberExpression" && parent.property === child && parent.computed === false) return true;
|
|
732
|
+
if ((parent.type === "TSAsExpression" || parent.type === "TSSatisfiesExpression" || parent.type === "TSNonNullExpression" || parent.type === "TSTypeAssertion" || parent.type === "ParenthesizedExpression") && parent.expression === child) {
|
|
733
|
+
child = parent;
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
if (parent.type === "SpreadElement" && parent.argument === child) {
|
|
737
|
+
const name = typeof identifier.name === "string" ? identifier.name : void 0;
|
|
738
|
+
const constant = name ? constants.get(name) : void 0;
|
|
739
|
+
if (!constant || !hasNestedReferenceValue(constant.value)) return true;
|
|
740
|
+
if ([...constants.values()].some((candidate) => candidate.dependencies.some((dependency) => dependency.name === name && dependency.start === parent.start))) return true;
|
|
741
|
+
return ancestors.some((ancestor) => {
|
|
742
|
+
if (ancestor.type !== "Decorator" || !ancestor.expression || typeof ancestor.expression !== "object") return false;
|
|
743
|
+
const expression = ancestor.expression;
|
|
744
|
+
if (expression.type !== "CallExpression" || !expression.callee || typeof expression.callee !== "object") return false;
|
|
745
|
+
const callee = expression.callee;
|
|
746
|
+
return callee.type === "Identifier" && callee.name === "smrt";
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
return false;
|
|
750
|
+
}
|
|
751
|
+
return false;
|
|
752
|
+
}
|
|
753
|
+
function reportUnresolvedSpreads(unresolved, filePath, sourceText, errors) {
|
|
754
|
+
for (const spread of unresolved) {
|
|
755
|
+
const loc = spread.start === void 0 ? void 0 : getLineColumn(sourceText, spread.start);
|
|
756
|
+
errors.push({
|
|
757
|
+
message: `Cannot statically resolve \`${spread.expression}\` while expanding a @smrt() config. Only literal keys/values and unescaped module-scope \`const\` object literals in the same file are supported. Inline the keys or remove the mutation/alias \u2014 an unresolved expression would drop api/mcp/cli from the manifest, and an absent surface defaults to open.`,
|
|
758
|
+
filePath,
|
|
759
|
+
line: loc?.line,
|
|
760
|
+
column: loc?.column,
|
|
761
|
+
severity: "error"
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
function extractClassFromNode(node, filePath, sourceText, importAliases, ctx) {
|
|
766
|
+
if (node.type === "ExportNamedDeclaration" && node.declaration) return extractClassFromNode(node.declaration, filePath, sourceText, importAliases, ctx);
|
|
767
|
+
if (node.type === "ExportDefaultDeclaration" && node.declaration) return extractClassFromNode(node.declaration, filePath, sourceText, importAliases, ctx);
|
|
768
|
+
if (node.type === "ClassDeclaration") return extractClassDeclaration(node, filePath, sourceText, importAliases, ctx);
|
|
533
769
|
return null;
|
|
534
770
|
}
|
|
535
|
-
function extractClassDeclaration(node, filePath, sourceText, importAliases) {
|
|
771
|
+
function extractClassDeclaration(node, filePath, sourceText, importAliases, ctx) {
|
|
536
772
|
const className = node.id?.name || "AnonymousClass";
|
|
537
773
|
const decorators = node.decorators || [];
|
|
538
774
|
const smrtDecorator = decorators.find((d) => isSmrtDecorator(d));
|
|
539
775
|
const reportDecorator = decorators.find((d) => isNamedDecorator(d, "report"));
|
|
540
776
|
const tenantScopedDecorator = decorators.find((d) => isNamedDecorator(d, "TenantScoped"));
|
|
541
777
|
const hasSmartDecorator = !!smrtDecorator;
|
|
542
|
-
const smrtConfig = smrtDecorator ? extractDecoratorConfig(smrtDecorator, sourceText
|
|
778
|
+
const smrtConfig = smrtDecorator ? extractDecoratorConfig(smrtDecorator, sourceText, ctx ? {
|
|
779
|
+
...ctx,
|
|
780
|
+
requireLiteralValues: true
|
|
781
|
+
} : ctx) : null;
|
|
543
782
|
const decoratorConfig = tenantScopedDecorator || reportDecorator ? {
|
|
544
783
|
...smrtConfig ?? {},
|
|
545
|
-
...reportDecorator ? { report: extractDecoratorConfig(reportDecorator, sourceText) } : {},
|
|
546
|
-
...tenantScopedDecorator ? { tenantScoped: extractDecoratorConfig(tenantScopedDecorator, sourceText) } : {}
|
|
784
|
+
...reportDecorator ? { report: extractDecoratorConfig(reportDecorator, sourceText, ctx) } : {},
|
|
785
|
+
...tenantScopedDecorator ? { tenantScoped: extractDecoratorConfig(tenantScopedDecorator, sourceText, ctx) } : {}
|
|
547
786
|
} : smrtConfig;
|
|
548
787
|
const { extendsClause, extendsTypeArg } = extractExtendsClause(node, importAliases);
|
|
549
788
|
const fields = [];
|
|
@@ -580,19 +819,45 @@ function isNamedDecorator(decorator, name) {
|
|
|
580
819
|
if (expr.type === "Identifier" && expr.name === name) return true;
|
|
581
820
|
return false;
|
|
582
821
|
}
|
|
583
|
-
function extractDecoratorConfig(decorator, sourceText) {
|
|
822
|
+
function extractDecoratorConfig(decorator, sourceText, ctx) {
|
|
584
823
|
const expr = decorator.expression;
|
|
585
824
|
if (expr.type === "CallExpression" && expr.arguments.length > 0) {
|
|
586
|
-
const arg = expr.arguments[0];
|
|
587
|
-
if (arg
|
|
825
|
+
const arg = unwrapTypeAssertion(expr.arguments[0]);
|
|
826
|
+
if (arg?.type === "ObjectExpression") return extractObjectLiteral(arg, sourceText, ctx);
|
|
588
827
|
}
|
|
589
828
|
return {};
|
|
590
829
|
}
|
|
591
|
-
function extractObjectLiteral(node, sourceText) {
|
|
830
|
+
function extractObjectLiteral(node, sourceText, ctx) {
|
|
592
831
|
const result = {};
|
|
593
|
-
for (const prop of node.properties)
|
|
594
|
-
|
|
595
|
-
|
|
832
|
+
for (const prop of node.properties) {
|
|
833
|
+
if (prop.type === "SpreadElement") {
|
|
834
|
+
const argument = unwrapTypeAssertion(prop.argument);
|
|
835
|
+
const constant = argument?.type === "Identifier" ? ctx?.constants.get(argument.name) : void 0;
|
|
836
|
+
const resolved = constant?.value ?? (argument?.type === "ObjectExpression" ? extractObjectLiteral(argument, sourceText, ctx) : void 0);
|
|
837
|
+
if (resolved) {
|
|
838
|
+
if (constant && ctx?.dependencies && argument?.type === "Identifier") ctx.dependencies.push({
|
|
839
|
+
name: argument.name,
|
|
840
|
+
start: prop.start
|
|
841
|
+
});
|
|
842
|
+
for (const [key, value] of Object.entries(resolved)) if (isSafeObjectKey(key)) result[key] = value;
|
|
843
|
+
if (constant?.unresolved.length && ctx && !ctx.dependencies) ctx.unresolved.push(...constant.unresolved.filter((unresolved) => hasNestedReferenceValue(constant.value) || prop.start === void 0 || unresolved.start === void 0 || unresolved.start <= prop.start));
|
|
844
|
+
} else if (ctx) ctx.unresolved.push({
|
|
845
|
+
expression: sliceSource(prop, sourceText) ?? "...<unknown>",
|
|
846
|
+
start: prop.start
|
|
847
|
+
});
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
if (prop.type === "Property") {
|
|
851
|
+
const key = getPropertyKey(prop.key);
|
|
852
|
+
if (prop.shorthand || prop.computed && !(prop.key.type === "Literal" && typeof prop.key.value === "string")) {
|
|
853
|
+
ctx?.unresolved.push({
|
|
854
|
+
expression: sliceSource(prop, sourceText) ?? "<unknown property>",
|
|
855
|
+
start: prop.start
|
|
856
|
+
});
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
if (key && isSafeObjectKey(key)) result[key] = extractValue(prop.value, sourceText, ctx);
|
|
860
|
+
}
|
|
596
861
|
}
|
|
597
862
|
return result;
|
|
598
863
|
}
|
|
@@ -601,7 +866,9 @@ function getPropertyKey(node) {
|
|
|
601
866
|
if (node.type === "Literal" && typeof node.value === "string") return node.value;
|
|
602
867
|
return null;
|
|
603
868
|
}
|
|
604
|
-
function extractValue(node, sourceText) {
|
|
869
|
+
function extractValue(node, sourceText, ctx) {
|
|
870
|
+
const unwrapped = unwrapTypeAssertion(node);
|
|
871
|
+
if (unwrapped && unwrapped !== node) return extractValue(unwrapped, sourceText, ctx);
|
|
605
872
|
switch (node.type) {
|
|
606
873
|
case "Literal": return node.value;
|
|
607
874
|
case "Identifier":
|
|
@@ -609,9 +876,18 @@ function extractValue(node, sourceText) {
|
|
|
609
876
|
if (node.name === "null") return null;
|
|
610
877
|
if (node.name === "true") return true;
|
|
611
878
|
if (node.name === "false") return false;
|
|
879
|
+
if (ctx?.requireLiteralValues) ctx.unresolved.push({
|
|
880
|
+
expression: sliceSource(node, sourceText) ?? node.name,
|
|
881
|
+
start: node.start
|
|
882
|
+
});
|
|
612
883
|
return node.name;
|
|
613
|
-
case "ArrayExpression":
|
|
614
|
-
|
|
884
|
+
case "ArrayExpression":
|
|
885
|
+
for (const el of node.elements) if (el && typeof el === "object" && el.type === "SpreadElement" && ctx) ctx.unresolved.push({
|
|
886
|
+
expression: sliceSource(el, sourceText) ?? "...<unknown>",
|
|
887
|
+
start: el.start
|
|
888
|
+
});
|
|
889
|
+
return node.elements.filter((el) => el !== null && typeof el === "object" && "type" in el && el.type !== "SpreadElement").map((el) => extractValue(el, sourceText, ctx));
|
|
890
|
+
case "ObjectExpression": return extractObjectLiteral(node, sourceText, ctx);
|
|
615
891
|
case "UnaryExpression":
|
|
616
892
|
if (node.operator === "-" && node.argument?.type === "Literal") {
|
|
617
893
|
const value = node.argument.value;
|
|
@@ -621,12 +897,24 @@ function extractValue(node, sourceText) {
|
|
|
621
897
|
case "CallExpression":
|
|
622
898
|
case "NewExpression": {
|
|
623
899
|
const src = sliceSource(node, sourceText);
|
|
624
|
-
if (src)
|
|
900
|
+
if (src) {
|
|
901
|
+
if (ctx?.requireLiteralValues) ctx.unresolved.push({
|
|
902
|
+
expression: src,
|
|
903
|
+
start: node.start
|
|
904
|
+
});
|
|
905
|
+
return src;
|
|
906
|
+
}
|
|
625
907
|
break;
|
|
626
908
|
}
|
|
627
909
|
}
|
|
628
910
|
const rawSrc = sliceSource(node, sourceText);
|
|
629
|
-
if (rawSrc)
|
|
911
|
+
if (rawSrc) {
|
|
912
|
+
if (ctx?.requireLiteralValues) ctx.unresolved.push({
|
|
913
|
+
expression: rawSrc,
|
|
914
|
+
start: node.start
|
|
915
|
+
});
|
|
916
|
+
return rawSrc;
|
|
917
|
+
}
|
|
630
918
|
}
|
|
631
919
|
function extractExtendsClause(node, importAliases) {
|
|
632
920
|
if (!node.superClass) return {
|
|
@@ -1817,4 +2105,4 @@ var OxcScanner = class {
|
|
|
1817
2105
|
//#endregion
|
|
1818
2106
|
export { parseSource as a, parseFile as i, ManifestAdapter as n, InheritanceResolver as o, extractSmrtImports as r, OxcScanner as t };
|
|
1819
2107
|
|
|
1820
|
-
//# sourceMappingURL=scanner-
|
|
2108
|
+
//# sourceMappingURL=scanner-_R1muI_s.js.map
|