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,10 +2,12 @@ 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 = PostEntity.knexLoader(viewerContext);
const postLoader = knexLoader(PostEntity, viewerContext);

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

// Loader with authorization results - only transformed when using knex methods
const userLoaderWithAuth = UserEntity.knexLoaderWithAuthorizationResults(viewerContext);
const userLoaderWithAuth = knexLoaderWithAuthorizationResults(UserEntity, 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,10 +1,12 @@
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 CommentEntity.knexLoader(viewerContext)
const comments = await knexLoader(CommentEntity, viewerContext)
.loadManyByFieldEqualityConjunctionAsync([
{ fieldName: 'postId', fieldValue: '123' }
]);
Expand All @@ -15,8 +17,7 @@ const loadComments = async (viewerContext: ViewerContext) => {
.loadByIDAsync('456');

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

// Edge cases - these should NOT be transformed
Expand Down
100 changes: 88 additions & 12 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,7 +79,9 @@ function isKnexSpecificMethodUsed(j: API['jscodeshift'], node: any): boolean {
return false;
}

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

// Find all entity expressions of the form `Entity.loader(viewerContext)`
root
.find(j.CallExpression, {
Expand All @@ -105,20 +107,28 @@ function transformLoaderToKnexLoader(j: API['jscodeshift'], root: Collection<any
if (firstChar === firstChar?.toUpperCase()) {
// Check if this loader uses knex-specific methods
if (isKnexSpecificMethodUsed(j, path)) {
// Rename loader to knexLoader
if (loaderCallee.property.type === 'Identifier') {
loaderCallee.property.name = 'knexLoader';
}
// 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;
}
}
}
});

return transformed;
}

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

// Find all entity expressions of the form `Entity.loaderWithAuthorizationResults(viewerContext)`
root
.find(j.CallExpression, {
Expand All @@ -144,22 +154,88 @@ function transformLoaderWithAuthorizationResultsToKnexLoaderWithAuthorizationRes
if (firstChar === firstChar?.toUpperCase()) {
// Check if this loader uses knex-specific methods
if (isKnexSpecificMethodUsed(j, path)) {
// Rename loaderWithAuthorizationResults to knexLoaderWithAuthorizationResults
if (loaderCallee.property.type === 'Identifier') {
loaderCallee.property.name = 'knexLoaderWithAuthorizationResults';
}
// 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;
}
}
}
});

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);

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

addKnexImportIfNeeded(j, root, needsKnexLoader, needsKnexLoaderWithAuthorizationResults);

return root.toSource();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,10 @@ import {
EntityDatabaseAdapter,
IEntityDatabaseAdapterProvider,
} from '@expo/entity';
import {
installEntityCompanionExtensions,
installEntityTableDataCoordinatorExtensions,
installReadonlyEntityExtensions,
installViewerScopedEntityCompanionExtensions,
} from '@expo/entity-database-adapter-knex';

import { StubPostgresDatabaseAdapter } from './StubPostgresDatabaseAdapter';

export class StubPostgresDatabaseAdapterProvider implements IEntityDatabaseAdapterProvider {
getExtensionsKey(): string {
return 'StubPostgresDatabaseAdapterProvider';
}

installExtensions({
EntityCompanionClass,
EntityTableDataCoordinatorClass,
ViewerScopedEntityCompanionClass,
ReadonlyEntityClass,
}: {
EntityCompanionClass: typeof import('@expo/entity').EntityCompanion;
EntityTableDataCoordinatorClass: typeof import('@expo/entity').EntityTableDataCoordinator;
ViewerScopedEntityCompanionClass: typeof import('@expo/entity').ViewerScopedEntityCompanion;
ReadonlyEntityClass: typeof import('@expo/entity').ReadonlyEntity;
}): void {
installEntityCompanionExtensions({ EntityCompanionClass });
installEntityTableDataCoordinatorExtensions({ EntityTableDataCoordinatorClass });
installViewerScopedEntityCompanionExtensions({ ViewerScopedEntityCompanionClass });
installReadonlyEntityExtensions({ ReadonlyEntityClass });
}

private readonly objectCollection = new Map();

getDatabaseAdapter<TFields extends Record<string, any>, TIDField extends keyof TFields>(
Expand Down
81 changes: 0 additions & 81 deletions packages/entity-database-adapter-knex/src/KnexEntityLoader.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {
EntityCompanion,
EntityConstructionUtils,
EntityPrivacyPolicy,
EntityPrivacyPolicyEvaluationContext,
EntityQueryContext,
ReadonlyEntity,
ViewerContext,
IEntityMetricsAdapter,
} from '@expo/entity';
import { EntityConstructionUtils } from '@expo/entity/src/EntityConstructionUtils';

import { AuthorizationResultBasedKnexEntityLoader } from './AuthorizationResultBasedKnexEntityLoader';
import { EnforcingKnexEntityLoader } from './EnforcingKnexEntityLoader';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ import {
} from '@expo/entity';

import { PostgresEntityDatabaseAdapter } from './PostgresEntityDatabaseAdapter';
import { installEntityCompanionExtensions } from './extensions/EntityCompanionExtensions';
import { installEntityTableDataCoordinatorExtensions } from './extensions/EntityTableDataCoordinatorExtensions';
import { installReadonlyEntityExtensions } from './extensions/ReadonlyEntityExtensions';
import { installViewerScopedEntityCompanionExtensions } from './extensions/ViewerScopedEntityCompanionExtensions';

export interface PostgresEntityDatabaseAdapterConfiguration {
/**
Expand All @@ -20,26 +16,6 @@ export interface PostgresEntityDatabaseAdapterConfiguration {

export class PostgresEntityDatabaseAdapterProvider implements IEntityDatabaseAdapterProvider {
constructor(private readonly configuration: PostgresEntityDatabaseAdapterConfiguration = {}) {}
getExtensionsKey(): string {
return 'PostgresEntityDatabaseAdapterProvider';
}

installExtensions({
EntityCompanionClass,
EntityTableDataCoordinatorClass,
ViewerScopedEntityCompanionClass,
ReadonlyEntityClass,
}: {
EntityCompanionClass: typeof import('@expo/entity').EntityCompanion;
EntityTableDataCoordinatorClass: typeof import('@expo/entity').EntityTableDataCoordinator;
ViewerScopedEntityCompanionClass: typeof import('@expo/entity').ViewerScopedEntityCompanion;
ReadonlyEntityClass: typeof import('@expo/entity').ReadonlyEntity;
}): void {
installEntityCompanionExtensions({ EntityCompanionClass });
installEntityTableDataCoordinatorExtensions({ EntityTableDataCoordinatorClass });
installViewerScopedEntityCompanionExtensions({ ViewerScopedEntityCompanionClass });
installReadonlyEntityExtensions({ ReadonlyEntityClass });
}

getDatabaseAdapter<TFields extends Record<string, any>, TIDField extends keyof TFields>(
entityConfiguration: EntityConfiguration<TFields, TIDField>,
Expand Down
Loading