Skip to content

Commit

Permalink
Adding Typescript support
Browse files Browse the repository at this point in the history
  • Loading branch information
Prashant Patel committed Jan 9, 2025
1 parent 00fbcdb commit 530e544
Show file tree
Hide file tree
Showing 15 changed files with 917 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
*.swp
*.swo
*.swn
*.cursorrules
121 changes: 121 additions & 0 deletions code-gen-projects/typescript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# TypeScript Code Generation Project

This directory contains a TypeScript project that demonstrates code generation using `ion-cli` with TypeScript as the target language.

## Project Structure

```
typescript/
├── src/
│ ├── models/ # Generated TypeScript interfaces
│ ├── serializers/ # Ion serialization code
│ └── validators/ # Schema validation code
├── tests/
│ ├── good/ # Valid test cases
│ └── bad/ # Invalid test cases
├── package.json
└── tsconfig.json
```

## Build Process

The TypeScript code generation is integrated into the build process using npm scripts. The build process:

1. Checks for `ion-cli` availability
2. Generates TypeScript code from schemas
3. Compiles TypeScript to JavaScript
4. Runs tests

### NPM Scripts

```json
{
"scripts": {
"generate": "ion-cli generate -l typescript -d ../../schema -o ./src/models",
"build": "tsc",
"test": "jest",
"clean": "rm -rf ./src/models/*"
}
}
```

### Environment Setup

1. Install ion-cli:
```bash
brew install ion-cli
# or
cargo install ion-cli
```

2. Set up environment:
```bash
export ION_CLI=/path/to/ion-cli # Optional, defaults to 'ion'
```

## Testing

The project includes comprehensive tests for the generated code:

### Unit Tests
- Type guard validation
- Serialization/deserialization
- Null value handling
- Type annotation preservation

### Integration Tests
- Roundtrip testing with good/bad inputs
- Schema validation
- Error handling

### Running Tests

```bash
npm test
```

## Type System

The generated TypeScript code follows these principles:

1. **Null Safety**
- Explicit null handling
- Optional type support
- Undefined vs null distinction

2. **Type Guards**
- Runtime type checking
- Custom validation rules
- Schema constraint validation

3. **Serialization**
- Binary format support
- Text format support
- Type annotation preservation

## Ion Type Mappings

| Ion Type | TypeScript Type |
|----------|----------------|
| null | null |
| bool | boolean |
| int | number/bigint |
| float | number |
| decimal | Decimal |
| timestamp| Date |
| string | string |
| symbol | Symbol |
| blob | Uint8Array |
| clob | string |
| struct | interface |
| list | Array |
| sexp | Array |

## Error Handling

The generated code includes comprehensive error handling:

- Schema validation errors
- Type conversion errors
- Serialization errors
- Runtime validation errors
76 changes: 76 additions & 0 deletions code-gen-projects/typescript/code-gen-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# TypeScript Code Generation Demo

This project demonstrates code generation using `ion-cli` with TypeScript as the target language. It uses the schema files from the parent directory and tests the generated code against both good and bad input files.

## Project Structure

```
code-gen-demo/
├── src/
│ └── generated/ # Generated TypeScript code from schemas
├── tests/
│ └── roundtrip.test.ts # Roundtrip tests for generated code
├── package.json
└── tsconfig.json
```

## Prerequisites

1. Install ion-cli:
```bash
brew install ion-cli
# or
cargo install ion-cli
```

2. Set up environment:
```bash
export ION_CLI=/path/to/ion-cli # Optional, defaults to 'ion'
export ION_INPUT=/path/to/input # Required for tests
```

## Build Process

The build process is integrated with npm scripts:

1. `npm run generate` - Generates TypeScript code from schemas
2. `npm run build` - Compiles TypeScript to JavaScript
3. `npm test` - Runs the test suite

## Running Tests

The tests verify that the generated code can:
- Read Ion data into TypeScript objects
- Write TypeScript objects back to Ion format
- Handle both valid and invalid input correctly

To run the tests:

```bash
# From the code-gen-demo directory
ION_INPUT=../../input npm test
```

## Test Cases

1. Good Input Tests:
- Struct with fields
- Sequences
- Enum types
- Nested structures
- Type annotations

2. Bad Input Tests:
- Invalid struct fields
- Invalid sequence elements
- Invalid enum values
- Type mismatches

## Generated Code Features

