@suss/adapter-typescript 0.14.0 → 0.15.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.
Files changed (3) hide show
  1. package/dist/index.cjs +142 -45
  2. package/dist/index.js +142 -45
  3. package/package.json +5 -5
package/dist/index.cjs CHANGED
@@ -2688,7 +2688,7 @@ var ORIGIN = {
2688
2688
  declaredBy: (origin, receiver) => receiver.callee !== null && origin.importedFrom.some(
2689
2689
  (module2) => methodDeclaredIn(receiver.callee, module2, receiver.resolve)
2690
2690
  ),
2691
- constructed: (origin, receiver) => madeFromNamed(origin, madeBy(receiver)) && origin.importedFrom.some(
2691
+ constructed: (origin, receiver) => madeFromNamed(origin, madeExpression(receiver)) && origin.importedFrom.some(
2692
2692
  (module2) => isImportedFrom(madeBy(receiver), module2)
2693
2693
  )
2694
2694
  };
@@ -2696,14 +2696,15 @@ function madeFromNamed(origin, made) {
2696
2696
  if (origin.origin !== "constructed" || origin.named === void 0) {
2697
2697
  return true;
2698
2698
  }
2699
- return origin.named.includes(made.getText());
2699
+ return origin.named.includes(constructorName(made));
2700
+ }
2701
+ function constructorName(made) {
2702
+ const expression = import_ts_morph14.Node.isNewExpression(made) || import_ts_morph14.Node.isCallExpression(made) ? made.getExpression() : made;
2703
+ return import_ts_morph14.Node.isPropertyAccessExpression(expression) ? expression.getName() : expression.getText();
2700
2704
  }
2701
2705
  var CALL_ORIGIN = {
2702
2706
  declaredBy: ORIGIN.declaredBy,
2703
- constructed: (origin, receiver) => madeFromNamed(
2704
- origin,
2705
- rootIdentifier(receiver.expression) ?? receiver.expression
2706
- ) && origin.importedFrom.some(
2707
+ constructed: (origin, receiver) => madeFromNamed(origin, receiver.expression) && origin.importedFrom.some(
2707
2708
  (module2) => isImportedFrom(
2708
2709
  rootIdentifier(receiver.expression) ?? receiver.expression,
2709
2710
  module2
@@ -2711,14 +2712,19 @@ var CALL_ORIGIN = {
2711
2712
  )
2712
2713
  };
2713
2714
  function madeBy(receiver) {
2715
+ const made = madeExpression(receiver);
2716
+ const expression = import_ts_morph14.Node.isNewExpression(made) || import_ts_morph14.Node.isCallExpression(made) ? made.getExpression() : made;
2717
+ return rootIdentifier(expression) ?? expression;
2718
+ }
2719
+ function madeExpression(receiver) {
2714
2720
  if (receiver.callee === null) {
2715
- return rootIdentifier(receiver.expression) ?? receiver.expression;
2721
+ return receiver.expression;
2716
2722
  }
2717
2723
  const written = receiver.resolve(receiver.callee.getExpression());
2718
2724
  if (written === null || !import_ts_morph14.Node.isNewExpression(written) && !import_ts_morph14.Node.isCallExpression(written)) {
2719
2725
  return receiver.callee;
2720
2726
  }
2721
- return rootIdentifier(written.getExpression()) ?? receiver.callee;
2727
+ return written;
2722
2728
  }
2723
2729
  function callOpsFor(call, resolveWrittenValue) {
2724
2730
  const resolve2 = resolveWrittenValue ?? (() => null);
@@ -2759,6 +2765,7 @@ function valueOpsFor(value, resolve2) {
2759
2765
  flag: () => literalFlag(written()),
2760
2766
  entries: (unsettled) => entriesOf(written(), unsettled, resolve2),
2761
2767
  items: () => itemsOf(written(), resolve2),
2768
+ asArg: () => effectArgOf(written()),
2762
2769
  property: (name) => {
2763
2770
  const object = written();
2764
2771
  const inside = import_ts_morph14.Node.isObjectLiteralExpression(object) ? initializerOf(object, name) : null;
@@ -3246,6 +3253,9 @@ var MAX_ARG_DEPTH = 8;
3246
3253
  function extractArgs(call) {
3247
3254
  return call.getArguments().map((arg) => extractArg(arg, MAX_ARG_DEPTH));
3248
3255
  }
3256
+ function effectArgOf(node) {
3257
+ return extractArg(node, MAX_ARG_DEPTH);
3258
+ }
3249
3259
  function extractArg(node, depth) {
3250
3260
  if (import_ts_morph15.Node.isAsExpression(node) || import_ts_morph15.Node.isTypeAssertion(node) || import_ts_morph15.Node.isSatisfiesExpression(node) || import_ts_morph15.Node.isNonNullExpression(node)) {
3251
3261
  return extractArg(node.getExpression(), depth);
@@ -7871,6 +7881,31 @@ function constructionRef(node, localNames, resolution) {
7871
7881
  // src/discovery/clientCall.ts
7872
7882
  var import_node_path4 = __toESM(require("path"), 1);
7873
7883
  var import_ts_morph34 = require("ts-morph");
7884
+
7885
+ // src/discovery/resolveImport.ts
7886
+ function resolveImportedLocalName(sourceFile, importModule, importName) {
7887
+ for (const importDecl of sourceFile.getImportDeclarations()) {
7888
+ if (importDecl.getModuleSpecifierValue() !== importModule) {
7889
+ continue;
7890
+ }
7891
+ for (const named2 of importDecl.getNamedImports()) {
7892
+ if (named2.getName() === importName || named2.getAliasNode()?.getText() === importName) {
7893
+ return named2.getAliasNode()?.getText() ?? named2.getName();
7894
+ }
7895
+ }
7896
+ const defaultImport = importDecl.getDefaultImport();
7897
+ if (defaultImport !== void 0 && defaultImport.getText() === importName) {
7898
+ return defaultImport.getText();
7899
+ }
7900
+ const namespaceImport = importDecl.getNamespaceImport();
7901
+ if (namespaceImport !== void 0 && namespaceImport.getText() === importName) {
7902
+ return namespaceImport.getText();
7903
+ }
7904
+ }
7905
+ return null;
7906
+ }
7907
+
7908
+ // src/discovery/clientCall.ts
7874
7909
  function discoverClientCalls(sourceFile, match, kind, resolution) {
7875
7910
  const results = [];
7876
7911
  const isGlobal = match.importModule === "global";
@@ -7889,7 +7924,7 @@ function discoverClientCalls(sourceFile, match, kind, resolution) {
7889
7924
  if (!isGlobal && importedLocalName !== null) {
7890
7925
  for (const varDecl of sourceFile.getVariableDeclarations()) {
7891
7926
  const init = varDecl.getInitializer();
7892
- if (init !== void 0 && import_ts_morph34.Node.isCallExpression(init) && isInstanceCreationCall(init, importedLocalName, match.factoryMethods)) {
7927
+ if (init !== void 0 && (import_ts_morph34.Node.isCallExpression(init) || import_ts_morph34.Node.isNewExpression(init)) && isInstanceCreationCall(init, importedLocalName, match.factoryMethods)) {
7893
7928
  clientVarNames.add(varDecl.getName());
7894
7929
  }
7895
7930
  }
@@ -7934,12 +7969,50 @@ function discoverClientCalls(sourceFile, match, kind, resolution) {
7934
7969
  });
7935
7970
  return results;
7936
7971
  }
7972
+ function clientReceiverCheckFor(sourceFile, match, resolution) {
7973
+ const localName = resolveImportedLocalName(
7974
+ sourceFile,
7975
+ match.importModule,
7976
+ match.importName
7977
+ );
7978
+ const constructedHere = /* @__PURE__ */ new Set();
7979
+ if (localName !== null) {
7980
+ sourceFile.forEachDescendant((node) => {
7981
+ if (import_ts_morph34.Node.isVariableDeclaration(node)) {
7982
+ const init = node.getInitializer();
7983
+ if (init !== void 0 && (import_ts_morph34.Node.isCallExpression(init) || import_ts_morph34.Node.isNewExpression(init)) && isInstanceCreationCall(init, localName, match.factoryMethods) || typedAsClient(node.getTypeNode(), localName)) {
7984
+ constructedHere.add(node.getName());
7985
+ }
7986
+ return;
7987
+ }
7988
+ if (import_ts_morph34.Node.isParameterDeclaration(node) && typedAsClient(node.getTypeNode(), localName)) {
7989
+ const written = node.getNameNode();
7990
+ if (import_ts_morph34.Node.isIdentifier(written)) {
7991
+ constructedHere.add(written.getText());
7992
+ }
7993
+ }
7994
+ });
7995
+ }
7996
+ const full = {
7997
+ type: "clientCall",
7998
+ importModule: match.importModule,
7999
+ importName: match.importName,
8000
+ ...match.factoryMethods === void 0 ? {} : { factoryMethods: match.factoryMethods }
8001
+ };
8002
+ return (subject) => localName !== null && import_ts_morph34.Node.isIdentifier(subject) && subject.getText() === localName || import_ts_morph34.Node.isIdentifier(subject) && constructedHere.has(subject.getText()) || resolvesToKnownInstance(subject, full, resolution);
8003
+ }
8004
+ function typedAsClient(typeNode, localName) {
8005
+ if (typeNode === void 0 || !import_ts_morph34.Node.isTypeReference(typeNode)) {
8006
+ return false;
8007
+ }
8008
+ return typeNode.getTypeName().getText() === localName;
8009
+ }
7937
8010
  function resolvesToKnownInstance(subject, match, resolution) {
7938
8011
  if (resolution === void 0 || match.importModule === "global") {
7939
8012
  return false;
7940
8013
  }
7941
8014
  const written = writtenNodeOf(subject, resolution);
7942
- if (written === null || !import_ts_morph34.Node.isCallExpression(written)) {
8015
+ if (written === null || !import_ts_morph34.Node.isCallExpression(written) && !import_ts_morph34.Node.isNewExpression(written)) {
7943
8016
  return false;
7944
8017
  }
7945
8018
  const writtenLocalName = resolvedImportLocalName(
@@ -9134,40 +9207,8 @@ function discoverGraphqlHookCalls(sourceFile, match, kind, resolution) {
9134
9207
 
9135
9208
  // src/discovery/graphqlImperativeCall.ts
9136
9209
  var import_ts_morph42 = require("ts-morph");
9137
-
9138
- // src/discovery/resolveImport.ts
9139
- function resolveImportedLocalName(sourceFile, importModule, importName) {
9140
- for (const importDecl of sourceFile.getImportDeclarations()) {
9141
- if (importDecl.getModuleSpecifierValue() !== importModule) {
9142
- continue;
9143
- }
9144
- for (const named2 of importDecl.getNamedImports()) {
9145
- if (named2.getName() === importName || named2.getAliasNode()?.getText() === importName) {
9146
- return named2.getAliasNode()?.getText() ?? named2.getName();
9147
- }
9148
- }
9149
- const defaultImport = importDecl.getDefaultImport();
9150
- if (defaultImport !== void 0 && defaultImport.getText() === importName) {
9151
- return defaultImport.getText();
9152
- }
9153
- const namespaceImport = importDecl.getNamespaceImport();
9154
- if (namespaceImport !== void 0 && namespaceImport.getText() === importName) {
9155
- return namespaceImport.getText();
9156
- }
9157
- }
9158
- return null;
9159
- }
9160
-
9161
- // src/discovery/graphqlImperativeCall.ts
9162
9210
  function discoverGraphqlImperativeCalls(sourceFile, match, kind, resolution) {
9163
- const localName = resolveImportedLocalName(
9164
- sourceFile,
9165
- match.importModule,
9166
- match.importName
9167
- );
9168
- if (localName === null) {
9169
- return [];
9170
- }
9211
+ const isTheClient = clientReceiverCheckFor(sourceFile, match, resolution);
9171
9212
  const methodSpec = /* @__PURE__ */ new Map();
9172
9213
  for (const method of match.methods) {
9173
9214
  methodSpec.set(method.methodName, {
@@ -9189,6 +9230,9 @@ function discoverGraphqlImperativeCalls(sourceFile, match, kind, resolution) {
9189
9230
  if (spec === void 0) {
9190
9231
  return;
9191
9232
  }
9233
+ if (!isTheClient(callee.getExpression())) {
9234
+ return;
9235
+ }
9192
9236
  const args = node.getArguments();
9193
9237
  if (args.length === 0 || !import_ts_morph42.Node.isObjectLiteralExpression(args[0])) {
9194
9238
  return;
@@ -10331,8 +10375,24 @@ function pathFromUrlNode(node, resolution) {
10331
10375
  if (import_ts_morph48.Node.isTemplateExpression(node)) {
10332
10376
  return pathFromTemplateUrl(node, resolution);
10333
10377
  }
10378
+ if (import_ts_morph48.Node.isBinaryExpression(node)) {
10379
+ const joined = joinedStringOf(node, resolution);
10380
+ return joined === void 0 ? void 0 : pathFromLiteralUrl(joined);
10381
+ }
10334
10382
  return void 0;
10335
10383
  }
10384
+ function joinedStringOf(node, resolution) {
10385
+ if (import_ts_morph48.Node.isStringLiteral(node) || import_ts_morph48.Node.isNoSubstitutionTemplateLiteral(node)) {
10386
+ return node.getLiteralValue();
10387
+ }
10388
+ if (import_ts_morph48.Node.isBinaryExpression(node) && node.getOperatorToken().getText() === "+") {
10389
+ const left = joinedStringOf(node.getLeft(), resolution);
10390
+ const right = joinedStringOf(node.getRight(), resolution);
10391
+ return left === void 0 || right === void 0 ? void 0 : left + right;
10392
+ }
10393
+ const written = resolution?.resolveWrittenValue(node) ?? null;
10394
+ return written === null || written === node ? void 0 : joinedStringOf(written, resolution);
10395
+ }
10336
10396
  function placeholderName(expr) {
10337
10397
  if (import_ts_morph48.Node.isIdentifier(expr)) {
10338
10398
  return expr.getText();
@@ -10665,18 +10725,38 @@ function extractRouteInfoFromBinding(call, methodName, binding, resolution) {
10665
10725
  var import_ts_morph50 = require("ts-morph");
10666
10726
  function discoverRegistrationLoops(sourceFile, match, kind, resolution) {
10667
10727
  const results = [];
10728
+ const routables = routableNames(sourceFile, match);
10729
+ if (routables !== null && routables.size === 0) {
10730
+ return results;
10731
+ }
10668
10732
  sourceFile.forEachDescendant((node) => {
10669
10733
  if (!import_ts_morph50.Node.isForOfStatement(node)) {
10670
10734
  return;
10671
10735
  }
10672
- const expanded = tryExpandLoop(node, match, kind, resolution);
10736
+ const expanded = tryExpandLoop(node, match, kind, resolution, routables);
10673
10737
  if (expanded !== null) {
10674
10738
  results.push(...expanded);
10675
10739
  }
10676
10740
  });
10677
10741
  return results;
10678
10742
  }
10679
- function tryExpandLoop(loop, match, kind, resolution) {
10743
+ function routableNames(sourceFile, match) {
10744
+ if (match.receiver === void 0) {
10745
+ return null;
10746
+ }
10747
+ const names = /* @__PURE__ */ new Set();
10748
+ for (const importName of match.receiver.importNames) {
10749
+ for (const name of registrationSubjectsOf(
10750
+ sourceFile,
10751
+ match.receiver.importModule,
10752
+ importName
10753
+ ).keys()) {
10754
+ names.add(name);
10755
+ }
10756
+ }
10757
+ return names;
10758
+ }
10759
+ function tryExpandLoop(loop, match, kind, resolution, routables) {
10680
10760
  const loopVar = loopVariableName(loop);
10681
10761
  if (loopVar === null) {
10682
10762
  return null;
@@ -10684,6 +10764,9 @@ function tryExpandLoop(loop, match, kind, resolution) {
10684
10764
  if (!bodyReferencesLoopVar(loop, loopVar)) {
10685
10765
  return null;
10686
10766
  }
10767
+ if (routables !== null && !bodyCallsOn(loop, routables)) {
10768
+ return null;
10769
+ }
10687
10770
  const arrayLit = arrayLiteralOf(loop.getExpression(), resolution);
10688
10771
  if (arrayLit === null) {
10689
10772
  return null;
@@ -10742,6 +10825,20 @@ function bodyReferencesLoopVar(loop, name) {
10742
10825
  });
10743
10826
  return referenced;
10744
10827
  }
10828
+ function bodyCallsOn(loop, routables) {
10829
+ let found = false;
10830
+ loop.getStatement().forEachDescendant((node) => {
10831
+ if (found || !import_ts_morph50.Node.isCallExpression(node)) {
10832
+ return;
10833
+ }
10834
+ const callee = node.getExpression();
10835
+ const subject = import_ts_morph50.Node.isPropertyAccessExpression(callee) || import_ts_morph50.Node.isElementAccessExpression(callee) ? callee.getExpression() : null;
10836
+ if (subject !== null && import_ts_morph50.Node.isIdentifier(subject) && routables.has(subject.getText())) {
10837
+ found = true;
10838
+ }
10839
+ });
10840
+ return found;
10841
+ }
10745
10842
  function readRouteSpec(element, shape, resolution) {
10746
10843
  let method = null;
10747
10844
  let path12 = null;
package/dist/index.js CHANGED
@@ -2651,7 +2651,7 @@ var ORIGIN = {
2651
2651
  declaredBy: (origin, receiver) => receiver.callee !== null && origin.importedFrom.some(
2652
2652
  (module) => methodDeclaredIn(receiver.callee, module, receiver.resolve)
2653
2653
  ),
2654
- constructed: (origin, receiver) => madeFromNamed(origin, madeBy(receiver)) && origin.importedFrom.some(
2654
+ constructed: (origin, receiver) => madeFromNamed(origin, madeExpression(receiver)) && origin.importedFrom.some(
2655
2655
  (module) => isImportedFrom(madeBy(receiver), module)
2656
2656
  )
2657
2657
  };
@@ -2659,14 +2659,15 @@ function madeFromNamed(origin, made) {
2659
2659
  if (origin.origin !== "constructed" || origin.named === void 0) {
2660
2660
  return true;
2661
2661
  }
2662
- return origin.named.includes(made.getText());
2662
+ return origin.named.includes(constructorName(made));
2663
+ }
2664
+ function constructorName(made) {
2665
+ const expression = Node13.isNewExpression(made) || Node13.isCallExpression(made) ? made.getExpression() : made;
2666
+ return Node13.isPropertyAccessExpression(expression) ? expression.getName() : expression.getText();
2663
2667
  }
2664
2668
  var CALL_ORIGIN = {
2665
2669
  declaredBy: ORIGIN.declaredBy,
2666
- constructed: (origin, receiver) => madeFromNamed(
2667
- origin,
2668
- rootIdentifier(receiver.expression) ?? receiver.expression
2669
- ) && origin.importedFrom.some(
2670
+ constructed: (origin, receiver) => madeFromNamed(origin, receiver.expression) && origin.importedFrom.some(
2670
2671
  (module) => isImportedFrom(
2671
2672
  rootIdentifier(receiver.expression) ?? receiver.expression,
2672
2673
  module
@@ -2674,14 +2675,19 @@ var CALL_ORIGIN = {
2674
2675
  )
2675
2676
  };
2676
2677
  function madeBy(receiver) {
2678
+ const made = madeExpression(receiver);
2679
+ const expression = Node13.isNewExpression(made) || Node13.isCallExpression(made) ? made.getExpression() : made;
2680
+ return rootIdentifier(expression) ?? expression;
2681
+ }
2682
+ function madeExpression(receiver) {
2677
2683
  if (receiver.callee === null) {
2678
- return rootIdentifier(receiver.expression) ?? receiver.expression;
2684
+ return receiver.expression;
2679
2685
  }
2680
2686
  const written = receiver.resolve(receiver.callee.getExpression());
2681
2687
  if (written === null || !Node13.isNewExpression(written) && !Node13.isCallExpression(written)) {
2682
2688
  return receiver.callee;
2683
2689
  }
2684
- return rootIdentifier(written.getExpression()) ?? receiver.callee;
2690
+ return written;
2685
2691
  }
2686
2692
  function callOpsFor(call, resolveWrittenValue) {
2687
2693
  const resolve2 = resolveWrittenValue ?? (() => null);
@@ -2722,6 +2728,7 @@ function valueOpsFor(value, resolve2) {
2722
2728
  flag: () => literalFlag(written()),
2723
2729
  entries: (unsettled) => entriesOf(written(), unsettled, resolve2),
2724
2730
  items: () => itemsOf(written(), resolve2),
2731
+ asArg: () => effectArgOf(written()),
2725
2732
  property: (name) => {
2726
2733
  const object = written();
2727
2734
  const inside = Node13.isObjectLiteralExpression(object) ? initializerOf(object, name) : null;
@@ -3209,6 +3216,9 @@ var MAX_ARG_DEPTH = 8;
3209
3216
  function extractArgs(call) {
3210
3217
  return call.getArguments().map((arg) => extractArg(arg, MAX_ARG_DEPTH));
3211
3218
  }
3219
+ function effectArgOf(node) {
3220
+ return extractArg(node, MAX_ARG_DEPTH);
3221
+ }
3212
3222
  function extractArg(node, depth) {
3213
3223
  if (Node14.isAsExpression(node) || Node14.isTypeAssertion(node) || Node14.isSatisfiesExpression(node) || Node14.isNonNullExpression(node)) {
3214
3224
  return extractArg(node.getExpression(), depth);
@@ -7848,6 +7858,31 @@ function constructionRef(node, localNames, resolution) {
7848
7858
  // src/discovery/clientCall.ts
7849
7859
  import path4 from "path";
7850
7860
  import { Node as Node32 } from "ts-morph";
7861
+
7862
+ // src/discovery/resolveImport.ts
7863
+ function resolveImportedLocalName(sourceFile, importModule, importName) {
7864
+ for (const importDecl of sourceFile.getImportDeclarations()) {
7865
+ if (importDecl.getModuleSpecifierValue() !== importModule) {
7866
+ continue;
7867
+ }
7868
+ for (const named2 of importDecl.getNamedImports()) {
7869
+ if (named2.getName() === importName || named2.getAliasNode()?.getText() === importName) {
7870
+ return named2.getAliasNode()?.getText() ?? named2.getName();
7871
+ }
7872
+ }
7873
+ const defaultImport = importDecl.getDefaultImport();
7874
+ if (defaultImport !== void 0 && defaultImport.getText() === importName) {
7875
+ return defaultImport.getText();
7876
+ }
7877
+ const namespaceImport = importDecl.getNamespaceImport();
7878
+ if (namespaceImport !== void 0 && namespaceImport.getText() === importName) {
7879
+ return namespaceImport.getText();
7880
+ }
7881
+ }
7882
+ return null;
7883
+ }
7884
+
7885
+ // src/discovery/clientCall.ts
7851
7886
  function discoverClientCalls(sourceFile, match, kind, resolution) {
7852
7887
  const results = [];
7853
7888
  const isGlobal = match.importModule === "global";
@@ -7866,7 +7901,7 @@ function discoverClientCalls(sourceFile, match, kind, resolution) {
7866
7901
  if (!isGlobal && importedLocalName !== null) {
7867
7902
  for (const varDecl of sourceFile.getVariableDeclarations()) {
7868
7903
  const init = varDecl.getInitializer();
7869
- if (init !== void 0 && Node32.isCallExpression(init) && isInstanceCreationCall(init, importedLocalName, match.factoryMethods)) {
7904
+ if (init !== void 0 && (Node32.isCallExpression(init) || Node32.isNewExpression(init)) && isInstanceCreationCall(init, importedLocalName, match.factoryMethods)) {
7870
7905
  clientVarNames.add(varDecl.getName());
7871
7906
  }
7872
7907
  }
@@ -7911,12 +7946,50 @@ function discoverClientCalls(sourceFile, match, kind, resolution) {
7911
7946
  });
7912
7947
  return results;
7913
7948
  }
7949
+ function clientReceiverCheckFor(sourceFile, match, resolution) {
7950
+ const localName = resolveImportedLocalName(
7951
+ sourceFile,
7952
+ match.importModule,
7953
+ match.importName
7954
+ );
7955
+ const constructedHere = /* @__PURE__ */ new Set();
7956
+ if (localName !== null) {
7957
+ sourceFile.forEachDescendant((node) => {
7958
+ if (Node32.isVariableDeclaration(node)) {
7959
+ const init = node.getInitializer();
7960
+ if (init !== void 0 && (Node32.isCallExpression(init) || Node32.isNewExpression(init)) && isInstanceCreationCall(init, localName, match.factoryMethods) || typedAsClient(node.getTypeNode(), localName)) {
7961
+ constructedHere.add(node.getName());
7962
+ }
7963
+ return;
7964
+ }
7965
+ if (Node32.isParameterDeclaration(node) && typedAsClient(node.getTypeNode(), localName)) {
7966
+ const written = node.getNameNode();
7967
+ if (Node32.isIdentifier(written)) {
7968
+ constructedHere.add(written.getText());
7969
+ }
7970
+ }
7971
+ });
7972
+ }
7973
+ const full = {
7974
+ type: "clientCall",
7975
+ importModule: match.importModule,
7976
+ importName: match.importName,
7977
+ ...match.factoryMethods === void 0 ? {} : { factoryMethods: match.factoryMethods }
7978
+ };
7979
+ return (subject) => localName !== null && Node32.isIdentifier(subject) && subject.getText() === localName || Node32.isIdentifier(subject) && constructedHere.has(subject.getText()) || resolvesToKnownInstance(subject, full, resolution);
7980
+ }
7981
+ function typedAsClient(typeNode, localName) {
7982
+ if (typeNode === void 0 || !Node32.isTypeReference(typeNode)) {
7983
+ return false;
7984
+ }
7985
+ return typeNode.getTypeName().getText() === localName;
7986
+ }
7914
7987
  function resolvesToKnownInstance(subject, match, resolution) {
7915
7988
  if (resolution === void 0 || match.importModule === "global") {
7916
7989
  return false;
7917
7990
  }
7918
7991
  const written = writtenNodeOf(subject, resolution);
7919
- if (written === null || !Node32.isCallExpression(written)) {
7992
+ if (written === null || !Node32.isCallExpression(written) && !Node32.isNewExpression(written)) {
7920
7993
  return false;
7921
7994
  }
7922
7995
  const writtenLocalName = resolvedImportLocalName(
@@ -9117,40 +9190,8 @@ function discoverGraphqlHookCalls(sourceFile, match, kind, resolution) {
9117
9190
 
9118
9191
  // src/discovery/graphqlImperativeCall.ts
9119
9192
  import { Node as Node40 } from "ts-morph";
9120
-
9121
- // src/discovery/resolveImport.ts
9122
- function resolveImportedLocalName(sourceFile, importModule, importName) {
9123
- for (const importDecl of sourceFile.getImportDeclarations()) {
9124
- if (importDecl.getModuleSpecifierValue() !== importModule) {
9125
- continue;
9126
- }
9127
- for (const named2 of importDecl.getNamedImports()) {
9128
- if (named2.getName() === importName || named2.getAliasNode()?.getText() === importName) {
9129
- return named2.getAliasNode()?.getText() ?? named2.getName();
9130
- }
9131
- }
9132
- const defaultImport = importDecl.getDefaultImport();
9133
- if (defaultImport !== void 0 && defaultImport.getText() === importName) {
9134
- return defaultImport.getText();
9135
- }
9136
- const namespaceImport = importDecl.getNamespaceImport();
9137
- if (namespaceImport !== void 0 && namespaceImport.getText() === importName) {
9138
- return namespaceImport.getText();
9139
- }
9140
- }
9141
- return null;
9142
- }
9143
-
9144
- // src/discovery/graphqlImperativeCall.ts
9145
9193
  function discoverGraphqlImperativeCalls(sourceFile, match, kind, resolution) {
9146
- const localName = resolveImportedLocalName(
9147
- sourceFile,
9148
- match.importModule,
9149
- match.importName
9150
- );
9151
- if (localName === null) {
9152
- return [];
9153
- }
9194
+ const isTheClient = clientReceiverCheckFor(sourceFile, match, resolution);
9154
9195
  const methodSpec = /* @__PURE__ */ new Map();
9155
9196
  for (const method of match.methods) {
9156
9197
  methodSpec.set(method.methodName, {
@@ -9172,6 +9213,9 @@ function discoverGraphqlImperativeCalls(sourceFile, match, kind, resolution) {
9172
9213
  if (spec === void 0) {
9173
9214
  return;
9174
9215
  }
9216
+ if (!isTheClient(callee.getExpression())) {
9217
+ return;
9218
+ }
9175
9219
  const args = node.getArguments();
9176
9220
  if (args.length === 0 || !Node40.isObjectLiteralExpression(args[0])) {
9177
9221
  return;
@@ -10314,8 +10358,24 @@ function pathFromUrlNode(node, resolution) {
10314
10358
  if (Node46.isTemplateExpression(node)) {
10315
10359
  return pathFromTemplateUrl(node, resolution);
10316
10360
  }
10361
+ if (Node46.isBinaryExpression(node)) {
10362
+ const joined = joinedStringOf(node, resolution);
10363
+ return joined === void 0 ? void 0 : pathFromLiteralUrl(joined);
10364
+ }
10317
10365
  return void 0;
10318
10366
  }
10367
+ function joinedStringOf(node, resolution) {
10368
+ if (Node46.isStringLiteral(node) || Node46.isNoSubstitutionTemplateLiteral(node)) {
10369
+ return node.getLiteralValue();
10370
+ }
10371
+ if (Node46.isBinaryExpression(node) && node.getOperatorToken().getText() === "+") {
10372
+ const left = joinedStringOf(node.getLeft(), resolution);
10373
+ const right = joinedStringOf(node.getRight(), resolution);
10374
+ return left === void 0 || right === void 0 ? void 0 : left + right;
10375
+ }
10376
+ const written = resolution?.resolveWrittenValue(node) ?? null;
10377
+ return written === null || written === node ? void 0 : joinedStringOf(written, resolution);
10378
+ }
10319
10379
  function placeholderName(expr) {
10320
10380
  if (Node46.isIdentifier(expr)) {
10321
10381
  return expr.getText();
@@ -10648,18 +10708,38 @@ function extractRouteInfoFromBinding(call, methodName, binding, resolution) {
10648
10708
  import { Node as Node48 } from "ts-morph";
10649
10709
  function discoverRegistrationLoops(sourceFile, match, kind, resolution) {
10650
10710
  const results = [];
10711
+ const routables = routableNames(sourceFile, match);
10712
+ if (routables !== null && routables.size === 0) {
10713
+ return results;
10714
+ }
10651
10715
  sourceFile.forEachDescendant((node) => {
10652
10716
  if (!Node48.isForOfStatement(node)) {
10653
10717
  return;
10654
10718
  }
10655
- const expanded = tryExpandLoop(node, match, kind, resolution);
10719
+ const expanded = tryExpandLoop(node, match, kind, resolution, routables);
10656
10720
  if (expanded !== null) {
10657
10721
  results.push(...expanded);
10658
10722
  }
10659
10723
  });
10660
10724
  return results;
10661
10725
  }
10662
- function tryExpandLoop(loop, match, kind, resolution) {
10726
+ function routableNames(sourceFile, match) {
10727
+ if (match.receiver === void 0) {
10728
+ return null;
10729
+ }
10730
+ const names = /* @__PURE__ */ new Set();
10731
+ for (const importName of match.receiver.importNames) {
10732
+ for (const name of registrationSubjectsOf(
10733
+ sourceFile,
10734
+ match.receiver.importModule,
10735
+ importName
10736
+ ).keys()) {
10737
+ names.add(name);
10738
+ }
10739
+ }
10740
+ return names;
10741
+ }
10742
+ function tryExpandLoop(loop, match, kind, resolution, routables) {
10663
10743
  const loopVar = loopVariableName(loop);
10664
10744
  if (loopVar === null) {
10665
10745
  return null;
@@ -10667,6 +10747,9 @@ function tryExpandLoop(loop, match, kind, resolution) {
10667
10747
  if (!bodyReferencesLoopVar(loop, loopVar)) {
10668
10748
  return null;
10669
10749
  }
10750
+ if (routables !== null && !bodyCallsOn(loop, routables)) {
10751
+ return null;
10752
+ }
10670
10753
  const arrayLit = arrayLiteralOf(loop.getExpression(), resolution);
10671
10754
  if (arrayLit === null) {
10672
10755
  return null;
@@ -10725,6 +10808,20 @@ function bodyReferencesLoopVar(loop, name) {
10725
10808
  });
10726
10809
  return referenced;
10727
10810
  }
10811
+ function bodyCallsOn(loop, routables) {
10812
+ let found = false;
10813
+ loop.getStatement().forEachDescendant((node) => {
10814
+ if (found || !Node48.isCallExpression(node)) {
10815
+ return;
10816
+ }
10817
+ const callee = node.getExpression();
10818
+ const subject = Node48.isPropertyAccessExpression(callee) || Node48.isElementAccessExpression(callee) ? callee.getExpression() : null;
10819
+ if (subject !== null && Node48.isIdentifier(subject) && routables.has(subject.getText())) {
10820
+ found = true;
10821
+ }
10822
+ });
10823
+ return found;
10824
+ }
10728
10825
  function readRouteSpec(element, shape, resolution) {
10729
10826
  let method = null;
10730
10827
  let path12 = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/adapter-typescript",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "type": "module",
5
5
  "description": "TypeScript adapter for suss — walks source files with ts-morph and emits raw extractions for the suss pipeline.",
6
6
  "keywords": [
@@ -46,10 +46,10 @@
46
46
  "typecheck": "tsc --noEmit"
47
47
  },
48
48
  "dependencies": {
49
- "@suss/behavioral-ir": "0.14.0",
50
- "@suss/datalog": "0.14.0",
51
- "@suss/extractor": "0.14.0",
52
- "@suss/resolution": "0.14.0",
49
+ "@suss/behavioral-ir": "0.15.0",
50
+ "@suss/datalog": "0.15.0",
51
+ "@suss/extractor": "0.15.0",
52
+ "@suss/resolution": "0.15.0",
53
53
  "graphql": "^16.8.0",
54
54
  "picomatch": "^4.0.4",
55
55
  "ts-morph": "^23.0.0"