Skip to content

codemods: model-to-schema typescript support#10474

Open
BobrImperator wants to merge 3 commits intowarp-drive-data:mainfrom
BobrImperator:codemod/typescript-support
Open

codemods: model-to-schema typescript support#10474
BobrImperator wants to merge 3 commits intowarp-drive-data:mainfrom
BobrImperator:codemod/typescript-support

Conversation

@BobrImperator
Copy link
Contributor

@BobrImperator BobrImperator commented Feb 17, 2026

  • Remove remnants of previous Signature type generation
  • Fix and normalize type imports
    Previously the imports were borked and misaligned - which is an on-going issue post Claude catastrophe.
    There's some work being done to unify transformations under a single source of truth representation of a file (Entity) Codemod/poc typescript BobrImperator/data#1
  • Normalizes schema names for Traits and Extensions especially.
  • Normalizes interface names to always include Trait or Extension
    • Names without suffixes are reserved for Resource Brands
  • Adds a solution for constructing resource Brands using Typescript interface merging.
    This does rely on the fact that some imports are effectively circular, but Typescript appears to be fine with that.

Note

A foldable section below 👇
The examples don't contain any actual imports since it's meant to be simply ran with tsx or IDE to verify typechecking.

PoC showcasing consuming code and file interaction in Typescript context
  • Consuming User

    import type { User } from './user.schema';
    
    function greetUser(user: User): string {
      // UserTrait fields
      const name: string = user.name;
      const email: string = user.email;
      const company: { name: string } = user.company;
      const projects: { title: string }[] = user.projects;
    
      // TimestampableExtension fields (via TimestampableTrait)
      const createdAt: Date | null = user.createdAt;
      const updatedAt: Date | null = user.updatedAt;
    
      // TimestampableExtension behavior
      const elapsed: number = user.timeSince();
    
      // UserExtension behavior
      const display: string = user.displayName;
      user.updateProfile({ name: 'new name' });
    
      return `Hello ${display}, member since ${createdAt}, ${elapsed}ms ago`;
    }
  • resources/user.schema.ts

    import type { TimestampableExtension } from './timestampable.ext';
    import type { UserExtension } from './user.ext';
    
    declare const Type: unique symbol;
    
    interface Company {
      name: string;
    }
    
    interface Project {
      title: string;
    }
    
    const UserSchema = {
      type: 'user',
      legacy: true,
      identity: { kind: '@id', name: 'id' },
      fields: [
        { kind: 'attribute', name: 'name', type: 'string' },
        { kind: 'attribute', name: 'email', type: 'string' },
        { kind: 'belongsTo', name: 'company', type: 'company' },
        { kind: 'hasMany', name: 'projects', type: 'project' },
      ],
      traits: ['timestampable'],
      objectExtensions: ['user', 'timestampable'],
    } as const;
    
    export default UserSchema;
    
    export interface UserTrait {
      readonly [Type]: 'user';
      name: string;
      email: string;
      company: Company;
      projects: Project[];
    }
    
    export interface User extends UserTrait, TimestampableExtension, UserExtension {}
  • resources/user.ext.ts

    import type { UserTrait } from './user.schema';
    
    export interface UserExtension extends UserTrait {}
    
    export class UserExtension {
      get displayName(): string {
        return this.name || this.email;
      }
    
      updateProfile(data: Record<string, unknown>): void {
        console.log('updating', data);
      }
    }
  • traits/timestampable.schema.ts

    const TimestampableSchema = {
      name: 'timestampable',
      mode: 'legacy',
      fields: [
        { kind: 'attribute', name: 'createdAt', type: 'date' },
        { kind: 'attribute', name: 'updatedAt', type: 'date' },
      ],
    } as const;
    
    export default TimestampableSchema;
    
    export interface TimestampableTrait {
      createdAt: Date | null;
      updatedAt: Date | null;
    }
  • traits/timestampable.ext.ts

    import type { TimestampableTrait } from './timestampable.schema';
    
    export interface TimestampableExtension extends TimestampableTrait {}
    
    export class TimestampableExtension {
      timeSince(): number {
        return Date.now() - (this.updatedAt?.getTime() ?? 0);
      }
    }

If this PR updates API docs, preview them by:

  • install bun (if needed)
  • install volta and configure it for pnpm (if needed)
  • run pnpm install in the root (if needed)
  • run pnpm preview in the root

  • Read the full contributing documentation
  • If you do not have permission to add labels or run the test-suite in CI, a team member will do this for you.

@github-project-automation github-project-automation bot moved this to needs triage in EmberData Feb 17, 2026
@BobrImperator BobrImperator force-pushed the codemod/typescript-support branch from 7d61f8b to 83d7f90 Compare February 17, 2026 18:28
collectTraitImports(extendedTraits, imports, options);

const traitSchemaName = traitInterfaceName;
const traitSchemaName = `${toPascalCase(baseName)}Schema`;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A recurring problem so far is shotgun surgeries like this which are also very error prone that Claude was very happy with. Additionally the codemod often lacks the ability to quickly refer from a file to it's dependencies for the exact same reason.

There's on-going effort to wrangle this up https://github.com/BobrImperator/data/compare/codemod/typescript-support...BobrImperator:data:codemod/poc-typescript?expand=1

@BobrImperator BobrImperator changed the title codemods: model-to-scheam typescript support codemods: model-to-schema typescript support Feb 17, 2026
@BobrImperator BobrImperator force-pushed the codemod/typescript-support branch 6 times, most recently from 255e935 to e71b50e Compare February 19, 2026 12:36
@BobrImperator BobrImperator force-pushed the codemod/typescript-support branch from e71b50e to 6d62aed Compare February 19, 2026 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments