@typeorm/codemod 1.0.0 → 1.0.2

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 (250) hide show
  1. package/README.md +13 -10
  2. package/dist/cli/print-guide.js +2 -2
  3. package/dist/cli/print-summary.js +3 -2
  4. package/dist/cli/print-summary.js.map +1 -1
  5. package/dist/cli/print-usage.js +2 -1
  6. package/dist/cli/print-usage.js.map +1 -1
  7. package/dist/cli/run-transforms.js +181 -79
  8. package/dist/cli/run-transforms.js.map +1 -1
  9. package/dist/dependencies/v1.js +23 -9
  10. package/dist/dependencies/v1.js.map +1 -1
  11. package/dist/index.js +8 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/transforms/ast-helpers.js +779 -19
  14. package/dist/transforms/ast-helpers.js.map +1 -1
  15. package/dist/transforms/stats.js +11 -0
  16. package/dist/transforms/stats.js.map +1 -1
  17. package/dist/transforms/todo.js +42 -2
  18. package/dist/transforms/todo.js.map +1 -1
  19. package/dist/transforms/v1/column-readonly.js +28 -13
  20. package/dist/transforms/v1/column-readonly.js.map +1 -1
  21. package/dist/transforms/v1/column-unsigned-numeric.js +27 -26
  22. package/dist/transforms/v1/column-unsigned-numeric.js.map +1 -1
  23. package/dist/transforms/v1/column-width-zerofill.js +2 -1
  24. package/dist/transforms/v1/column-width-zerofill.js.map +1 -1
  25. package/dist/transforms/v1/connection-manager.js +82 -0
  26. package/dist/transforms/v1/connection-manager.js.map +1 -0
  27. package/dist/transforms/v1/connection-options-reader.js +214 -0
  28. package/dist/transforms/v1/connection-options-reader.js.map +1 -0
  29. package/dist/transforms/v1/connection-to-datasource.js +858 -93
  30. package/dist/transforms/v1/connection-to-datasource.js.map +1 -1
  31. package/dist/transforms/v1/datasource-expo.js +79 -0
  32. package/dist/transforms/v1/datasource-expo.js.map +1 -0
  33. package/dist/transforms/v1/datasource-mongodb.js +103 -64
  34. package/dist/transforms/v1/datasource-mongodb.js.map +1 -1
  35. package/dist/transforms/v1/datasource-mssql.js +88 -46
  36. package/dist/transforms/v1/datasource-mssql.js.map +1 -1
  37. package/dist/transforms/v1/datasource-mysql-connector.js +25 -6
  38. package/dist/transforms/v1/datasource-mysql-connector.js.map +1 -1
  39. package/dist/transforms/v1/datasource-name.js +19 -14
  40. package/dist/transforms/v1/datasource-name.js.map +1 -1
  41. package/dist/transforms/v1/datasource-sap.js +51 -26
  42. package/dist/transforms/v1/datasource-sap.js.map +1 -1
  43. package/dist/transforms/v1/datasource-sqlite-options.js +48 -13
  44. package/dist/transforms/v1/datasource-sqlite-options.js.map +1 -1
  45. package/dist/transforms/v1/datasource-sqlite-type.js +28 -7
  46. package/dist/transforms/v1/datasource-sqlite-type.js.map +1 -1
  47. package/dist/transforms/v1/file-logger.js +163 -0
  48. package/dist/transforms/v1/file-logger.js.map +1 -0
  49. package/dist/transforms/v1/find-options-join.js +84 -0
  50. package/dist/transforms/v1/find-options-join.js.map +1 -0
  51. package/dist/transforms/v1/find-options-lock-modes.js +26 -21
  52. package/dist/transforms/v1/find-options-lock-modes.js.map +1 -1
  53. package/dist/transforms/v1/find-options-string-relations.js +75 -9
  54. package/dist/transforms/v1/find-options-string-relations.js.map +1 -1
  55. package/dist/transforms/v1/find-options-string-select.js +75 -11
  56. package/dist/transforms/v1/find-options-string-select.js.map +1 -1
  57. package/dist/transforms/v1/global-functions.js +150 -64
  58. package/dist/transforms/v1/global-functions.js.map +1 -1
  59. package/dist/transforms/v1/index.js +16 -3
  60. package/dist/transforms/v1/index.js.map +1 -1
  61. package/dist/transforms/v1/migrations-get-all.js +25 -9
  62. package/dist/transforms/v1/migrations-get-all.js.map +1 -1
  63. package/dist/transforms/v1/mongodb-stats.js +21 -11
  64. package/dist/transforms/v1/mongodb-stats.js.map +1 -1
  65. package/dist/transforms/v1/mongodb-types.js +59 -2
  66. package/dist/transforms/v1/mongodb-types.js.map +1 -1
  67. package/dist/transforms/v1/query-builder-native-parameters.js +2 -0
  68. package/dist/transforms/v1/query-builder-native-parameters.js.map +1 -1
  69. package/dist/transforms/v1/query-builder-on-conflict.js +32 -18
  70. package/dist/transforms/v1/query-builder-on-conflict.js.map +1 -1
  71. package/dist/transforms/v1/query-builder-or-update.js +8 -4
  72. package/dist/transforms/v1/query-builder-or-update.js.map +1 -1
  73. package/dist/transforms/v1/query-builder-print-sql.js +18 -12
  74. package/dist/transforms/v1/query-builder-print-sql.js.map +1 -1
  75. package/dist/transforms/v1/query-builder-replace-property-names.js +16 -12
  76. package/dist/transforms/v1/query-builder-replace-property-names.js.map +1 -1
  77. package/dist/transforms/v1/query-builder-where-expression.js +29 -12
  78. package/dist/transforms/v1/query-builder-where-expression.js.map +1 -1
  79. package/dist/transforms/v1/query-runner-loaded-tables-views.js +1 -6
  80. package/dist/transforms/v1/query-runner-loaded-tables-views.js.map +1 -1
  81. package/dist/transforms/v1/relation-count.js +78 -12
  82. package/dist/transforms/v1/relation-count.js.map +1 -1
  83. package/dist/transforms/v1/repository-abstract.js +148 -34
  84. package/dist/transforms/v1/repository-abstract.js.map +1 -1
  85. package/dist/transforms/v1/repository-exist.js +20 -1
  86. package/dist/transforms/v1/repository-exist.js.map +1 -1
  87. package/dist/transforms/v1/repository-find-by-ids.js +67 -27
  88. package/dist/transforms/v1/repository-find-by-ids.js.map +1 -1
  89. package/dist/transforms/v1/repository-find-one-by-id.js +6 -0
  90. package/dist/transforms/v1/repository-find-one-by-id.js.map +1 -1
  91. package/dist/transforms/v1/use-container.js +65 -15
  92. package/dist/transforms/v1/use-container.js.map +1 -1
  93. package/package.json +3 -1
  94. package/dist/cli/colors.d.ts +0 -7
  95. package/dist/cli/colors.js +0 -12
  96. package/dist/cli/colors.js.map +0 -1
  97. package/dist/cli/error.d.ts +0 -1
  98. package/dist/cli/error.js +0 -11
  99. package/dist/cli/error.js.map +0 -1
  100. package/dist/cli/list-transforms.d.ts +0 -1
  101. package/dist/cli/parse-args.d.ts +0 -10
  102. package/dist/cli/print-guide.d.ts +0 -1
  103. package/dist/cli/print-summary.d.ts +0 -17
  104. package/dist/cli/print-todos.d.ts +0 -1
  105. package/dist/cli/print-usage.d.ts +0 -1
  106. package/dist/cli/run-dependencies.d.ts +0 -6
  107. package/dist/cli/run-transforms.d.ts +0 -21
  108. package/dist/cli/run.d.ts +0 -9
  109. package/dist/dependencies/config.d.ts +0 -24
  110. package/dist/dependencies/find-package-json.d.ts +0 -5
  111. package/dist/dependencies/index.d.ts +0 -5
  112. package/dist/dependencies/upgrade.d.ts +0 -11
  113. package/dist/dependencies/v1.d.ts +0 -2
  114. package/dist/index.d.ts +0 -2
  115. package/dist/lib/colors.d.ts +0 -9
  116. package/dist/lib/error.d.ts +0 -1
  117. package/dist/lib/format-time.d.ts +0 -1
  118. package/dist/lib/highlight.d.ts +0 -1
  119. package/dist/lib/spinner.d.ts +0 -5
  120. package/dist/transforms/ast-helpers.d.ts +0 -50
  121. package/dist/transforms/index.d.ts +0 -7
  122. package/dist/transforms/resolve.d.ts +0 -1
  123. package/dist/transforms/scan.d.ts +0 -4
  124. package/dist/transforms/scan.js +0 -33
  125. package/dist/transforms/scan.js.map +0 -1
  126. package/dist/transforms/stats.d.ts +0 -11
  127. package/dist/transforms/todo.d.ts +0 -2
  128. package/dist/transforms/transformer.d.ts +0 -8
  129. package/dist/transforms/v1/column-readonly.d.ts +0 -6
  130. package/dist/transforms/v1/column-unsigned-numeric.d.ts +0 -6
  131. package/dist/transforms/v1/column-width-zerofill.d.ts +0 -6
  132. package/dist/transforms/v1/connection-to-datasource.d.ts +0 -6
  133. package/dist/transforms/v1/datasource-mongodb.d.ts +0 -7
  134. package/dist/transforms/v1/datasource-mssql-domain.d.ts +0 -7
  135. package/dist/transforms/v1/datasource-mssql-domain.js +0 -35
  136. package/dist/transforms/v1/datasource-mssql-domain.js.map +0 -1
  137. package/dist/transforms/v1/datasource-mssql.d.ts +0 -7
  138. package/dist/transforms/v1/datasource-mysql-connector.d.ts +0 -6
  139. package/dist/transforms/v1/datasource-name.d.ts +0 -6
  140. package/dist/transforms/v1/datasource-sap.d.ts +0 -6
  141. package/dist/transforms/v1/datasource-sqlite-options.d.ts +0 -6
  142. package/dist/transforms/v1/datasource-sqlite-type.d.ts +0 -6
  143. package/dist/transforms/v1/find-options-lock-modes.d.ts +0 -6
  144. package/dist/transforms/v1/find-options-string-relations.d.ts +0 -6
  145. package/dist/transforms/v1/find-options-string-select.d.ts +0 -6
  146. package/dist/transforms/v1/global-functions.d.ts +0 -7
  147. package/dist/transforms/v1/index.d.ts +0 -40
  148. package/dist/transforms/v1/migrations-get-all.d.ts +0 -7
  149. package/dist/transforms/v1/mongodb-stats.d.ts +0 -7
  150. package/dist/transforms/v1/mongodb-types.d.ts +0 -6
  151. package/dist/transforms/v1/query-builder-native-parameters.d.ts +0 -6
  152. package/dist/transforms/v1/query-builder-on-conflict.d.ts +0 -7
  153. package/dist/transforms/v1/query-builder-or-update.d.ts +0 -6
  154. package/dist/transforms/v1/query-builder-print-sql.d.ts +0 -7
  155. package/dist/transforms/v1/query-builder-replace-property-names.d.ts +0 -7
  156. package/dist/transforms/v1/query-builder-where-expression.d.ts +0 -6
  157. package/dist/transforms/v1/query-runner-loaded-tables-views.d.ts +0 -7
  158. package/dist/transforms/v1/relation-count.d.ts +0 -7
  159. package/dist/transforms/v1/remove-abstract-repository.d.ts +0 -5
  160. package/dist/transforms/v1/remove-abstract-repository.js +0 -93
  161. package/dist/transforms/v1/remove-abstract-repository.js.map +0 -1
  162. package/dist/transforms/v1/remove-connector-package.d.ts +0 -4
  163. package/dist/transforms/v1/remove-connector-package.js +0 -19
  164. package/dist/transforms/v1/remove-connector-package.js.map +0 -1
  165. package/dist/transforms/v1/remove-datasource-name.d.ts +0 -4
  166. package/dist/transforms/v1/remove-datasource-name.js +0 -56
  167. package/dist/transforms/v1/remove-datasource-name.js.map +0 -1
  168. package/dist/transforms/v1/remove-mongodb-options.d.ts +0 -5
  169. package/dist/transforms/v1/remove-mongodb-options.js +0 -86
  170. package/dist/transforms/v1/remove-mongodb-options.js.map +0 -1
  171. package/dist/transforms/v1/remove-relation-count.d.ts +0 -5
  172. package/dist/transforms/v1/remove-relation-count.js +0 -49
  173. package/dist/transforms/v1/remove-relation-count.js.map +0 -1
  174. package/dist/transforms/v1/remove-replace-property-names.d.ts +0 -5
  175. package/dist/transforms/v1/remove-replace-property-names.js +0 -35
  176. package/dist/transforms/v1/remove-replace-property-names.js.map +0 -1
  177. package/dist/transforms/v1/remove-sap-options.d.ts +0 -4
  178. package/dist/transforms/v1/remove-sap-options.js +0 -54
  179. package/dist/transforms/v1/remove-sap-options.js.map +0 -1
  180. package/dist/transforms/v1/remove-unsigned-numeric.d.ts +0 -4
  181. package/dist/transforms/v1/remove-unsigned-numeric.js +0 -76
  182. package/dist/transforms/v1/remove-unsigned-numeric.js.map +0 -1
  183. package/dist/transforms/v1/remove-use-container.d.ts +0 -5
  184. package/dist/transforms/v1/remove-use-container.js +0 -63
  185. package/dist/transforms/v1/remove-use-container.js.map +0 -1
  186. package/dist/transforms/v1/remove-width-zerofill.d.ts +0 -4
  187. package/dist/transforms/v1/remove-width-zerofill.js +0 -29
  188. package/dist/transforms/v1/remove-width-zerofill.js.map +0 -1
  189. package/dist/transforms/v1/rename-connection-to-datasource.d.ts +0 -4
  190. package/dist/transforms/v1/rename-connection-to-datasource.js +0 -111
  191. package/dist/transforms/v1/rename-connection-to-datasource.js.map +0 -1
  192. package/dist/transforms/v1/rename-exist-to-exists.d.ts +0 -4
  193. package/dist/transforms/v1/rename-exist-to-exists.js +0 -25
  194. package/dist/transforms/v1/rename-exist-to-exists.js.map +0 -1
  195. package/dist/transforms/v1/rename-find-by-ids.d.ts +0 -4
  196. package/dist/transforms/v1/rename-find-by-ids.js +0 -64
  197. package/dist/transforms/v1/rename-find-by-ids.js.map +0 -1
  198. package/dist/transforms/v1/rename-get-all-migrations.d.ts +0 -5
  199. package/dist/transforms/v1/rename-get-all-migrations.js +0 -32
  200. package/dist/transforms/v1/rename-get-all-migrations.js.map +0 -1
  201. package/dist/transforms/v1/rename-loaded-tables-views.d.ts +0 -5
  202. package/dist/transforms/v1/rename-loaded-tables-views.js +0 -48
  203. package/dist/transforms/v1/rename-loaded-tables-views.js.map +0 -1
  204. package/dist/transforms/v1/rename-or-update-overload.d.ts +0 -4
  205. package/dist/transforms/v1/rename-or-update-overload.js +0 -46
  206. package/dist/transforms/v1/rename-or-update-overload.js.map +0 -1
  207. package/dist/transforms/v1/rename-print-sql-to-log-query.d.ts +0 -4
  208. package/dist/transforms/v1/rename-print-sql-to-log-query.js +0 -25
  209. package/dist/transforms/v1/rename-print-sql-to-log-query.js.map +0 -1
  210. package/dist/transforms/v1/replace-global-functions.d.ts +0 -5
  211. package/dist/transforms/v1/replace-global-functions.js +0 -92
  212. package/dist/transforms/v1/replace-global-functions.js.map +0 -1
  213. package/dist/transforms/v1/replace-lock-modes.d.ts +0 -4
  214. package/dist/transforms/v1/replace-lock-modes.js +0 -75
  215. package/dist/transforms/v1/replace-lock-modes.js.map +0 -1
  216. package/dist/transforms/v1/replace-mongodb-stats.d.ts +0 -5
  217. package/dist/transforms/v1/replace-mongodb-stats.js +0 -54
  218. package/dist/transforms/v1/replace-mongodb-stats.js.map +0 -1
  219. package/dist/transforms/v1/replace-mongodb-types.d.ts +0 -4
  220. package/dist/transforms/v1/replace-mongodb-types.js +0 -72
  221. package/dist/transforms/v1/replace-mongodb-types.js.map +0 -1
  222. package/dist/transforms/v1/replace-mssql-domain.d.ts +0 -5
  223. package/dist/transforms/v1/replace-mssql-domain.js +0 -25
  224. package/dist/transforms/v1/replace-mssql-domain.js.map +0 -1
  225. package/dist/transforms/v1/replace-on-conflict.d.ts +0 -5
  226. package/dist/transforms/v1/replace-on-conflict.js +0 -49
  227. package/dist/transforms/v1/replace-on-conflict.js.map +0 -1
  228. package/dist/transforms/v1/replace-readonly-column.d.ts +0 -4
  229. package/dist/transforms/v1/replace-readonly-column.js +0 -28
  230. package/dist/transforms/v1/replace-readonly-column.js.map +0 -1
  231. package/dist/transforms/v1/replace-set-native-parameters.d.ts +0 -4
  232. package/dist/transforms/v1/replace-set-native-parameters.js +0 -25
  233. package/dist/transforms/v1/replace-set-native-parameters.js.map +0 -1
  234. package/dist/transforms/v1/replace-sqlite-options.d.ts +0 -4
  235. package/dist/transforms/v1/replace-sqlite-options.js +0 -29
  236. package/dist/transforms/v1/replace-sqlite-options.js.map +0 -1
  237. package/dist/transforms/v1/replace-sqlite-type.d.ts +0 -4
  238. package/dist/transforms/v1/replace-sqlite-type.js +0 -22
  239. package/dist/transforms/v1/replace-sqlite-type.js.map +0 -1
  240. package/dist/transforms/v1/replace-where-expression-type.d.ts +0 -4
  241. package/dist/transforms/v1/replace-where-expression-type.js +0 -41
  242. package/dist/transforms/v1/replace-where-expression-type.js.map +0 -1
  243. package/dist/transforms/v1/repository-abstract.d.ts +0 -7
  244. package/dist/transforms/v1/repository-exist.d.ts +0 -6
  245. package/dist/transforms/v1/repository-find-by-ids.d.ts +0 -6
  246. package/dist/transforms/v1/repository-find-one-by-id.d.ts +0 -6
  247. package/dist/transforms/v1/upgrade-dependencies.d.ts +0 -15
  248. package/dist/transforms/v1/upgrade-dependencies.js +0 -163
  249. package/dist/transforms/v1/upgrade-dependencies.js.map +0 -1
  250. package/dist/transforms/v1/use-container.d.ts +0 -7
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.renameMemberMethod = exports.removeImportSpecifiers = exports.removeObjectProperties = exports.forEachDecoratorObjectArg = exports.forEachIdentifierParam = exports.fileImportsFrom = exports.isIdentifier = exports.setStringValue = exports.getStringValue = void 0;
3
+ exports.isRepositoryReceiver = exports.collectRepositoryBindings = exports.TYPEORM_DATASOURCE_TYPES = exports.TYPEORM_REPOSITORY_TYPES = exports.getTypeReferenceRootName = exports.renameMemberMethod = exports.renameReExportSpecifiers = exports.removeReExportSpecifiers = exports.removeImportSpecifiers = exports.removeObjectPropertiesWhere = exports.removeObjectProperties = exports.unwrapTsExpression = exports.isFindMethodCallArgument = exports.isObjectFromEntriesCall = exports.TYPEORM_FIND_OPTIONS_METHODS = exports.getObjectPropertyKeyName = exports.forEachDecoratorObjectArg = exports.expandLocalNamesForImports = exports.TYPEORM_COLUMN_DECORATORS = exports.forEachIdentifierParam = exports.getNamespaceLocalNames = exports.getLocalNamesForImport = exports.fileImportsFrom = exports.isIdentifier = exports.setStringValue = exports.getStringValue = void 0;
4
4
  /**
5
5
  * Extracts a string value from a StringLiteral or Literal node.
6
6
  * Returns null if the node is not a string literal.
@@ -30,14 +30,174 @@ exports.setStringValue = setStringValue;
30
30
  const isIdentifier = (node) => node.type === "Identifier";
31
31
  exports.isIdentifier = isIdentifier;
32
32
  /**
33
- * Checks whether the file contains an import from the given module.
33
+ * Checks whether the file references the given module as an import or a
34
+ * re-export source. Matches the exact module name (`"typeorm"`) and any
35
+ * sub-path (`"typeorm/..."`), and recognizes ESM `import`, ESM
36
+ * `export { X } from "..."` / `export * from "..."`, TypeScript
37
+ * `import = require(...)`, and CommonJS `require(...)` forms so barrel
38
+ * files and `.js`/`.jsx` callers still pass the scope guard.
34
39
  */
