-
Notifications
You must be signed in to change notification settings - Fork 17
Adding Typescript support #188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
|
|
| 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 |
| 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/**/*"] | ||
| } |
| 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 |
| 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" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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'; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. File naming here is in snake case and probably |
||
| 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; | ||
There was a problem hiding this comment.
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.