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
15 changes: 13 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
- **Complex Types**: Union and intersection types, nested object structures, template literal types
- **Utility Types**: Built-in support for Pick, Omit, Partial, Required, Record, Readonly, and other TypeScript utility types
- **Advanced Features**: Conditional types, mapped types, keyof operators, indexed access types
- **JavaScript Built-in Types**: Native support for Date type using TypeBox's JavaScript type system
- **Import Resolution**: Cross-file type dependencies with qualified naming and circular dependency handling

## Core Components
Expand Down Expand Up @@ -133,8 +134,9 @@ The handler system in <mcfile name="handlers/typebox" path="src/handlers/typebox
6. **Simple Handlers**: <mcfile name="simple-type-handler.ts" path="src/handlers/typebox/simple-type-handler.ts"></mcfile>, <mcfile name="literal-type-handler.ts" path="src/handlers/typebox/literal-type-handler.ts"></mcfile>
7. **Advanced Handlers**: <mcfile name="template-literal-type-handler.ts" path="src/handlers/typebox/template-literal-type-handler.ts"></mcfile>, <mcfile name="type-operator-handler.ts" path="src/handlers/typebox/type-operator-handler.ts"></mcfile>, <mcfile name="keyof-type-handler.ts" path="src/handlers/typebox/keyof-type-handler.ts"></mcfile>
8. **Function Handlers**: <mcfile name="function-type-handler.ts" path="src/handlers/typebox/function-type-handler.ts"></mcfile>
9. **Type Query Handlers**: <mcfile name="type-query-handler.ts" path="src/handlers/typebox/type-query-handler.ts"></mcfile>, <mcfile name="typeof-type-handler.ts" path="src/handlers/typebox/typeof-type-handler.ts"></mcfile>
10. **Access Handlers**: <mcfile name="indexed-access-type-handler.ts" path="src/handlers/typebox/indexed-access-type-handler.ts"></mcfile>, <mcfile name="type-reference-handler.ts" path="src/handlers/typebox/type-reference-handler.ts"></mcfile>
9. **JavaScript Type Handlers**: <mcfile name="date-type-handler.ts" path="src/handlers/typebox/date-type-handler.ts"></mcfile> - Handles JavaScript built-in types like Date using TypeBox's extended type system
10. **Type Query Handlers**: <mcfile name="type-query-handler.ts" path="src/handlers/typebox/type-query-handler.ts"></mcfile>, <mcfile name="typeof-type-handler.ts" path="src/handlers/typebox/typeof-type-handler.ts"></mcfile>
11. **Access Handlers**: <mcfile name="indexed-access-type-handler.ts" path="src/handlers/typebox/indexed-access-type-handler.ts"></mcfile>, <mcfile name="type-reference-handler.ts" path="src/handlers/typebox/type-reference-handler.ts"></mcfile>

#### Readonly Type Handling

Expand All @@ -157,11 +159,20 @@ This dual approach ensures proper handling of both TypeScript readonly construct
- `type ReadonlyArray = readonly string[]` (array modifier)
- `type ReadonlyTuple = readonly [string, number]` (tuple modifier)

#### JavaScript Built-in Type Support

The system provides comprehensive support for JavaScript built-in types through specialized handlers:

- **Date Type Handler**: The <mcfile name="date-type-handler.ts" path="src/handlers/typebox/date-type-handler.ts"></mcfile> handles TypeScript's `Date` type references and converts them to TypeBox's `Type.Date()` schema
- **Type Reference Registration**: JavaScript built-in types are registered in the `typeReferenceHandlers` map for O(1) lookup performance
- **Extended Type System**: Leverages TypeBox's JavaScript type system for types that extend beyond standard JSON Schema

#### Handler Management

The <mcfile name="typebox-type-handlers.ts" path="src/handlers/typebox/typebox-type-handlers.ts"></mcfile> class orchestrates all handlers through:

- **Handler Caching**: Caches handler instances for performance optimization
- **Type Reference Mapping**: O(1) lookup for built-in types like Date, utility types like Partial, and other type references
- **Fallback System**: Provides fallback handlers for complex cases including readonly array modifiers