35
40
  const fileImportsFrom = (root, j, moduleName) => {
36
- return (root.find(j.ImportDeclaration, {
37
- source: { value: moduleName },
38
- }).length > 0);
41
+ const prefix = `${moduleName}/`;
42
+ const matchesModule = (source) => typeof source === "string" &&
43
+ (source === moduleName || source.startsWith(prefix));
44
+ // ESM: import ... from "typeorm[/subpath]"
45
+ if (root
46
+ .find(j.ImportDeclaration)
47
+ .some((path) => matchesModule(path.node.source.value))) {
48
+ return true;
49
+ }
50
+ // ESM: export { X } from "typeorm[/subpath]" / export * from "..."
51
+ // Barrel files that only re-export TypeORM APIs have no import at all,
52
+ // but their re-export source still counts as a reference to the module.
53
+ if (root
54
+ .find(j.ExportNamedDeclaration)
55
+ .some((path) => matchesModule(path.node.source?.value))) {
56
+ return true;
57
+ }
58
+ if (root
59
+ .find(j.ExportAllDeclaration)
60
+ .some((path) => matchesModule(path.node.source?.value))) {
61
+ return true;
62
+ }
63
+ // TS: import ... = require("typeorm[/subpath]")
64
+ if (root.find(j.TSImportEqualsDeclaration).some((path) => {
65
+ const ref = path.node.moduleReference;
66
+ return (ref.type === "TSExternalModuleReference" &&
67
+ matchesModule((0, exports.getStringValue)(ref.expression)));
68
+ })) {
69
+ return true;
70
+ }
71
+ // CommonJS: require("typeorm[/subpath]")
72
+ return root
73
+ .find(j.CallExpression, {
74
+ callee: { type: "Identifier", name: "require" },
75
+ })
76
+ .some((path) => {
77
+ const [arg] = path.node.arguments;
78
+ return arg !== undefined && matchesModule((0, exports.getStringValue)(arg));
79
+ });
39
80
  };
