Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import { ViewerContext } from '@expo/entity';
import { UserEntity } from './entities/UserEntity';
import { PostEntity } from './entities/PostEntity';

import { knexLoader, knexLoaderWithAuthorizationResults } from "@expo/entity-database-adapter-knex";

async function loadUser(viewerContext: ViewerContext) {
// Basic loader calls - only transformed when using knex-specific methods
const userLoader = UserEntity.loader(viewerContext);
const postLoader = knexLoader(PostEntity, viewerContext);
const postLoader = PostEntity.knexLoader(viewerContext);

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

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

// Loader that doesn't use knex methods - should NOT be transformed
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { ViewerContext } from '@expo/entity';
import { CommentEntity } from './entities/CommentEntity';

import { knexLoader, knexLoaderWithAuthorizationResults } from "@expo/entity-database-adapter-knex";

// Chained calls
const loadComments = async (viewerContext: ViewerContext) => {
// Direct chaining with knex-specific method
const comments = await knexLoader(CommentEntity, viewerContext)
const comments = await CommentEntity.knexLoader(viewerContext)
.loadManyByFieldEqualityConjunctionAsync([
{ fieldName: 'postId', fieldValue: '123' }
]);
Expand All @@ -17,7 +15,8 @@ const loadComments = async (viewerContext: ViewerContext) => {
.loadByIDAsync('456');

// With authorization results and knex method
const commentsWithAuth = await knexLoaderWithAuthorizationResults(CommentEntity, viewerContext)
const commentsWithAuth = await CommentEntity
.knexLoaderWithAuthorizationResults(viewerContext)
.loadManyByRawWhereClauseAsync('postId = ?', ['456']);

// Edge cases - these should NOT be transformed
Expand Down
100 changes: 12 additions & 88 deletions packages/entity-codemod/src/transforms/v0.55.0-v0.56.0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,7 @@ function isKnexSpecificMethodUsed(j: API['jscodeshift'], node: any): boolean {
return false;
}

function transformLoaderToKnexLoader(j: API['jscodeshift'], root: Collection<any>): boolean {
let transformed = false;

function transformLoaderToKnexLoader(j: API['jscodeshift'], root: Collection<any>): void {
// Find all entity expressions of the form `Entity.loader(viewerContext)`
root
.find(j.CallExpression, {
Expand All @@ -107,28 +105,20 @@ function transformLoaderToKnexLoader(j: API['jscodeshift'], root: Collection<any
if (firstChar === firstChar?.toUpperCase()) {
// Check if this loader uses knex-specific methods
if (isKnexSpecificMethodUsed(j, path)) {
// Transform Entity.loader(viewerContext) → knexLoader(Entity, viewerContext)
const entityIdentifier = loaderCallee.object;
const args = loaderCallExpression.arguments;

j(path).replaceWith(
j.callExpression(j.identifier('knexLoader'), [entityIdentifier, ...args]),
);
transformed = true;
// Rename loader to knexLoader
if (loaderCallee.property.type === 'Identifier') {
loaderCallee.property.name = 'knexLoader';
}
}
}
}
});

return transformed;
}

function transformLoaderWithAuthorizationResultsToKnexLoaderWithAuthorizationResults(
j: API['jscodeshift'],
root: Collection<any>,
): boolean {
let transformed = false;

): void {
// Find all entity expressions of the form `Entity.loaderWithAuthorizationResults(viewerContext)`
root
.find(j.CallExpression, {
Expand All @@ -154,88 +144,22 @@ function transformLoaderWithAuthorizationResultsToKnexLoaderWithAuthorizationRes
if (firstChar === firstChar?.toUpperCase()) {
// Check if this loader uses knex-specific methods
if (isKnexSpecificMethodUsed(j, path)) {
// Transform Entity.loaderWithAuthorizationResults(viewerContext) → knexLoaderWithAuthorizationResults(Entity, viewerContext)
const entityIdentifier = loaderCallee.object;
const args = loaderCallExpression.arguments;

j(path).replaceWith(
j.callExpression(j.identifier('knexLoaderWithAuthorizationResults'), [
entityIdentifier,
...args,
]),
);
transformed = true;
// Rename loaderWithAuthorizationResults to knexLoaderWithAuthorizationResults
if (loaderCallee.property.type === 'Identifier') {
loaderCallee.property.name = 'knexLoaderWithAuthorizationResults';
}
}
}
}
});

return transformed;
}

function addKnexImportIfNeeded(
j: API['jscodeshift'],
root: Collection<any>,
needsKnexLoader: boolean,
needsKnexLoaderWithAuthorizationResults: boolean,
): void {
if (!needsKnexLoader && !needsKnexLoaderWithAuthorizationResults) {
return;
}

const specifiers: string[] = [];
if (needsKnexLoader) {
specifiers.push('knexLoader');
}
if (needsKnexLoaderWithAuthorizationResults) {
specifiers.push('knexLoaderWithAuthorizationResults');
}

// Check if the import already exists
const existingImport = root.find(j.ImportDeclaration, {
source: { value: '@expo/entity-database-adapter-knex' },
});

if (existingImport.size() > 0) {
// Add specifiers to existing import
const importDecl = existingImport.get();
const existingSpecifierNames = new Set(
importDecl.node.specifiers?.map((s: any) => s.imported?.name).filter(Boolean) ?? [],
);

for (const specifier of specifiers) {
if (!existingSpecifierNames.has(specifier)) {
importDecl.node.specifiers?.push(j.importSpecifier(j.identifier(specifier)));
}
}
} else {
// Create new import declaration
const importSpecifiers = specifiers.map((s) => j.importSpecifier(j.identifier(s)));
const importDecl = j.importDeclaration(
importSpecifiers,
j.literal('@expo/entity-database-adapter-knex'),
);

// Add after the last import
const allImports = root.find(j.ImportDeclaration);
if (allImports.size() > 0) {
allImports.at(-1).insertAfter(importDecl);
} else {
// No imports, add at the top
root.get().node.program.body.unshift(importDecl);
}
}
}

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

const needsKnexLoader = transformLoaderToKnexLoader(j, root);
const needsKnexLoaderWithAuthorizationResults =
transformLoaderWithAuthorizationResultsToKnexLoaderWithAuthorizationResults(j, root);

addKnexImportIfNeeded(j, root, needsKnexLoader, needsKnexLoaderWithAuthorizationResults);
transformLoaderToKnexLoader(j, root);
transformLoaderWithAuthorizationResultsToKnexLoaderWithAuthorizationResults(j, root);

return root.toSource();
}