Skip to content
Open
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
*.swp
*.swo
*.swn
*.cursorrules
*/node_modules/*
code-gen-projects/typescript/code-gen-demo/node_modules
code-gen-projects/typescript/code-gen-demo/package-lock.json
1 change: 1 addition & 0 deletions code-gen-projects/input/bad/complex_types.ion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure if this was intended but all the complex_types.* files are empty.

1 change: 1 addition & 0 deletions code-gen-projects/input/good/complex_types.ion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

1 change: 1 addition & 0 deletions code-gen-projects/schema/complex_types.isl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

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
20 changes: 20 additions & 0 deletions code-gen-projects/typescript/code-gen-demo/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking"
],
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": {
"@typescript-eslint/explicit-function-return-type": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/strict-boolean-expressions": "error"
},
"ignorePatterns": ["src/generated/**/*"]
}
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
53 changes: 53 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,53 @@
{
"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 --noEmit && tsc",
"lint": "eslint . --ext .ts",
"pretest": "npm run build",
"test": "jest --coverage",
"clean": "rimraf dist src/generated/* coverage"
},
"dependencies": {
"ion-js": "^4.3.0",
"decimal.js": "^10.4.3"
},
"devDependencies": {
"@types/jest": "^29.5.0",
"@types/node": "^18.15.11",
"@typescript-eslint/eslint-plugin": "^6.4.0",
"@typescript-eslint/parser": "^6.4.0",
"eslint": "^8.47.0",
"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"
},
"collectCoverageFrom": [
"src/**/*.ts",
"!src/generated/**/*.ts"
],
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as ion from 'ion-js';
import { IonSerializable } from './ion_generated_code';



export enum EnumType {FOO_BAR_BAZ = "FooBarBaz", BAR = "bar", BAZ = "baz", FOO = "foo"
Copy link
Contributor

Choose a reason for hiding this comment

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

I understand you submitting these generated files for making it easy for the reviewer to look at it but we generally don't merge the generated code int the repository. You can probably just create some gists and add it to the PR description.

}

/**
* Type guard for EnumType
* @param value - Value to check
* @returns True if value is EnumType
*/
export function isEnumType(value: any): value is EnumType {
return Object.values(EnumType).includes(value);
}

/**
* Implementation class for EnumType serialization
*/
export class EnumTypeImpl implements IonSerializable {
private value: EnumType;

constructor(value: EnumType) {
this.value = value;
}

/**
* Serialize to Ion format
* @returns Serialized bytes
*/
public toIon(): any {
const writer = ion.makeTextWriter();
writer.writeSymbol(this.value);
return writer.getBytes();
}

/**
* Deserialize from Ion format
* @param reader - Ion reader
* @returns Deserialized EnumType
* @throws Error if value is invalid
*/
public static fromIon(reader: ion.Reader): EnumType {
const value = reader.stringValue();
if (!value || !isEnumType(value)) {
throw new Error(`Invalid enum value for EnumType: ${value}`);
}
return value as EnumType;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as ion from 'ion-js';
Copy link
Contributor

Choose a reason for hiding this comment

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

File naming here is in snake case and probably utils might be a better name to use here.

import { Decimal } from 'decimal.js';

export interface IonSerializable {
toIon(): any;
}

export interface IonSymbol {
text: string;
sid?: number;
local_sid?: number;
}

export interface IonTimestamp {
value: Date;
}

export interface IonDecimal {
value: string;
coefficient: bigint;
exponent: number;
}

// Re-export the Ion types we need
export const { LIST: ListType, STRUCT: StructType } = ion.IonTypes;
Loading
Loading