The generated TypeScript code includes:
- Type-safe interfaces
- Runtime type guards
- Ion serialization/deserialization
- Null safety
- Type annotations support
37 changes: 37 additions & 0 deletions code-gen-projects/typescript/code-gen-demo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "ion-cli-typescript-demo",
"version": "1.0.0",
"description": "TypeScript code generation demo for ion-cli",
"scripts": {
"pregenerate": "rimraf src/generated/*",
"generate": "ion-cli generate -l typescript -d ../../schema -o ./src/generated",
"prebuild": "npm run generate",
"build": "tsc",
"pretest": "npm run build",
"test": "jest",
"clean": "rimraf dist src/generated/*"
},
"dependencies": {
"ion-js": "^4.3.0",
"decimal.js": "^10.4.3"
},
"devDependencies": {
"@types/jest": "^29.5.0",
"@types/node": "^18.15.11",
"jest": "^29.5.0",
"rimraf": "^5.0.0",
"ts-jest": "^29.1.0",
"typescript": "^5.0.3"
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"roots": [
"<rootDir>/src",
"<rootDir>/tests"
],
"moduleNameMapper": {
"@generated/(.*)": "<rootDir>/src/generated/$1"
}
}
}
103 changes: 103 additions & 0 deletions code-gen-projects/typescript/code-gen-demo/tests/roundtrip.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { readFileSync } from 'fs';
import { makeReader, makeWriter } from 'ion-js';
import path from 'path';

// Import all generated types (these will be available after code generation)
import * as generated from '@generated/index';

describe('Ion TypeScript Code Generation Tests', () => {
const ION_INPUT = process.env.ION_INPUT || '../../input';

const readIonFile = (filePath: string) => {
const fullPath = path.join(ION_INPUT, filePath);
return readFileSync(fullPath);
};

describe('Good Input Tests', () => {
test('struct_with_fields roundtrip', () => {
const data = readIonFile('good/struct_with_fields.ion');
const reader = makeReader(data);

// Read from Ion
const value = generated.StructWithFields.fromIon(reader);
expect(value).toBeDefined();

// Write back to Ion
const writer = makeWriter();
value.toIon(writer);
const serialized = writer.getBytes();

// Read again and compare
const newReader = makeReader(serialized);
const newValue = generated.StructWithFields.fromIon(newReader);
expect(newValue).toEqual(value);
});

test('sequence roundtrip', () => {
const data = readIonFile('good/sequence.ion');
const reader = makeReader(data);

// Read from Ion
const value = generated.Sequence.fromIon(reader);
expect(value).toBeDefined();

// Write back to Ion
const writer = makeWriter();
value.toIon(writer);
const serialized = writer.getBytes();

// Read again and compare
const newReader = makeReader(serialized);
const newValue = generated.Sequence.fromIon(newReader);
expect(newValue).toEqual(value);
});

test('enum_type roundtrip', () => {
const data = readIonFile('good/enum_type.ion');
const reader = makeReader(data);

// Read from Ion
const value = generated.EnumType.fromIon(reader);
expect(value).toBeDefined();

// Write back to Ion
const writer = makeWriter();
value.toIon(writer);
const serialized = writer.getBytes();

// Read again and compare
const newReader = makeReader(serialized);
const newValue = generated.EnumType.fromIon(newReader);
expect(newValue).toEqual(value);
});
});

describe('Bad Input Tests', () => {
test('invalid struct_with_fields', () => {
const data = readIonFile('bad/struct_with_fields.ion');
const reader = makeReader(data);

expect(() => {
generated.StructWithFields.fromIon(reader);
}).toThrow();
});

test('invalid sequence', () => {
const data = readIonFile('bad/sequence.ion');
const reader = makeReader(data);

expect(() => {
generated.Sequence.fromIon(reader);
}).toThrow();
});

test('invalid enum_type', () => {
const data = readIonFile('bad/enum_type.ion');
const reader = makeReader(data);

expect(() => {
generated.EnumType.fromIon(reader);
}).toThrow();
});
});
});
38 changes: 38 additions & 0 deletions code-gen-projects/typescript/code-gen-demo/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"declaration": true,
"outDir": "./dist",
"rootDir": "./",
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"baseUrl": "./",
"paths": {
"@generated/*": ["src/generated/*"]
},
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": [
"src/**/*",
"tests/**/*"
],
"exclude": [
"node_modules",
"dist"
]
}
Loading

0 comments on commit 530e544

Please sign in to comment.