@expo/entity-codemod 0.40.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/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # @expo/entity-codemod
2
+
3
+ A package containing jscodeshift codemods for @expo/entity upgrades.
4
+
5
+ Codemods are transformations that run on your codebase programmatically. This allows for a large amount of changes to be applied without having to manually go through every file.
6
+
7
+ ## Usage
8
+
9
+ These should be used via the [jscodeshift CLI](https://github.com/facebook/jscodeshift?tab=readme-ov-file#usage-cli).
10
+
11
+ For example:
12
+
13
+ ```
14
+ yarn add -D @expo/entity-codemod
15
+ ```
16
+
17
+ Then, in package.json scripts:
18
+
19
+ ```json
20
+ "jscodeshift": "jscodeshift --extensions=ts --parser=ts"
21
+ ```
22
+
23
+ And finally:
24
+ ```sh
25
+ yarn jscodeshift src -t node_modules/@expo/entity-codemod/build/v0.39.0-v0.40.0.js
26
+ ```
File without changes
package/build/index.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const fs_1 = require("fs");
4
+ const path_1 = require("path");
5
+ jest.autoMockOff();
6
+ const defineTest = require('jscodeshift/dist/testUtils').defineTest;
7
+ const fixtureDir = 'v0.39.0-v0.40.0';
8
+ const fixtureDirPath = (0, path_1.join)(__dirname, '..', '__testfixtures__', fixtureDir);
9
+ const fixtures = (0, fs_1.readdirSync)(fixtureDirPath)
10
+ .filter((file) => file.endsWith('.input.ts'))
11
+ .map((file) => file.replace('.input.ts', ''));
12
+ for (const fixture of fixtures) {
13
+ const prefix = `${fixtureDir}/${fixture}`;
14
+ defineTest(__dirname, 'v0.39.0-v0.40.0', null, prefix, { parser: 'ts' });
15
+ }
16
+ //# sourceMappingURL=v0.39.0-v0.40.0-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v0.39.0-v0.40.0-test.js","sourceRoot":"","sources":["../../../src/transforms/__tests__/v0.39.0-v0.40.0-test.ts"],"names":[],"mappings":";;AAAA,2BAAiC;AACjC,+BAA4B;AAE5B,IAAI,CAAC,WAAW,EAAE,CAAC;AACnB,MAAM,UAAU,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC,UAAU,CAAC;AAEpE,MAAM,UAAU,GAAG,iBAAiB,CAAC;AACrC,MAAM,cAAc,GAAG,IAAA,WAAI,EAAC,SAAS,EAAE,IAAI,EAAE,kBAAkB,EAAE,UAAU,CAAC,CAAC;AAC7E,MAAM,QAAQ,GAAG,IAAA,gBAAW,EAAC,cAAc,CAAC;KACzC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;KAC5C,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC;AAEhD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,GAAG,UAAU,IAAI,OAAO,EAAE,CAAC;IAC1C,UAAU,CAAC,SAAS,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { API, FileInfo, Options } from 'jscodeshift';
2
+ export default function transformer(file: FileInfo, api: API, _options: Options): string;
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = transformer;
4
+ function transformEnforcingEntityChainMethod(j, root, type, crudType, methodBefore, methodAfter) {
5
+ // Find all entity creator expressions of the form
6
+ // `TestEntity.creator(viewerContext).setField('wat', 2).enforceCreateAsync()`
7
+ root
8
+ .find(j.CallExpression, {
9
+ callee: {
10
+ type: 'MemberExpression',
11
+ property: {
12
+ name: methodBefore,
13
+ },
14
+ },
15
+ })
16
+ .forEach((path) => {
17
+ const enforceCreateAsyncCallExpression = path.node; // TestEntity.creator(viewerContext).setField('wat', 2).enforceCreateAsync()
18
+ const enforceCreateAsyncCallee = enforceCreateAsyncCallExpression.callee; // TestEntity.creator(viewerContext).setField('wat', 2).enforceCreateAsync
19
+ if (enforceCreateAsyncCallee.type !== 'MemberExpression') {
20
+ return;
21
+ }
22
+ if (enforceCreateAsyncCallee.property.type !== 'Identifier') {
23
+ return;
24
+ }
25
+ // traverse in until we find the first non-setField call
26
+ let lastCallee = enforceCreateAsyncCallee;
27
+ while (true) {
28
+ // TestEntity.creator(viewerContext).setField('wat', 2)
29
+ const maybeSetFieldObjectCallExpression = lastCallee.object;
30
+ if (maybeSetFieldObjectCallExpression.type !== 'CallExpression') {
31
+ break;
32
+ }
33
+ // TestEntity.creator(viewerContext).setField
34
+ const maybeSetFieldObjectCallee = maybeSetFieldObjectCallExpression.callee;
35
+ if (maybeSetFieldObjectCallee.type !== 'MemberExpression') {
36
+ break;
37
+ }
38
+ const maybeSetFieldObjectCalleeProperty = maybeSetFieldObjectCallee.property;
39
+ if (maybeSetFieldObjectCalleeProperty.type !== 'Identifier') {
40
+ break;
41
+ }
42
+ if (maybeSetFieldObjectCalleeProperty.name === 'setField') {
43
+ lastCallee = maybeSetFieldObjectCallee;
44
+ }
45
+ else {
46
+ break;
47
+ }
48
+ }
49
+ // TestEntity.creator(viewerContext)
50
+ const maybeCreatorCallExpression = lastCallee.object;
51
+ if (maybeCreatorCallExpression.type !== 'CallExpression') {
52
+ return;
53
+ }
54
+ if (maybeCreatorCallExpression.callee.type !== 'MemberExpression' ||
55
+ maybeCreatorCallExpression.callee.property.type !== 'Identifier' ||
56
+ maybeCreatorCallExpression.callee.property.name !== crudType) {
57
+ return;
58
+ }
59
+ // TestEntity.creator(viewerContext).enforcing()
60
+ const newCreatorCallExpression = j.callExpression(j.memberExpression(maybeCreatorCallExpression, j.identifier(type)), []);
61
+ // TestEntity.creator(viewerContext).enforcing().setField('wat', 2).createAsync()
62
+ lastCallee.object = newCreatorCallExpression;
63
+ // change enforceCreateAsync to createAsync at the end so if this returns early it doesn't mutate
64
+ enforceCreateAsyncCallee.property.name = methodAfter;
65
+ });
66
+ }
67
+ function transformEnforcingEntityDeleteMethod(j, root, type, methodBefore, methodAfter) {
68
+ // Find all entity creator expressions of the form
69
+ // `TestEntity.enforceDeleteAsync(entity)`
70
+ root
71
+ .find(j.CallExpression, {
72
+ callee: {
73
+ type: 'MemberExpression',
74
+ property: {
75
+ name: methodBefore,
76
+ },
77
+ },
78
+ })
79
+ .forEach((path) => {
80
+ const enforceDeleteAsyncCallExpression = path.node; // TestEntity.enforceDeleteAsync(entity)
81
+ const args = enforceDeleteAsyncCallExpression.arguments; // [entity]
82
+ enforceDeleteAsyncCallExpression.arguments = [];
83
+ const enforceDeleteAsyncCallee = enforceDeleteAsyncCallExpression.callee; // TestEntity.enforceDeleteAsync
84
+ if (enforceDeleteAsyncCallee.type !== 'MemberExpression') {
85
+ return;
86
+ }
87
+ // change enforceDeleteAsync to deleteAsync
88
+ if (enforceDeleteAsyncCallee.property.type !== 'Identifier') {
89
+ return;
90
+ }
91
+ enforceDeleteAsyncCallee.property.name = methodAfter;
92
+ enforceDeleteAsyncCallee.object = j.callExpression(j.memberExpression(j.callExpression(j.memberExpression(enforceDeleteAsyncCallee.object, j.identifier('deleter')), args), j.identifier(type)), []);
93
+ });
94
+ }
95
+ function transformAssociationLoaderMethod(j, root) {
96
+ // Find all entity associationLoader expressions of the form
97
+ // `this.associationLoader()`
98
+ root
99
+ .find(j.CallExpression, {
100
+ callee: {
101
+ type: 'MemberExpression',
102
+ property: {
103
+ name: 'associationLoader',
104
+ },
105
+ },
106
+ })
107
+ .forEach((path) => {
108
+ const associationLoaderCallExpression = path.node; // this.associationLoader()
109
+ // replace with this.associationLoader().withAuthorizationResults()
110
+ path.replace(j.callExpression(j.memberExpression(associationLoaderCallExpression, j.identifier('withAuthorizationResults')), []));
111
+ });
112
+ }
113
+ function transformer(file, api, _options) {
114
+ const j = api.jscodeshift;
115
+ const root = j.withParser('ts')(file.source);
116
+ // do authorization results first since it uses the same detection pattern as enforcing post-transform (i.e. it looks for createAsync)
117
+ transformEnforcingEntityChainMethod(j, root, 'withAuthorizationResults', 'creator', 'createAsync', 'createAsync');
118
+ transformEnforcingEntityChainMethod(j, root, 'withAuthorizationResults', 'updater', 'updateAsync', 'updateAsync');
119
+ transformEnforcingEntityDeleteMethod(j, root, 'withAuthorizationResults', 'deleteAsync', 'deleteAsync');
120
+ // now do enforcing
121
+ transformEnforcingEntityChainMethod(j, root, 'enforcing', 'creator', 'enforceCreateAsync', 'createAsync');
122
+ transformEnforcingEntityChainMethod(j, root, 'enforcing', 'updater', 'enforceUpdateAsync', 'updateAsync');
123
+ transformEnforcingEntityDeleteMethod(j, root, 'enforcing', 'enforceDeleteAsync', 'deleteAsync');
124
+ // association loader
125
+ transformAssociationLoaderMethod(j, root);
126
+ return root.toSource();
127
+ }
128
+ //# sourceMappingURL=v0.39.0-v0.40.0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v0.39.0-v0.40.0.js","sourceRoot":"","sources":["../../src/transforms/v0.39.0-v0.40.0.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@expo/entity-codemod",
3
+ "version": "0.40.0",
4
+ "description": "jscodeshift codemods for @expo/entity upgrades",
5
+ "files": [
6
+ "build",
7
+ "src"
8
+ ],
9
+ "main": "build/index.js",
10
+ "types": "build/index.d.ts",
11
+ "scripts": {
12
+ "tsc": "tsc",
13
+ "clean": "rm -rf build coverage coverage-integration",
14
+ "lint": "eslint src",
15
+ "lint-fix": "eslint src --fix",
16
+ "test": "jest --rootDir . --config ../../resources/jest.config.js"
17
+ },
18
+ "engines": {
19
+ "node": ">=16"
20
+ },
21
+ "keywords": [
22
+ "entity"
23
+ ],
24
+ "author": "Expo",
25
+ "license": "MIT",
26
+ "devDependencies": {
27
+ "@types/jest": "^29.5.12",
28
+ "@types/jscodeshift": "^0.12.0",
29
+ "@types/node": "^20.14.1",
30
+ "eslint": "^8.57.1",
31
+ "eslint-config-universe": "^14.0.0",
32
+ "eslint-plugin-tsdoc": "^0.3.0",
33
+ "jest": "^29.7.0",
34
+ "jscodeshift": "^17.1.2",
35
+ "prettier": "^3.3.3",
36
+ "ts-jest": "^29.2.5",
37
+ "ts-mockito": "^2.6.1",
38
+ "typescript": "^5.7.3"
39
+ }
40
+ }
package/src/index.ts ADDED
File without changes
@@ -0,0 +1,79 @@
1
+ /* eslint-disable */
2
+
3
+ import { ViewerContext } from '@expo/entity';
4
+
5
+ async function testWithAuthorizationResults(viewerContext: ViewerContext): Promise<void> {
6
+ // creator
7
+ const entityResult = await TestEntity.creator(viewerContext).setField('wat', 2).createAsync();
8
+
9
+ // updater
10
+ const updatedEntityResult = await TestEntity.updater(entityResult.enforceValue()).setField('wat', 3).updateAsync();
11
+
12
+ // deleter
13
+ await TestEntity.deleteAsync(updatedEntityResult.enforceValue());
14
+ }
15
+
16
+ async function testEnforcing(viewerContext: ViewerContext): Promise<void> {
17
+ // creator
18
+ const entity = await TestEntity.creator(viewerContext).setField('wat', 2).enforceCreateAsync();
19
+ const entity2 = await TestEntity.creator(viewerContext).setField('wat', 2).setField('who', 3).enforceCreateAsync();
20
+ const entity3 = await TestEntity.creator(viewerContext)
21
+ .setField('wat', 2)
22
+ .setField('who', 3)
23
+ .setField('who', 4)
24
+ .setField('who', 5)
25
+ .setField('who', 6)
26
+ .setField('who', 7)
27
+ .setField('who', 8)
28
+ .setField('who', 9)
29
+ .enforceCreateAsync();
30
+
31
+ // updater
32
+ const updatedEntity = await TestEntity.updater(entity).setField('wat', 3).enforceUpdateAsync();
33
+ const updatedEntity2 = await TestEntity.updater(entity2).setField('wat', 3).setField('who', 4).enforceUpdateAsync();
34
+ const updatedEntity3 = await TestEntity.updater(entity3)
35
+ .setField('wat', 3)
36
+ .setField('who', 4)
37
+ .setField('who', 5)
38
+ .setField('who', 6)
39
+ .setField('who', 7)
40
+ .setField('who', 8)
41
+ .setField('who', 9)
42
+ .enforceUpdateAsync();
43
+
44
+ // deleter
45
+ await TestEntity.enforceDeleteAsync(entity);
46
+ }
47
+
48
+ async function testNoSetField(viewerContext: ViewerContext): Promise<void> {
49
+ // creator
50
+ const entity = await TestEntity.creator(viewerContext).enforceCreateAsync();
51
+
52
+ // updater
53
+ const updatedEntity = await TestEntity.updater(entity).enforceUpdateAsync();
54
+ }
55
+
56
+ async function testEnforcingWithQueryContext(viewerContext: ViewerContext): Promise<void> {
57
+ // creator
58
+ const entity = await TestEntity.creator(viewerContext, queryContext).setField('wat', 2).enforceCreateAsync();
59
+
60
+ // updater
61
+ const updatedEntity = await TestEntity.updater(entity, queryContext).setField('wat', 3).enforceUpdateAsync();
62
+
63
+ // deleter
64
+ await TestEntity.enforceDeleteAsync(entity, queryContext);
65
+ }
66
+
67
+ async function testAssociationLoader(): Promise<void> {
68
+ await this.associationLoader().loadAssociatedEntityAsync(
69
+ 'another_id',
70
+ AnotherEntity,
71
+ queryContext
72
+ );
73
+
74
+ await entity.associationLoader().loadAssociatedEntityAsync(
75
+ 'another_id',
76
+ AnotherEntity,
77
+ queryContext
78
+ )
79
+ }
@@ -0,0 +1,79 @@
1
+ /* eslint-disable */
2
+
3
+ import { ViewerContext } from '@expo/entity';
4
+
5
+ async function testWithAuthorizationResults(viewerContext: ViewerContext): Promise<void> {
6
+ // creator
7
+ const entityResult = await TestEntity.creator(viewerContext).withAuthorizationResults().setField('wat', 2).createAsync();
8
+
9
+ // updater
10
+ const updatedEntityResult = await TestEntity.updater(entityResult.enforceValue()).withAuthorizationResults().setField('wat', 3).updateAsync();
11
+
12
+ // deleter
13
+ await TestEntity.deleter(updatedEntityResult.enforceValue()).withAuthorizationResults().deleteAsync();
14
+ }
15
+
16
+ async function testEnforcing(viewerContext: ViewerContext): Promise<void> {
17
+ // creator
18
+ const entity = await TestEntity.creator(viewerContext).enforcing().setField('wat', 2).createAsync();
19
+ const entity2 = await TestEntity.creator(viewerContext).enforcing().setField('wat', 2).setField('who', 3).createAsync();
20
+ const entity3 = await TestEntity.creator(viewerContext).enforcing()
21
+ .setField('wat', 2)
22
+ .setField('who', 3)
23
+ .setField('who', 4)
24
+ .setField('who', 5)
25
+ .setField('who', 6)
26
+ .setField('who', 7)
27
+ .setField('who', 8)
28
+ .setField('who', 9)
29
+ .createAsync();
30
+
31
+ // updater
32
+ const updatedEntity = await TestEntity.updater(entity).enforcing().setField('wat', 3).updateAsync();
33
+ const updatedEntity2 = await TestEntity.updater(entity2).enforcing().setField('wat', 3).setField('who', 4).updateAsync();
34
+ const updatedEntity3 = await TestEntity.updater(entity3).enforcing()
35
+ .setField('wat', 3)
36
+ .setField('who', 4)
37
+ .setField('who', 5)
38
+ .setField('who', 6)
39
+ .setField('who', 7)
40
+ .setField('who', 8)
41
+ .setField('who', 9)
42
+ .updateAsync();
43
+
44
+ // deleter
45
+ await TestEntity.deleter(entity).enforcing().deleteAsync();
46
+ }
47
+
48
+ async function testNoSetField(viewerContext: ViewerContext): Promise<void> {
49
+ // creator
50
+ const entity = await TestEntity.creator(viewerContext).enforcing().createAsync();
51
+
52
+ // updater
53
+ const updatedEntity = await TestEntity.updater(entity).enforcing().updateAsync();
54
+ }
55
+
56
+ async function testEnforcingWithQueryContext(viewerContext: ViewerContext): Promise<void> {
57
+ // creator
58
+ const entity = await TestEntity.creator(viewerContext, queryContext).enforcing().setField('wat', 2).createAsync();
59
+
60
+ // updater
61
+ const updatedEntity = await TestEntity.updater(entity, queryContext).enforcing().setField('wat', 3).updateAsync();
62
+
63
+ // deleter
64
+ await TestEntity.deleter(entity, queryContext).enforcing().deleteAsync();
65
+ }
66
+
67
+ async function testAssociationLoader(): Promise<void> {
68
+ await this.associationLoader().withAuthorizationResults().loadAssociatedEntityAsync(
69
+ 'another_id',
70
+ AnotherEntity,
71
+ queryContext
72
+ );
73
+
74
+ await entity.associationLoader().withAuthorizationResults().loadAssociatedEntityAsync(
75
+ 'another_id',
76
+ AnotherEntity,
77
+ queryContext
78
+ )
79
+ }
@@ -0,0 +1,10 @@
1
+ /* eslint-disable */
2
+
3
+ async function testAsync(viewerContext: ViewerContext): Promise<void> {
4
+ const testTrigger = await TestTriggerEntity.createAsync(actorViewerContext, undefined, {
5
+ appId,
6
+ });
7
+
8
+ await TestTriggerEntity.enforceDeleteAsync(testTrigger);
9
+ return testTrigger;
10
+ };
@@ -0,0 +1,10 @@
1
+ /* eslint-disable */
2
+
3
+ async function testAsync(viewerContext: ViewerContext): Promise<void> {
4
+ const testTrigger = await TestTriggerEntity.createAsync(actorViewerContext, undefined, {
5
+ appId,
6
+ });
7
+
8
+ await TestTriggerEntity.deleter(testTrigger).enforcing().deleteAsync();
9
+ return testTrigger;
10
+ };
@@ -0,0 +1,5 @@
1
+ /* eslint-disable */
2
+
3
+ async function anotherCase(updater: any): Promise<any> {
4
+ return await updater.enforceUpdateAsync();
5
+ }
@@ -0,0 +1,5 @@
1
+ /* eslint-disable */
2
+
3
+ async function anotherCase(updater: any): Promise<any> {
4
+ return await updater.enforceUpdateAsync();
5
+ }
@@ -0,0 +1,16 @@
1
+ import { readdirSync } from 'fs';
2
+ import { join } from 'path';
3
+
4
+ jest.autoMockOff();
5
+ const defineTest = require('jscodeshift/dist/testUtils').defineTest;
6
+
7
+ const fixtureDir = 'v0.39.0-v0.40.0';
8
+ const fixtureDirPath = join(__dirname, '..', '__testfixtures__', fixtureDir);
9
+ const fixtures = readdirSync(fixtureDirPath)
10
+ .filter((file) => file.endsWith('.input.ts'))
11
+ .map((file) => file.replace('.input.ts', ''));
12
+
13
+ for (const fixture of fixtures) {
14
+ const prefix = `${fixtureDir}/${fixture}`;
15
+ defineTest(__dirname, 'v0.39.0-v0.40.0', null, prefix, { parser: 'ts' });
16
+ }
@@ -0,0 +1,213 @@
1
+ import { API, Collection, FileInfo, Options } from 'jscodeshift';
2
+
3
+ function transformEnforcingEntityChainMethod(
4
+ j: API['jscodeshift'],
5
+ root: Collection<any>,
6
+ type: 'enforcing' | 'withAuthorizationResults',
7
+ crudType: 'creator' | 'updater',
8
+ methodBefore: string,
9
+ methodAfter: string,
10
+ ): void {
11
+ // Find all entity creator expressions of the form
12
+ // `TestEntity.creator(viewerContext).setField('wat', 2).enforceCreateAsync()`
13
+ root
14
+ .find(j.CallExpression, {
15
+ callee: {
16
+ type: 'MemberExpression',
17
+ property: {
18
+ name: methodBefore,
19
+ },
20
+ },
21
+ })
22
+ .forEach((path) => {
23
+ const enforceCreateAsyncCallExpression = path.node; // TestEntity.creator(viewerContext).setField('wat', 2).enforceCreateAsync()
24
+ const enforceCreateAsyncCallee = enforceCreateAsyncCallExpression.callee; // TestEntity.creator(viewerContext).setField('wat', 2).enforceCreateAsync
25
+ if (enforceCreateAsyncCallee.type !== 'MemberExpression') {
26
+ return;
27
+ }
28
+
29
+ if (enforceCreateAsyncCallee.property.type !== 'Identifier') {
30
+ return;
31
+ }
32
+
33
+ // traverse in until we find the first non-setField call
34
+ let lastCallee = enforceCreateAsyncCallee;
35
+ while (true) {
36
+ // TestEntity.creator(viewerContext).setField('wat', 2)
37
+ const maybeSetFieldObjectCallExpression = lastCallee.object;
38
+ if (maybeSetFieldObjectCallExpression.type !== 'CallExpression') {
39
+ break;
40
+ }
41
+
42
+ // TestEntity.creator(viewerContext).setField
43
+ const maybeSetFieldObjectCallee = maybeSetFieldObjectCallExpression.callee;
44
+ if (maybeSetFieldObjectCallee.type !== 'MemberExpression') {
45
+ break;
46
+ }
47
+
48
+ const maybeSetFieldObjectCalleeProperty = maybeSetFieldObjectCallee.property;
49
+ if (maybeSetFieldObjectCalleeProperty.type !== 'Identifier') {
50
+ break;
51
+ }
52
+
53
+ if (maybeSetFieldObjectCalleeProperty.name === 'setField') {
54
+ lastCallee = maybeSetFieldObjectCallee;
55
+ } else {
56
+ break;
57
+ }
58
+ }
59
+
60
+ // TestEntity.creator(viewerContext)
61
+ const maybeCreatorCallExpression = lastCallee.object;
62
+ if (maybeCreatorCallExpression.type !== 'CallExpression') {
63
+ return;
64
+ }
65
+ if (
66
+ maybeCreatorCallExpression.callee.type !== 'MemberExpression' ||
67
+ maybeCreatorCallExpression.callee.property.type !== 'Identifier' ||
68
+ maybeCreatorCallExpression.callee.property.name !== crudType
69
+ ) {
70
+ return;
71
+ }
72
+
73
+ // TestEntity.creator(viewerContext).enforcing()
74
+ const newCreatorCallExpression = j.callExpression(
75
+ j.memberExpression(maybeCreatorCallExpression, j.identifier(type)),
76
+ [],
77
+ );
78
+
79
+ // TestEntity.creator(viewerContext).enforcing().setField('wat', 2).createAsync()
80
+ lastCallee.object = newCreatorCallExpression;
81
+
82
+ // change enforceCreateAsync to createAsync at the end so if this returns early it doesn't mutate
83
+ enforceCreateAsyncCallee.property.name = methodAfter;
84
+ });
85
+ }
86
+
87
+ function transformEnforcingEntityDeleteMethod(
88
+ j: API['jscodeshift'],
89
+ root: Collection<any>,
90
+ type: 'enforcing' | 'withAuthorizationResults',
91
+ methodBefore: string,
92
+ methodAfter: string,
93
+ ): void {
94
+ // Find all entity creator expressions of the form
95
+ // `TestEntity.enforceDeleteAsync(entity)`
96
+ root
97
+ .find(j.CallExpression, {
98
+ callee: {
99
+ type: 'MemberExpression',
100
+ property: {
101
+ name: methodBefore,
102
+ },
103
+ },
104
+ })
105
+ .forEach((path) => {
106
+ const enforceDeleteAsyncCallExpression = path.node; // TestEntity.enforceDeleteAsync(entity)
107
+ const args = enforceDeleteAsyncCallExpression.arguments; // [entity]
108
+ enforceDeleteAsyncCallExpression.arguments = [];
109
+ const enforceDeleteAsyncCallee = enforceDeleteAsyncCallExpression.callee; // TestEntity.enforceDeleteAsync
110
+ if (enforceDeleteAsyncCallee.type !== 'MemberExpression') {
111
+ return;
112
+ }
113
+
114
+ // change enforceDeleteAsync to deleteAsync
115
+ if (enforceDeleteAsyncCallee.property.type !== 'Identifier') {
116
+ return;
117
+ }
118
+ enforceDeleteAsyncCallee.property.name = methodAfter;
119
+
120
+ enforceDeleteAsyncCallee.object = j.callExpression(
121
+ j.memberExpression(
122
+ j.callExpression(
123
+ j.memberExpression(enforceDeleteAsyncCallee.object, j.identifier('deleter')),
124
+ args,
125
+ ),
126
+ j.identifier(type),
127
+ ),
128
+ [],
129
+ );
130
+ });
131
+ }
132
+
133
+ function transformAssociationLoaderMethod(j: API['jscodeshift'], root: Collection<any>): void {
134
+ // Find all entity associationLoader expressions of the form
135
+ // `this.associationLoader()`
136
+ root
137
+ .find(j.CallExpression, {
138
+ callee: {
139
+ type: 'MemberExpression',
140
+ property: {
141
+ name: 'associationLoader',
142
+ },
143
+ },
144
+ })
145
+ .forEach((path) => {
146
+ const associationLoaderCallExpression = path.node; // this.associationLoader()
147
+
148
+ // replace with this.associationLoader().withAuthorizationResults()
149
+ path.replace(
150
+ j.callExpression(
151
+ j.memberExpression(
152
+ associationLoaderCallExpression,
153
+ j.identifier('withAuthorizationResults'),
154
+ ),
155
+ [],
156
+ ),
157
+ );
158
+ });
159
+ }
160
+
161
+ export default function transformer(file: FileInfo, api: API, _options: Options): string {
162
+ const j = api.jscodeshift;
163
+ const root = j.withParser('ts')(file.source);
164
+
165
+ // do authorization results first since it uses the same detection pattern as enforcing post-transform (i.e. it looks for createAsync)
166
+ transformEnforcingEntityChainMethod(
167
+ j,
168
+ root,
169
+ 'withAuthorizationResults',
170
+ 'creator',
171
+ 'createAsync',
172
+ 'createAsync',
173
+ );
174
+ transformEnforcingEntityChainMethod(
175
+ j,
176
+ root,
177
+ 'withAuthorizationResults',
178
+ 'updater',
179
+ 'updateAsync',
180
+ 'updateAsync',
181
+ );
182
+ transformEnforcingEntityDeleteMethod(
183
+ j,
184
+ root,
185
+ 'withAuthorizationResults',
186
+ 'deleteAsync',
187
+ 'deleteAsync',
188
+ );
189
+
190
+ // now do enforcing
191
+ transformEnforcingEntityChainMethod(
192
+ j,
193
+ root,
194
+ 'enforcing',
195
+ 'creator',
196
+ 'enforceCreateAsync',
197
+ 'createAsync',
198
+ );
199
+ transformEnforcingEntityChainMethod(
200
+ j,
201
+ root,
202
+ 'enforcing',
203
+ 'updater',
204
+ 'enforceUpdateAsync',
205
+ 'updateAsync',
206
+ );
207
+ transformEnforcingEntityDeleteMethod(j, root, 'enforcing', 'enforceDeleteAsync', 'deleteAsync');
208
+
209
+ // association loader
210
+ transformAssociationLoaderMethod(j, root);
211
+
212
+ return root.toSource();
213
+ }