40
81
  exports.fileImportsFrom = fileImportsFrom;
82
+ /**
83
+ * Returns the set of local identifiers bound to a given named export.
84
+ * Handles ESM direct/aliased imports and CommonJS destructured requires:
85
+ *
86
+ * import { RelationCount } from "typeorm" → { "RelationCount" }
87
+ * import { RelationCount as RC } from "typeorm" → { "RC" }
88
+ * const { RelationCount } = require("typeorm") → { "RelationCount" }
89
+ * const { RelationCount: RC } = require("typeorm")→ { "RC" }
90
+ */
91
+ const getLocalNamesForImport = (root, j, moduleName, importedName) => {
92
+ const localNames = new Set();
93
+ // ESM: `import { X [as Y] } from "moduleName"`
94
+ root.find(j.ImportDeclaration, {
95
+ source: { value: moduleName },
96
+ }).forEach((importPath) => {
97
+ for (const spec of importPath.node.specifiers ?? []) {
98
+ if (spec.type !== "ImportSpecifier" ||
99
+ spec.imported.type !== "Identifier" ||
100
+ spec.imported.name !== importedName) {
101
+ continue;
102
+ }
103
+ const local = spec.local ?? spec.imported;
104
+ if (local.type === "Identifier") {
105
+ localNames.add(local.name);
106
+ }
107
+ }
108
+ });
109
+ // CommonJS: `const { X [: Y] } = require("moduleName")`
110
+ root.find(j.CallExpression, {
111
+ callee: { type: "Identifier", name: "require" },
112
+ }).forEach((callPath) => {
113
+ const [arg] = callPath.node.arguments;
114
+ if (!arg || (0, exports.getStringValue)(arg) !== moduleName)
115
+ return;
116
+ const parent = callPath.parent.node;
117
+ if (parent.type !== "VariableDeclarator")
118
+ return;
119
+ const id = parent.id;
120
+ if (id.type !== "ObjectPattern")
121
+ return;
122
+ for (const prop of id.properties) {
123
+ if (prop.type !== "Property" && prop.type !== "ObjectProperty") {
124
+ continue;
125
+ }
126
+ if (prop.key.type !== "Identifier" ||
127
+ prop.key.name !== importedName) {
128
+ continue;
129
+ }
130
+ // Extract the binding name from each destructuring variant:
131
+ // { X } → "X" (Identifier)
132
+ // { X: Y } → "Y" (Identifier alias)
133
+ // { X = fallback } → "X" (AssignmentPattern, shorthand)
134
+ // { X: Y = fallback }→ "Y" (AssignmentPattern, aliased)
135
+ let localName = prop.key.name;
136
+ if (prop.value.type === "Identifier") {
137
+ localName = prop.value.name;
138
+ }
139
+ else if (prop.value.type === "AssignmentPattern" &&
140
+ prop.value.left.type === "Identifier") {
141
+ localName = prop.value.left.name;
142
+ }
143
+ localNames.add(localName);
144
+ }
145
+ });
146
+ return localNames;
147
+ };
148
+ exports.getLocalNamesForImport = getLocalNamesForImport;
149
+ /**
150
+ * Collects local namespace bindings for a module. Covers:
151
+ *
152
+ * import * as typeorm from "typeorm" → "typeorm"
153
+ * const typeorm = require("typeorm") → "typeorm"
154
+ * import typeorm = require("typeorm") → "typeorm"
155
+ *
156
+ * Useful when a transform needs to recognise `typeorm.Foo` member-expression
157
+ * references alongside named `Foo` imports handled by `getLocalNamesForImport`.
158
+ */
159
+ const getNamespaceLocalNames = (root, j, moduleName) => {
160
+ const localNames = new Set();
161
+ // ESM: `import * as ns from "moduleName"`
162
+ root.find(j.ImportDeclaration, {
163
+ source: { value: moduleName },
164
+ }).forEach((importPath) => {
165
+ for (const spec of importPath.node.specifiers ?? []) {
166
+ if (spec.type === "ImportNamespaceSpecifier" &&
167
+ spec.local?.type === "Identifier") {
168
+ localNames.add(spec.local.name);
169
+ }
170
+ }
171
+ });
172
+ // TypeScript: `import ns = require("moduleName")`
173
+ root.find(j.TSImportEqualsDeclaration).forEach((importPath) => {
174
+ const ref = importPath.node.moduleReference;
175
+ if (ref.type !== "TSExternalModuleReference")
176
+ return;
177
+ if ((0, exports.getStringValue)(ref.expression) !== moduleName)
178
+ return;
179
+ if (importPath.node.id?.type === "Identifier") {
180
+ localNames.add(importPath.node.id.name);
181
+ }
182
+ });
183
+ // CommonJS: `const ns = require("moduleName")`
184
+ root.find(j.CallExpression, {
185
+ callee: { type: "Identifier", name: "require" },
186
+ }).forEach((callPath) => {
187
+ const [arg] = callPath.node.arguments;
188
+ if (!arg || (0, exports.getStringValue)(arg) !== moduleName)
189
+ return;
190
+ const parent = callPath.parent.node;
191
+ if (parent.type !== "VariableDeclarator" ||
192
+ parent.id.type !== "Identifier") {
193
+ return;
194
+ }
195
+ const name = parent.id.name;
196
+ localNames.add(name);
197
+ });
198
+ return localNames;
199
+ };
200
+ exports.getNamespaceLocalNames = getNamespaceLocalNames;
41
201
  /**
42
202
  * Calls `callback` for each Identifier parameter found in function-like
43
203
  * nodes (functions, methods, arrows) and TSParameterProperty nodes.
@@ -57,22 +217,62 @@ const forEachIdentifierParam = (root, j, callback) => {
57
217
  });
58
218
  };
59
219
  exports.forEachIdentifierParam = forEachIdentifierParam;
220
+ /**
221
+ * TypeORM column-family decorator names. Exposed so decorator-scoped
222
+ * transforms can narrow their match set and skip unrelated decorators like
223
+ * Angular's `@Input` or class-validator's `@IsDefined` without relying on a
224
+ * file-level `fileImportsFrom` guard.
225
+ */
226
+ exports.TYPEORM_COLUMN_DECORATORS = new Set([
227
+ "Column",
228
+ "PrimaryColumn",
229
+ "PrimaryGeneratedColumn",
230
+ "VersionColumn",
231
+ "CreateDateColumn",
232
+ "UpdateDateColumn",
233
+ "DeleteDateColumn",
234
+ "ObjectIdColumn",
235
+ "ViewColumn",
236
+ ]);
237
+ /**
238
+ * Expands a set of exported names into the local bindings each one has in
239
+ * the file — covers ESM aliases (`import { Column as C }`) and CJS aliases
240
+ * (`const { Column: C } = require(...)`). Returns a union set suitable for
241
+ * alias-aware identifier matching.
242
+ */
243
+ const expandLocalNamesForImports = (root, j, moduleName, importedNames) => {
244
+ const expanded = new Set();
245
+ for (const name of importedNames) {
246
+ for (const local of (0, exports.getLocalNamesForImport)(root, j, moduleName, name)) {
247
+ expanded.add(local);
248
+ }
249
+ }
250
+ return expanded;
251
+ };
252
+ exports.expandLocalNamesForImports = expandLocalNamesForImports;
60
253
  /**
61
254
  * Traverses ClassProperty decorators and calls `callback` for each
62
255
  * ObjectExpression argument found in decorator call expressions.
63
256
  *
64
- * This avoids duplicating the decorator-traversal boilerplate across
65
- * multiple transforms.
257
+ * Pass `decoratorNames` to restrict the traversal to a known set of callees
258
+ * (e.g. TypeORM's column decorators). Without it, every decorator-with-object
259
+ * on every class property is visited.
66
260
  */
