From 18142398ee36dbea3a2a2b2da691c5e96442df0a Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Thu, 4 Aug 2022 16:04:03 +0900 Subject: [PATCH 1/8] createInterface --- .../jsonschema-types.spec.ts.snap | 3 ++ .../src/jsonschema-types.spec.ts | 27 +++++++++++++++ .../wasm-ast-types/src/jsonschema-types.ts | 34 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 packages/wasm-ast-types/src/__snapshots__/jsonschema-types.spec.ts.snap create mode 100644 packages/wasm-ast-types/src/jsonschema-types.spec.ts create mode 100644 packages/wasm-ast-types/src/jsonschema-types.ts diff --git a/packages/wasm-ast-types/src/__snapshots__/jsonschema-types.spec.ts.snap b/packages/wasm-ast-types/src/__snapshots__/jsonschema-types.spec.ts.snap new file mode 100644 index 00000000..fc04bd33 --- /dev/null +++ b/packages/wasm-ast-types/src/__snapshots__/jsonschema-types.spec.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`createInterface 1`] = `"export interface InstantiateMsg {}"`; diff --git a/packages/wasm-ast-types/src/jsonschema-types.spec.ts b/packages/wasm-ast-types/src/jsonschema-types.spec.ts new file mode 100644 index 00000000..b17cd445 --- /dev/null +++ b/packages/wasm-ast-types/src/jsonschema-types.spec.ts @@ -0,0 +1,27 @@ +import { importStmt } from './utils' +import generate from '@babel/generator'; +import * as t from '@babel/types'; + +import execute_msg from './../../../__fixtures__/basic/execute_msg_for__empty.json'; + +import { + createInterface +} from './jsonschema-types' + +const expectCode = (ast) => { + expect( + generate(ast).code + ).toMatchSnapshot(); +} + +const printCode = (ast) => { + console.log( + generate(ast).code + ); +} + +it('createInterface', () => { + printCode(createInterface( + execute_msg + )) +}); diff --git a/packages/wasm-ast-types/src/jsonschema-types.ts b/packages/wasm-ast-types/src/jsonschema-types.ts new file mode 100644 index 00000000..8cb6c223 --- /dev/null +++ b/packages/wasm-ast-types/src/jsonschema-types.ts @@ -0,0 +1,34 @@ +import * as t from '@babel/types'; + +export const createInterface = () => { + return t.exportNamedDeclaration( + t.tsInterfaceDeclaration( + t.identifier('InstantiateMsg'), + null, + [], + t.tsInterfaceBody([ + t.tsPropertySignature( + t.identifier('admin'), + t.tsTypeAnnotation( + t.tsUnionType( + [ + t.tsStringKeyword(), + t.tsNullKeyword() + ] + ) + ) + ), + t.tsPropertySignature( + t.identifier('members'), + t.tsTypeAnnotation( + t.tsArrayType( + t.tsTypeReference( + t.identifier('Member') + ) + ) + ) + ) + ]) + ) + ); +}; \ No newline at end of file From 15a88d6b82bb86a70c004f59947f8cf1db7ed98c Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Fri, 5 Aug 2022 11:51:02 +0900 Subject: [PATCH 2/8] render types --- .../wasm-ast-types/src/jsonschema-types.ts | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/packages/wasm-ast-types/src/jsonschema-types.ts b/packages/wasm-ast-types/src/jsonschema-types.ts index 8cb6c223..68e46959 100644 --- a/packages/wasm-ast-types/src/jsonschema-types.ts +++ b/packages/wasm-ast-types/src/jsonschema-types.ts @@ -1,4 +1,13 @@ import * as t from '@babel/types'; +import { identifier, tsPropertySignature } from './utils'; + +interface JsonSchemaObject { + type: string; + required?: string[]; + properties?: object; + additionalProperties?: boolean; +} + export const createInterface = () => { return t.exportNamedDeclaration( @@ -31,4 +40,74 @@ export const createInterface = () => { ]) ) ); +}; + +export const additionalPropertiesUnknownType = ( + schema: JsonSchemaObject, + prop: string +) => { + return t.tsPropertySignature( + t.identifier(prop), + t.tsTypeAnnotation( + t.tsTypeLiteral( + [ + t.tsIndexSignature( + [ + identifier( + prop, + t.tsTypeAnnotation( + t.tsStringKeyword() + ) + ) + ], + t.tsTypeAnnotation( + t.tsUnknownKeyword() + ) + ) + ] + ) + ) + ); +}; + +export const renderType = ( + schema: JsonSchemaObject, + prop: string +) => { + const properties = schema.properties ?? {}; + const type = properties[prop].type; + + switch (type) { + case 'string': + return t.tsStringKeyword(); + case 'boolean': + return t.tSBooleanKeyword(); + case 'integer': + return t.tsNumberKeyword(); + case 'object': + if (properties[prop].properties) { + const childSchema: JsonSchemaObject = properties[prop]; + return t.tsTypeLiteral( + Object.keys(childSchema.properties).map(property => { + return renderProperty(childSchema, property); + }) + ); + } + return t.tsObjectKeyword(); + default: + throw new Error('renderType() contact maintainers [unknown type]: ' + type); + } +} + +export const renderProperty = ( + schema: JsonSchemaObject, + prop: string +) => { + return tsPropertySignature( + t.identifier(prop), + t.tsTypeAnnotation( + renderType(schema, prop) + ), + !schema.required?.includes?.(prop) + ); }; \ No newline at end of file From 1a06d4b94fed4afa730d1cd68e062647f620ca3e Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Fri, 5 Aug 2022 15:42:44 +0900 Subject: [PATCH 3/8] array with context --- .../wasm-ast-types/src/jsonschema-types.ts | 167 ++++++++++++++++-- 1 file changed, 154 insertions(+), 13 deletions(-) diff --git a/packages/wasm-ast-types/src/jsonschema-types.ts b/packages/wasm-ast-types/src/jsonschema-types.ts index 68e46959..ba20a390 100644 --- a/packages/wasm-ast-types/src/jsonschema-types.ts +++ b/packages/wasm-ast-types/src/jsonschema-types.ts @@ -70,12 +70,138 @@ export const additionalPropertiesUnknownType = ( ); }; -export const renderType = ( +const renderArrayType1 = (schema, items) => { + if (items.type === 'array') { + if (Array.isArray(items.items)) { + return t.tsArrayType( + t.tsArrayType( + renderType( + items.items[0] + ) + ) + ); + } else { + return t.tsArrayType( + renderArrayType( + schema, + items.items + ) + ); + } + } + return t.tsArrayType( + renderType(items) + ); +} + + +interface RenderType { + type: string; +} + +export const renderType = (obj: RenderType) => { + switch (obj.type) { + case 'string': + return t.tsStringKeyword(); + case 'boolean': + return t.tSBooleanKeyword(); + case 'integer': + return t.tsNumberKeyword(); + default: + throw new Error('contact maintainers [unknown type]: ' + obj.type); + } +} + +export const renderObjectType = ( + context: RenderContext, schema: JsonSchemaObject, - prop: string + name: string ) => { const properties = schema.properties ?? {}; - const type = properties[prop].type; + if (properties[name].properties) { + const childSchema: JsonSchemaObject = properties[name]; + return t.tsTypeLiteral( + Object.keys(childSchema.properties).map(property => { + return renderProperty( + context, + childSchema, + property + ); + }) + ); + } + return t.tsObjectKeyword(); +} + +interface JsonArrayObj { + type: 'array', + items: { type: string, format: string }[] | { type: string, [k: string]: any }; + maxItems?: number; + minItems?: number; +} + +const renderItemsTuple = (items: { type: string }[]) => { + return t.tsTupleType(items.map(item => { + return renderType(item) + })) +}; + +const renderArrayType = ( + context: RenderContext, + schema: JsonArrayObj +) => { + if (Array.isArray(schema.items)) { + if (schema.maxItems === schema.minItems) { + return renderItemsTuple(schema.items); + } + return t.tsArrayType( + renderType( + schema.items[0] + ) + ); + } + if (Array.isArray(schema)) { + if (schema.maxItems === schema.minItems) { + return renderItemsTuple(schema); + } + return t.tsArrayType( + renderType( + schema[0] + ) + ); + } + + switch (schema.items.type) { + case 'array': + if (context.options.optionalArrays) { + return t.tsUnionType([ + t.tsTupleType([]), + t.tsArrayType( + renderArrayType( + context, + schema.items.items + ) + ) + ]); + } + return t.tsArrayType( + renderArrayType( + context, + schema.items.items + ) + ); + } + throw new Error('renderArrayType() contact maintainer: unknown type') +} + +export const renderTypeObjectProperty = ( + context: RenderContext, + schema: JsonSchemaObject, + name: string +) => { + const properties = schema.properties ?? {}; + const prop = properties[name]; + const type = prop.type; switch (type) { case 'string': @@ -85,28 +211,43 @@ export const renderType = ( case 'integer': return t.tsNumberKeyword(); case 'object': - if (properties[prop].properties) { - const childSchema: JsonSchemaObject = properties[prop]; - return t.tsTypeLiteral( - Object.keys(childSchema.properties).map(property => { - return renderProperty(childSchema, property); - }) - ); + return renderObjectType( + context, schema, name + ); + case 'array': + if (context.options.optionalArrays) { + return t.tsUnionType([ + t.tsTupleType([]), + renderArrayType(context, prop) + ]); } - return t.tsObjectKeyword(); + return renderArrayType(context, prop); default: - throw new Error('renderType() contact maintainers [unknown type]: ' + type); + throw new Error('renderTypeObjectProperty() contact maintainers [unknown type]: ' + type); } } +export interface RenderOptions { + optionalArrays: boolean; +} +export interface RenderContext { + definitions?: any; + options: RenderOptions; +} + export const renderProperty = ( + context: RenderContext, schema: JsonSchemaObject, prop: string ) => { return tsPropertySignature( t.identifier(prop), t.tsTypeAnnotation( - renderType(schema, prop) + renderTypeObjectProperty( + context, + schema, + prop + ) ), !schema.required?.includes?.(prop) ); From 9774e7b5afd4a02b5ca68013468c9bcb8da38ece Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Fri, 5 Aug 2022 16:53:47 +0900 Subject: [PATCH 4/8] types --- .../jsonschema-types.spec.ts.snap | 24 +- .../src/jsonschema-types.spec.ts | 218 +++++++++++++++++- .../wasm-ast-types/src/jsonschema-types.ts | 182 +++++++++------ 3 files changed, 342 insertions(+), 82 deletions(-) diff --git a/packages/wasm-ast-types/src/__snapshots__/jsonschema-types.spec.ts.snap b/packages/wasm-ast-types/src/__snapshots__/jsonschema-types.spec.ts.snap index fc04bd33..22a13a84 100644 --- a/packages/wasm-ast-types/src/__snapshots__/jsonschema-types.spec.ts.snap +++ b/packages/wasm-ast-types/src/__snapshots__/jsonschema-types.spec.ts.snap @@ -1,3 +1,25 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`createInterface 1`] = `"export interface InstantiateMsg {}"`; +exports[`nested 1`] = `"nested: [] | ([] | [number, number][]);"`; + +exports[`renderProperty 1`] = `"edges: [] | [number, number];"`; + +exports[`renderProperty 2`] = ` +"update_edges: { + edges: [] | ([] | [number, number][]); + nested: [] | ([] | [number, number][]); + supernested: [] | ([] | ([] | ([] | [string, string][])[])[]); +};" +`; + +exports[`renderProperty 3`] = `"mint: object;"`; + +exports[`renderProperty 4`] = `"whitelist: string;"`; + +exports[`renderProperty 5`] = ` +"set_whitelist: { + whitelist: string; +};" +`; + +exports[`supernested 1`] = `"supernested: [] | ([] | ([] | ([] | [string, string][])[])[]);"`; diff --git a/packages/wasm-ast-types/src/jsonschema-types.spec.ts b/packages/wasm-ast-types/src/jsonschema-types.spec.ts index b17cd445..62aed6f3 100644 --- a/packages/wasm-ast-types/src/jsonschema-types.spec.ts +++ b/packages/wasm-ast-types/src/jsonschema-types.spec.ts @@ -2,10 +2,14 @@ import { importStmt } from './utils' import generate from '@babel/generator'; import * as t from '@babel/types'; -import execute_msg from './../../../__fixtures__/basic/execute_msg_for__empty.json'; +import execute_msg from './../../../__fixtures__/minter/execute_msg.json'; +import arrays from './../../../__fixtures__/arrays/schema/schema.json'; import { - createInterface + createInterface, + createType, + RenderContext, + renderProperty } from './jsonschema-types' const expectCode = (ast) => { @@ -20,8 +24,212 @@ const printCode = (ast) => { ); } -it('createInterface', () => { - printCode(createInterface( - execute_msg + +const context: RenderContext = { + options: { + optionalArrays: true + } +} + +it('createType', () => { + printCode(createType( + context, + arrays, + 'HelloInterface' + )) + printCode(createType( + context, + execute_msg, + 'HelloInterface' + )) +}); + +it('renderProperty', () => { + expectCode(renderProperty( + context, + { + "type": "object", + "required": [ + "edges" + ], + "properties": { + "edges": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "integer", + "format": "int32" + } + ], + "maxItems": 2, + "minItems": 2 + } + } + }, + 'edges' + )) +}); + +it('nested', () => { + expectCode(renderProperty( + context, + { + "type": "object", + "required": [ + "nested" + ], + "properties": { + "nested": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "integer", + "format": "int32" + } + ], + "maxItems": 2, + "minItems": 2 + }, + "maxItems": 2, + "minItems": 2 + } + } + } + }, + 'nested' + )) +}); + +it('supernested', () => { + expectCode(renderProperty( + context, + { + "type": "object", + "required": [ + "supernested" + ], + "properties": { + "supernested": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "maxItems": 2, + "minItems": 2 + }, + "maxItems": 2, + "minItems": 2 + } + }, + "maxItems": 2, + "minItems": 2 + }, + "maxItems": 2, + "minItems": 2 + } + } + } + }, + 'supernested' + )) +}); + + +it('renderProperty', () => { + expectCode(renderProperty( + context, + arrays.oneOf[0], + 'update_edges' + )) +}); + +it('renderProperty', () => { + expectCode(renderProperty( + context, + { + "type": "object", + "required": [ + "mint" + ], + "properties": { + "mint": { + "type": "object" + } + }, + "additionalProperties": false + }, + 'mint' + )) +}); + +it('renderProperty', () => { + expectCode(renderProperty( + context, + { + "type": "object", + "required": [ + "whitelist" + ], + "properties": { + "whitelist": { + "type": "string" + } + } + }, + 'whitelist' + )) +}); + +it('renderProperty', () => { + expectCode(renderProperty( + context, + { + "type": "object", + "required": [ + "set_whitelist" + ], + "properties": { + "set_whitelist": { + "type": "object", + "required": [ + "whitelist" + ], + "properties": { + "whitelist": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + 'set_whitelist' )) }); diff --git a/packages/wasm-ast-types/src/jsonschema-types.ts b/packages/wasm-ast-types/src/jsonschema-types.ts index ba20a390..ea144a8c 100644 --- a/packages/wasm-ast-types/src/jsonschema-types.ts +++ b/packages/wasm-ast-types/src/jsonschema-types.ts @@ -1,48 +1,95 @@ import * as t from '@babel/types'; import { identifier, tsPropertySignature } from './utils'; - -interface JsonSchemaObject { +export interface JsonSchemaObject { type: string; required?: string[]; properties?: object; additionalProperties?: boolean; } +export interface JsonSchemaDefnObject { + $schema: string; + oneOf?: JsonSchemaObject[]; + anyOf?: JsonSchemaObject[]; + allOf?: JsonSchemaObject[]; +} +export interface RenderType { + type: string; +} +export interface JsonArrayObj { + type: 'array', + items: { type: string, format: string }[] | { type: string, [k: string]: any }; + maxItems?: number; + minItems?: number; +} +export interface RenderOptions { + optionalArrays: boolean; +} +export interface RenderContext { + definitions?: any; + options: RenderOptions; +} -export const createInterface = () => { +const schemaPropertyKeys = (schema: JsonSchemaObject) => { + return Object.keys(schema.properties ?? {}); +} + +export const createInterface = ( + context: RenderContext, + schema: JsonSchemaObject, + name: string +) => { return t.exportNamedDeclaration( t.tsInterfaceDeclaration( - t.identifier('InstantiateMsg'), + t.identifier(name), null, [], - t.tsInterfaceBody([ - t.tsPropertySignature( - t.identifier('admin'), - t.tsTypeAnnotation( - t.tsUnionType( - [ - t.tsStringKeyword(), - t.tsNullKeyword() - ] - ) - ) - ), - t.tsPropertySignature( - t.identifier('members'), - t.tsTypeAnnotation( - t.tsArrayType( - t.tsTypeReference( - t.identifier('Member') - ) - ) - ) + t.tsInterfaceBody( + renderSchema( + context, + schema ) - ]) + ) + ) + ); +}; + +export const createType = ( + context: RenderContext, + schema: JsonSchemaDefnObject, + name: string +) => { + let key = null; + if (schema.anyOf) key = 'anyOf'; + if (schema.oneOf) key = 'oneOf'; + if (schema.allOf) key = 'allOf'; + + const members = schema[key].map(childSchema => { + return renderSchema( + context, + childSchema + ); + }); + + return t.exportNamedDeclaration( + t.tsTypeAliasDeclaration( + t.identifier(name), + null, + key === 'allOf' ? + t.tsIntersectionType(members.map(m => { + return t.tsTypeLiteral(m) + })) : + t.tsUnionType(members.map(m => { + return t.tsTypeLiteral(m) + })) ) ); + + throw new Error('createType() schema') }; export const additionalPropertiesUnknownType = ( + context: RenderContext, schema: JsonSchemaObject, prop: string ) => { @@ -70,35 +117,6 @@ export const additionalPropertiesUnknownType = ( ); }; -const renderArrayType1 = (schema, items) => { - if (items.type === 'array') { - if (Array.isArray(items.items)) { - return t.tsArrayType( - t.tsArrayType( - renderType( - items.items[0] - ) - ) - ); - } else { - return t.tsArrayType( - renderArrayType( - schema, - items.items - ) - ); - } - } - return t.tsArrayType( - renderType(items) - ); -} - - -interface RenderType { - type: string; -} - export const renderType = (obj: RenderType) => { switch (obj.type) { case 'string': @@ -121,23 +139,30 @@ export const renderObjectType = ( if (properties[name].properties) { const childSchema: JsonSchemaObject = properties[name]; return t.tsTypeLiteral( - Object.keys(childSchema.properties).map(property => { - return renderProperty( - context, - childSchema, - property - ); - }) + schemaPropertyKeys(childSchema) + .map(property => { + return renderProperty( + context, + childSchema, + property + ); + }) ); } return t.tsObjectKeyword(); } -interface JsonArrayObj { - type: 'array', - items: { type: string, format: string }[] | { type: string, [k: string]: any }; - maxItems?: number; - minItems?: number; +export const renderSchema = ( + context: RenderContext, + schema: JsonSchemaObject +): t.TSTypeElement[] => { + return schemaPropertyKeys(schema).map(key => { + return renderProperty( + context, + schema, + key + ); + }); } const renderItemsTuple = (items: { type: string }[]) => { @@ -194,6 +219,15 @@ const renderArrayType = ( throw new Error('renderArrayType() contact maintainer: unknown type') } + +const getTypeFromRef = ($ref) => { + if ($ref?.startsWith('#/definitions/')) { + return t.tsTypeReference(t.identifier($ref.replace('#/definitions/', ''))) + } + throw new Error('what is $ref: ' + $ref); +} + + export const renderTypeObjectProperty = ( context: RenderContext, schema: JsonSchemaObject, @@ -222,17 +256,13 @@ export const renderTypeObjectProperty = ( ]); } return renderArrayType(context, prop); - default: - throw new Error('renderTypeObjectProperty() contact maintainers [unknown type]: ' + type); } -} -export interface RenderOptions { - optionalArrays: boolean; -} -export interface RenderContext { - definitions?: any; - options: RenderOptions; + if (prop.$ref) { + return getTypeFromRef(prop.$ref); + } + + throw new Error('renderTypeObjectProperty() contact maintainers [unknown type]: ' + type); } export const renderProperty = ( From 9132356e9a84bc305eb3304752068c6e74154fd1 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Fri, 5 Aug 2022 17:27:45 +0900 Subject: [PATCH 5/8] types and interfaces --- .../src/jsonschema-types.spec.ts | 13 ++++- .../wasm-ast-types/src/jsonschema-types.ts | 57 +++++++------------ 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/packages/wasm-ast-types/src/jsonschema-types.spec.ts b/packages/wasm-ast-types/src/jsonschema-types.spec.ts index 62aed6f3..db3615d8 100644 --- a/packages/wasm-ast-types/src/jsonschema-types.spec.ts +++ b/packages/wasm-ast-types/src/jsonschema-types.spec.ts @@ -24,7 +24,6 @@ const printCode = (ast) => { ); } - const context: RenderContext = { options: { optionalArrays: true @@ -42,6 +41,18 @@ it('createType', () => { execute_msg, 'HelloInterface' )) + printCode(createType( + context, + { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + 'HelloInterface' + )) }); it('renderProperty', () => { diff --git a/packages/wasm-ast-types/src/jsonschema-types.ts b/packages/wasm-ast-types/src/jsonschema-types.ts index ea144a8c..3a74d346 100644 --- a/packages/wasm-ast-types/src/jsonschema-types.ts +++ b/packages/wasm-ast-types/src/jsonschema-types.ts @@ -1,6 +1,7 @@ import * as t from '@babel/types'; import { identifier, tsPropertySignature } from './utils'; export interface JsonSchemaObject { + $ref?: string; type: string; required?: string[]; properties?: object; @@ -11,6 +12,7 @@ export interface JsonSchemaDefnObject { oneOf?: JsonSchemaObject[]; anyOf?: JsonSchemaObject[]; allOf?: JsonSchemaObject[]; + definitions?: Record; } export interface RenderType { type: string; @@ -34,26 +36,6 @@ const schemaPropertyKeys = (schema: JsonSchemaObject) => { return Object.keys(schema.properties ?? {}); } -export const createInterface = ( - context: RenderContext, - schema: JsonSchemaObject, - name: string -) => { - return t.exportNamedDeclaration( - t.tsInterfaceDeclaration( - t.identifier(name), - null, - [], - t.tsInterfaceBody( - renderSchema( - context, - schema - ) - ) - ) - ); -}; - export const createType = ( context: RenderContext, schema: JsonSchemaDefnObject, @@ -71,19 +53,17 @@ export const createType = ( ); }); - return t.exportNamedDeclaration( - t.tsTypeAliasDeclaration( - t.identifier(name), - null, - key === 'allOf' ? - t.tsIntersectionType(members.map(m => { - return t.tsTypeLiteral(m) - })) : - t.tsUnionType(members.map(m => { - return t.tsTypeLiteral(m) - })) - ) - ); + if (key) { + return t.exportNamedDeclaration( + t.tsTypeAliasDeclaration( + t.identifier(name), + null, + key === 'allOf' ? + t.tsIntersectionType(members) : + t.tsUnionType(members) + ) + ); + } throw new Error('createType() schema') }; @@ -155,14 +135,19 @@ export const renderObjectType = ( export const renderSchema = ( context: RenderContext, schema: JsonSchemaObject -): t.TSTypeElement[] => { - return schemaPropertyKeys(schema).map(key => { +): t.TSTypeLiteral | t.TSTypeReference => { + + if (schema.$ref) { + return getTypeFromRef(schema.$ref); + } + + return t.tsTypeLiteral(schemaPropertyKeys(schema).map(key => { return renderProperty( context, schema, key ); - }); + })); } const renderItemsTuple = (items: { type: string }[]) => { From 10e5d921fa9cb6ac8675a3c6053f090504edf646 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Fri, 5 Aug 2022 17:42:37 +0900 Subject: [PATCH 6/8] types --- .../jsonschema-types.spec.ts.snap | 94 ++++++++++++++++--- .../src/jsonschema-types.spec.ts | 62 +++++++++--- .../wasm-ast-types/src/jsonschema-types.ts | 48 +++++++++- 3 files changed, 176 insertions(+), 28 deletions(-) diff --git a/packages/wasm-ast-types/src/__snapshots__/jsonschema-types.spec.ts.snap b/packages/wasm-ast-types/src/__snapshots__/jsonschema-types.spec.ts.snap index 22a13a84..b6152537 100644 --- a/packages/wasm-ast-types/src/__snapshots__/jsonschema-types.spec.ts.snap +++ b/packages/wasm-ast-types/src/__snapshots__/jsonschema-types.spec.ts.snap @@ -1,25 +1,97 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`nested 1`] = `"nested: [] | ([] | [number, number][]);"`; +exports[`arrays 1`] = `"edges: [] | number[];"`; -exports[`renderProperty 1`] = `"edges: [] | [number, number];"`; +exports[`createType 1`] = ` +"export type HelloInterface = { + update_edges: { + edges: [] | ([] | number[][]); + nested: [] | ([] | [number, number][]); + supernested: [] | ([] | ([] | ([] | string[][])[])[]); + }; +};" +`; -exports[`renderProperty 2`] = ` -"update_edges: { - edges: [] | ([] | [number, number][]); - nested: [] | ([] | [number, number][]); - supernested: [] | ([] | ([] | ([] | [string, string][])[])[]); +exports[`createType 2`] = ` +"export type HelloInterface = { + mint: object; +} | { + set_whitelist: { + whitelist: string; + }; +} | { + update_start_time: Timestamp; +} | { + update_per_address_limit: { + per_address_limit: number; + }; +} | { + mint_to: { + recipient: string; + }; +} | { + mint_for: { + recipient: string; + token_id: number; + }; +} | { + withdraw: object; };" `; -exports[`renderProperty 3`] = `"mint: object;"`; +exports[`createType 3`] = `"export type HelloInterface = Uint64;"`; -exports[`renderProperty 4`] = `"whitelist: string;"`; +exports[`min max items 1`] = `"edges: [] | [number, number];"`; -exports[`renderProperty 5`] = ` +exports[`mint 1`] = `"mint: object;"`; + +exports[`nested 1`] = `"nested: [] | ([] | [number, number][]);"`; + +exports[`processTypes 1`] = ` +"export type ExecuteMsg = { + mint: object; +} | { + set_whitelist: { + whitelist: string; + }; +} | { + update_start_time: Timestamp; +} | { + update_per_address_limit: { + per_address_limit: number; + }; +} | { + mint_to: { + recipient: string; + }; +} | { + mint_for: { + recipient: string; + token_id: number; + }; +} | { + withdraw: object; +};" +`; + +exports[`processTypes 2`] = `"export type Timestamp = Uint64;"`; + +exports[`processTypes 3`] = `"export type Uint64 = string;"`; + +exports[`set_whitelist 1`] = ` "set_whitelist: { whitelist: string; };" `; -exports[`supernested 1`] = `"supernested: [] | ([] | ([] | ([] | [string, string][])[])[]);"`; +exports[`supernested 1`] = `"supernested: [] | ([] | ([] | ([] | string[][])[])[]);"`; + +exports[`update_edges 1`] = ` +"update_edges: { + edges: [] | ([] | number[][]); + nested: [] | ([] | [number, number][]); + supernested: [] | ([] | ([] | ([] | string[][])[])[]); +};" +`; + +exports[`whitelist 1`] = `"whitelist: string;"`; diff --git a/packages/wasm-ast-types/src/jsonschema-types.spec.ts b/packages/wasm-ast-types/src/jsonschema-types.spec.ts index db3615d8..ec5126a0 100644 --- a/packages/wasm-ast-types/src/jsonschema-types.spec.ts +++ b/packages/wasm-ast-types/src/jsonschema-types.spec.ts @@ -1,15 +1,12 @@ -import { importStmt } from './utils' import generate from '@babel/generator'; -import * as t from '@babel/types'; - import execute_msg from './../../../__fixtures__/minter/execute_msg.json'; import arrays from './../../../__fixtures__/arrays/schema/schema.json'; import { - createInterface, createType, RenderContext, - renderProperty + processTypes, + renderProperty, } from './jsonschema-types' const expectCode = (ast) => { @@ -31,17 +28,17 @@ const context: RenderContext = { } it('createType', () => { - printCode(createType( + expectCode(createType( context, arrays, 'HelloInterface' )) - printCode(createType( + expectCode(createType( context, execute_msg, 'HelloInterface' )) - printCode(createType( + expectCode(createType( context, { "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", @@ -55,7 +52,7 @@ it('createType', () => { )) }); -it('renderProperty', () => { +it('min max items', () => { expectCode(renderProperty( context, { @@ -85,6 +82,34 @@ it('renderProperty', () => { )) }); +it('arrays', () => { + expectCode(renderProperty( + context, + { + "type": "object", + "required": [ + "edges" + ], + "properties": { + "edges": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "integer", + "format": "int32" + } + ] + } + } + }, + 'edges' + )) +}); + it('nested', () => { expectCode(renderProperty( context, @@ -173,7 +198,7 @@ it('supernested', () => { }); -it('renderProperty', () => { +it('update_edges', () => { expectCode(renderProperty( context, arrays.oneOf[0], @@ -181,7 +206,7 @@ it('renderProperty', () => { )) }); -it('renderProperty', () => { +it('mint', () => { expectCode(renderProperty( context, { @@ -200,7 +225,7 @@ it('renderProperty', () => { )) }); -it('renderProperty', () => { +it('whitelist', () => { expectCode(renderProperty( context, { @@ -218,7 +243,7 @@ it('renderProperty', () => { )) }); -it('renderProperty', () => { +it('set_whitelist', () => { expectCode(renderProperty( context, { @@ -244,3 +269,14 @@ it('renderProperty', () => { 'set_whitelist' )) }); + + + +it('processTypes', () => { + processTypes( + context, + execute_msg + ).map(ast => { + expectCode(ast); + }) +}); \ No newline at end of file diff --git a/packages/wasm-ast-types/src/jsonschema-types.ts b/packages/wasm-ast-types/src/jsonschema-types.ts index 3a74d346..1924d63b 100644 --- a/packages/wasm-ast-types/src/jsonschema-types.ts +++ b/packages/wasm-ast-types/src/jsonschema-types.ts @@ -2,13 +2,17 @@ import * as t from '@babel/types'; import { identifier, tsPropertySignature } from './utils'; export interface JsonSchemaObject { $ref?: string; - type: string; + type?: string; + description?: string; required?: string[]; properties?: object; additionalProperties?: boolean; } export interface JsonSchemaDefnObject { - $schema: string; + $schema?: string; + title?: string; + type?: string; + description?: string; oneOf?: JsonSchemaObject[]; anyOf?: JsonSchemaObject[]; allOf?: JsonSchemaObject[]; @@ -46,6 +50,17 @@ export const createType = ( if (schema.oneOf) key = 'oneOf'; if (schema.allOf) key = 'allOf'; + if (!key) { + if (schema.type) { + return t.exportNamedDeclaration( + t.tsTypeAliasDeclaration( + t.identifier(name), + null, + renderType(schema) + ) + ) + } + } const members = schema[key].map(childSchema => { return renderSchema( context, @@ -161,7 +176,7 @@ const renderArrayType = ( schema: JsonArrayObj ) => { if (Array.isArray(schema.items)) { - if (schema.maxItems === schema.minItems) { + if ((schema.minItems || schema.maxItems) && schema.maxItems === schema.minItems) { return renderItemsTuple(schema.items); } return t.tsArrayType( @@ -171,7 +186,7 @@ const renderArrayType = ( ); } if (Array.isArray(schema)) { - if (schema.maxItems === schema.minItems) { + if ((schema.minItems || schema.maxItems) && schema.maxItems === schema.minItems) { return renderItemsTuple(schema); } return t.tsArrayType( @@ -266,4 +281,29 @@ export const renderProperty = ( ), !schema.required?.includes?.(prop) ); +}; + +export const processTypes = ( + context: RenderContext, + schema: JsonSchemaDefnObject +) => { + const definitions = schema.definitions ?? {}; + const defns = Object.keys(definitions).map(name => { + return createType( + context, + definitions[name], + name + ); + }); + + const main = createType( + context, + schema, + schema.title + ); + + return [ + main, + ...defns + ]; }; \ No newline at end of file From 15b64c3a9ee2ae1a9eb5712db7975dd97c8988f6 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Fri, 5 Aug 2022 18:05:23 +0900 Subject: [PATCH 7/8] battle test --- .../src/jsonschema/battle-test.spec.ts | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 packages/wasm-ast-types/src/jsonschema/battle-test.spec.ts diff --git a/packages/wasm-ast-types/src/jsonschema/battle-test.spec.ts b/packages/wasm-ast-types/src/jsonschema/battle-test.spec.ts new file mode 100644 index 00000000..2a6b8a6f --- /dev/null +++ b/packages/wasm-ast-types/src/jsonschema/battle-test.spec.ts @@ -0,0 +1,43 @@ +import generate from '@babel/generator'; +import { sync as glob } from 'glob'; +import { readFileSync } from 'fs'; +import { + RenderContext, + processTypes, +} from './jsonschema-types' +import cases from 'jest-in-case'; + +const minter = glob(__dirname + '/../../../../__fixtures__/minter/*.json').map(file => { + return { name: file.split('__fixtures__')[1], content: JSON.parse(readFileSync(file, 'utf-8')) } +}); + +const expectCode = (ast) => { + expect( + generate(ast).code + ).toMatchSnapshot(); +} + +const printCode = (ast) => { + console.log( + generate(ast).code + ); +} + +const context: RenderContext = { + options: { + optionalArrays: true + } +} + +cases('minter', opts => { + processTypes( + context, + opts.content + ).map(ast => { + expectCode(ast); + }) +}, minter); + +it('processTypes', () => { + +}); \ No newline at end of file From 4acdad63d4b759b744dac38d0ad3dec61003d8a9 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Fri, 5 Aug 2022 18:05:41 +0900 Subject: [PATCH 8/8] structure --- .../__snapshots__/battle-test.spec.ts.snap | 173 ++++++++++++++++++ .../jsonschema-types.spec.ts.snap | 0 .../wasm-ast-types/src/jsonschema/index.ts | 4 + .../{ => jsonschema}/jsonschema-types.spec.ts | 4 +- .../src/{ => jsonschema}/jsonschema-types.ts | 2 +- 5 files changed, 180 insertions(+), 3 deletions(-) create mode 100644 packages/wasm-ast-types/src/jsonschema/__snapshots__/battle-test.spec.ts.snap rename packages/wasm-ast-types/src/{ => jsonschema}/__snapshots__/jsonschema-types.spec.ts.snap (100%) create mode 100644 packages/wasm-ast-types/src/jsonschema/index.ts rename packages/wasm-ast-types/src/{ => jsonschema}/jsonschema-types.spec.ts (98%) rename packages/wasm-ast-types/src/{ => jsonschema}/jsonschema-types.ts (99%) diff --git a/packages/wasm-ast-types/src/jsonschema/__snapshots__/battle-test.spec.ts.snap b/packages/wasm-ast-types/src/jsonschema/__snapshots__/battle-test.spec.ts.snap new file mode 100644 index 00000000..205c5f62 --- /dev/null +++ b/packages/wasm-ast-types/src/jsonschema/__snapshots__/battle-test.spec.ts.snap @@ -0,0 +1,173 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`minter /Users/pyramation/code/cosmwasm/ts-codegen/__fixtures__/minter/execute_msg.json 1`] = ` +"export type ExecuteMsg = { + mint: object; +} | { + set_whitelist: { + whitelist: string; + }; +} | { + update_start_time: Timestamp; +} | { + update_per_address_limit: { + per_address_limit: number; + }; +} | { + mint_to: { + recipient: string; + }; +} | { + mint_for: { + recipient: string; + token_id: number; + }; +} | { + withdraw: object; +};" +`; + +exports[`minter /Users/pyramation/code/cosmwasm/ts-codegen/__fixtures__/minter/execute_msg.json 2`] = `"export type Timestamp = Uint64;"`; + +exports[`minter /Users/pyramation/code/cosmwasm/ts-codegen/__fixtures__/minter/execute_msg.json 3`] = `"export type Uint64 = string;"`; + +exports[`minter /Users/pyramation/code/cosmwasm/ts-codegen/__fixtures__/minter/query_msg.json 1`] = ` +"export type QueryMsg = { + config: object; +} | { + mintable_num_tokens: object; +} | { + start_time: object; +} | { + mint_price: object; +} | { + mint_count: { + address: string; + }; +};" +`; + +exports[`minter /minter/execute_msg.json 1`] = ` +"export type ExecuteMsg = { + mint: object; +} | { + set_whitelist: { + whitelist: string; + }; +} | { + update_start_time: Timestamp; +} | { + update_per_address_limit: { + per_address_limit: number; + }; +} | { + mint_to: { + recipient: string; + }; +} | { + mint_for: { + recipient: string; + token_id: number; + }; +} | { + withdraw: object; +};" +`; + +exports[`minter /minter/execute_msg.json 2`] = `"export type Timestamp = Uint64;"`; + +exports[`minter /minter/execute_msg.json 3`] = `"export type Uint64 = string;"`; + +exports[`minter /minter/query_msg.json 1`] = ` +"export type QueryMsg = { + config: object; +} | { + mintable_num_tokens: object; +} | { + start_time: object; +} | { + mint_price: object; +} | { + mint_count: { + address: string; + }; +};" +`; + +exports[`minter case: 3 1`] = ` +"export type ExecuteMsg = { + mint: object; +} | { + set_whitelist: { + whitelist: string; + }; +} | { + update_start_time: Timestamp; +} | { + update_per_address_limit: { + per_address_limit: number; + }; +} | { + mint_to: { + recipient: string; + }; +} | { + mint_for: { + recipient: string; + token_id: number; + }; +} | { + withdraw: object; +};" +`; + +exports[`minter case: 3 2`] = `"export type Timestamp = Uint64;"`; + +exports[`minter case: 3 3`] = `"export type Uint64 = string;"`; + +exports[`minter case: 5 1`] = ` +"export type QueryMsg = { + config: object; +} | { + mintable_num_tokens: object; +} | { + start_time: object; +} | { + mint_price: object; +} | { + mint_count: { + address: string; + }; +};" +`; + +exports[`processTypes 1`] = ` +"export type ExecuteMsg = { + mint: object; +} | { + set_whitelist: { + whitelist: string; + }; +} | { + update_start_time: Timestamp; +} | { + update_per_address_limit: { + per_address_limit: number; + }; +} | { + mint_to: { + recipient: string; + }; +} | { + mint_for: { + recipient: string; + token_id: number; + }; +} | { + withdraw: object; +};" +`; + +exports[`processTypes 2`] = `"export type Timestamp = Uint64;"`; + +exports[`processTypes 3`] = `"export type Uint64 = string;"`; diff --git a/packages/wasm-ast-types/src/__snapshots__/jsonschema-types.spec.ts.snap b/packages/wasm-ast-types/src/jsonschema/__snapshots__/jsonschema-types.spec.ts.snap similarity index 100% rename from packages/wasm-ast-types/src/__snapshots__/jsonschema-types.spec.ts.snap rename to packages/wasm-ast-types/src/jsonschema/__snapshots__/jsonschema-types.spec.ts.snap diff --git a/packages/wasm-ast-types/src/jsonschema/index.ts b/packages/wasm-ast-types/src/jsonschema/index.ts new file mode 100644 index 00000000..5efc93ec --- /dev/null +++ b/packages/wasm-ast-types/src/jsonschema/index.ts @@ -0,0 +1,4 @@ +export { + createType, + processTypes +} from './jsonschema-types' \ No newline at end of file diff --git a/packages/wasm-ast-types/src/jsonschema-types.spec.ts b/packages/wasm-ast-types/src/jsonschema/jsonschema-types.spec.ts similarity index 98% rename from packages/wasm-ast-types/src/jsonschema-types.spec.ts rename to packages/wasm-ast-types/src/jsonschema/jsonschema-types.spec.ts index ec5126a0..21445a67 100644 --- a/packages/wasm-ast-types/src/jsonschema-types.spec.ts +++ b/packages/wasm-ast-types/src/jsonschema/jsonschema-types.spec.ts @@ -1,6 +1,6 @@ import generate from '@babel/generator'; -import execute_msg from './../../../__fixtures__/minter/execute_msg.json'; -import arrays from './../../../__fixtures__/arrays/schema/schema.json'; +import execute_msg from '../../../../__fixtures__/minter/execute_msg.json'; +import arrays from '../../../../__fixtures__/arrays/schema/schema.json'; import { createType, diff --git a/packages/wasm-ast-types/src/jsonschema-types.ts b/packages/wasm-ast-types/src/jsonschema/jsonschema-types.ts similarity index 99% rename from packages/wasm-ast-types/src/jsonschema-types.ts rename to packages/wasm-ast-types/src/jsonschema/jsonschema-types.ts index 1924d63b..2a8311f7 100644 --- a/packages/wasm-ast-types/src/jsonschema-types.ts +++ b/packages/wasm-ast-types/src/jsonschema/jsonschema-types.ts @@ -1,5 +1,5 @@ import * as t from '@babel/types'; -import { identifier, tsPropertySignature } from './utils'; +import { identifier, tsPropertySignature } from '../utils'; export interface JsonSchemaObject { $ref?: string; type?: string;