-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
help wantedExtra attention is neededExtra attention is needed
Description
Current structure is not tree-shakeable and has a lot of duplicate code.
Proposed solutions:
1. Tree-shaking:
### 1\. The Architecture Problem
#### Current: The Monolith (Not Tree-Shakeable)
You likely generate a class that looks like this:
```typescript
// dist/index.mjs
import { UserSchema, GameSchema, ... } from './schemas'; // Imports ALL 180KB of Schemas
export class LichessClient {
constructor(token) { ... }
// These are all bundled together. You can't separate them.
getUser() { ... } // Uses UserSchema
getGame() { ... } // Uses GameSchema
getTournament() { ... } // Uses TournamentSchema
// ... + 100 other methods
}
```
**Result:** The user imports `LichessClient`, and the bundler sees that `LichessClient` depends on *every single schema*. The whole 300KB+ blob gets included.
#### Target: Functional Composition (Tree-Shakeable)
This is the pattern used by **Firebase v9+**, **Supabase**, and **Octokit**. Instead of a class with methods, you have a lightweight "client" (that just holds state) and standalone functions.
```typescript
// dist/client.mjs
// 1. The Client is just state (URL, token). 0KB logic.
export const createClient = (token) => ({ token, baseUrl: '...' });
// dist/users.mjs
// 2. Functions are standalone.
// This file ONLY imports UserSchema. It does not know about Games or Tournaments.
import { UserSchema } from './schemas/users';
export const getUser = async (client, id) => {
const res = await fetch(`${client.baseUrl}/user/${id}`, ...);
return UserSchema.parse(res);
}
```
**Result:** If the user only imports `getUser`, the bundler **only** includes the code for `getUser` and `UserSchema`. The `GameSchema` and `TournamentSchema` are never touched and are dropped from the bundle.
-----
### 2\. How to Refactor Your Generator
Since you are using a script (`gen:client`) to build this, you don't need to rewrite code manually. You need to change the template your generator uses.
#### Phase 1: Update `scripts/yaml-to-client`
Instead of generating one big class string, generate many small exports.
**Change this output structure:**
```typescript
export class LichessClient {
async getUser(id: string) { ... }
async getGame(id: string) { ... }
}
```
**To this output structure:**
```typescript
// src/client.ts (The minimal core)
export interface LichessContext {
token?: string;
baseUrl?: string;
fetch?: typeof fetch;
}
// src/endpoints/users.ts (Generated separately or as named exports)
import type { LichessContext } from '../client';
import { UserSchema } from '../schemas'; // Ideally import only specific schema
export const getUser = (ctx: LichessContext, id: string) => {
// implementation
}
```
### 3\. Addressing the "Types are 300KB" Issue
You mentioned your client types are 300KB. This suggests **Type Inlining**.
If your generated code looks like this:
```typescript
// generated
export function getUser(id: string): { id: string, username: string, title?: string ... } { ... }
```
TypeScript is repeating the entire User object structure in the `.d.ts` file for every function that uses it.
**The Fix:**
Ensure your generator outputs code that references the *Interface* by name, not value.
```typescript
import type { User } from '../schemas'; // Import the type definition
// generated
export function getUser(id: string): User { ... }
```
This makes your `.d.ts` file tiny because it just points to the definition in `schemas.d.ts` rather than rewriting it.
### 4\. A "Hybrid" Approach (for DX)
If you hate the functional syntax (`getUser(client, 'id')`) and prefer the dot notation (`client.users.get('id')`), you can still achieve tree-shaking by using **namespaced exports** or **sub-path exports**, but it is harder.
The functional approach (Option 2 above) is the gold standard for package size.
**Example usage of the new Functional approach:**
```typescript
import { createClient } from '@lichess/api';
import { getUser } from '@lichess/api/users'; // Subpath import
const client = createClient({ token: '...' });
// Only bundles code for getUser and the User Schema
const user = await getUser(client, 'gamerman');
```
### Summary of Next Steps
1. **Modify `yaml-to-client`**: Stop generating a `class`. Start generating standalone functions that accept a `client` object as the first argument.
2. **Check Imports**: Ensure each generated function file only imports the *specific* Zod schema it needs, not a barrel file (`index.ts`) that exports *all* schemas.
3. **Fix Type References**: Ensure the return types reference the named interfaces from `@lichess/api/schemas` to reduce `.d.ts` bloat.Generated by Google Gemini
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
help wantedExtra attention is neededExtra attention is needed