67
- const forEachDecoratorObjectArg = (root, j, callback) => {
261
+ const forEachDecoratorObjectArg = (root, j, callback, decoratorNames) => {
68
262
  root.find(j.ClassProperty).forEach((path) => {
69
- // ast-types omits `decorators` from ClassProperty — extend it
70
263
  const node = path.node;
71
264
  if (!node.decorators)
72
265
  return;
73
266
  for (const decorator of node.decorators) {
74
267
  if (decorator.expression.type !== "CallExpression")
75
268
  continue;
269
+ if (decoratorNames) {
270
+ const callee = decorator.expression.callee;
271
+ if (callee.type !== "Identifier" ||
272
+ !decoratorNames.has(callee.name)) {
273
+ continue;
274
+ }
275
+ }
76
276
  for (const arg of decorator.expression.arguments) {
77
277
  if (arg.type !== "ObjectExpression")
78
278
  continue;
@@ -82,23 +282,153 @@ const forEachDecoratorObjectArg = (root, j, callback) => {
82
282
  });
83
283
  };
84
284
  exports.forEachDecoratorObjectArg = forEachDecoratorObjectArg;
285
+ /**
286
+ * Returns the key name for an `ObjectProperty` / `Property` node. Handles
287
+ * both identifier keys (`name`) and string-literal keys (`"name"`); returns
288
+ * null for computed, numeric, or otherwise non-string keys, and for node
289
+ * types that don't carry a key at all (spread elements, etc.).
290
+ */
291
+ const getObjectPropertyKeyName = (prop) => {
292
+ if (prop.type !== "Property" && prop.type !== "ObjectProperty")
293
+ return null;
294
+ if (prop.key.type === "Identifier")
295
+ return prop.key.name;
296
+ return (0, exports.getStringValue)(prop.key);
297
+ };
298
+ exports.getObjectPropertyKeyName = getObjectPropertyKeyName;
299
+ /**
300
+ * TypeORM `Repository` / `EntityManager` methods that accept a FindOptions
301
+ * object (with `select`/`relations`/`where`/…). Used to scope the
302
+ * `select: [...]` / `relations: [...]` → object-form transforms to
303
+ * arguments passed into these methods. Deliberately excludes the
304
+ * `*By` variants (`findBy`, `findOneBy`, `findOneByOrFail`,
305
+ * `findAndCountBy`, `countBy`) — those accept a plain WHERE object, so
306
+ * rewriting a top-level `select` or `relations` key there would mangle
307
+ * matches against entity fields of those names.
308
+ *
309
+ * The method-name check (rather than a file-level typeorm import gate)
310
+ * lets the transforms fire in NestJS-style service files that only pull
311
+ * TypeORM types via a wrapper module.
312
+ */
313
+ exports.TYPEORM_FIND_OPTIONS_METHODS = new Set([
314
+ "find",
315
+ "findAndCount",
316
+ "findOne",
317
+ "findOneOrFail",
318
+ "count",
319
+ ]);
320
+ /**
321
+ * Returns true when `node` is a call of shape `Object.fromEntries(...)`.
322
+ * Used by the find-option transforms to stay idempotent — a second pass
323
+ * must not wrap an already-wrapped dynamic value in another `fromEntries`.
324
+ */
325
+ const isObjectFromEntriesCall = (node) => {
326
+ if (node.type !== "CallExpression")
327
+ return false;
328
+ const callee = node.callee;
329
+ if (callee.type !== "MemberExpression")
330
+ return false;
331
+ const m = callee;
332
+ return (m.object.type === "Identifier" &&
333
+ m.object.name === "Object" &&
334
+ m.property.type === "Identifier" &&
335
+ m.property.name === "fromEntries");
336
+ };
337
+ exports.isObjectFromEntriesCall = isObjectFromEntriesCall;
338
+ /**
339
+ * Returns true when the given `ObjectProperty` lives inside an object that
340
+ * is an argument to one of the TYPEORM_FIND_OPTIONS_METHODS. Matches both
341
+ * `repo.find({ select: [...] })` (object is the single argument) and
342
+ * `manager.find(Entity, { select: [...] })` (object is the second arg).
343
+ */
344
+ const isFindMethodCallArgument = (propPath) => {
345
+ const objExprPath = propPath.parent;
346
+ if (objExprPath?.node.type !== "ObjectExpression") {
347
+ return false;
348
+ }
349
+ // Walk up through TS expression wrappers (`{ select: [...] } as T`,
350
+ // `satisfies Opts`, parens) before expecting a CallExpression. Without
351
+ // this, `repo.find({ select: [...] } as FindOptions)` is missed.
352
+ let ancestor = objExprPath.parent;
353
+ while (ancestor) {
354
+ const t = ancestor.node.type;
355
+ if (t === "TSAsExpression" ||
356
+ t === "TSSatisfiesExpression" ||
357
+ t === "TSTypeAssertion" ||
358
+ t === "TSNonNullExpression" ||
359
+ t === "ParenthesizedExpression") {
360
+ ancestor = ancestor.parent;
361
+ continue;
362
+ }
363
+ break;
364
+ }
365
+ if (!ancestor)
366
+ return false;
367
+ const callNode = ancestor.node;
368
+ if (callNode.type !== "CallExpression" &&
369
+ callNode.type !== "OptionalCallExpression") {
370
+ return false;
371
+ }
372
+ const callee = callNode.callee;
373
+ if (callee.type !== "MemberExpression" &&
374
+ callee.type !== "OptionalMemberExpression") {
375
+ return false;
376
+ }
377
+ const prop = callee.property;
378
+ if (prop.type !== "Identifier")
379
+ return false;
380
+ return exports.TYPEORM_FIND_OPTIONS_METHODS.has(prop.name);
381
+ };
382
+ exports.isFindMethodCallArgument = isFindMethodCallArgument;
383
+ /**
384
+ * Peels TypeScript expression wrappers and parenthesized expressions around
385
+ * a value so callers see the underlying node. Handles `as X` / `x!` /
386
+ * `x satisfies X` / `<X>x` / `(x)`. Used by transforms that inspect values
387
+ * which users may annotate with type assertions — e.g.
388
+ * `type: "expo" as const`, `type: ("mongodb")`,
389
+ * `{ logPath: "x" } as Options`.
390
+ *
391
+ * Generic over the node type so callers can keep their original narrowing
392
+ * — in practice the returned node is the same type as the input (with TS
393
+ * wrapper variants peeled off).
394
+ */
395
+ const unwrapTsExpression = (node) => {
396
+ let current = node;
397
+ while (current.type === "TSAsExpression" ||
398
+ current.type === "TSNonNullExpression" ||
399
+ current.type === "TSSatisfiesExpression" ||
400
+ current.type === "TSTypeAssertion" ||
401
+ current.type === "ParenthesizedExpression") {
402
+ const inner = current.expression;
403
+ if (!inner)
404
+ break;
405
+ current = inner;
406
+ }
407
+ return current;
408
+ };
409
+ exports.unwrapTsExpression = unwrapTsExpression;
85
410
  /**
86
411
  * Removes properties matching the given key names from an ObjectExpression.
412
+ * Matches both identifier keys (`name`) and string-literal keys (`"name"`).
87
413
  * Returns true if any properties were removed.
88
414
  */
89
- const removeObjectProperties = (obj, propertyNames) => {
415
+ const removeObjectProperties = (obj, propertyNames) => (0, exports.removeObjectPropertiesWhere)(obj, (prop) => {
416
+ const key = (0, exports.getObjectPropertyKeyName)(prop);
417
+ return key !== null && propertyNames.has(key);
418
+ });
419
+ exports.removeObjectProperties = removeObjectProperties;
420
+ /**
421
+ * Removes every property from `obj` that satisfies `predicate`. Returns true
422
+ * if any property was removed. Use this over `removeObjectProperties` when
423
+ * removal needs to inspect the property value (not just the key name) —
424
+ * e.g. "remove `driver` only when its value is `require("expo-sqlite")`".
425
+ */
426
+ const removeObjectPropertiesWhere = (obj, predicate) => {
90
427
  const original = obj.properties.length;
91
- obj.properties = obj.properties.filter((prop) => {
92
- if ((prop.type === "Property" || prop.type === "ObjectProperty") &&
93
- prop.key.type === "Identifier" &&
94
- propertyNames.has(prop.key.name)) {
95
- return false;
96
- }
97
- return true;
98
- });
428
+ obj.properties = obj.properties.filter((prop) => !predicate(prop));
99
429
  return obj.properties.length !== original;
100
430
  };
101
- exports.removeObjectProperties = removeObjectProperties;
431
+ exports.removeObjectPropertiesWhere = removeObjectPropertiesWhere;
102
432
  /**
103
433
  * Finds imports from a module, removes the specified named import specifiers,
104
434
  * and removes the entire import declaration if no specifiers remain.
@@ -128,6 +458,77 @@ const removeImportSpecifiers = (root, j, moduleName, specifierNames) => {
128
458
  return removed;
129
459
  };
130
460
  exports.removeImportSpecifiers = removeImportSpecifiers;
461
+ // Returns true for `"moduleName"` and any sub-path like `"moduleName/..."`.
462
+ // Used so re-export helpers can match `export { X } from "typeorm"` as well
463
+ // as `export { SapConnectionOptions } from "typeorm/driver/sap/SapConnectionOptions"`.
464
+ const matchesModuleOrSubPath = (source, moduleName) => typeof source === "string" &&
465
+ (source === moduleName || source.startsWith(`${moduleName}/`));
466
+ /**
467
+ * Finds re-exports from a module (`export { X } from "module"`) and removes
468
+ * the named specifiers listed in `specifierNames`. Also matches sub-path
469
+ * re-exports (`export { X } from "module/sub/path"`). Removes the entire
470
+ * `ExportNamedDeclaration` if no specifiers remain. Returns true if any
471
+ * specifiers were removed.
472
+ */
473
+ const removeReExportSpecifiers = (root, j, moduleName, specifierNames) => {
474
+ let removed = false;
475
+ root.find(j.ExportNamedDeclaration).forEach((exportPath) => {
476
+ const source = exportPath.node.source?.value;
477
+ if (!matchesModuleOrSubPath(source, moduleName))
478
+ return;
479
+ const remaining = exportPath.node.specifiers?.filter((spec) => {
480
+ if (spec.type === "ExportSpecifier" &&
481
+ spec.local?.type === "Identifier" &&
482
+ specifierNames.has(spec.local.name)) {
483
+ removed = true;
484
+ return false;
485
+ }
486
+ return true;
487
+ });
488
+ if (remaining?.length === 0) {
489
+ j(exportPath).remove();
490
+ }
491
+ else if (remaining) {
492
+ exportPath.node.specifiers = remaining;
493
+ }
494
+ });
495
+ return removed;
496
+ };
497
+ exports.removeReExportSpecifiers = removeReExportSpecifiers;
498
+ /**
499
+ * Finds re-exports from a module (`export { X } from "module"`) and renames
500
+ * specifiers according to the `renames` map. Also matches sub-path
501
+ * re-exports (`export { X } from "module/sub/path"`). When the re-export has
502
+ * an alias (`export { X as Y }`), only the local name is renamed so
503
+ * downstream consumers continue to see the same exported name. Returns true
504
+ * if any specifiers were renamed.
505
+ */
506
+ const renameReExportSpecifiers = (root, j, moduleName, renames) => {
507
+ let renamed = false;
508
+ root.find(j.ExportNamedDeclaration).forEach((exportPath) => {
509
+ const source = exportPath.node.source?.value;
510
+ if (!matchesModuleOrSubPath(source, moduleName))
511
+ return;
512
+ exportPath.node.specifiers?.forEach((spec) => {
513
+ if (spec.type !== "ExportSpecifier" ||
514
+ spec.local?.type !== "Identifier") {
515
+ return;
516
+ }
517
+ const newName = renames[spec.local.name];
518
+ if (!newName)
519
+ return;
520
+ const wasAlias = spec.exported.type === "Identifier" &&
521
+ spec.exported.name !== spec.local.name;
522
+ spec.local.name = newName;
523
+ if (!wasAlias && spec.exported.type === "Identifier") {
524
+ spec.exported.name = newName;
525
+ }
526
+ renamed = true;
527
+ });
528
+ });
529
+ return renamed;
530
+ };
531
+ exports.renameReExportSpecifiers = renameReExportSpecifiers;
131
532
  /**
132
533
  * Finds CallExpression nodes with a MemberExpression callee where the
133
534
  * property matches `oldName`, and renames the property to `newName`.
@@ -150,4 +551,363 @@ const renameMemberMethod = (root, j, oldName, newName) => {
150
551
  return renamed;
151
552
  };
152
553
  exports.renameMemberMethod = renameMemberMethod;
554
+ /**
555
+ * Walks a TypeScript type-annotation node and returns the root identifier
556
+ * name — e.g. `Repository<User>` → `"Repository"`, `Repository<User> | null`
557
+ * → `"Repository"`, `typeof Repository` → `"Repository"`. Returns null when
558
+ * the annotation doesn't root on a TSTypeReference with an Identifier name.
559
+ *
560
+ * When `typeormNamespaceNames` is passed, qualified references like
561
+ * `ns.Repository<User>` are only returned if `ns` is one of the provided
562
+ * namespace locals — otherwise the qualified reference is rejected so that
563
+ * unrelated namespaces (`Foo.Repository<T>` from some-other-lib) are not
564
+ * mistaken for TypeORM types. Without the parameter, any qualifier is
565
+ * accepted, which matches legacy callers that don't care about provenance.
566
+ */
567
+ // Walks the members of a union/intersection type and prefers the first
568
+ // TypeORM-family name (so `FooBar | Repository<T>` or `null | Repository<T>`
569
+ // classify correctly). Falls back to the first non-null root name to keep
570
+ // pre-existing behavior for callers that don't care about TypeORM gating.
571
+ const findUnionOrIntersectionRoot = (types, typeormNamespaceNames) => {
572
+ let firstName = null;
573
+ for (const member of types) {
574
+ const name = (0, exports.getTypeReferenceRootName)(member, typeormNamespaceNames);
575
+ if (!name)
576
+ continue;
577
+ if (exports.TYPEORM_REPOSITORY_TYPES.has(name) ||
578
+ exports.TYPEORM_DATASOURCE_TYPES.has(name)) {
579
+ return name;
580
+ }
581
+ firstName ??= name;
582
+ }
583
+ return firstName;
584
+ };
585
+ // Returns the rightmost segment of a `TSQualifiedName` (`a.b.c` in type
586
+ // position) — always an `Identifier` in valid TS, since the nesting lives
587
+ // on `.left`. When `typeormNamespaceNames` is provided, the leftmost segment
588
+ // is checked against the set of typeorm namespace locals and the rightmost
589
+ // name is only returned if the qualifier matches — so `typeorm.Repository`
590
+ // resolves to `"Repository"` while `other.Repository` returns null, avoiding
591
+ // false-positive classification of identically-named types from other
592
+ // modules. Without the parameter, any qualifier is accepted (legacy
593
+ // behaviour for callers that only look at the rightmost segment).
594
+ const getQualifiedNameRightmost = (node, typeormNamespaceNames) => {
595
+ const n = node;
596
+ if (n.right.type !== "Identifier")
597
+ return null;
598
+ if (typeormNamespaceNames !== undefined) {
599
+ // Walk down `.left` to the leftmost identifier (qualifier root).
600
+ let leftmost = n.left;
601
+ while (leftmost.type === "TSQualifiedName") {
602
+ leftmost = leftmost.left;
603
+ }
604
+ if (leftmost.type !== "Identifier")
605
+ return null;
606
+ if (!typeormNamespaceNames.has(leftmost.name))
607
+ return null;
608
+ }
609
+ return n.right.name;
610
+ };
611
+ const getTypeReferenceRootName = (node, typeormNamespaceNames) => {
612
+ if (!node)
613
+ return null;
614
+ if (node.type === "TSTypeReference") {
615
+ const n = node;
616
+ if (n.typeName.type === "Identifier") {
617
+ return n.typeName.name;
618
+ }
619
+ // `typeorm.Repository<User>` — TSTypeReference wraps a TSQualifiedName
620
+ // (namespace-import access). Without this branch,
621
+ // `import * as typeorm from "typeorm"` callers would be missed.
622
+ if (n.typeName.type === "TSQualifiedName") {
623
+ return getQualifiedNameRightmost(n.typeName, typeormNamespaceNames);
624
+ }
625
+ }
626
+ // `const X: typeof Repository` — TSTypeQuery wraps the referenced
627
+ // identifier in `exprName`. Without this branch, type-of annotations on
628
+ // typeorm-family types wouldn't register as repository bindings.
629
+ if (node.type === "TSTypeQuery") {
630
+ const n = node;
631
+ if (n.exprName.type === "Identifier") {
632
+ return n.exprName.name;
633
+ }
634
+ if (n.exprName.type === "TSQualifiedName") {
635
+ return getQualifiedNameRightmost(n.exprName, typeormNamespaceNames);
636
+ }
637
+ }
638
+ if (node.type === "TSTypeAnnotation") {
639
+ const n = node;
640
+ return (0, exports.getTypeReferenceRootName)(n.typeAnnotation, typeormNamespaceNames);
641
+ }
642
+ if (node.type === "TSUnionType" || node.type === "TSIntersectionType") {
643
+ return findUnionOrIntersectionRoot(node.types, typeormNamespaceNames);
644
+ }
645
+ return null;
646
+ };
647
+ exports.getTypeReferenceRootName = getTypeReferenceRootName;
648
+ /**
649
+ * TypeORM Repository-family type names used to detect bindings that hold a
650
+ * Repository/EntityManager instance, so transforms can scope method renames
651
+ * (`.findByIds`, `.findOneById`, `.exist`, `.stats`) to those receivers.
652
+ */
653
+ exports.TYPEORM_REPOSITORY_TYPES = new Set([
654
+ "Repository",
655
+ "TreeRepository",
656
+ "MongoRepository",
657
+ "EntityRepository",
658
+ "AbstractRepository",
659
+ "EntityManager",
660
+ "MongoEntityManager",
661
+ "SqljsEntityManager",
662
+ ]);
663
+ /**
664
+ * Call shapes whose return value is a Repository/EntityManager:
665
+ * `.getRepository(...)`, `.getMongoRepository(...)`, `.getTreeRepository(...)`,
666
+ * `.getCustomRepository(...)`, `.manager` (property access, see caller).
667
+ */
668
+ const REPOSITORY_RETURNING_METHODS = new Set([
669
+ "getRepository",
670
+ "getMongoRepository",
671
+ "getTreeRepository",
672
+ "getCustomRepository",
673
+ ]);
674
+ // Returns true when `node` is a call expression whose callee ends in one of
675
+ // the Repository-returning method names (e.g. `ds.getRepository(User)`).
676
+ const isRepositoryReturningCall = (node) => {
677
+ if (node.type !== "CallExpression" &&
678
+ node.type !== "OptionalCallExpression")
679
+ return false;
680
+ const call = node;
681
+ const callee = call.callee;
682
+ if (callee.type !== "MemberExpression" &&
683
+ callee.type !== "OptionalMemberExpression")
684
+ return false;
685
+ const member = callee;
686
+ if (member.property.type === "Identifier") {
687
+ return REPOSITORY_RETURNING_METHODS.has(member.property.name);
688
+ }
689
+ if (member.computed) {
690
+ const name = (0, exports.getStringValue)(member.property);
691
+ return name !== null && REPOSITORY_RETURNING_METHODS.has(name);
692
+ }
693
+ return false;
694
+ };
695
+ /**
696
+ * TypeORM DataSource-family type names, recognized on local bindings so
697
+ * `dataSource.manager.X()` can be classified as a Repository receiver via
698
+ * the `.manager` accessor chain.
699
+ */
700
+ exports.TYPEORM_DATASOURCE_TYPES = new Set([
701
+ "DataSource",
702
+ "Connection",
703
+ ]);
704
+ // Classifies a type name into one of the tracked receiver buckets, or
705
+ // returns null when the type isn't a TypeORM receiver we care about.
706
+ const classifyRepositoryTypeName = (typeName, bindings, forThisMember) => {
707
+ if (exports.TYPEORM_REPOSITORY_TYPES.has(typeName)) {
708
+ return forThisMember ? bindings.classProps : bindings.locals;
709
+ }
710
+ if (exports.TYPEORM_DATASOURCE_TYPES.has(typeName)) {
711
+ return forThisMember
712
+ ? bindings.dataSourceClassProps
713
+ : bindings.dataSourceLocals;
714
+ }
715
+ return null;
716
+ };
717
+ // Records a typed identifier (variable / parameter) into the appropriate
718
+ // binding bucket based on its TypeScript annotation.
719
+ const recordTypedIdentifier = (id, annotation, bindings, typeormNamespaceNames) => {
720
+ if (id.type !== "Identifier")
721
+ return;
722
+ const typeName = (0, exports.getTypeReferenceRootName)(annotation, typeormNamespaceNames);
723
+ if (!typeName)
724
+ return;
725
+ const bucket = classifyRepositoryTypeName(typeName, bindings, false);
726
+ bucket?.add(id.name);
727
+ };
728
+ // Resolves the underlying `Identifier` inside a function parameter — peeling
729
+ // `AssignmentPattern` (default params) and `TSParameterProperty` (constructor
730
+ // parameter properties). `isParameterProperty` is true only for
731
+ // TSParameterProperty, so callers can additionally record it as a
732
+ // `this.<name>` class member.
733
+ const unwrapParameterIdentifier = (param) => {
734
+ if (param.type === "Identifier") {
735
+ return {
736
+ id: param,
737
+ annotation: param.typeAnnotation ?? null,
738
+ isParameterProperty: false,
739
+ };
740
+ }
741
+ if (param.type !== "AssignmentPattern" &&
742
+ param.type !== "TSParameterProperty") {
743
+ return null;
744
+ }
745
+ const inner = param.left ??
746
+ param.parameter;
747
+ if (inner?.type !== "Identifier")
748
+ return null;
749
+ return {
750
+ id: inner,
751
+ annotation: inner.typeAnnotation ?? null,
752
+ isParameterProperty: param.type === "TSParameterProperty",
753
+ };
754
+ };
755
+ // Scans VariableDeclarators for typed identifiers and initializers that
756
+ // return a Repository via `.getRepository()`-family calls.
757
+ const collectDeclaratorBindings = (root, j, bindings, typeormNamespaceNames) => {
758
+ root.find(j.VariableDeclarator).forEach((p) => {
759
+ const id = p.node.id;
760
+ if (id.type !== "Identifier")
761
+ return;
762
+ recordTypedIdentifier(id, id.typeAnnotation ?? null, bindings, typeormNamespaceNames);
763
+ const init = p.node.init;
764
+ if (init && isRepositoryReturningCall(init)) {
765
+ bindings.locals.add(id.name);
766
+ }
767
+ });
768
+ root.find(j.AssignmentExpression).forEach((p) => {
769
+ const left = p.node.left;
770
+ if (left.type !== "Identifier")
771
+ return;
772
+ if (!isRepositoryReturningCall(p.node.right))
773
+ return;
774
+ bindings.locals.add(left.name);
775
+ });
776
+ };
777
+ // Scans function parameters on every function-like node. TSParameterProperty
778
+ // bindings (`constructor(private repo: Repository<T>)`) are tracked in both
779
+ // `locals` (for accesses inside the constructor body) AND `classProps` (for
780
+ // `this.repo` accesses elsewhere in the class).
781
+ const collectFunctionParamBindings = (root, j, bindings, typeormNamespaceNames) => {
782
+ const visit = (node) => {
783
+ for (const param of node.params) {
784
+ const unwrapped = unwrapParameterIdentifier(param);
785
+ if (!unwrapped)
786
+ continue;
787
+ recordTypedIdentifier(unwrapped.id, unwrapped.annotation, bindings, typeormNamespaceNames);
788
+ if (unwrapped.isParameterProperty &&
789
+ unwrapped.id.type === "Identifier") {
790
+ const typeName = (0, exports.getTypeReferenceRootName)(unwrapped.annotation, typeormNamespaceNames);
791
+ if (typeName) {
792
+ const classBucket = classifyRepositoryTypeName(typeName, bindings, true);
793
+ classBucket?.add(unwrapped.id.name);
794
+ }
795
+ }
796
+ }
797
+ };
798
+ root.find(j.FunctionDeclaration).forEach((p) => visit(p.node));
799
+ root.find(j.FunctionExpression).forEach((p) => visit(p.node));
800
+ root.find(j.ArrowFunctionExpression).forEach((p) => visit(p.node));
801
+ root.find(j.ClassMethod).forEach((p) => visit(p.node));
802
+ root.find(j.TSDeclareMethod).forEach((p) => visit(p.node));
803
+ };
804
+ // Scans class properties — `this.<name>` receivers are tracked separately
805
+ // from local identifiers because the access shape is different.
806
+ const collectClassPropertyBindings = (root, j, bindings, typeormNamespaceNames) => {
807
+ root.find(j.ClassProperty).forEach((p) => {
808
+ const key = p.node.key;
809
+ if (key.type !== "Identifier")
810
+ return;
811
+ const annotation = p.node.typeAnnotation ?? null;
812
+ const typeName = (0, exports.getTypeReferenceRootName)(annotation, typeormNamespaceNames);
813
+ if (!typeName)
814
+ return;
815
+ const bucket = classifyRepositoryTypeName(typeName, bindings, true);
816
+ bucket?.add(key.name);
817
+ });
818
+ };
819
+ const collectRepositoryBindings = (root, j) => {
820
+ const bindings = {
821
+ locals: new Set(),
822
+ classProps: new Set(),
823
+ dataSourceLocals: new Set(),
824
+ dataSourceClassProps: new Set(),
825
+ };
826
+ // Qualified type references like `typeorm.Repository<T>` must only be
827
+ // classified as TypeORM receivers when the qualifier is an actual
828
+ // namespace-local of `import * as typeorm from "typeorm"` (or the
829
+ // `import ns = require("typeorm")` variants). Without this gate, a
830
+ // file containing both a typeorm import and `Foo.Repository<T>` from
831
+ // some-other-lib would treat `Foo.Repository`-typed bindings as
832
+ // Repository receivers and mis-rewrite their method calls.
833
+ const typeormNamespaceNames = (0, exports.getNamespaceLocalNames)(root, j, "typeorm");
834
+ collectDeclaratorBindings(root, j, bindings, typeormNamespaceNames);
835
+ collectFunctionParamBindings(root, j, bindings, typeormNamespaceNames);
836
+ collectClassPropertyBindings(root, j, bindings, typeormNamespaceNames);
837
+ return bindings;
838
+ };
839
+ exports.collectRepositoryBindings = collectRepositoryBindings;
840
+ /**
841
+ * Returns true when `receiver` is a MemberExpression-eligible node whose
842
+ * root identifier is a Repository-bound local or `this.X` where `X` is a
843
+ * Repository-typed class property. Also accepts fresh inline
844
+ * `.getRepository(...)` call-expression receivers and DataSource-typed
845
+ * bindings dereferenced via `.manager` (e.g. `ds.manager.findByIds(...)`).
846
+ *
847
+ * When the file contains no Repository-typed bindings at all (no typed
848
+ * variables, no `.getRepository()` assignments, no Repository class
849
+ * properties), falls back to permissive matching — the codemod has no
850
+ * type signal and must trust the historical `fileImportsFrom("typeorm")`
851
+ * file-level guard. Callers should add negative fixtures to pin the
852
+ * common false-positive vectors.
853
+ */
854
+ // Matches `this.<prop>` where `<prop>` is a tracked DataSource class prop.
855
+ const isThisDataSourceProp = (node, dsClassProps) => (node.type === "MemberExpression" ||
856
+ node.type === "OptionalMemberExpression") &&
857
+ node.object.type === "ThisExpression" &&
858
+ node.property.type === "Identifier" &&
859
+ (dsClassProps?.has(node.property.name) ??
860
+ false);
861
+ // `<ds>.manager` — DataSource local/class-prop's `.manager` accessor
862
+ // returns an EntityManager, whose find-family methods match Repository's.
863
+ const isDataSourceManagerChain = (member, dsLocals, dsClassProps) => {
864
+ if (member.property.type !== "Identifier" ||
865
+ member.property.name !== "manager") {
866
+ return false;
867
+ }
868
+ if (member.object.type === "Identifier" &&
869
+ (dsLocals?.has(member.object.name) ?? false)) {
870
+ return true;
871
+ }
872
+ return isThisDataSourceProp(member.object, dsClassProps);
873
+ };
874
+ // Classifies MemberExpression / OptionalMemberExpression receivers.
875
+ const classifyMemberReceiver = (receiver, bindings, noBindingsFound) => {
876
+ const member = receiver;
877
+ if (isDataSourceManagerChain(member, bindings.dataSourceLocals, bindings.dataSourceClassProps)) {
878
+ return true;
879
+ }
880
+ if (member.object.type === "ThisExpression" &&
881
+ member.property.type === "Identifier") {
882
+ if (bindings.classProps.has(member.property.name))
883
+ return true;
884
+ return noBindingsFound;
885
+ }
886
+ // Chained access like `service.userRepo.findByIds(...)` — accept only
887
+ // when we have no binding info to disambiguate.
888
+ return noBindingsFound;
889
+ };
890
+ const isRepositoryReceiver = (receiver, bindings) => {
891
+ const noBindingsFound = bindings.locals.size === 0 &&
892
+ bindings.classProps.size === 0 &&
893
+ (bindings.dataSourceLocals?.size ?? 0) === 0 &&
894
+ (bindings.dataSourceClassProps?.size ?? 0) === 0;
895
+ if (receiver.type === "Identifier") {
896
+ if (bindings.locals.has(receiver.name))
897
+ return true;
898
+ return noBindingsFound;
899
+ }
900
+ if (receiver.type === "MemberExpression" ||
901
+ receiver.type === "OptionalMemberExpression") {
902
+ return classifyMemberReceiver(receiver, bindings, noBindingsFound);
903
+ }
904
+ if (receiver.type === "CallExpression" ||
905
+ receiver.type === "OptionalCallExpression") {
906
+ if (isRepositoryReturningCall(receiver))
907
+ return true;
908
+ return noBindingsFound;
909
+ }
910
+ return noBindingsFound;
911
+ };
912
+ exports.isRepositoryReceiver = isRepositoryReceiver;
153
913
  //# sourceMappingURL=ast-helpers.js.map