@expo/entity-codemod 0.54.0 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/src/transforms/v0.55.0-v0.56.0.d.ts +2 -0
- package/build/src/transforms/v0.55.0-v0.56.0.js +144 -0
- package/build/src/transforms/v0.55.0-v0.56.0.js.map +1 -0
- package/package.json +3 -3
- package/src/transforms/__testfixtures__/v0.55.0-v0.56.0/test1.input.ts +37 -0
- package/src/transforms/__testfixtures__/v0.55.0-v0.56.0/test1.output.ts +37 -0
- package/src/transforms/__testfixtures__/v0.55.0-v0.56.0/test2.input.ts +33 -0
- package/src/transforms/__testfixtures__/v0.55.0-v0.56.0/test2.output.ts +33 -0
- package/src/transforms/__tests__/v0.55.0-v0.56.0-test.ts +17 -0
- package/src/transforms/v0.55.0-v0.56.0.ts +165 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = transformer;
|
|
4
|
+
const KNEX_SPECIFIC_METHODS = [
|
|
5
|
+
'loadFirstByFieldEqualityConjunctionAsync',
|
|
6
|
+
'loadManyByFieldEqualityConjunctionAsync',
|
|
7
|
+
'loadManyByRawWhereClauseAsync',
|
|
8
|
+
];
|
|
9
|
+
function isKnexSpecificMethodUsed(j, node) {
|
|
10
|
+
// Check if this loader call is followed by a knex-specific method
|
|
11
|
+
// We need to traverse the AST to find usages of the loader result
|
|
12
|
+
const parent = node.parent;
|
|
13
|
+
// Check if the loader call is directly chained with a knex method
|
|
14
|
+
if (parent?.value.type === 'MemberExpression' && parent.value.object === node.value) {
|
|
15
|
+
const grandParent = parent.parent;
|
|
16
|
+
if (grandParent?.value.type === 'CallExpression' && grandParent.value.callee === parent.value) {
|
|
17
|
+
if (parent.value.property.type === 'Identifier' &&
|
|
18
|
+
KNEX_SPECIFIC_METHODS.includes(parent.value.property.name)) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// Check if the loader is assigned to a variable and then used with knex methods
|
|
24
|
+
if (parent?.value.type === 'VariableDeclarator' && parent.value.init === node.value) {
|
|
25
|
+
const variableName = parent.value.id.name;
|
|
26
|
+
const scope = parent.scope;
|
|
27
|
+
// Find all references to this variable in the same scope
|
|
28
|
+
const references = j(scope.path)
|
|
29
|
+
.find(j.Identifier, { name: variableName })
|
|
30
|
+
.filter((path) => {
|
|
31
|
+
// Check if this identifier is used as object in member expression
|
|
32
|
+
const parentNode = path.parent.value;
|
|
33
|
+
if (parentNode.type === 'MemberExpression' && parentNode.object === path.value) {
|
|
34
|
+
const prop = parentNode.property;
|
|
35
|
+
if (prop.type === 'Identifier' && KNEX_SPECIFIC_METHODS.includes(prop.name)) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
});
|
|
41
|
+
if (references.size() > 0) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Check await expressions
|
|
46
|
+
if (parent?.value.type === 'AwaitExpression' && parent.value.argument === node.value) {
|
|
47
|
+
const awaitParent = parent.parent;
|
|
48
|
+
if (awaitParent?.value.type === 'VariableDeclarator') {
|
|
49
|
+
const variableName = awaitParent.value.id.name;
|
|
50
|
+
const scope = awaitParent.scope;
|
|
51
|
+
// Find all references to this variable in the same scope
|
|
52
|
+
const references = j(scope.path)
|
|
53
|
+
.find(j.Identifier, { name: variableName })
|
|
54
|
+
.filter((path) => {
|
|
55
|
+
const parentNode = path.parent.value;
|
|
56
|
+
if (parentNode.type === 'MemberExpression' && parentNode.object === path.value) {
|
|
57
|
+
const prop = parentNode.property;
|
|
58
|
+
if (prop.type === 'Identifier' && KNEX_SPECIFIC_METHODS.includes(prop.name)) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
});
|
|
64
|
+
if (references.size() > 0) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
function transformLoaderToKnexLoader(j, root) {
|
|
72
|
+
// Find all entity expressions of the form `Entity.loader(viewerContext)`
|
|
73
|
+
root
|
|
74
|
+
.find(j.CallExpression, {
|
|
75
|
+
callee: {
|
|
76
|
+
type: 'MemberExpression',
|
|
77
|
+
property: {
|
|
78
|
+
name: 'loader',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
})
|
|
82
|
+
.forEach((path) => {
|
|
83
|
+
const loaderCallExpression = path.node; // Entity.loader(viewerContext)
|
|
84
|
+
const loaderCallee = loaderCallExpression.callee; // Entity.loader
|
|
85
|
+
if (loaderCallee.type !== 'MemberExpression') {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// Make sure this is a static method call on an entity (not on an instance)
|
|
89
|
+
// Typically entity names start with uppercase letter
|
|
90
|
+
if (loaderCallee.object.type === 'Identifier') {
|
|
91
|
+
const firstChar = loaderCallee.object.name[0];
|
|
92
|
+
if (firstChar === firstChar?.toUpperCase()) {
|
|
93
|
+
// Check if this loader uses knex-specific methods
|
|
94
|
+
if (isKnexSpecificMethodUsed(j, path)) {
|
|
95
|
+
// Rename loader to knexLoader
|
|
96
|
+
if (loaderCallee.property.type === 'Identifier') {
|
|
97
|
+
loaderCallee.property.name = 'knexLoader';
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function transformLoaderWithAuthorizationResultsToKnexLoaderWithAuthorizationResults(j, root) {
|
|
105
|
+
// Find all entity expressions of the form `Entity.loaderWithAuthorizationResults(viewerContext)`
|
|
106
|
+
root
|
|
107
|
+
.find(j.CallExpression, {
|
|
108
|
+
callee: {
|
|
109
|
+
type: 'MemberExpression',
|
|
110
|
+
property: {
|
|
111
|
+
name: 'loaderWithAuthorizationResults',
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
.forEach((path) => {
|
|
116
|
+
const loaderCallExpression = path.node; // Entity.loaderWithAuthorizationResults(viewerContext)
|
|
117
|
+
const loaderCallee = loaderCallExpression.callee; // Entity.loaderWithAuthorizationResults
|
|
118
|
+
if (loaderCallee.type !== 'MemberExpression') {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// Make sure this is a static method call on an entity (not on an instance)
|
|
122
|
+
// Typically entity names start with uppercase letter
|
|
123
|
+
if (loaderCallee.object.type === 'Identifier') {
|
|
124
|
+
const firstChar = loaderCallee.object.name[0];
|
|
125
|
+
if (firstChar === firstChar?.toUpperCase()) {
|
|
126
|
+
// Check if this loader uses knex-specific methods
|
|
127
|
+
if (isKnexSpecificMethodUsed(j, path)) {
|
|
128
|
+
// Rename loaderWithAuthorizationResults to knexLoaderWithAuthorizationResults
|
|
129
|
+
if (loaderCallee.property.type === 'Identifier') {
|
|
130
|
+
loaderCallee.property.name = 'knexLoaderWithAuthorizationResults';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function transformer(file, api, _options) {
|
|
138
|
+
const j = api.jscodeshift;
|
|
139
|
+
const root = j.withParser('ts')(file.source);
|
|
140
|
+
transformLoaderToKnexLoader(j, root);
|
|
141
|
+
transformLoaderWithAuthorizationResultsToKnexLoaderWithAuthorizationResults(j, root);
|
|
142
|
+
return root.toSource();
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=v0.55.0-v0.56.0.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v0.55.0-v0.56.0.js","sourceRoot":"","sources":["../../../src/transforms/v0.55.0-v0.56.0.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expo/entity-codemod",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.57.0",
|
|
4
4
|
"description": "jscodeshift codemods for @expo/entity upgrades",
|
|
5
5
|
"files": [
|
|
6
6
|
"build",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@jest/globals": "30.2.0",
|
|
31
31
|
"@types/jscodeshift": "17.3.0",
|
|
32
|
-
"@types/node": "24.10.
|
|
32
|
+
"@types/node": "24.10.13",
|
|
33
33
|
"jscodeshift": "17.3.0",
|
|
34
34
|
"typescript": "5.9.3"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "e876cfb27bb9b0004d81b40c9067481e3e0c2beb"
|
|
37
37
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ViewerContext } from '@expo/entity';
|
|
2
|
+
import { UserEntity } from './entities/UserEntity';
|
|
3
|
+
import { PostEntity } from './entities/PostEntity';
|
|
4
|
+
|
|
5
|
+
async function loadUser(viewerContext: ViewerContext) {
|
|
6
|
+
// Basic loader calls - only transformed when using knex-specific methods
|
|
7
|
+
const userLoader = UserEntity.loader(viewerContext);
|
|
8
|
+
const postLoader = PostEntity.loader(viewerContext);
|
|
9
|
+
|
|
10
|
+
// These use knex-specific methods, so they should be transformed
|
|
11
|
+
const posts = await postLoader.loadManyByFieldEqualityConjunctionAsync([
|
|
12
|
+
{ fieldName: 'status', fieldValue: 'published' }
|
|
13
|
+
]);
|
|
14
|
+
const firstPost = await postLoader.loadFirstByFieldEqualityConjunctionAsync([
|
|
15
|
+
{ fieldName: 'id', fieldValue: '123' }
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
// Loader with authorization results - only transformed when using knex methods
|
|
19
|
+
const userLoaderWithAuth = UserEntity.loaderWithAuthorizationResults(viewerContext);
|
|
20
|
+
const rawResults = await userLoaderWithAuth.loadManyByRawWhereClauseAsync('age > ?', [18]);
|
|
21
|
+
|
|
22
|
+
// Loader that doesn't use knex methods - should NOT be transformed
|
|
23
|
+
const standardLoader = PostEntity.loader(viewerContext);
|
|
24
|
+
const post = await standardLoader.loadByIDAsync('456');
|
|
25
|
+
|
|
26
|
+
// Should not transform instance methods or other properties
|
|
27
|
+
const user = await userLoader.loadByIDAsync('123');
|
|
28
|
+
const userLoadMethod = user.loader; // This should not be transformed
|
|
29
|
+
|
|
30
|
+
// Should not transform lowercase object methods
|
|
31
|
+
const customLoader = {
|
|
32
|
+
loader: (ctx: any) => ctx,
|
|
33
|
+
};
|
|
34
|
+
customLoader.loader(viewerContext);
|
|
35
|
+
|
|
36
|
+
return user;
|
|
37
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ViewerContext } from '@expo/entity';
|
|
2
|
+
import { UserEntity } from './entities/UserEntity';
|
|
3
|
+
import { PostEntity } from './entities/PostEntity';
|
|
4
|
+
|
|
5
|
+
async function loadUser(viewerContext: ViewerContext) {
|
|
6
|
+
// Basic loader calls - only transformed when using knex-specific methods
|
|
7
|
+
const userLoader = UserEntity.loader(viewerContext);
|
|
8
|
+
const postLoader = PostEntity.knexLoader(viewerContext);
|
|
9
|
+
|
|
10
|
+
// These use knex-specific methods, so they should be transformed
|
|
11
|
+
const posts = await postLoader.loadManyByFieldEqualityConjunctionAsync([
|
|
12
|
+
{ fieldName: 'status', fieldValue: 'published' }
|
|
13
|
+
]);
|
|
14
|
+
const firstPost = await postLoader.loadFirstByFieldEqualityConjunctionAsync([
|
|
15
|
+
{ fieldName: 'id', fieldValue: '123' }
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
// Loader with authorization results - only transformed when using knex methods
|
|
19
|
+
const userLoaderWithAuth = UserEntity.knexLoaderWithAuthorizationResults(viewerContext);
|
|
20
|
+
const rawResults = await userLoaderWithAuth.loadManyByRawWhereClauseAsync('age > ?', [18]);
|
|
21
|
+
|
|
22
|
+
// Loader that doesn't use knex methods - should NOT be transformed
|
|
23
|
+
const standardLoader = PostEntity.loader(viewerContext);
|
|
24
|
+
const post = await standardLoader.loadByIDAsync('456');
|
|
25
|
+
|
|
26
|
+
// Should not transform instance methods or other properties
|
|
27
|
+
const user = await userLoader.loadByIDAsync('123');
|
|
28
|
+
const userLoadMethod = user.loader; // This should not be transformed
|
|
29
|
+
|
|
30
|
+
// Should not transform lowercase object methods
|
|
31
|
+
const customLoader = {
|
|
32
|
+
loader: (ctx: any) => ctx,
|
|
33
|
+
};
|
|
34
|
+
customLoader.loader(viewerContext);
|
|
35
|
+
|
|
36
|
+
return user;
|
|
37
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ViewerContext } from '@expo/entity';
|
|
2
|
+
import { CommentEntity } from './entities/CommentEntity';
|
|
3
|
+
|
|
4
|
+
// Chained calls
|
|
5
|
+
const loadComments = async (viewerContext: ViewerContext) => {
|
|
6
|
+
// Direct chaining with knex-specific method
|
|
7
|
+
const comments = await CommentEntity.loader(viewerContext)
|
|
8
|
+
.loadManyByFieldEqualityConjunctionAsync([
|
|
9
|
+
{ fieldName: 'postId', fieldValue: '123' }
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
// Direct chaining with regular method - should NOT be transformed
|
|
13
|
+
const singleComment = await CommentEntity
|
|
14
|
+
.loader(viewerContext)
|
|
15
|
+
.loadByIDAsync('456');
|
|
16
|
+
|
|
17
|
+
// With authorization results and knex method
|
|
18
|
+
const commentsWithAuth = await CommentEntity
|
|
19
|
+
.loaderWithAuthorizationResults(viewerContext)
|
|
20
|
+
.loadManyByRawWhereClauseAsync('postId = ?', ['456']);
|
|
21
|
+
|
|
22
|
+
// Edge cases - these should NOT be transformed
|
|
23
|
+
const anotherEntity = {
|
|
24
|
+
loader: (ctx: any) => ctx, // This is not an entity class
|
|
25
|
+
};
|
|
26
|
+
anotherEntity.loader(viewerContext); // Should NOT be transformed (lowercase object)
|
|
27
|
+
|
|
28
|
+
// Complex chaining with regular method - should NOT be transformed
|
|
29
|
+
return CommentEntity
|
|
30
|
+
.loader(viewerContext)
|
|
31
|
+
.withAuthenticationResults()
|
|
32
|
+
.loadByIDAsync('789');
|
|
33
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ViewerContext } from '@expo/entity';
|
|
2
|
+
import { CommentEntity } from './entities/CommentEntity';
|
|
3
|
+
|
|
4
|
+
// Chained calls
|
|
5
|
+
const loadComments = async (viewerContext: ViewerContext) => {
|
|
6
|
+
// Direct chaining with knex-specific method
|
|
7
|
+
const comments = await CommentEntity.knexLoader(viewerContext)
|
|
8
|
+
.loadManyByFieldEqualityConjunctionAsync([
|
|
9
|
+
{ fieldName: 'postId', fieldValue: '123' }
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
// Direct chaining with regular method - should NOT be transformed
|
|
13
|
+
const singleComment = await CommentEntity
|
|
14
|
+
.loader(viewerContext)
|
|
15
|
+
.loadByIDAsync('456');
|
|
16
|
+
|
|
17
|
+
// With authorization results and knex method
|
|
18
|
+
const commentsWithAuth = await CommentEntity
|
|
19
|
+
.knexLoaderWithAuthorizationResults(viewerContext)
|
|
20
|
+
.loadManyByRawWhereClauseAsync('postId = ?', ['456']);
|
|
21
|
+
|
|
22
|
+
// Edge cases - these should NOT be transformed
|
|
23
|
+
const anotherEntity = {
|
|
24
|
+
loader: (ctx: any) => ctx, // This is not an entity class
|
|
25
|
+
};
|
|
26
|
+
anotherEntity.loader(viewerContext); // Should NOT be transformed (lowercase object)
|
|
27
|
+
|
|
28
|
+
// Complex chaining with regular method - should NOT be transformed
|
|
29
|
+
return CommentEntity
|
|
30
|
+
.loader(viewerContext)
|
|
31
|
+
.withAuthenticationResults()
|
|
32
|
+
.loadByIDAsync('789');
|
|
33
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
import { readdirSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
jest.autoMockOff();
|
|
6
|
+
const defineTest = require('jscodeshift/dist/testUtils').defineTest;
|
|
7
|
+
|
|
8
|
+
const fixtureDir = 'v0.55.0-v0.56.0';
|
|
9
|
+
const fixtureDirPath = join(__dirname, '..', '__testfixtures__', fixtureDir);
|
|
10
|
+
const fixtures = readdirSync(fixtureDirPath)
|
|
11
|
+
.filter((file) => file.endsWith('.input.ts'))
|
|
12
|
+
.map((file) => file.replace('.input.ts', ''));
|
|
13
|
+
|
|
14
|
+
for (const fixture of fixtures) {
|
|
15
|
+
const prefix = `${fixtureDir}/${fixture}`;
|
|
16
|
+
defineTest(__dirname, 'v0.55.0-v0.56.0', null, prefix, { parser: 'ts' });
|
|
17
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { API, Collection, FileInfo, Options } from 'jscodeshift';
|
|
2
|
+
|
|
3
|
+
const KNEX_SPECIFIC_METHODS = [
|
|
4
|
+
'loadFirstByFieldEqualityConjunctionAsync',
|
|
5
|
+
'loadManyByFieldEqualityConjunctionAsync',
|
|
6
|
+
'loadManyByRawWhereClauseAsync',
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
function isKnexSpecificMethodUsed(j: API['jscodeshift'], node: any): boolean {
|
|
10
|
+
// Check if this loader call is followed by a knex-specific method
|
|
11
|
+
// We need to traverse the AST to find usages of the loader result
|
|
12
|
+
const parent = node.parent;
|
|
13
|
+
|
|
14
|
+
// Check if the loader call is directly chained with a knex method
|
|
15
|
+
if (parent?.value.type === 'MemberExpression' && parent.value.object === node.value) {
|
|
16
|
+
const grandParent = parent.parent;
|
|
17
|
+
if (grandParent?.value.type === 'CallExpression' && grandParent.value.callee === parent.value) {
|
|
18
|
+
if (
|
|
19
|
+
parent.value.property.type === 'Identifier' &&
|
|
20
|
+
KNEX_SPECIFIC_METHODS.includes(parent.value.property.name)
|
|
21
|
+
) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Check if the loader is assigned to a variable and then used with knex methods
|
|
28
|
+
if (parent?.value.type === 'VariableDeclarator' && parent.value.init === node.value) {
|
|
29
|
+
const variableName = parent.value.id.name;
|
|
30
|
+
const scope = parent.scope;
|
|
31
|
+
|
|
32
|
+
// Find all references to this variable in the same scope
|
|
33
|
+
const references = j(scope.path)
|
|
34
|
+
.find(j.Identifier, { name: variableName })
|
|
35
|
+
.filter((path) => {
|
|
36
|
+
// Check if this identifier is used as object in member expression
|
|
37
|
+
const parentNode = path.parent.value;
|
|
38
|
+
if (parentNode.type === 'MemberExpression' && parentNode.object === path.value) {
|
|
39
|
+
const prop = parentNode.property;
|
|
40
|
+
if (prop.type === 'Identifier' && KNEX_SPECIFIC_METHODS.includes(prop.name)) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (references.size() > 0) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check await expressions
|
|
53
|
+
if (parent?.value.type === 'AwaitExpression' && parent.value.argument === node.value) {
|
|
54
|
+
const awaitParent = parent.parent;
|
|
55
|
+
if (awaitParent?.value.type === 'VariableDeclarator') {
|
|
56
|
+
const variableName = awaitParent.value.id.name;
|
|
57
|
+
const scope = awaitParent.scope;
|
|
58
|
+
|
|
59
|
+
// Find all references to this variable in the same scope
|
|
60
|
+
const references = j(scope.path)
|
|
61
|
+
.find(j.Identifier, { name: variableName })
|
|
62
|
+
.filter((path) => {
|
|
63
|
+
const parentNode = path.parent.value;
|
|
64
|
+
if (parentNode.type === 'MemberExpression' && parentNode.object === path.value) {
|
|
65
|
+
const prop = parentNode.property;
|
|
66
|
+
if (prop.type === 'Identifier' && KNEX_SPECIFIC_METHODS.includes(prop.name)) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (references.size() > 0) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function transformLoaderToKnexLoader(j: API['jscodeshift'], root: Collection<any>): void {
|
|
83
|
+
// Find all entity expressions of the form `Entity.loader(viewerContext)`
|
|
84
|
+
root
|
|
85
|
+
.find(j.CallExpression, {
|
|
86
|
+
callee: {
|
|
87
|
+
type: 'MemberExpression',
|
|
88
|
+
property: {
|
|
89
|
+
name: 'loader',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
.forEach((path) => {
|
|
94
|
+
const loaderCallExpression = path.node; // Entity.loader(viewerContext)
|
|
95
|
+
const loaderCallee = loaderCallExpression.callee; // Entity.loader
|
|
96
|
+
|
|
97
|
+
if (loaderCallee.type !== 'MemberExpression') {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Make sure this is a static method call on an entity (not on an instance)
|
|
102
|
+
// Typically entity names start with uppercase letter
|
|
103
|
+
if (loaderCallee.object.type === 'Identifier') {
|
|
104
|
+
const firstChar = loaderCallee.object.name[0];
|
|
105
|
+
if (firstChar === firstChar?.toUpperCase()) {
|
|
106
|
+
// Check if this loader uses knex-specific methods
|
|
107
|
+
if (isKnexSpecificMethodUsed(j, path)) {
|
|
108
|
+
// Rename loader to knexLoader
|
|
109
|
+
if (loaderCallee.property.type === 'Identifier') {
|
|
110
|
+
loaderCallee.property.name = 'knexLoader';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function transformLoaderWithAuthorizationResultsToKnexLoaderWithAuthorizationResults(
|
|
119
|
+
j: API['jscodeshift'],
|
|
120
|
+
root: Collection<any>,
|
|
121
|
+
): void {
|
|
122
|
+
// Find all entity expressions of the form `Entity.loaderWithAuthorizationResults(viewerContext)`
|
|
123
|
+
root
|
|
124
|
+
.find(j.CallExpression, {
|
|
125
|
+
callee: {
|
|
126
|
+
type: 'MemberExpression',
|
|
127
|
+
property: {
|
|
128
|
+
name: 'loaderWithAuthorizationResults',
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
})
|
|
132
|
+
.forEach((path) => {
|
|
133
|
+
const loaderCallExpression = path.node; // Entity.loaderWithAuthorizationResults(viewerContext)
|
|
134
|
+
const loaderCallee = loaderCallExpression.callee; // Entity.loaderWithAuthorizationResults
|
|
135
|
+
|
|
136
|
+
if (loaderCallee.type !== 'MemberExpression') {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Make sure this is a static method call on an entity (not on an instance)
|
|
141
|
+
// Typically entity names start with uppercase letter
|
|
142
|
+
if (loaderCallee.object.type === 'Identifier') {
|
|
143
|
+
const firstChar = loaderCallee.object.name[0];
|
|
144
|
+
if (firstChar === firstChar?.toUpperCase()) {
|
|
145
|
+
// Check if this loader uses knex-specific methods
|
|
146
|
+
if (isKnexSpecificMethodUsed(j, path)) {
|
|
147
|
+
// Rename loaderWithAuthorizationResults to knexLoaderWithAuthorizationResults
|
|
148
|
+
if (loaderCallee.property.type === 'Identifier') {
|
|
149
|
+
loaderCallee.property.name = 'knexLoaderWithAuthorizationResults';
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export default function transformer(file: FileInfo, api: API, _options: Options): string {
|
|
158
|
+
const j = api.jscodeshift;
|
|
159
|
+
const root = j.withParser('ts')(file.source);
|
|
160
|
+
|
|
161
|
+
transformLoaderToKnexLoader(j, root);
|
|
162
|
+
transformLoaderWithAuthorizationResultsToKnexLoaderWithAuthorizationResults(j, root);
|
|
163
|
+
|
|
164
|
+
return root.toSource();
|
|
165
|
+
}
|