### Import Resolution
Expand Down
16 changes: 16 additions & 0 deletions src/handlers/typebox/date-type-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { BaseTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/base-type-handler'
import { makeTypeCall } from '@daxserver/validation-schema-codegen/utils/typebox-codegen-utils'
import { Node, ts, TypeReferenceNode } from 'ts-morph'

export class DateTypeHandler extends BaseTypeHandler {
canHandle(node: TypeReferenceNode): boolean {
const typeName = node.getTypeName()

return Node.isIdentifier(typeName) && typeName.getText() === 'Date'
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
handle(_node: TypeReferenceNode): ts.Expression {
return makeTypeCall('Date')
}
}
3 changes: 3 additions & 0 deletions src/handlers/typebox/typebox-type-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ArrayTypeHandler } from '@daxserver/validation-schema-codegen/handlers/
import { IntersectionTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/collection/intersection-type-handler'
import { TupleTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/collection/tuple-type-handler'
import { UnionTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/collection/union-type-handler'
import { DateTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/date-type-handler'
import { FunctionTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/function-type-handler'
import { IndexedAccessTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/indexed-access-type-handler'
import { KeyOfTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/keyof-type-handler'
Expand Down Expand Up @@ -52,6 +53,7 @@ export class TypeBoxTypeHandlers {
const typeofTypeHandler = new TypeofTypeHandler()
const readonlyTypeHandler = new ReadonlyTypeHandler()
const readonlyArrayTypeHandler = new ReadonlyArrayTypeHandler()
const dateTypeHandler = new DateTypeHandler()

// O(1) lookup by SyntaxKind
this.syntaxKindHandlers.set(SyntaxKind.AnyKeyword, simpleTypeHandler)
Expand Down Expand Up @@ -83,6 +85,7 @@ export class TypeBoxTypeHandlers {
this.typeReferenceHandlers.set('Omit', omitTypeHandler)
this.typeReferenceHandlers.set('Required', requiredTypeHandler)
this.typeReferenceHandlers.set('Readonly', readonlyTypeHandler)
this.typeReferenceHandlers.set('Date', dateTypeHandler)

// Fallback handlers for complex cases
this.fallbackHandlers = [
Expand Down
113 changes: 113 additions & 0 deletions tests/handlers/typebox/date-types.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { createSourceFile, formatWithPrettier, generateFormattedCode } from '@test-fixtures/utils'
import { beforeEach, describe, expect, test } from 'bun:test'
import { Project } from 'ts-morph'

describe('Date types', () => {
let project: Project

beforeEach(() => {
project = new Project()
})

test('without export', () => {
const sourceFile = createSourceFile(project, `type A = Date`)

expect(generateFormattedCode(sourceFile)).toBe(
formatWithPrettier(`
export const A = Type.Date();

export type A = Static<typeof A>;
`),
)
})

test('with export', () => {
const sourceFile = createSourceFile(project, `export type A = Date`)

expect(generateFormattedCode(sourceFile)).toBe(
formatWithPrettier(`
export const A = Type.Date();

export type A = Static<typeof A>;
`),
)
})

test('simple Date type alias', () => {
const sourceFile = createSourceFile(project, `type Timestamp = Date`)

expect(generateFormattedCode(sourceFile)).toBe(
formatWithPrettier(`
export const Timestamp = Type.Date();

export type Timestamp = Static<typeof Timestamp>;
`),
)
})

test('Date in object property', () => {
const sourceFile = createSourceFile(
project,
`
interface User {
name: string;
createdAt: Date;
}
`,
)

expect(generateFormattedCode(sourceFile)).toBe(
formatWithPrettier(`
export const User = Type.Object({
name: Type.String(),
createdAt: Type.Date(),
});

export type User = Static<typeof User>;
`),
)
})

test('Date in union type', () => {
const sourceFile = createSourceFile(project, `type Value = string | Date | number`)

expect(generateFormattedCode(sourceFile)).toBe(
formatWithPrettier(`
export const Value = Type.Union([Type.String(), Type.Date(), Type.Number()]);

export type Value = Static<typeof Value>;
`),
)
})

test('Date in array', () => {
const sourceFile = createSourceFile(project, `type Dates = Date[]`)

expect(generateFormattedCode(sourceFile)).toBe(
formatWithPrettier(`
export const Dates = Type.Array(Type.Date());

export type Dates = Static<typeof Dates>;
`),
)
})

test('Date in function parameter and return type', () => {
const sourceFile = createSourceFile(
project,
`
function formatDate(date: Date): string {
return date.toString();
}
`,
)

expect(generateFormattedCode(sourceFile)).toBe(
formatWithPrettier(`
export const formatDate = Type.Function([Type.Date()], Type.String());

export type formatDate = Static<typeof formatDate>;
`),
)
})
})