@typeorm/codemod 1.0.1 → 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 +1 -0
- package/dist/cli/print-usage.js +1 -0
- package/dist/cli/print-usage.js.map +1 -1
- package/dist/cli/run-transforms.js +167 -79
- package/dist/cli/run-transforms.js.map +1 -1
- package/dist/dependencies/v1.js +1 -0
- 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 +641 -17
- 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 +22 -5
- package/dist/transforms/todo.js.map +1 -1
- package/dist/transforms/v1/column-unsigned-numeric.js +21 -18
- package/dist/transforms/v1/column-unsigned-numeric.js.map +1 -1
- package/dist/transforms/v1/connection-options-reader.js +23 -7
- package/dist/transforms/v1/connection-options-reader.js.map +1 -1
- package/dist/transforms/v1/connection-to-datasource.js +613 -77
- 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 +77 -50
- package/dist/transforms/v1/datasource-mongodb.js.map +1 -1
- package/dist/transforms/v1/datasource-mssql.js +88 -44
- package/dist/transforms/v1/datasource-mssql.js.map +1 -1
- package/dist/transforms/v1/datasource-mysql-connector.js +22 -6
- package/dist/transforms/v1/datasource-mysql-connector.js.map +1 -1
- package/dist/transforms/v1/datasource-name.js +17 -14
- package/dist/transforms/v1/datasource-name.js.map +1 -1
- package/dist/transforms/v1/datasource-sap.js +51 -39
- package/dist/transforms/v1/datasource-sap.js.map +1 -1
- package/dist/transforms/v1/file-logger.js +2 -21
- package/dist/transforms/v1/file-logger.js.map +1 -1
- package/dist/transforms/v1/find-options-string-relations.js +75 -11
- package/dist/transforms/v1/find-options-string-relations.js.map +1 -1
- package/dist/transforms/v1/find-options-string-select.js +75 -13
- package/dist/transforms/v1/find-options-string-select.js.map +1 -1
- package/dist/transforms/v1/global-functions.js +79 -21
- package/dist/transforms/v1/global-functions.js.map +1 -1
- package/dist/transforms/v1/index.js +6 -1
- package/dist/transforms/v1/index.js.map +1 -1
- package/dist/transforms/v1/mongodb-stats.js +18 -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-on-conflict.js +30 -17
- package/dist/transforms/v1/query-builder-on-conflict.js.map +1 -1
- package/dist/transforms/v1/query-builder-print-sql.js +15 -12
- package/dist/transforms/v1/query-builder-print-sql.js.map +1 -1
- package/dist/transforms/v1/query-builder-replace-property-names.js +13 -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 +0 -1
- package/dist/transforms/v1/query-runner-loaded-tables-views.js.map +1 -1
- package/dist/transforms/v1/relation-count.js +3 -0
- package/dist/transforms/v1/relation-count.js.map +1 -1
- package/dist/transforms/v1/repository-abstract.js +146 -29
- package/dist/transforms/v1/repository-abstract.js.map +1 -1
- package/dist/transforms/v1/repository-exist.js +18 -1
- package/dist/transforms/v1/repository-exist.js.map +1 -1
- package/dist/transforms/v1/repository-find-by-ids.js +67 -24
- package/dist/transforms/v1/repository-find-by-ids.js.map +1 -1
- package/dist/transforms/v1/repository-find-one-by-id.js +3 -0
- package/dist/transforms/v1/repository-find-one-by-id.js.map +1 -1
- package/dist/transforms/v1/use-container.js +60 -12
- package/dist/transforms/v1/use-container.js.map +1 -1
- package/package.json +1 -1
|
@@ -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.expandLocalNamesForImports = exports.TYPEORM_COLUMN_DECORATORS = exports.forEachIdentifierParam = exports.getLocalNamesForImport = 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,11 +30,12 @@ exports.setStringValue = setStringValue;
|
|
|
30
30
|
const isIdentifier = (node) => node.type === "Identifier";
|
|
31
31
|
exports.isIdentifier = isIdentifier;
|
|
32
32
|
/**
|
|
33
|
-
* Checks whether the file
|
|
34
|
-
*
|
|
35
|
-
* and recognizes ESM `import`,
|
|
36
|
-
*
|
|
37
|
-
*
|
|
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.
|
|
38
39
|
*/
|
|
39
40
|
const fileImportsFrom = (root, j, moduleName) => {
|
|
40
41
|
const prefix = `${moduleName}/`;
|
|
@@ -46,6 +47,19 @@ const fileImportsFrom = (root, j, moduleName) => {
|
|
|
46
47
|
.some((path) => matchesModule(path.node.source.value))) {
|
|
47
48
|
return true;
|
|
48
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
|
+
}
|
|
49
63
|
// TS: import ... = require("typeorm[/subpath]")
|
|
50
64
|
if (root.find(j.TSImportEqualsDeclaration).some((path) => {
|
|
51
65
|
const ref = path.node.moduleReference;
|
|
@@ -132,6 +146,58 @@ const getLocalNamesForImport = (root, j, moduleName, importedName) => {
|
|
|
132
146
|
return localNames;
|
|
133
147
|
};
|
|
134
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;
|
|
135
201
|
/**
|
|
136
202
|
* Calls `callback` for each Identifier parameter found in function-like
|
|
137
203
|
* nodes (functions, methods, arrows) and TSParameterProperty nodes.
|
|
@@ -216,25 +282,153 @@ const forEachDecoratorObjectArg = (root, j, callback, decoratorNames) => {
|
|
|
216
282
|
});
|
|
217
283
|
};
|
|
218
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;
|
|
219
410
|
/**
|
|
220
411
|
* Removes properties matching the given key names from an ObjectExpression.
|
|
221
412
|
* Matches both identifier keys (`name`) and string-literal keys (`"name"`).
|
|
222
413
|
* Returns true if any properties were removed.
|
|
223
414
|
*/
|
|
224
|
-
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) => {
|
|
225
427
|
const original = obj.properties.length;
|
|
226
|
-
obj.properties = obj.properties.filter((prop) =>
|
|
227
|
-
if (prop.type !== "Property" && prop.type !== "ObjectProperty") {
|
|
228
|
-
return true;
|
|
229
|
-
}
|
|
230
|
-
const keyName = prop.key.type === "Identifier"
|
|
231
|
-
? prop.key.name
|
|
232
|
-
: (0, exports.getStringValue)(prop.key);
|
|
233
|
-
return keyName === null ? true : !propertyNames.has(keyName);
|
|
234
|
-
});
|
|
428
|
+
obj.properties = obj.properties.filter((prop) => !predicate(prop));
|
|
235
429
|
return obj.properties.length !== original;
|
|
236
430
|
};
|
|
237
|
-
exports.
|
|
431
|
+
exports.removeObjectPropertiesWhere = removeObjectPropertiesWhere;
|
|
238
432
|
/**
|
|
239
433
|
* Finds imports from a module, removes the specified named import specifiers,
|
|
240
434
|
* and removes the entire import declaration if no specifiers remain.
|
|
@@ -264,6 +458,77 @@ const removeImportSpecifiers = (root, j, moduleName, specifierNames) => {
|
|
|
264
458
|
return removed;
|
|
265
459
|
};
|
|
266
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;
|
|
267
532
|
/**
|
|
268
533
|
* Finds CallExpression nodes with a MemberExpression callee where the
|
|
269
534
|
* property matches `oldName`, and renames the property to `newName`.
|
|
@@ -286,4 +551,363 @@ const renameMemberMethod = (root, j, oldName, newName) => {
|
|
|
286
551
|
return renamed;
|
|
287
552
|
};
|
|
288
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;
|
|
289
913
|
//# sourceMappingURL=ast-helpers.js.map
|