@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.
- package/README.md +13 -10
- package/dist/cli/print-guide.js +2 -2
- package/dist/cli/print-summary.js +3 -2
- package/dist/cli/print-summary.js.map +1 -1
- package/dist/cli/print-usage.js +2 -1
- package/dist/cli/print-usage.js.map +1 -1
- package/dist/cli/run-transforms.js +181 -79
- package/dist/cli/run-transforms.js.map +1 -1
- package/dist/dependencies/v1.js +23 -9
- package/dist/dependencies/v1.js.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/transforms/ast-helpers.js +779 -19
- package/dist/transforms/ast-helpers.js.map +1 -1
- package/dist/transforms/stats.js +11 -0
- package/dist/transforms/stats.js.map +1 -1
- package/dist/transforms/todo.js +42 -2
- package/dist/transforms/todo.js.map +1 -1
- package/dist/transforms/v1/column-readonly.js +28 -13
- package/dist/transforms/v1/column-readonly.js.map +1 -1
- package/dist/transforms/v1/column-unsigned-numeric.js +27 -26
- package/dist/transforms/v1/column-unsigned-numeric.js.map +1 -1
- package/dist/transforms/v1/column-width-zerofill.js +2 -1
- package/dist/transforms/v1/column-width-zerofill.js.map +1 -1
- package/dist/transforms/v1/connection-manager.js +82 -0
- package/dist/transforms/v1/connection-manager.js.map +1 -0
- package/dist/transforms/v1/connection-options-reader.js +214 -0
- package/dist/transforms/v1/connection-options-reader.js.map +1 -0
- package/dist/transforms/v1/connection-to-datasource.js +858 -93
- package/dist/transforms/v1/connection-to-datasource.js.map +1 -1
- package/dist/transforms/v1/datasource-expo.js +79 -0
- package/dist/transforms/v1/datasource-expo.js.map +1 -0
- package/dist/transforms/v1/datasource-mongodb.js +103 -64
- package/dist/transforms/v1/datasource-mongodb.js.map +1 -1
- package/dist/transforms/v1/datasource-mssql.js +88 -46
- package/dist/transforms/v1/datasource-mssql.js.map +1 -1
- package/dist/transforms/v1/datasource-mysql-connector.js +25 -6
- package/dist/transforms/v1/datasource-mysql-connector.js.map +1 -1
- package/dist/transforms/v1/datasource-name.js +19 -14
- package/dist/transforms/v1/datasource-name.js.map +1 -1
- package/dist/transforms/v1/datasource-sap.js +51 -26
- package/dist/transforms/v1/datasource-sap.js.map +1 -1
- package/dist/transforms/v1/datasource-sqlite-options.js +48 -13
- package/dist/transforms/v1/datasource-sqlite-options.js.map +1 -1
- package/dist/transforms/v1/datasource-sqlite-type.js +28 -7
- package/dist/transforms/v1/datasource-sqlite-type.js.map +1 -1
- package/dist/transforms/v1/file-logger.js +163 -0
- package/dist/transforms/v1/file-logger.js.map +1 -0
- package/dist/transforms/v1/find-options-join.js +84 -0
- package/dist/transforms/v1/find-options-join.js.map +1 -0
- package/dist/transforms/v1/find-options-lock-modes.js +26 -21
- package/dist/transforms/v1/find-options-lock-modes.js.map +1 -1
- package/dist/transforms/v1/find-options-string-relations.js +75 -9
- package/dist/transforms/v1/find-options-string-relations.js.map +1 -1
- package/dist/transforms/v1/find-options-string-select.js +75 -11
- package/dist/transforms/v1/find-options-string-select.js.map +1 -1
- package/dist/transforms/v1/global-functions.js +150 -64
- package/dist/transforms/v1/global-functions.js.map +1 -1
- package/dist/transforms/v1/index.js +16 -3
- package/dist/transforms/v1/index.js.map +1 -1
- package/dist/transforms/v1/migrations-get-all.js +25 -9
- package/dist/transforms/v1/migrations-get-all.js.map +1 -1
- package/dist/transforms/v1/mongodb-stats.js +21 -11
- package/dist/transforms/v1/mongodb-stats.js.map +1 -1
- package/dist/transforms/v1/mongodb-types.js +59 -2
- package/dist/transforms/v1/mongodb-types.js.map +1 -1
- package/dist/transforms/v1/query-builder-native-parameters.js +2 -0
- package/dist/transforms/v1/query-builder-native-parameters.js.map +1 -1
- package/dist/transforms/v1/query-builder-on-conflict.js +32 -18
- package/dist/transforms/v1/query-builder-on-conflict.js.map +1 -1
- package/dist/transforms/v1/query-builder-or-update.js +8 -4
- package/dist/transforms/v1/query-builder-or-update.js.map +1 -1
- package/dist/transforms/v1/query-builder-print-sql.js +18 -12
- package/dist/transforms/v1/query-builder-print-sql.js.map +1 -1
- package/dist/transforms/v1/query-builder-replace-property-names.js +16 -12
- package/dist/transforms/v1/query-builder-replace-property-names.js.map +1 -1
- package/dist/transforms/v1/query-builder-where-expression.js +29 -12
- package/dist/transforms/v1/query-builder-where-expression.js.map +1 -1
- package/dist/transforms/v1/query-runner-loaded-tables-views.js +1 -6
- package/dist/transforms/v1/query-runner-loaded-tables-views.js.map +1 -1
- package/dist/transforms/v1/relation-count.js +78 -12
- package/dist/transforms/v1/relation-count.js.map +1 -1
- package/dist/transforms/v1/repository-abstract.js +148 -34
- package/dist/transforms/v1/repository-abstract.js.map +1 -1
- package/dist/transforms/v1/repository-exist.js +20 -1
- package/dist/transforms/v1/repository-exist.js.map +1 -1
- package/dist/transforms/v1/repository-find-by-ids.js +67 -27
- package/dist/transforms/v1/repository-find-by-ids.js.map +1 -1
- package/dist/transforms/v1/repository-find-one-by-id.js +6 -0
- package/dist/transforms/v1/repository-find-one-by-id.js.map +1 -1
- package/dist/transforms/v1/use-container.js +65 -15
- package/dist/transforms/v1/use-container.js.map +1 -1
- package/package.json +3 -1
- package/dist/cli/colors.d.ts +0 -7
- package/dist/cli/colors.js +0 -12
- package/dist/cli/colors.js.map +0 -1
- package/dist/cli/error.d.ts +0 -1
- package/dist/cli/error.js +0 -11
- package/dist/cli/error.js.map +0 -1
- package/dist/cli/list-transforms.d.ts +0 -1
- package/dist/cli/parse-args.d.ts +0 -10
- package/dist/cli/print-guide.d.ts +0 -1
- package/dist/cli/print-summary.d.ts +0 -17
- package/dist/cli/print-todos.d.ts +0 -1
- package/dist/cli/print-usage.d.ts +0 -1
- package/dist/cli/run-dependencies.d.ts +0 -6
- package/dist/cli/run-transforms.d.ts +0 -21
- package/dist/cli/run.d.ts +0 -9
- package/dist/dependencies/config.d.ts +0 -24
- package/dist/dependencies/find-package-json.d.ts +0 -5
- package/dist/dependencies/index.d.ts +0 -5
- package/dist/dependencies/upgrade.d.ts +0 -11
- package/dist/dependencies/v1.d.ts +0 -2
- package/dist/index.d.ts +0 -2
- package/dist/lib/colors.d.ts +0 -9
- package/dist/lib/error.d.ts +0 -1
- package/dist/lib/format-time.d.ts +0 -1
- package/dist/lib/highlight.d.ts +0 -1
- package/dist/lib/spinner.d.ts +0 -5
- package/dist/transforms/ast-helpers.d.ts +0 -50
- package/dist/transforms/index.d.ts +0 -7
- package/dist/transforms/resolve.d.ts +0 -1
- package/dist/transforms/scan.d.ts +0 -4
- package/dist/transforms/scan.js +0 -33
- package/dist/transforms/scan.js.map +0 -1
- package/dist/transforms/stats.d.ts +0 -11
- package/dist/transforms/todo.d.ts +0 -2
- package/dist/transforms/transformer.d.ts +0 -8
- package/dist/transforms/v1/column-readonly.d.ts +0 -6
- package/dist/transforms/v1/column-unsigned-numeric.d.ts +0 -6
- package/dist/transforms/v1/column-width-zerofill.d.ts +0 -6
- package/dist/transforms/v1/connection-to-datasource.d.ts +0 -6
- package/dist/transforms/v1/datasource-mongodb.d.ts +0 -7
- package/dist/transforms/v1/datasource-mssql-domain.d.ts +0 -7
- package/dist/transforms/v1/datasource-mssql-domain.js +0 -35
- package/dist/transforms/v1/datasource-mssql-domain.js.map +0 -1
- package/dist/transforms/v1/datasource-mssql.d.ts +0 -7
- package/dist/transforms/v1/datasource-mysql-connector.d.ts +0 -6
- package/dist/transforms/v1/datasource-name.d.ts +0 -6
- package/dist/transforms/v1/datasource-sap.d.ts +0 -6
- package/dist/transforms/v1/datasource-sqlite-options.d.ts +0 -6
- package/dist/transforms/v1/datasource-sqlite-type.d.ts +0 -6
- package/dist/transforms/v1/find-options-lock-modes.d.ts +0 -6
- package/dist/transforms/v1/find-options-string-relations.d.ts +0 -6
- package/dist/transforms/v1/find-options-string-select.d.ts +0 -6
- package/dist/transforms/v1/global-functions.d.ts +0 -7
- package/dist/transforms/v1/index.d.ts +0 -40
- package/dist/transforms/v1/migrations-get-all.d.ts +0 -7
- package/dist/transforms/v1/mongodb-stats.d.ts +0 -7
- package/dist/transforms/v1/mongodb-types.d.ts +0 -6
- package/dist/transforms/v1/query-builder-native-parameters.d.ts +0 -6
- package/dist/transforms/v1/query-builder-on-conflict.d.ts +0 -7
- package/dist/transforms/v1/query-builder-or-update.d.ts +0 -6
- package/dist/transforms/v1/query-builder-print-sql.d.ts +0 -7
- package/dist/transforms/v1/query-builder-replace-property-names.d.ts +0 -7
- package/dist/transforms/v1/query-builder-where-expression.d.ts +0 -6
- package/dist/transforms/v1/query-runner-loaded-tables-views.d.ts +0 -7
- package/dist/transforms/v1/relation-count.d.ts +0 -7
- package/dist/transforms/v1/remove-abstract-repository.d.ts +0 -5
- package/dist/transforms/v1/remove-abstract-repository.js +0 -93
- package/dist/transforms/v1/remove-abstract-repository.js.map +0 -1
- package/dist/transforms/v1/remove-connector-package.d.ts +0 -4
- package/dist/transforms/v1/remove-connector-package.js +0 -19
- package/dist/transforms/v1/remove-connector-package.js.map +0 -1
- package/dist/transforms/v1/remove-datasource-name.d.ts +0 -4
- package/dist/transforms/v1/remove-datasource-name.js +0 -56
- package/dist/transforms/v1/remove-datasource-name.js.map +0 -1
- package/dist/transforms/v1/remove-mongodb-options.d.ts +0 -5
- package/dist/transforms/v1/remove-mongodb-options.js +0 -86
- package/dist/transforms/v1/remove-mongodb-options.js.map +0 -1
- package/dist/transforms/v1/remove-relation-count.d.ts +0 -5
- package/dist/transforms/v1/remove-relation-count.js +0 -49
- package/dist/transforms/v1/remove-relation-count.js.map +0 -1
- package/dist/transforms/v1/remove-replace-property-names.d.ts +0 -5
- package/dist/transforms/v1/remove-replace-property-names.js +0 -35
- package/dist/transforms/v1/remove-replace-property-names.js.map +0 -1
- package/dist/transforms/v1/remove-sap-options.d.ts +0 -4
- package/dist/transforms/v1/remove-sap-options.js +0 -54
- package/dist/transforms/v1/remove-sap-options.js.map +0 -1
- package/dist/transforms/v1/remove-unsigned-numeric.d.ts +0 -4
- package/dist/transforms/v1/remove-unsigned-numeric.js +0 -76
- package/dist/transforms/v1/remove-unsigned-numeric.js.map +0 -1
- package/dist/transforms/v1/remove-use-container.d.ts +0 -5
- package/dist/transforms/v1/remove-use-container.js +0 -63
- package/dist/transforms/v1/remove-use-container.js.map +0 -1
- package/dist/transforms/v1/remove-width-zerofill.d.ts +0 -4
- package/dist/transforms/v1/remove-width-zerofill.js +0 -29
- package/dist/transforms/v1/remove-width-zerofill.js.map +0 -1
- package/dist/transforms/v1/rename-connection-to-datasource.d.ts +0 -4
- package/dist/transforms/v1/rename-connection-to-datasource.js +0 -111
- package/dist/transforms/v1/rename-connection-to-datasource.js.map +0 -1
- package/dist/transforms/v1/rename-exist-to-exists.d.ts +0 -4
- package/dist/transforms/v1/rename-exist-to-exists.js +0 -25
- package/dist/transforms/v1/rename-exist-to-exists.js.map +0 -1
- package/dist/transforms/v1/rename-find-by-ids.d.ts +0 -4
- package/dist/transforms/v1/rename-find-by-ids.js +0 -64
- package/dist/transforms/v1/rename-find-by-ids.js.map +0 -1
- package/dist/transforms/v1/rename-get-all-migrations.d.ts +0 -5
- package/dist/transforms/v1/rename-get-all-migrations.js +0 -32
- package/dist/transforms/v1/rename-get-all-migrations.js.map +0 -1
- package/dist/transforms/v1/rename-loaded-tables-views.d.ts +0 -5
- package/dist/transforms/v1/rename-loaded-tables-views.js +0 -48
- package/dist/transforms/v1/rename-loaded-tables-views.js.map +0 -1
- package/dist/transforms/v1/rename-or-update-overload.d.ts +0 -4
- package/dist/transforms/v1/rename-or-update-overload.js +0 -46
- package/dist/transforms/v1/rename-or-update-overload.js.map +0 -1
- package/dist/transforms/v1/rename-print-sql-to-log-query.d.ts +0 -4
- package/dist/transforms/v1/rename-print-sql-to-log-query.js +0 -25
- package/dist/transforms/v1/rename-print-sql-to-log-query.js.map +0 -1
- package/dist/transforms/v1/replace-global-functions.d.ts +0 -5
- package/dist/transforms/v1/replace-global-functions.js +0 -92
- package/dist/transforms/v1/replace-global-functions.js.map +0 -1
- package/dist/transforms/v1/replace-lock-modes.d.ts +0 -4
- package/dist/transforms/v1/replace-lock-modes.js +0 -75
- package/dist/transforms/v1/replace-lock-modes.js.map +0 -1
- package/dist/transforms/v1/replace-mongodb-stats.d.ts +0 -5
- package/dist/transforms/v1/replace-mongodb-stats.js +0 -54
- package/dist/transforms/v1/replace-mongodb-stats.js.map +0 -1
- package/dist/transforms/v1/replace-mongodb-types.d.ts +0 -4
- package/dist/transforms/v1/replace-mongodb-types.js +0 -72
- package/dist/transforms/v1/replace-mongodb-types.js.map +0 -1
- package/dist/transforms/v1/replace-mssql-domain.d.ts +0 -5
- package/dist/transforms/v1/replace-mssql-domain.js +0 -25
- package/dist/transforms/v1/replace-mssql-domain.js.map +0 -1
- package/dist/transforms/v1/replace-on-conflict.d.ts +0 -5
- package/dist/transforms/v1/replace-on-conflict.js +0 -49
- package/dist/transforms/v1/replace-on-conflict.js.map +0 -1
- package/dist/transforms/v1/replace-readonly-column.d.ts +0 -4
- package/dist/transforms/v1/replace-readonly-column.js +0 -28
- package/dist/transforms/v1/replace-readonly-column.js.map +0 -1
- package/dist/transforms/v1/replace-set-native-parameters.d.ts +0 -4
- package/dist/transforms/v1/replace-set-native-parameters.js +0 -25
- package/dist/transforms/v1/replace-set-native-parameters.js.map +0 -1
- package/dist/transforms/v1/replace-sqlite-options.d.ts +0 -4
- package/dist/transforms/v1/replace-sqlite-options.js +0 -29
- package/dist/transforms/v1/replace-sqlite-options.js.map +0 -1
- package/dist/transforms/v1/replace-sqlite-type.d.ts +0 -4
- package/dist/transforms/v1/replace-sqlite-type.js +0 -22
- package/dist/transforms/v1/replace-sqlite-type.js.map +0 -1
- package/dist/transforms/v1/replace-where-expression-type.d.ts +0 -4
- package/dist/transforms/v1/replace-where-expression-type.js +0 -41
- package/dist/transforms/v1/replace-where-expression-type.js.map +0 -1
- package/dist/transforms/v1/repository-abstract.d.ts +0 -7
- package/dist/transforms/v1/repository-exist.d.ts +0 -6
- package/dist/transforms/v1/repository-find-by-ids.d.ts +0 -6
- package/dist/transforms/v1/repository-find-one-by-id.d.ts +0 -6
- package/dist/transforms/v1/upgrade-dependencies.d.ts +0 -15
- package/dist/transforms/v1/upgrade-dependencies.js +0 -163
- package/dist/transforms/v1/upgrade-dependencies.js.map +0 -1
- 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
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
*
|
|
65
|
-
*
|
|
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.
|
|
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
|