Skip to content

Commit bced9f8

Browse files
committed
fix: refactor install method to instead be free functions
1 parent 0938147 commit bced9f8

28 files changed

Lines changed: 470 additions & 838 deletions

packages/entity-codemod/src/transforms/__testfixtures__/v0.55.0-v0.56.0/test1.output.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import { ViewerContext } from '@expo/entity';
22
import { UserEntity } from './entities/UserEntity';
33
import { PostEntity } from './entities/PostEntity';
44

5+
import { knexLoader, knexLoaderWithAuthorizationResults } from "@expo/entity-database-adapter-knex";
6+
57
async function loadUser(viewerContext: ViewerContext) {
68
// Basic loader calls - only transformed when using knex-specific methods
79
const userLoader = UserEntity.loader(viewerContext);
8-
const postLoader = PostEntity.knexLoader(viewerContext);
10+
const postLoader = knexLoader(PostEntity, viewerContext);
911

1012
// These use knex-specific methods, so they should be transformed
1113
const posts = await postLoader.loadManyByFieldEqualityConjunctionAsync([
@@ -16,7 +18,7 @@ async function loadUser(viewerContext: ViewerContext) {
1618
]);
1719

1820
// Loader with authorization results - only transformed when using knex methods
19-
const userLoaderWithAuth = UserEntity.knexLoaderWithAuthorizationResults(viewerContext);
21+
const userLoaderWithAuth = knexLoaderWithAuthorizationResults(UserEntity, viewerContext);
2022
const rawResults = await userLoaderWithAuth.loadManyByRawWhereClauseAsync('age > ?', [18]);
2123

2224
// Loader that doesn't use knex methods - should NOT be transformed

packages/entity-codemod/src/transforms/__testfixtures__/v0.55.0-v0.56.0/test2.output.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { ViewerContext } from '@expo/entity';
22
import { CommentEntity } from './entities/CommentEntity';
33

4+
import { knexLoader, knexLoaderWithAuthorizationResults } from "@expo/entity-database-adapter-knex";
5+
46
// Chained calls
57
const loadComments = async (viewerContext: ViewerContext) => {
68
// Direct chaining with knex-specific method
7-
const comments = await CommentEntity.knexLoader(viewerContext)
9+
const comments = await knexLoader(CommentEntity, viewerContext)
810
.loadManyByFieldEqualityConjunctionAsync([
911
{ fieldName: 'postId', fieldValue: '123' }
1012
]);
@@ -15,8 +17,7 @@ const loadComments = async (viewerContext: ViewerContext) => {
1517
.loadByIDAsync('456');
1618

1719
// With authorization results and knex method
18-
const commentsWithAuth = await CommentEntity
19-
.knexLoaderWithAuthorizationResults(viewerContext)
20+
const commentsWithAuth = await knexLoaderWithAuthorizationResults(CommentEntity, viewerContext)
2021
.loadManyByRawWhereClauseAsync('postId = ?', ['456']);
2122

2223
// Edge cases - these should NOT be transformed

packages/entity-codemod/src/transforms/v0.55.0-v0.56.0.ts

Lines changed: 88 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ function isKnexSpecificMethodUsed(j: API['jscodeshift'], node: any): boolean {
8383
return false;
8484
}
8585

86-
function transformLoaderToKnexLoader(j: API['jscodeshift'], root: Collection<any>): void {
86+
function transformLoaderToKnexLoader(j: API['jscodeshift'], root: Collection<any>): boolean {
87+
let transformed = false;
88+
8789
// Find all entity expressions of the form `Entity.loader(viewerContext)`
8890
root
8991
.find(j.CallExpression, {
@@ -109,20 +111,28 @@ function transformLoaderToKnexLoader(j: API['jscodeshift'], root: Collection<any
109111
if (firstChar && firstChar === firstChar.toUpperCase()) {
110112
// Check if this loader uses knex-specific methods
111113
if (isKnexSpecificMethodUsed(j, path)) {
112-
// Rename loader to knexLoader
113-
if (loaderCallee.property.type === 'Identifier') {
114-
loaderCallee.property.name = 'knexLoader';
115-
}
114+
// Transform Entity.loader(viewerContext) → knexLoader(Entity, viewerContext)
115+
const entityIdentifier = loaderCallee.object;
116+
const args = loaderCallExpression.arguments;
117+
118+
j(path).replaceWith(
119+
j.callExpression(j.identifier('knexLoader'), [entityIdentifier, ...args]),
120+
);
121+
transformed = true;
116122
}
117123
}
118124
}
119125
});
126+
127+
return transformed;
120128
}
121129

122130
function transformLoaderWithAuthorizationResultsToKnexLoaderWithAuthorizationResults(
123131
j: API['jscodeshift'],
124132
root: Collection<any>,
125-
): void {
133+
): boolean {
134+
let transformed = false;
135+
126136
// Find all entity expressions of the form `Entity.loaderWithAuthorizationResults(viewerContext)`
127137
root
128138
.find(j.CallExpression, {
@@ -148,22 +158,88 @@ function transformLoaderWithAuthorizationResultsToKnexLoaderWithAuthorizationRes
148158
if (firstChar && firstChar === firstChar.toUpperCase()) {
149159
// Check if this loader uses knex-specific methods
150160
if (isKnexSpecificMethodUsed(j, path)) {
151-
// Rename loaderWithAuthorizationResults to knexLoaderWithAuthorizationResults
152-
if (loaderCallee.property.type === 'Identifier') {
153-
loaderCallee.property.name = 'knexLoaderWithAuthorizationResults';
154-
}
161+
// Transform Entity.loaderWithAuthorizationResults(viewerContext) → knexLoaderWithAuthorizationResults(Entity, viewerContext)
162+
const entityIdentifier = loaderCallee.object;
163+
const args = loaderCallExpression.arguments;
164+
165+
j(path).replaceWith(
166+
j.callExpression(j.identifier('knexLoaderWithAuthorizationResults'), [
167+
entityIdentifier,
168+
...args,
169+
]),
170+
);
171+
transformed = true;
155172
}
156173
}
157174
}
158175
});
176+
177+
return transformed;
178+
}
179+
180+
function addKnexImportIfNeeded(
181+
j: API['jscodeshift'],
182+
root: Collection<any>,
183+
needsKnexLoader: boolean,
184+
needsKnexLoaderWithAuthorizationResults: boolean,
185+
): void {
186+
if (!needsKnexLoader && !needsKnexLoaderWithAuthorizationResults) {
187+
return;
188+
}
189+
190+
const specifiers: string[] = [];
191+
if (needsKnexLoader) {
192+
specifiers.push('knexLoader');
193+
}
194+
if (needsKnexLoaderWithAuthorizationResults) {
195+
specifiers.push('knexLoaderWithAuthorizationResults');
196+
}
197+
198+
// Check if the import already exists
199+
const existingImport = root.find(j.ImportDeclaration, {
200+
source: { value: '@expo/entity-database-adapter-knex' },
201+
});
202+
203+
if (existingImport.size() > 0) {
204+
// Add specifiers to existing import
205+
const importDecl = existingImport.get();
206+
const existingSpecifierNames = new Set(
207+
importDecl.node.specifiers?.map((s: any) => s.imported?.name).filter(Boolean) ?? [],
208+
);
209+
210+
for (const specifier of specifiers) {
211+
if (!existingSpecifierNames.has(specifier)) {
212+
importDecl.node.specifiers?.push(j.importSpecifier(j.identifier(specifier)));
213+
}
214+
}
215+
} else {
216+
// Create new import declaration
217+
const importSpecifiers = specifiers.map((s) => j.importSpecifier(j.identifier(s)));
218+
const importDecl = j.importDeclaration(
219+
importSpecifiers,
220+
j.literal('@expo/entity-database-adapter-knex'),
221+
);
222+
223+
// Add after the last import
224+
const allImports = root.find(j.ImportDeclaration);
225+
if (allImports.size() > 0) {
226+
allImports.at(-1).insertAfter(importDecl);
227+
} else {
228+
// No imports, add at the top
229+
root.get().node.program.body.unshift(importDecl);
230+
}
231+
}
159232
}
160233

161234
export default function transformer(file: FileInfo, api: API, _options: Options): string {
162235
const j = api.jscodeshift;
163236
const root = j.withParser('ts')(file.source);
164237

165-
transformLoaderToKnexLoader(j, root);
166-
transformLoaderWithAuthorizationResultsToKnexLoaderWithAuthorizationResults(j, root);
238+
const needsKnexLoader = transformLoaderToKnexLoader(j, root);
239+
const needsKnexLoaderWithAuthorizationResults =
240+
transformLoaderWithAuthorizationResultsToKnexLoaderWithAuthorizationResults(j, root);
241+
242+
addKnexImportIfNeeded(j, root, needsKnexLoader, needsKnexLoaderWithAuthorizationResults);
167243

168244
return root.toSource();
169245
}

packages/entity-database-adapter-knex-testing-utils/src/StubPostgresDatabaseAdapterProvider.ts

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,10 @@ import {
33
EntityDatabaseAdapter,
44
IEntityDatabaseAdapterProvider,
55
} from '@expo/entity';
6-
import {
7-
installEntityCompanionExtensions,
8-
installEntityTableDataCoordinatorExtensions,
9-
installReadonlyEntityExtensions,
10-
installViewerScopedEntityCompanionExtensions,
11-
} from '@expo/entity-database-adapter-knex';
126

137
import { StubPostgresDatabaseAdapter } from './StubPostgresDatabaseAdapter';
148

159
export class StubPostgresDatabaseAdapterProvider implements IEntityDatabaseAdapterProvider {
16-
getExtensionsKey(): string {
17-
return 'StubPostgresDatabaseAdapterProvider';
18-
}
19-
20-
installExtensions({
21-
EntityCompanionClass,
22-
EntityTableDataCoordinatorClass,
23-
ViewerScopedEntityCompanionClass,
24-
ReadonlyEntityClass,
25-
}: {
26-
EntityCompanionClass: typeof import('@expo/entity').EntityCompanion;
27-
EntityTableDataCoordinatorClass: typeof import('@expo/entity').EntityTableDataCoordinator;
28-
ViewerScopedEntityCompanionClass: typeof import('@expo/entity').ViewerScopedEntityCompanion;
29-
ReadonlyEntityClass: typeof import('@expo/entity').ReadonlyEntity;
30-
}): void {
31-
installEntityCompanionExtensions({ EntityCompanionClass });
32-
installEntityTableDataCoordinatorExtensions({ EntityTableDataCoordinatorClass });
33-
installViewerScopedEntityCompanionExtensions({ ViewerScopedEntityCompanionClass });
34-
installReadonlyEntityExtensions({ ReadonlyEntityClass });
35-
}
36-
3710
private readonly objectCollection = new Map();
3811

3912
getDatabaseAdapter<TFields extends Record<string, any>, TIDField extends keyof TFields>(

packages/entity-database-adapter-knex/src/KnexEntityLoader.ts

Lines changed: 0 additions & 81 deletions
This file was deleted.

packages/entity-database-adapter-knex/src/KnexEntityLoaderFactory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import {
22
EntityCompanion,
3+
EntityConstructionUtils,
34
EntityPrivacyPolicy,
45
EntityPrivacyPolicyEvaluationContext,
56
EntityQueryContext,
67
ReadonlyEntity,
78
ViewerContext,
89
IEntityMetricsAdapter,
910
} from '@expo/entity';
10-
import { EntityConstructionUtils } from '@expo/entity/src/EntityConstructionUtils';
1111

1212
import { AuthorizationResultBasedKnexEntityLoader } from './AuthorizationResultBasedKnexEntityLoader';
1313
import { EnforcingKnexEntityLoader } from './EnforcingKnexEntityLoader';

packages/entity-database-adapter-knex/src/PostgresEntityDatabaseAdapterProvider.ts

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@ import {
55
} from '@expo/entity';
66

77
import { PostgresEntityDatabaseAdapter } from './PostgresEntityDatabaseAdapter';
8-
import { installEntityCompanionExtensions } from './extensions/EntityCompanionExtensions';
9-
import { installEntityTableDataCoordinatorExtensions } from './extensions/EntityTableDataCoordinatorExtensions';
10-
import { installReadonlyEntityExtensions } from './extensions/ReadonlyEntityExtensions';
11-
import { installViewerScopedEntityCompanionExtensions } from './extensions/ViewerScopedEntityCompanionExtensions';
128

139
export interface PostgresEntityDatabaseAdapterConfiguration {
1410
/**
@@ -20,26 +16,6 @@ export interface PostgresEntityDatabaseAdapterConfiguration {
2016

2117
export class PostgresEntityDatabaseAdapterProvider implements IEntityDatabaseAdapterProvider {
2218
constructor(private readonly configuration: PostgresEntityDatabaseAdapterConfiguration = {}) {}
23-
getExtensionsKey(): string {
24-
return 'PostgresEntityDatabaseAdapterProvider';
25-
}
26-
27-
installExtensions({
28-
EntityCompanionClass,
29-
EntityTableDataCoordinatorClass,
30-
ViewerScopedEntityCompanionClass,
31-
ReadonlyEntityClass,
32-
}: {
33-
EntityCompanionClass: typeof import('@expo/entity').EntityCompanion;
34-
EntityTableDataCoordinatorClass: typeof import('@expo/entity').EntityTableDataCoordinator;
35-
ViewerScopedEntityCompanionClass: typeof import('@expo/entity').ViewerScopedEntityCompanion;
36-
ReadonlyEntityClass: typeof import('@expo/entity').ReadonlyEntity;
37-
}): void {
38-
installEntityCompanionExtensions({ EntityCompanionClass });
39-
installEntityTableDataCoordinatorExtensions({ EntityTableDataCoordinatorClass });
40-
installViewerScopedEntityCompanionExtensions({ ViewerScopedEntityCompanionClass });
41-
installReadonlyEntityExtensions({ ReadonlyEntityClass });
42-
}
4319

4420
getDatabaseAdapter<TFields extends Record<string, any>, TIDField extends keyof TFields>(
4521
entityConfiguration: EntityConfiguration<TFields, TIDField>,

0 commit comments

Comments
 (0)