From e89be37afae8598aa49e61c95fd10b39fad20963 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Fri, 16 Dec 2022 21:00:20 +0100 Subject: [PATCH 01/90] (wip): import resolution re-do --- packages/schema/compose/src/index.ts | 8 +- packages/schema/compose/src/resolve.ts | 29 +-- packages/schema/compose/src/resolver.ts | 245 ++++++++++++++++++++++++ packages/schema/compose/src/tempabi.ts | 143 ++++++++++++++ packages/schema/compose/src/todo.md | 1 + 5 files changed, 410 insertions(+), 16 deletions(-) create mode 100644 packages/schema/compose/src/resolver.ts create mode 100644 packages/schema/compose/src/tempabi.ts create mode 100644 packages/schema/compose/src/todo.md diff --git a/packages/schema/compose/src/index.ts b/packages/schema/compose/src/index.ts index 57225dda95..5760e55887 100644 --- a/packages/schema/compose/src/index.ts +++ b/packages/schema/compose/src/index.ts @@ -1,4 +1,4 @@ -import { SchemaFile, AbiResolvers } from "./types"; +import { SchemaFile } from "./types"; import { resolveImportsAndParseSchemas } from "./resolve"; import { renderSchema } from "./render"; @@ -9,7 +9,8 @@ export { renderSchema }; export interface ComposerOptions { schema: SchemaFile; - resolvers: AbiResolvers; + abis: Map; + schemas: Map; } export async function composeSchema( @@ -18,6 +19,7 @@ export async function composeSchema( return await resolveImportsAndParseSchemas( options.schema.schema, options.schema.absolutePath, - options.resolvers + options.schemas, + options.abis ); } diff --git a/packages/schema/compose/src/resolve.ts b/packages/schema/compose/src/resolve.ts index 70fe6f9c8b..8c082372aa 100644 --- a/packages/schema/compose/src/resolve.ts +++ b/packages/schema/compose/src/resolve.ts @@ -5,10 +5,7 @@ import { ExternalImport, LocalImport, - AbiResolvers, SYNTAX_REFERENCE, - AbiResolver, - SchemaResolver, } from "./types"; import { parseExternalImports, parseLocalImports, parseUse } from "./parse"; import { renderSchema } from "./render"; @@ -65,6 +62,9 @@ type ImplementationWithInterfaces = { const TYPE_NAME_REGEX = `[a-zA-Z0-9_]+`; +type UriStr = string; +type SchemaPath = string; + export async function resolveUseStatements( schema: string, schemaPath: string, @@ -130,7 +130,8 @@ export async function resolveUseStatements( export async function resolveImportsAndParseSchemas( schema: string, schemaPath: string, - resolvers: AbiResolvers, + importSchemas: Map, + importsAbis: Map, noValidate = false ): Promise { const importKeywordCapture = /^(?:#|""")*import\s/gm; @@ -181,15 +182,15 @@ export async function resolveImportsAndParseSchemas( const externalImports = await resolveExternalImports( externalImportsToResolve, - resolvers.external, + importsAbis, subAbi ); await resolveLocalImports( localImportsToResolve, - resolvers.local, + importSchemas, subAbi, - resolvers + importsAbis ); const capabilitiesByModule = await resolveUseStatements( schema, @@ -645,7 +646,7 @@ function resolveInterfaces( async function resolveExternalImports( importsToResolve: ExternalImport[], - resolveAbi: AbiResolver, + externalAbisMap: Map, abi: WrapAbi ): Promise { // Keep track of all imported object type names @@ -655,7 +656,8 @@ async function resolveExternalImports( const { uri, namespace, importedTypes } = importToResolve; // Resolve the schema - const extAbi = await resolveAbi(uri); + // const extAbi = await resolveAbi(uri); + const extAbi = externalAbisMap.get(uri) if (!extAbi) { throw Error(`Unable to resolve abi at "${uri}"`); @@ -962,15 +964,15 @@ async function resolveExternalImports( async function resolveLocalImports( importsToResolve: LocalImport[], - resolveSchema: SchemaResolver, + importSchemas: Map, abi: WrapAbi, - resolvers: AbiResolvers + importAbis: Map ): Promise { for (const importToResolve of importsToResolve) { const { importedTypes, path } = importToResolve; // Resolve the schema - let schema = await resolveSchema(path); + let schema = importSchemas.get(path); if (!schema) { throw Error(`Unable to resolve schema at "${path}"`); @@ -985,7 +987,8 @@ async function resolveLocalImports( const localAbi = await resolveImportsAndParseSchemas( schema, path, - resolvers, + importSchemas, + importAbis, true ); diff --git a/packages/schema/compose/src/resolver.ts b/packages/schema/compose/src/resolver.ts new file mode 100644 index 0000000000..c5f12ff16c --- /dev/null +++ b/packages/schema/compose/src/resolver.ts @@ -0,0 +1,245 @@ +import { createImportedEnumDefinition, createImportedEnvDefinition, createImportedModuleDefinition, createImportedObjectDefinition, visitEnumDefinition, visitEnvDefinition, visitModuleDefinition, visitObjectDefinition } from "@polywrap/schema-parse"; +import { EnumDefinition, EnvDefinition, GenericDefinition, ImportedEnumDefinition, ImportedEnvDefinition, ImportedModuleDefinition, ImportedObjectDefinition, ModuleDefinition, ObjectDefinition, WrapAbi } from "@polywrap/wrap-manifest-types-js"; + +type UriStr = string; +interface ImportStatement { + typeNames: string[] +} + +interface ExternalImportStatement extends ImportStatement { + uri: UriStr; + namespace: string; +} + +interface Namespaced { + __namespaced?: boolean; +} + +type ResolvedType = (ImportedModuleDefinition | ImportedObjectDefinition | ImportedEnumDefinition | ImportedEnvDefinition) & Namespaced; + +type ImportMap = Map< + string, ResolvedType +>; + +type ImportDefWithKind = { + extDefinition: ImportedModuleDefinition, + kind: "ImportedModule" +} | { + extDefinition: ImportedEnvDefinition, + kind: "ImportedEnv" +} | { + extDefinition: ImportedObjectDefinition, + kind: "ImportedObject" +} | { + extDefinition: ImportedEnumDefinition, + kind: "ImportedEnum" +} + +type LocalDefWithKind = { + extDefinition: ModuleDefinition, + kind: "Module" +} | { + extDefinition: EnvDefinition, + kind: "Env" +} | { + extDefinition: ObjectDefinition, + kind: "Object" +} | { + extDefinition: EnumDefinition, + kind: "Enum" +} + +type ExternalDefinitionWithKind = ImportDefWithKind | LocalDefWithKind + + +export abstract class ImportsResolver { + constructor(protected mainAbi: WrapAbi, protected imports: TImportStatement[], protected importAbis: Map) { } + + protected determineKind(importAbi: WrapAbi, importType: string): ExternalDefinitionWithKind { + const extObj = importAbi.objectTypes?.find(def => def.type == importType) + if (extObj) { + return { extDefinition: extObj, kind: "Object" } + } + + const extImportObj = importAbi.importedObjectTypes?.find(def => def.type == importType); + if (extImportObj) { + return { extDefinition: extImportObj, kind: "ImportedObject" } + } + + const extEnum = importAbi.enumTypes?.find(def => def.type == importType); + if (extEnum) { + return { extDefinition: extEnum, kind: "Enum" } + } + + const extImportEnum = importAbi.importedEnumTypes?.find(def => def.type == importType); + if (extImportEnum) { + return { extDefinition: extImportEnum, kind: "ImportedEnum" } + } + + const extModule = importAbi.moduleType; + if (extModule && extModule.type == importType) { + return { extDefinition: extModule, kind: "Module" } + } + + const extModuleImport = importAbi.importedModuleTypes?.find(def => def.type == importType); + if (extModuleImport) { + return { extDefinition: extModuleImport, kind: "ImportedModule" } + } + + const extEnv = importAbi.envType; + if (extEnv && extEnv.type == importType) { + return { extDefinition: extEnv, kind: "Env" } + } + + const extImportEnv = importAbi.importedEnvTypes?.find(def => def.type == importType); + if (extImportEnv) { + return { extDefinition: extImportEnv, kind: "ImportedEnv" } + } + + throw new Error(`Could not determine kind of imported type '${importType}'`) + } + + // private copyAllDefinitions(importAbi: WrapAbi) { + // if (importAbi) { + // this.mainAbi.importedEnumTypes = this.mainAbi.importedEnumTypes?.concat(importAbi.enumTypes ?? []) + // } + // } + + protected namespaceResolvedType(unnamespacedResolvedType: ResolvedType, uri: UriStr, namespace: string): ResolvedType { + // TODO: more performant way of doing it? + const objectCopy = JSON.parse(JSON.stringify(unnamespacedResolvedType)) as ResolvedType; + + objectCopy.namespace = namespace; + objectCopy.uri = uri; + objectCopy.type = `${namespace}_${objectCopy.type}`; + + return objectCopy + } + + protected _resolveImport(importAbi: WrapAbi, importType: string): { unnamespacedResolvedType: ResolvedType; visitor: Function } { + const extDefWithKind = this.determineKind(importAbi, importType) + // TODO: Namespace and Uri in TrueType + + switch (extDefWithKind.kind) { + case "Object": + case "ImportedObject": + return { + unnamespacedResolvedType: { + ...createImportedObjectDefinition({ + ...extDefWithKind.extDefinition, + type: importType, + name: undefined, + required: undefined, + nativeType: extDefWithKind.extDefinition.type, + uri: "", + namespace: "" + }), + properties: extDefWithKind.extDefinition.properties, + }, + + visitor: visitObjectDefinition + }; + case "Enum": + case "ImportedEnum": + return { + unnamespacedResolvedType: createImportedEnumDefinition({ + ...extDefWithKind.extDefinition, + type: importType, + name: undefined, + required: undefined, + nativeType: extDefWithKind.extDefinition.type, + uri: "", + namespace: "" + }), + visitor: visitEnumDefinition + }; + case "Module": + return { + unnamespacedResolvedType: { + ...createImportedModuleDefinition({ + ...extDefWithKind.extDefinition, + required: undefined, + nativeType: extDefWithKind.extDefinition.type, + uri: "", + namespace: "", + }), + methods: extDefWithKind.extDefinition.methods, + }, + visitor: visitModuleDefinition + }; + case "Env": + return { + unnamespacedResolvedType: { + ...createImportedEnvDefinition({ + ...extDefWithKind.extDefinition, + name: undefined, + required: undefined, + nativeType: extDefWithKind.extDefinition.type, + uri: "", + namespace: "", + }), + properties: extDefWithKind.extDefinition.properties, + }, + visitor: visitEnvDefinition + }; + case "ImportedModule": + case "ImportedEnv": + throw new Error(`Cannot import an import's imported ${extDefWithKind.kind}. Tried to import ${importType}`) + } + } + + + abstract resolveImportsStatement(importStatement: TImportStatement): ImportMap; + + + // TODO: Add all imported types into the aggregate Abi +} + +export class ExternalImportsResolver extends ImportsResolver { + protected extractDependencyTypes(importAbi: WrapAbi, importType: string): ResolvedType[] { + + const extDefWithKind = this.determineKind(importAbi, importType); + + switch(extDefWithKind.kind) { + case "Object": + case "ImportedObject": + extDefWithKind.extDefinition.properties?.map(({ type, scalar }) => { + if (scalar) { + + } + this.extractDependencyTypes(importAbi, type) + }) + } + } + + resolveImportsStatement(importStatatement: ExternalImportStatement): ImportMap { + const { uri, namespace, typeNames } = importStatatement; + const importAbi = this.importAbis.get(uri); + const typesToImport: ImportMap = new Map() + + if (!importAbi) { + throw Error(`Unable to resolve abi at "${uri}"`); + } + + if (typeNames.includes("*")) { + // TODO + } + + for (const typeName of typeNames) { + const { visitor, unnamespacedResolvedType } = this._resolveImport(importAbi, typeName) + + // TODO: EXTERNAL IMPORTS ONLY + + const namespacedResolvedType = this.namespaceResolvedType(unnamespacedResolvedType, uri, namespace) + + + //TODO: continue if we already imported it? + + typesToImport.set(namespacedResolvedType.type, namespacedResolvedType) + + visitor(namespacedResolvedType, extractImportDependencies()) + } + + return typesToImport; + } +} \ No newline at end of file diff --git a/packages/schema/compose/src/tempabi.ts b/packages/schema/compose/src/tempabi.ts new file mode 100644 index 0000000000..f634624caf --- /dev/null +++ b/packages/schema/compose/src/tempabi.ts @@ -0,0 +1,143 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +/* tslint:disable */ + +export type ObjectDefinition = GenericDefinition & + WithComment & { + properties?: PropertyDefinition[]; + interfaces?: InterfaceImplementedDefinition[]; + }; +export type GenericDefinition = WithKind & { + type: string; + name?: string; + required?: boolean; +}; +export type PropertyDefinition = WithComment & AnyDefinition; +export type AnyDefinition = GenericDefinition & { + array?: ArrayDefinition; + scalar?: ScalarDefinition; + map?: MapDefinition; + object?: ObjectRef; + enum?: EnumRef; + unresolvedObjectOrEnum?: UnresolvedObjectOrEnumRef; +}; +export type ArrayDefinition = AnyDefinition & { + item?: GenericDefinition; +}; +export type ScalarDefinition = GenericDefinition & { + type: + | "UInt" + | "UInt8" + | "UInt16" + | "UInt32" + | "Int" + | "Int8" + | "Int16" + | "Int32" + | "String" + | "Boolean" + | "Bytes" + | "BigInt" + | "BigNumber" + | "JSON"; +}; +export type MapDefinition = AnyDefinition & + WithComment & { + key?: MapKeyDefinition; + value?: GenericDefinition; + }; +export type MapKeyDefinition = AnyDefinition & { + type?: "UInt" | "UInt8" | "UInt16" | "UInt32" | "Int" | "Int8" | "Int16" | "Int32" | "String"; +}; +export type ObjectRef = GenericDefinition; +export type EnumRef = GenericDefinition; +export type UnresolvedObjectOrEnumRef = GenericDefinition; +export type InterfaceImplementedDefinition = GenericDefinition; +export type ModuleDefinition = GenericDefinition & + WithComment & { + methods?: MethodDefinition[]; + imports?: ImportedModuleRef[]; + interfaces?: InterfaceImplementedDefinition[]; + }; +export type MethodDefinition = GenericDefinition & + WithComment & { + arguments?: PropertyDefinition[]; + env?: { + required?: boolean; + }; + return?: PropertyDefinition; + }; +export type EnumDefinition = GenericDefinition & + WithComment & { + constants?: string[]; + }; +export type InterfaceDefinition = GenericDefinition & + ImportedDefinition & { + capabilities?: CapabilityDefinition; + }; +export type ImportedObjectDefinition = ObjectDefinition & ImportedDefinition & WithComment; +export type ImportedModuleDefinition = GenericDefinition & + ImportedDefinition & + WithComment & { + methods?: MethodDefinition[]; + isInterface?: boolean; + }; +export type ImportedEnumDefinition = EnumDefinition & ImportedDefinition; +export type ImportedEnvDefinition = ImportedObjectDefinition; +export type EnvDefinition = ObjectDefinition; + +export interface WrapManifest { + /** + * WRAP Standard Version + */ + version: "0.1.0" | "0.1"; + /** + * Wrapper Package Type + */ + type: "wasm" | "interface" | "plugin"; + /** + * Wrapper Name + */ + name: string; + abi: Abi; +} +/** + * Information of modules + */ +export interface Abi { + /** + * ABI Version + */ + version?: "0.1"; + types?: { + objectTypes?: ObjectDefinition[]; + moduleType?: ModuleDefinition; + enumTypes?: EnumDefinition[]; + interfaceTypes?: InterfaceDefinition[]; + envType?: EnvDefinition; + } + importTypes?: { + importedObjectTypes?: ImportedObjectDefinition[]; + importedModuleTypes?: ImportedModuleDefinition[]; + importedEnumTypes?: ImportedEnumDefinition[]; + importedEnvTypes?: ImportedEnvDefinition[]; + } +} +export interface WithKind { + kind: number; +} +export interface WithComment { + comment?: string; +} +export interface ImportedModuleRef { + type?: string; +} +export interface ImportedDefinition { + uri: string; + namespace: string; + nativeType: string; +} +export interface CapabilityDefinition { + getImplementations?: { + enabled: boolean; + }; +} diff --git a/packages/schema/compose/src/todo.md b/packages/schema/compose/src/todo.md new file mode 100644 index 0000000000..ceab0a4479 --- /dev/null +++ b/packages/schema/compose/src/todo.md @@ -0,0 +1 @@ +- Parse everything first -> give already parsed ABIs to compose \ No newline at end of file From 73863d649984b39dd11ad28c1b2db8a345ac6654 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Tue, 20 Dec 2022 20:01:24 +0100 Subject: [PATCH 02/90] (feat): new definitions --- packages/schema/parse/src/definitions.ts | 111 +++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 packages/schema/parse/src/definitions.ts diff --git a/packages/schema/parse/src/definitions.ts b/packages/schema/parse/src/definitions.ts new file mode 100644 index 0000000000..cc3251cf3f --- /dev/null +++ b/packages/schema/parse/src/definitions.ts @@ -0,0 +1,111 @@ +//TODO: interfaces and capabilities + +export interface Definition { + name: string; + kind: string; + comment?: string; +} + +export interface ImportedDefinition { + uri: "test-interface.eth", + namespace: "Interface", + imported: true; +} + +export interface Reference { + definition: Definition; + required: boolean +} + +// Definitions + +export interface ScalarDefinition extends Definition { + kind: "Scalar"; + name: "UInt" + | "UInt8" + | "UInt16" + | "UInt32" + | "Int" + | "Int8" + | "Int16" + | "Int32" + | "String" + | "Boolean" + | "Bytes" + | "BigInt" + | "BigNumber" + | "JSON"; +} + +export interface ObjectDefinition extends Definition { + kind: "Object"; + properties: ObjectProperty[] +} + +export interface EnvDefinition extends Definition { + kind: "Env"; + properties: ObjectProperty[] +} + +export interface EnumDefinition extends Definition { + kind: "Enum"; + constants: string[]; +} + +export interface MethodDefinition extends Definition { + kind: "Method"; + arguments: MethodArgument[]; + env?: { + required?: boolean + }; + return: Reference +} + +export interface ModuleDefinition extends Definition { + kind: "Module" + methods: MethodDefinition[] +} + +// References + +export interface MapReference extends Reference { + keys: Reference, + values: Reference +} + +export interface ArrayReference extends Reference { + items: Reference; +} + +// Helpers + +export interface ObjectProperty { + name: string; + type: Reference +} + +export interface MethodArgument { + name: string; + type: Reference; +} + +// Exports + +export interface WrapManifest { + version: "0.1.0" | "0.1"; + type: "wasm" | "interface" | "plugin"; + name: string; + abi: Abi; +} + +export interface Abi { + version: "0.1"; + objectTypes: ObjectDefinition[]; + moduleType?: ModuleDefinition; + enumTypes: EnumDefinition[]; + importedObjectTypes: (ObjectDefinition & ImportedDefinition)[]; + importedModuleTypes: (ModuleDefinition & ImportedDefinition)[]; + importedEnumTypes: (EnumDefinition & ImportedDefinition)[]; + importedEnvTypes: (EnvDefinition & ImportedDefinition)[]; + envType?: EnvDefinition; +} \ No newline at end of file From f2d08c31d310d180544efbb88337f66ceab05c46 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Fri, 23 Dec 2022 00:35:37 +0100 Subject: [PATCH 03/90] (wip): new definitions and object extractor --- packages/schema/compose/src/resolver.ts | 59 ++-- packages/schema/parse/src/creators.ts | 92 ++++++ packages/schema/parse/src/definitions.ts | 92 ++++-- .../schema/parse/src/extract/Blackboard.ts | 45 --- .../schema/parse/src/extract/object-types.ts | 113 ++++--- .../parse/src/extract/utils/map-utils.ts | 281 ++++++++---------- .../src/extract/utils/object-types-utils.ts | 136 --------- .../parse/src/extract/utils/property-utils.ts | 59 ---- 8 files changed, 388 insertions(+), 489 deletions(-) create mode 100644 packages/schema/parse/src/creators.ts delete mode 100644 packages/schema/parse/src/extract/Blackboard.ts delete mode 100644 packages/schema/parse/src/extract/utils/object-types-utils.ts delete mode 100644 packages/schema/parse/src/extract/utils/property-utils.ts diff --git a/packages/schema/compose/src/resolver.ts b/packages/schema/compose/src/resolver.ts index c5f12ff16c..a76f9ad89d 100644 --- a/packages/schema/compose/src/resolver.ts +++ b/packages/schema/compose/src/resolver.ts @@ -1,4 +1,4 @@ -import { createImportedEnumDefinition, createImportedEnvDefinition, createImportedModuleDefinition, createImportedObjectDefinition, visitEnumDefinition, visitEnvDefinition, visitModuleDefinition, visitObjectDefinition } from "@polywrap/schema-parse"; +import { createImportedEnumDefinition, createImportedEnvDefinition, createImportedModuleDefinition, createImportedObjectDefinition, DefinitionKind, visitEnumDefinition, visitEnvDefinition, visitModuleDefinition, visitObjectDefinition } from "@polywrap/schema-parse"; import { EnumDefinition, EnvDefinition, GenericDefinition, ImportedEnumDefinition, ImportedEnvDefinition, ImportedModuleDefinition, ImportedObjectDefinition, ModuleDefinition, ObjectDefinition, WrapAbi } from "@polywrap/wrap-manifest-types-js"; type UriStr = string; @@ -6,13 +6,12 @@ interface ImportStatement { typeNames: string[] } -interface ExternalImportStatement extends ImportStatement { - uri: UriStr; - namespace: string; -} +type ExternalImportStatement = ImportStatement & Namespaced interface Namespaced { __namespaced?: boolean; + uri: UriStr; + namespace: string; } type ResolvedType = (ImportedModuleDefinition | ImportedObjectDefinition | ImportedEnumDefinition | ImportedEnvDefinition) & Namespaced; @@ -136,7 +135,7 @@ export abstract class ImportsResolver }), properties: extDefWithKind.extDefinition.properties, }, - + visitor: visitObjectDefinition }; case "Enum": @@ -196,19 +195,45 @@ export abstract class ImportsResolver } export class ExternalImportsResolver extends ImportsResolver { - protected extractDependencyTypes(importAbi: WrapAbi, importType: string): ResolvedType[] { - - const extDefWithKind = this.determineKind(importAbi, importType); + protected extractDependencyTypes(importAbi: WrapAbi, genericDefinition: GenericDefinition): GenericDefinition[] { + + switch (genericDefinition.kind) { + case DefinitionKind.Scalar: + return []; + case DefinitionKind.ObjectRef: + return [] + case DefinitionKind.EnumRef: + return [genericDefinition]; + case DefinitionKind.Object: + case DefinitionKind.ImportedObject: + return [genericDefinition, ...(genericDefinition as ObjectDefinition).properties?.map(p => this.extractDependencyTypes(importAbi, p))] + } + } + + protected extractDeps(resolvedType: ResolvedType, extracted: ResolvedType[] = []) { + switch (resolvedType.kind) { + case DefinitionKind.Scalar: + return [] + case DefinitionKind.Object: + case DefinitionKind.ImportedObject: + const abi = this.importAbis.get(resolvedType.uri); + + (resolvedType as ObjectDefinition).properties?.forEach(property => { - switch(extDefWithKind.kind) { - case "Object": - case "ImportedObject": - extDefWithKind.extDefinition.properties?.map(({ type, scalar }) => { - if (scalar) { - - } - this.extractDependencyTypes(importAbi, type) }) + + const objectDef = abi?.objectTypes?.find(obj => obj.type === resolvedType.nativeType); + objectDef?.properties?.forEach(property => { + + }) + + if + + const importObjectDef = abi?.importedObjectTypes?.find(obj => obj.type === resolvedType.nativeType) + + if (importObjectDef) { + this.extractDeps(importObjectDef) + } } } diff --git a/packages/schema/parse/src/creators.ts b/packages/schema/parse/src/creators.ts new file mode 100644 index 0000000000..45a6c16f82 --- /dev/null +++ b/packages/schema/parse/src/creators.ts @@ -0,0 +1,92 @@ +import { ArrayReference, EnumDefinition, EnvDefinition, Imported, MapReference, ModuleDefinition, ObjectDefinition, Scalar, ScalarDefinition } from "./definitions"; + +export const createScalarDefinition = (scalar: Scalar): ScalarDefinition => { + return { + kind: "Scalar", + name: scalar, + } +} + +export const createObjectDefinition = (args: Omit): ObjectDefinition => { + return { + kind: "Object", + ...args + } +} + +export const createImportedObjectDefinition = (args: Omit): ObjectDefinition & Imported => { + return { + kind: "Object", + ...args + } +} + +export const createEnvDefinition = (args: Omit): EnvDefinition => { + return { + kind: "Env", + ...args + } +} + +export const createImportedEnvDefinition = (args: Omit): EnvDefinition & Imported => { + return { + kind: "Env", + ...args + } +} + +export const createEnumDefinition = (args: Omit): EnumDefinition => { + return { + kind: "Enum", + ...args + } +} + +export const createImportedEnumDefinition = (args: Omit): EnumDefinition & Imported => { + return { + kind: "Enum", + ...args + } +} + +export const createModuleDefinition = (args: Omit): ModuleDefinition => { + return { + kind: "Module", + ...args + } +} + +export const createImportedModuleDefinition = (args: Omit): ModuleDefinition & Imported => { + return { + kind: "Module", + ...args + } +} + +export const createMapReference = (args: Omit): MapReference => { + return { + kind: "Map", + ...args + } +} + +export const createImportedMapReference = (args: Omit): MapReference & Imported => { + return { + kind: "Map", + ...args + } +} + +export const createArrayReference = (args: Omit): ArrayReference => { + return { + kind: "Array", + ...args + } +} + +export const createImportedArrayReference = (args: Omit): ArrayReference & Imported => { + return { + kind: "Array", + ...args + } +} \ No newline at end of file diff --git a/packages/schema/parse/src/definitions.ts b/packages/schema/parse/src/definitions.ts index cc3251cf3f..67779568fe 100644 --- a/packages/schema/parse/src/definitions.ts +++ b/packages/schema/parse/src/definitions.ts @@ -1,19 +1,65 @@ //TODO: interfaces and capabilities +export const SUPPORTED_SCALARS = [ + "UInt", + "UInt8", + "UInt16", + "UInt32", + "Int", + "Int8", + "Int16", + "Int32", + "String", + "Boolean", + "Bytes", + "BigInt", + "BigNumber", + "JSON", +] as const + +export type Scalar = typeof SUPPORTED_SCALARS[number]; + +export const SUPPORTED_MAP_KEYS = [ + "UInt", + "UInt8", + "UInt16", + "UInt32", + "Int", + "Int8", + "Int16", + "Int32", + "String" +] as const + +export type MapKeyType = typeof SUPPORTED_MAP_KEYS[number]; + export interface Definition { name: string; kind: string; comment?: string; } -export interface ImportedDefinition { +export interface Imported { uri: "test-interface.eth", namespace: "Interface", imported: true; } -export interface Reference { - definition: Definition; +export type Reference = { + kind: "Scalar" | "Object" | "Enum" + required: boolean + type: string +} | ArrayReference | MapReference + +export interface ArrayReference { + kind: "Array" + definition: ArrayDefinition + required: boolean +} + +export interface MapReference { + kind: "Map" + definition: MapDefinition required: boolean } @@ -21,20 +67,7 @@ export interface Reference { export interface ScalarDefinition extends Definition { kind: "Scalar"; - name: "UInt" - | "UInt8" - | "UInt16" - | "UInt32" - | "Int" - | "Int8" - | "Int16" - | "Int32" - | "String" - | "Boolean" - | "Bytes" - | "BigInt" - | "BigNumber" - | "JSON"; + name: Scalar; } export interface ObjectDefinition extends Definition { @@ -66,20 +99,29 @@ export interface ModuleDefinition extends Definition { methods: MethodDefinition[] } -// References +// Special cases: References with embedded definitions -export interface MapReference extends Reference { - keys: Reference, +export interface MapDefinition extends Definition { + name: "" + kind: "Map", + keys: { + kind: "Scalar" + required: boolean + type: string + }, values: Reference } -export interface ArrayReference extends Reference { +export interface ArrayDefinition extends Definition { + name: "" + kind: "Array" items: Reference; } // Helpers export interface ObjectProperty { + comment?: string; name: string; type: Reference } @@ -103,9 +145,9 @@ export interface Abi { objectTypes: ObjectDefinition[]; moduleType?: ModuleDefinition; enumTypes: EnumDefinition[]; - importedObjectTypes: (ObjectDefinition & ImportedDefinition)[]; - importedModuleTypes: (ModuleDefinition & ImportedDefinition)[]; - importedEnumTypes: (EnumDefinition & ImportedDefinition)[]; - importedEnvTypes: (EnvDefinition & ImportedDefinition)[]; + importedObjectTypes: (ObjectDefinition & Imported)[]; + importedModuleTypes: (ModuleDefinition & Imported)[]; + importedEnumTypes: (EnumDefinition & Imported)[]; + importedEnvTypes: (EnvDefinition & Imported)[]; envType?: EnvDefinition; } \ No newline at end of file diff --git a/packages/schema/parse/src/extract/Blackboard.ts b/packages/schema/parse/src/extract/Blackboard.ts deleted file mode 100644 index 0d44fa6c98..0000000000 --- a/packages/schema/parse/src/extract/Blackboard.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { - DocumentNode, - visit, - ObjectTypeDefinitionNode, - EnumTypeDefinitionNode, -} from "graphql"; - -export interface CustomType { - name: string; - type: "enum" | "object"; -} - -export class Blackboard { - private _customTypes?: CustomType[]; - - constructor(private _astNode: DocumentNode) {} - - getCustomTypes(): CustomType[] { - if (this._customTypes) { - return this._customTypes; - } - - const customTypes: CustomType[] = []; - - visit(this._astNode, { - enter: { - ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { - customTypes.push({ - name: node.name.value, - type: "object", - }); - }, - EnumTypeDefinition: (node: EnumTypeDefinitionNode) => { - customTypes.push({ - name: node.name.value, - type: "enum", - }); - }, - }, - }); - - this._customTypes = customTypes; - return this._customTypes; - } -} diff --git a/packages/schema/parse/src/extract/object-types.ts b/packages/schema/parse/src/extract/object-types.ts index 8ab6b33a4b..17cbbf3388 100644 --- a/packages/schema/parse/src/extract/object-types.ts +++ b/packages/schema/parse/src/extract/object-types.ts @@ -1,28 +1,68 @@ import { - createObjectDefinition, - createInterfaceImplementedDefinition, isEnvType, isModuleType, } from ".."; -import { - extractFieldDefinition, - extractListType, - extractNamedType, - State, -} from "./utils/object-types-utils"; import { ObjectTypeDefinitionNode, - NonNullTypeNode, - NamedTypeNode, - ListTypeNode, FieldDefinitionNode, DirectiveNode, ASTVisitor, + StringValueNode, + TypeNode, } from "graphql"; -import { ObjectDefinition, WrapAbi } from "@polywrap/wrap-manifest-types-js"; +import { ObjectDefinition, Abi as WrapAbi, ObjectProperty, Reference, EnumDefinition } from "../definitions"; +import { parseMapReference } from "./utils/map-utils"; + +const extractObjectProperty = (node: FieldDefinitionNode, enumDefs: string[]): ObjectProperty => { + const extractType = (node: TypeNode, required = false): Reference => { + switch (node.kind) { + case "NonNullType": + return extractType(node.type, true) + case "ListType": + return { + kind: "Array", + required, + definition: { + kind: "Array", + items: extractType(node.type), + name: "", + } + } + case "NamedType": + return { + required, + kind: enumDefs.includes(node.name.value) ? "Enum" : "Object", + type: node.name.value + } + } + } + + if (node.directives) { + for (const dir of node.directives) { + if (dir.name.value === "annotate") { + const typeName = (dir.arguments?.find((arg) => arg.name.value === "type") + ?.value as StringValueNode).value; + if (!typeName) { + throw new Error( + `Annotate directive: ${node.name.value} has invalid arguments` + ); + } + return { + name: node.name.value, + type: parseMapReference(typeName, enumDefs) + } + } + } + } -const visitorEnter = (objectTypes: ObjectDefinition[], state: State) => ({ + return { + name: node.name.value, + type: extractType(node.type) + } +} + +const visitorEnter = (objectTypes: ObjectDefinition[], enumDefs: EnumDefinition[]) => ({ ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { const typeName = node.name.value; @@ -41,50 +81,25 @@ const visitorEnter = (objectTypes: ObjectDefinition[], state: State) => ({ return; } - const interfaces = node.interfaces?.map((x) => - createInterfaceImplementedDefinition({ type: x.name.value }) - ); + // TODO: restore interfaces support + // const interfaces = node.interfaces?.map((x) => + // createInterfaceImplementedDefinition({ type: x.name.value }) + // ); // Create a new TypeDefinition - const type = createObjectDefinition({ - type: typeName, - interfaces: interfaces?.length ? interfaces : undefined, + const type = { + kind: "Object" as const, comment: node.description?.value, - }); + name: typeName, + properties: node.fields?.map(fieldNode => extractObjectProperty(fieldNode, enumDefs.map(e => e.name))) ?? [] + }; objectTypes.push(type); - state.currentType = type; - }, - NonNullType: (_node: NonNullTypeNode) => { - state.nonNullType = true; - }, - NamedType: (node: NamedTypeNode) => { - extractNamedType(node, state); - }, - ListType: (_node: ListTypeNode) => { - extractListType(state); - }, - FieldDefinition: (node: FieldDefinitionNode) => { - extractFieldDefinition(node, state); - }, -}); - -const visitorLeave = (state: State) => ({ - ObjectTypeDefinition: (_node: ObjectTypeDefinitionNode) => { - state.currentType = undefined; - }, - FieldDefinition: (_node: FieldDefinitionNode) => { - state.currentProperty = undefined; - }, - NonNullType: (_node: NonNullTypeNode) => { - state.nonNullType = undefined; }, }); export const getObjectTypesVisitor = (abi: WrapAbi): ASTVisitor => { - const state: State = {}; - return { - enter: visitorEnter(abi.objectTypes || [], state), - leave: visitorLeave(state), + // TODO: Ensure enums were extracted previously + enter: visitorEnter(abi.objectTypes || [], abi.enumTypes), }; }; diff --git a/packages/schema/parse/src/extract/utils/map-utils.ts b/packages/schema/parse/src/extract/utils/map-utils.ts index cbc70f0efb..5131e3e6a2 100644 --- a/packages/schema/parse/src/extract/utils/map-utils.ts +++ b/packages/schema/parse/src/extract/utils/map-utils.ts @@ -1,191 +1,156 @@ import { - createArrayDefinition, - createMapDefinition, - createMapKeyDefinition, - createScalarDefinition, - createUnresolvedObjectOrEnumRef, - isMapKeyType, isScalarType, } from "../.."; -import { - GenericDefinition, - MapKeyDefinition, - ScalarDefinition, -} from "@polywrap/wrap-manifest-types-js"; +import { Reference, SUPPORTED_MAP_KEYS, SUPPORTED_SCALARS } from "../../definitions"; -type CurrentAbi = { - currentType: string; - subType: string | undefined; - required: boolean | undefined; -}; // TODO: Make sure map also works for imported types and modules -const _parseCurrentType = (rootType: string, type: string): CurrentAbi => { - let required = undefined; - if (type.startsWith("[")) { - const closeSquareBracketIdx = type.lastIndexOf("]"); - if (type[closeSquareBracketIdx + 1] === "!") { - required = true; - } +export const parseMapReference = (typeName: string, enumDefs: string[]): Reference => { + if (isArray(typeName)) { + const closeSquareBracketIdx = typeName.lastIndexOf("]"); + const required = typeName[closeSquareBracketIdx + 1] === "!" return { - currentType: "Array", - subType: type.substring(1, closeSquareBracketIdx), - required: required, + kind: "Array", + required, + definition: { + items: parseMapReference(typeName.substring(1, closeSquareBracketIdx), enumDefs), + kind: "Array", + name: "" + } }; - } - - let hasSubType = true; - const openAngleBracketIdx = type.indexOf("<"); - const closeAngleBracketIdx = type.lastIndexOf(">"); - - if ( - (openAngleBracketIdx === -1 && closeAngleBracketIdx !== -1) || - (openAngleBracketIdx !== -1 && closeAngleBracketIdx === -1) - ) { - throw new Error(`Invalid map value type: ${rootType}`); - } - - if (openAngleBracketIdx === -1 && closeAngleBracketIdx === -1) { - if (type === "Array" || type === "Map") { - throw new Error(`Invalid map value type: ${rootType}`); + } else if (isMap(typeName)) { + const openAngleBracketIdx = typeName.indexOf("<"); + const closeAngleBracketIdx = typeName.lastIndexOf(">"); + + if ( + closeAngleBracketIdx === -1 + ) { + throw new Error(`Invalid map value type: ${typeName}`); } - if (type.endsWith("!")) { - required = true; - } - hasSubType = false; - } - if (type[closeAngleBracketIdx + 1] === "!") { - required = true; - } + const required = typeName.endsWith("!"); + const subtype = typeName.substring(openAngleBracketIdx + 1, closeAngleBracketIdx); - return { - currentType: hasSubType - ? type.substring(0, openAngleBracketIdx) - : required - ? type.substring(0, type.length - 1) - : type, - subType: hasSubType - ? type.substring(openAngleBracketIdx + 1, closeAngleBracketIdx) - : undefined, - required: required, - }; -}; - -const _toGraphQLType = (rootType: string, type: string): string => { - const parsedCurrentType = _parseCurrentType(rootType, type); - let { subType } = parsedCurrentType; - const { currentType } = parsedCurrentType; - - if (!subType) { - return currentType; - } + const firstDelimiter = subtype.indexOf(","); - switch (currentType) { - case "Array": { - if (subType.endsWith("!")) { - subType = subType.slice(0, -1); - } - return `[${_toGraphQLType(rootType, subType)}]`; + const _keyType = subtype.substring(0, firstDelimiter).trim(); + const valType = subtype.substring(firstDelimiter + 1).trim(); + + if (!_keyType || !valType) { + throw new Error(`Invalid map value type: ${typeName}`); } - case "Map": { - const firstDelimiter = subType.indexOf(","); - const keyType = subType.substring(0, firstDelimiter).trim(); - const valType = subType.substring(firstDelimiter + 1).trim(); + // TODO: Is there a better way to enforce this -> Map key should always be required + // TODO: Should we throw an error if it's not? + const keyRequired = true; + const keyType = _keyType.endsWith("!") ? _keyType.slice(0, -1) : _keyType; - return `Map<${_toGraphQLType(rootType, keyType)}, ${_toGraphQLType( - rootType, - valType - )}>`; - } - default: + if (!isMapKey(keyType)) { throw new Error( - `Found unknown type ${currentType} while parsing ${rootType}` + `Found invalid map key type: ${keyType} while parsing ${typeName}` ); - } -}; - -const _parseMapType = ( - rootType: string, - type: string, - name?: string -): GenericDefinition => { - const { currentType, subType, required } = _parseCurrentType(rootType, type); - - if (!subType) { - if (isScalarType(currentType)) { - return createScalarDefinition({ - name: name, - type: currentType as ScalarDefinition["type"], - required: required, - }); } - return createUnresolvedObjectOrEnumRef({ - name: name, - type: currentType, - required: required, - }); - } - - switch (currentType) { - case "Array": { - return createArrayDefinition({ - name: name, - type: _toGraphQLType(rootType, type), - item: _parseMapType(rootType, subType, name), - required: required, - }); + return { + kind: "Map", + definition: { + name: "", + kind: "Map", + //TODO: validate possible keys + keys: { + kind: "Scalar", + type: keyType as typeof SUPPORTED_SCALARS[number], + required: keyRequired + }, + values: parseMapReference(valType, enumDefs) + }, + required, } - case "Map": { - const firstDelimiter = subType.indexOf(","); - - const _keyType = subType.substring(0, firstDelimiter).trim(); - const valType = subType.substring(firstDelimiter + 1).trim(); + } else if (isScalarType(typeName)) { + const required = typeName.endsWith("!"); + const subtype = required ? typeName.substring(0, typeName.length - 1) : typeName - if (!_keyType || !valType) { - throw new Error(`Invalid map value type: ${rootType}`); - } - - // TODO: Is there a better way to enforce this -> Map key should always be required - // TODO: Should we throw an error if it's not? - const keyRequired = true; - const keyType = _keyType.endsWith("!") ? _keyType.slice(0, -1) : _keyType; + return { + kind: "Scalar", + type: subtype as typeof SUPPORTED_SCALARS[number], + required + } + } else if (isEnum(typeName, enumDefs)) { + const required = typeName.endsWith("!"); - if (!isMapKeyType(keyType)) { - throw new Error( - `Found invalid map key type: ${keyType} while parsing ${rootType}` - ); - } + return { + type: enumDefs.find(e => e === typeName) as string, + kind: "Enum", + required + } + } else { + const required = typeName.endsWith("!"); - return createMapDefinition({ - type: _toGraphQLType(rootType, type), - name: name, - key: createMapKeyDefinition({ - name: name, - type: keyType as MapKeyDefinition["type"], - required: keyRequired, - }), - value: _parseMapType(rootType, valType, name), - required: required, - }); + return { + type: typeName, + kind: "Object", + required } - default: - throw new Error(`Invalid map value type: ${type}`); } -}; + // TODO: is this case necessary? + // else { + // throw new Error(`Unrecognized reference type '${typeName}'`) + // } +} + +const isMap = (typeName: string): boolean => { + //TODO: would this be the right condition? + return typeName.startsWith("Map<") +} -export function parseCurrentType(type: string): CurrentAbi { - return _parseCurrentType(type, type); +const isEnum = (typeName: string, enumDefs: string[]): boolean => { + return !!enumDefs.find(o => o === typeName); } -export function parseMapType(type: string, name?: string): GenericDefinition { - return _parseMapType(type, type, name); +const isMapKey = (typeName: string): boolean => { + return SUPPORTED_MAP_KEYS.includes(typeName as typeof SUPPORTED_MAP_KEYS[number]); } -export function toGraphQLType(type: string): string { - return _toGraphQLType(type, type); +const isArray = (typeName: string): boolean => { + return typeName.startsWith("[") } + +// const _toGraphQLType = (rootType: string, type: string): string => { +// const parsedCurrentType = _parseCurrentType(rootType, type); +// let { subType } = parsedCurrentType; +// const { currentType } = parsedCurrentType; + +// if (!subType) { +// return currentType; +// } + +// switch (currentType) { +// case "Array": { +// if (subType.endsWith("!")) { +// subType = subType.slice(0, -1); +// } +// return `[${_toGraphQLType(rootType, subType)}]`; +// } +// case "Map": { +// const firstDelimiter = subType.indexOf(","); + +// const keyType = subType.substring(0, firstDelimiter).trim(); +// const valType = subType.substring(firstDelimiter + 1).trim(); + +// return `Map<${_toGraphQLType(rootType, keyType)}, ${_toGraphQLType( +// rootType, +// valType +// )}>`; +// } +// default: +// throw new Error( +// `Found unknown type ${currentType} while parsing ${rootType}` +// ); +// } +// }; + +// export function toGraphQLType(type: string): string { +// return _toGraphQLType(type, type); +// } diff --git a/packages/schema/parse/src/extract/utils/object-types-utils.ts b/packages/schema/parse/src/extract/utils/object-types-utils.ts deleted file mode 100644 index 0fd8fa4b89..0000000000 --- a/packages/schema/parse/src/extract/utils/object-types-utils.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { createArrayDefinition, createPropertyDefinition } from "../.."; -import { parseMapType } from "./map-utils"; -import { setPropertyType } from "./property-utils"; - -import { - FieldDefinitionNode, - InputValueDefinitionNode, - NamedTypeNode, - StringValueNode, -} from "graphql"; -import { - GenericDefinition, - MapDefinition, - ObjectDefinition, - PropertyDefinition, -} from "@polywrap/wrap-manifest-types-js"; - -export interface State { - currentType?: ObjectDefinition; - currentProperty?: PropertyDefinition; - nonNullType?: boolean; -} - -export interface AnnotatedDefinition { - type?: string; - def?: GenericDefinition; -} - -export function extractAnnotateDirective( - node: FieldDefinitionNode | InputValueDefinitionNode, - name: string -): AnnotatedDefinition { - let type: string | undefined; - let def: GenericDefinition | undefined; - - if (node.directives) { - for (const dir of node.directives) { - if (dir.name.value === "annotate") { - type = (dir.arguments?.find((arg) => arg.name.value === "type") - ?.value as StringValueNode).value; - if (!type) { - throw new Error( - `Annotate directive: ${node.name.value} has invalid arguments` - ); - } - def = parseMapType(type, name); - } - } - } - - return { type, def }; -} - -export function extractFieldDefinition( - node: FieldDefinitionNode, - state: State -): void { - const importDef = state.currentType; - - if (!importDef) { - return; - } - - if (node.arguments && node.arguments.length > 0) { - throw Error( - `Imported types cannot have methods. See type "${importDef.name}"` - ); - } - - const name = node.name.value; - const { type, def } = extractAnnotateDirective(node, name); - - const property = createPropertyDefinition({ - type: type ? type : "N/A", - name: name, - map: def ? (def as MapDefinition) : undefined, - comment: node.description?.value, - required: def && def.required, - }); - - state.currentProperty = property; - if (!importDef.properties) { - importDef.properties = []; - } - importDef.properties.push(property); -} - -export function extractNamedType(node: NamedTypeNode, state: State): void { - const property = state.currentProperty; - - if (!property) { - return; - } - - if (property.scalar) { - return; - } - - if (!property.name) { - throw Error( - "extractNamedType: Invalid state. Uninitialized currentProperty, name not found.\n" + - `Method: ${JSON.stringify(property, null, 2)}\nState: ${JSON.stringify( - state, - null, - 2 - )}` - ); - } - - setPropertyType(property, property.name, { - type: node.name.value, - required: state.nonNullType, - }); - - state.nonNullType = undefined; -} - -export function extractListType(state: State): void { - const property = state.currentProperty; - - if (!property) { - return; - } - - if (property.scalar) { - return; - } - - property.array = createArrayDefinition({ - name: property.name, - type: "N/A", - required: state.nonNullType, - }); - state.currentProperty = property.array; - state.nonNullType = undefined; -} diff --git a/packages/schema/parse/src/extract/utils/property-utils.ts b/packages/schema/parse/src/extract/utils/property-utils.ts deleted file mode 100644 index 2748c407d5..0000000000 --- a/packages/schema/parse/src/extract/utils/property-utils.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - createScalarDefinition, - createMapDefinition, - createUnresolvedObjectOrEnumRef, - isScalarType, -} from "../.."; - -import { - PropertyDefinition, - ScalarDefinition, -} from "@polywrap/wrap-manifest-types-js"; - -const toBoolean = (val: unknown) => !!val; - -export function setPropertyType( - property: PropertyDefinition, - name: string, - type: { - type: string; - required: boolean | undefined; - } -): void { - if (isScalarType(type.type)) { - property.scalar = createScalarDefinition({ - name: name, - type: type.type as ScalarDefinition["type"], - required: type.required, - }); - return; - } - - if (type.type === "Map") { - if (toBoolean(type.required) !== toBoolean(property.map?.required)) { - throw new Error( - `Map defined as ${ - type.required ? "required" : "optional" - } while declaring type but defined as ${ - property.required ? "required" : "optional" - } while annotating: ${property.type} in ${property.name}` - ); - } - - property.map = { - ...createMapDefinition({ - type: type.type, - }), - ...property.map, - name: name, - required: type.required, - }; - return; - } - - property.unresolvedObjectOrEnum = createUnresolvedObjectOrEnumRef({ - name: name, - type: type.type, - required: type.required, - }); -} From 0b6b94e55d7fa8bc3cb9d531b70af14252a701e4 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Wed, 4 Jan 2023 17:41:32 +0100 Subject: [PATCH 04/90] (wip): updated 0.2 defintions and map parsing --- packages/schema/parse/src/definitions.ts | 245 +++++++++--------- .../schema/parse/src/extract/object-types.ts | 63 +++-- .../parse/src/extract/utils/map-utils.ts | 142 +++++----- 3 files changed, 235 insertions(+), 215 deletions(-) diff --git a/packages/schema/parse/src/definitions.ts b/packages/schema/parse/src/definitions.ts index 67779568fe..dd40d3c6c8 100644 --- a/packages/schema/parse/src/definitions.ts +++ b/packages/schema/parse/src/definitions.ts @@ -1,153 +1,164 @@ -//TODO: interfaces and capabilities +/// ABIs -export const SUPPORTED_SCALARS = [ - "UInt", - "UInt8", - "UInt16", - "UInt32", - "Int", - "Int8", - "Int16", - "Int32", - "String", - "Boolean", - "Bytes", - "BigInt", - "BigNumber", - "JSON", -] as const +export interface Abi extends AbiDefs { + version: "0.2"; + imports?: ImportedAbi[]; +} + +export interface ImportedAbi extends AbiDefs { + namespace: string; + uri: string; +} -export type Scalar = typeof SUPPORTED_SCALARS[number]; +export interface AbiDefs { + functions?: FunctionDef[]; + objects?: ObjectDef[]; + enums?: EnumDef[]; + env?: EnvDef; +} -export const SUPPORTED_MAP_KEYS = [ - "UInt", - "UInt8", - "UInt16", - "UInt32", - "Int", - "Int8", - "Int16", - "Int32", - "String" -] as const +/// Definitions (user-defined) -export type MapKeyType = typeof SUPPORTED_MAP_KEYS[number]; +export type UniqueDefKind = + | "Function" + | "Object" + | "Enum" + | "Env"; -export interface Definition { - name: string; - kind: string; - comment?: string; -} +export type DefKind = + | UniqueDefKind + | "Argument" + | "Result" + | "Property"; -export interface Imported { - uri: "test-interface.eth", - namespace: "Interface", - imported: true; +export interface Def { + kind: DefKind; } -export type Reference = { - kind: "Scalar" | "Object" | "Enum" - required: boolean - type: string -} | ArrayReference | MapReference - -export interface ArrayReference { - kind: "Array" - definition: ArrayDefinition - required: boolean +export interface NamedDef extends Def { + name: string; + comment?: string; } -export interface MapReference { - kind: "Map" - definition: MapDefinition - required: boolean +export interface TypeDef extends Def { + required: boolean; + type: AnyType; } -// Definitions +export interface NamedTypeDef extends NamedDef, TypeDef { } -export interface ScalarDefinition extends Definition { - kind: "Scalar"; - name: Scalar; +export interface FunctionDef extends NamedDef { + kind: "Function"; + args: ArgumentDef[]; + result: ResultDef; } -export interface ObjectDefinition extends Definition { - kind: "Object"; - properties: ObjectProperty[] +export interface ArgumentDef extends NamedTypeDef { + kind: "Argument"; } -export interface EnvDefinition extends Definition { - kind: "Env"; - properties: ObjectProperty[] +export interface ResultDef extends TypeDef { + kind: "Result"; } -export interface EnumDefinition extends Definition { - kind: "Enum"; - constants: string[]; +export interface ObjectDef extends NamedDef { + kind: "Object"; + props: PropertyDef[]; } -export interface MethodDefinition extends Definition { - kind: "Method"; - arguments: MethodArgument[]; - env?: { - required?: boolean - }; - return: Reference +export interface PropertyDef extends NamedTypeDef { + kind: "Property"; } -export interface ModuleDefinition extends Definition { - kind: "Module" - methods: MethodDefinition[] +export interface EnumDef extends NamedDef { + kind: "Enum"; + constants: string[]; } -// Special cases: References with embedded definitions - -export interface MapDefinition extends Definition { - name: "" - kind: "Map", - keys: { - kind: "Scalar" - required: boolean - type: string - }, - values: Reference +export interface EnvDef extends NamedDef { + kind: "Env"; + name: "Env"; + props: PropertyDef[]; } -export interface ArrayDefinition extends Definition { - name: "" - kind: "Array" - items: Reference; -} +/// Types (built-ins) + +export type AnyType = + | ScalarType + | ArrayType + | MapType + | RefType; -// Helpers +export type TypeKind = + | "Scalar" + | "Array" + | "Map" + | "Ref"; -export interface ObjectProperty { - comment?: string; - name: string; - type: Reference +export interface Type { + kind: TypeKind; } -export interface MethodArgument { - name: string; - type: Reference; +export interface ScalarType< + TScalarTypeName extends ScalarTypeName = ScalarTypeName +> extends Type { + kind: "Scalar"; + scalar: TScalarTypeName; } -// Exports +export interface ArrayType extends Type { + kind: "Array"; + required: boolean; + item: AnyType; +} + +export interface MapType extends Type { + kind: "Map"; + key: ScalarType; + required: boolean; + value: AnyType; +} -export interface WrapManifest { - version: "0.1.0" | "0.1"; - type: "wasm" | "interface" | "plugin"; - name: string; - abi: Abi; +export interface RefType extends Type { + kind: "Ref"; + ref_kind: UniqueDefKind; + ref_name: string; } -export interface Abi { - version: "0.1"; - objectTypes: ObjectDefinition[]; - moduleType?: ModuleDefinition; - enumTypes: EnumDefinition[]; - importedObjectTypes: (ObjectDefinition & Imported)[]; - importedModuleTypes: (ModuleDefinition & Imported)[]; - importedEnumTypes: (EnumDefinition & Imported)[]; - importedEnvTypes: (EnvDefinition & Imported)[]; - envType?: EnvDefinition; -} \ No newline at end of file +/// Constants + +export const scalarTypeSet = { + UInt: "UInt", + UInt8: "UInt8", + UInt16: "UInt16", + UInt32: "UInt32", + Int: "Int", + Int8: "Int8", + Int16: "Int16", + Int32: "Int32", + String: "String", + Boolean: "Boolean", + Bytes: "Bytes", + // TODO: remove complex types + BigInt: "BigInt", + BigNumber: "BigNumber", + JSON: "JSON", +}; +export type ScalarTypeSet = typeof scalarTypeSet; + +export type ScalarTypeName = keyof ScalarTypeSet; + +export const mapKeyTypeSet = { + UInt: "UInt", + UInt8: "UInt8", + UInt16: "UInt16", + UInt32: "UInt32", + Int: "Int", + Int8: "Int8", + Int16: "Int16", + Int32: "Int32", + String: "String", +}; +export type MapKeyTypeSet = typeof mapKeyTypeSet; + +export type MapKeyTypeName = keyof MapKeyTypeSet; diff --git a/packages/schema/parse/src/extract/object-types.ts b/packages/schema/parse/src/extract/object-types.ts index 17cbbf3388..9f626d45b8 100644 --- a/packages/schema/parse/src/extract/object-types.ts +++ b/packages/schema/parse/src/extract/object-types.ts @@ -1,6 +1,7 @@ import { isEnvType, isModuleType, + isScalarType, } from ".."; import { @@ -11,29 +12,53 @@ import { StringValueNode, TypeNode, } from "graphql"; -import { ObjectDefinition, Abi as WrapAbi, ObjectProperty, Reference, EnumDefinition } from "../definitions"; +import { ObjectDef, Abi as WrapAbi, PropertyDef, AnyType, EnumDef, ScalarTypeName } from "../definitions"; import { parseMapReference } from "./utils/map-utils"; -const extractObjectProperty = (node: FieldDefinitionNode, enumDefs: string[]): ObjectProperty => { - const extractType = (node: TypeNode, required = false): Reference => { +const extractPropertyDef = (node: FieldDefinitionNode, enumDefs: string[]): PropertyDef => { + // const extractType = (node: TypeNode, required = false): AnyType => { + // switch (node.kind) { + // case "NonNullType": + // return extractType(node.type, true) + // case "ListType": + // return { + // kind: "Array", + // item: extractType(node.type), + // required + // } + // case "NamedType": + // return { + // kind: "Ref", + // // TODO: Revisit this condition as it is not future proof + // ref_kind: enumDefs.includes(node.name.value) ? "Enum" : "Object", + // ref_name: node.name.value + // } + // } + // } + + const extractType = (node: TypeNode): AnyType => { switch (node.kind) { case "NonNullType": - return extractType(node.type, true) + return extractType(node.type) case "ListType": return { kind: "Array", - required, - definition: { - kind: "Array", - items: extractType(node.type), - name: "", - } + required: node.type.kind === "NonNullType", + item: extractType(node.type) } case "NamedType": + if (isScalarType(node.name.value)) { + return { + kind: "Scalar", + scalar: node.name.value as ScalarTypeName + } + } + return { - required, - kind: enumDefs.includes(node.name.value) ? "Enum" : "Object", - type: node.name.value + kind: "Ref", + // TODO: Revisit this condition as it is not future proof + ref_kind: enumDefs.includes(node.name.value) ? "Enum" : "Object", + ref_name: node.name.value } } } @@ -49,6 +74,8 @@ const extractObjectProperty = (node: FieldDefinitionNode, enumDefs: string[]): O ); } return { + kind: "Property", + required: node.type.kind === "NonNullType", name: node.name.value, type: parseMapReference(typeName, enumDefs) } @@ -57,12 +84,14 @@ const extractObjectProperty = (node: FieldDefinitionNode, enumDefs: string[]): O } return { + kind: "Property", name: node.name.value, + required: node.type.kind === "NonNullType", type: extractType(node.type) } } -const visitorEnter = (objectTypes: ObjectDefinition[], enumDefs: EnumDefinition[]) => ({ +const visitorEnter = (objectTypes: ObjectDef[], enumDefs: EnumDef[]) => ({ ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { const typeName = node.name.value; @@ -87,11 +116,11 @@ const visitorEnter = (objectTypes: ObjectDefinition[], enumDefs: EnumDefinition[ // ); // Create a new TypeDefinition - const type = { - kind: "Object" as const, + const type: ObjectDef = { + kind: "Object", comment: node.description?.value, name: typeName, - properties: node.fields?.map(fieldNode => extractObjectProperty(fieldNode, enumDefs.map(e => e.name))) ?? [] + props: node.fields?.map(fieldNode => extractPropertyDef(fieldNode, enumDefs.map(e => e.name))) ?? [] }; objectTypes.push(type); }, diff --git a/packages/schema/parse/src/extract/utils/map-utils.ts b/packages/schema/parse/src/extract/utils/map-utils.ts index 5131e3e6a2..e060af96f6 100644 --- a/packages/schema/parse/src/extract/utils/map-utils.ts +++ b/packages/schema/parse/src/extract/utils/map-utils.ts @@ -1,103 +1,83 @@ import { isScalarType, } from "../.."; - -import { Reference, SUPPORTED_MAP_KEYS, SUPPORTED_SCALARS } from "../../definitions"; +import { AnyType, MapKeyTypeName, mapKeyTypeSet, MapType, ScalarTypeName } from "../../definitions"; // TODO: Make sure map also works for imported types and modules -export const parseMapReference = (typeName: string, enumDefs: string[]): Reference => { - if (isArray(typeName)) { - const closeSquareBracketIdx = typeName.lastIndexOf("]"); - const required = typeName[closeSquareBracketIdx + 1] === "!" - return { - kind: "Array", - required, - definition: { - items: parseMapReference(typeName.substring(1, closeSquareBracketIdx), enumDefs), +export const parseMapReference = (typeName: string, enumDefs: string[]): MapType => { + const extractType = (typeName: string): AnyType => { + if (isArray(typeName)) { + const closeSquareBracketIdx = typeName.lastIndexOf("]"); + const required = typeName[closeSquareBracketIdx + 1] === "!" + return { kind: "Array", - name: "" + required, + item: extractType(typeName.substring(1, closeSquareBracketIdx)) + }; + } else if (isMap(typeName)) { + return parseMapReference(typeName, enumDefs) + } else if (isScalarType(typeName)) { + return { + kind: "Scalar", + scalar: typeName as ScalarTypeName, + } + } else { + return { + kind: "Ref", + ref_kind: isEnum(typeName, enumDefs) ? "Enum" : "Object", + ref_name: typeName, } - }; - } else if (isMap(typeName)) { - const openAngleBracketIdx = typeName.indexOf("<"); - const closeAngleBracketIdx = typeName.lastIndexOf(">"); - - if ( - closeAngleBracketIdx === -1 - ) { - throw new Error(`Invalid map value type: ${typeName}`); } + // TODO: is this case necessary? + // else { + // throw new Error(`Unrecognized reference type '${typeName}'`) + // } + } - const required = typeName.endsWith("!"); - const subtype = typeName.substring(openAngleBracketIdx + 1, closeAngleBracketIdx); - - const firstDelimiter = subtype.indexOf(","); + const openAngleBracketIdx = typeName.indexOf("<"); + const closeAngleBracketIdx = typeName.lastIndexOf(">"); - const _keyType = subtype.substring(0, firstDelimiter).trim(); - const valType = subtype.substring(firstDelimiter + 1).trim(); + if ( + closeAngleBracketIdx === -1 + ) { + throw new Error(`Invalid map value type: ${typeName}`); + } - if (!_keyType || !valType) { - throw new Error(`Invalid map value type: ${typeName}`); - } + const subtype = typeName.substring(openAngleBracketIdx + 1, closeAngleBracketIdx); - // TODO: Is there a better way to enforce this -> Map key should always be required - // TODO: Should we throw an error if it's not? - const keyRequired = true; - const keyType = _keyType.endsWith("!") ? _keyType.slice(0, -1) : _keyType; + const firstDelimiter = subtype.indexOf(","); - if (!isMapKey(keyType)) { - throw new Error( - `Found invalid map key type: ${keyType} while parsing ${typeName}` - ); - } + const _keyType = subtype.substring(0, firstDelimiter).trim(); + const _valType = subtype.substring(firstDelimiter + 1).trim(); - return { - kind: "Map", - definition: { - name: "", - kind: "Map", - //TODO: validate possible keys - keys: { - kind: "Scalar", - type: keyType as typeof SUPPORTED_SCALARS[number], - required: keyRequired - }, - values: parseMapReference(valType, enumDefs) - }, - required, - } - } else if (isScalarType(typeName)) { - const required = typeName.endsWith("!"); - const subtype = required ? typeName.substring(0, typeName.length - 1) : typeName + if (!_keyType || !_valType) { + throw new Error(`Invalid map value type: ${typeName}`); + } - return { - kind: "Scalar", - type: subtype as typeof SUPPORTED_SCALARS[number], - required - } - } else if (isEnum(typeName, enumDefs)) { - const required = typeName.endsWith("!"); + // TODO: Is there a better way to enforce this -> Map key should always be required + // TODO: Should we throw an error if it's not? + // const keyRequired = true; + const keyType = _keyType.endsWith("!") ? _keyType.slice(0, -1) : _keyType; + const valType = _valType.endsWith("!") ? _valType.slice(0, -1) : _valType; - return { - type: enumDefs.find(e => e === typeName) as string, - kind: "Enum", - required - } - } else { - const required = typeName.endsWith("!"); + if (!isMapKey(keyType)) { + throw new Error( + `Found invalid map key type: ${keyType} while parsing ${typeName}` + ); + } - return { - type: typeName, - kind: "Object", - required - } + return { + kind: "Map", + key: { + kind: "Scalar", + scalar: keyType as MapKeyTypeName + }, + value: extractType(valType), + required: valType.endsWith("!"), } - // TODO: is this case necessary? - // else { - // throw new Error(`Unrecognized reference type '${typeName}'`) - // } + } const isMap = (typeName: string): boolean => { @@ -110,7 +90,7 @@ const isEnum = (typeName: string, enumDefs: string[]): boolean => { } const isMapKey = (typeName: string): boolean => { - return SUPPORTED_MAP_KEYS.includes(typeName as typeof SUPPORTED_MAP_KEYS[number]); + return typeName in mapKeyTypeSet; } const isArray = (typeName: string): boolean => { From 111a76b3086570bf3809a7aaaa9ad109e9ec5878 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Thu, 5 Jan 2023 11:43:12 +0100 Subject: [PATCH 05/90] (fix): object type extractors --- .../schema/parse/src/extract/object-types.ts | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/packages/schema/parse/src/extract/object-types.ts b/packages/schema/parse/src/extract/object-types.ts index 9f626d45b8..0cb934e000 100644 --- a/packages/schema/parse/src/extract/object-types.ts +++ b/packages/schema/parse/src/extract/object-types.ts @@ -16,26 +16,6 @@ import { ObjectDef, Abi as WrapAbi, PropertyDef, AnyType, EnumDef, ScalarTypeNam import { parseMapReference } from "./utils/map-utils"; const extractPropertyDef = (node: FieldDefinitionNode, enumDefs: string[]): PropertyDef => { - // const extractType = (node: TypeNode, required = false): AnyType => { - // switch (node.kind) { - // case "NonNullType": - // return extractType(node.type, true) - // case "ListType": - // return { - // kind: "Array", - // item: extractType(node.type), - // required - // } - // case "NamedType": - // return { - // kind: "Ref", - // // TODO: Revisit this condition as it is not future proof - // ref_kind: enumDefs.includes(node.name.value) ? "Enum" : "Object", - // ref_name: node.name.value - // } - // } - // } - const extractType = (node: TypeNode): AnyType => { switch (node.kind) { case "NonNullType": @@ -129,6 +109,6 @@ const visitorEnter = (objectTypes: ObjectDef[], enumDefs: EnumDef[]) => ({ export const getObjectTypesVisitor = (abi: WrapAbi): ASTVisitor => { return { // TODO: Ensure enums were extracted previously - enter: visitorEnter(abi.objectTypes || [], abi.enumTypes), + enter: visitorEnter(abi.objects ?? [], abi.enums ?? []), }; }; From 8af84a77e9b8df92dd3839757948362730024320 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Thu, 5 Jan 2023 22:09:08 +0100 Subject: [PATCH 06/90] (wip): new extractors structure --- .../schema/parse/src/extract/object-types.ts | 23 ++--- .../parse/src/extract/utils/map-utils.ts | 17 +--- .../parse/src/extract/utils/refParser.ts | 15 +++ packages/schema/parse/src/extract2/enum.ts | 21 +++++ packages/schema/parse/src/extract2/env.ts | 29 ++++++ packages/schema/parse/src/extract2/object.ts | 91 +++++++++++++++++++ packages/schema/parse/src/extract2/types.ts | 6 ++ 7 files changed, 176 insertions(+), 26 deletions(-) create mode 100644 packages/schema/parse/src/extract/utils/refParser.ts create mode 100644 packages/schema/parse/src/extract2/enum.ts create mode 100644 packages/schema/parse/src/extract2/env.ts create mode 100644 packages/schema/parse/src/extract2/object.ts create mode 100644 packages/schema/parse/src/extract2/types.ts diff --git a/packages/schema/parse/src/extract/object-types.ts b/packages/schema/parse/src/extract/object-types.ts index 0cb934e000..8804a9a687 100644 --- a/packages/schema/parse/src/extract/object-types.ts +++ b/packages/schema/parse/src/extract/object-types.ts @@ -12,10 +12,11 @@ import { StringValueNode, TypeNode, } from "graphql"; -import { ObjectDef, Abi as WrapAbi, PropertyDef, AnyType, EnumDef, ScalarTypeName } from "../definitions"; +import { ObjectDef, Abi as WrapAbi, PropertyDef, AnyType, ScalarTypeName, UniqueDefKind } from "../definitions"; import { parseMapReference } from "./utils/map-utils"; +import { parseRef } from "./utils/refParser"; -const extractPropertyDef = (node: FieldDefinitionNode, enumDefs: string[]): PropertyDef => { +const extractPropertyDef = (node: FieldDefinitionNode, uniqueDefs: Map): PropertyDef => { const extractType = (node: TypeNode): AnyType => { switch (node.kind) { case "NonNullType": @@ -34,12 +35,7 @@ const extractPropertyDef = (node: FieldDefinitionNode, enumDefs: string[]): Prop } } - return { - kind: "Ref", - // TODO: Revisit this condition as it is not future proof - ref_kind: enumDefs.includes(node.name.value) ? "Enum" : "Object", - ref_name: node.name.value - } + return parseRef(node.name.value, uniqueDefs) } } @@ -57,7 +53,7 @@ const extractPropertyDef = (node: FieldDefinitionNode, enumDefs: string[]): Prop kind: "Property", required: node.type.kind === "NonNullType", name: node.name.value, - type: parseMapReference(typeName, enumDefs) + type: parseMapReference(typeName, uniqueDefs) } } } @@ -71,7 +67,7 @@ const extractPropertyDef = (node: FieldDefinitionNode, enumDefs: string[]): Prop } } -const visitorEnter = (objectTypes: ObjectDef[], enumDefs: EnumDef[]) => ({ +const visitorEnter = (objectTypes: ObjectDef[], uniqueDefs: Map) => ({ ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { const typeName = node.name.value; @@ -100,15 +96,14 @@ const visitorEnter = (objectTypes: ObjectDef[], enumDefs: EnumDef[]) => ({ kind: "Object", comment: node.description?.value, name: typeName, - props: node.fields?.map(fieldNode => extractPropertyDef(fieldNode, enumDefs.map(e => e.name))) ?? [] + props: node.fields?.map(fieldNode => extractPropertyDef(fieldNode, uniqueDefs)) ?? [] }; objectTypes.push(type); }, }); -export const getObjectTypesVisitor = (abi: WrapAbi): ASTVisitor => { +export const getObjectTypesVisitor = (abi: WrapAbi, uniqueDefs: Map): ASTVisitor => { return { - // TODO: Ensure enums were extracted previously - enter: visitorEnter(abi.objects ?? [], abi.enums ?? []), + enter: visitorEnter(abi.objects ?? [], uniqueDefs), }; }; diff --git a/packages/schema/parse/src/extract/utils/map-utils.ts b/packages/schema/parse/src/extract/utils/map-utils.ts index e060af96f6..5b4447c87f 100644 --- a/packages/schema/parse/src/extract/utils/map-utils.ts +++ b/packages/schema/parse/src/extract/utils/map-utils.ts @@ -1,12 +1,13 @@ import { isScalarType, } from "../.."; -import { AnyType, MapKeyTypeName, mapKeyTypeSet, MapType, ScalarTypeName } from "../../definitions"; +import { AnyType, MapKeyTypeName, mapKeyTypeSet, MapType, ScalarTypeName, UniqueDefKind } from "../../definitions"; +import { parseRef } from "./refParser"; // TODO: Make sure map also works for imported types and modules -export const parseMapReference = (typeName: string, enumDefs: string[]): MapType => { +export const parseMapReference = (typeName: string, uniqueDefs: Map): MapType => { const extractType = (typeName: string): AnyType => { if (isArray(typeName)) { const closeSquareBracketIdx = typeName.lastIndexOf("]"); @@ -17,18 +18,14 @@ export const parseMapReference = (typeName: string, enumDefs: string[]): MapType item: extractType(typeName.substring(1, closeSquareBracketIdx)) }; } else if (isMap(typeName)) { - return parseMapReference(typeName, enumDefs) + return parseMapReference(typeName, uniqueDefs) } else if (isScalarType(typeName)) { return { kind: "Scalar", scalar: typeName as ScalarTypeName, } } else { - return { - kind: "Ref", - ref_kind: isEnum(typeName, enumDefs) ? "Enum" : "Object", - ref_name: typeName, - } + return parseRef(typeName, uniqueDefs) } // TODO: is this case necessary? // else { @@ -85,10 +82,6 @@ const isMap = (typeName: string): boolean => { return typeName.startsWith("Map<") } -const isEnum = (typeName: string, enumDefs: string[]): boolean => { - return !!enumDefs.find(o => o === typeName); -} - const isMapKey = (typeName: string): boolean => { return typeName in mapKeyTypeSet; } diff --git a/packages/schema/parse/src/extract/utils/refParser.ts b/packages/schema/parse/src/extract/utils/refParser.ts new file mode 100644 index 0000000000..0e387b0e37 --- /dev/null +++ b/packages/schema/parse/src/extract/utils/refParser.ts @@ -0,0 +1,15 @@ +import { RefType, UniqueDefKind } from "../../definitions"; + +export const parseRef = (refName: string, uniqueDefs: Map): RefType => { + const kind = uniqueDefs.get(refName); + + if (!kind) { + throw new Error(`Found ref to unknown definition '${refName}'`) + } + + return { + kind: "Ref", + ref_kind: kind, + ref_name: refName + } +} \ No newline at end of file diff --git a/packages/schema/parse/src/extract2/enum.ts b/packages/schema/parse/src/extract2/enum.ts new file mode 100644 index 0000000000..c00c0c5aa6 --- /dev/null +++ b/packages/schema/parse/src/extract2/enum.ts @@ -0,0 +1,21 @@ +import { ASTVisitor, EnumTypeDefinitionNode } from "graphql"; +import { Abi, EnumDef } from "../definitions"; +import { VisitorBuilder } from "./types"; + +export class EnumVisitorBuilder implements VisitorBuilder { + build(abi: Abi): ASTVisitor { + return { + enter: { + EnumTypeDefinition: (node: EnumTypeDefinitionNode) => { + const def: EnumDef = { + name: node.name.value, + kind: "Enum", + constants: node.values?.map(value => value.name.value) ?? [] + } + + abi.enums = abi.enums ? [...abi.enums, def] : [def]; + }, + }, + }; + } +} \ No newline at end of file diff --git a/packages/schema/parse/src/extract2/env.ts b/packages/schema/parse/src/extract2/env.ts new file mode 100644 index 0000000000..5aadf0c124 --- /dev/null +++ b/packages/schema/parse/src/extract2/env.ts @@ -0,0 +1,29 @@ +import { ASTVisitor, ObjectTypeDefinitionNode } from "graphql"; +import { isEnvType } from "../abi"; +import { Abi, EnvDef } from "../definitions"; +import { ObjectVisitorBuilder } from "./object"; + +export class EnvVisitorBuilder extends ObjectVisitorBuilder { + build(abi: Abi): ASTVisitor { + return { + enter: { + ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { + const typeName = node.name.value; + + if (!isEnvType(typeName)) { + return; + } + + const def: EnvDef = { + kind: "Env", + name: "Env", + comment: node.description?.value, + props: node.fields?.map(fieldNode => this.extractPropertyDef(fieldNode, this.uniqueDefs)) ?? [] + }; + + abi.env = def; + }, + }, + }; + } +} \ No newline at end of file diff --git a/packages/schema/parse/src/extract2/object.ts b/packages/schema/parse/src/extract2/object.ts new file mode 100644 index 0000000000..17e82a4f5f --- /dev/null +++ b/packages/schema/parse/src/extract2/object.ts @@ -0,0 +1,91 @@ +import { ASTVisitor, FieldDefinitionNode, isScalarType, ObjectTypeDefinitionNode, StringValueNode, TypeNode } from "graphql"; +import { isModuleType, isEnvType } from "../abi"; +import { Abi, AnyType, ObjectDef, PropertyDef, ScalarTypeName, UniqueDefKind } from "../definitions"; +import { parseMapReference } from "../extract/utils/map-utils"; +import { parseRef } from "../extract/utils/refParser"; +import { VisitorBuilder } from "./types"; + +export class ObjectVisitorBuilder implements VisitorBuilder { + constructor(protected readonly uniqueDefs: Map) { } + + protected extractPropertyDef(node: FieldDefinitionNode, uniqueDefs: Map): PropertyDef { + const extractType = (node: TypeNode): AnyType => { + switch (node.kind) { + case "NonNullType": + return extractType(node.type) + case "ListType": + return { + kind: "Array", + required: node.type.kind === "NonNullType", + item: extractType(node.type) + } + case "NamedType": + if (isScalarType(node.name.value)) { + return { + kind: "Scalar", + scalar: node.name.value as ScalarTypeName + } + } + + return parseRef(node.name.value, uniqueDefs) + } + } + + if (node.directives) { + for (const dir of node.directives) { + if (dir.name.value === "annotate") { + const typeName = (dir.arguments?.find((arg) => arg.name.value === "type") + ?.value as StringValueNode).value; + if (!typeName) { + throw new Error( + `Annotate directive: ${node.name.value} has invalid arguments` + ); + } + return { + kind: "Property", + required: node.type.kind === "NonNullType", + name: node.name.value, + type: parseMapReference(typeName, uniqueDefs) + } + } + } + } + + return { + kind: "Property", + name: node.name.value, + required: node.type.kind === "NonNullType", + type: extractType(node.type) + } + } + + build(abi: Abi): ASTVisitor { + return { + enter: { + ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { + const typeName = node.name.value; + + // Skip non-custom types + if (isModuleType(typeName) || isEnvType(typeName)) { + return; + } + + // TODO: restore interfaces support + // const interfaces = node.interfaces?.map((x) => + // createInterfaceImplementedDefinition({ type: x.name.value }) + // ); + + // Create a new TypeDefinition + const def: ObjectDef = { + kind: "Object", + comment: node.description?.value, + name: typeName, + props: node.fields?.map(fieldNode => this.extractPropertyDef(fieldNode, this.uniqueDefs)) ?? [] + }; + + abi.objects = abi.objects ? [...abi.objects, def] : [def]; + }, + }, + }; + } +} \ No newline at end of file diff --git a/packages/schema/parse/src/extract2/types.ts b/packages/schema/parse/src/extract2/types.ts new file mode 100644 index 0000000000..951a7748a4 --- /dev/null +++ b/packages/schema/parse/src/extract2/types.ts @@ -0,0 +1,6 @@ +import { ASTVisitor } from "graphql"; +import { Abi } from "../definitions"; + +export interface VisitorBuilder { + build(abi: Abi): ASTVisitor +} \ No newline at end of file From 1c1ab95cc11ef33fe97fcba22df4b74d7ba6f63e Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Fri, 6 Jan 2023 05:57:07 +0100 Subject: [PATCH 07/90] (chore): new module and directives extractors --- .../schema/parse/src/extract2/directives.ts | 61 ++++++++++++++ packages/schema/parse/src/extract2/module.ts | 84 +++++++++++++++++++ packages/schema/parse/src/extract2/object.ts | 61 +++++++------- 3 files changed, 173 insertions(+), 33 deletions(-) create mode 100644 packages/schema/parse/src/extract2/directives.ts create mode 100644 packages/schema/parse/src/extract2/module.ts diff --git a/packages/schema/parse/src/extract2/directives.ts b/packages/schema/parse/src/extract2/directives.ts new file mode 100644 index 0000000000..19e900c103 --- /dev/null +++ b/packages/schema/parse/src/extract2/directives.ts @@ -0,0 +1,61 @@ +import { DirectiveNode, FieldDefinitionNode } from "graphql"; +import { MapType, UniqueDefKind } from "../definitions"; +import { parseMapString } from "../extract/utils/map-utils"; +import { EnvDirDefinition } from "../extract/utils/module-types-utils"; + +export const parseDirectivesInField = (node: FieldDefinitionNode, uniqueDefs: Map) => { + let map: MapType | undefined; + let env: EnvDirDefinition | undefined; + + if (node.directives) { + for (const dir of node.directives) { + switch (dir.name.value) { + case "annotate": + map = parseAnnotateDirective(dir, uniqueDefs); + break; + + case "env": + env = parseEnvDirective(dir) + } + } + } + + return { + map, + env + } +} + +export const parseAnnotateDirective = (node: DirectiveNode, uniqueDefs: Map): MapType => { + const mapStringValue = node.arguments?.find((arg) => arg.name.value === "type")?.value; + + if (!mapStringValue || mapStringValue.kind !== "StringValue") { + throw new Error( + `Annotate directive: ${node.name.value} has invalid arguments` + ); + } + + const mapString = mapStringValue.value; + + return parseMapString(mapString, uniqueDefs) +} + +export function parseEnvDirective( + node: DirectiveNode +): EnvDirDefinition { + const requiredValue = node.arguments?.find( + (arg) => arg.name.value === "required" + )?.value; + + if (!requiredValue || requiredValue.kind !== "BooleanValue") { + throw new Error( + `Env directive: ${node.name.value} has invalid arguments` + ); + } + + const required = requiredValue.value; + + return { + required, + }; +} \ No newline at end of file diff --git a/packages/schema/parse/src/extract2/module.ts b/packages/schema/parse/src/extract2/module.ts new file mode 100644 index 0000000000..16a9529e06 --- /dev/null +++ b/packages/schema/parse/src/extract2/module.ts @@ -0,0 +1,84 @@ +import { ASTVisitor, FieldDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; +import { isModuleType } from "../abi"; +import { Abi, UniqueDefKind, ResultDef, FunctionDef, ArgumentDef } from "../definitions"; +import { parseAnnotateDirective, parseDirectivesInField } from "./directives"; +import { extractType } from "./object"; +import { VisitorBuilder } from "./types"; + +export class ModuleVisitorBuilder implements VisitorBuilder { + constructor(protected readonly uniqueDefs: Map) { } + + private extractMethodFromFieldDefNode(node: FieldDefinitionNode): FunctionDef { + const { map, env } = parseDirectivesInField(node, this.uniqueDefs); + + const resultDef: ResultDef = { + kind: "Result", + required: node.type.kind === "NonNullType", + type: map ?? extractType(node.type, this.uniqueDefs) + } + + const args: ArgumentDef[] = node.arguments?.map(inputNode => { + if (inputNode.directives) { + for (const dir of inputNode.directives) { + if (dir.name.value === "annotate") { + const map = parseAnnotateDirective(dir, this.uniqueDefs); + + return { + kind: "Argument", + required: inputNode.type.kind === "NonNullType", + name: inputNode.name.value, + type: map + } + } + } + } + + return { + kind: "Argument", + name: inputNode.name.value, + required: inputNode.type.kind === "NonNullType", + type: extractType(inputNode.type, this.uniqueDefs) + } + }) ?? []; + + if (env) { + args.push({ + kind: "Argument", + name: "env", + required: env.required, + type: { + kind: "Ref", + ref_kind: "Env", + ref_name: "Env", + } + }) + } + + const method: FunctionDef = { + kind: "Function", + result: resultDef, + args, + name: node.name.value + } + + return method; + } + + build(abi: Abi): ASTVisitor { + return { + enter: { + ObjectTypeDefinition: (objectDefNode: ObjectTypeDefinitionNode) => { + const nodeName = objectDefNode.name.value; + + if (!isModuleType(nodeName)) { + return; + } + + const methods = objectDefNode.fields?.map(node => this.extractMethodFromFieldDefNode(node)) + + abi.functions = methods ?? [] + }, + }, + }; + } +} \ No newline at end of file diff --git a/packages/schema/parse/src/extract2/object.ts b/packages/schema/parse/src/extract2/object.ts index 17e82a4f5f..3a74011f99 100644 --- a/packages/schema/parse/src/extract2/object.ts +++ b/packages/schema/parse/src/extract2/object.ts @@ -1,51 +1,46 @@ -import { ASTVisitor, FieldDefinitionNode, isScalarType, ObjectTypeDefinitionNode, StringValueNode, TypeNode } from "graphql"; +import { ASTVisitor, FieldDefinitionNode, isScalarType, ObjectTypeDefinitionNode, TypeNode } from "graphql"; import { isModuleType, isEnvType } from "../abi"; import { Abi, AnyType, ObjectDef, PropertyDef, ScalarTypeName, UniqueDefKind } from "../definitions"; -import { parseMapReference } from "../extract/utils/map-utils"; import { parseRef } from "../extract/utils/refParser"; +import { parseAnnotateDirective } from "./directives"; import { VisitorBuilder } from "./types"; +export const extractType = (node: TypeNode, uniqueDefs: Map): AnyType => { + switch (node.kind) { + case "NonNullType": + return extractType(node.type, uniqueDefs) + case "ListType": + return { + kind: "Array", + required: node.type.kind === "NonNullType", + item: extractType(node.type, uniqueDefs) + } + case "NamedType": + if (isScalarType(node.name.value)) { + return { + kind: "Scalar", + scalar: node.name.value as ScalarTypeName + } + } + + return parseRef(node.name.value, uniqueDefs) + } +} + export class ObjectVisitorBuilder implements VisitorBuilder { constructor(protected readonly uniqueDefs: Map) { } protected extractPropertyDef(node: FieldDefinitionNode, uniqueDefs: Map): PropertyDef { - const extractType = (node: TypeNode): AnyType => { - switch (node.kind) { - case "NonNullType": - return extractType(node.type) - case "ListType": - return { - kind: "Array", - required: node.type.kind === "NonNullType", - item: extractType(node.type) - } - case "NamedType": - if (isScalarType(node.name.value)) { - return { - kind: "Scalar", - scalar: node.name.value as ScalarTypeName - } - } - - return parseRef(node.name.value, uniqueDefs) - } - } - if (node.directives) { for (const dir of node.directives) { if (dir.name.value === "annotate") { - const typeName = (dir.arguments?.find((arg) => arg.name.value === "type") - ?.value as StringValueNode).value; - if (!typeName) { - throw new Error( - `Annotate directive: ${node.name.value} has invalid arguments` - ); - } + const map = parseAnnotateDirective(dir, uniqueDefs); + return { kind: "Property", required: node.type.kind === "NonNullType", name: node.name.value, - type: parseMapReference(typeName, uniqueDefs) + type: map } } } @@ -55,7 +50,7 @@ export class ObjectVisitorBuilder implements VisitorBuilder { kind: "Property", name: node.name.value, required: node.type.kind === "NonNullType", - type: extractType(node.type) + type: extractType(node.type, uniqueDefs) } } From 3973f086befafe0726a5477cc93abfb04315ec64 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Fri, 6 Jan 2023 06:00:06 +0100 Subject: [PATCH 08/90] (chore): simplified property extraction in object extractor --- packages/schema/parse/src/extract2/object.ts | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/packages/schema/parse/src/extract2/object.ts b/packages/schema/parse/src/extract2/object.ts index 3a74011f99..9f4d20a3f0 100644 --- a/packages/schema/parse/src/extract2/object.ts +++ b/packages/schema/parse/src/extract2/object.ts @@ -2,7 +2,7 @@ import { ASTVisitor, FieldDefinitionNode, isScalarType, ObjectTypeDefinitionNode import { isModuleType, isEnvType } from "../abi"; import { Abi, AnyType, ObjectDef, PropertyDef, ScalarTypeName, UniqueDefKind } from "../definitions"; import { parseRef } from "../extract/utils/refParser"; -import { parseAnnotateDirective } from "./directives"; +import { parseDirectivesInField } from "./directives"; import { VisitorBuilder } from "./types"; export const extractType = (node: TypeNode, uniqueDefs: Map): AnyType => { @@ -31,26 +31,13 @@ export class ObjectVisitorBuilder implements VisitorBuilder { constructor(protected readonly uniqueDefs: Map) { } protected extractPropertyDef(node: FieldDefinitionNode, uniqueDefs: Map): PropertyDef { - if (node.directives) { - for (const dir of node.directives) { - if (dir.name.value === "annotate") { - const map = parseAnnotateDirective(dir, uniqueDefs); - - return { - kind: "Property", - required: node.type.kind === "NonNullType", - name: node.name.value, - type: map - } - } - } - } + const { map } = parseDirectivesInField(node, uniqueDefs) return { kind: "Property", name: node.name.value, required: node.type.kind === "NonNullType", - type: extractType(node.type, uniqueDefs) + type: map ?? extractType(node.type, uniqueDefs) } } From 8f6befe8dc649dd7d32762431fddeb9ecb625f19 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Fri, 6 Jan 2023 06:08:07 +0100 Subject: [PATCH 09/90] (chore): moved legacy extractors into legacy dir. Working with new ones --- .../{extract => extract-legacy}/enum-types.ts | 0 .../{extract => extract-legacy}/env-types.ts | 0 .../imported-enum-types.ts | 0 .../imported-env-types.ts | 0 .../imported-module-types.ts | 0 .../imported-object-types.ts | 0 .../src/{extract => extract-legacy}/index.ts | 0 .../module-types.ts | 107 --------------- .../object-types.ts | 0 .../utils/imported-types-utils.ts | 0 .../src/extract-legacy/utils/map-utils.ts | 37 +++++ .../utils/module-types-utils.ts | 41 +++--- .../src/{extract2 => extract}/directives.ts | 4 +- .../parse/src/{extract2 => extract}/enum.ts | 0 .../parse/src/{extract2 => extract}/env.ts | 0 .../parse/src/{extract2 => extract}/module.ts | 2 +- .../parse/src/{extract2 => extract}/object.ts | 28 +--- .../parse/src/{extract2 => extract}/types.ts | 0 packages/schema/parse/src/extract/utils.ts | 123 +++++++++++++++++ .../parse/src/extract/utils/map-utils.ts | 129 ------------------ .../parse/src/extract/utils/refParser.ts | 15 -- 21 files changed, 185 insertions(+), 301 deletions(-) rename packages/schema/parse/src/{extract => extract-legacy}/enum-types.ts (100%) rename packages/schema/parse/src/{extract => extract-legacy}/env-types.ts (100%) rename packages/schema/parse/src/{extract => extract-legacy}/imported-enum-types.ts (100%) rename packages/schema/parse/src/{extract => extract-legacy}/imported-env-types.ts (100%) rename packages/schema/parse/src/{extract => extract-legacy}/imported-module-types.ts (100%) rename packages/schema/parse/src/{extract => extract-legacy}/imported-object-types.ts (100%) rename packages/schema/parse/src/{extract => extract-legacy}/index.ts (100%) rename packages/schema/parse/src/{extract => extract-legacy}/module-types.ts (64%) rename packages/schema/parse/src/{extract => extract-legacy}/object-types.ts (100%) rename packages/schema/parse/src/{extract => extract-legacy}/utils/imported-types-utils.ts (100%) create mode 100644 packages/schema/parse/src/extract-legacy/utils/map-utils.ts rename packages/schema/parse/src/{extract => extract-legacy}/utils/module-types-utils.ts (81%) rename packages/schema/parse/src/{extract2 => extract}/directives.ts (91%) rename packages/schema/parse/src/{extract2 => extract}/enum.ts (100%) rename packages/schema/parse/src/{extract2 => extract}/env.ts (100%) rename packages/schema/parse/src/{extract2 => extract}/module.ts (98%) rename packages/schema/parse/src/{extract2 => extract}/object.ts (64%) rename packages/schema/parse/src/{extract2 => extract}/types.ts (100%) create mode 100644 packages/schema/parse/src/extract/utils.ts delete mode 100644 packages/schema/parse/src/extract/utils/map-utils.ts delete mode 100644 packages/schema/parse/src/extract/utils/refParser.ts diff --git a/packages/schema/parse/src/extract/enum-types.ts b/packages/schema/parse/src/extract-legacy/enum-types.ts similarity index 100% rename from packages/schema/parse/src/extract/enum-types.ts rename to packages/schema/parse/src/extract-legacy/enum-types.ts diff --git a/packages/schema/parse/src/extract/env-types.ts b/packages/schema/parse/src/extract-legacy/env-types.ts similarity index 100% rename from packages/schema/parse/src/extract/env-types.ts rename to packages/schema/parse/src/extract-legacy/env-types.ts diff --git a/packages/schema/parse/src/extract/imported-enum-types.ts b/packages/schema/parse/src/extract-legacy/imported-enum-types.ts similarity index 100% rename from packages/schema/parse/src/extract/imported-enum-types.ts rename to packages/schema/parse/src/extract-legacy/imported-enum-types.ts diff --git a/packages/schema/parse/src/extract/imported-env-types.ts b/packages/schema/parse/src/extract-legacy/imported-env-types.ts similarity index 100% rename from packages/schema/parse/src/extract/imported-env-types.ts rename to packages/schema/parse/src/extract-legacy/imported-env-types.ts diff --git a/packages/schema/parse/src/extract/imported-module-types.ts b/packages/schema/parse/src/extract-legacy/imported-module-types.ts similarity index 100% rename from packages/schema/parse/src/extract/imported-module-types.ts rename to packages/schema/parse/src/extract-legacy/imported-module-types.ts diff --git a/packages/schema/parse/src/extract/imported-object-types.ts b/packages/schema/parse/src/extract-legacy/imported-object-types.ts similarity index 100% rename from packages/schema/parse/src/extract/imported-object-types.ts rename to packages/schema/parse/src/extract-legacy/imported-object-types.ts diff --git a/packages/schema/parse/src/extract/index.ts b/packages/schema/parse/src/extract-legacy/index.ts similarity index 100% rename from packages/schema/parse/src/extract/index.ts rename to packages/schema/parse/src/extract-legacy/index.ts diff --git a/packages/schema/parse/src/extract/module-types.ts b/packages/schema/parse/src/extract-legacy/module-types.ts similarity index 64% rename from packages/schema/parse/src/extract/module-types.ts rename to packages/schema/parse/src/extract-legacy/module-types.ts index 521678346e..8db19c5251 100644 --- a/packages/schema/parse/src/extract/module-types.ts +++ b/packages/schema/parse/src/extract-legacy/module-types.ts @@ -15,7 +15,6 @@ import { extractNamedType, State, } from "./utils/module-types-utils"; -import { extractAnnotateDirective } from "./utils/object-types-utils"; import { ObjectTypeDefinitionNode, @@ -115,112 +114,6 @@ const visitorEnter = (abi: WrapAbi, state: State) => ({ }, }); -const parseCapabilitiesDirective = ( - nodeName: string, - node: ObjectTypeDefinitionNode -): InterfaceDefinition[] => { - const interfaces: InterfaceDefinition[] = []; - const interfacesByNamespace: Record = {}; - - if (!node.directives) { - return interfaces; - } - - for (const dir of node.directives) { - if (dir.name.value !== "capability") { - continue; - } - - if (!dir.arguments) { - throw Error( - `@capability directive is incomplete, missing arguments. See type ${nodeName}.` - ); - } - - const typeIndex = dir.arguments.findIndex( - (arg: ArgumentNode) => arg.name.value === "type" - ); - - if (typeIndex === -1) { - throw Error( - `@capability directive missing required argument "type". See type ${nodeName}.` - ); - } - - const typeArg = dir.arguments[typeIndex]; - - if (typeArg.value.kind !== "StringValue") { - throw Error( - `@capability directive's "type" argument must be a String type. See type ${nodeName}.` - ); - } - - if (!capabilityTypes.includes(typeArg.value.value as CapabilityType)) { - throw Error( - `@capability directive's "type" argument must be one from ${JSON.stringify( - capabilityTypes - )}. See type ${nodeName}.` - ); - } - - const capabilityType = typeArg.value.value as CapabilityType; - - const uriIndex = dir.arguments.findIndex( - (arg: ArgumentNode) => arg.name.value === "uri" - ); - - if (uriIndex === -1) { - throw Error( - `@capability directive missing required argument "uri". See type ${nodeName}.` - ); - } - - const uriArg = dir.arguments[uriIndex]; - - if (uriArg.value.kind !== "StringValue") { - throw Error( - `@capability directive's "uri" argument must be a String type. See type ${nodeName}.` - ); - } - - const uri = uriArg.value.value; - - const namespaceIndex = dir.arguments.findIndex( - (arg: ArgumentNode) => arg.name.value === "namespace" - ); - - if (namespaceIndex === -1) { - throw Error( - `@capability directive missing required argument "namespace". See type ${nodeName}.` - ); - } - - const namespaceArg = dir.arguments[namespaceIndex]; - - if (namespaceArg.value.kind !== "StringValue") { - throw Error( - `@capability directive's "namespace" argument must be a String type. See type ${nodeName}.` - ); - } - - const namespace = namespaceArg.value.value; - - if (!interfacesByNamespace[namespace]) { - interfacesByNamespace[namespace] = createInterfaceDefinition({ - type: namespace, - uri: uri, - namespace: namespace, - capabilities: createCapability({ - type: capabilityType, - enabled: true, - }), - }); - } - } - - return Array.from(Object.values(interfacesByNamespace)); -}; - const parseImportsDirective = ( nodeName: string, node: ObjectTypeDefinitionNode diff --git a/packages/schema/parse/src/extract/object-types.ts b/packages/schema/parse/src/extract-legacy/object-types.ts similarity index 100% rename from packages/schema/parse/src/extract/object-types.ts rename to packages/schema/parse/src/extract-legacy/object-types.ts diff --git a/packages/schema/parse/src/extract/utils/imported-types-utils.ts b/packages/schema/parse/src/extract-legacy/utils/imported-types-utils.ts similarity index 100% rename from packages/schema/parse/src/extract/utils/imported-types-utils.ts rename to packages/schema/parse/src/extract-legacy/utils/imported-types-utils.ts diff --git a/packages/schema/parse/src/extract-legacy/utils/map-utils.ts b/packages/schema/parse/src/extract-legacy/utils/map-utils.ts new file mode 100644 index 0000000000..428843af92 --- /dev/null +++ b/packages/schema/parse/src/extract-legacy/utils/map-utils.ts @@ -0,0 +1,37 @@ +// const _toGraphQLType = (rootType: string, type: string): string => { +// const parsedCurrentType = _parseCurrentType(rootType, type); +// let { subType } = parsedCurrentType; +// const { currentType } = parsedCurrentType; + +// if (!subType) { +// return currentType; +// } + +// switch (currentType) { +// case "Array": { +// if (subType.endsWith("!")) { +// subType = subType.slice(0, -1); +// } +// return `[${_toGraphQLType(rootType, subType)}]`; +// } +// case "Map": { +// const firstDelimiter = subType.indexOf(","); + +// const keyType = subType.substring(0, firstDelimiter).trim(); +// const valType = subType.substring(firstDelimiter + 1).trim(); + +// return `Map<${_toGraphQLType(rootType, keyType)}, ${_toGraphQLType( +// rootType, +// valType +// )}>`; +// } +// default: +// throw new Error( +// `Found unknown type ${currentType} while parsing ${rootType}` +// ); +// } +// }; + +// export function toGraphQLType(type: string): string { +// return _toGraphQLType(type, type); +// } diff --git a/packages/schema/parse/src/extract/utils/module-types-utils.ts b/packages/schema/parse/src/extract-legacy/utils/module-types-utils.ts similarity index 81% rename from packages/schema/parse/src/extract/utils/module-types-utils.ts rename to packages/schema/parse/src/extract-legacy/utils/module-types-utils.ts index 4605e5e9cf..1016c1cdc5 100644 --- a/packages/schema/parse/src/extract/utils/module-types-utils.ts +++ b/packages/schema/parse/src/extract-legacy/utils/module-types-utils.ts @@ -1,6 +1,4 @@ import { createPropertyDefinition, createArrayDefinition } from "../.."; -import { setPropertyType } from "./property-utils"; -import { extractAnnotateDirective } from "./object-types-utils"; import { BooleanValueNode, @@ -13,7 +11,6 @@ import { InterfaceDefinition, MapDefinition, MethodDefinition, - ModuleDefinition, PropertyDefinition, } from "@polywrap/wrap-manifest-types-js"; @@ -22,7 +19,7 @@ export interface EnvDirDefinition { } export interface State { - currentModule?: ModuleDefinition; + insideModule: boolean; currentMethod?: MethodDefinition; currentArgument?: PropertyDefinition; currentReturn?: PropertyDefinition; @@ -48,10 +45,10 @@ export function extractNamedType(node: NamedTypeNode, state: State): void { } // Argument value - setPropertyType(argument, argument.name, { - type: node.name.value, - required: state.nonNullType, - }); + // setPropertyType(argument, argument.name, { + // type: node.name.value, + // required: state.nonNullType, + // }); state.nonNullType = undefined; } else if (method) { @@ -72,10 +69,10 @@ export function extractNamedType(node: NamedTypeNode, state: State): void { } if (state.currentReturn) { - setPropertyType(state.currentReturn, method.name, { - type: node.name.value, - required: state.nonNullType, - }); + // setPropertyType(state.currentReturn, method.name, { + // type: node.name.value, + // required: state.nonNullType, + // }); } state.nonNullType = undefined; @@ -128,21 +125,21 @@ export function extractInputValueDefinition( } const name = node.name.value; - const { type, def } = extractAnnotateDirective(node, name); + // const { type, def } = extractAnnotateDirective(node, name); - const argument = createPropertyDefinition({ - type: type ? type : "N/A", - name: name, - map: def ? (def as MapDefinition) : undefined, - comment: node.description?.value, - required: def && def.required ? true : undefined, - }); + // const argument = createPropertyDefinition({ + // type: type ? type : "N/A", + // name: name, + // map: def ? (def as MapDefinition) : undefined, + // comment: node.description?.value, + // required: def && def.required ? true : undefined, + // }); if (!method.arguments) { method.arguments = []; } - method.arguments.push(argument); - state.currentArgument = argument; + // method.arguments.push(argument); + // state.currentArgument = argument; } export function extractEnvDirective( diff --git a/packages/schema/parse/src/extract2/directives.ts b/packages/schema/parse/src/extract/directives.ts similarity index 91% rename from packages/schema/parse/src/extract2/directives.ts rename to packages/schema/parse/src/extract/directives.ts index 19e900c103..2d35bb506a 100644 --- a/packages/schema/parse/src/extract2/directives.ts +++ b/packages/schema/parse/src/extract/directives.ts @@ -1,7 +1,7 @@ import { DirectiveNode, FieldDefinitionNode } from "graphql"; import { MapType, UniqueDefKind } from "../definitions"; -import { parseMapString } from "../extract/utils/map-utils"; -import { EnvDirDefinition } from "../extract/utils/module-types-utils"; +import { EnvDirDefinition } from "../extract-legacy/utils/module-types-utils"; +import { parseMapString } from "./utils"; export const parseDirectivesInField = (node: FieldDefinitionNode, uniqueDefs: Map) => { let map: MapType | undefined; diff --git a/packages/schema/parse/src/extract2/enum.ts b/packages/schema/parse/src/extract/enum.ts similarity index 100% rename from packages/schema/parse/src/extract2/enum.ts rename to packages/schema/parse/src/extract/enum.ts diff --git a/packages/schema/parse/src/extract2/env.ts b/packages/schema/parse/src/extract/env.ts similarity index 100% rename from packages/schema/parse/src/extract2/env.ts rename to packages/schema/parse/src/extract/env.ts diff --git a/packages/schema/parse/src/extract2/module.ts b/packages/schema/parse/src/extract/module.ts similarity index 98% rename from packages/schema/parse/src/extract2/module.ts rename to packages/schema/parse/src/extract/module.ts index 16a9529e06..99a5ba063e 100644 --- a/packages/schema/parse/src/extract2/module.ts +++ b/packages/schema/parse/src/extract/module.ts @@ -2,8 +2,8 @@ import { ASTVisitor, FieldDefinitionNode, ObjectTypeDefinitionNode } from "graph import { isModuleType } from "../abi"; import { Abi, UniqueDefKind, ResultDef, FunctionDef, ArgumentDef } from "../definitions"; import { parseAnnotateDirective, parseDirectivesInField } from "./directives"; -import { extractType } from "./object"; import { VisitorBuilder } from "./types"; +import { extractType } from "./utils"; export class ModuleVisitorBuilder implements VisitorBuilder { constructor(protected readonly uniqueDefs: Map) { } diff --git a/packages/schema/parse/src/extract2/object.ts b/packages/schema/parse/src/extract/object.ts similarity index 64% rename from packages/schema/parse/src/extract2/object.ts rename to packages/schema/parse/src/extract/object.ts index 9f4d20a3f0..318c04d745 100644 --- a/packages/schema/parse/src/extract2/object.ts +++ b/packages/schema/parse/src/extract/object.ts @@ -1,31 +1,9 @@ -import { ASTVisitor, FieldDefinitionNode, isScalarType, ObjectTypeDefinitionNode, TypeNode } from "graphql"; +import { ASTVisitor, FieldDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; import { isModuleType, isEnvType } from "../abi"; -import { Abi, AnyType, ObjectDef, PropertyDef, ScalarTypeName, UniqueDefKind } from "../definitions"; -import { parseRef } from "../extract/utils/refParser"; +import { Abi, ObjectDef, PropertyDef, UniqueDefKind } from "../definitions"; import { parseDirectivesInField } from "./directives"; import { VisitorBuilder } from "./types"; - -export const extractType = (node: TypeNode, uniqueDefs: Map): AnyType => { - switch (node.kind) { - case "NonNullType": - return extractType(node.type, uniqueDefs) - case "ListType": - return { - kind: "Array", - required: node.type.kind === "NonNullType", - item: extractType(node.type, uniqueDefs) - } - case "NamedType": - if (isScalarType(node.name.value)) { - return { - kind: "Scalar", - scalar: node.name.value as ScalarTypeName - } - } - - return parseRef(node.name.value, uniqueDefs) - } -} +import { extractType } from "./utils"; export class ObjectVisitorBuilder implements VisitorBuilder { constructor(protected readonly uniqueDefs: Map) { } diff --git a/packages/schema/parse/src/extract2/types.ts b/packages/schema/parse/src/extract/types.ts similarity index 100% rename from packages/schema/parse/src/extract2/types.ts rename to packages/schema/parse/src/extract/types.ts diff --git a/packages/schema/parse/src/extract/utils.ts b/packages/schema/parse/src/extract/utils.ts new file mode 100644 index 0000000000..fc3d6a02eb --- /dev/null +++ b/packages/schema/parse/src/extract/utils.ts @@ -0,0 +1,123 @@ +import { isScalarType, TypeNode } from "graphql"; +import { UniqueDefKind, RefType, AnyType, MapKeyTypeName, mapKeyTypeSet, MapType, ScalarTypeName } from "../definitions"; + +export const extractType = (node: TypeNode, uniqueDefs: Map): AnyType => { + switch (node.kind) { + case "NonNullType": + return extractType(node.type, uniqueDefs) + case "ListType": + return { + kind: "Array", + required: node.type.kind === "NonNullType", + item: extractType(node.type, uniqueDefs) + } + case "NamedType": + if (isScalarType(node.name.value)) { + return { + kind: "Scalar", + scalar: node.name.value as ScalarTypeName + } + } + + return parseRef(node.name.value, uniqueDefs) + } +} + +export const parseRef = (refName: string, uniqueDefs: Map): RefType => { + const kind = uniqueDefs.get(refName); + + if (!kind) { + throw new Error(`Found ref to unknown definition '${refName}'`) + } + + return { + kind: "Ref", + ref_kind: kind, + ref_name: refName + } +} + +// TODO: Make sure map also works for imported types and modules + +export const parseMapString = (mapString: string, uniqueDefs: Map): MapType => { + const extractType = (mapString: string): AnyType => { + if (isArray(mapString)) { + const closeSquareBracketIdx = mapString.lastIndexOf("]"); + const required = mapString[closeSquareBracketIdx + 1] === "!" + return { + kind: "Array", + required, + item: extractType(mapString.substring(1, closeSquareBracketIdx)) + }; + } else if (isMap(mapString)) { + return parseMapString(mapString, uniqueDefs) + } else if (isScalarType(mapString)) { + return { + kind: "Scalar", + scalar: mapString as ScalarTypeName, + } + } else { + return parseRef(mapString, uniqueDefs) + } + // TODO: is this case necessary? + // else { + // throw new Error(`Unrecognized reference type '${mapString}'`) + // } + } + + const openAngleBracketIdx = mapString.indexOf("<"); + const closeAngleBracketIdx = mapString.lastIndexOf(">"); + + if ( + closeAngleBracketIdx === -1 + ) { + throw new Error(`Invalid map value type: ${mapString}`); + } + + const subtype = mapString.substring(openAngleBracketIdx + 1, closeAngleBracketIdx); + + const firstDelimiter = subtype.indexOf(","); + + const _keyType = subtype.substring(0, firstDelimiter).trim(); + const _valType = subtype.substring(firstDelimiter + 1).trim(); + + if (!_keyType || !_valType) { + throw new Error(`Invalid map value type: ${mapString}`); + } + + // TODO: Is there a better way to enforce this -> Map key should always be required + // TODO: Should we throw an error if it's not? + // const keyRequired = true; + const keyType = _keyType.endsWith("!") ? _keyType.slice(0, -1) : _keyType; + const valType = _valType.endsWith("!") ? _valType.slice(0, -1) : _valType; + + if (!isMapKey(keyType)) { + throw new Error( + `Found invalid map key type: ${keyType} while parsing ${mapString}` + ); + } + + return { + kind: "Map", + key: { + kind: "Scalar", + scalar: keyType as MapKeyTypeName + }, + value: extractType(valType), + required: valType.endsWith("!"), + } + +} + +const isMap = (typeName: string): boolean => { + //TODO: would this be the right condition? + return typeName.startsWith("Map<") +} + +const isMapKey = (typeName: string): boolean => { + return typeName in mapKeyTypeSet; +} + +const isArray = (typeName: string): boolean => { + return typeName.startsWith("[") +} \ No newline at end of file diff --git a/packages/schema/parse/src/extract/utils/map-utils.ts b/packages/schema/parse/src/extract/utils/map-utils.ts deleted file mode 100644 index 5b4447c87f..0000000000 --- a/packages/schema/parse/src/extract/utils/map-utils.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { - isScalarType, -} from "../.."; -import { AnyType, MapKeyTypeName, mapKeyTypeSet, MapType, ScalarTypeName, UniqueDefKind } from "../../definitions"; -import { parseRef } from "./refParser"; - - -// TODO: Make sure map also works for imported types and modules - -export const parseMapReference = (typeName: string, uniqueDefs: Map): MapType => { - const extractType = (typeName: string): AnyType => { - if (isArray(typeName)) { - const closeSquareBracketIdx = typeName.lastIndexOf("]"); - const required = typeName[closeSquareBracketIdx + 1] === "!" - return { - kind: "Array", - required, - item: extractType(typeName.substring(1, closeSquareBracketIdx)) - }; - } else if (isMap(typeName)) { - return parseMapReference(typeName, uniqueDefs) - } else if (isScalarType(typeName)) { - return { - kind: "Scalar", - scalar: typeName as ScalarTypeName, - } - } else { - return parseRef(typeName, uniqueDefs) - } - // TODO: is this case necessary? - // else { - // throw new Error(`Unrecognized reference type '${typeName}'`) - // } - } - - const openAngleBracketIdx = typeName.indexOf("<"); - const closeAngleBracketIdx = typeName.lastIndexOf(">"); - - if ( - closeAngleBracketIdx === -1 - ) { - throw new Error(`Invalid map value type: ${typeName}`); - } - - const subtype = typeName.substring(openAngleBracketIdx + 1, closeAngleBracketIdx); - - const firstDelimiter = subtype.indexOf(","); - - const _keyType = subtype.substring(0, firstDelimiter).trim(); - const _valType = subtype.substring(firstDelimiter + 1).trim(); - - if (!_keyType || !_valType) { - throw new Error(`Invalid map value type: ${typeName}`); - } - - // TODO: Is there a better way to enforce this -> Map key should always be required - // TODO: Should we throw an error if it's not? - // const keyRequired = true; - const keyType = _keyType.endsWith("!") ? _keyType.slice(0, -1) : _keyType; - const valType = _valType.endsWith("!") ? _valType.slice(0, -1) : _valType; - - if (!isMapKey(keyType)) { - throw new Error( - `Found invalid map key type: ${keyType} while parsing ${typeName}` - ); - } - - return { - kind: "Map", - key: { - kind: "Scalar", - scalar: keyType as MapKeyTypeName - }, - value: extractType(valType), - required: valType.endsWith("!"), - } - -} - -const isMap = (typeName: string): boolean => { - //TODO: would this be the right condition? - return typeName.startsWith("Map<") -} - -const isMapKey = (typeName: string): boolean => { - return typeName in mapKeyTypeSet; -} - -const isArray = (typeName: string): boolean => { - return typeName.startsWith("[") -} - -// const _toGraphQLType = (rootType: string, type: string): string => { -// const parsedCurrentType = _parseCurrentType(rootType, type); -// let { subType } = parsedCurrentType; -// const { currentType } = parsedCurrentType; - -// if (!subType) { -// return currentType; -// } - -// switch (currentType) { -// case "Array": { -// if (subType.endsWith("!")) { -// subType = subType.slice(0, -1); -// } -// return `[${_toGraphQLType(rootType, subType)}]`; -// } -// case "Map": { -// const firstDelimiter = subType.indexOf(","); - -// const keyType = subType.substring(0, firstDelimiter).trim(); -// const valType = subType.substring(firstDelimiter + 1).trim(); - -// return `Map<${_toGraphQLType(rootType, keyType)}, ${_toGraphQLType( -// rootType, -// valType -// )}>`; -// } -// default: -// throw new Error( -// `Found unknown type ${currentType} while parsing ${rootType}` -// ); -// } -// }; - -// export function toGraphQLType(type: string): string { -// return _toGraphQLType(type, type); -// } diff --git a/packages/schema/parse/src/extract/utils/refParser.ts b/packages/schema/parse/src/extract/utils/refParser.ts deleted file mode 100644 index 0e387b0e37..0000000000 --- a/packages/schema/parse/src/extract/utils/refParser.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { RefType, UniqueDefKind } from "../../definitions"; - -export const parseRef = (refName: string, uniqueDefs: Map): RefType => { - const kind = uniqueDefs.get(refName); - - if (!kind) { - throw new Error(`Found ref to unknown definition '${refName}'`) - } - - return { - kind: "Ref", - ref_kind: kind, - ref_name: refName - } -} \ No newline at end of file From 6f688459f2252aa29df16cb24c1ba93d08a1c78a Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 9 Jan 2023 01:07:44 +0100 Subject: [PATCH 10/90] (chore): removed old extractors. Minor fixes --- .../parse/src/extract-legacy/enum-types.ts | 38 ---- .../parse/src/extract-legacy/env-types.ts | 61 ------ .../src/extract-legacy/imported-enum-types.ts | 39 ---- .../src/extract-legacy/imported-env-types.ts | 85 -------- .../extract-legacy/imported-module-types.ts | 134 ------------ .../extract-legacy/imported-object-types.ts | 85 -------- .../schema/parse/src/extract-legacy/index.ts | 24 -- .../parse/src/extract-legacy/module-types.ts | 206 ------------------ .../parse/src/extract-legacy/object-types.ts | 109 --------- .../utils/imported-types-utils.ts | 52 ----- .../src/extract-legacy/utils/map-utils.ts | 37 ---- .../utils/module-types-utils.ts | 167 -------------- .../schema/parse/src/extract/directives.ts | 5 +- packages/schema/parse/src/extract/index.ts | 4 + packages/schema/parse/src/extract/types.ts | 6 +- 15 files changed, 12 insertions(+), 1040 deletions(-) delete mode 100644 packages/schema/parse/src/extract-legacy/enum-types.ts delete mode 100644 packages/schema/parse/src/extract-legacy/env-types.ts delete mode 100644 packages/schema/parse/src/extract-legacy/imported-enum-types.ts delete mode 100644 packages/schema/parse/src/extract-legacy/imported-env-types.ts delete mode 100644 packages/schema/parse/src/extract-legacy/imported-module-types.ts delete mode 100644 packages/schema/parse/src/extract-legacy/imported-object-types.ts delete mode 100644 packages/schema/parse/src/extract-legacy/index.ts delete mode 100644 packages/schema/parse/src/extract-legacy/module-types.ts delete mode 100644 packages/schema/parse/src/extract-legacy/object-types.ts delete mode 100644 packages/schema/parse/src/extract-legacy/utils/imported-types-utils.ts delete mode 100644 packages/schema/parse/src/extract-legacy/utils/map-utils.ts delete mode 100644 packages/schema/parse/src/extract-legacy/utils/module-types-utils.ts create mode 100644 packages/schema/parse/src/extract/index.ts diff --git a/packages/schema/parse/src/extract-legacy/enum-types.ts b/packages/schema/parse/src/extract-legacy/enum-types.ts deleted file mode 100644 index a6454ed8ad..0000000000 --- a/packages/schema/parse/src/extract-legacy/enum-types.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { createEnumDefinition } from ".."; - -import { WrapAbi, EnumDefinition } from "@polywrap/wrap-manifest-types-js"; -import { ASTVisitor, DirectiveNode, EnumTypeDefinitionNode } from "graphql"; - -const visitorEnter = (enumTypes: EnumDefinition[]) => ({ - EnumTypeDefinition: (node: EnumTypeDefinitionNode) => { - // Skip imported types - if ( - node.directives && - node.directives.findIndex( - (dir: DirectiveNode) => dir.name.value === "imported" - ) > -1 - ) { - return; - } - - const constants: string[] = []; - if (node.values) { - for (const value of node.values) { - constants.push(value.name.value); - } - } - - const enumType = createEnumDefinition({ - type: node.name.value, - constants, - comment: node.description?.value, - }); - enumTypes.push(enumType); - }, -}); - -export const getEnumTypesVisitor = (abi: WrapAbi): ASTVisitor => { - return { - enter: visitorEnter(abi.enumTypes || []), - }; -}; diff --git a/packages/schema/parse/src/extract-legacy/env-types.ts b/packages/schema/parse/src/extract-legacy/env-types.ts deleted file mode 100644 index b2c14d1c58..0000000000 --- a/packages/schema/parse/src/extract-legacy/env-types.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { isEnvType, createEnvDefinition } from ".."; -import { - extractFieldDefinition, - extractListType, - extractNamedType, - State, -} from "./utils/object-types-utils"; - -import { - ObjectTypeDefinitionNode, - NonNullTypeNode, - NamedTypeNode, - ListTypeNode, - FieldDefinitionNode, - ASTVisitor, -} from "graphql"; -import { WrapAbi } from "@polywrap/wrap-manifest-types-js"; - -const visitorEnter = (abi: WrapAbi, state: State) => ({ - ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { - const typeName = node.name.value; - - if (isEnvType(typeName)) { - abi.envType = createEnvDefinition({}); - state.currentType = abi.envType; - } - }, - NonNullType: (_node: NonNullTypeNode) => { - state.nonNullType = true; - }, - NamedType: (node: NamedTypeNode) => { - extractNamedType(node, state); - }, - ListType: (_node: ListTypeNode) => { - extractListType(state); - }, - FieldDefinition: (node: FieldDefinitionNode) => { - extractFieldDefinition(node, state); - }, -}); - -const visitorLeave = (state: State) => ({ - ObjectTypeDefinition: (_node: ObjectTypeDefinitionNode) => { - state.currentType = undefined; - }, - FieldDefinition: (_node: FieldDefinitionNode) => { - state.currentProperty = undefined; - }, - NonNullType: (_node: NonNullTypeNode) => { - state.nonNullType = undefined; - }, -}); - -export function getEnvVisitor(abi: WrapAbi): ASTVisitor { - const state: State = {}; - - return { - enter: visitorEnter(abi, state), - leave: visitorLeave(state), - }; -} diff --git a/packages/schema/parse/src/extract-legacy/imported-enum-types.ts b/packages/schema/parse/src/extract-legacy/imported-enum-types.ts deleted file mode 100644 index c374735a7b..0000000000 --- a/packages/schema/parse/src/extract-legacy/imported-enum-types.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { createImportedEnumDefinition } from ".."; -import { extractImportedDefinition } from "./utils/imported-types-utils"; - -import { ASTVisitor, EnumTypeDefinitionNode } from "graphql"; -import { - ImportedEnumDefinition, - WrapAbi, -} from "@polywrap/wrap-manifest-types-js"; - -const visitorEnter = (importedEnumTypes: ImportedEnumDefinition[]) => ({ - EnumTypeDefinition: (node: EnumTypeDefinitionNode) => { - const constants: string[] = []; - const imported = extractImportedDefinition(node); - - if (!imported) { - return; - } - - if (node.values) { - for (const value of node.values) { - constants.push(value.name.value); - } - } - - const enumType = createImportedEnumDefinition({ - type: node.name.value, - constants, - uri: imported.uri, - namespace: imported.namespace, - nativeType: imported.nativeType, - comment: node.description?.value, - }); - importedEnumTypes.push(enumType); - }, -}); - -export const getImportedEnumTypesVisitor = (abi: WrapAbi): ASTVisitor => ({ - enter: visitorEnter(abi.importedEnumTypes || []), -}); diff --git a/packages/schema/parse/src/extract-legacy/imported-env-types.ts b/packages/schema/parse/src/extract-legacy/imported-env-types.ts deleted file mode 100644 index 276292d113..0000000000 --- a/packages/schema/parse/src/extract-legacy/imported-env-types.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { - createInterfaceImplementedDefinition, - createImportedEnvDefinition, -} from ".."; -import { - extractFieldDefinition, - extractListType, - extractNamedType, - State, -} from "./utils/object-types-utils"; -import { extractImportedDefinition } from "./utils/imported-types-utils"; - -import { - ObjectTypeDefinitionNode, - NonNullTypeNode, - NamedTypeNode, - ListTypeNode, - FieldDefinitionNode, - ASTVisitor, -} from "graphql"; -import { - ImportedEnvDefinition, - WrapAbi, -} from "@polywrap/wrap-manifest-types-js"; - -const visitorEnter = ( - importedEnvTypes: ImportedEnvDefinition[], - state: State -) => ({ - ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { - const imported = extractImportedDefinition(node, "env"); - - if (!imported) { - return; - } - - const interfaces = node.interfaces?.map((x) => - createInterfaceImplementedDefinition({ type: x.name.value }) - ); - - const importedType = createImportedEnvDefinition({ - uri: imported.uri, - namespace: imported.namespace, - nativeType: imported.nativeType, - interfaces: interfaces?.length ? interfaces : undefined, - comment: node.description?.value, - }); - - importedEnvTypes.push(importedType); - state.currentType = importedType; - }, - NonNullType: (_node: NonNullTypeNode) => { - state.nonNullType = true; - }, - NamedType: (node: NamedTypeNode) => { - extractNamedType(node, state); - }, - ListType: (_node: ListTypeNode) => { - extractListType(state); - }, - FieldDefinition: (node: FieldDefinitionNode) => { - extractFieldDefinition(node, state); - }, -}); - -const visitorLeave = (state: State) => ({ - ObjectTypeDefinition: (_node: ObjectTypeDefinitionNode) => { - state.currentType = undefined; - }, - FieldDefinition: (_node: FieldDefinitionNode) => { - state.currentProperty = undefined; - }, - NonNullType: (_node: NonNullTypeNode) => { - state.nonNullType = undefined; - }, -}); - -export const getImportedEnvTypesVisitor = (abi: WrapAbi): ASTVisitor => { - const state: State = {}; - - return { - enter: visitorEnter(abi.importedEnvTypes || [], state), - leave: visitorLeave(state), - }; -}; diff --git a/packages/schema/parse/src/extract-legacy/imported-module-types.ts b/packages/schema/parse/src/extract-legacy/imported-module-types.ts deleted file mode 100644 index e29e5cf6ec..0000000000 --- a/packages/schema/parse/src/extract-legacy/imported-module-types.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { - createImportedModuleDefinition, - createMethodDefinition, - createPropertyDefinition, -} from ".."; -import { extractImportedDefinition } from "./utils/imported-types-utils"; -import { - extractEnvDirective, - extractInputValueDefinition, - extractListType, - extractNamedType, - State, -} from "./utils/module-types-utils"; -import { extractAnnotateDirective } from "./utils/object-types-utils"; - -import { - ASTVisitor, - FieldDefinitionNode, - InputValueDefinitionNode, - ListTypeNode, - NamedTypeNode, - NonNullTypeNode, - ObjectTypeDefinitionNode, -} from "graphql"; -import { - ImportedModuleDefinition, - MapDefinition, - WrapAbi, -} from "@polywrap/wrap-manifest-types-js"; - -const visitorEnter = ( - importedModuleTypes: ImportedModuleDefinition[], - state: State -) => ({ - ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { - const imported = extractImportedDefinition(node, "module"); - - if (!imported) { - return; - } - - const dir = - node.directives && - node.directives.find((dir) => dir.name.value === "enabled_interface"); - const isInterface = dir ? true : false; - - const importedType = createImportedModuleDefinition({ - uri: imported.uri, - namespace: imported.namespace, - nativeType: imported.nativeType, - isInterface: isInterface, - comment: node.description?.value, - }); - importedModuleTypes.push(importedType); - state.currentImport = importedType; - }, - FieldDefinition: (node: FieldDefinitionNode) => { - const importDef = state.currentImport; - - if (!importDef) { - return; - } - - const name = node.name.value; - - const { type, def } = extractAnnotateDirective(node, name); - - const returnType = createPropertyDefinition({ - type: type ? type : "N/A", - name: node.name.value, - map: def - ? ({ ...def, name: node.name.value } as MapDefinition) - : undefined, - required: def && def.required ? true : undefined, - }); - - const method = createMethodDefinition({ - name: node.name.value, - return: returnType, - comment: node.description?.value, - }); - - const envDirDefinition = extractEnvDirective(node); - - if (envDirDefinition) { - method.env = envDirDefinition; - } - - if (!importDef.methods) { - importDef.methods = []; - } - - importDef.methods.push(method); - state.currentMethod = method; - state.currentReturn = returnType; - }, - InputValueDefinition: (node: InputValueDefinitionNode) => { - extractInputValueDefinition(node, state); - }, - NonNullType: (_node: NonNullTypeNode) => { - state.nonNullType = true; - }, - NamedType: (node: NamedTypeNode) => { - extractNamedType(node, state); - }, - ListType: (_node: ListTypeNode) => { - extractListType(state); - }, -}); - -const visitorLeave = (state: State) => ({ - ObjectTypeDefinition: (_node: ObjectTypeDefinitionNode) => { - state.currentImport = undefined; - }, - FieldDefinition: (_node: FieldDefinitionNode) => { - state.currentMethod = undefined; - state.currentReturn = undefined; - }, - InputValueDefinition: (_node: InputValueDefinitionNode) => { - state.currentArgument = undefined; - }, - NonNullType: (_node: NonNullTypeNode) => { - state.nonNullType = undefined; - }, -}); - -export const getImportedModuleTypesVisitor = (abi: WrapAbi): ASTVisitor => { - const state: State = {}; - - return { - enter: visitorEnter(abi.importedModuleTypes || [], state), - leave: visitorLeave(state), - }; -}; diff --git a/packages/schema/parse/src/extract-legacy/imported-object-types.ts b/packages/schema/parse/src/extract-legacy/imported-object-types.ts deleted file mode 100644 index 49b4191ca4..0000000000 --- a/packages/schema/parse/src/extract-legacy/imported-object-types.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { - createImportedObjectDefinition, - createInterfaceImplementedDefinition, -} from ".."; -import { - extractFieldDefinition, - extractListType, - extractNamedType, - State, -} from "./utils/object-types-utils"; -import { extractImportedDefinition } from "./utils/imported-types-utils"; - -import { - ObjectTypeDefinitionNode, - NonNullTypeNode, - NamedTypeNode, - ListTypeNode, - FieldDefinitionNode, - ASTVisitor, -} from "graphql"; -import { - ImportedObjectDefinition, - WrapAbi, -} from "@polywrap/wrap-manifest-types-js"; - -const visitorEnter = ( - importedObjectTypes: ImportedObjectDefinition[], - state: State -) => ({ - ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { - const imported = extractImportedDefinition(node); - - if (!imported) { - return; - } - - const interfaces = node.interfaces?.map((x) => - createInterfaceImplementedDefinition({ type: x.name.value }) - ); - - const importedType = createImportedObjectDefinition({ - type: node.name.value, - uri: imported.uri, - namespace: imported.namespace, - nativeType: imported.nativeType, - interfaces: interfaces?.length ? interfaces : undefined, - comment: node.description?.value, - }); - importedObjectTypes.push(importedType); - state.currentType = importedType; - }, - NonNullType: (_node: NonNullTypeNode) => { - state.nonNullType = true; - }, - NamedType: (node: NamedTypeNode) => { - extractNamedType(node, state); - }, - ListType: (_node: ListTypeNode) => { - extractListType(state); - }, - FieldDefinition: (node: FieldDefinitionNode) => { - extractFieldDefinition(node, state); - }, -}); - -const visitorLeave = (state: State) => ({ - ObjectTypeDefinition: (_node: ObjectTypeDefinitionNode) => { - state.currentType = undefined; - }, - FieldDefinition: (_node: FieldDefinitionNode) => { - state.currentProperty = undefined; - }, - NonNullType: (_node: NonNullTypeNode) => { - state.nonNullType = undefined; - }, -}); - -export const getImportedObjectTypesVisitor = (abi: WrapAbi): ASTVisitor => { - const state: State = {}; - - return { - enter: visitorEnter(abi.importedObjectTypes || [], state), - leave: visitorLeave(state), - }; -}; diff --git a/packages/schema/parse/src/extract-legacy/index.ts b/packages/schema/parse/src/extract-legacy/index.ts deleted file mode 100644 index 5089ce3278..0000000000 --- a/packages/schema/parse/src/extract-legacy/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { getEnumTypesVisitor } from "./enum-types"; -import { getObjectTypesVisitor } from "./object-types"; -import { getModuleTypesVisitor } from "./module-types"; -import { getImportedObjectTypesVisitor } from "./imported-object-types"; -import { getImportedModuleTypesVisitor } from "./imported-module-types"; -import { getImportedEnumTypesVisitor } from "./imported-enum-types"; -import { getEnvVisitor } from "./env-types"; -import { getImportedEnvTypesVisitor } from "./imported-env-types"; - -import { ASTVisitor } from "graphql"; -import { WrapAbi } from "@polywrap/wrap-manifest-types-js"; - -export type SchemaExtractorBuilder = (abi: WrapAbi) => ASTVisitor; - -export const extractors: SchemaExtractorBuilder[] = [ - getEnumTypesVisitor, - getImportedEnumTypesVisitor, - getObjectTypesVisitor, - getImportedObjectTypesVisitor, - getModuleTypesVisitor, - getImportedModuleTypesVisitor, - getEnvVisitor, - getImportedEnvTypesVisitor, -]; diff --git a/packages/schema/parse/src/extract-legacy/module-types.ts b/packages/schema/parse/src/extract-legacy/module-types.ts deleted file mode 100644 index 8db19c5251..0000000000 --- a/packages/schema/parse/src/extract-legacy/module-types.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { - createModuleDefinition, - createMethodDefinition, - createPropertyDefinition, - createInterfaceImplementedDefinition, - CapabilityType, - createCapability, - createInterfaceDefinition, - capabilityTypes, -} from ".."; -import { - extractEnvDirective, - extractInputValueDefinition, - extractListType, - extractNamedType, - State, -} from "./utils/module-types-utils"; - -import { - ObjectTypeDefinitionNode, - NonNullTypeNode, - NamedTypeNode, - ListTypeNode, - FieldDefinitionNode, - InputValueDefinitionNode, - DirectiveNode, - ArgumentNode, - ValueNode, - ASTVisitor, -} from "graphql"; -import { - InterfaceDefinition, - MapDefinition, - WrapAbi, -} from "@polywrap/wrap-manifest-types-js"; - -const visitorEnter = (abi: WrapAbi, state: State) => ({ - ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { - const nodeName = node.name.value; - - if (nodeName !== "Module") { - return; - } - - const imports = parseImportsDirective(nodeName, node); - - const enabledInterfaces = parseCapabilitiesDirective(nodeName, node); - state.currentInterfaces = enabledInterfaces; - - const interfaces = node.interfaces?.map((x) => - createInterfaceImplementedDefinition({ type: x.name.value }) - ); - - const module = createModuleDefinition({ - imports: imports.length ? imports : undefined, - interfaces: interfaces?.length ? interfaces : undefined, - comment: node.description?.value, - }); - - abi.moduleType = module; - state.currentModule = module; - }, - FieldDefinition: (node: FieldDefinitionNode) => { - const module = state.currentModule; - - if (!module) { - return; - } - - const name = node.name.value; - - const { type, def } = extractAnnotateDirective(node, name); - - const returnType = createPropertyDefinition({ - type: type ? type : "N/A", - name: node.name.value, - map: def - ? ({ ...def, name: node.name.value } as MapDefinition) - : undefined, - required: def && def.required, - }); - - const method = createMethodDefinition({ - name: node.name.value, - return: returnType, - comment: node.description?.value, - }); - - const envDirDefinition = extractEnvDirective(node); - - if (envDirDefinition) { - method.env = envDirDefinition; - } - - if (!module.methods) { - module.methods = []; - } - - module.methods.push(method); - state.currentMethod = method; - state.currentReturn = returnType; - }, - InputValueDefinition: (node: InputValueDefinitionNode) => { - extractInputValueDefinition(node, state); - }, - NonNullType: (_node: NonNullTypeNode) => { - state.nonNullType = true; - }, - NamedType: (node: NamedTypeNode) => { - extractNamedType(node, state); - }, - ListType: (_node: ListTypeNode) => { - extractListType(state); - }, -}); - -const parseImportsDirective = ( - nodeName: string, - node: ObjectTypeDefinitionNode -): { type: string }[] => { - // Look for the imports directive, and gather imported types - const imports: { type: string }[] = []; - - if (!node.directives) { - return imports; - } - - const importsIndex = node.directives.findIndex( - (dir: DirectiveNode) => dir.name.value === "imports" - ); - - if (importsIndex !== -1) { - const importsDir = node.directives[importsIndex]; - - if (!importsDir.arguments) { - throw Error( - `@imports directive is incomplete, missing arguments. See type ${nodeName}.` - ); - } - - const typesIndex = importsDir.arguments.findIndex( - (arg: ArgumentNode) => arg.name.value === "types" - ); - - if (typesIndex === -1) { - throw Error( - `@imports directive missing required argument "types". See type ${nodeName}.` - ); - } - - const typesArg = importsDir.arguments[typesIndex]; - - if (typesArg.value.kind !== "ListValue") { - throw Error( - `@imports directive's "types" argument must be a List type. See type ${nodeName}.` - ); - } - - const listValue = typesArg.value; - - listValue.values.forEach((value: ValueNode) => { - if (value.kind !== "StringValue") { - throw Error( - `@imports directive's "types" list must only contain strings. See type ${nodeName}.` - ); - } - - imports.push({ type: value.value }); - }); - } - - return imports; -}; - -const visitorLeave = (abi: WrapAbi, state: State) => ({ - ObjectTypeDefinition: (_node: ObjectTypeDefinitionNode) => { - if (!abi.interfaceTypes) { - abi.interfaceTypes = []; - } - if (state.currentInterfaces) { - abi.interfaceTypes = [...abi.interfaceTypes, ...state.currentInterfaces]; - } - - state.currentInterfaces = undefined; - state.currentModule = undefined; - }, - FieldDefinition: (_node: FieldDefinitionNode) => { - state.currentMethod = undefined; - state.currentReturn = undefined; - }, - InputValueDefinition: (_node: InputValueDefinitionNode) => { - state.currentArgument = undefined; - }, - NonNullType: (_node: NonNullTypeNode) => { - state.nonNullType = undefined; - }, -}); - -export const getModuleTypesVisitor = (abi: WrapAbi): ASTVisitor => { - const state: State = {}; - - return { - enter: visitorEnter(abi, state), - leave: visitorLeave(abi, state), - }; -}; diff --git a/packages/schema/parse/src/extract-legacy/object-types.ts b/packages/schema/parse/src/extract-legacy/object-types.ts deleted file mode 100644 index 8804a9a687..0000000000 --- a/packages/schema/parse/src/extract-legacy/object-types.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { - isEnvType, - isModuleType, - isScalarType, -} from ".."; - -import { - ObjectTypeDefinitionNode, - FieldDefinitionNode, - DirectiveNode, - ASTVisitor, - StringValueNode, - TypeNode, -} from "graphql"; -import { ObjectDef, Abi as WrapAbi, PropertyDef, AnyType, ScalarTypeName, UniqueDefKind } from "../definitions"; -import { parseMapReference } from "./utils/map-utils"; -import { parseRef } from "./utils/refParser"; - -const extractPropertyDef = (node: FieldDefinitionNode, uniqueDefs: Map): PropertyDef => { - const extractType = (node: TypeNode): AnyType => { - switch (node.kind) { - case "NonNullType": - return extractType(node.type) - case "ListType": - return { - kind: "Array", - required: node.type.kind === "NonNullType", - item: extractType(node.type) - } - case "NamedType": - if (isScalarType(node.name.value)) { - return { - kind: "Scalar", - scalar: node.name.value as ScalarTypeName - } - } - - return parseRef(node.name.value, uniqueDefs) - } - } - - if (node.directives) { - for (const dir of node.directives) { - if (dir.name.value === "annotate") { - const typeName = (dir.arguments?.find((arg) => arg.name.value === "type") - ?.value as StringValueNode).value; - if (!typeName) { - throw new Error( - `Annotate directive: ${node.name.value} has invalid arguments` - ); - } - return { - kind: "Property", - required: node.type.kind === "NonNullType", - name: node.name.value, - type: parseMapReference(typeName, uniqueDefs) - } - } - } - } - - return { - kind: "Property", - name: node.name.value, - required: node.type.kind === "NonNullType", - type: extractType(node.type) - } -} - -const visitorEnter = (objectTypes: ObjectDef[], uniqueDefs: Map) => ({ - ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { - const typeName = node.name.value; - - // Skip non-custom types - if (isModuleType(typeName) || isEnvType(typeName)) { - return; - } - - // Skip imported types - if ( - node.directives && - node.directives.findIndex( - (dir: DirectiveNode) => dir.name.value === "imported" - ) > -1 - ) { - return; - } - - // TODO: restore interfaces support - // const interfaces = node.interfaces?.map((x) => - // createInterfaceImplementedDefinition({ type: x.name.value }) - // ); - - // Create a new TypeDefinition - const type: ObjectDef = { - kind: "Object", - comment: node.description?.value, - name: typeName, - props: node.fields?.map(fieldNode => extractPropertyDef(fieldNode, uniqueDefs)) ?? [] - }; - objectTypes.push(type); - }, -}); - -export const getObjectTypesVisitor = (abi: WrapAbi, uniqueDefs: Map): ASTVisitor => { - return { - enter: visitorEnter(abi.objects ?? [], uniqueDefs), - }; -}; diff --git a/packages/schema/parse/src/extract-legacy/utils/imported-types-utils.ts b/packages/schema/parse/src/extract-legacy/utils/imported-types-utils.ts deleted file mode 100644 index cdb1290b34..0000000000 --- a/packages/schema/parse/src/extract-legacy/utils/imported-types-utils.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { isImportedEnvType, isImportedModuleType } from "../.."; - -import { DirectiveNode, TypeDefinitionNode } from "graphql"; -import { ImportedDefinition } from "@polywrap/wrap-manifest-types-js"; - -export function extractImportedDefinition( - node: TypeDefinitionNode, - type?: "module" | "env" -): ImportedDefinition | undefined { - if (!node.directives) { - return undefined; - } - - // Look for the imported directive - const importedIndex = node.directives.findIndex( - (dir: DirectiveNode) => dir.name.value === "imported" - ); - - if (importedIndex === -1) { - return undefined; - } - - const typeName = node.name.value; - - if ( - (type === "module" && !isImportedModuleType(typeName)) || - (type !== "module" && isImportedModuleType(typeName)) || - (type === "env" && !isImportedEnvType(typeName)) || - (type !== "env" && isImportedEnvType(typeName)) - ) { - return undefined; - } - - const importedDir = node.directives[importedIndex]; - - const args = importedDir.arguments || []; - const result: ImportedDefinition = { - namespace: "", - nativeType: "", - uri: "", - }; - - Object.keys(result).map((key: keyof typeof result) => { - const argumentNode = args.find((arg) => arg.name.value === key); - - if (argumentNode && argumentNode.value.kind === "StringValue") { - result[key] = argumentNode.value.value; - } - }); - - return result; -} diff --git a/packages/schema/parse/src/extract-legacy/utils/map-utils.ts b/packages/schema/parse/src/extract-legacy/utils/map-utils.ts deleted file mode 100644 index 428843af92..0000000000 --- a/packages/schema/parse/src/extract-legacy/utils/map-utils.ts +++ /dev/null @@ -1,37 +0,0 @@ -// const _toGraphQLType = (rootType: string, type: string): string => { -// const parsedCurrentType = _parseCurrentType(rootType, type); -// let { subType } = parsedCurrentType; -// const { currentType } = parsedCurrentType; - -// if (!subType) { -// return currentType; -// } - -// switch (currentType) { -// case "Array": { -// if (subType.endsWith("!")) { -// subType = subType.slice(0, -1); -// } -// return `[${_toGraphQLType(rootType, subType)}]`; -// } -// case "Map": { -// const firstDelimiter = subType.indexOf(","); - -// const keyType = subType.substring(0, firstDelimiter).trim(); -// const valType = subType.substring(firstDelimiter + 1).trim(); - -// return `Map<${_toGraphQLType(rootType, keyType)}, ${_toGraphQLType( -// rootType, -// valType -// )}>`; -// } -// default: -// throw new Error( -// `Found unknown type ${currentType} while parsing ${rootType}` -// ); -// } -// }; - -// export function toGraphQLType(type: string): string { -// return _toGraphQLType(type, type); -// } diff --git a/packages/schema/parse/src/extract-legacy/utils/module-types-utils.ts b/packages/schema/parse/src/extract-legacy/utils/module-types-utils.ts deleted file mode 100644 index 1016c1cdc5..0000000000 --- a/packages/schema/parse/src/extract-legacy/utils/module-types-utils.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { createPropertyDefinition, createArrayDefinition } from "../.."; - -import { - BooleanValueNode, - FieldDefinitionNode, - InputValueDefinitionNode, - NamedTypeNode, -} from "graphql"; -import { - ImportedModuleDefinition, - InterfaceDefinition, - MapDefinition, - MethodDefinition, - PropertyDefinition, -} from "@polywrap/wrap-manifest-types-js"; - -export interface EnvDirDefinition { - required: boolean; -} - -export interface State { - insideModule: boolean; - currentMethod?: MethodDefinition; - currentArgument?: PropertyDefinition; - currentReturn?: PropertyDefinition; - nonNullType?: boolean; - currentInterfaces?: InterfaceDefinition[]; - currentImport?: ImportedModuleDefinition; -} - -export function extractNamedType(node: NamedTypeNode, state: State): void { - const argument = state.currentArgument; - const method = state.currentMethod; - - if (method && argument) { - if (!argument.name) { - throw Error( - "extractNamedType: Invalid state. Uninitialized currentArgument, name not found.\n" + - `Argument: ${JSON.stringify( - argument, - null, - 2 - )}\nState: ${JSON.stringify(state, null, 2)}` - ); - } - - // Argument value - // setPropertyType(argument, argument.name, { - // type: node.name.value, - // required: state.nonNullType, - // }); - - state.nonNullType = undefined; - } else if (method) { - // Return value - if (!state.currentReturn) { - state.currentReturn = method.return; - } - - if (!method.name) { - throw Error( - "extractNamedType: Invalid state. Uninitialized currentMethod, name not found.\n" + - `Method: ${JSON.stringify(method, null, 2)}\nState: ${JSON.stringify( - state, - null, - 2 - )}` - ); - } - - if (state.currentReturn) { - // setPropertyType(state.currentReturn, method.name, { - // type: node.name.value, - // required: state.nonNullType, - // }); - } - - state.nonNullType = undefined; - } -} - -export function extractListType(state: State): void { - const argument = state.currentArgument; - const method = state.currentMethod; - - if (method && argument) { - // Argument value - argument.array = createArrayDefinition({ - name: argument.name, - type: "N/A", - required: state.nonNullType, - }); - state.currentArgument = argument.array; - state.nonNullType = undefined; - } else if (method) { - // Return value - if (!method.return) { - method.return = createPropertyDefinition({ - type: "N/A", - name: method.name, - }); - state.currentReturn = method.return; - } else if (!state.currentReturn) { - state.currentReturn = method.return; - } - - state.currentReturn.array = createArrayDefinition({ - type: "N/A", - name: method.name, - required: state.nonNullType, - }); - state.currentReturn = state.currentReturn.array; - state.nonNullType = undefined; - } -} - -export function extractInputValueDefinition( - node: InputValueDefinitionNode, - state: State -): void { - const method = state.currentMethod; - - if (!method) { - return; - } - - const name = node.name.value; - // const { type, def } = extractAnnotateDirective(node, name); - - // const argument = createPropertyDefinition({ - // type: type ? type : "N/A", - // name: name, - // map: def ? (def as MapDefinition) : undefined, - // comment: node.description?.value, - // required: def && def.required ? true : undefined, - // }); - - if (!method.arguments) { - method.arguments = []; - } - // method.arguments.push(argument); - // state.currentArgument = argument; -} - -export function extractEnvDirective( - node: FieldDefinitionNode -): EnvDirDefinition | undefined { - if (node.directives) { - for (const dir of node.directives) { - if (dir.name.value === "env") { - const required = (dir.arguments?.find( - (arg) => arg.name.value === "required" - )?.value as BooleanValueNode).value; - if (required === undefined) { - throw new Error( - `Env directive: ${node.name.value} has invalid arguments` - ); - } - return { - required, - }; - } - } - } - - return undefined; -} diff --git a/packages/schema/parse/src/extract/directives.ts b/packages/schema/parse/src/extract/directives.ts index 2d35bb506a..31df3445e9 100644 --- a/packages/schema/parse/src/extract/directives.ts +++ b/packages/schema/parse/src/extract/directives.ts @@ -1,8 +1,11 @@ import { DirectiveNode, FieldDefinitionNode } from "graphql"; import { MapType, UniqueDefKind } from "../definitions"; -import { EnvDirDefinition } from "../extract-legacy/utils/module-types-utils"; import { parseMapString } from "./utils"; +interface EnvDirDefinition { + required: boolean; +} + export const parseDirectivesInField = (node: FieldDefinitionNode, uniqueDefs: Map) => { let map: MapType | undefined; let env: EnvDirDefinition | undefined; diff --git a/packages/schema/parse/src/extract/index.ts b/packages/schema/parse/src/extract/index.ts new file mode 100644 index 0000000000..559f5d326c --- /dev/null +++ b/packages/schema/parse/src/extract/index.ts @@ -0,0 +1,4 @@ +export * from "./object"; +export * from "./env"; +export * from "./enum"; +export * from "./module"; \ No newline at end of file diff --git a/packages/schema/parse/src/extract/types.ts b/packages/schema/parse/src/extract/types.ts index 951a7748a4..236e6c36f8 100644 --- a/packages/schema/parse/src/extract/types.ts +++ b/packages/schema/parse/src/extract/types.ts @@ -1,6 +1,8 @@ import { ASTVisitor } from "graphql"; -import { Abi } from "../definitions"; +import { Abi, UniqueDefKind } from "../definitions"; export interface VisitorBuilder { build(abi: Abi): ASTVisitor -} \ No newline at end of file +} + +export type ExternalVisitorBuilder = (abi: Abi, uniqueDefs?: Map) => ASTVisitor \ No newline at end of file From 40db593d258bce60fabe8750eeb167e1b85a331f Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 9 Jan 2023 01:08:39 +0100 Subject: [PATCH 11/90] (feat): added main transform visitor funcs. Added pre-pass visit --- packages/schema/parse/src/abi/index.ts | 11 +- packages/schema/parse/src/index.ts | 43 +- .../src/transform/finalizePropertyDef.ts | 213 ------- packages/schema/parse/src/transform/index.ts | 556 ++++-------------- 4 files changed, 164 insertions(+), 659 deletions(-) delete mode 100644 packages/schema/parse/src/transform/finalizePropertyDef.ts diff --git a/packages/schema/parse/src/abi/index.ts b/packages/schema/parse/src/abi/index.ts index 6ad712a5a4..e8003ff028 100644 --- a/packages/schema/parse/src/abi/index.ts +++ b/packages/schema/parse/src/abi/index.ts @@ -1,4 +1,4 @@ -import { Abi } from "@polywrap/wrap-manifest-types-js"; +import { Abi } from "../definitions"; export * from "@polywrap/wrap-manifest-types-js"; export * from "./definitions"; @@ -7,13 +7,6 @@ export * from "./utils"; export function createAbi(): Abi { return { - version: "0.1", - objectTypes: [], - enumTypes: [], - interfaceTypes: [], - importedObjectTypes: [], - importedModuleTypes: [], - importedEnumTypes: [], - importedEnvTypes: [], + version: "0.2" }; } diff --git a/packages/schema/parse/src/index.ts b/packages/schema/parse/src/index.ts index 43f84b99e3..51114786a5 100644 --- a/packages/schema/parse/src/index.ts +++ b/packages/schema/parse/src/index.ts @@ -1,13 +1,16 @@ -import { createAbi } from "./abi"; -import { extractors, SchemaExtractorBuilder } from "./extract"; +import { createAbi, isEnvType, isModuleType } from "./abi"; import { AbiTransforms, transformAbi, finalizePropertyDef } from "./transform"; import { validators, SchemaValidatorBuilder } from "./validate"; import { DocumentNode, parse, visit, visitInParallel } from "graphql"; -import { WrapAbi } from "@polywrap/wrap-manifest-types-js"; +import { Abi, UniqueDefKind } from "./definitions"; +import { ExternalVisitorBuilder, VisitorBuilder } from "./extract/types"; +import { ObjectVisitorBuilder } from "./extract"; +import { EnvVisitorBuilder } from "./extract"; +import { EnumVisitorBuilder } from "./extract"; +import { ModuleVisitorBuilder } from "./extract"; export * from "./abi"; -export * from "./extract"; export * from "./transform"; export * from "./validate"; export * from "./header"; @@ -18,16 +21,42 @@ interface ParserOptions { // Use custom validators validators?: SchemaValidatorBuilder[]; // Use custom extractors - extractors?: SchemaExtractorBuilder[]; + extractors?: ExternalVisitorBuilder[]; // Use custom transformations transforms?: AbiTransforms[]; } +const extractUniqueDefinitionNames = (document: DocumentNode): Map => { + const uniqueDefs = new Map(); + + visit(document, { + ObjectTypeDefinition: (node) => { + const name = node.name.value; + + if (!isModuleType(name) && !isEnvType(name)) { + uniqueDefs.set(name, "Object") + } + }, + EnumTypeDefinition: (node) => { + uniqueDefs.set(node.name.value, "Enum") + } + }); + + return uniqueDefs; +} + export function parseSchema( schema: string, options: ParserOptions = {} -): WrapAbi { +): Abi { const astNode = parse(schema); + const uniqueDefs = extractUniqueDefinitionNames(astNode); + const defaultExtractors: VisitorBuilder[] = [ + new ObjectVisitorBuilder(uniqueDefs), + new EnvVisitorBuilder(uniqueDefs), + new EnumVisitorBuilder(), + new ModuleVisitorBuilder(uniqueDefs) + ] // Validate GraphQL Schema if (!options.noValidate) { @@ -38,7 +67,7 @@ export function parseSchema( // Extract & Build Abi let info = createAbi(); - const extracts = options.extractors || extractors; + const extracts = options.extractors?.map(extractorBuilder => extractorBuilder(info, uniqueDefs)) ?? defaultExtractors.map(e => e.build(info)); extract(astNode, info, extracts); // Finalize & Transform Abi diff --git a/packages/schema/parse/src/transform/finalizePropertyDef.ts b/packages/schema/parse/src/transform/finalizePropertyDef.ts deleted file mode 100644 index d22fe27398..0000000000 --- a/packages/schema/parse/src/transform/finalizePropertyDef.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { AbiTransforms } from "."; -import { createEnumRef, createObjectRef } from ".."; - -import { - AnyDefinition, - ArrayDefinition, - GenericDefinition, - MapDefinition, - PropertyDefinition, - WrapAbi, -} from "@polywrap/wrap-manifest-types-js"; - -export const finalizePropertyDef = (abi: WrapAbi): AbiTransforms => { - return { - enter: { - // eslint-disable-next-line @typescript-eslint/naming-convention - PropertyDefinition: (def: PropertyDefinition): PropertyDefinition => { - populatePropertyType(def, abi); - return def; - }, - }, - }; -}; - -export function populatePropertyType( - property: PropertyDefinition, - abi: WrapAbi -): void { - let propertyType: GenericDefinition | undefined; - if (property.array) { - populateArrayType(property.array, abi); - propertyType = property.array; - } else if (property.unresolvedObjectOrEnum) { - propertyType = resolveObjectOrEnumKind(property, abi); - } else if (property.scalar) { - propertyType = property.scalar; - } else if (property.object) { - propertyType = property.object; - } else if (property.enum) { - propertyType = property.enum; - } else if (property.map) { - populateMapType(property.map, abi); - propertyType = property.map; - } else { - throw Error("Property type is undefined, this should never happen."); - } - - property.type = propertyType.type; - property.required = propertyType.required; -} - -function populateMapType(map: MapDefinition, abi: WrapAbi) { - let baseTypeFound = false; - - let currentType: AnyDefinition = map; - while (!baseTypeFound) { - if (currentType.map) { - currentType = currentType.map; - populateMapType(currentType as MapDefinition, abi); - } else if (currentType.array) { - currentType = currentType.array; - populateArrayType(currentType as ArrayDefinition, abi); - } else if ( - currentType.scalar || - currentType.object || - currentType.enum || - currentType.unresolvedObjectOrEnum - ) { - baseTypeFound = true; - } else { - throw Error( - `This should never happen, MapDefinition is malformed.\n${JSON.stringify( - map, - null, - 2 - )}` - ); - } - } - - if (map.array) { - map.value = map.array; - } else if (map.unresolvedObjectOrEnum) { - map.value = resolveObjectOrEnumKind(map, abi); - } else if (map.scalar) { - map.value = map.scalar; - } else if (map.enum) { - map.value = map.enum; - } else if (map.map) { - map.value = map.map; - } else { - map.value = map.object; - } - - if (!map.value) { - throw Error("Map isn't valid."); - } -} - -function populateArrayType(array: ArrayDefinition, abi: WrapAbi) { - let baseTypeFound = false; - - let currentArray = array; - while (!baseTypeFound) { - if (currentArray.array) { - currentArray = currentArray.array; - populateArrayType(currentArray, abi); - } else if ( - currentArray.scalar || - currentArray.object || - currentArray.enum || - currentArray.unresolvedObjectOrEnum - ) { - baseTypeFound = true; - } else { - throw Error( - `This should never happen, ArrayDefinition is malformed.\n${JSON.stringify( - array, - null, - 2 - )}` - ); - } - } - - if (array.array) { - array.item = array.array; - } else if (array.unresolvedObjectOrEnum) { - array.item = resolveObjectOrEnumKind(array, abi); - } else if (array.scalar) { - array.item = array.scalar; - } else if (array.enum) { - array.item = array.enum; - } else if (array.map) { - array.item = array.map; - } else { - array.item = array.object; - } - - if (!array.item) { - throw Error("Array isn't valid."); - } - - array.type = "[" + array.item.type + "]"; -} - -function resolveObjectOrEnumKind( - property: PropertyDefinition, - abi: WrapAbi -): GenericDefinition { - if (!property.unresolvedObjectOrEnum) { - throw Error("Type reference is undefined, this should never happen."); - } - - const unresolved = property.unresolvedObjectOrEnum; - - // Check to see if the type is a part of the custom types defined inside the schema (objects, enums, envs) - let customType: GenericDefinition | undefined = - abi.objectTypes && - abi.objectTypes.find((type) => type.type === unresolved.type); - - customType = customType - ? customType - : abi.importedObjectTypes && - abi.importedObjectTypes.find((type) => type.type === unresolved.type); - - const envType = abi.envType; - customType = customType - ? customType - : envType?.type === unresolved.type - ? envType - : undefined; - - customType = customType - ? customType - : abi.importedEnvTypes && - abi.importedEnvTypes.find((type) => type.type === unresolved.type); - - if (!customType) { - customType = - abi.enumTypes && - abi.enumTypes.find((type) => type.type === unresolved.type); - - customType = customType - ? customType - : abi.importedEnumTypes && - abi.importedEnumTypes.find((type) => type.type === unresolved.type); - - if (!customType) { - throw new Error(`Unsupported type ${unresolved.type}`); - } - - property.enum = createEnumRef({ - name: unresolved.name, - required: unresolved.required ?? undefined, - type: unresolved.type, - }); - - property.unresolvedObjectOrEnum = undefined; - - return property.enum; - } else { - property.object = createObjectRef({ - name: property.unresolvedObjectOrEnum.name, - required: property.unresolvedObjectOrEnum.required ?? undefined, - type: property.unresolvedObjectOrEnum.type, - }); - - property.unresolvedObjectOrEnum = undefined; - - return property.object; - } -} diff --git a/packages/schema/parse/src/transform/index.ts b/packages/schema/parse/src/transform/index.ts index 8990ae7093..3c432364c4 100644 --- a/packages/schema/parse/src/transform/index.ts +++ b/packages/schema/parse/src/transform/index.ts @@ -1,154 +1,52 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/naming-convention */ -import { DefinitionKind, isKind } from "../abi"; - -import { - AnyDefinition, - EnvDefinition, - ImportedEnvDefinition, - GenericDefinition, - ObjectDefinition, - ScalarDefinition, - PropertyDefinition, - ArrayDefinition, - MethodDefinition, - ModuleDefinition, - ImportedModuleDefinition, - ImportedObjectDefinition, - EnumDefinition, - ImportedEnumDefinition, - InterfaceImplementedDefinition, - EnumRef, - ObjectRef, - InterfaceDefinition, - WithKind, - MapDefinition, - WrapAbi, -} from "@polywrap/wrap-manifest-types-js"; - -export * from "./finalizePropertyDef"; -export * from "./extendType"; -export * from "./addFirstLast"; -export * from "./interfaceUris"; -export * from "./methodParentPointers"; -export * from "./toGraphQLType"; -export * from "./moduleCapabilities"; -export * from "./hasImports"; -export * from "./addAnnotations"; +import { Abi, AnyType, ArgumentDef, ArrayType, EnumDef, EnvDef, FunctionDef, MapKeyTypeName, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType } from "../definitions"; export interface AbiTransforms { enter?: AbiTransformer; leave?: AbiTransformer; } +type TypeToTransform = + | FunctionDef + | ObjectDef + | EnumDef + | EnvDef + | ArgumentDef + | ResultDef + | PropertyDef + | ScalarType + | ArrayType + | MapType + | RefType + export interface AbiTransformer { - Abi?: (abi: WrapAbi) => WrapAbi; - GenericDefinition?: (def: GenericDefinition) => GenericDefinition; - ObjectDefinition?: (def: ObjectDefinition) => ObjectDefinition; - ObjectRef?: (def: ObjectRef) => ObjectRef; - AnyDefinition?: (def: AnyDefinition) => AnyDefinition; - ScalarDefinition?: (def: ScalarDefinition) => ScalarDefinition; - EnumDefinition?: (def: EnumDefinition) => EnumDefinition; - EnumRef?: (def: EnumRef) => EnumRef; - PropertyDefinition?: (def: PropertyDefinition) => PropertyDefinition; - ArrayDefinition?: (def: ArrayDefinition) => ArrayDefinition; - MethodDefinition?: (def: MethodDefinition) => MethodDefinition; - ModuleDefinition?: (def: ModuleDefinition) => ModuleDefinition; - InterfaceDefinition?: (def: InterfaceDefinition) => InterfaceDefinition; - ImportedEnumDefinition?: ( - def: ImportedEnumDefinition - ) => ImportedEnumDefinition; - ImportedModuleDefinition?: ( - def: ImportedModuleDefinition - ) => ImportedModuleDefinition; - ImportedEnvDefinition?: (def: ImportedEnvDefinition) => ImportedEnvDefinition; - ImportedObjectDefinition?: ( - def: ImportedObjectDefinition - ) => ImportedObjectDefinition; - InterfaceImplementedDefinition?: ( - def: InterfaceImplementedDefinition - ) => InterfaceImplementedDefinition; - EnvDefinition?: (def: EnvDefinition) => EnvDefinition; - MapDefinition?: (def: MapDefinition) => MapDefinition; + FunctionDefinition?: (def: FunctionDef) => FunctionDef + ObjectDefinition?: (def: ObjectDef) => ObjectDef + EnumDefinition?: (def: EnumDef) => EnumDef + EnvDefinition?: (def: EnvDef) => EnvDef + + ArgumentDefinition?: (def: ArgumentDef) => ArgumentDef + ResultDefinition?: (def: ResultDef) => ResultDef + PropertyDefinition?: (def: PropertyDef) => PropertyDef + + ScalarType?: (type: ScalarType) => ScalarType + ArrayType?: (type: ArrayType) => ArrayType + MapType?: (type: MapType) => MapType + RefType?: (type: RefType) => RefType + + Abi?: (abi: Abi) => Abi } -export function transformAbi(abi: WrapAbi, transforms: AbiTransforms): WrapAbi { +export const transformAbi = (abi: Abi, transforms: AbiTransforms): Abi => { let result = Object.assign({}, abi); if (transforms.enter && transforms.enter.Abi) { result = transforms.enter.Abi(result); } - if (result.interfaceTypes) { - for (let i = 0; i < result.interfaceTypes.length; ++i) { - result.interfaceTypes[i] = visitInterfaceDefinition( - result.interfaceTypes[i], - transforms - ); - } - } - - if (result.enumTypes) { - for (let i = 0; i < result.enumTypes.length; ++i) { - result.enumTypes[i] = visitEnumDefinition( - result.enumTypes[i], - transforms - ); - } - } - - if (result.objectTypes) { - for (let i = 0; i < result.objectTypes.length; ++i) { - result.objectTypes[i] = visitObjectDefinition( - result.objectTypes[i], - transforms - ); - } - } - - if (result.moduleType) { - result.moduleType = visitModuleDefinition(result.moduleType, transforms); - } - - if (result.envType) { - result.envType = visitEnvDefinition(result.envType, transforms); - } - - if (result.importedObjectTypes) { - for (let i = 0; i < result.importedObjectTypes.length; ++i) { - result.importedObjectTypes[i] = visitImportedObjectDefinition( - result.importedObjectTypes[i], - transforms - ); - } - } - - if (result.importedModuleTypes) { - for (let i = 0; i < result.importedModuleTypes.length; ++i) { - result.importedModuleTypes[i] = visitImportedModuleDefinition( - result.importedModuleTypes[i], - transforms - ); - } - } - - if (result.importedEnumTypes) { - for (let i = 0; i < result.importedEnumTypes.length; ++i) { - result.importedEnumTypes[i] = visitImportedEnumDefinition( - result.importedEnumTypes[i], - transforms - ); - } - } - - if (result.importedEnvTypes) { - for (let i = 0; i < result.importedEnvTypes.length; ++i) { - result.importedEnvTypes[i] = visitImportedEnvDefinition( - result.importedEnvTypes[i], - transforms - ); - } - } + result.enums = result.enums?.map(def => visitEnumDefinition(def, transforms)) + result.objects = result.objects?.map(def => visitObjectDefinition(def, transforms)) + result.functions = result.functions?.map(def => visitFunctionDefinition(def, transforms)) + result.env = result.env ? visitEnvDefinition(result.env, transforms) : undefined if (transforms.leave && transforms.leave.Abi) { result = transforms.leave.Abi(result); @@ -157,351 +55,149 @@ export function transformAbi(abi: WrapAbi, transforms: AbiTransforms): WrapAbi { return result; } -export function visitObjectDefinition( - def: ObjectDefinition, - transforms: AbiTransforms -): ObjectDefinition { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - - if (result.properties) { - for (let i = 0; i < result.properties.length; ++i) { - result.properties[i] = visitPropertyDefinition( - result.properties[i], - transforms - ); - } +const transformType = (type: T, transform?: AbiTransformer): T => { + if (!transform) { + return type; } - if (result.interfaces) { - for (let i = 0; i < result.interfaces.length; ++i) { - result.interfaces[i] = visitInterfaceImplementedDefinition( - result.interfaces[i], - transforms - ); - } + let result: T = Object.assign({}, type); + const { + FunctionDefinition, + ObjectDefinition, + EnumDefinition, + EnvDefinition, + ArgumentDefinition, + ResultDefinition, + PropertyDefinition, + ScalarType, + ArrayType, + MapType, + RefType, + } = transform; + + if (FunctionDefinition && result.kind === "Function") { + result = Object.assign(result, FunctionDefinition(result)) + } else if (ObjectDefinition && result.kind === "Object") { + result = Object.assign(result, ObjectDefinition(result)) + } else if (EnumDefinition && result.kind === "Enum") { + result = Object.assign(result, EnumDefinition(result)) + } else if (EnvDefinition && result.kind === "Env") { + result = Object.assign(result, EnvDefinition(result)) + } else if (ArgumentDefinition && result.kind === "Argument") { + result = Object.assign(result, ArgumentDefinition(result)) + } else if (ResultDefinition && result.kind === "Result") { + result = Object.assign(result, ResultDefinition(result)) + } else if (PropertyDefinition && result.kind === "Property") { + result = Object.assign(result, PropertyDefinition(result)) + } else if (ScalarType && result.kind === "Scalar") { + result = Object.assign(result, ScalarType(result)) + } else if (ArrayType && result.kind === "Array") { + result = Object.assign(result, ArrayType(result)) + } else if (MapType && result.kind === "Map") { + result = Object.assign(result, MapType(result)) + } else if (RefType && result.kind === "Ref") { + result = Object.assign(result, RefType(result)) } - return transformType(result, transforms.leave); + return result; } -export function visitObjectRef( - def: ObjectRef, - transforms: AbiTransforms -): ObjectRef { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); +const visitRefType = (type: RefType, transforms: AbiTransforms) => { + let result = Object.assign({}, type); + result = transformType(result, transforms.enter) - return transformType(result, transforms.leave); + return transformType(result, transforms.leave) } -export function visitInterfaceImplementedDefinition( - def: InterfaceImplementedDefinition, - transforms: AbiTransforms -): InterfaceImplementedDefinition { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); +const visitScalarType = (type: ScalarType, transforms: AbiTransforms) => { + let result = Object.assign({}, type); + result = transformType(result, transforms.enter) return transformType(result, transforms.leave); } -export function visitAnyDefinition( - def: AnyDefinition, - transforms: AbiTransforms -): AnyDefinition { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - - if (result.array) { - result.array = visitArrayDefinition(result.array, transforms); - } - - if (result.map) { - result.map = visitMapDefinition(result.map, transforms); - } - - if (result.scalar) { - result.scalar = visitScalarDefinition(result.scalar, transforms); - } - - if (result.object) { - result.object = visitObjectRef(result.object, transforms); - } - - if (result.enum) { - result.enum = visitEnumRef(result.enum, transforms); - } - - return result; -} +const visitArrayType = (type: ArrayType, transforms: AbiTransforms) => { + let result = Object.assign({}, type); + result = transformType(result, transforms.enter) -export function visitScalarDefinition( - def: ScalarDefinition, - transforms: AbiTransforms -): ScalarDefinition { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); + result.item = visitAnyType(result.item, transforms) return transformType(result, transforms.leave); } -export function visitEnumDefinition( - def: EnumDefinition, - transforms: AbiTransforms -): EnumDefinition { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - return transformType(result, transforms.leave); -} +const visitMapType = (type: MapType, transforms: AbiTransforms) => { + let result = Object.assign({}, type); + result = transformType(result, transforms.enter) -export function visitEnumRef(def: EnumRef, transforms: AbiTransforms): EnumRef { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); + result.key = visitScalarType(result.key, transforms) as ScalarType + result.value = visitAnyType(result.value, transforms) return transformType(result, transforms.leave); } -export function visitArrayDefinition( - def: ArrayDefinition, - transforms: AbiTransforms -): ArrayDefinition { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - - result = visitAnyDefinition(result, transforms) as any; - - if (result.item) { - result.item = transformType(result.item, transforms.enter); - result.item = transformType(result.item, transforms.leave); +const visitAnyType = (type: AnyType, transforms: AbiTransforms) => { + switch (type.kind) { + case "Ref": return visitRefType(type, transforms); + case "Array": return visitArrayType(type, transforms); + case "Scalar": return visitScalarType(type, transforms); + case "Map": return visitMapType(type, transforms); } - - return transformType(result, transforms.leave); } -export function visitPropertyDefinition( - def: PropertyDefinition, - transforms: AbiTransforms -): PropertyDefinition { +const visitPropertyDefinition = (def: PropertyDef, transforms: AbiTransforms) => { let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - - result = visitAnyDefinition(result, transforms); + result = transformType(result, transforms.enter) + result.type = visitAnyType(result.type, transforms); - return transformType(result, transforms.leave); + return transformType(result, transforms.leave) } -export function visitMethodDefinition( - def: MethodDefinition, - transforms: AbiTransforms -): MethodDefinition { +const visitObjectDefinition = (def: ObjectDef, transforms: AbiTransforms) => { let result = Object.assign({}, def); result = transformType(result, transforms.enter); - if (result.arguments) { - for (let i = 0; i < result.arguments.length; ++i) { - result.arguments[i] = visitPropertyDefinition( - result.arguments[i], - transforms - ); - } - } + result.props.forEach((prop, i) => { + result.props[i] = visitPropertyDefinition(prop, transforms) + }) - if (result.return) { - result.return = visitPropertyDefinition(result.return, transforms); - } - - return transformType(result, transforms.leave); + return transformType(result, transforms.leave) } -export function visitModuleDefinition( - def: ModuleDefinition, - transforms: AbiTransforms -): ModuleDefinition { +const visitEnvDefinition = (def: EnvDef, transforms: AbiTransforms) => { let result = Object.assign({}, def); result = transformType(result, transforms.enter); - if (result.methods) { - for (let i = 0; i < result.methods.length; ++i) { - result.methods[i] = visitMethodDefinition(result.methods[i], transforms); - } - } + result.props.forEach((prop, i) => { + result.props[i] = visitPropertyDefinition(prop, transforms) + }) - return transformType(result, transforms.leave); + return transformType(result, transforms.leave) } -export function visitInterfaceDefinition( - def: InterfaceDefinition, - transforms: AbiTransforms -): InterfaceDefinition { +const visitEnumDefinition = (def: EnumDef, transforms: AbiTransforms) => { let result = Object.assign({}, def); result = transformType(result, transforms.enter); - return transformType(result, transforms.leave); + return transformType(result, transforms.leave) } -export function visitImportedModuleDefinition( - def: ImportedModuleDefinition, - transforms: AbiTransforms -): ImportedModuleDefinition { +const visitArgumentDefinition = (def: ArgumentDef, transforms: AbiTransforms) => { let result = Object.assign({}, def); result = transformType(result, transforms.enter); - - if (result.methods) { - for (let i = 0; i < result.methods.length; ++i) { - result.methods[i] = visitMethodDefinition(result.methods[i], transforms); - } - } - - return transformType(result, transforms.leave); + result.type = visitAnyType(result.type, transforms); + return transformType(result, transforms.leave) } -export function visitImportedObjectDefinition( - def: ImportedObjectDefinition, - transforms: AbiTransforms -): ImportedObjectDefinition { - return visitObjectDefinition(def, transforms) as ImportedObjectDefinition; -} - -export function visitImportedEnumDefinition( - def: ImportedEnumDefinition, - transforms: AbiTransforms -): ImportedEnumDefinition { - return visitEnumDefinition(def, transforms) as ImportedEnumDefinition; -} - -export function visitImportedEnvDefinition( - def: ImportedEnvDefinition, - transforms: AbiTransforms -): ImportedEnvDefinition { - return visitEnvDefinition(def, transforms) as ImportedEnvDefinition; -} - -export function visitEnvDefinition( - def: EnvDefinition, - transforms: AbiTransforms -): EnvDefinition { - return visitObjectDefinition(def, transforms); -} - -export function visitMapDefinition( - def: MapDefinition, - transforms: AbiTransforms -): MapDefinition { +const visitResultDefinition = (def: ResultDef, transforms: AbiTransforms) => { let result = Object.assign({}, def); result = transformType(result, transforms.enter); - - result = visitAnyDefinition(result, transforms) as any; - - if (result.key) { - result.key = transformType(result.key, transforms.enter); - result.key = transformType(result.key, transforms.leave); - } - - if (result.value) { - result.value = transformType(result.value, transforms.enter); - result.value = transformType(result.value, transforms.leave); - } - - return transformType(result, transforms.leave); + result.type = visitAnyType(result.type, transforms); + return transformType(result, transforms.leave) } -export function transformType( - type: TDefinition, - transform?: AbiTransformer -): TDefinition { - if (!transform) { - return type; - } - - let result = Object.assign({}, type); - const { - GenericDefinition, - ObjectDefinition, - ObjectRef, - AnyDefinition, - ScalarDefinition, - EnumDefinition, - EnumRef, - ArrayDefinition, - PropertyDefinition, - MethodDefinition, - ModuleDefinition, - InterfaceDefinition, - ImportedEnumDefinition, - ImportedModuleDefinition, - ImportedObjectDefinition, - InterfaceImplementedDefinition, - EnvDefinition, - MapDefinition, - ImportedEnvDefinition, - } = transform; +const visitFunctionDefinition = (def: FunctionDef, transforms: AbiTransforms) => { + let result = Object.assign({}, def); + result = transformType(result, transforms.enter); - if (GenericDefinition && isKind(result, DefinitionKind.Generic)) { - result = Object.assign(result, GenericDefinition(result as any)); - } - if (ObjectDefinition && isKind(result, DefinitionKind.Object)) { - result = Object.assign(result, ObjectDefinition(result as any)); - } - if (ObjectRef && isKind(result, DefinitionKind.ObjectRef)) { - result = Object.assign(result, ObjectRef(result as any)); - } - if (AnyDefinition && isKind(result, DefinitionKind.Any)) { - result = Object.assign(result, AnyDefinition(result as any)); - } - if (ScalarDefinition && isKind(result, DefinitionKind.Scalar)) { - result = Object.assign(result, ScalarDefinition(result as any)); - } - if (EnumDefinition && isKind(result, DefinitionKind.Enum)) { - result = Object.assign(result, EnumDefinition(result as any)); - } - if (EnumRef && isKind(result, DefinitionKind.EnumRef)) { - result = Object.assign(result, EnumRef(result as any)); - } - if (ArrayDefinition && isKind(result, DefinitionKind.Array)) { - result = Object.assign(result, ArrayDefinition(result as any)); - } - if (PropertyDefinition && isKind(result, DefinitionKind.Property)) { - result = Object.assign(result, PropertyDefinition(result as any)); - } - if (MethodDefinition && isKind(result, DefinitionKind.Method)) { - result = Object.assign(result, MethodDefinition(result as any)); - } - if (ModuleDefinition && isKind(result, DefinitionKind.Module)) { - result = Object.assign(result, ModuleDefinition(result as any)); - } - if (InterfaceDefinition && isKind(result, DefinitionKind.Interface)) { - result = Object.assign(result, InterfaceDefinition(result as any)); - } - if ( - ImportedModuleDefinition && - isKind(result, DefinitionKind.ImportedModule) - ) { - result = Object.assign(result, ImportedModuleDefinition(result as any)); - } - if (ImportedEnumDefinition && isKind(result, DefinitionKind.ImportedEnum)) { - result = Object.assign(result, ImportedEnumDefinition(result as any)); - } - if ( - ImportedObjectDefinition && - isKind(result, DefinitionKind.ImportedObject) - ) { - result = Object.assign(result, ImportedObjectDefinition(result as any)); - } - if ( - InterfaceImplementedDefinition && - isKind(result, DefinitionKind.InterfaceImplemented) - ) { - result = Object.assign( - result, - InterfaceImplementedDefinition(result as any) - ); - } - if (EnvDefinition && isKind(result, DefinitionKind.Env)) { - result = Object.assign(result, EnvDefinition(result as any)); - } - if (ImportedEnvDefinition && isKind(result, DefinitionKind.ImportedEnv)) { - result = Object.assign(result, ImportedEnvDefinition(result as any)); - } - if (MapDefinition && isKind(result, DefinitionKind.Map)) { - result = Object.assign(result, MapDefinition(result as any)); - } + result.args = result.args.map(arg => visitArgumentDefinition(arg, transforms)) + result.result = visitResultDefinition(result.result, transforms) - return result; -} + return transformType(result, transforms.leave) +} \ No newline at end of file From 8fa38b0cddbc6cb48f340ef50043064313c8135b Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 9 Jan 2023 14:41:11 +0100 Subject: [PATCH 12/90] (feat): added Definition transformer, fixed transforms, added toMapStr --- packages/schema/parse/src/extract/utils.ts | 21 +++++++- packages/schema/parse/src/index.ts | 5 +- .../parse/src/transform/addAnnotations.ts | 14 +++--- .../parse/src/transform/addFirstLast.ts | 15 +++--- .../schema/parse/src/transform/extendType.ts | 6 +-- .../schema/parse/src/transform/hasImports.ts | 13 +---- packages/schema/parse/src/transform/index.ts | 48 ++++++++++++++----- 7 files changed, 77 insertions(+), 45 deletions(-) diff --git a/packages/schema/parse/src/extract/utils.ts b/packages/schema/parse/src/extract/utils.ts index fc3d6a02eb..ed144d3316 100644 --- a/packages/schema/parse/src/extract/utils.ts +++ b/packages/schema/parse/src/extract/utils.ts @@ -120,4 +120,23 @@ const isMapKey = (typeName: string): boolean => { const isArray = (typeName: string): boolean => { return typeName.startsWith("[") -} \ No newline at end of file +} + +export const toMapString = (map: MapType): string => { + const stringifyMapValueType = (anyType: AnyType, required: boolean): string => { + let result = ""; + switch (anyType.kind) { + case "Array": result = `[${stringifyMapValueType(anyType.item, anyType.required)}]` + break; + case "Ref": result = anyType.ref_name + break; + case "Scalar": result = anyType.scalar + break; + case "Map": result = toMapString(anyType) + break; + } + return required ? `${result}!` : result; + } + + return `Map<${map.key.scalar}!, ${stringifyMapValueType(map.value, map.required)}>` +} diff --git a/packages/schema/parse/src/index.ts b/packages/schema/parse/src/index.ts index 51114786a5..4cf3085431 100644 --- a/packages/schema/parse/src/index.ts +++ b/packages/schema/parse/src/index.ts @@ -1,5 +1,5 @@ import { createAbi, isEnvType, isModuleType } from "./abi"; -import { AbiTransforms, transformAbi, finalizePropertyDef } from "./transform"; +import { AbiTransforms, transformAbi } from "./transform"; import { validators, SchemaValidatorBuilder } from "./validate"; import { DocumentNode, parse, visit, visitInParallel } from "graphql"; @@ -70,9 +70,6 @@ export function parseSchema( const extracts = options.extractors?.map(extractorBuilder => extractorBuilder(info, uniqueDefs)) ?? defaultExtractors.map(e => e.build(info)); extract(astNode, info, extracts); - // Finalize & Transform Abi - info = transformAbi(info, finalizePropertyDef(info)); - if (options && options.transforms) { for (const transform of options.transforms) { info = transformAbi(info, transform); diff --git a/packages/schema/parse/src/transform/addAnnotations.ts b/packages/schema/parse/src/transform/addAnnotations.ts index f64500d433..8640b1e5a8 100644 --- a/packages/schema/parse/src/transform/addAnnotations.ts +++ b/packages/schema/parse/src/transform/addAnnotations.ts @@ -1,18 +1,18 @@ -import { toGraphQL } from "."; import { AbiTransforms } from ".."; - -import { PropertyDefinition } from "@polywrap/wrap-manifest-types-js"; +import { PropertyDef } from "../definitions"; +import { toMapString } from "../extract/utils"; export const addAnnotations: AbiTransforms = { enter: { - PropertyDefinition: (def: PropertyDefinition): PropertyDefinition => { - if (!def.map) return def; + PropertyDefinition: (def) => { + const typeInProperty = def.type; + if (typeInProperty.kind !== "Map") return def; return { ...def, toGraphQLType: (): string => - `Map${def.required ? "!" : ""} @annotate(type: "${toGraphQL(def)}")`, - } as PropertyDefinition; + `Map${def.required ? "!" : ""} @annotate(type: "${toMapString(typeInProperty)}")`, + } as PropertyDef; }, }, }; diff --git a/packages/schema/parse/src/transform/addFirstLast.ts b/packages/schema/parse/src/transform/addFirstLast.ts index c8ee7a1581..b678ea8717 100644 --- a/packages/schema/parse/src/transform/addFirstLast.ts +++ b/packages/schema/parse/src/transform/addFirstLast.ts @@ -1,10 +1,8 @@ import { AbiTransforms } from "."; -import { Abi, GenericDefinition } from "@polywrap/wrap-manifest-types-js"; - export const addFirstLast: AbiTransforms = { enter: { - GenericDefinition: (def: GenericDefinition): GenericDefinition => { + DefinitionOrType: (def) => { const arrays: Record = {}; for (const key of Object.keys(def)) { @@ -20,12 +18,13 @@ export const addFirstLast: AbiTransforms = { ...arrays, }; }, - Abi: (abi: Abi): Abi => ({ + Abi: (abi) => ({ ...abi, - objectTypes: setFirstLast(abi.objectTypes), - importedObjectTypes: setFirstLast(abi.importedObjectTypes), - importedModuleTypes: setFirstLast(abi.importedModuleTypes), - importedEnvTypes: setFirstLast(abi.importedEnvTypes), + objectTypes: setFirstLast(abi.objects), + // TODO: why only local objects? + // importedObjectTypes: setFirstLast(abi.importedObjectTypes), + // importedModuleTypes: setFirstLast(abi.importedModuleTypes), + // importedEnvTypes: setFirstLast(abi.importedEnvTypes), }), }, }; diff --git a/packages/schema/parse/src/transform/extendType.ts b/packages/schema/parse/src/transform/extendType.ts index 092578a42a..ccdcf2b098 100644 --- a/packages/schema/parse/src/transform/extendType.ts +++ b/packages/schema/parse/src/transform/extendType.ts @@ -1,17 +1,17 @@ import { AbiTransforms } from "."; -import { GenericDefinition, WrapAbi } from "@polywrap/wrap-manifest-types-js"; +// TODO: where is this one used? // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types export function extendType(extension: any): AbiTransforms { return { enter: { - Abi: (abi: WrapAbi) => ({ + Abi: (abi) => ({ ...abi, extension, }), // eslint-disable-next-line @typescript-eslint/naming-convention - GenericDefinition: (def: GenericDefinition) => ({ + DefinitionOrType: (def) => ({ ...def, ...extension, }), diff --git a/packages/schema/parse/src/transform/hasImports.ts b/packages/schema/parse/src/transform/hasImports.ts index d2350d1e05..dc8ae537cc 100644 --- a/packages/schema/parse/src/transform/hasImports.ts +++ b/packages/schema/parse/src/transform/hasImports.ts @@ -1,19 +1,10 @@ import { AbiTransforms } from "."; -import { WrapAbi } from "@polywrap/wrap-manifest-types-js"; - export const hasImports: AbiTransforms = { enter: { - Abi: (abi: WrapAbi) => ({ + Abi: (abi) => ({ ...abi, - hasImports: () => { - return ( - (abi.importedEnumTypes && abi.importedEnumTypes.length) || - (abi.importedObjectTypes && abi.importedObjectTypes.length) || - (abi.importedModuleTypes && abi.importedModuleTypes.length) || - (abi.importedEnvTypes && abi.importedEnvTypes.length) - ); - }, + hasImports: () => abi.imports && abi.imports.length, }), }, }; diff --git a/packages/schema/parse/src/transform/index.ts b/packages/schema/parse/src/transform/index.ts index 3c432364c4..e956651a67 100644 --- a/packages/schema/parse/src/transform/index.ts +++ b/packages/schema/parse/src/transform/index.ts @@ -1,4 +1,4 @@ -import { Abi, AnyType, ArgumentDef, ArrayType, EnumDef, EnvDef, FunctionDef, MapKeyTypeName, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType } from "../definitions"; +import { Abi, AnyType, ArgumentDef, ArrayType, Def, EnumDef, EnvDef, FunctionDef, MapKeyTypeName, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType, Type } from "../definitions"; export interface AbiTransforms { enter?: AbiTransformer; @@ -33,6 +33,7 @@ export interface AbiTransformer { MapType?: (type: MapType) => MapType RefType?: (type: RefType) => RefType + DefinitionOrType?: (def: T) => T Abi?: (abi: Abi) => Abi } @@ -73,29 +74,54 @@ const transformType = (type: T, transform?: AbiTransf ArrayType, MapType, RefType, + DefinitionOrType } = transform; + if (DefinitionOrType) { + result = Object.assign(result, DefinitionOrType(result)) + } + if (FunctionDefinition && result.kind === "Function") { result = Object.assign(result, FunctionDefinition(result)) - } else if (ObjectDefinition && result.kind === "Object") { + } + + if (ObjectDefinition && result.kind === "Object") { result = Object.assign(result, ObjectDefinition(result)) - } else if (EnumDefinition && result.kind === "Enum") { + } + + if (EnumDefinition && result.kind === "Enum") { result = Object.assign(result, EnumDefinition(result)) - } else if (EnvDefinition && result.kind === "Env") { + } + + if (EnvDefinition && result.kind === "Env") { result = Object.assign(result, EnvDefinition(result)) - } else if (ArgumentDefinition && result.kind === "Argument") { + } + + if (ArgumentDefinition && result.kind === "Argument") { result = Object.assign(result, ArgumentDefinition(result)) - } else if (ResultDefinition && result.kind === "Result") { + } + + if (ResultDefinition && result.kind === "Result") { result = Object.assign(result, ResultDefinition(result)) - } else if (PropertyDefinition && result.kind === "Property") { + } + + if (PropertyDefinition && result.kind === "Property") { result = Object.assign(result, PropertyDefinition(result)) - } else if (ScalarType && result.kind === "Scalar") { + } + + if (ScalarType && result.kind === "Scalar") { result = Object.assign(result, ScalarType(result)) - } else if (ArrayType && result.kind === "Array") { + } + + if (ArrayType && result.kind === "Array") { result = Object.assign(result, ArrayType(result)) - } else if (MapType && result.kind === "Map") { + } + + if (MapType && result.kind === "Map") { result = Object.assign(result, MapType(result)) - } else if (RefType && result.kind === "Ref") { + } + + if (RefType && result.kind === "Ref") { result = Object.assign(result, RefType(result)) } From 8bf932055d95212cb1e8618291c2b50e2ca92ca2 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 9 Jan 2023 16:26:27 +0100 Subject: [PATCH 13/90] (chore): toGraphQLType function updated --- packages/schema/parse/src/extract/utils.ts | 19 --- .../parse/src/transform/addAnnotations.ts | 4 +- .../parse/src/transform/toGraphQLType.ts | 154 +++--------------- 3 files changed, 25 insertions(+), 152 deletions(-) diff --git a/packages/schema/parse/src/extract/utils.ts b/packages/schema/parse/src/extract/utils.ts index ed144d3316..fad8c9eb46 100644 --- a/packages/schema/parse/src/extract/utils.ts +++ b/packages/schema/parse/src/extract/utils.ts @@ -121,22 +121,3 @@ const isMapKey = (typeName: string): boolean => { const isArray = (typeName: string): boolean => { return typeName.startsWith("[") } - -export const toMapString = (map: MapType): string => { - const stringifyMapValueType = (anyType: AnyType, required: boolean): string => { - let result = ""; - switch (anyType.kind) { - case "Array": result = `[${stringifyMapValueType(anyType.item, anyType.required)}]` - break; - case "Ref": result = anyType.ref_name - break; - case "Scalar": result = anyType.scalar - break; - case "Map": result = toMapString(anyType) - break; - } - return required ? `${result}!` : result; - } - - return `Map<${map.key.scalar}!, ${stringifyMapValueType(map.value, map.required)}>` -} diff --git a/packages/schema/parse/src/transform/addAnnotations.ts b/packages/schema/parse/src/transform/addAnnotations.ts index 8640b1e5a8..242ecfb09e 100644 --- a/packages/schema/parse/src/transform/addAnnotations.ts +++ b/packages/schema/parse/src/transform/addAnnotations.ts @@ -1,6 +1,6 @@ import { AbiTransforms } from ".."; import { PropertyDef } from "../definitions"; -import { toMapString } from "../extract/utils"; +import { toGraphQL } from "./toGraphQLType"; export const addAnnotations: AbiTransforms = { enter: { @@ -11,7 +11,7 @@ export const addAnnotations: AbiTransforms = { return { ...def, toGraphQLType: (): string => - `Map${def.required ? "!" : ""} @annotate(type: "${toMapString(typeInProperty)}")`, + `Map${def.required ? "!" : ""} @annotate(type: "${toGraphQL(typeInProperty, def.required)}")`, } as PropertyDef; }, }, diff --git a/packages/schema/parse/src/transform/toGraphQLType.ts b/packages/schema/parse/src/transform/toGraphQLType.ts index 300b2417d3..9dc2d67020 100644 --- a/packages/schema/parse/src/transform/toGraphQLType.ts +++ b/packages/schema/parse/src/transform/toGraphQLType.ts @@ -1,127 +1,40 @@ -import { AbiTransforms } from "."; -import { DefinitionKind } from ".."; - -import { - GenericDefinition, - AnyDefinition, - ArrayDefinition, - MethodDefinition, - MapDefinition, -} from "@polywrap/wrap-manifest-types-js"; +import { ArgumentDef, ArrayType, EnumDef, EnvDef, FunctionDef, MapType, ObjectDef, PropertyDef, RefType, ScalarType } from "../definitions"; function applyRequired(type: string, required: boolean | undefined): string { return `${type}${required ? "!" : ""}`; } -function anyToGraphQL(any: AnyDefinition, prefixed: boolean): string { - if (any.object) { - return toGraphQL(any.object, prefixed); - } else if (any.array) { - return toGraphQL(any.array, prefixed); - } else if (any.scalar) { - return toGraphQL(any.scalar, prefixed); - } else if (any.enum) { - return toGraphQL(any.enum, prefixed); - } else if (any.map) { - return toGraphQL(any.map, prefixed); - } else { - throw Error( - `anyToGraphQL: Any type is invalid.\n${JSON.stringify(any, null, 2)}` - ); - } -} - -export function toGraphQL(def: GenericDefinition, prefixed = false): string { +export function toGraphQL(def: ObjectDef | FunctionDef | ArgumentDef | ScalarType | EnvDef | EnumDef | PropertyDef | ArrayType | MapType | RefType, required: boolean): string { switch (def.kind) { - case DefinitionKind.Object: - case DefinitionKind.ObjectRef: - case DefinitionKind.Scalar: - case DefinitionKind.ImportedObject: - return applyRequired(def.type, def.required); - case DefinitionKind.Enum: - case DefinitionKind.EnumRef: - case DefinitionKind.ImportedEnum: - if (prefixed) { - return applyRequired(`Enum_${def.type}`, def.required); - } - - return applyRequired(def.type, def.required); - case DefinitionKind.Any: - case DefinitionKind.Property: - return anyToGraphQL(def as AnyDefinition, prefixed); - case DefinitionKind.Array: { - const array = def as ArrayDefinition; - - if (!array.item) { - throw Error( - `toGraphQL: ArrayDefinition's item type is undefined.\n${JSON.stringify( - array, - null, - 2 - )}` - ); - } - + case "Object": + case "Env": + return applyRequired(def.name, required); + case "Scalar": + return applyRequired(def.scalar, required); + case "Enum": + return applyRequired(def.name, required);; + case "Property": + return toGraphQL(def.type, def.required); + case "Array": return applyRequired( - `[${toGraphQL(array.item, prefixed)}]`, - array.required + `[${toGraphQL(def.item, def.required)}]`, + required ); + case "Map": { + return `Map<${def.key}!, ${toGraphQL(def.value, def.required)}>` } - case DefinitionKind.Map: { - const map = def as MapDefinition; - if (!map.key) { - throw Error( - `toGraphQL: MapDefinition's key type is undefined.\n${JSON.stringify( - map, - null, - 2 - )}` - ); - } - if (!map.value) { - throw Error( - `toGraphQL: MapDefinition's value type is undefined.\n${JSON.stringify( - map, - null, - 2 - )}` - ); - } - return applyRequired( - `Map<${toGraphQL(map.key, prefixed)}, ${toGraphQL( - map.value, - prefixed - )}>`, - map.required - ); - } - case DefinitionKind.Method: { - const method = def as MethodDefinition; - - if (!method.return) { - throw Error( - `toGraphQL: MethodDefinition's return type is undefined.\n${JSON.stringify( - method, - null, - 2 - )}` - ); - } + case "Function": { - const result = `${method.name}( - ${(method.arguments || []) - .map((arg) => `${arg.name}: ${toGraphQL(arg, prefixed)}`) - .join("\n ")} -): ${toGraphQL(method.return, prefixed)}`; + const result = `${def.name}( + ${(def.args || []) + .map((arg) => `${arg.name}: ${toGraphQL(arg.type, arg.required)}`) + .join("\n ")} +): ${toGraphQL(def.result.type, def.result.required)}`; return result; } - case DefinitionKind.Module: - return def.type; - case DefinitionKind.ImportedModule: - return def.type; default: throw Error( - `toGraphQL: Unrecognized DefinitionKind.\n${JSON.stringify( + `toGraphQL: Unrecognized Definition or Type.\n${JSON.stringify( def, null, 2 @@ -130,24 +43,3 @@ export function toGraphQL(def: GenericDefinition, prefixed = false): string { } } -export function toPrefixedGraphQL(def: GenericDefinition): string { - return toGraphQL(def, true); -} - -export const toPrefixedGraphQLType: AbiTransforms = { - enter: { - GenericDefinition: (def: GenericDefinition) => ({ - ...def, - toGraphQLType: () => toPrefixedGraphQL(def), - }), - }, -}; - -export const toGraphQLType: AbiTransforms = { - enter: { - GenericDefinition: (def: GenericDefinition) => ({ - ...def, - toGraphQLType: () => toGraphQL(def), - }), - }, -}; From a29eea00b37656284d60057633323243241fcc71 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 9 Jan 2023 16:40:55 +0100 Subject: [PATCH 14/90] (chore): removed unused code --- packages/schema/parse/src/abi/definitions.ts | 417 ------------------ packages/schema/parse/src/creators.ts | 92 ---- .../src/transform/methodParentPointers.ts | 45 -- 3 files changed, 554 deletions(-) delete mode 100644 packages/schema/parse/src/abi/definitions.ts delete mode 100644 packages/schema/parse/src/creators.ts delete mode 100644 packages/schema/parse/src/transform/methodParentPointers.ts diff --git a/packages/schema/parse/src/abi/definitions.ts b/packages/schema/parse/src/abi/definitions.ts deleted file mode 100644 index 2e5a815b2f..0000000000 --- a/packages/schema/parse/src/abi/definitions.ts +++ /dev/null @@ -1,417 +0,0 @@ -import { isMapKeyType, isModuleType, isScalarType } from "./utils"; - -import { - AnyDefinition, - ArrayDefinition, - CapabilityDefinition, - EnumDefinition, - EnumRef, - EnvDefinition, - GenericDefinition, - ImportedEnumDefinition, - ImportedEnvDefinition, - ImportedModuleDefinition, - ImportedObjectDefinition, - InterfaceDefinition, - InterfaceImplementedDefinition, - MapDefinition, - MapKeyDefinition, - MethodDefinition, - ModuleDefinition, - ObjectDefinition, - ObjectRef, - PropertyDefinition, - ScalarDefinition, - UnresolvedObjectOrEnumRef, - WithKind, - WithComment, -} from "@polywrap/wrap-manifest-types-js"; - -export enum DefinitionKind { - Generic = 0, - Object = 1 << 0, - Any = 1 << 1, - Scalar = 1 << 2, - Enum = 1 << 3, - Array = (1 << 4) | DefinitionKind.Any, - Property = (1 << 5) | DefinitionKind.Any, - Method = 1 << 6, - Module = 1 << 7, - ImportedModule = 1 << 8, - ImportedEnum = (1 << 9) | DefinitionKind.Enum, - ImportedObject = (1 << 10) | DefinitionKind.Object, - InterfaceImplemented = 1 << 11, - UnresolvedObjectOrEnum = 1 << 12, - ObjectRef = 1 << 13, - EnumRef = 1 << 14, - Interface = 1 << 15, - Env = 1 << 16, - MapKey = 1 << 17, - Map = (1 << 18) | DefinitionKind.Any, - ImportedEnv = 1 << 19, -} - -export function isKind(type: WithKind, kind: DefinitionKind): boolean { - return (type.kind & kind) === kind; -} - -export function createGenericDefinition( - args: Omit -): GenericDefinition { - return { - ...args, - kind: DefinitionKind.Generic, - }; -} - -export function createObjectDefinition( - args: Omit -): ObjectDefinition { - return { - ...args, - kind: DefinitionKind.Object, - }; -} - -export function createObjectRef(args: Omit): ObjectRef { - return { - ...args, - kind: DefinitionKind.ObjectRef, - }; -} - -export function createAnyDefinition( - args: Omit -): AnyDefinition { - return { - ...args, - kind: DefinitionKind.Any, - }; -} - -export function createMapKeyDefinition( - args: Omit -): MapKeyDefinition { - if (!isMapKeyType(args.type)) { - throw Error( - `createMapKeyDefinition: Unrecognized Map key type provided "${args.type}"` - ); - } - return { - ...args, - kind: DefinitionKind.Scalar, - }; -} - -export function createScalarDefinition( - args: Omit -): ScalarDefinition { - if (!isScalarType(args.type)) { - throw Error( - `createScalarDefinition: Unrecognized scalar type provided "${args.type}"` - ); - } - return { - ...args, - kind: DefinitionKind.Scalar, - }; -} - -export function createEnumDefinition( - args: Omit -): EnumDefinition { - return { - ...args, - kind: DefinitionKind.Enum, - }; -} - -export function createEnumRef(args: Omit): EnumRef { - return { - ...args, - kind: DefinitionKind.EnumRef, - }; -} - -export function createUnresolvedObjectOrEnumRef( - args: Omit -): UnresolvedObjectOrEnumRef { - return { - ...args, - kind: DefinitionKind.UnresolvedObjectOrEnum, - }; -} - -export function createMapDefinition( - args: Omit -): MapDefinition { - return { - ...args, - ...createAnyDefinition({ - ...args, - array: - args.value && isKind(args.value, DefinitionKind.Array) - ? (args.value as ArrayDefinition) - : undefined, - map: - args.value && isKind(args.value, DefinitionKind.Map) - ? (args.value as MapDefinition) - : undefined, - scalar: - args.value && isKind(args.value, DefinitionKind.Scalar) - ? (args.value as ScalarDefinition) - : undefined, - object: - args.value && isKind(args.value, DefinitionKind.ObjectRef) - ? (args.value as ObjectRef) - : undefined, - enum: - args.value && isKind(args.value, DefinitionKind.EnumRef) - ? (args.value as EnumRef) - : undefined, - unresolvedObjectOrEnum: - args.value && isKind(args.value, DefinitionKind.UnresolvedObjectOrEnum) - ? (args.value as UnresolvedObjectOrEnumRef) - : undefined, - }), - kind: DefinitionKind.Map, - }; -} - -export function createArrayDefinition( - args: Omit -): ArrayDefinition { - return { - ...args, - ...createAnyDefinition({ - ...args, - array: - args.item && isKind(args.item, DefinitionKind.Array) - ? (args.item as ArrayDefinition) - : undefined, - map: - args.item && isKind(args.item, DefinitionKind.Map) - ? (args.item as MapDefinition) - : undefined, - scalar: - args.item && isKind(args.item, DefinitionKind.Scalar) - ? (args.item as ScalarDefinition) - : undefined, - object: - args.item && isKind(args.item, DefinitionKind.ObjectRef) - ? (args.item as ObjectRef) - : undefined, - enum: - args.item && isKind(args.item, DefinitionKind.EnumRef) - ? (args.item as EnumRef) - : undefined, - unresolvedObjectOrEnum: - args.item && isKind(args.item, DefinitionKind.UnresolvedObjectOrEnum) - ? (args.item as UnresolvedObjectOrEnumRef) - : undefined, - }), - kind: DefinitionKind.Array, - }; -} - -export function createPropertyDefinition( - args: Omit -): PropertyDefinition { - return { - ...args, - kind: DefinitionKind.Property, - }; -} - -export function createInterfaceImplementedDefinition( - args: Omit -): InterfaceImplementedDefinition { - return { - ...args, - kind: DefinitionKind.InterfaceImplemented, - }; -} - -export function createArrayPropertyDefinition( - args: Omit & WithComment -): PropertyDefinition { - const comment = args.comment; - delete args.comment; - const result = createPropertyDefinition({ - name: args.name, - type: args.type, - required: args.required, - array: createArrayDefinition(args), - }); - return comment ? { ...result, comment } : result; -} - -export function createMapPropertyDefinition( - args: Omit & WithComment -): PropertyDefinition { - const comment = args.comment; - delete args.comment; - const result = createPropertyDefinition({ - name: args.name, - type: args.type, - required: args.required, - map: createMapDefinition(args), - }); - return comment ? { ...result, comment } : result; -} - -export function createScalarPropertyDefinition( - args: Omit & WithComment -): PropertyDefinition { - const comment = args.comment; - delete args.comment; - const result = createPropertyDefinition({ - name: args.name, - type: args.type, - required: args.required, - scalar: createScalarDefinition(args), - }); - return comment ? { ...result, comment } : result; -} - -export function createEnumPropertyDefinition( - args: Omit & WithComment -): PropertyDefinition { - const comment = args.comment; - delete args.comment; - const result = createPropertyDefinition({ - name: args.name, - type: args.type, - required: args.required, - enum: createEnumRef(args), - }); - return comment ? { ...result, comment } : result; -} - -export function createObjectPropertyDefinition( - args: Omit & WithComment -): PropertyDefinition { - const comment = args.comment; - delete args.comment; - const result = createPropertyDefinition({ - name: args.name, - type: args.type, - required: args.required, - object: createObjectRef(args), - }); - return comment ? { ...result, comment } : result; -} - -export function createMethodDefinition( - args: Omit, "type"> -): MethodDefinition { - return { - ...args, - ...createGenericDefinition({ - ...args, - type: "Method", - }), - required: true, - kind: DefinitionKind.Method, - }; -} - -export function createModuleDefinition( - args: Omit, "type"> -): ModuleDefinition { - return { - ...args, - ...createGenericDefinition({ - ...args, - type: "Module", - }), - kind: DefinitionKind.Module, - }; -} - -export function createImportedEnumDefinition( - args: Omit -): ImportedEnumDefinition { - return { - ...args, - ...createEnumDefinition(args), - kind: DefinitionKind.ImportedEnum, - }; -} - -// TODO: We don't want this hard coded -export const capabilityTypes = ["getImplementations"] as const; -export type CapabilityType = typeof capabilityTypes[number]; -export function createCapability(args: { - type: CapabilityType; - enabled: boolean; -}): CapabilityDefinition { - return { - [args.type]: { - enabled: args.enabled, - }, - }; -} - -export function createInterfaceDefinition( - args: Omit, "nativeType"> -): InterfaceDefinition { - return { - ...args, - ...createGenericDefinition(args), - nativeType: "Interface", - kind: DefinitionKind.Interface, - }; -} - -export function createImportedModuleDefinition( - args: Omit, "type"> -): ImportedModuleDefinition { - if (!isModuleType(args.nativeType)) { - throw Error( - `createImportedModuleDefinition: Unrecognized module type provided "${args.nativeType}"` - ); - } - - return { - ...args, - ...createGenericDefinition({ - ...args, - type: `${args.namespace}_${args.nativeType}`, - }), - kind: DefinitionKind.ImportedModule, - }; -} - -export function createImportedObjectDefinition( - args: Omit -): ImportedObjectDefinition { - return { - ...args, - ...createObjectDefinition(args), - kind: DefinitionKind.ImportedObject, - }; -} - -export function createEnvDefinition( - args: Omit, "type"> -): EnvDefinition { - return { - ...args, - ...createObjectDefinition({ ...args, type: "Env" }), - kind: DefinitionKind.Env, - }; -} - -export function createImportedEnvDefinition( - args: Omit, "type"> -): ImportedEnvDefinition { - return { - ...args, - ...createObjectDefinition({ - ...args, - type: `${args.namespace}_Env`, - }), - kind: DefinitionKind.ImportedEnv, - }; -} diff --git a/packages/schema/parse/src/creators.ts b/packages/schema/parse/src/creators.ts deleted file mode 100644 index 45a6c16f82..0000000000 --- a/packages/schema/parse/src/creators.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { ArrayReference, EnumDefinition, EnvDefinition, Imported, MapReference, ModuleDefinition, ObjectDefinition, Scalar, ScalarDefinition } from "./definitions"; - -export const createScalarDefinition = (scalar: Scalar): ScalarDefinition => { - return { - kind: "Scalar", - name: scalar, - } -} - -export const createObjectDefinition = (args: Omit): ObjectDefinition => { - return { - kind: "Object", - ...args - } -} - -export const createImportedObjectDefinition = (args: Omit): ObjectDefinition & Imported => { - return { - kind: "Object", - ...args - } -} - -export const createEnvDefinition = (args: Omit): EnvDefinition => { - return { - kind: "Env", - ...args - } -} - -export const createImportedEnvDefinition = (args: Omit): EnvDefinition & Imported => { - return { - kind: "Env", - ...args - } -} - -export const createEnumDefinition = (args: Omit): EnumDefinition => { - return { - kind: "Enum", - ...args - } -} - -export const createImportedEnumDefinition = (args: Omit): EnumDefinition & Imported => { - return { - kind: "Enum", - ...args - } -} - -export const createModuleDefinition = (args: Omit): ModuleDefinition => { - return { - kind: "Module", - ...args - } -} - -export const createImportedModuleDefinition = (args: Omit): ModuleDefinition & Imported => { - return { - kind: "Module", - ...args - } -} - -export const createMapReference = (args: Omit): MapReference => { - return { - kind: "Map", - ...args - } -} - -export const createImportedMapReference = (args: Omit): MapReference & Imported => { - return { - kind: "Map", - ...args - } -} - -export const createArrayReference = (args: Omit): ArrayReference => { - return { - kind: "Array", - ...args - } -} - -export const createImportedArrayReference = (args: Omit): ArrayReference & Imported => { - return { - kind: "Array", - ...args - } -} \ No newline at end of file diff --git a/packages/schema/parse/src/transform/methodParentPointers.ts b/packages/schema/parse/src/transform/methodParentPointers.ts deleted file mode 100644 index ba250812f8..0000000000 --- a/packages/schema/parse/src/transform/methodParentPointers.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { AbiTransforms } from "."; - -import { - ImportedModuleDefinition, - MethodDefinition, - ModuleDefinition, -} from "@polywrap/wrap-manifest-types-js"; - -export function methodParentPointers(): AbiTransforms { - const visitorStack: (ModuleDefinition | ImportedModuleDefinition)[] = []; - - return { - enter: { - ModuleDefinition: (def: ModuleDefinition) => { - visitorStack.push(def); - return def; - }, - ImportedModuleDefinition: (def: ImportedModuleDefinition) => { - visitorStack.push(def); - return def; - }, - MethodDefinition: (def: MethodDefinition) => { - const parent = - visitorStack.length > 0 - ? visitorStack[visitorStack.length - 1] - : undefined; - - return { - ...def, - parent, - }; - }, - }, - leave: { - ModuleDefinition: (def: ModuleDefinition) => { - visitorStack.pop(); - return def; - }, - ImportedModuleDefinition: (def: ImportedModuleDefinition) => { - visitorStack.pop(); - return def; - }, - }, - }; -} From dfb642ae8e383446c391af32c6dcdb68b0a11b7b Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Wed, 8 Feb 2023 11:56:26 +0100 Subject: [PATCH 15/90] (feat): updated ABI definitions. Removed Env from parse --- packages/schema/parse/src/abi/env.ts | 16 ----- packages/schema/parse/src/definitions.ts | 63 ++++++++++--------- .../schema/parse/src/extract/directives.ts | 29 --------- packages/schema/parse/src/extract/env.ts | 29 --------- packages/schema/parse/src/extract/index.ts | 1 - packages/schema/parse/src/extract/module.ts | 15 +---- packages/schema/parse/src/extract/object.ts | 10 +-- packages/schema/parse/src/index.ts | 33 +++------- 8 files changed, 46 insertions(+), 150 deletions(-) delete mode 100644 packages/schema/parse/src/abi/env.ts delete mode 100644 packages/schema/parse/src/extract/env.ts diff --git a/packages/schema/parse/src/abi/env.ts b/packages/schema/parse/src/abi/env.ts deleted file mode 100644 index 9bc00583ba..0000000000 --- a/packages/schema/parse/src/abi/env.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const envTypeNames = { - objectType: "Env", - argName: "env", -}; - -export function isEnvType(type: string): boolean { - return type === envTypeNames.objectType; -} - -export function isEnvArgName(name: string): boolean { - return name === envTypeNames.argName; -} - -export function isImportedEnvType(type: string): boolean { - return type.endsWith(`_${envTypeNames.objectType}`); -} diff --git a/packages/schema/parse/src/definitions.ts b/packages/schema/parse/src/definitions.ts index dd40d3c6c8..a8570cd73a 100644 --- a/packages/schema/parse/src/definitions.ts +++ b/packages/schema/parse/src/definitions.ts @@ -1,20 +1,26 @@ /// ABIs +export interface AbiDefs { + functions?: FunctionDef[]; + objects?: ObjectDef[]; + enums?: EnumDef[]; +} + export interface Abi extends AbiDefs { version: "0.2"; imports?: ImportedAbi[]; } +export type ImportAbiType = + | "wasm" + | "interface"; + export interface ImportedAbi extends AbiDefs { - namespace: string; + id: string; uri: string; -} - -export interface AbiDefs { - functions?: FunctionDef[]; - objects?: ObjectDef[]; - enums?: EnumDef[]; - env?: EnvDef; + type: ImportAbiType; + namespace: string; + imports?: ImportedAbi[]; } /// Definitions (user-defined) @@ -23,7 +29,6 @@ export type UniqueDefKind = | "Function" | "Object" | "Enum" - | "Env"; export type DefKind = | UniqueDefKind @@ -37,15 +42,11 @@ export interface Def { export interface NamedDef extends Def { name: string; - comment?: string; } -export interface TypeDef extends Def { - required: boolean; - type: AnyType; -} +export interface InlinedTypeDef extends Def, OptionalType { } -export interface NamedTypeDef extends NamedDef, TypeDef { } +export interface NamedTypeDef extends NamedDef, InlinedTypeDef { } export interface FunctionDef extends NamedDef { kind: "Function"; @@ -57,7 +58,7 @@ export interface ArgumentDef extends NamedTypeDef { kind: "Argument"; } -export interface ResultDef extends TypeDef { +export interface ResultDef extends InlinedTypeDef { kind: "Result"; } @@ -75,25 +76,21 @@ export interface EnumDef extends NamedDef { constants: string[]; } -export interface EnvDef extends NamedDef { - kind: "Env"; - name: "Env"; - props: PropertyDef[]; -} - /// Types (built-ins) export type AnyType = | ScalarType | ArrayType | MapType - | RefType; + | RefType + | ImportRefType; export type TypeKind = | "Scalar" | "Array" | "Map" - | "Ref"; + | "Ref" + | "ImportRef"; export interface Type { kind: TypeKind; @@ -108,15 +105,13 @@ export interface ScalarType< export interface ArrayType extends Type { kind: "Array"; - required: boolean; - item: AnyType; + item: OptionalType; } export interface MapType extends Type { kind: "Map"; key: ScalarType; - required: boolean; - value: AnyType; + value: OptionalType; } export interface RefType extends Type { @@ -125,6 +120,18 @@ export interface RefType extends Type { ref_name: string; } +export interface ImportRefType extends Type { + kind: "ImportRef"; + import_id: string; + ref_kind: UniqueDefKind; + ref_name: string; +} + +export interface OptionalType { + required: boolean; + type: AnyType; +} + /// Constants export const scalarTypeSet = { diff --git a/packages/schema/parse/src/extract/directives.ts b/packages/schema/parse/src/extract/directives.ts index 31df3445e9..1bc68715c4 100644 --- a/packages/schema/parse/src/extract/directives.ts +++ b/packages/schema/parse/src/extract/directives.ts @@ -2,13 +2,8 @@ import { DirectiveNode, FieldDefinitionNode } from "graphql"; import { MapType, UniqueDefKind } from "../definitions"; import { parseMapString } from "./utils"; -interface EnvDirDefinition { - required: boolean; -} - export const parseDirectivesInField = (node: FieldDefinitionNode, uniqueDefs: Map) => { let map: MapType | undefined; - let env: EnvDirDefinition | undefined; if (node.directives) { for (const dir of node.directives) { @@ -16,16 +11,12 @@ export const parseDirectivesInField = (node: FieldDefinitionNode, uniqueDefs: Ma case "annotate": map = parseAnnotateDirective(dir, uniqueDefs); break; - - case "env": - env = parseEnvDirective(dir) } } } return { map, - env } } @@ -42,23 +33,3 @@ export const parseAnnotateDirective = (node: DirectiveNode, uniqueDefs: Map arg.name.value === "required" - )?.value; - - if (!requiredValue || requiredValue.kind !== "BooleanValue") { - throw new Error( - `Env directive: ${node.name.value} has invalid arguments` - ); - } - - const required = requiredValue.value; - - return { - required, - }; -} \ No newline at end of file diff --git a/packages/schema/parse/src/extract/env.ts b/packages/schema/parse/src/extract/env.ts deleted file mode 100644 index 5aadf0c124..0000000000 --- a/packages/schema/parse/src/extract/env.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { ASTVisitor, ObjectTypeDefinitionNode } from "graphql"; -import { isEnvType } from "../abi"; -import { Abi, EnvDef } from "../definitions"; -import { ObjectVisitorBuilder } from "./object"; - -export class EnvVisitorBuilder extends ObjectVisitorBuilder { - build(abi: Abi): ASTVisitor { - return { - enter: { - ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { - const typeName = node.name.value; - - if (!isEnvType(typeName)) { - return; - } - - const def: EnvDef = { - kind: "Env", - name: "Env", - comment: node.description?.value, - props: node.fields?.map(fieldNode => this.extractPropertyDef(fieldNode, this.uniqueDefs)) ?? [] - }; - - abi.env = def; - }, - }, - }; - } -} \ No newline at end of file diff --git a/packages/schema/parse/src/extract/index.ts b/packages/schema/parse/src/extract/index.ts index 559f5d326c..15bf3a2039 100644 --- a/packages/schema/parse/src/extract/index.ts +++ b/packages/schema/parse/src/extract/index.ts @@ -1,4 +1,3 @@ export * from "./object"; -export * from "./env"; export * from "./enum"; export * from "./module"; \ No newline at end of file diff --git a/packages/schema/parse/src/extract/module.ts b/packages/schema/parse/src/extract/module.ts index 99a5ba063e..d7e45eec08 100644 --- a/packages/schema/parse/src/extract/module.ts +++ b/packages/schema/parse/src/extract/module.ts @@ -9,7 +9,7 @@ export class ModuleVisitorBuilder implements VisitorBuilder { constructor(protected readonly uniqueDefs: Map) { } private extractMethodFromFieldDefNode(node: FieldDefinitionNode): FunctionDef { - const { map, env } = parseDirectivesInField(node, this.uniqueDefs); + const { map } = parseDirectivesInField(node, this.uniqueDefs); const resultDef: ResultDef = { kind: "Result", @@ -41,19 +41,6 @@ export class ModuleVisitorBuilder implements VisitorBuilder { } }) ?? []; - if (env) { - args.push({ - kind: "Argument", - name: "env", - required: env.required, - type: { - kind: "Ref", - ref_kind: "Env", - ref_name: "Env", - } - }) - } - const method: FunctionDef = { kind: "Function", result: resultDef, diff --git a/packages/schema/parse/src/extract/object.ts b/packages/schema/parse/src/extract/object.ts index 318c04d745..46a15f77eb 100644 --- a/packages/schema/parse/src/extract/object.ts +++ b/packages/schema/parse/src/extract/object.ts @@ -1,5 +1,5 @@ import { ASTVisitor, FieldDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; -import { isModuleType, isEnvType } from "../abi"; +import { isModuleType } from "../abi"; import { Abi, ObjectDef, PropertyDef, UniqueDefKind } from "../definitions"; import { parseDirectivesInField } from "./directives"; import { VisitorBuilder } from "./types"; @@ -26,19 +26,13 @@ export class ObjectVisitorBuilder implements VisitorBuilder { const typeName = node.name.value; // Skip non-custom types - if (isModuleType(typeName) || isEnvType(typeName)) { + if (isModuleType(typeName)) { return; } - - // TODO: restore interfaces support - // const interfaces = node.interfaces?.map((x) => - // createInterfaceImplementedDefinition({ type: x.name.value }) - // ); // Create a new TypeDefinition const def: ObjectDef = { kind: "Object", - comment: node.description?.value, name: typeName, props: node.fields?.map(fieldNode => this.extractPropertyDef(fieldNode, this.uniqueDefs)) ?? [] }; diff --git a/packages/schema/parse/src/index.ts b/packages/schema/parse/src/index.ts index 4cf3085431..82b94f63d2 100644 --- a/packages/schema/parse/src/index.ts +++ b/packages/schema/parse/src/index.ts @@ -1,4 +1,4 @@ -import { createAbi, isEnvType, isModuleType } from "./abi"; +import { createAbi, isModuleType } from "./abi"; import { AbiTransforms, transformAbi } from "./transform"; import { validators, SchemaValidatorBuilder } from "./validate"; @@ -6,7 +6,6 @@ import { DocumentNode, parse, visit, visitInParallel } from "graphql"; import { Abi, UniqueDefKind } from "./definitions"; import { ExternalVisitorBuilder, VisitorBuilder } from "./extract/types"; import { ObjectVisitorBuilder } from "./extract"; -import { EnvVisitorBuilder } from "./extract"; import { EnumVisitorBuilder } from "./extract"; import { ModuleVisitorBuilder } from "./extract"; @@ -33,7 +32,7 @@ const extractUniqueDefinitionNames = (document: DocumentNode): Map { const name = node.name.value; - if (!isModuleType(name) && !isEnvType(name)) { + if (!isModuleType(name)) { uniqueDefs.set(name, "Object") } }, @@ -53,7 +52,6 @@ export function parseSchema( const uniqueDefs = extractUniqueDefinitionNames(astNode); const defaultExtractors: VisitorBuilder[] = [ new ObjectVisitorBuilder(uniqueDefs), - new EnvVisitorBuilder(uniqueDefs), new EnumVisitorBuilder(), new ModuleVisitorBuilder(uniqueDefs) ] @@ -77,26 +75,11 @@ export function parseSchema( } return { - version: "0.1", - objectTypes: info.objectTypes?.length ? info.objectTypes : undefined, - moduleType: info.moduleType ? info.moduleType : undefined, - enumTypes: info.enumTypes?.length ? info.enumTypes : undefined, - interfaceTypes: info.interfaceTypes?.length - ? info.interfaceTypes - : undefined, - importedObjectTypes: info.importedObjectTypes?.length - ? info.importedObjectTypes - : undefined, - importedModuleTypes: info.importedModuleTypes?.length - ? info.importedModuleTypes - : undefined, - importedEnumTypes: info.importedEnumTypes?.length - ? info.importedEnumTypes - : undefined, - importedEnvTypes: info.importedEnvTypes?.length - ? info.importedEnvTypes - : undefined, - envType: info.envType ? info.envType : undefined, + version: "0.2", + objects: info.objects?.length ? info.objects : undefined, + functions: info.functions?.length ? info.functions : undefined, + enums: info.enums?.length ? info.enums : undefined, + imports: info.imports?.length ? info.imports : undefined, }; } @@ -119,7 +102,7 @@ const validate = ( const extract = ( astNode: DocumentNode, - abi: WrapAbi, + abi: Abi, extractors: SchemaExtractorBuilder[] ) => { const allVisitors = extractors.map((getVisitor) => getVisitor(abi)); From d085bfadbfb14fa74afb5d40dcfde9cc4c4d6b5f Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Wed, 8 Feb 2023 12:08:45 +0100 Subject: [PATCH 16/90] (feat): removed env from compose --- .../schema/compose/src/__tests__/env.spec.ts | 52 ---- packages/schema/compose/src/env.ts | 22 -- packages/schema/compose/src/resolve.ts | 270 +++++------------- packages/schema/compose/src/resolver.ts | 38 +-- packages/schema/compose/src/tempabi.ts | 143 ---------- .../compose/src/templates/schema.mustache.ts | 28 +- packages/schema/compose/src/todo.md | 1 - 7 files changed, 81 insertions(+), 473 deletions(-) delete mode 100644 packages/schema/compose/src/__tests__/env.spec.ts delete mode 100644 packages/schema/compose/src/env.ts delete mode 100644 packages/schema/compose/src/tempabi.ts delete mode 100644 packages/schema/compose/src/todo.md diff --git a/packages/schema/compose/src/__tests__/env.spec.ts b/packages/schema/compose/src/__tests__/env.spec.ts deleted file mode 100644 index 8ecc904a83..0000000000 --- a/packages/schema/compose/src/__tests__/env.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { createObjectDefinition, createPropertyDefinition } from "@polywrap/schema-parse"; -import { checkDuplicateEnvProperties } from "../env"; - -describe("Check duplicate environment properties", () => { - it("should throw error if duplicate property found", () => { - try { - checkDuplicateEnvProperties( - createObjectDefinition({ - type: "ModuleEnv", - properties: [ - createPropertyDefinition({ - type: "String", - name: "prop" - }) - ] - }), - [ - createPropertyDefinition({ - type: "Int", - name: "prop" - }) - ] - ); - - fail("Error not thrown"); - } catch (error) { - expect(error.message).toEqual( - "Type 'ModuleEnv' contains duplicate property 'prop' of type 'Env'" - ) - } - }); - - it("should do nothing if no duplicate properties found", () => { - checkDuplicateEnvProperties( - createObjectDefinition({ - type: "ModuleEnv", - properties: [ - createPropertyDefinition({ - type: "String", - name: "prop" - }) - ] - }), - [ - createPropertyDefinition({ - type: "Int", - name: "prop2" - }) - ] - ); - }); -}); diff --git a/packages/schema/compose/src/env.ts b/packages/schema/compose/src/env.ts deleted file mode 100644 index f6d914585f..0000000000 --- a/packages/schema/compose/src/env.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { - ObjectDefinition, - AnyDefinition, -} from "@polywrap/wrap-manifest-types-js"; - -export function checkDuplicateEnvProperties( - envType: ObjectDefinition, - envProperties: AnyDefinition[] -): void { - const envPropertiesSet = new Set( - envProperties.map((envProperty) => envProperty.name) - ); - if (envType.properties) { - for (const specificProperty of envType.properties) { - if (envPropertiesSet.has(specificProperty.name)) { - throw new Error( - `Type '${envType.type}' contains duplicate property '${specificProperty.name}' of type 'Env'` - ); - } - } - } -} diff --git a/packages/schema/compose/src/resolve.ts b/packages/schema/compose/src/resolve.ts index 8c082372aa..591b4ff3a7 100644 --- a/packages/schema/compose/src/resolve.ts +++ b/packages/schema/compose/src/resolve.ts @@ -9,7 +9,6 @@ import { } from "./types"; import { parseExternalImports, parseLocalImports, parseUse } from "./parse"; import { renderSchema } from "./render"; -import { checkDuplicateEnvProperties } from "./env"; import { addHeader } from "./templates/header.mustache"; import { @@ -24,15 +23,12 @@ import { InterfaceImplementedDefinition, ObjectRef, EnumRef, - EnvDefinition, - ImportedEnvDefinition, } from "@polywrap/wrap-manifest-types-js"; import { parseSchema, AbiTransforms, visitObjectDefinition, visitModuleDefinition, - visitEnvDefinition, DefinitionKind, visitImportedModuleDefinition, visitImportedObjectDefinition, @@ -41,18 +37,13 @@ import { isKind, isImportedModuleType, header, - isEnvType, createImportedObjectDefinition, createImportedEnumDefinition, createImportedModuleDefinition, createInterfaceDefinition, createCapability, ModuleCapability, - createEnvDefinition, createModuleDefinition, - createImportedEnvDefinition, - visitImportedEnvDefinition, - isImportedEnvType, } from "@polywrap/schema-parse"; type ImplementationWithInterfaces = { @@ -177,7 +168,6 @@ export async function resolveImportsAndParseSchemas( importedEnumTypes: [], importedObjectTypes: [], importedModuleTypes: [], - importedEnvTypes: [], }; const externalImports = await resolveExternalImports( @@ -239,14 +229,13 @@ type ImportMap = Record< | ImportedModuleDefinition | ImportedEnumDefinition ) & - Namespaced + Namespaced >; -type EnumOrObjectOrEnv = ObjectDefinition | EnumDefinition | EnvDefinition; -type ImportedEnumOrObjectOrEnv = +type EnumOrObject = ObjectDefinition | EnumDefinition; +type ImportedEnumOrObject = | ImportedObjectDefinition | ImportedEnumDefinition - | ImportedEnvDefinition; // A transformation that converts all object definitions into // imported object definitions @@ -259,10 +248,10 @@ const extractObjectImportDependencies = ( const findImport = ( type: string, namespaceType: string, - rootTypes: EnumOrObjectOrEnv[], - importedTypes: ImportedEnumOrObjectOrEnv[], + rootTypes: EnumOrObject[], + importedTypes: ImportedEnumOrObject[], kind: DefinitionKind - ): ImportedEnumOrObjectOrEnv & Namespaced => { + ): ImportedEnumOrObject & Namespaced => { // Find this type's ObjectDefinition in the root type info let idx = rootTypes.findIndex((obj) => obj.type === type); let obj = undefined; @@ -276,9 +265,9 @@ const extractObjectImportDependencies = ( if (idx === -1) { throw Error( `extractObjectImportDependencies: Cannot find the dependent type within the root type info.\n` + - `Type: ${type}\nAbi: ${JSON.stringify( - rootAbi - )}\n${namespace}\n${JSON.stringify(Object.keys(importsFound))}` + `Type: ${type}\nAbi: ${JSON.stringify( + rootAbi + )}\n${namespace}\n${JSON.stringify(Object.keys(importsFound))}` ); } else if (obj === undefined) { obj = importedTypes[idx]; @@ -308,49 +297,26 @@ const extractObjectImportDependencies = ( const namespaceType = appendNamespace(namespace, type); if (!importsFound[namespaceType]) { - if (isEnvType(type)) { - const importFound = findImport( - type, - namespaceType, - rootAbi.envType ? [rootAbi.envType] : [], - rootAbi.importedEnvTypes || [], - DefinitionKind.ImportedObject - ) as ImportedEnvDefinition; - - // Keep track of it - importsFound[importFound.type] = importFound; - - // Traverse this newly added object - visitImportedEnvDefinition(importFound, { - ...extractObjectImportDependencies( - importsFound, - rootAbi, - namespace, - uri - ), - }); - } else { - const importFound = findImport( - type, - namespaceType, - rootAbi.objectTypes || [], - rootAbi.importedObjectTypes || [], - DefinitionKind.ImportedObject - ) as ImportedObjectDefinition; - - // Keep track of it - importsFound[importFound.type] = importFound; - - // Traverse this newly added object - visitObjectDefinition(importFound, { - ...extractObjectImportDependencies( - importsFound, - rootAbi, - namespace, - uri - ), - }); - } + const importFound = findImport( + type, + namespaceType, + rootAbi.objectTypes || [], + rootAbi.importedObjectTypes || [], + DefinitionKind.ImportedObject + ) as ImportedObjectDefinition; + + // Keep track of it + importsFound[importFound.type] = importFound; + + // Traverse this newly added object + visitObjectDefinition(importFound, { + ...extractObjectImportDependencies( + importsFound, + rootAbi, + namespace, + uri + ), + }); } return def; @@ -367,49 +333,26 @@ const extractObjectImportDependencies = ( const namespaceType = appendNamespace(namespace, type); if (!importsFound[namespaceType]) { - if (isEnvType(type)) { - const importFound = findImport( - type, - namespaceType, - rootAbi.envType ? [rootAbi.envType] : [], - rootAbi.importedEnvTypes || [], - DefinitionKind.ImportedObject - ) as ImportedEnvDefinition; - - // Keep track of it - importsFound[importFound.type] = importFound; - - // Traverse this newly added object - visitEnvDefinition(importFound, { - ...extractObjectImportDependencies( - importsFound, - rootAbi, - namespace, - uri - ), - }); - } else { - const importFound = findImport( - type, - namespaceType, - rootAbi.objectTypes || [], - rootAbi.importedObjectTypes || [], - DefinitionKind.ImportedObject - ) as ImportedObjectDefinition; - - // Keep track of it - importsFound[importFound.type] = importFound; - - // Traverse this newly added object - visitObjectDefinition(importFound, { - ...extractObjectImportDependencies( - importsFound, - rootAbi, - namespace, - uri - ), - }); - } + const importFound = findImport( + type, + namespaceType, + rootAbi.objectTypes || [], + rootAbi.importedObjectTypes || [], + DefinitionKind.ImportedObject + ) as ImportedObjectDefinition; + + // Keep track of it + importsFound[importFound.type] = importFound; + + // Traverse this newly added object + visitObjectDefinition(importFound, { + ...extractObjectImportDependencies( + importsFound, + rootAbi, + namespace, + uri + ), + }); } return def; @@ -680,10 +623,6 @@ async function resolveExternalImports( if (extAbi.moduleType) { extTypesToImport.push(extAbi.moduleType.type); } - - if (extAbi.envType) { - extTypesToImport.push(extAbi.envType.type); - } } // For each imported type to resolve @@ -721,31 +660,6 @@ async function resolveExternalImports( throw Error( `Cannot import an import's imported module type. Tried to import ${importedType} from ${uri}.` ); - } else if (isEnvType(importedType)) { - if (!extAbi.envType) { - throw new Error( - `Tried to import env type from ${uri} but it doesn't exist.` - ); - } - - extTypes = [extAbi.envType]; - visitorFunc = visitEnvDefinition; - const type = extAbi.envType; - trueType = { - ...createImportedEnvDefinition({ - ...type, - name: undefined, - required: undefined, - uri, - nativeType: type.type, - namespace, - }), - properties: type.properties, - }; - } else if (isImportedEnvType(importedType)) { - throw Error( - `Cannot import an import's imported env type. Tried to import ${importedType} from ${uri}.` - ); } else { const objIdx = extAbi.objectTypes ? extAbi.objectTypes.findIndex((def) => def.type === importedType) @@ -753,8 +667,8 @@ async function resolveExternalImports( const impObjIdx = objIdx === -1 && extAbi.importedObjectTypes ? extAbi.importedObjectTypes.findIndex( - (def) => def.type === importedType - ) + (def) => def.type === importedType + ) : -1; const enumIdx = impObjIdx === -1 && extAbi.enumTypes @@ -763,8 +677,8 @@ async function resolveExternalImports( const impEnumIdx = enumIdx === -1 && extAbi.importedEnumTypes ? extAbi.importedEnumTypes.findIndex( - (def) => def.type === importedType - ) + (def) => def.type === importedType + ) : -1; if (objIdx > -1) { @@ -847,8 +761,7 @@ async function resolveExternalImports( if (!trueType) { throw Error( - `Cannot find type "${importedType}" in the schema at ${uri}.\nFound: ${ - extTypes && JSON.stringify(extTypes.map((type) => type.type)) + `Cannot find type "${importedType}" in the schema at ${uri}.\nFound: ${extTypes && JSON.stringify(extTypes.map((type) => type.type)) }` ); } @@ -884,20 +797,10 @@ async function resolveExternalImports( | ImportedObjectDefinition[] | ImportedModuleDefinition[] | ImportedEnumDefinition[] - | ImportedEnvDefinition[] | undefined; let append; - if (importType.kind === DefinitionKind.ImportedEnv) { - destArray = abi.importedEnvTypes; - append = () => { - const importDef = importType as ImportedEnvDefinition; - abi.importedEnvTypes && - abi.importedEnvTypes.push( - visitImportedEnvDefinition(importDef, namespaceTypes(namespace)) - ); - }; - } else if (importType.kind === DefinitionKind.ImportedObject) { + if (importType.kind === DefinitionKind.ImportedObject) { destArray = abi.importedObjectTypes; append = () => { const importDef = importType as ImportedObjectDefinition; @@ -1024,35 +927,29 @@ async function resolveLocalImports( let type: ObjectDefinition | EnumDefinition | undefined; let visitorFunc: Function; - if (isEnvType(importedType)) { - visitorFunc = visitEnvDefinition; - type = localAbi.envType; - } else { - const objectIdx = localAbi.objectTypes - ? localAbi.objectTypes.findIndex((type) => type.type === importedType) - : -1; + const objectIdx = localAbi.objectTypes + ? localAbi.objectTypes.findIndex((type) => type.type === importedType) + : -1; - const enumIdx = - objectIdx === -1 && localAbi.enumTypes - ? localAbi.enumTypes.findIndex((type) => type.type === importedType) - : -1; + const enumIdx = + objectIdx === -1 && localAbi.enumTypes + ? localAbi.enumTypes.findIndex((type) => type.type === importedType) + : -1; - if (objectIdx > -1) { - visitorFunc = visitObjectDefinition; - type = localAbi.objectTypes && localAbi.objectTypes[objectIdx]; - } else if (enumIdx > -1) { - visitorFunc = visitEnumDefinition; - type = - localAbi.enumTypes && - localAbi.enumTypes.find((type) => type.type === importedType); - } + if (objectIdx > -1) { + visitorFunc = visitObjectDefinition; + type = localAbi.objectTypes && localAbi.objectTypes[objectIdx]; + } else if (enumIdx > -1) { + visitorFunc = visitEnumDefinition; + type = + localAbi.enumTypes && + localAbi.enumTypes.find((type) => type.type === importedType); } if (!type) { throw Error( - `Cannot find type "${importedType}" in the schema at ${path}.\nFound: [ ${ - localAbi.objectTypes && - localAbi.objectTypes.map((type) => type.type + " ") + `Cannot find type "${importedType}" in the schema at ${path}.\nFound: [ ${localAbi.objectTypes && + localAbi.objectTypes.map((type) => type.type + " ") }]` ); } @@ -1061,7 +958,7 @@ async function resolveLocalImports( const findImport = ( def: GenericDefinition, - rootTypes: EnumOrObjectOrEnv[] + rootTypes: EnumOrObject[] ) => { // Skip objects that we've already processed if (typesToImport[def.type]) { @@ -1074,7 +971,7 @@ async function resolveLocalImports( if (idx === -1) { throw Error( `resolveLocalImports: Cannot find the requested type within the Abi.\n` + - `Type: ${def.type}\nAbi: ${JSON.stringify(localAbi)}` + `Type: ${def.type}\nAbi: ${JSON.stringify(localAbi)}` ); } @@ -1140,22 +1037,7 @@ async function resolveLocalImports( // Add all imported types into the aggregate Abi for (const importType of Object.keys(typesToImport)) { - if (isKind(typesToImport[importType], DefinitionKind.Env)) { - if (!abi.envType) { - abi.envType = createEnvDefinition({}); - } - - const sharedEnv = localAbi.envType as EnvDefinition; - - if (sharedEnv.properties) { - checkDuplicateEnvProperties(abi.envType, sharedEnv.properties); - if (abi.envType.properties) { - abi.envType.properties.push(...sharedEnv.properties); - } else { - abi.envType.properties = sharedEnv.properties; - } - } - } else if ( + if ( isKind(typesToImport[importType], DefinitionKind.ImportedObject) ) { if ( @@ -1181,7 +1063,7 @@ async function resolveLocalImports( if ( abi.importedEnumTypes && abi.importedEnumTypes.findIndex((def) => def.type === importType) === - -1 + -1 ) { abi.importedEnumTypes.push( typesToImport[importType] as ImportedEnumDefinition diff --git a/packages/schema/compose/src/resolver.ts b/packages/schema/compose/src/resolver.ts index a76f9ad89d..00390ef008 100644 --- a/packages/schema/compose/src/resolver.ts +++ b/packages/schema/compose/src/resolver.ts @@ -1,5 +1,5 @@ -import { createImportedEnumDefinition, createImportedEnvDefinition, createImportedModuleDefinition, createImportedObjectDefinition, DefinitionKind, visitEnumDefinition, visitEnvDefinition, visitModuleDefinition, visitObjectDefinition } from "@polywrap/schema-parse"; -import { EnumDefinition, EnvDefinition, GenericDefinition, ImportedEnumDefinition, ImportedEnvDefinition, ImportedModuleDefinition, ImportedObjectDefinition, ModuleDefinition, ObjectDefinition, WrapAbi } from "@polywrap/wrap-manifest-types-js"; +import { createImportedEnumDefinition, createImportedModuleDefinition, createImportedObjectDefinition, DefinitionKind, visitEnumDefinition, visitModuleDefinition, visitObjectDefinition } from "@polywrap/schema-parse"; +import { EnumDefinition, GenericDefinition, ImportedEnumDefinition, ImportedModuleDefinition, ImportedObjectDefinition, ModuleDefinition, ObjectDefinition, WrapAbi } from "@polywrap/wrap-manifest-types-js"; type UriStr = string; interface ImportStatement { @@ -14,7 +14,7 @@ interface Namespaced { namespace: string; } -type ResolvedType = (ImportedModuleDefinition | ImportedObjectDefinition | ImportedEnumDefinition | ImportedEnvDefinition) & Namespaced; +type ResolvedType = (ImportedModuleDefinition | ImportedObjectDefinition | ImportedEnumDefinition) & Namespaced; type ImportMap = Map< string, ResolvedType @@ -23,9 +23,6 @@ type ImportMap = Map< type ImportDefWithKind = { extDefinition: ImportedModuleDefinition, kind: "ImportedModule" -} | { - extDefinition: ImportedEnvDefinition, - kind: "ImportedEnv" } | { extDefinition: ImportedObjectDefinition, kind: "ImportedObject" @@ -37,9 +34,6 @@ type ImportDefWithKind = { type LocalDefWithKind = { extDefinition: ModuleDefinition, kind: "Module" -} | { - extDefinition: EnvDefinition, - kind: "Env" } | { extDefinition: ObjectDefinition, kind: "Object" @@ -85,16 +79,6 @@ export abstract class ImportsResolver return { extDefinition: extModuleImport, kind: "ImportedModule" } } - const extEnv = importAbi.envType; - if (extEnv && extEnv.type == importType) { - return { extDefinition: extEnv, kind: "Env" } - } - - const extImportEnv = importAbi.importedEnvTypes?.find(def => def.type == importType); - if (extImportEnv) { - return { extDefinition: extImportEnv, kind: "ImportedEnv" } - } - throw new Error(`Could not determine kind of imported type '${importType}'`) } @@ -166,23 +150,7 @@ export abstract class ImportsResolver }, visitor: visitModuleDefinition }; - case "Env": - return { - unnamespacedResolvedType: { - ...createImportedEnvDefinition({ - ...extDefWithKind.extDefinition, - name: undefined, - required: undefined, - nativeType: extDefWithKind.extDefinition.type, - uri: "", - namespace: "", - }), - properties: extDefWithKind.extDefinition.properties, - }, - visitor: visitEnvDefinition - }; case "ImportedModule": - case "ImportedEnv": throw new Error(`Cannot import an import's imported ${extDefWithKind.kind}. Tried to import ${importType}`) } } diff --git a/packages/schema/compose/src/tempabi.ts b/packages/schema/compose/src/tempabi.ts deleted file mode 100644 index f634624caf..0000000000 --- a/packages/schema/compose/src/tempabi.ts +++ /dev/null @@ -1,143 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -/* tslint:disable */ - -export type ObjectDefinition = GenericDefinition & - WithComment & { - properties?: PropertyDefinition[]; - interfaces?: InterfaceImplementedDefinition[]; - }; -export type GenericDefinition = WithKind & { - type: string; - name?: string; - required?: boolean; -}; -export type PropertyDefinition = WithComment & AnyDefinition; -export type AnyDefinition = GenericDefinition & { - array?: ArrayDefinition; - scalar?: ScalarDefinition; - map?: MapDefinition; - object?: ObjectRef; - enum?: EnumRef; - unresolvedObjectOrEnum?: UnresolvedObjectOrEnumRef; -}; -export type ArrayDefinition = AnyDefinition & { - item?: GenericDefinition; -}; -export type ScalarDefinition = GenericDefinition & { - type: - | "UInt" - | "UInt8" - | "UInt16" - | "UInt32" - | "Int" - | "Int8" - | "Int16" - | "Int32" - | "String" - | "Boolean" - | "Bytes" - | "BigInt" - | "BigNumber" - | "JSON"; -}; -export type MapDefinition = AnyDefinition & - WithComment & { - key?: MapKeyDefinition; - value?: GenericDefinition; - }; -export type MapKeyDefinition = AnyDefinition & { - type?: "UInt" | "UInt8" | "UInt16" | "UInt32" | "Int" | "Int8" | "Int16" | "Int32" | "String"; -}; -export type ObjectRef = GenericDefinition; -export type EnumRef = GenericDefinition; -export type UnresolvedObjectOrEnumRef = GenericDefinition; -export type InterfaceImplementedDefinition = GenericDefinition; -export type ModuleDefinition = GenericDefinition & - WithComment & { - methods?: MethodDefinition[]; - imports?: ImportedModuleRef[]; - interfaces?: InterfaceImplementedDefinition[]; - }; -export type MethodDefinition = GenericDefinition & - WithComment & { - arguments?: PropertyDefinition[]; - env?: { - required?: boolean; - }; - return?: PropertyDefinition; - }; -export type EnumDefinition = GenericDefinition & - WithComment & { - constants?: string[]; - }; -export type InterfaceDefinition = GenericDefinition & - ImportedDefinition & { - capabilities?: CapabilityDefinition; - }; -export type ImportedObjectDefinition = ObjectDefinition & ImportedDefinition & WithComment; -export type ImportedModuleDefinition = GenericDefinition & - ImportedDefinition & - WithComment & { - methods?: MethodDefinition[]; - isInterface?: boolean; - }; -export type ImportedEnumDefinition = EnumDefinition & ImportedDefinition; -export type ImportedEnvDefinition = ImportedObjectDefinition; -export type EnvDefinition = ObjectDefinition; - -export interface WrapManifest { - /** - * WRAP Standard Version - */ - version: "0.1.0" | "0.1"; - /** - * Wrapper Package Type - */ - type: "wasm" | "interface" | "plugin"; - /** - * Wrapper Name - */ - name: string; - abi: Abi; -} -/** - * Information of modules - */ -export interface Abi { - /** - * ABI Version - */ - version?: "0.1"; - types?: { - objectTypes?: ObjectDefinition[]; - moduleType?: ModuleDefinition; - enumTypes?: EnumDefinition[]; - interfaceTypes?: InterfaceDefinition[]; - envType?: EnvDefinition; - } - importTypes?: { - importedObjectTypes?: ImportedObjectDefinition[]; - importedModuleTypes?: ImportedModuleDefinition[]; - importedEnumTypes?: ImportedEnumDefinition[]; - importedEnvTypes?: ImportedEnvDefinition[]; - } -} -export interface WithKind { - kind: number; -} -export interface WithComment { - comment?: string; -} -export interface ImportedModuleRef { - type?: string; -} -export interface ImportedDefinition { - uri: string; - namespace: string; - nativeType: string; -} -export interface CapabilityDefinition { - getImplementations?: { - enabled: boolean; - }; -} diff --git a/packages/schema/compose/src/templates/schema.mustache.ts b/packages/schema/compose/src/templates/schema.mustache.ts index 69803ae9ab..6419a43c7f 100644 --- a/packages/schema/compose/src/templates/schema.mustache.ts +++ b/packages/schema/compose/src/templates/schema.mustache.ts @@ -106,7 +106,7 @@ type {{type}}{{#interfaces.length}} implements{{#interfaces}} {{type}}{{^last}} {{/comment}} {{name}}: {{toGraphQLType}} {{/arguments}} - ){{/arguments.length}}: {{#return}}{{toGraphQLType}}{{/return}}{{#env}} @env(required: {{required}}){{/env}} + ){{/arguments.length}}: {{#return}}{{toGraphQLType}}{{/return}} {{^last}} {{/last}} @@ -155,30 +155,6 @@ enum {{type}} @imported( } {{/importedEnumTypes}} -### Imported Objects END ### - -### Imported Envs START ### - -{{#importedEnvTypes}}{{#comment}} -""" -{{comment}} -""" -{{/comment}} -type {{type}}{{#interfaces.length}} implements{{#interfaces}} {{type}}{{^last}} &{{/last}}{{/interfaces}}{{/interfaces.length}} @imported( - uri: "{{uri}}", - namespace: "{{namespace}}", - nativeType: "{{nativeType}}" -){{#properties.length}} { - {{#properties}}{{#comment}} - """ - {{comment}} - """ - {{/comment}} - {{name}}: {{toGraphQLType}} - {{/properties}} -}{{/properties.length}} - -{{/importedEnvTypes}} -### Imported Envs END ###{{/abi}}`; +### Imported Objects END ###{{/abi}}`; export { template }; diff --git a/packages/schema/compose/src/todo.md b/packages/schema/compose/src/todo.md deleted file mode 100644 index ceab0a4479..0000000000 --- a/packages/schema/compose/src/todo.md +++ /dev/null @@ -1 +0,0 @@ -- Parse everything first -> give already parsed ABIs to compose \ No newline at end of file From 8518ff92df925efc711ddfa39bf781a9845c973d Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Wed, 8 Feb 2023 16:48:43 +0100 Subject: [PATCH 17/90] (feat): remove env from bind --- .../src/bindings/assemblyscript/wasm/index.ts | 28 --- .../wasm/templates/entry-ts.mustache | 6 +- .../wasm/templates/env-type/index-ts.mustache | 37 ---- .../env-type/serialization-ts.mustache | 149 --------------- .../imported/env-type/index-ts.mustache | 41 ----- .../env-type/serialization-ts.mustache | 149 --------------- .../wasm/templates/imported/index-ts.mustache | 3 - .../wasm/templates/index-ts.mustache | 6 - .../templates/module-type/wrapped-ts.mustache | 23 +-- .../bind/src/bindings/rust/wasm/index.ts | 31 ---- .../rust/wasm/templates/entry-rs.mustache | 6 +- .../wasm/templates/env-type/mod-rs.mustache | 54 ------ .../env-type/serialization-rs.mustache | 169 ------------------ .../imported/env-type/mod-rs.mustache | 59 ------ .../env-type/serialization-rs.mustache | 169 ------------------ .../wasm/templates/imported/mod-rs.mustache | 6 +- .../templates/module-type/wrapped-rs.mustache | 31 +--- .../rust/wasm/transforms/propertyDeps.ts | 23 +-- .../plugin/templates/module-ts.mustache | 2 +- .../plugin/templates/types-ts.mustache | 10 -- 20 files changed, 13 insertions(+), 989 deletions(-) delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/env-type/index-ts.mustache delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/env-type/serialization-ts.mustache delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/env-type/index-ts.mustache delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/env-type/serialization-ts.mustache delete mode 100644 packages/schema/bind/src/bindings/rust/wasm/templates/env-type/mod-rs.mustache delete mode 100644 packages/schema/bind/src/bindings/rust/wasm/templates/env-type/serialization-rs.mustache delete mode 100644 packages/schema/bind/src/bindings/rust/wasm/templates/imported/env-type/mod-rs.mustache delete mode 100644 packages/schema/bind/src/bindings/rust/wasm/templates/imported/env-type/serialization-rs.mustache diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts b/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts index 1bfdea57cc..41105bbe47 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts @@ -63,21 +63,6 @@ export const generateBinding: GenerateBindingFn = ( } } - // Generate imported env type folders - if (abi.importedEnvTypes) { - for (const importedEnvType of abi.importedEnvTypes) { - importEntries.push({ - type: "Directory", - name: importedEnvType.type, - data: renderTemplates( - templatePath("imported/env-type"), - importedEnvType, - subTemplates - ), - }); - } - } - // Generate imported enum type folders if (abi.importedEnumTypes) { for (const importedEnumType of abi.importedEnumTypes) { @@ -162,19 +147,6 @@ export const generateBinding: GenerateBindingFn = ( } } - // Generate env type folders - if (abi.envType) { - output.entries.push({ - type: "Directory", - name: abi.envType.type, - data: renderTemplates( - templatePath("env-type"), - abi.envType, - subTemplates - ), - }); - } - // Generate root entry file output.entries.push(...renderTemplates(templatePath(""), abi, subTemplates)); diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.mustache index 0bf55b2b8b..e44fc81f68 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.mustache @@ -15,7 +15,7 @@ import { {{/methods.length}} {{/moduleType}} -export function _wrap_invoke(method_size: u32, args_size: u32, env_size: u32): bool { +export function _wrap_invoke(method_size: u32, args_size: u32): bool { const args: InvokeArgs = wrap_invoke_args( method_size, args_size @@ -24,12 +24,12 @@ export function _wrap_invoke(method_size: u32, args_size: u32, env_size: u32): b {{#moduleType}} {{#methods}} {{^first}}else {{/first}}if (args.method == "{{name}}") { - return wrap_invoke(args, env_size, {{name}}Wrapped); + return wrap_invoke(args, {{name}}Wrapped); } {{/methods}} {{/moduleType}} else { - return wrap_invoke(args, env_size, null); + return wrap_invoke(args, null); } } diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/env-type/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/env-type/index-ts.mustache deleted file mode 100644 index 8199149c0d..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/env-type/index-ts.mustache +++ /dev/null @@ -1,37 +0,0 @@ -import { - Read, - Write, - Box, - BigInt, - BigNumber, - JSON -} from "@polywrap/wasm-as"; -import { - serialize{{type}}, - deserialize{{type}}, - write{{type}}, - read{{type}} -} from "./serialization"; -import * as Types from ".."; - -export class {{#detectKeyword}}{{type}}{{/detectKeyword}} { - {{#properties}} - {{#detectKeyword}}{{name}}{{/detectKeyword}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}}; - {{/properties}} - - static toBuffer(type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): ArrayBuffer { - return serialize{{type}}(type); - } - - static fromBuffer(buffer: ArrayBuffer): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - return deserialize{{type}}(buffer); - } - - static write(writer: Write, type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): void { - write{{type}}(writer, type); - } - - static read(reader: Read): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - return read{{type}}(reader); - } -} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/env-type/serialization-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/env-type/serialization-ts.mustache deleted file mode 100644 index 5b9a53d9b6..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/env-type/serialization-ts.mustache +++ /dev/null @@ -1,149 +0,0 @@ -{{> serialization_imports}} -import { {{#detectKeyword}}{{type}}{{/detectKeyword}} } from "./"; -import * as Types from ".."; - -export function serialize{{type}}(type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): ArrayBuffer { - const sizerContext: Context = new Context("Serializing (sizing) env-type: {{type}}"); - const sizer = new WriteSizer(sizerContext); - write{{type}}(sizer, type); - const buffer = new ArrayBuffer(sizer.length); - const encoderContext: Context = new Context("Serializing (encoding) env-type: {{type}}"); - const encoder = new WriteEncoder(buffer, sizer, encoderContext); - write{{type}}(encoder, type); - return buffer; -} - -export function write{{type}}(writer: Write, type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): void { - {{#properties.length}} - writer.writeMapLength({{properties.length}}); - {{/properties.length}} - {{^properties}} - writer.writeMapLength(0); - {{/properties}} - {{#properties}} - writer.context().push("{{name}}", "{{#toWasm}}{{toGraphQLType}}{{/toWasm}}", "writing property"); - writer.writeString("{{name}}"); - {{#scalar}} - writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); - {{/scalar}} - {{#array}} - writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(type.{{#detectKeyword}}{{name}}{{/detectKeyword}}, (writer: Write, item: {{#item}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/item}}): void => { - {{> serialize_array}} - }); - {{/array}} - {{#map}} - writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(type.{{#detectKeyword}}{{name}}{{/detectKeyword}}, (writer: Write, key: {{#key}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/key}}) => { - writer.write{{#key}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/key}}(key); - }, (writer: Write, value: {{#value}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/value}}): void => { - {{> serialize_map_value}} - }); - {{/map}} - {{#object}} - {{#required}} - Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}.write(writer, type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); - {{/required}} - {{^required}} - if (type.{{#detectKeyword}}{{name}}{{/detectKeyword}}) { - Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}.write(writer, type.{{#detectKeyword}}{{name}}{{/detectKeyword}} as Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}); - } else { - writer.writeNil(); - } - {{/required}} - {{/object}} - {{#enum}} - {{#required}} - writer.writeInt32(type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); - {{/required}} - {{^required}} - writer.writeOptionalInt32(type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); - {{/required}} - {{/enum}} - writer.context().pop(); - {{/properties}} -} - -export function deserialize{{type}}(buffer: ArrayBuffer): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - const context: Context = new Context("Deserializing env-type {{type}}"); - const reader = new ReadDecoder(buffer, context); - return read{{type}}(reader); -} - -export function read{{type}}(reader: Read): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - let numFields = reader.readMapLength(); - - {{#properties}} - {{^object}} - let _{{name}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} = {{#toWasmInit}}{{toGraphQLType}}{{/toWasmInit}}; - {{/object}} - {{#object}} - {{#required}} - let _{{name}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} | null = null; - {{/required}} - {{^required}} - let _{{name}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} = {{#toWasmInit}}{{toGraphQLType}}{{/toWasmInit}}; - {{/required}} - {{/object}} - {{#required}} - let _{{name}}Set: bool = false; - {{/required}} - {{/properties}} - - while (numFields > 0) { - numFields--; - const field = reader.readString(); - - reader.context().push(field, "unknown", "searching for property type"); - {{#properties}} - {{^first}}else {{/first}}if (field == "{{name}}") { - reader.context().push(field, "{{#toWasm}}{{toGraphQLType}}{{/toWasm}}", "type found, reading property"); - {{#scalar}} - _{{name}} = reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(); - {{/scalar}} - {{#array}} - _{{name}} = reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}((reader: Read): {{#item}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/item}} => { - {{> deserialize_array}} - }); - {{/array}} - {{#map}} - _{{name}} = reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}((reader: Read): {{#key}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/key}} => { - return reader.read{{#key}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/key}}(); - }, (reader: Read): {{#value}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/value}} => { - {{> deserialize_map_value}} - }); - {{/map}} - {{#enum}} - {{> deserialize_enum}} - _{{name}} = value; - {{/enum}} - {{#object}} - {{> deserialize_object }} - _{{name}} = object; - {{/object}} - {{#required}} - _{{name}}Set = true; - {{/required}} - reader.context().pop(); - } - {{/properties}} - reader.context().pop(); - } - - {{#properties}} - {{#required}} - {{^object}} - if (!_{{name}}Set) { - {{/object}} - {{#object}} - if (!_{{name}} || !_{{name}}Set) { - {{/object}} - throw new Error(reader.context().printWithContext("Missing required property: '{{name}}: {{type}}'")); - } - {{/required}} - {{/properties}} - - return { - {{#properties}} - {{#detectKeyword}}{{name}}{{/detectKeyword}}: _{{name}}{{^last}},{{/last}} - {{/properties}} - }; -} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/env-type/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/env-type/index-ts.mustache deleted file mode 100644 index a9e01076b8..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/env-type/index-ts.mustache +++ /dev/null @@ -1,41 +0,0 @@ -import { - Read, - Write, - BigInt, - BigNumber, - JSON, -} from "@polywrap/wasm-as"; -import { - serialize{{type}}, - deserialize{{type}}, - write{{type}}, - read{{type}} -} from "./serialization"; -import * as Types from "../.."; - -@serializable -export class {{#detectKeyword}}{{type}}{{/detectKeyword}} { - - public static uri: string = "{{uri}}"; - - {{#properties}} - {{#detectKeyword}}{{name}}{{/detectKeyword}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}}; - {{/properties}} - - static toBuffer(type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): ArrayBuffer { - return serialize{{type}}(type); - } - - static fromBuffer(buffer: ArrayBuffer): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - return deserialize{{type}}(buffer); - } - - static write(writer: Write, type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): void { - write{{type}}(writer, type); - } - - static read(reader: Read): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - return read{{type}}(reader); - } - {{> json_methods}} -} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/env-type/serialization-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/env-type/serialization-ts.mustache deleted file mode 100644 index 9d524437cf..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/env-type/serialization-ts.mustache +++ /dev/null @@ -1,149 +0,0 @@ -{{> serialization_imports}} -import { {{#detectKeyword}}{{type}}{{/detectKeyword}} } from "./"; -import * as Types from "../.."; - -export function serialize{{type}}(type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): ArrayBuffer { - const sizerContext: Context = new Context("Serializing (sizing) imported env-type: {{type}}"); - const sizer = new WriteSizer(sizerContext); - write{{type}}(sizer, type); - const buffer = new ArrayBuffer(sizer.length); - const encoderContext: Context = new Context("Serializing (encoding) imported env-type: {{type}}"); - const encoder = new WriteEncoder(buffer, sizer, encoderContext); - write{{type}}(encoder, type); - return buffer; -} - -export function write{{type}}(writer: Write, type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): void { - {{#properties.length}} - writer.writeMapLength({{properties.length}}); - {{/properties.length}} - {{^properties}} - writer.writeMapLength(0); - {{/properties}} - {{#properties}} - writer.context().push("{{name}}", "{{#toWasm}}{{toGraphQLType}}{{/toWasm}}", "writing property"); - writer.writeString("{{name}}"); - {{#scalar}} - writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); - {{/scalar}} - {{#array}} - writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(type.{{#detectKeyword}}{{name}}{{/detectKeyword}}, (writer: Write, item: {{#item}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/item}}): void => { - {{> serialize_array}} - }); - {{/array}} - {{#map}} - writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(type.{{#detectKeyword}}{{name}}{{/detectKeyword}}, (writer: Write, key: {{#key}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/key}}) => { - writer.write{{#key}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/key}}(key); - }, (writer: Write, value: {{#value}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/value}}): void => { - {{> serialize_map_value}} - }); - {{/map}} - {{#object}} - {{#required}} - Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}.write(writer, type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); - {{/required}} - {{^required}} - if (type.{{#detectKeyword}}{{name}}{{/detectKeyword}}) { - Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}.write(writer, type.{{#detectKeyword}}{{name}}{{/detectKeyword}} as Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}); - } else { - writer.writeNil(); - } - {{/required}} - {{/object}} - {{#enum}} - {{#required}} - writer.writeInt32(type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); - {{/required}} - {{^required}} - writer.writeOptionalInt32(type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); - {{/required}} - {{/enum}} - writer.context().pop(); - {{/properties}} -} - -export function deserialize{{type}}(buffer: ArrayBuffer): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - const context: Context = new Context("Deserializing imported env-type {{type}}"); - const reader = new ReadDecoder(buffer, context); - return read{{type}}(reader); -} - -export function read{{type}}(reader: Read): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - let numFields = reader.readMapLength(); - - {{#properties}} - {{^object}} - let _{{name}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} = {{#toWasmInit}}{{toGraphQLType}}{{/toWasmInit}}; - {{/object}} - {{#object}} - {{#required}} - let _{{name}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} | null = null; - {{/required}} - {{^required}} - let _{{name}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} = {{#toWasmInit}}{{toGraphQLType}}{{/toWasmInit}}; - {{/required}} - {{/object}} - {{#required}} - let _{{name}}Set: bool = false; - {{/required}} - {{/properties}} - - while (numFields > 0) { - numFields--; - const field = reader.readString(); - - reader.context().push(field, "unknown", "searching for property type"); - {{#properties}} - {{^first}}else {{/first}}if (field == "{{name}}") { - reader.context().push(field, "{{#toWasm}}{{toGraphQLType}}{{/toWasm}}", "type found, reading property"); - {{#scalar}} - _{{name}} = reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(); - {{/scalar}} - {{#array}} - _{{name}} = reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}((reader: Read): {{#item}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/item}} => { - {{> deserialize_array}} - }); - {{/array}} - {{#map}} - _{{name}} = reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}((reader: Read): {{#key}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/key}} => { - return reader.read{{#key}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/key}}(); - }, (reader: Read): {{#value}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/value}} => { - {{> deserialize_map_value}} - }); - {{/map}} - {{#enum}} - {{> deserialize_enum}} - _{{name}} = value; - {{/enum}} - {{#object}} - {{> deserialize_object }} - _{{name}} = object; - {{/object}} - {{#required}} - _{{name}}Set = true; - {{/required}} - reader.context().pop(); - } - {{/properties}} - reader.context().pop(); - } - - {{#properties}} - {{#required}} - {{^object}} - if (!_{{name}}Set) { - {{/object}} - {{#object}} - if (!_{{name}} || !_{{name}}Set) { - {{/object}} - throw new Error(reader.context().printWithContext("Missing required property: '{{name}}: {{type}}'")); - } - {{/required}} - {{/properties}} - - return { - {{#properties}} - {{#detectKeyword}}{{name}}{{/detectKeyword}}: _{{name}}{{^last}},{{/last}} - {{/properties}} - }; -} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.mustache index f37a0641b8..6f31291e32 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.mustache @@ -7,6 +7,3 @@ export * from "./{{type}}"; {{#importedEnumTypes}} export * from "./{{type}}"; {{/importedEnumTypes}} -{{#importedEnvTypes}} -export * from "./{{type}}"; -{{/importedEnvTypes}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.mustache index 2f2c2f2941..be38308fd7 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.mustache @@ -33,9 +33,6 @@ export { {{#detectKeyword}}{{type}}{{/detectKeyword}} } from "./imported/{{type} {{#importedObjectTypes}} export { {{#detectKeyword}}{{type}}{{/detectKeyword}} } from "./imported/{{type}}"; {{/importedObjectTypes}} -{{#importedEnvTypes}} -export { {{#detectKeyword}}{{type}}{{/detectKeyword}} } from "./imported/{{type}}"; -{{/importedEnvTypes}} {{#importedEnumTypes}} export { {{#detectKeyword}}{{type}}{{/detectKeyword}}, @@ -47,6 +44,3 @@ export { {{#interfaceTypes}} export { {{#detectKeyword}}{{namespace}}{{/detectKeyword}} } from "./{{namespace}}"; {{/interfaceTypes}} -{{#envType}} -export { Env } from "./Env"; -{{/envType}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.mustache index 239f0cb479..c7fa81c62e 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.mustache @@ -1,5 +1,4 @@ {{#methods.length}} -import { wrap_load_env } from "@polywrap/wasm-as"; import { {{#methods}} {{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} @@ -15,24 +14,7 @@ import { import * as Types from ".."; {{#methods}} -export function {{name}}Wrapped(argsBuf: ArrayBuffer, env_size: u32): ArrayBuffer { - {{#env}} - {{#required}} - if (env_size == 0) { - throw new Error("Environment is not set, and it is required by method 'objectMethod'") - } - - const envBuf = wrap_load_env(env_size); - const env = Types.Env.fromBuffer(envBuf); - {{/required}} - {{^required}} - let env: Types.Env | null = null; - if (env_size > 0) { - const envBuf = wrap_load_env(env_size); - env = Types.Env.fromBuffer(envBuf); - } - {{/required}} - {{/env}} +export function {{name}}Wrapped(argsBuf: ArrayBuffer): ArrayBuffer { {{#arguments.length}} const args = deserialize{{name}}Args(argsBuf); {{/arguments.length}} @@ -42,8 +24,7 @@ export function {{name}}Wrapped(argsBuf: ArrayBuffer, env_size: u32): ArrayBuffe {{#arguments}} {{#detectKeyword}}{{name}}{{/detectKeyword}}: args.{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} {{/arguments}} - }{{/arguments.length}}{{^arguments.length}}{}{{/arguments.length}}{{#env}}, - env{{/env}} + }{{/arguments.length}}{{^arguments.length}}{}{{/arguments.length}} ); return serialize{{name}}Result(result); } diff --git a/packages/schema/bind/src/bindings/rust/wasm/index.ts b/packages/schema/bind/src/bindings/rust/wasm/index.ts index 6fcb91eeb2..31ce29bc63 100644 --- a/packages/schema/bind/src/bindings/rust/wasm/index.ts +++ b/packages/schema/bind/src/bindings/rust/wasm/index.ts @@ -50,19 +50,6 @@ export const generateBinding: GenerateBindingFn = ( } } - // Generate env type folders - if (abi.envType) { - output.entries.push({ - type: "Directory", - name: Functions.detectKeyword()(toLower(abi.envType.type), (str) => str), - data: renderTemplates( - templatePath("env-type"), - abi.envType, - subTemplates - ), - }); - } - // Generate imported folder const importEntries: OutputEntry[] = []; @@ -117,24 +104,6 @@ export const generateBinding: GenerateBindingFn = ( } } - // Generate imported env type folders - if (abi.importedEnvTypes) { - for (const importedEnvType of abi.importedEnvTypes) { - importEntries.push({ - type: "Directory", - name: Functions.detectKeyword()( - toLower(importedEnvType.type), - (str) => str - ), - data: renderTemplates( - templatePath("imported/env-type"), - importedEnvType, - subTemplates - ), - }); - } - } - if (importEntries.length > 0) { output.entries.push({ type: "Directory", diff --git a/packages/schema/bind/src/bindings/rust/wasm/templates/entry-rs.mustache b/packages/schema/bind/src/bindings/rust/wasm/templates/entry-rs.mustache index 5e23b926cc..450d2c5cd7 100644 --- a/packages/schema/bind/src/bindings/rust/wasm/templates/entry-rs.mustache +++ b/packages/schema/bind/src/bindings/rust/wasm/templates/entry-rs.mustache @@ -14,7 +14,7 @@ use polywrap_wasm_rs::{ }; #[no_mangle] -pub extern "C" fn _wrap_invoke(method_size: u32, args_size: u32, env_size: u32) -> bool { +pub extern "C" fn _wrap_invoke(method_size: u32, args_size: u32) -> bool { // Ensure the abort handler is properly setup abort::wrap_abort_setup(); @@ -23,9 +23,9 @@ pub extern "C" fn _wrap_invoke(method_size: u32, args_size: u32, env_size: u32) match args.method.as_str() { {{#moduleType}} {{#methods}} - "{{name}}" => invoke::wrap_invoke(args, env_size, Some({{#toLower}}{{name}}{{/toLower}}_wrapped)), + "{{name}}" => invoke::wrap_invoke(args, Some({{#toLower}}{{name}}{{/toLower}}_wrapped)), {{/methods}} {{/moduleType}} - _ => invoke::wrap_invoke(args, env_size, None), + _ => invoke::wrap_invoke(args, None), } } diff --git a/packages/schema/bind/src/bindings/rust/wasm/templates/env-type/mod-rs.mustache b/packages/schema/bind/src/bindings/rust/wasm/templates/env-type/mod-rs.mustache deleted file mode 100644 index 77569c3e85..0000000000 --- a/packages/schema/bind/src/bindings/rust/wasm/templates/env-type/mod-rs.mustache +++ /dev/null @@ -1,54 +0,0 @@ -use serde::{Serialize, Deserialize}; -pub mod serialization; -use polywrap_wasm_rs::{ - BigInt, - BigNumber, - Map, - DecodeError, - EncodeError, - Read, - Write, - JSON, -}; -pub use serialization::{ - deserialize_{{#toLower}}{{type}}{{/toLower}}, - read_{{#toLower}}{{type}}{{/toLower}}, - serialize_{{#toLower}}{{type}}{{/toLower}}, - write_{{#toLower}}{{type}}{{/toLower}} -}; -{{#propertyDeps}} -use {{crate}}::{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}; -{{/propertyDeps}} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}} { - {{#properties}} - {{#serdeKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/serdeKeyword}}pub {{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}}, - {{/properties}} -} - -impl {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}} { - pub fn new() -> {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}} { - {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}} { - {{#properties}} - {{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}: {{#toWasmInit}}{{toGraphQLType}}{{/toWasmInit}}, - {{/properties}} - } - } - - pub fn to_buffer(args: &{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}) -> Result, EncodeError> { - serialize_{{#toLower}}{{type}}{{/toLower}}(args).map_err(|e| EncodeError::TypeWriteError(e.to_string())) - } - - pub fn from_buffer(args: &[u8]) -> Result<{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}, DecodeError> { - deserialize_{{#toLower}}{{type}}{{/toLower}}(args).map_err(|e| DecodeError::TypeReadError(e.to_string())) - } - - pub fn write(args: &{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}, writer: &mut W) -> Result<(), EncodeError> { - write_{{#toLower}}{{type}}{{/toLower}}(args, writer).map_err(|e| EncodeError::TypeWriteError(e.to_string())) - } - - pub fn read(reader: &mut R) -> Result<{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}, DecodeError> { - read_{{#toLower}}{{type}}{{/toLower}}(reader).map_err(|e| DecodeError::TypeReadError(e.to_string())) - } -} diff --git a/packages/schema/bind/src/bindings/rust/wasm/templates/env-type/serialization-rs.mustache b/packages/schema/bind/src/bindings/rust/wasm/templates/env-type/serialization-rs.mustache deleted file mode 100644 index fad845897d..0000000000 --- a/packages/schema/bind/src/bindings/rust/wasm/templates/env-type/serialization-rs.mustache +++ /dev/null @@ -1,169 +0,0 @@ -use std::convert::TryFrom; -use polywrap_wasm_rs::{ - BigInt, - BigNumber, - Map, - Context, - DecodeError, - EncodeError, - Read, - ReadDecoder, - Write, - WriteEncoder, - JSON, -}; -use crate::{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}; -{{#propertyDeps.length}} - -{{/propertyDeps.length}}{{#propertyDeps}} -{{^isEnum}} -use {{crate}}::{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}; -{{/isEnum}} -{{#isEnum}} -use crate::{ - {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}, - get_{{#toLower}}{{type}}{{/toLower}}_value, - sanitize_{{#toLower}}{{type}}{{/toLower}}_value -}; -{{/isEnum}} -{{/propertyDeps}} - -pub fn serialize_{{#toLower}}{{type}}{{/toLower}}(args: &{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}) -> Result, EncodeError> { - let mut encoder_context = Context::new(); - encoder_context.description = "Serializing (encoding) env-type: {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}".to_string(); - let mut encoder = WriteEncoder::new(&[], encoder_context); - write_{{#toLower}}{{type}}{{/toLower}}(args, &mut encoder)?; - Ok(encoder.get_buffer()) -} - -pub fn write_{{#toLower}}{{type}}{{/toLower}}(args: &{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}, writer: &mut W) -> Result<(), EncodeError> { - {{#properties.length}} - writer.write_map_length(&{{properties.length}})?; - {{/properties.length}} - {{^properties}} - writer.write_map_length(&0)?; - {{/properties}} - {{#properties}} - writer.context().push("{{name}}", "{{#toWasm}}{{toGraphQLType}}{{/toWasm}}", "writing property"); - writer.write_string("{{name}}")?; - {{#scalar}} - writer.write_{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}(&args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}})?; - {{/scalar}} - {{#array}} - writer.write_{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}(&args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}, |writer, item| { - {{> serialize_array}} - })?; - {{/array}} - {{#map}} - writer.write_{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}(&args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}, |writer, key| { - writer.write_{{#key}}{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}{{/key}}(key) - }, |writer, value| { - {{> serialize_map_value}} - })?; - {{/map}} - {{#object}} - {{#required}} - {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}::write(&args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}, writer)?; - {{/required}} - {{^required}} - if args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}.is_some() { - {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}::write(args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}.as_ref().as_ref().unwrap(), writer)?; - } else { - writer.write_nil()?; - } - {{/required}} - {{/object}} - {{#enum}} - {{#required}} - writer.write_i32(&(args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}} as i32))?; - {{/required}} - {{^required}} - writer.write_optional_i32(&args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}.map(|f| f as i32))?; - {{/required}} - {{/enum}} - writer.context().pop(); - {{/properties}} - Ok(()) -} - -pub fn deserialize_{{#toLower}}{{type}}{{/toLower}}(args: &[u8]) -> Result<{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}, DecodeError> { - let mut context = Context::new(); - context.description = "Deserializing env-type: {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}".to_string(); - let mut reader = ReadDecoder::new(args, context); - read_{{#toLower}}{{type}}{{/toLower}}(&mut reader) -} - -pub fn read_{{#toLower}}{{type}}{{/toLower}}(reader: &mut R) -> Result<{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}, DecodeError> { - let mut num_of_fields = reader.read_map_length()?; - - {{#properties}} - {{^object}} - let mut _{{#toLower}}{{name}}{{/toLower}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} = {{#toWasmInit}}{{toGraphQLType}}{{/toWasmInit}}; - {{/object}} - {{#object}} - {{#required}} - let mut _{{#toLower}}{{name}}{{/toLower}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} = {{#toWasmInit}}{{toGraphQLType}}{{/toWasmInit}}; - {{/required}} - {{^required}} - let mut _{{#toLower}}{{name}}{{/toLower}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} = None; - {{/required}} - {{/object}} - {{#required}} - let mut _{{#toLower}}{{name}}{{/toLower}}_set = false; - {{/required}} - {{/properties}} - - while num_of_fields > 0 { - num_of_fields -= 1; - let field = reader.read_string()?; - - match field.as_str() { - {{#properties}} - "{{name}}" => { - reader.context().push(&field, "{{#toWasm}}{{toGraphQLType}}{{/toWasm}}", "type found, reading property"); - {{#scalar}} - _{{#toLower}}{{name}}{{/toLower}} = reader.read_{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}()?; - {{/scalar}} - {{#array}} - _{{#toLower}}{{name}}{{/toLower}} = reader.read_{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}(|reader| { - {{> deserialize_array_nobox}} - })?; - {{/array}} - {{#map}} - _{{#toLower}}{{name}}{{/toLower}} = reader.read_{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}(|reader| { - reader.read_{{#key}}{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}{{/key}}()? - }, |reader| { - {{> deserialize_map_value_nobox}} - })?; - {{/map}} - {{#enum}} - {{> deserialize_enum}} - _{{#toLower}}{{name}}{{/toLower}} = value; - {{/enum}} - {{#object}} - {{> deserialize_object_nobox}} - _{{#toLower}}{{name}}{{/toLower}} = object; - {{/object}} - {{#required}} - _{{#toLower}}{{name}}{{/toLower}}_set = true; - {{/required}} - reader.context().pop(); - } - {{/properties}} - err => return Err(DecodeError::UnknownFieldName(err.to_string())), - } - } - {{#properties}} - {{#required}} - if !_{{#toLower}}{{name}}{{/toLower}}_set { - return Err(DecodeError::MissingField("{{name}}: {{type}}.".to_string())); - } - {{/required}} - {{/properties}} - - Ok({{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}} { - {{#properties}} - {{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}: _{{#toLower}}{{name}}{{/toLower}}, - {{/properties}} - }) -} diff --git a/packages/schema/bind/src/bindings/rust/wasm/templates/imported/env-type/mod-rs.mustache b/packages/schema/bind/src/bindings/rust/wasm/templates/imported/env-type/mod-rs.mustache deleted file mode 100644 index 0553426858..0000000000 --- a/packages/schema/bind/src/bindings/rust/wasm/templates/imported/env-type/mod-rs.mustache +++ /dev/null @@ -1,59 +0,0 @@ -use serde::{Serialize, Deserialize}; -pub mod serialization; -use polywrap_wasm_rs::{ - BigInt, - BigNumber, - Map, - DecodeError, - EncodeError, - Read, - Write, - JSON, -}; -pub use serialization::{ - deserialize_{{#toLower}}{{type}}{{/toLower}}, - read_{{#toLower}}{{type}}{{/toLower}}, - serialize_{{#toLower}}{{type}}{{/toLower}}, - write_{{#toLower}}{{type}}{{/toLower}} -}; -{{#propertyDeps.length}} - -{{#propertyDeps}} -use {{crate}}::{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}; -{{/propertyDeps}} -{{/propertyDeps.length}} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}} { - {{#properties}} - pub {{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}}, - {{/properties}} -} - -impl {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}} { - pub const URI: &'static str = "{{uri}}"; - - pub fn new() -> {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}} { - {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}} { - {{#properties}} - {{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}: {{#toWasmInit}}{{toGraphQLType}}{{/toWasmInit}}, - {{/properties}} - } - } - - pub fn to_buffer(args: &{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}) -> Result, EncodeError> { - serialize_{{#toLower}}{{type}}{{/toLower}}(args).map_err(|e| EncodeError::TypeWriteError(e.to_string())) - } - - pub fn from_buffer(args: &[u8]) -> Result<{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}, DecodeError> { - deserialize_{{#toLower}}{{type}}{{/toLower}}(args).map_err(|e| DecodeError::TypeReadError(e.to_string())) - } - - pub fn write(args: &{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}, writer: &mut W) -> Result<(), EncodeError> { - write_{{#toLower}}{{type}}{{/toLower}}(args, writer).map_err(|e| EncodeError::TypeWriteError(e.to_string())) - } - - pub fn read(reader: &mut R) -> Result<{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}, DecodeError> { - read_{{#toLower}}{{type}}{{/toLower}}(reader).map_err(|e| DecodeError::TypeReadError(e.to_string())) - } -} diff --git a/packages/schema/bind/src/bindings/rust/wasm/templates/imported/env-type/serialization-rs.mustache b/packages/schema/bind/src/bindings/rust/wasm/templates/imported/env-type/serialization-rs.mustache deleted file mode 100644 index e451fe9180..0000000000 --- a/packages/schema/bind/src/bindings/rust/wasm/templates/imported/env-type/serialization-rs.mustache +++ /dev/null @@ -1,169 +0,0 @@ -use std::convert::TryFrom; -use polywrap_wasm_rs::{ - BigInt, - BigNumber, - Map, - Context, - DecodeError, - EncodeError, - Read, - ReadDecoder, - Write, - WriteEncoder, - JSON, -}; -use crate::{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}; -{{#propertyDeps.length}} - -{{/propertyDeps.length}}{{#propertyDeps}} -{{^isEnum}} -use {{crate}}::{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}; -{{/isEnum}} -{{#isEnum}} -use crate::{ - {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}, - get_{{#toLower}}{{type}}{{/toLower}}_value, - sanitize_{{#toLower}}{{type}}{{/toLower}}_value -}; -{{/isEnum}} -{{/propertyDeps}} - -pub fn serialize_{{#toLower}}{{type}}{{/toLower}}(args: &{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}) -> Result, EncodeError> { - let mut encoder_context = Context::new(); - encoder_context.description = "Serializing (encoding) imported env-type: {{#toUpper}}{{type}}{{/toUpper}}".to_string(); - let mut encoder = WriteEncoder::new(&[], encoder_context); - write_{{#toLower}}{{type}}{{/toLower}}(args, &mut encoder)?; - Ok(encoder.get_buffer()) -} - -pub fn write_{{#toLower}}{{type}}{{/toLower}}(args: &{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}, writer: &mut W) -> Result<(), EncodeError> { - {{#properties.length}} - writer.write_map_length(&{{properties.length}})?; - {{/properties.length}} - {{^properties}} - writer.write_map_length(&0)?; - {{/properties}} - {{#properties}} - writer.context().push("{{name}}", "{{#toWasm}}{{toGraphQLType}}{{/toWasm}}", "writing property"); - writer.write_string("{{name}}")?; - {{#scalar}} - writer.write_{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}(&args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}})?; - {{/scalar}} - {{#array}} - writer.write_{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}(&args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}, |writer, item| { - {{> serialize_array}} - })?; - {{/array}} - {{#map}} - writer.write_{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}(&args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}, |writer, key| { - writer.write_{{#key}}{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}{{/key}}(key) - }, |writer, value| { - {{> serialize_map_value}} - })?; - {{/map}} - {{#object}} - {{#required}} - {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}::write(&args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}, writer)?; - {{/required}} - {{^required}} - if args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}.is_some() { - {{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}::write(args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}.as_ref().as_ref().unwrap(), writer)?; - } else { - writer.write_nil()?; - } - {{/required}} - {{/object}} - {{#enum}} - {{#required}} - writer.write_i32(&(args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}} as i32))?; - {{/required}} - {{^required}} - writer.write_optional_i32(&args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}.map(|f| f as i32))?; - {{/required}} - {{/enum}} - writer.context().pop(); - {{/properties}} - Ok(()) -} - -pub fn deserialize_{{#toLower}}{{type}}{{/toLower}}(args: &[u8]) -> Result<{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}, DecodeError> { - let mut context = Context::new(); - context.description = "Deserializing imported env-type: {{#toUpper}}{{type}}{{/toUpper}}".to_string(); - let mut reader = ReadDecoder::new(args, context); - read_{{#toLower}}{{type}}{{/toLower}}(&mut reader) -} - -pub fn read_{{#toLower}}{{type}}{{/toLower}}(reader: &mut R) -> Result<{{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}}, DecodeError> { - let mut num_of_fields = reader.read_map_length()?; - - {{#properties}} - {{^object}} - let mut _{{#toLower}}{{name}}{{/toLower}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} = {{#toWasmInit}}{{toGraphQLType}}{{/toWasmInit}}; - {{/object}} - {{#object}} - {{#required}} - let mut _{{#toLower}}{{name}}{{/toLower}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} = {{#toWasmInit}}{{toGraphQLType}}{{/toWasmInit}}; - {{/required}} - {{^required}} - let mut _{{#toLower}}{{name}}{{/toLower}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} = None; - {{/required}} - {{/object}} - {{#required}} - let mut _{{#toLower}}{{name}}{{/toLower}}_set = false; - {{/required}} - {{/properties}} - - while num_of_fields > 0 { - num_of_fields -= 1; - let field = reader.read_string()?; - - match field.as_str() { - {{#properties}} - "{{name}}" => { - reader.context().push(&field, "{{#toWasm}}{{toGraphQLType}}{{/toWasm}}", "type found, reading property"); - {{#scalar}} - _{{#toLower}}{{name}}{{/toLower}} = reader.read_{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}()?; - {{/scalar}} - {{#array}} - _{{#toLower}}{{name}}{{/toLower}} = reader.read_{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}(|reader| { - {{> deserialize_array_nobox}} - })?; - {{/array}} - {{#map}} - _{{#toLower}}{{name}}{{/toLower}} = reader.read_{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}(|reader| { - reader.read_{{#key}}{{#toLower}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/toLower}}{{/key}}()? - }, |reader| { - {{> deserialize_map_value_nobox}} - })?; - {{/map}} - {{#enum}} - {{> deserialize_enum}} - _{{#toLower}}{{name}}{{/toLower}} = value; - {{/enum}} - {{#object}} - {{> deserialize_object_nobox}} - _{{#toLower}}{{name}}{{/toLower}} = object; - {{/object}} - {{#required}} - _{{#toLower}}{{name}}{{/toLower}}_set = true; - {{/required}} - reader.context().pop(); - } - {{/properties}} - err => return Err(DecodeError::UnknownFieldName(err.to_string())), - } - } - {{#properties}} - {{#required}} - if !_{{#toLower}}{{name}}{{/toLower}}_set { - return Err(DecodeError::MissingField("{{name}}: {{type}}.".to_string())); - } - {{/required}} - {{/properties}} - - Ok({{#detectKeyword}}{{#toUpper}}{{type}}{{/toUpper}}{{/detectKeyword}} { - {{#properties}} - {{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}: _{{#toLower}}{{name}}{{/toLower}}, - {{/properties}} - }) -} diff --git a/packages/schema/bind/src/bindings/rust/wasm/templates/imported/mod-rs.mustache b/packages/schema/bind/src/bindings/rust/wasm/templates/imported/mod-rs.mustache index 1ba73c7a99..610b24fab7 100644 --- a/packages/schema/bind/src/bindings/rust/wasm/templates/imported/mod-rs.mustache +++ b/packages/schema/bind/src/bindings/rust/wasm/templates/imported/mod-rs.mustache @@ -9,8 +9,4 @@ pub use {{#toLower}}{{type}}{{/toLower}}::*; {{#importedModuleTypes}} pub mod {{#toLower}}{{type}}{{/toLower}}; pub use {{#toLower}}{{type}}{{/toLower}}::*; -{{/importedModuleTypes}} -{{#importedEnvTypes}} -pub mod {{#toLower}}{{type}}{{/toLower}}; -pub use {{#toLower}}{{type}}{{/toLower}}::*; -{{/importedEnvTypes}} \ No newline at end of file +{{/importedModuleTypes}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/rust/wasm/templates/module-type/wrapped-rs.mustache b/packages/schema/bind/src/bindings/rust/wasm/templates/module-type/wrapped-rs.mustache index 0152116543..1c8728baaf 100644 --- a/packages/schema/bind/src/bindings/rust/wasm/templates/module-type/wrapped-rs.mustache +++ b/packages/schema/bind/src/bindings/rust/wasm/templates/module-type/wrapped-rs.mustache @@ -1,8 +1,5 @@ {{#moduleType}} {{#methods.length}} -use polywrap_wasm_rs::{ - wrap_load_env -}; use crate::{ {{#methods}} @@ -15,33 +12,9 @@ use crate::{ {{/methods.length}} {{/moduleType}} -{{#envType}} -use crate::Env; -{{/envType}} - {{#moduleType}} {{#methods}} -pub fn {{#toLower}}{{name}}{{/toLower}}_wrapped(args: &[u8], env_size: u32) -> Vec { - {{#env}} - {{#required}} - if env_size == 0 { - panic!("Environment is not set, and it is required by method 'objectMethod'"); - } - - let env_buf = wrap_load_env(env_size); - let env = Env::from_buffer(&env_buf).unwrap(); - - {{/required}} - {{^required}} - let mut env: Option = None; - - if env_size > 0 { - let env_buf = wrap_load_env(env_size); - env = Some(Env::from_buffer(&env_buf).unwrap()); - } - - {{/required}} - {{/env}} +pub fn {{#toLower}}{{name}}{{/toLower}}_wrapped(args: &[u8]) -> Vec { {{#arguments.length}} match deserialize_{{#toLower}}{{name}}{{/toLower}}_args(args) { Ok(args) => { @@ -50,7 +23,7 @@ pub fn {{#toLower}}{{name}}{{/toLower}}_wrapped(args: &[u8], env_size: u32) -> V {{#arguments}} {{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}: args.{{#detectKeyword}}{{#toLower}}{{name}}{{/toLower}}{{/detectKeyword}}, {{/arguments}} - }{{#env}}, env{{/env}}); + }); serialize_{{#toLower}}{{name}}{{/toLower}}_result({{#return}}&{{/return}}result).unwrap() {{#arguments.length}} } diff --git a/packages/schema/bind/src/bindings/rust/wasm/transforms/propertyDeps.ts b/packages/schema/bind/src/bindings/rust/wasm/transforms/propertyDeps.ts index 793ef1f641..3cfbdd125f 100644 --- a/packages/schema/bind/src/bindings/rust/wasm/transforms/propertyDeps.ts +++ b/packages/schema/bind/src/bindings/rust/wasm/transforms/propertyDeps.ts @@ -7,7 +7,6 @@ import { ObjectDefinition, AnyDefinition, ModuleDefinition, - EnvDefinition, } from "@polywrap/wrap-manifest-types-js"; import { AbiTransforms } from "@polywrap/schema-parse"; @@ -18,7 +17,6 @@ interface PropertyDep { } interface PropertyDepsState { - envDefinition?: EnvDefinition; objectDefinition?: ObjectDefinition; moduleDefinition?: ModuleDefinition; importedModuleDefinition?: ImportedModuleDefinition; @@ -30,11 +28,6 @@ export function propertyDeps(): AbiTransforms { return { enter: { - EnvDefinition: (def: EnvDefinition) => { - state.envDefinition = def; - state.propertyDeps = []; - return def; - }, ObjectDefinition: (def: ObjectDefinition) => { state.objectDefinition = def; state.propertyDeps = []; @@ -104,12 +97,7 @@ export function propertyDeps(): AbiTransforms { return array; }; - if (state.envDefinition && state.propertyDeps) { - state.propertyDeps = appendPropertyDep( - state.envDefinition.type, - state.propertyDeps - ); - } else if (state.objectDefinition && state.propertyDeps) { + if (state.objectDefinition && state.propertyDeps) { state.propertyDeps = appendPropertyDep( state.objectDefinition.type, state.propertyDeps @@ -130,15 +118,6 @@ export function propertyDeps(): AbiTransforms { }, }, leave: { - EnvDefinition: (def: EnvDefinition) => { - const propertyDeps = state.propertyDeps; - state.propertyDeps = undefined; - state.envDefinition = undefined; - return { - ...def, - propertyDeps, - }; - }, ObjectDefinition: (def: ObjectDefinition) => { const propertyDeps = state.propertyDeps; state.propertyDeps = undefined; diff --git a/packages/schema/bind/src/bindings/typescript/plugin/templates/module-ts.mustache b/packages/schema/bind/src/bindings/typescript/plugin/templates/module-ts.mustache index dac08c0e51..c5fa5c331d 100644 --- a/packages/schema/bind/src/bindings/typescript/plugin/templates/module-ts.mustache +++ b/packages/schema/bind/src/bindings/typescript/plugin/templates/module-ts.mustache @@ -16,7 +16,7 @@ export interface Args_{{name}} { {{/methods}} {{/moduleType}} -export abstract class Module extends PluginModule { +export abstract class Module extends PluginModule { {{#moduleType}} {{#methods}} abstract {{name}}( diff --git a/packages/schema/bind/src/bindings/typescript/plugin/templates/types-ts.mustache b/packages/schema/bind/src/bindings/typescript/plugin/templates/types-ts.mustache index e7eb717357..086eb81def 100644 --- a/packages/schema/bind/src/bindings/typescript/plugin/templates/types-ts.mustache +++ b/packages/schema/bind/src/bindings/typescript/plugin/templates/types-ts.mustache @@ -26,16 +26,6 @@ export type Json = string; export type String = string; export type Boolean = boolean; -/// Env START /// -{{#envType}} -export interface {{#detectKeyword}}{{type}}{{/detectKeyword}} extends Record { - {{#properties}} - {{name}}{{^required}}?{{/required}}: {{#toTypescript}}{{toGraphQLType}}{{/toTypescript}}; - {{/properties}} -} -{{/envType}} -/// Env END /// - /// Objects START /// {{#objectTypes}} export interface {{#detectKeyword}}{{type}}{{/detectKeyword}} { From 772badea702754e275ad72dbeee5930883dcc127 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Wed, 8 Feb 2023 16:51:41 +0100 Subject: [PATCH 18/90] (chore): removed interfaces and capabilities from parse --- packages/schema/parse/src/header.ts | 10 --- .../parse/src/transform/interfaceUris.ts | 66 ------------------- .../parse/src/transform/moduleCapabilities.ts | 56 ---------------- .../schema/parse/src/validate/directives.ts | 3 - 4 files changed, 135 deletions(-) delete mode 100644 packages/schema/parse/src/transform/interfaceUris.ts delete mode 100644 packages/schema/parse/src/transform/moduleCapabilities.ts diff --git a/packages/schema/parse/src/header.ts b/packages/schema/parse/src/header.ts index 55b996c26d..3f3d7a9413 100644 --- a/packages/schema/parse/src/header.ts +++ b/packages/schema/parse/src/header.ts @@ -23,17 +23,7 @@ directive @imports( types: [String!]! ) on OBJECT -directive @capability( - type: String! - uri: String! - namespace: String! -) repeatable on OBJECT - -directive @enabled_interface on OBJECT - directive @annotate(type: String!) on FIELD -directive @env(required: Boolean!) on FIELD_DEFINITION - ### Polywrap Header END ### `; diff --git a/packages/schema/parse/src/transform/interfaceUris.ts b/packages/schema/parse/src/transform/interfaceUris.ts deleted file mode 100644 index b0dcb1b3b4..0000000000 --- a/packages/schema/parse/src/transform/interfaceUris.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { AbiTransforms } from "."; - -import { - ModuleDefinition, - ObjectDefinition, - WrapAbi, -} from "@polywrap/wrap-manifest-types-js"; - -export function interfaceUris(): AbiTransforms { - const uniqueInterfaceUris: Record = {}; - const uniqueModuleInterfaceTypes: Record = {}; - const uniqueObjectInterfaceTypes: Record = {}; - - return { - enter: { - ModuleDefinition: (def: ModuleDefinition) => { - if (def.interfaces) { - for (const interfaceDef of def.interfaces) { - uniqueModuleInterfaceTypes[interfaceDef.type] = true; - } - } - return def; - }, - ObjectDefinition: (def: ObjectDefinition) => { - if (def.interfaces) { - for (const interfaceDef of def.interfaces) { - uniqueObjectInterfaceTypes[interfaceDef.type] = true; - } - } - return def; - }, - }, - leave: { - Abi: (abi: WrapAbi) => { - for (const interfaceType of Object.keys(uniqueModuleInterfaceTypes)) { - const importedInterface = - abi.importedModuleTypes && - abi.importedModuleTypes.find( - (importedModule) => importedModule.type === interfaceType - ); - - if (importedInterface) { - uniqueInterfaceUris[importedInterface.uri] = true; - } - } - - for (const interfaceType of Object.keys(uniqueObjectInterfaceTypes)) { - const importedInterface = - abi.importedObjectTypes && - abi.importedObjectTypes.find( - (importedObject) => importedObject.type === interfaceType - ); - - if (importedInterface) { - uniqueInterfaceUris[importedInterface.uri] = true; - } - } - - return { - ...abi, - interfaceUris: Object.keys(uniqueInterfaceUris), - }; - }, - }, - }; -} diff --git a/packages/schema/parse/src/transform/moduleCapabilities.ts b/packages/schema/parse/src/transform/moduleCapabilities.ts deleted file mode 100644 index 267005e5d6..0000000000 --- a/packages/schema/parse/src/transform/moduleCapabilities.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { AbiTransforms } from "."; - -import { - CapabilityDefinition, - InterfaceDefinition, - WrapAbi, -} from "@polywrap/wrap-manifest-types-js"; - -export interface ModuleCapability { - type: string; - uri: string; - namespace: string; -} - -export function moduleCapabilities(): AbiTransforms { - const capabilities: ModuleCapability[] = []; - - const enabledInterfaces: Set = new Set(); - - return { - enter: { - InterfaceDefinition: (def: InterfaceDefinition) => { - for (const type in def.capabilities) { - const info = def.capabilities[type as keyof CapabilityDefinition]; - if (info?.enabled) { - capabilities.push({ - uri: def.uri, - namespace: def.namespace, - type, - }); - enabledInterfaces.add(def.namespace); - } - } - return def; - }, - }, - leave: { - Abi: (info: WrapAbi) => { - if (info.moduleType) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (info.moduleType as any).capabilities = capabilities; - } - - if (info.importedModuleTypes) { - for (const importedModuleDef of info.importedModuleTypes) { - if (enabledInterfaces.has(importedModuleDef.namespace)) { - importedModuleDef.isInterface = true; - } - } - } - - return info; - }, - }, - }; -} diff --git a/packages/schema/parse/src/validate/directives.ts b/packages/schema/parse/src/validate/directives.ts index 8d3abcb51a..7d1d9d556a 100644 --- a/packages/schema/parse/src/validate/directives.ts +++ b/packages/schema/parse/src/validate/directives.ts @@ -8,10 +8,7 @@ export const getSupportedDirectivesValidator = (): SchemaValidator => { const supportedDirectives = [ "imported", "imports", - "capability", - "enabled_interface", "annotate", - "env", ]; const unsupportedUsages: string[] = []; From f13f3c4e01e2adfcc4bd3ac445a370447b25aa3b Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Wed, 8 Feb 2023 17:10:36 +0100 Subject: [PATCH 19/90] (chore): removed env transforms --- packages/schema/parse/src/transform/index.ts | 21 +------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/packages/schema/parse/src/transform/index.ts b/packages/schema/parse/src/transform/index.ts index e956651a67..dddcf7e5ac 100644 --- a/packages/schema/parse/src/transform/index.ts +++ b/packages/schema/parse/src/transform/index.ts @@ -1,4 +1,4 @@ -import { Abi, AnyType, ArgumentDef, ArrayType, Def, EnumDef, EnvDef, FunctionDef, MapKeyTypeName, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType, Type } from "../definitions"; +import { Abi, AnyType, ArgumentDef, ArrayType, Def, EnumDef, FunctionDef, MapKeyTypeName, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType, Type } from "../definitions"; export interface AbiTransforms { enter?: AbiTransformer; @@ -9,7 +9,6 @@ type TypeToTransform = | FunctionDef | ObjectDef | EnumDef - | EnvDef | ArgumentDef | ResultDef | PropertyDef @@ -22,7 +21,6 @@ export interface AbiTransformer { FunctionDefinition?: (def: FunctionDef) => FunctionDef ObjectDefinition?: (def: ObjectDef) => ObjectDef EnumDefinition?: (def: EnumDef) => EnumDef - EnvDefinition?: (def: EnvDef) => EnvDef ArgumentDefinition?: (def: ArgumentDef) => ArgumentDef ResultDefinition?: (def: ResultDef) => ResultDef @@ -47,7 +45,6 @@ export const transformAbi = (abi: Abi, transforms: AbiTransforms): Abi => { result.enums = result.enums?.map(def => visitEnumDefinition(def, transforms)) result.objects = result.objects?.map(def => visitObjectDefinition(def, transforms)) result.functions = result.functions?.map(def => visitFunctionDefinition(def, transforms)) - result.env = result.env ? visitEnvDefinition(result.env, transforms) : undefined if (transforms.leave && transforms.leave.Abi) { result = transforms.leave.Abi(result); @@ -66,7 +63,6 @@ const transformType = (type: T, transform?: AbiTransf FunctionDefinition, ObjectDefinition, EnumDefinition, - EnvDefinition, ArgumentDefinition, ResultDefinition, PropertyDefinition, @@ -93,10 +89,6 @@ const transformType = (type: T, transform?: AbiTransf result = Object.assign(result, EnumDefinition(result)) } - if (EnvDefinition && result.kind === "Env") { - result = Object.assign(result, EnvDefinition(result)) - } - if (ArgumentDefinition && result.kind === "Argument") { result = Object.assign(result, ArgumentDefinition(result)) } @@ -187,17 +179,6 @@ const visitObjectDefinition = (def: ObjectDef, transforms: AbiTransforms) => { return transformType(result, transforms.leave) } -const visitEnvDefinition = (def: EnvDef, transforms: AbiTransforms) => { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - - result.props.forEach((prop, i) => { - result.props[i] = visitPropertyDefinition(prop, transforms) - }) - - return transformType(result, transforms.leave) -} - const visitEnumDefinition = (def: EnumDef, transforms: AbiTransforms) => { let result = Object.assign({}, def); result = transformType(result, transforms.enter); From 936fb2bc7d5badc4caa5ce80a87ba7f9531d81d3 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Wed, 8 Feb 2023 22:59:57 +0100 Subject: [PATCH 20/90] (chore): updated extractors and transformes to new definitions --- .../schema/parse/src/__tests__/utils.spec.ts | 32 +++++++++++ packages/schema/parse/src/abi/index.ts | 2 - packages/schema/parse/src/definitions.ts | 4 ++ .../src/extract/{module.ts => functions.ts} | 2 +- packages/schema/parse/src/extract/index.ts | 2 +- packages/schema/parse/src/extract/utils.ts | 18 ++++-- packages/schema/parse/src/index.ts | 15 ++--- packages/schema/parse/src/transform/index.ts | 56 ++++++++++--------- .../parse/src/transform/toGraphQLType.ts | 9 ++- 9 files changed, 89 insertions(+), 51 deletions(-) create mode 100644 packages/schema/parse/src/__tests__/utils.spec.ts rename packages/schema/parse/src/extract/{module.ts => functions.ts} (96%) diff --git a/packages/schema/parse/src/__tests__/utils.spec.ts b/packages/schema/parse/src/__tests__/utils.spec.ts new file mode 100644 index 0000000000..d45e4a5b44 --- /dev/null +++ b/packages/schema/parse/src/__tests__/utils.spec.ts @@ -0,0 +1,32 @@ +import { MapType } from "../definitions" +import { parseMapString } from "../extract/utils" + +describe("Polywrap Schema Abi Utils", () => { + it("parseMapString", () => { + const mapString = "Map" + const expected: MapType = { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String" + }, + value: { + required: true, + type: { + kind: "Array", + item: { + required: false, + type: { + kind: "Scalar", + scalar: "String" + } + } + } + } + } + + const result = parseMapString(mapString, new Map()) + + expect(result).toMatchObject(expected) + }) +}) \ No newline at end of file diff --git a/packages/schema/parse/src/abi/index.ts b/packages/schema/parse/src/abi/index.ts index e8003ff028..471cb0cc17 100644 --- a/packages/schema/parse/src/abi/index.ts +++ b/packages/schema/parse/src/abi/index.ts @@ -1,8 +1,6 @@ import { Abi } from "../definitions"; export * from "@polywrap/wrap-manifest-types-js"; -export * from "./definitions"; -export * from "./env"; export * from "./utils"; export function createAbi(): Abi { diff --git a/packages/schema/parse/src/definitions.ts b/packages/schema/parse/src/definitions.ts index a8570cd73a..76d3ded194 100644 --- a/packages/schema/parse/src/definitions.ts +++ b/packages/schema/parse/src/definitions.ts @@ -25,6 +25,8 @@ export interface ImportedAbi extends AbiDefs { /// Definitions (user-defined) +export type AnyDef = FunctionDef | ObjectDef | EnumDef | PropertyDef | ArgumentDef | ResultDef; + export type UniqueDefKind = | "Function" | "Object" @@ -169,3 +171,5 @@ export const mapKeyTypeSet = { export type MapKeyTypeSet = typeof mapKeyTypeSet; export type MapKeyTypeName = keyof MapKeyTypeSet; + +export type AnyTypeOrDef = AnyType | AnyDef; \ No newline at end of file diff --git a/packages/schema/parse/src/extract/module.ts b/packages/schema/parse/src/extract/functions.ts similarity index 96% rename from packages/schema/parse/src/extract/module.ts rename to packages/schema/parse/src/extract/functions.ts index d7e45eec08..df485b3734 100644 --- a/packages/schema/parse/src/extract/module.ts +++ b/packages/schema/parse/src/extract/functions.ts @@ -5,7 +5,7 @@ import { parseAnnotateDirective, parseDirectivesInField } from "./directives"; import { VisitorBuilder } from "./types"; import { extractType } from "./utils"; -export class ModuleVisitorBuilder implements VisitorBuilder { +export class FunctionsVisitorBuilder implements VisitorBuilder { constructor(protected readonly uniqueDefs: Map) { } private extractMethodFromFieldDefNode(node: FieldDefinitionNode): FunctionDef { diff --git a/packages/schema/parse/src/extract/index.ts b/packages/schema/parse/src/extract/index.ts index 15bf3a2039..f33f7ecb6a 100644 --- a/packages/schema/parse/src/extract/index.ts +++ b/packages/schema/parse/src/extract/index.ts @@ -1,3 +1,3 @@ export * from "./object"; export * from "./enum"; -export * from "./module"; \ No newline at end of file +export * from "./functions"; \ No newline at end of file diff --git a/packages/schema/parse/src/extract/utils.ts b/packages/schema/parse/src/extract/utils.ts index fad8c9eb46..d070432655 100644 --- a/packages/schema/parse/src/extract/utils.ts +++ b/packages/schema/parse/src/extract/utils.ts @@ -8,8 +8,10 @@ export const extractType = (node: TypeNode, uniqueDefs: Map extractorBuilder(info, uniqueDefs)) ?? defaultExtractors.map(e => e.build(info)); - extract(astNode, info, extracts); + extract(astNode, extracts); if (options && options.transforms) { for (const transform of options.transforms) { @@ -102,10 +102,7 @@ const validate = ( const extract = ( astNode: DocumentNode, - abi: Abi, - extractors: SchemaExtractorBuilder[] + extractors: ASTVisitor[] ) => { - const allVisitors = extractors.map((getVisitor) => getVisitor(abi)); - - visit(astNode, visitInParallel(allVisitors)); + visit(astNode, visitInParallel(extractors)); }; diff --git a/packages/schema/parse/src/transform/index.ts b/packages/schema/parse/src/transform/index.ts index dddcf7e5ac..8162476c71 100644 --- a/packages/schema/parse/src/transform/index.ts +++ b/packages/schema/parse/src/transform/index.ts @@ -1,22 +1,10 @@ -import { Abi, AnyType, ArgumentDef, ArrayType, Def, EnumDef, FunctionDef, MapKeyTypeName, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType, Type } from "../definitions"; +import { Abi, AnyType, AnyTypeOrDef, ArgumentDef, ArrayType, Def, EnumDef, FunctionDef, ImportRefType, MapKeyTypeName, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType, Type } from "../definitions"; export interface AbiTransforms { enter?: AbiTransformer; leave?: AbiTransformer; } -type TypeToTransform = - | FunctionDef - | ObjectDef - | EnumDef - | ArgumentDef - | ResultDef - | PropertyDef - | ScalarType - | ArrayType - | MapType - | RefType - export interface AbiTransformer { FunctionDefinition?: (def: FunctionDef) => FunctionDef ObjectDefinition?: (def: ObjectDef) => ObjectDef @@ -30,6 +18,7 @@ export interface AbiTransformer { ArrayType?: (type: ArrayType) => ArrayType MapType?: (type: MapType) => MapType RefType?: (type: RefType) => RefType + ImportRefType?: (type: ImportRefType) => ImportRefType DefinitionOrType?: (def: T) => T Abi?: (abi: Abi) => Abi @@ -53,7 +42,7 @@ export const transformAbi = (abi: Abi, transforms: AbiTransforms): Abi => { return result; } -const transformType = (type: T, transform?: AbiTransformer): T => { +const transformType = (type: T, transform?: AbiTransformer): T => { if (!transform) { return type; } @@ -70,7 +59,8 @@ const transformType = (type: T, transform?: AbiTransf ArrayType, MapType, RefType, - DefinitionOrType + DefinitionOrType, + ImportRefType, } = transform; if (DefinitionOrType) { @@ -78,43 +68,47 @@ const transformType = (type: T, transform?: AbiTransf } if (FunctionDefinition && result.kind === "Function") { - result = Object.assign(result, FunctionDefinition(result)) + result = Object.assign(result, FunctionDefinition(result as FunctionDef)) } if (ObjectDefinition && result.kind === "Object") { - result = Object.assign(result, ObjectDefinition(result)) + result = Object.assign(result, ObjectDefinition(result as ObjectDef)) } if (EnumDefinition && result.kind === "Enum") { - result = Object.assign(result, EnumDefinition(result)) + result = Object.assign(result, EnumDefinition(result as EnumDef)) } if (ArgumentDefinition && result.kind === "Argument") { - result = Object.assign(result, ArgumentDefinition(result)) + result = Object.assign(result, ArgumentDefinition(result as ArgumentDef)) } if (ResultDefinition && result.kind === "Result") { - result = Object.assign(result, ResultDefinition(result)) + result = Object.assign(result, ResultDefinition(result as ResultDef)) } if (PropertyDefinition && result.kind === "Property") { - result = Object.assign(result, PropertyDefinition(result)) + result = Object.assign(result, PropertyDefinition(result as PropertyDef)) } if (ScalarType && result.kind === "Scalar") { - result = Object.assign(result, ScalarType(result)) + result = Object.assign(result, ScalarType(result as ScalarType)) } if (ArrayType && result.kind === "Array") { - result = Object.assign(result, ArrayType(result)) + result = Object.assign(result, ArrayType(result as ArrayType)) } if (MapType && result.kind === "Map") { - result = Object.assign(result, MapType(result)) + result = Object.assign(result, MapType(result as MapType)) } if (RefType && result.kind === "Ref") { - result = Object.assign(result, RefType(result)) + result = Object.assign(result, RefType(result as RefType)) + } + + if (ImportRefType && result.kind === "ImportRef") { + result = Object.assign(result, ImportRefType(result as ImportRefType)) } return result; @@ -127,6 +121,13 @@ const visitRefType = (type: RefType, transforms: AbiTransforms) => { return transformType(result, transforms.leave) } +const visitImportRefType = (type: ImportRefType, transforms: AbiTransforms) => { + let result = Object.assign({}, type); + result = transformType(result, transforms.enter) + + return transformType(result, transforms.leave) +} + const visitScalarType = (type: ScalarType, transforms: AbiTransforms) => { let result = Object.assign({}, type); result = transformType(result, transforms.enter) @@ -138,7 +139,7 @@ const visitArrayType = (type: ArrayType, transforms: AbiTransforms) => { let result = Object.assign({}, type); result = transformType(result, transforms.enter) - result.item = visitAnyType(result.item, transforms) + result.item.type = visitAnyType(result.item.type, transforms) return transformType(result, transforms.leave); } @@ -147,7 +148,7 @@ const visitMapType = (type: MapType, transforms: AbiTransforms) => { result = transformType(result, transforms.enter) result.key = visitScalarType(result.key, transforms) as ScalarType - result.value = visitAnyType(result.value, transforms) + result.value.type = visitAnyType(result.value.type, transforms) return transformType(result, transforms.leave); } @@ -157,6 +158,7 @@ const visitAnyType = (type: AnyType, transforms: AbiTransforms) => { case "Array": return visitArrayType(type, transforms); case "Scalar": return visitScalarType(type, transforms); case "Map": return visitMapType(type, transforms); + case "ImportRef": return visitImportRefType(type, transforms); } } diff --git a/packages/schema/parse/src/transform/toGraphQLType.ts b/packages/schema/parse/src/transform/toGraphQLType.ts index 9dc2d67020..7227606526 100644 --- a/packages/schema/parse/src/transform/toGraphQLType.ts +++ b/packages/schema/parse/src/transform/toGraphQLType.ts @@ -1,13 +1,12 @@ -import { ArgumentDef, ArrayType, EnumDef, EnvDef, FunctionDef, MapType, ObjectDef, PropertyDef, RefType, ScalarType } from "../definitions"; +import { AnyType, ArgumentDef, EnumDef, FunctionDef, ObjectDef, PropertyDef } from "../definitions"; function applyRequired(type: string, required: boolean | undefined): string { return `${type}${required ? "!" : ""}`; } -export function toGraphQL(def: ObjectDef | FunctionDef | ArgumentDef | ScalarType | EnvDef | EnumDef | PropertyDef | ArrayType | MapType | RefType, required: boolean): string { +export function toGraphQL(def: ObjectDef | FunctionDef | ArgumentDef | EnumDef | PropertyDef | AnyType, required: boolean): string { switch (def.kind) { case "Object": - case "Env": return applyRequired(def.name, required); case "Scalar": return applyRequired(def.scalar, required); @@ -17,11 +16,11 @@ export function toGraphQL(def: ObjectDef | FunctionDef | ArgumentDef | ScalarTyp return toGraphQL(def.type, def.required); case "Array": return applyRequired( - `[${toGraphQL(def.item, def.required)}]`, + `[${toGraphQL(def.item.type, def.item.required)}]`, required ); case "Map": { - return `Map<${def.key}!, ${toGraphQL(def.value, def.required)}>` + return `Map<${def.key}!, ${toGraphQL(def.value.type, def.value.required)}>` } case "Function": { From ebb21e9af28f6ef2499f163b5c8f4f276ad1c09c Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Thu, 9 Feb 2023 00:14:32 +0100 Subject: [PATCH 21/90] (chore): re-did GraphQL string parsers --- packages/schema/parse/src/extract/utils.ts | 152 +++++++++++++-------- 1 file changed, 96 insertions(+), 56 deletions(-) diff --git a/packages/schema/parse/src/extract/utils.ts b/packages/schema/parse/src/extract/utils.ts index d070432655..60b88f72a1 100644 --- a/packages/schema/parse/src/extract/utils.ts +++ b/packages/schema/parse/src/extract/utils.ts @@ -1,5 +1,6 @@ -import { isScalarType, TypeNode } from "graphql"; -import { UniqueDefKind, RefType, AnyType, MapKeyTypeName, mapKeyTypeSet, MapType, ScalarTypeName } from "../definitions"; +import { TypeNode } from "graphql"; +import { isScalarType } from "../abi/utils"; +import { UniqueDefKind, RefType, AnyType, MapKeyTypeName, mapKeyTypeSet, MapType, ScalarTypeName, ArrayType, ScalarType } from "../definitions"; export const extractType = (node: TypeNode, uniqueDefs: Map): AnyType => { switch (node.kind) { @@ -41,82 +42,121 @@ export const parseRef = (refName: string, uniqueDefs: Map // TODO: Make sure map also works for imported types and modules -export const parseMapString = (mapString: string, uniqueDefs: Map): MapType => { - const extractType = (mapString: string): AnyType => { - if (isArray(mapString)) { - const closeSquareBracketIdx = mapString.lastIndexOf("]"); - const required = mapString[closeSquareBracketIdx + 1] === "!" - return { - kind: "Array", - item: { - type: extractType(mapString.substring(1, closeSquareBracketIdx)), - required - } - }; - } else if (isMap(mapString)) { - return parseMapString(mapString, uniqueDefs) - } else if (isScalarType(mapString)) { - return { - kind: "Scalar", - scalar: mapString as ScalarTypeName, - } - } else { - return parseRef(mapString, uniqueDefs) - } - // TODO: is this case necessary? - // else { - // throw new Error(`Unrecognized reference type '${mapString}'`) - // } +const extractRequired = (typeString: string): { required: boolean, innerString: string } => { + const required = typeString.endsWith("!"); + + return { + required: required, + innerString: required ? typeString.slice(0, -1) : typeString + } +} + +const parseScalarString = (scalarString: string): ScalarType => { + return { + kind: "Scalar", + scalar: scalarString as ScalarTypeName, } +} - const openAngleBracketIdx = mapString.indexOf("<"); - const closeAngleBracketIdx = mapString.lastIndexOf(">"); +const parseRefString = (refString: string, uniqueDefs: Map): RefType => { + const ref_kind = uniqueDefs.get(refString); - if ( - closeAngleBracketIdx === -1 - ) { - throw new Error(`Invalid map value type: ${mapString}`); + if (!ref_kind) { + throw new Error(`Found ref to unknown definition '${refString}'`) + } + + return { + kind: "Ref", + ref_kind, + ref_name: refString + } +} + +const parseArrayString = (arrayString: string, uniqueDefs: Map): ArrayType => { + if (!arrayString.startsWith("[") || !arrayString.endsWith("]")) { + throw new Error(`Invalid array type: ${arrayString}`); } - const subtype = mapString.substring(openAngleBracketIdx + 1, closeAngleBracketIdx); + const { required: isInnerTypeRequired, innerString: innerTypeString } = extractRequired(arrayString.slice(1, -1)); - const firstDelimiter = subtype.indexOf(","); + let innerType: AnyType; - const _keyType = subtype.substring(0, firstDelimiter).trim(); - const _valType = subtype.substring(firstDelimiter + 1).trim(); + if (isArray(innerTypeString)) { + innerType = parseArrayString(innerTypeString, uniqueDefs) + } else if (isMap(innerTypeString)) { + innerType = parseMapString(innerTypeString, uniqueDefs) + } else if (isScalarType(innerTypeString)) { + innerType = parseScalarString(innerTypeString) + } else { + innerType = parseRefString(innerTypeString, uniqueDefs) + } + + const item = { + required: isInnerTypeRequired, + type: innerType + } + + return { + kind: "Array", + item + } +} + +export const parseMapString = (mapString: string, uniqueDefs: Map): MapType => { + if (!mapString.startsWith("Map<") || !mapString.endsWith(">")) { + throw new Error(`Invalid map value type: ${mapString}`); + } - if (!_keyType || !_valType) { + const innerMapString = mapString.slice(4, -1); + const mapStringSplit = innerMapString.split(","); + + if (mapStringSplit.length !== 2) { throw new Error(`Invalid map value type: ${mapString}`); } - // TODO: Is there a better way to enforce this -> Map key should always be required - // TODO: Should we throw an error if it's not? - // const keyRequired = true; - const keyType = _keyType.endsWith("!") ? _keyType.slice(0, -1) : _keyType; - const valType = _valType.endsWith("!") ? _valType.slice(0, -1) : _valType; + const [keyTypeString, valTypeString] = mapStringSplit; + + // If key contains !, remove it. It will always be required anyways + const innerKeyString = keyTypeString.endsWith("!") ? keyTypeString.slice(0, -1) : keyTypeString; - if (!isMapKey(keyType)) { + if (!isMapKey(innerKeyString)) { throw new Error( - `Found invalid map key type: ${keyType} while parsing ${mapString}` + `Found invalid map key type: ${innerKeyString} while parsing ${mapString}` ); } + const key = { + kind: "Scalar" as const, + scalar: innerKeyString as MapKeyTypeName + } + + const { required: isValueRequired, innerString: innerValueString } = extractRequired(valTypeString); + + let valueType: AnyType; + + if (isArray(innerValueString)) { + valueType = parseArrayString(innerValueString, uniqueDefs) + } else if (isMap(innerValueString)) { + valueType = parseMapString(innerValueString, uniqueDefs) + } else if (isScalarType(innerValueString)) { + valueType = parseScalarString(innerValueString) + } else { + valueType = parseRef(innerValueString, uniqueDefs) + } + + const value = { + type: valueType, + required: isValueRequired, + } + return { kind: "Map", - key: { - kind: "Scalar", - scalar: keyType as MapKeyTypeName - }, - value: { - type: extractType(valType), - required: valType.endsWith("!"), - } + key, + value } - } const isMap = (typeName: string): boolean => { - //TODO: would this be the right condition? return typeName.startsWith("Map<") } From 4518fd3812088854eeb8deac2286c6cc9b44d523 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Thu, 9 Feb 2023 02:08:21 +0100 Subject: [PATCH 22/90] (chore): trimming GraphQL strings --- packages/schema/parse/src/extract/utils.ts | 44 ++++++++++++---------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/packages/schema/parse/src/extract/utils.ts b/packages/schema/parse/src/extract/utils.ts index 60b88f72a1..008f67e7b7 100644 --- a/packages/schema/parse/src/extract/utils.ts +++ b/packages/schema/parse/src/extract/utils.ts @@ -72,23 +72,24 @@ const parseRefString = (refString: string, uniqueDefs: Map): ArrayType => { +export const parseArrayString = (arrayString: string, uniqueDefs: Map): ArrayType => { if (!arrayString.startsWith("[") || !arrayString.endsWith("]")) { throw new Error(`Invalid array type: ${arrayString}`); } const { required: isInnerTypeRequired, innerString: innerTypeString } = extractRequired(arrayString.slice(1, -1)); + const trimmedInnerTypeString = innerTypeString.trim(); let innerType: AnyType; - if (isArray(innerTypeString)) { - innerType = parseArrayString(innerTypeString, uniqueDefs) - } else if (isMap(innerTypeString)) { - innerType = parseMapString(innerTypeString, uniqueDefs) - } else if (isScalarType(innerTypeString)) { - innerType = parseScalarString(innerTypeString) + if (isArray(trimmedInnerTypeString)) { + innerType = parseArrayString(trimmedInnerTypeString, uniqueDefs) + } else if (isMap(trimmedInnerTypeString)) { + innerType = parseMapString(trimmedInnerTypeString, uniqueDefs) + } else if (isScalarType(trimmedInnerTypeString)) { + innerType = parseScalarString(trimmedInnerTypeString) } else { - innerType = parseRefString(innerTypeString, uniqueDefs) + innerType = parseRefString(trimmedInnerTypeString, uniqueDefs) } const item = { @@ -108,16 +109,18 @@ export const parseMapString = (mapString: string, uniqueDefs: Map Date: Thu, 9 Feb 2023 02:08:37 +0100 Subject: [PATCH 23/90] (feat): added extractors and utils unit tests --- .../parse/src/__tests__/extractor.spec.ts | 163 ++++++++++++ .../schema/parse/src/__tests__/utils.spec.ts | 232 ++++++++++++++++-- 2 files changed, 375 insertions(+), 20 deletions(-) create mode 100644 packages/schema/parse/src/__tests__/extractor.spec.ts diff --git a/packages/schema/parse/src/__tests__/extractor.spec.ts b/packages/schema/parse/src/__tests__/extractor.spec.ts new file mode 100644 index 0000000000..910eac66da --- /dev/null +++ b/packages/schema/parse/src/__tests__/extractor.spec.ts @@ -0,0 +1,163 @@ +import { parse, visit, visitInParallel } from "graphql" +import { Abi, EnumDef, FunctionDef, ObjectDef } from "../definitions" +import { EnumVisitorBuilder, FunctionsVisitorBuilder, ObjectVisitorBuilder } from "../extract" + +describe("Extractors", () => { + describe("Enums", () => { + it("Extracts enum defs", () => { + const schema = ` + enum Color { + RED + GREEN + BLUE + } + + enum Size { + SMALL + MEDIUM + } + ` + + const astNode = parse(schema); + const abi: Abi = { + version: "0.2" + } + const enumExtractor = new EnumVisitorBuilder().build(abi); + + const expected: EnumDef[] = [ + { + name: "Color", + kind: "Enum", + constants: ["RED", "GREEN", "BLUE"] + }, + { + name: "Size", + kind: "Enum", + constants: ["SMALL", "MEDIUM"] + } + ] + + visit(astNode, visitInParallel([enumExtractor])) + + expect(abi.enums).toMatchObject(expected) + }) + }) + describe("Objects", () => { + it("Extracts object defs", () => { + const schema = ` + type Custom { + prop1: String! + nested: Nested + } + + type Nested { + prop: UInt8 + } + ` + + const astNode = parse(schema); + const abi: Abi = { + version: "0.2" + } + const uniqueDefs = new Map([ + ["Custom", "Object" as const], + ["Nested", "Object" as const] + ]) + const objectExtractor = new ObjectVisitorBuilder(uniqueDefs).build(abi); + + const expected: ObjectDef[] = [ + { + kind: "Object", + name: "Custom", + props: [ + { + kind: "Property", + name: "prop1", + type: { + kind: "Scalar", + scalar: "String" + }, + required: true + }, + { + kind: "Property", + name: "nested", + type: { + kind: "Ref", + ref_name: "Nested", + ref_kind: "Object" + }, + required: false + } + ] + }, + { + kind: "Object", + name: "Nested", + props: [ + { + kind: "Property", + name: "prop", + type: { + kind: "Scalar", + scalar: "UInt8" + }, + required: false + }, + ] + } + ] + + visit(astNode, visitInParallel([objectExtractor])) + + expect(abi.objects).toMatchObject(expected) + }) + }) + describe("Functions", () => { + it("Extract functions defs", () => { + const schema = ` + type Module { + fooFunc( + arg: String! + ): String! + } + ` + + const astNode = parse(schema); + const abi: Abi = { + version: "0.2" + } + const functionsExtractor = new FunctionsVisitorBuilder(new Map()).build(abi); + + const expected: FunctionDef[] = [ + { + kind: "Function", + name: "fooFunc", + args: [ + { + kind: "Argument", + name: "arg", + type: { + kind: "Scalar", + scalar: "String" + }, + required: true, + } + ], + result: { + kind: "Result", + type: { + kind: "Scalar", + scalar: "String" + }, + required: true + } + } + ] + + visit(astNode, visitInParallel([functionsExtractor])) + + expect(abi.functions).toMatchObject(expected) + }) + }) +}) \ No newline at end of file diff --git a/packages/schema/parse/src/__tests__/utils.spec.ts b/packages/schema/parse/src/__tests__/utils.spec.ts index d45e4a5b44..e2ccd6eb0b 100644 --- a/packages/schema/parse/src/__tests__/utils.spec.ts +++ b/packages/schema/parse/src/__tests__/utils.spec.ts @@ -1,32 +1,224 @@ -import { MapType } from "../definitions" -import { parseMapString } from "../extract/utils" +import { ArrayType, MapType } from "../definitions" +import { parseArrayString, parseMapString } from "../extract/utils" describe("Polywrap Schema Abi Utils", () => { - it("parseMapString", () => { - const mapString = "Map" - const expected: MapType = { - kind: "Map", - key: { - kind: "Scalar", - scalar: "String" - }, - value: { - required: true, - type: { - kind: "Array", - item: { - required: false, - type: { + describe("parseArrayString", () => { + it("Parses a simple array", () => { + const arrayString = "[String!]" + const expected: ArrayType = { + kind: "Array", + item: { + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + } + } + + const result = parseArrayString(arrayString, new Map()) + expect(result).toMatchObject(expected) + }) + + it("Parses a ref value", () => { + const arrayString = "[Foo!]" + const expected: ArrayType = { + kind: "Array", + item: { + required: true, + type: { + kind: "Ref", + ref_kind: "Enum", + ref_name: "Foo" + } + } + } + + const result = parseArrayString(arrayString, new Map([["Foo", "Enum"]])) + expect(result).toMatchObject(expected) + }) + + it("Parses a multi-level nested array", () => { + const arrayString = "[[[Foo!]]!]" + const expected: ArrayType = { + kind: "Array", + item: { + required: true, + type: { + kind: "Array", + item: { + required: false, + type: { + kind: "Array", + item: { + required: true, + type: { + kind: "Ref", + ref_kind: "Enum", + ref_name: "Foo" + } + } + } + } + } + } + } + + const result = parseArrayString(arrayString, new Map([["Foo", "Enum"]])) + expect(result).toMatchObject(expected) + }) + }) + + describe("parseMapString", () => { + it("Without a explicitly required key", () => { + const mapString = "Map" + const expected: MapType = { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String" + }, + value: { + required: true, + type: { + kind: "Array", + item: { + required: false, + type: { + kind: "Scalar", + scalar: "String" + } + } + } + } + } + + const result = parseMapString(mapString, new Map()) + expect(result).toMatchObject(expected) + }) + + it("With a explicitly required key", () => { + const mapString = "Map" + const expected: MapType = { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String" + }, + value: { + required: true, + type: { + kind: "Array", + item: { + required: false, + type: { + kind: "Scalar", + scalar: "String" + } + } + } + } + } + + const result = parseMapString(mapString, new Map()) + expect(result).toMatchObject(expected) + }) + + it("With a explicitly required key", () => { + const mapString = "Map" + const expected: MapType = { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String" + }, + value: { + required: true, + type: { + kind: "Array", + item: { + required: false, + type: { + kind: "Scalar", + scalar: "String" + } + } + } + } + } + + const result = parseMapString(mapString, new Map()) + expect(result).toMatchObject(expected) + }) + + it("Trims inner values", () => { + const mapString = "Map< String! , [String]! >" + const expected: MapType = { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String" + }, + value: { + required: true, + type: { + kind: "Array", + item: { + required: false, + type: { + kind: "Scalar", + scalar: "String" + } + } + } + } + } + + const result = parseMapString(mapString, new Map()) + expect(result).toMatchObject(expected) + }) + + it("Parses a complex nested Map", () => { + const mapString = "Map!>" + const expected: MapType = { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String" + }, + value: { + required: true, + type: { + kind: "Map", + key: { kind: "Scalar", scalar: "String" + }, + value: { + required: true, + type: { + kind: "Array", + item: { + required: false, + type: { + kind: "Ref", + ref_kind: "Object", + ref_name: "Foo" + } + } + } } } } } - } - const result = parseMapString(mapString, new Map()) + const result = parseMapString(mapString, new Map([["Foo", "Object"]])) + expect(result).toMatchObject(expected) + }) - expect(result).toMatchObject(expected) + it("Throws if Map string is invalid", () => { + const mapString = "" + expect(() => parseMapString(mapString, new Map())).toThrow() + }) }) }) \ No newline at end of file From 8d926d25b01e45c5a3bd1b80509c44732d1e28be Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Thu, 9 Feb 2023 02:37:40 +0100 Subject: [PATCH 24/90] (chore): added multiple test cases for object defs --- .../parse/src/__tests__/extractor.spec.ts | 228 +++++++++++++++++- 1 file changed, 223 insertions(+), 5 deletions(-) diff --git a/packages/schema/parse/src/__tests__/extractor.spec.ts b/packages/schema/parse/src/__tests__/extractor.spec.ts index 910eac66da..cd74149d81 100644 --- a/packages/schema/parse/src/__tests__/extractor.spec.ts +++ b/packages/schema/parse/src/__tests__/extractor.spec.ts @@ -4,7 +4,7 @@ import { EnumVisitorBuilder, FunctionsVisitorBuilder, ObjectVisitorBuilder } fro describe("Extractors", () => { describe("Enums", () => { - it("Extracts enum defs", () => { + it("Extracts simple enum defs", () => { const schema = ` enum Color { RED @@ -35,7 +35,7 @@ describe("Extractors", () => { kind: "Enum", constants: ["SMALL", "MEDIUM"] } - ] + ] visit(astNode, visitInParallel([enumExtractor])) @@ -43,11 +43,49 @@ describe("Extractors", () => { }) }) describe("Objects", () => { - it("Extracts object defs", () => { + it("Extracts simple object defs", () => { + const schema = ` + type Nested { + prop: UInt8 + } + ` + + const astNode = parse(schema); + const abi: Abi = { + version: "0.2" + } + + const objectExtractor = new ObjectVisitorBuilder(new Map()).build(abi); + + const expected: ObjectDef[] = [ + { + kind: "Object", + name: "Nested", + props: [ + { + kind: "Property", + name: "prop", + type: { + kind: "Scalar", + scalar: "UInt8" + }, + required: false + }, + ] + } + ] + + visit(astNode, visitInParallel([objectExtractor])) + + expect(abi.objects).toMatchObject(expected) + }) + + it("Extracts objects with ref types", () => { const schema = ` type Custom { prop1: String! nested: Nested + bar: Bar } type Nested { @@ -61,7 +99,8 @@ describe("Extractors", () => { } const uniqueDefs = new Map([ ["Custom", "Object" as const], - ["Nested", "Object" as const] + ["Nested", "Object" as const], + ["Bar", "Enum" as const] ]) const objectExtractor = new ObjectVisitorBuilder(uniqueDefs).build(abi); @@ -88,6 +127,16 @@ describe("Extractors", () => { ref_kind: "Object" }, required: false + }, + { + kind: "Property", + name: "bar", + type: { + kind: "Ref", + ref_name: "Bar", + ref_kind: "Enum" + }, + required: false } ] }, @@ -112,9 +161,178 @@ describe("Extractors", () => { expect(abi.objects).toMatchObject(expected) }) + + it("Extracts objects with array types", () => { + const schema = ` + type Custom { + prop1: String! + nested: [Nested!]! + } + + type Nested { + prop: [UInt8] + } + ` + + const astNode = parse(schema); + const abi: Abi = { + version: "0.2" + } + const uniqueDefs = new Map([ + ["Custom", "Object" as const], + ["Nested", "Object" as const] + ]) + const objectExtractor = new ObjectVisitorBuilder(uniqueDefs).build(abi); + + const expected: ObjectDef[] = [ + { + kind: "Object", + name: "Custom", + props: [ + { + kind: "Property", + name: "prop1", + type: { + kind: "Scalar", + scalar: "String" + }, + required: true + }, + { + kind: "Property", + name: "nested", + type: { + kind: "Array", + item: { + required: true, + type: { + kind: "Ref", + ref_name: "Nested", + ref_kind: "Object" + } + } + }, + required: true + } + ] + }, + { + kind: "Object", + name: "Nested", + props: [ + { + kind: "Property", + name: "prop", + type: { + kind: "Array", + item: { + required: false, + type: { + kind: "Scalar", + scalar: "UInt8" + } + + } + }, + required: false + } + ] + } + ] + + visit(astNode, visitInParallel([objectExtractor])) + + expect(abi.objects).toMatchObject(expected) + }) + + it("Extracts objects with map types", () => { + const schema = ` + type Custom { + nested: Map! @annotate(type: "Map") + } + + type Nested { + prop: Map @annotate(type: "Map") + } + ` + + const astNode = parse(schema); + const abi: Abi = { + version: "0.2" + } + const uniqueDefs = new Map([ + ["Custom", "Object" as const], + ["Nested", "Object" as const] + ]) + const objectExtractor = new ObjectVisitorBuilder(uniqueDefs).build(abi); + + const expected: ObjectDef[] = [ + { + kind: "Object", + name: "Custom", + props: [ + { + kind: "Property", + name: "nested", + type: { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String" + }, + value: { + required: true, + type: { + kind: "Array", + item: { + required: true, + type: { + kind: "Ref", + ref_name: "Nested", + ref_kind: "Object" + } + } + } + } + }, + required: true + } + ] + }, + { + kind: "Object", + name: "Nested", + props: [ + { + kind: "Property", + name: "prop", + type: { + kind: "Map", + key: { + kind: "Scalar", + scalar: "UInt8" + }, + value: { + required: false, + type: { + kind: "Scalar", + scalar: "UInt8" + } + } + }, + required: false + } + ] + } + ] + + visit(astNode, visitInParallel([objectExtractor])) + + expect(abi.objects).toMatchObject(expected) + }) }) describe("Functions", () => { - it("Extract functions defs", () => { + it("Extract simple functions defs", () => { const schema = ` type Module { fooFunc( From ec2b5dedc150f18f9d91769539ce425e58f7176d Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Fri, 10 Feb 2023 23:41:02 +0100 Subject: [PATCH 25/90] (chore): started migrating parsing logic from compose --- packages/schema/compose/src/parse.ts | 163 ------------------------- packages/schema/compose/src/resolve.ts | 62 ---------- packages/schema/compose/src/types.ts | 11 +- packages/schema/compose/src/utils.ts | 10 -- packages/schema/parse/src/abi/utils.ts | 30 +---- 5 files changed, 5 insertions(+), 271 deletions(-) delete mode 100644 packages/schema/compose/src/parse.ts delete mode 100644 packages/schema/compose/src/utils.ts diff --git a/packages/schema/compose/src/parse.ts b/packages/schema/compose/src/parse.ts deleted file mode 100644 index bf3dfdc2de..0000000000 --- a/packages/schema/compose/src/parse.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { ExternalImport, LocalImport, SYNTAX_REFERENCE, Use } from "./types"; -import { getDuplicates } from "./utils"; - -import Path from "path"; -import { CapabilityType } from "@polywrap/schema-parse"; - -export function parseUse(useStatements: RegExpMatchArray[]): Use[] { - const uses: Use[] = []; - - for (const useStatement of useStatements) { - if (useStatement.length !== 3) { - throw Error( - `Invalid use statement found:\n${useStatement[0]}\n` + - `Please use the following syntax...\n${SYNTAX_REFERENCE}` - ); - } - - const usedTypes = useStatement[1] - .split(",") - .map((str) => str.replace(/\s+/g, "")) // Trim all whitespace - .filter(Boolean); // Remove empty strings - - const useForName = useStatement[2]; - - // Make sure the developer does not import the same dependency more than once - const duplicateUsedTypes = getDuplicates(usedTypes); - if (duplicateUsedTypes.length > 0) { - throw Error( - `Duplicate type found: ${duplicateUsedTypes} \nIn Use: ${useForName}` - ); - } - - uses.push({ - usedTypes: usedTypes as CapabilityType[], - namespace: useForName, - }); - } - return uses; -} - -export function parseExternalImports( - imports: RegExpMatchArray[] -): ExternalImport[] { - const externalImports: ExternalImport[] = []; - - for (const importStatement of imports) { - if (importStatement.length !== 4) { - throw Error( - `Invalid external import statement found:\n${importStatement[0]}\n` + - `Please use the following syntax...\n${SYNTAX_REFERENCE}` - ); - } - - const importedTypes = importStatement[1] - .split(",") - // Trim all whitespace and brackets - .map((str) => str.replace(/(\s+|\{|\})/g, "")) - // Remove empty strings - .filter(Boolean); - - const importFromName = importStatement[3]; - - // Make sure the developer does not import the same dependency more than once - const duplicateimportedTypes = getDuplicates(importedTypes); - if (duplicateimportedTypes.length > 0) { - throw Error( - `Duplicate type found: ${duplicateimportedTypes} \nIn import: ${importFromName}` - ); - } - - // Make sure the developer does not try to import a dependencies dependency - const index = importedTypes.findIndex((str) => str.indexOf("_") > -1); - if (index > -1) { - throw Error( - `Importing a dependency's imported type is forbidden. Only import types that do not have an '_' in the typename.` - ); - } - - const namespace = importStatement[2]; - const uri = importStatement[3]; - - externalImports.push({ - importedTypes, - namespace, - uri, - }); - } - - // Make sure namespaces are unique - const namespaces = externalImports.map((extImport) => extImport.namespace); - const duplicateNamespaces = getDuplicates(namespaces); - if (duplicateNamespaces.length > 0) { - throw Error(`Duplicate namespaces found: ${duplicateNamespaces}`); - } - - // Make sure all uris have the same namespace - const uriToNamespace: Record = {}; - for (const ext of externalImports) { - if (uriToNamespace[ext.uri]) { - if (uriToNamespace[ext.uri] !== ext.namespace) { - throw Error( - `Imports from a single URI must be imported into the same namespace.\nURI: ${ - ext.uri - }\nNamespace 1: ${ext.namespace}\nNamespace 2: ${ - uriToNamespace[ext.uri] - }` - ); - } - } else { - uriToNamespace[ext.uri] = ext.namespace; - } - } - - return externalImports; -} - -export function parseLocalImports( - imports: RegExpMatchArray[], - schemaPath: string -): LocalImport[] { - const localImports: LocalImport[] = []; - - for (const importStatement of imports) { - if (importStatement.length !== 3) { - throw Error( - `Invalid local import statement found:\n${importStatement[0]}\n` + - `Please use the following syntax...\n${SYNTAX_REFERENCE}` - ); - } - - const importTypes = importStatement[1] - .split(",") - // Trim all whitespace and brackets - .map((str) => str.replace(/(\s+|\{|\})/g, "")) - // Remove empty strings - .filter(Boolean); - const importPath = importStatement[2]; - const path = Path.join(Path.dirname(schemaPath), importPath); - - // Make sure the developer does not try to import a dependencies dependency - const index = importTypes.findIndex((str) => str.indexOf("_") > -1); - if (index > -1) { - throw Error( - `User defined types with '_' in their name are forbidden. This is used for Polywrap import namespacing.` - ); - } - - localImports.push({ - importedTypes: importTypes, - path, - }); - } - - // Make sure types are unique - const localImportNames: string[] = []; - localImports.forEach((imp) => localImportNames.push(...imp.importedTypes)); - const duplicateImportTypes = getDuplicates(localImportNames); - if (duplicateImportTypes.length > 0) { - throw Error(`Duplicate type found: ${duplicateImportTypes}`); - } - - return localImports; -} diff --git a/packages/schema/compose/src/resolve.ts b/packages/schema/compose/src/resolve.ts index 591b4ff3a7..adfb255f2f 100644 --- a/packages/schema/compose/src/resolve.ts +++ b/packages/schema/compose/src/resolve.ts @@ -56,68 +56,6 @@ const TYPE_NAME_REGEX = `[a-zA-Z0-9_]+`; type UriStr = string; type SchemaPath = string; -export async function resolveUseStatements( - schema: string, - schemaPath: string, - abi: WrapAbi -): Promise { - const useKeywordCapture = /^(?:#|""")*use[ \n\t]/gm; - const useCapture = /(?:#|""")*use[ \n\t]*{([a-zA-Z0-9_, \n\t]+)}[ \n\t]*for[ \n\t]*(\w+)[ \n\t]/g; - - const keywords = [...schema.matchAll(useKeywordCapture)]; - const useStatements = [...schema.matchAll(useCapture)]; - - if (keywords.length !== useStatements.length) { - throw Error( - `Invalid use statement found in file ${schemaPath}.\nPlease use one of the following syntaxes...\n${SYNTAX_REFERENCE}` - ); - } - - const importedModuleByNamespace: Record< - string, - ImportedModuleDefinition - > = {}; - - abi.importedModuleTypes && - abi.importedModuleTypes.forEach((value) => { - importedModuleByNamespace[value.namespace] = value; - }); - - // TODO: come back to this - const capabilitiesExt: ModuleCapability[] = []; - - const parsedUses = parseUse(useStatements); - for (const parsedUse of parsedUses) { - const importedModule = importedModuleByNamespace[parsedUse.namespace]; - if (!importedModule) { - throw Error(`Invalid use statement: namespace used hasn't been imported`); - } - - const capabilities = parsedUse.usedTypes - .map((type) => { - capabilitiesExt.push({ - type, - uri: importedModule.uri, - namespace: parsedUse.namespace, - }); - return createCapability({ type, enabled: true }); - }) - .reduce((o1, o2) => ({ ...o1, ...o2 })); - - const interfaceType = createInterfaceDefinition({ - type: parsedUse.namespace, - uri: importedModule.uri, - namespace: parsedUse.namespace, - capabilities: capabilities, - }); - - abi.interfaceTypes = abi.interfaceTypes - ? [...abi.interfaceTypes, interfaceType] - : [interfaceType]; - } - return capabilitiesExt; -} - export async function resolveImportsAndParseSchemas( schema: string, schemaPath: string, diff --git a/packages/schema/compose/src/types.ts b/packages/schema/compose/src/types.ts index a24e15a5b7..6c60716b8b 100644 --- a/packages/schema/compose/src/types.ts +++ b/packages/schema/compose/src/types.ts @@ -13,21 +13,14 @@ export interface AbiResolvers { local: SchemaResolver; } -export interface ExternalImport { - importedTypes: string[]; - namespace: string; - uri: string; -} + export interface Use { usedTypes: CapabilityType[]; namespace: string; } -export interface LocalImport { - importedTypes: string[]; - path: string; -} + export const SYNTAX_REFERENCE = "External Import:\n" + diff --git a/packages/schema/compose/src/utils.ts b/packages/schema/compose/src/utils.ts deleted file mode 100644 index c1d9e7f8f7..0000000000 --- a/packages/schema/compose/src/utils.ts +++ /dev/null @@ -1,10 +0,0 @@ -const countDuplicates = (array: string[]): Record => - array.reduce( - (a: Record, b: string) => ({ ...a, [b]: (a[b] || 0) + 1 }), - {} - ); - -export const getDuplicates = (array: string[]): string[] => { - const counts = countDuplicates(array); - return Object.keys(counts).filter((a) => counts[a] > 1); -}; diff --git a/packages/schema/parse/src/abi/utils.ts b/packages/schema/parse/src/abi/utils.ts index 74ad904cc1..9e896c3cea 100644 --- a/packages/schema/parse/src/abi/utils.ts +++ b/packages/schema/parse/src/abi/utils.ts @@ -1,29 +1,7 @@ -export const MapKeyTypes = { - UInt: "UInt", - UInt8: "UInt8", - UInt16: "UInt16", - UInt32: "UInt32", - Int: "Int", - Int8: "Int8", - Int16: "Int16", - Int32: "Int32", - String: "String", -}; - -export const ScalarTypes = { - ...MapKeyTypes, - Boolean: "Boolean", - Bytes: "Bytes", - BigInt: "BigInt", - BigNumber: "BigNumber", - JSON: "JSON", -}; - -export type ScalarType = keyof typeof ScalarTypes; -export type MapKeyType = keyof typeof MapKeyTypes; +import { mapKeyTypeSet, scalarTypeSet } from "../definitions"; export function isMapKeyType(type: string): boolean { - return type in MapKeyTypes; + return type in mapKeyTypeSet; } export const MODULE_NAME = "Module"; @@ -37,7 +15,5 @@ export function isImportedModuleType(type: string): boolean { } export function isScalarType(type: string): boolean { - return type in ScalarTypes; + return type in scalarTypeSet; } - -export const scalarTypeNames = Object.keys(ScalarTypes); From e9fa8c8485f6e2a3e026d5472999cf2b5c4dfe3f Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Fri, 10 Feb 2023 23:41:21 +0100 Subject: [PATCH 26/90] (feat): discover imports in parse --- packages/schema/parse/src/extract/imports.ts | 301 +++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 packages/schema/parse/src/extract/imports.ts diff --git a/packages/schema/parse/src/extract/imports.ts b/packages/schema/parse/src/extract/imports.ts new file mode 100644 index 0000000000..473c863495 --- /dev/null +++ b/packages/schema/parse/src/extract/imports.ts @@ -0,0 +1,301 @@ +import { WrapAbi } from ".."; + +import Path from "path"; +import { isModuleType } from "../abi"; +import { DocumentNode, Kind, parse, visit, Visitor } from "graphql"; +import { parseAnnotateDirective } from "./directives"; + +const TYPE_NAME_REGEX = `[a-zA-Z0-9_]+`; +const SYNTAX_REFERENCE = + "External Import:\n" + + `import { Type, Module } into Namespace from "external.uri"\n` + + `import * into Namespace from "external.uri"\n` + + "Local Import:\n" + + `import { Type } from "./local/path/file.graphql"\n` + + `import * from "./local/path/file.graphql"`; + +interface AbiImport { + kind: "external" | "local"; + uriOrPath: string; +} + +export interface ExternalImport extends AbiImport { + kind: "external"; + importedTypes: string[]; + namespace: string; + uri: string; +} + +export interface LocalImport extends AbiImport { + kind: "local"; + importedTypes: string[]; + path: string; +} + +const countDuplicates = (array: string[]): Record => + array.reduce( + (a: Record, b: string) => ({ ...a, [b]: (a[b] || 0) + 1 }), + {} + ); + +const getDuplicates = (array: string[]): string[] => { + const counts = countDuplicates(array); + return Object.keys(counts).filter((a) => counts[a] > 1); +}; + + +export function parseExternalImports( + imports: RegExpMatchArray[] +): ExternalImport[] { + const externalImports: ExternalImport[] = []; + + for (const importStatement of imports) { + if (importStatement.length !== 4) { + throw Error( + `Invalid external import statement found:\n${importStatement[0]}\n` + + `Please use the following syntax...\n${SYNTAX_REFERENCE}` + ); + } + + const importedTypes = importStatement[1] + .split(",") + // Trim all whitespace and brackets + .map((str) => str.replace(/(\s+|\{|\})/g, "")) + // Remove empty strings + .filter(Boolean); + + const importFromName = importStatement[3]; + + // Make sure the developer does not import the same dependency more than once + const duplicateimportedTypes = getDuplicates(importedTypes); + if (duplicateimportedTypes.length > 0) { + throw Error( + `Duplicate type found: ${duplicateimportedTypes} \nIn import: ${importFromName}` + ); + } + + // Make sure the developer does not try to import a dependencies dependency + const index = importedTypes.findIndex((str) => str.indexOf("_") > -1); + if (index > -1) { + throw Error( + `Importing a dependency's imported type is forbidden. Only import types that do not have an '_' in the typename.` + ); + } + + const namespace = importStatement[2]; + const uri = importStatement[3]; + + externalImports.push({ + kind: "external", + uriOrPath: uri, + importedTypes, + namespace, + uri, + }); + } + + // Make sure namespaces are unique + const namespaces = externalImports.map((extImport) => extImport.namespace); + const duplicateNamespaces = getDuplicates(namespaces); + if (duplicateNamespaces.length > 0) { + throw Error(`Duplicate namespaces found: ${duplicateNamespaces}`); + } + + // Make sure all uris have the same namespace + const uriToNamespace: Record = {}; + for (const ext of externalImports) { + if (uriToNamespace[ext.uri]) { + if (uriToNamespace[ext.uri] !== ext.namespace) { + throw Error( + `Imports from a single URI must be imported into the same namespace.\nURI: ${ext.uri + }\nNamespace 1: ${ext.namespace}\nNamespace 2: ${uriToNamespace[ext.uri] + }` + ); + } + } else { + uriToNamespace[ext.uri] = ext.namespace; + } + } + + return externalImports; +} + +export function parseLocalImports( + imports: RegExpMatchArray[], + schemaPath: string +): LocalImport[] { + const localImports: LocalImport[] = []; + + for (const importStatement of imports) { + if (importStatement.length !== 3) { + throw Error( + `Invalid local import statement found:\n${importStatement[0]}\n` + + `Please use the following syntax...\n${SYNTAX_REFERENCE}` + ); + } + + const importTypes = importStatement[1] + .split(",") + // Trim all whitespace and brackets + .map((str) => str.replace(/(\s+|\{|\})/g, "")) + // Remove empty strings + .filter(Boolean); + const importPath = importStatement[2]; + const path = Path.join(Path.dirname(schemaPath), importPath); + + // Make sure the developer does not try to import a dependencies dependency + const index = importTypes.findIndex((str) => str.indexOf("_") > -1); + if (index > -1) { + throw Error( + `User defined types with '_' in their name are forbidden. This is used for Polywrap import namespacing.` + ); + } + + localImports.push({ + kind: "local", + importedTypes: importTypes, + path, + uriOrPath: path + }); + } + + // Make sure types are unique + const localImportNames: string[] = []; + localImports.forEach((imp) => localImportNames.push(...imp.importedTypes)); + const duplicateImportTypes = getDuplicates(localImportNames); + if (duplicateImportTypes.length > 0) { + throw Error(`Duplicate type found: ${duplicateImportTypes}`); + } + + return localImports; +} + +export function parseImportStatements( + schema: string, + schemaPath: string +) { + const importKeywordCapture = /^(?:#|""")*import\s/gm; + const externalImportCapture = /(?:#|""")*import\s*(?:({[^}]+}|\*))\s*into\s*(\w+?)\s*from\s*[\"'`]([^\"'`\s]+)[\"'`]/g; + const localImportCapture = /(?:#|""")*import\s*(?:({[^}]+}|\*))\s*from\s*[\"'`]([^\"'`\s]+)[\"'`]/g; + + const keywords = [...schema.matchAll(importKeywordCapture)]; + const externalImportStatements = [...schema.matchAll(externalImportCapture)]; + const localImportStatements = [...schema.matchAll(localImportCapture)]; + const totalStatements = + externalImportStatements.length + localImportStatements.length; + + if (keywords.length !== totalStatements) { + throw Error( + `Invalid import statement found in file ${schemaPath}.\nPlease use one of the following syntaxes...\n${SYNTAX_REFERENCE}` + ); + } + + const externalImportsToResolve: ExternalImport[] = parseExternalImports( + externalImportStatements + ); + + const localImportsToResolve: LocalImport[] = parseLocalImports( + localImportStatements, + schemaPath + ); + + return { + externalImportStatements: externalImportsToResolve, + localImportStatements: localImportsToResolve, + } +} + +export function parseExternalImportStatements( + schema: string, +) { + const importKeywordCapture = /^(?:#|""")*import\s/gm; + const externalImportCapture = /(?:#|""")*import\s*(?:({[^}]+}|\*))\s*into\s*(\w+?)\s*from\s*[\"'`]([^\"'`\s]+)[\"'`]/g; + const localImportCapture = /(?:#|""")*import\s*(?:({[^}]+}|\*))\s*from\s*[\"'`]([^\"'`\s]+)[\"'`]/g; + + const keywords = [...schema.matchAll(importKeywordCapture)]; + const externalImportStatements = [...schema.matchAll(externalImportCapture)]; + const localImportStatements = [...schema.matchAll(localImportCapture)]; + const totalStatements = + externalImportStatements.length + localImportStatements.length; + + if (keywords.length !== totalStatements) { + throw Error( + `Invalid import statement found in file.\nPlease use one of the following syntaxes...\n${SYNTAX_REFERENCE}` + ); + } + + const externalImportsToResolve: ExternalImport[] = parseExternalImports( + externalImportStatements + ); + + return externalImportsToResolve; +} + +const fetchExternalSchema = async (uri: string): Promise => { + throw new Error("Not implemented") +} + +const resolveImportStatement = async (importStatement: ExternalImport, importedSchemasRegistry: Map) => { + const schemaString = await fetchExternalSchema(importStatement.uri); + const importsOfTheImport = parseExternalImportStatements(schemaString) + + const astNode = parse(schemaString); + importedSchemasRegistry.set(importStatement.uri, astNode) + + // TODO: Maps are missing from this logic + const state: { + currentObject?: string, + } = {} + + const nextImportStatementsToSearch: ExternalImport[] = []; + + visit(astNode, { + enter: { + ObjectTypeDefinition: (node) => { + const name = node.name.value; + + if (importStatement && importStatement.importedTypes.includes(name)) { + state.currentObject = node.name.value; + } + }, + NamedType: (node) => { + if (!state.currentObject) { + return + } + + importsOfTheImport.forEach((extImportStatement) => { + if (extImportStatement.importedTypes.includes(node.name.value)) { + nextImportStatementsToSearch.push(extImportStatement) + } + }) + } + }, + leave: { + ObjectTypeDefinition: () => { + state.currentObject = undefined; + }, + }, + }) + + nextImportStatementsToSearch.forEach((nextImportStatement) => { + resolveImportStatement(nextImportStatement, importedSchemasRegistry) + }) +} + +const discoverImportAbis = ( + schema: string, + schemaPath: string, +): Map => { + const importedSchemasRegistry = new Map(); + + const { + externalImportStatements, + localImportStatements + } = parseImportStatements(schema, schemaPath); + + externalImportStatements.forEach((importStatement) => { + resolveImportStatement(importStatement, importedSchemasRegistry) + }) + + return importedSchemasRegistry; +} \ No newline at end of file From 7057f96f8351449dce1d4e214b9afc511b87e107 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Sat, 11 Feb 2023 01:18:31 +0100 Subject: [PATCH 27/90] (feat): uniqueDefinitions pre-pass --- packages/schema/compose/src/resolve.ts | 98 -------------- packages/schema/compose/src/types.ts | 7 - .../parse/src/extract/imports/constants.ts | 8 ++ .../extract/{imports.ts => imports/parse.ts} | 125 ++---------------- .../parse/src/extract/imports/registry.ts | 28 ++++ .../schema/parse/src/extract/imports/utils.ts | 14 ++ packages/schema/parse/src/extract/utils.ts | 23 +++- packages/schema/parse/src/index.ts | 45 ++++--- 8 files changed, 106 insertions(+), 242 deletions(-) create mode 100644 packages/schema/parse/src/extract/imports/constants.ts rename packages/schema/parse/src/extract/{imports.ts => imports/parse.ts} (62%) create mode 100644 packages/schema/parse/src/extract/imports/registry.ts create mode 100644 packages/schema/parse/src/extract/imports/utils.ts diff --git a/packages/schema/compose/src/resolve.ts b/packages/schema/compose/src/resolve.ts index adfb255f2f..2ed25851c8 100644 --- a/packages/schema/compose/src/resolve.ts +++ b/packages/schema/compose/src/resolve.ts @@ -56,105 +56,7 @@ const TYPE_NAME_REGEX = `[a-zA-Z0-9_]+`; type UriStr = string; type SchemaPath = string; -export async function resolveImportsAndParseSchemas( - schema: string, - schemaPath: string, - importSchemas: Map, - importsAbis: Map, - noValidate = false -): Promise { - const importKeywordCapture = /^(?:#|""")*import\s/gm; - const externalImportCapture = /(?:#|""")*import\s*(?:({[^}]+}|\*))\s*into\s*(\w+?)\s*from\s*[\"'`]([^\"'`\s]+)[\"'`]/g; - const localImportCapture = /(?:#|""")*import\s*(?:({[^}]+}|\*))\s*from\s*[\"'`]([^\"'`\s]+)[\"'`]/g; - - const keywords = [...schema.matchAll(importKeywordCapture)]; - const externalImportStatements = [...schema.matchAll(externalImportCapture)]; - const localImportStatements = [...schema.matchAll(localImportCapture)]; - const totalStatements = - externalImportStatements.length + localImportStatements.length; - - if (keywords.length !== totalStatements) { - throw Error( - `Invalid import statement found in file ${schemaPath}.\nPlease use one of the following syntaxes...\n${SYNTAX_REFERENCE}` - ); - } - - const interfaceCapture = new RegExp( - `type\\s+${TYPE_NAME_REGEX}\\s+implements\\s([^{]*){`, - "g" - ); - const implementInterfaceStatments = [...schema.matchAll(interfaceCapture)]; - - const implementationsWithInterfaces = parseInterfaces( - implementInterfaceStatments - ); - - const externalImportsToResolve: ExternalImport[] = parseExternalImports( - externalImportStatements - ); - - const localImportsToResolve: LocalImport[] = parseLocalImports( - localImportStatements, - schemaPath - ); - - const subAbi: WrapAbi = { - version: "0.1", - objectTypes: [], - enumTypes: [], - interfaceTypes: [], - importedEnumTypes: [], - importedObjectTypes: [], - importedModuleTypes: [], - }; - const externalImports = await resolveExternalImports( - externalImportsToResolve, - importsAbis, - subAbi - ); - - await resolveLocalImports( - localImportsToResolve, - importSchemas, - subAbi, - importsAbis - ); - const capabilitiesByModule = await resolveUseStatements( - schema, - schemaPath, - subAbi - ); - - // Remove all import statements - let newSchema = schema - .replace(externalImportCapture, "") - .replace(localImportCapture, ""); - - // Remove all non documentation comments - newSchema = newSchema.replace(/#[^\n]*\n/g, ""); - - // Add the @imports directive - newSchema = addModuleImportsDirective(newSchema, externalImports); - - // Add the @capability directive - newSchema = addCapabilityDirective(newSchema, capabilitiesByModule); - - // Combine the new schema with the subAbi - newSchema = header + newSchema + renderSchema(subAbi, false); - - newSchema = resolveInterfaces(newSchema, implementationsWithInterfaces); - - // Replace types that have empty curly brackets with types that have no curly brackets - // because GraphQL parser doesn't support empty curly brackets but supports no curly brackets - newSchema = newSchema.replace( - new RegExp(`(type\\s+${TYPE_NAME_REGEX}[^{]*){\\s*}`, "g"), - "$1" - ); - - // Parse and return the newly formed schema - return parseSchema(newSchema, { noValidate }); -} interface Namespaced { __namespaced?: boolean; diff --git a/packages/schema/compose/src/types.ts b/packages/schema/compose/src/types.ts index 6c60716b8b..379e46de30 100644 --- a/packages/schema/compose/src/types.ts +++ b/packages/schema/compose/src/types.ts @@ -22,10 +22,3 @@ export interface Use { -export const SYNTAX_REFERENCE = - "External Import:\n" + - `import { Type, Module } into Namespace from "external.uri"\n` + - `import * into Namespace from "external.uri"\n` + - "Local Import:\n" + - `import { Type } from "./local/path/file.graphql"\n` + - `import * from "./local/path/file.graphql"`; diff --git a/packages/schema/parse/src/extract/imports/constants.ts b/packages/schema/parse/src/extract/imports/constants.ts new file mode 100644 index 0000000000..f0b6fd8da3 --- /dev/null +++ b/packages/schema/parse/src/extract/imports/constants.ts @@ -0,0 +1,8 @@ +export const TYPE_NAME_REGEX = `[a-zA-Z0-9_]+`; +export const SYNTAX_REFERENCE = + "External Import:\n" + + `import { Type, Module } into Namespace from "external.uri"\n` + + `import * into Namespace from "external.uri"\n` + + "Local Import:\n" + + `import { Type } from "./local/path/file.graphql"\n` + + `import * from "./local/path/file.graphql"`; \ No newline at end of file diff --git a/packages/schema/parse/src/extract/imports.ts b/packages/schema/parse/src/extract/imports/parse.ts similarity index 62% rename from packages/schema/parse/src/extract/imports.ts rename to packages/schema/parse/src/extract/imports/parse.ts index 473c863495..cce8607f59 100644 --- a/packages/schema/parse/src/extract/imports.ts +++ b/packages/schema/parse/src/extract/imports/parse.ts @@ -1,53 +1,23 @@ -import { WrapAbi } from ".."; +import { SYNTAX_REFERENCE } from "./constants"; +import { getDuplicates } from "./utils"; import Path from "path"; -import { isModuleType } from "../abi"; -import { DocumentNode, Kind, parse, visit, Visitor } from "graphql"; -import { parseAnnotateDirective } from "./directives"; - -const TYPE_NAME_REGEX = `[a-zA-Z0-9_]+`; -const SYNTAX_REFERENCE = - "External Import:\n" + - `import { Type, Module } into Namespace from "external.uri"\n` + - `import * into Namespace from "external.uri"\n` + - "Local Import:\n" + - `import { Type } from "./local/path/file.graphql"\n` + - `import * from "./local/path/file.graphql"`; - -interface AbiImport { - kind: "external" | "local"; - uriOrPath: string; -} -export interface ExternalImport extends AbiImport { - kind: "external"; +export interface ExternalImportStatement { importedTypes: string[]; namespace: string; uri: string; } -export interface LocalImport extends AbiImport { - kind: "local"; +export interface LocalImportStatement { importedTypes: string[]; path: string; } -const countDuplicates = (array: string[]): Record => - array.reduce( - (a: Record, b: string) => ({ ...a, [b]: (a[b] || 0) + 1 }), - {} - ); - -const getDuplicates = (array: string[]): string[] => { - const counts = countDuplicates(array); - return Object.keys(counts).filter((a) => counts[a] > 1); -}; - - export function parseExternalImports( imports: RegExpMatchArray[] -): ExternalImport[] { - const externalImports: ExternalImport[] = []; +): ExternalImportStatement[] { + const externalImports: ExternalImportStatement[] = []; for (const importStatement of imports) { if (importStatement.length !== 4) { @@ -86,8 +56,6 @@ export function parseExternalImports( const uri = importStatement[3]; externalImports.push({ - kind: "external", - uriOrPath: uri, importedTypes, namespace, uri, @@ -123,8 +91,8 @@ export function parseExternalImports( export function parseLocalImports( imports: RegExpMatchArray[], schemaPath: string -): LocalImport[] { - const localImports: LocalImport[] = []; +): LocalImportStatement[] { + const localImports: LocalImportStatement[] = []; for (const importStatement of imports) { if (importStatement.length !== 3) { @@ -152,10 +120,8 @@ export function parseLocalImports( } localImports.push({ - kind: "local", importedTypes: importTypes, path, - uriOrPath: path }); } @@ -190,11 +156,11 @@ export function parseImportStatements( ); } - const externalImportsToResolve: ExternalImport[] = parseExternalImports( + const externalImportsToResolve: ExternalImportStatement[] = parseExternalImports( externalImportStatements ); - const localImportsToResolve: LocalImport[] = parseLocalImports( + const localImportsToResolve: LocalImportStatement[] = parseLocalImports( localImportStatements, schemaPath ); @@ -224,78 +190,9 @@ export function parseExternalImportStatements( ); } - const externalImportsToResolve: ExternalImport[] = parseExternalImports( + const externalImportsToResolve: ExternalImportStatement[] = parseExternalImports( externalImportStatements ); return externalImportsToResolve; -} - -const fetchExternalSchema = async (uri: string): Promise => { - throw new Error("Not implemented") -} - -const resolveImportStatement = async (importStatement: ExternalImport, importedSchemasRegistry: Map) => { - const schemaString = await fetchExternalSchema(importStatement.uri); - const importsOfTheImport = parseExternalImportStatements(schemaString) - - const astNode = parse(schemaString); - importedSchemasRegistry.set(importStatement.uri, astNode) - - // TODO: Maps are missing from this logic - const state: { - currentObject?: string, - } = {} - - const nextImportStatementsToSearch: ExternalImport[] = []; - - visit(astNode, { - enter: { - ObjectTypeDefinition: (node) => { - const name = node.name.value; - - if (importStatement && importStatement.importedTypes.includes(name)) { - state.currentObject = node.name.value; - } - }, - NamedType: (node) => { - if (!state.currentObject) { - return - } - - importsOfTheImport.forEach((extImportStatement) => { - if (extImportStatement.importedTypes.includes(node.name.value)) { - nextImportStatementsToSearch.push(extImportStatement) - } - }) - } - }, - leave: { - ObjectTypeDefinition: () => { - state.currentObject = undefined; - }, - }, - }) - - nextImportStatementsToSearch.forEach((nextImportStatement) => { - resolveImportStatement(nextImportStatement, importedSchemasRegistry) - }) -} - -const discoverImportAbis = ( - schema: string, - schemaPath: string, -): Map => { - const importedSchemasRegistry = new Map(); - - const { - externalImportStatements, - localImportStatements - } = parseImportStatements(schema, schemaPath); - - externalImportStatements.forEach((importStatement) => { - resolveImportStatement(importStatement, importedSchemasRegistry) - }) - - return importedSchemasRegistry; } \ No newline at end of file diff --git a/packages/schema/parse/src/extract/imports/registry.ts b/packages/schema/parse/src/extract/imports/registry.ts new file mode 100644 index 0000000000..d431f34993 --- /dev/null +++ b/packages/schema/parse/src/extract/imports/registry.ts @@ -0,0 +1,28 @@ +import { DocumentNode, parse } from "graphql"; +import fs from "fs"; +import { parseImportStatements } from "./parse"; +import { fetchExternalSchema } from "./utils"; + +export const getImportedAbisRegistry = async ( + schema: string, + schemaPath: string, +): Promise> => { + let importedAbiRegistry = new Map(); + + const { + externalImportStatements, + localImportStatements + } = parseImportStatements(schema, schemaPath); + + for await (const externalImportStatement of externalImportStatements) { + const schemaString = await fetchExternalSchema(externalImportStatement.uri); + importedAbiRegistry.set(externalImportStatement.uri, parse(schemaString)); + } + + for (const localImportStatement of localImportStatements) { + const localSchemaFileSource = fs.readFileSync(localImportStatement.path, "utf8"); + importedAbiRegistry.set(localImportStatement.path, parse(localSchemaFileSource)); + } + + return importedAbiRegistry; +} diff --git a/packages/schema/parse/src/extract/imports/utils.ts b/packages/schema/parse/src/extract/imports/utils.ts new file mode 100644 index 0000000000..fded680da6 --- /dev/null +++ b/packages/schema/parse/src/extract/imports/utils.ts @@ -0,0 +1,14 @@ +export const countDuplicates = (array: string[]): Record => + array.reduce( + (a: Record, b: string) => ({ ...a, [b]: (a[b] || 0) + 1 }), + {} + ); + +export const getDuplicates = (array: string[]): string[] => { + const counts = countDuplicates(array); + return Object.keys(counts).filter((a) => counts[a] > 1); +}; + +export const fetchExternalSchema = async (uri: string): Promise => { + throw new Error("Not implemented") +} \ No newline at end of file diff --git a/packages/schema/parse/src/extract/utils.ts b/packages/schema/parse/src/extract/utils.ts index 008f67e7b7..bed23d725d 100644 --- a/packages/schema/parse/src/extract/utils.ts +++ b/packages/schema/parse/src/extract/utils.ts @@ -1,5 +1,5 @@ -import { TypeNode } from "graphql"; -import { isScalarType } from "../abi/utils"; +import { DocumentNode, TypeNode, visit } from "graphql"; +import { isModuleType, isScalarType } from "../abi/utils"; import { UniqueDefKind, RefType, AnyType, MapKeyTypeName, mapKeyTypeSet, MapType, ScalarTypeName, ArrayType, ScalarType } from "../definitions"; export const extractType = (node: TypeNode, uniqueDefs: Map): AnyType => { @@ -171,3 +171,22 @@ const isMapKey = (typeName: string): boolean => { const isArray = (typeName: string): boolean => { return typeName.startsWith("[") } + +export const extractUniqueDefinitionNames = (document: DocumentNode): Map => { + const uniqueDefs = new Map(); + + visit(document, { + ObjectTypeDefinition: (node) => { + const name = node.name.value; + + if (!isModuleType(name)) { + uniqueDefs.set(name, "Object") + } + }, + EnumTypeDefinition: (node) => { + uniqueDefs.set(node.name.value, "Enum") + } + }); + + return uniqueDefs; +} diff --git a/packages/schema/parse/src/index.ts b/packages/schema/parse/src/index.ts index bc626a42d5..5687646d68 100644 --- a/packages/schema/parse/src/index.ts +++ b/packages/schema/parse/src/index.ts @@ -8,6 +8,8 @@ import { ExternalVisitorBuilder, VisitorBuilder } from "./extract/types"; import { ObjectVisitorBuilder } from "./extract"; import { EnumVisitorBuilder } from "./extract"; import { FunctionsVisitorBuilder } from "./extract"; +import { extractUniqueDefinitionNames } from "./extract/utils"; +import { getImportedAbisRegistry } from "./extract/imports/registry"; export * from "./abi"; export * from "./transform"; @@ -25,35 +27,36 @@ interface ParserOptions { transforms?: AbiTransforms[]; } -const extractUniqueDefinitionNames = (document: DocumentNode): Map => { - const uniqueDefs = new Map(); +const parseSchemaAndImports = async (schema: string, schemaPath: string): Promise<{ abi: Abi, imports: Map }> => { + const importsRegistry = await getImportedAbisRegistry(schema, schemaPath); + let allUniqueDefinitions = new Map(); + + for (const importedAbi of importsRegistry.values()) { + allUniqueDefinitions = new Map([...allUniqueDefinitions, ...extractUniqueDefinitionNames(importedAbi)]); + } - visit(document, { - ObjectTypeDefinition: (node) => { - const name = node.name.value; + const importedAbis = new Map(); - if (!isModuleType(name)) { - uniqueDefs.set(name, "Object") - } - }, - EnumTypeDefinition: (node) => { - uniqueDefs.set(node.name.value, "Enum") - } - }); + for (const [importPath, importedAbi] of importsRegistry.entries()) { + importedAbis.set(importPath, transformSchemaToAbi(importedAbi, allUniqueDefinitions)); + } - return uniqueDefs; + return { + abi: transformSchemaToAbi(parse(schema), allUniqueDefinitions), + imports: importedAbis + } } -export function parseSchema( - schema: string, +function transformSchemaToAbi( + astNode: DocumentNode, + uniqueDefinitions: Map, options: ParserOptions = {} ): Abi { - const astNode = parse(schema); - const uniqueDefs = extractUniqueDefinitionNames(astNode); + const defaultExtractors: VisitorBuilder[] = [ - new ObjectVisitorBuilder(uniqueDefs), + new ObjectVisitorBuilder(uniqueDefinitions), new EnumVisitorBuilder(), - new FunctionsVisitorBuilder(uniqueDefs) + new FunctionsVisitorBuilder(uniqueDefinitions) ] // Validate GraphQL Schema @@ -65,7 +68,7 @@ export function parseSchema( // Extract & Build Abi let info = createAbi(); - const extracts = options.extractors?.map(extractorBuilder => extractorBuilder(info, uniqueDefs)) ?? defaultExtractors.map(e => e.build(info)); + const extracts = options.extractors?.map(extractorBuilder => extractorBuilder(info, uniqueDefinitions)) ?? defaultExtractors.map(e => e.build(info)); extract(astNode, extracts); if (options && options.transforms) { From 35a06e164edda9a743993b49c7057e7299ff8c64 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Sat, 11 Feb 2023 02:31:42 +0100 Subject: [PATCH 28/90] (chore): started migrating GraphQL specific impl outside --- packages/schema/parse/src/extract/enum.ts | 21 -- .../parse/src/extract/imports/constants.ts | 8 - .../schema/parse/src/extract/imports/parse.ts | 198 ------------------ .../parse/src/extract/imports/registry.ts | 28 --- .../schema/parse/src/extract/imports/utils.ts | 14 -- packages/schema/parse/src/index.ts | 4 +- 6 files changed, 2 insertions(+), 271 deletions(-) delete mode 100644 packages/schema/parse/src/extract/enum.ts delete mode 100644 packages/schema/parse/src/extract/imports/constants.ts delete mode 100644 packages/schema/parse/src/extract/imports/parse.ts delete mode 100644 packages/schema/parse/src/extract/imports/registry.ts delete mode 100644 packages/schema/parse/src/extract/imports/utils.ts diff --git a/packages/schema/parse/src/extract/enum.ts b/packages/schema/parse/src/extract/enum.ts deleted file mode 100644 index c00c0c5aa6..0000000000 --- a/packages/schema/parse/src/extract/enum.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ASTVisitor, EnumTypeDefinitionNode } from "graphql"; -import { Abi, EnumDef } from "../definitions"; -import { VisitorBuilder } from "./types"; - -export class EnumVisitorBuilder implements VisitorBuilder { - build(abi: Abi): ASTVisitor { - return { - enter: { - EnumTypeDefinition: (node: EnumTypeDefinitionNode) => { - const def: EnumDef = { - name: node.name.value, - kind: "Enum", - constants: node.values?.map(value => value.name.value) ?? [] - } - - abi.enums = abi.enums ? [...abi.enums, def] : [def]; - }, - }, - }; - } -} \ No newline at end of file diff --git a/packages/schema/parse/src/extract/imports/constants.ts b/packages/schema/parse/src/extract/imports/constants.ts deleted file mode 100644 index f0b6fd8da3..0000000000 --- a/packages/schema/parse/src/extract/imports/constants.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const TYPE_NAME_REGEX = `[a-zA-Z0-9_]+`; -export const SYNTAX_REFERENCE = - "External Import:\n" + - `import { Type, Module } into Namespace from "external.uri"\n` + - `import * into Namespace from "external.uri"\n` + - "Local Import:\n" + - `import { Type } from "./local/path/file.graphql"\n` + - `import * from "./local/path/file.graphql"`; \ No newline at end of file diff --git a/packages/schema/parse/src/extract/imports/parse.ts b/packages/schema/parse/src/extract/imports/parse.ts deleted file mode 100644 index cce8607f59..0000000000 --- a/packages/schema/parse/src/extract/imports/parse.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { SYNTAX_REFERENCE } from "./constants"; -import { getDuplicates } from "./utils"; - -import Path from "path"; - -export interface ExternalImportStatement { - importedTypes: string[]; - namespace: string; - uri: string; -} - -export interface LocalImportStatement { - importedTypes: string[]; - path: string; -} - -export function parseExternalImports( - imports: RegExpMatchArray[] -): ExternalImportStatement[] { - const externalImports: ExternalImportStatement[] = []; - - for (const importStatement of imports) { - if (importStatement.length !== 4) { - throw Error( - `Invalid external import statement found:\n${importStatement[0]}\n` + - `Please use the following syntax...\n${SYNTAX_REFERENCE}` - ); - } - - const importedTypes = importStatement[1] - .split(",") - // Trim all whitespace and brackets - .map((str) => str.replace(/(\s+|\{|\})/g, "")) - // Remove empty strings - .filter(Boolean); - - const importFromName = importStatement[3]; - - // Make sure the developer does not import the same dependency more than once - const duplicateimportedTypes = getDuplicates(importedTypes); - if (duplicateimportedTypes.length > 0) { - throw Error( - `Duplicate type found: ${duplicateimportedTypes} \nIn import: ${importFromName}` - ); - } - - // Make sure the developer does not try to import a dependencies dependency - const index = importedTypes.findIndex((str) => str.indexOf("_") > -1); - if (index > -1) { - throw Error( - `Importing a dependency's imported type is forbidden. Only import types that do not have an '_' in the typename.` - ); - } - - const namespace = importStatement[2]; - const uri = importStatement[3]; - - externalImports.push({ - importedTypes, - namespace, - uri, - }); - } - - // Make sure namespaces are unique - const namespaces = externalImports.map((extImport) => extImport.namespace); - const duplicateNamespaces = getDuplicates(namespaces); - if (duplicateNamespaces.length > 0) { - throw Error(`Duplicate namespaces found: ${duplicateNamespaces}`); - } - - // Make sure all uris have the same namespace - const uriToNamespace: Record = {}; - for (const ext of externalImports) { - if (uriToNamespace[ext.uri]) { - if (uriToNamespace[ext.uri] !== ext.namespace) { - throw Error( - `Imports from a single URI must be imported into the same namespace.\nURI: ${ext.uri - }\nNamespace 1: ${ext.namespace}\nNamespace 2: ${uriToNamespace[ext.uri] - }` - ); - } - } else { - uriToNamespace[ext.uri] = ext.namespace; - } - } - - return externalImports; -} - -export function parseLocalImports( - imports: RegExpMatchArray[], - schemaPath: string -): LocalImportStatement[] { - const localImports: LocalImportStatement[] = []; - - for (const importStatement of imports) { - if (importStatement.length !== 3) { - throw Error( - `Invalid local import statement found:\n${importStatement[0]}\n` + - `Please use the following syntax...\n${SYNTAX_REFERENCE}` - ); - } - - const importTypes = importStatement[1] - .split(",") - // Trim all whitespace and brackets - .map((str) => str.replace(/(\s+|\{|\})/g, "")) - // Remove empty strings - .filter(Boolean); - const importPath = importStatement[2]; - const path = Path.join(Path.dirname(schemaPath), importPath); - - // Make sure the developer does not try to import a dependencies dependency - const index = importTypes.findIndex((str) => str.indexOf("_") > -1); - if (index > -1) { - throw Error( - `User defined types with '_' in their name are forbidden. This is used for Polywrap import namespacing.` - ); - } - - localImports.push({ - importedTypes: importTypes, - path, - }); - } - - // Make sure types are unique - const localImportNames: string[] = []; - localImports.forEach((imp) => localImportNames.push(...imp.importedTypes)); - const duplicateImportTypes = getDuplicates(localImportNames); - if (duplicateImportTypes.length > 0) { - throw Error(`Duplicate type found: ${duplicateImportTypes}`); - } - - return localImports; -} - -export function parseImportStatements( - schema: string, - schemaPath: string -) { - const importKeywordCapture = /^(?:#|""")*import\s/gm; - const externalImportCapture = /(?:#|""")*import\s*(?:({[^}]+}|\*))\s*into\s*(\w+?)\s*from\s*[\"'`]([^\"'`\s]+)[\"'`]/g; - const localImportCapture = /(?:#|""")*import\s*(?:({[^}]+}|\*))\s*from\s*[\"'`]([^\"'`\s]+)[\"'`]/g; - - const keywords = [...schema.matchAll(importKeywordCapture)]; - const externalImportStatements = [...schema.matchAll(externalImportCapture)]; - const localImportStatements = [...schema.matchAll(localImportCapture)]; - const totalStatements = - externalImportStatements.length + localImportStatements.length; - - if (keywords.length !== totalStatements) { - throw Error( - `Invalid import statement found in file ${schemaPath}.\nPlease use one of the following syntaxes...\n${SYNTAX_REFERENCE}` - ); - } - - const externalImportsToResolve: ExternalImportStatement[] = parseExternalImports( - externalImportStatements - ); - - const localImportsToResolve: LocalImportStatement[] = parseLocalImports( - localImportStatements, - schemaPath - ); - - return { - externalImportStatements: externalImportsToResolve, - localImportStatements: localImportsToResolve, - } -} - -export function parseExternalImportStatements( - schema: string, -) { - const importKeywordCapture = /^(?:#|""")*import\s/gm; - const externalImportCapture = /(?:#|""")*import\s*(?:({[^}]+}|\*))\s*into\s*(\w+?)\s*from\s*[\"'`]([^\"'`\s]+)[\"'`]/g; - const localImportCapture = /(?:#|""")*import\s*(?:({[^}]+}|\*))\s*from\s*[\"'`]([^\"'`\s]+)[\"'`]/g; - - const keywords = [...schema.matchAll(importKeywordCapture)]; - const externalImportStatements = [...schema.matchAll(externalImportCapture)]; - const localImportStatements = [...schema.matchAll(localImportCapture)]; - const totalStatements = - externalImportStatements.length + localImportStatements.length; - - if (keywords.length !== totalStatements) { - throw Error( - `Invalid import statement found in file.\nPlease use one of the following syntaxes...\n${SYNTAX_REFERENCE}` - ); - } - - const externalImportsToResolve: ExternalImportStatement[] = parseExternalImports( - externalImportStatements - ); - - return externalImportsToResolve; -} \ No newline at end of file diff --git a/packages/schema/parse/src/extract/imports/registry.ts b/packages/schema/parse/src/extract/imports/registry.ts deleted file mode 100644 index d431f34993..0000000000 --- a/packages/schema/parse/src/extract/imports/registry.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { DocumentNode, parse } from "graphql"; -import fs from "fs"; -import { parseImportStatements } from "./parse"; -import { fetchExternalSchema } from "./utils"; - -export const getImportedAbisRegistry = async ( - schema: string, - schemaPath: string, -): Promise> => { - let importedAbiRegistry = new Map(); - - const { - externalImportStatements, - localImportStatements - } = parseImportStatements(schema, schemaPath); - - for await (const externalImportStatement of externalImportStatements) { - const schemaString = await fetchExternalSchema(externalImportStatement.uri); - importedAbiRegistry.set(externalImportStatement.uri, parse(schemaString)); - } - - for (const localImportStatement of localImportStatements) { - const localSchemaFileSource = fs.readFileSync(localImportStatement.path, "utf8"); - importedAbiRegistry.set(localImportStatement.path, parse(localSchemaFileSource)); - } - - return importedAbiRegistry; -} diff --git a/packages/schema/parse/src/extract/imports/utils.ts b/packages/schema/parse/src/extract/imports/utils.ts deleted file mode 100644 index fded680da6..0000000000 --- a/packages/schema/parse/src/extract/imports/utils.ts +++ /dev/null @@ -1,14 +0,0 @@ -export const countDuplicates = (array: string[]): Record => - array.reduce( - (a: Record, b: string) => ({ ...a, [b]: (a[b] || 0) + 1 }), - {} - ); - -export const getDuplicates = (array: string[]): string[] => { - const counts = countDuplicates(array); - return Object.keys(counts).filter((a) => counts[a] > 1); -}; - -export const fetchExternalSchema = async (uri: string): Promise => { - throw new Error("Not implemented") -} \ No newline at end of file diff --git a/packages/schema/parse/src/index.ts b/packages/schema/parse/src/index.ts index 5687646d68..02cb677217 100644 --- a/packages/schema/parse/src/index.ts +++ b/packages/schema/parse/src/index.ts @@ -1,4 +1,4 @@ -import { createAbi, isModuleType } from "./abi"; +import { createAbi } from "./abi"; import { AbiTransforms, transformAbi } from "./transform"; import { validators, SchemaValidatorBuilder } from "./validate"; @@ -27,7 +27,7 @@ interface ParserOptions { transforms?: AbiTransforms[]; } -const parseSchemaAndImports = async (schema: string, schemaPath: string): Promise<{ abi: Abi, imports: Map }> => { +export const parseSchemaAndImports = async (schema: string, schemaPath: string): Promise<{ abi: Abi, imports: Map }> => { const importsRegistry = await getImportedAbisRegistry(schema, schemaPath); let allUniqueDefinitions = new Map(); From 28bdf430ce8f6977d23f007482ab8c707178c8a4 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Sat, 11 Feb 2023 03:07:52 +0100 Subject: [PATCH 29/90] (chore): migrated extractors outside --- .../schema/parse/src/extract/directives.ts | 35 ---- .../schema/parse/src/extract/functions.ts | 71 ------- packages/schema/parse/src/extract/index.ts | 3 - packages/schema/parse/src/extract/object.ts | 45 ---- packages/schema/parse/src/extract/types.ts | 8 - packages/schema/parse/src/extract/utils.ts | 192 ------------------ packages/schema/parse/src/index.ts | 14 +- packages/schema/parse/src/validate/types.ts | 5 +- 8 files changed, 10 insertions(+), 363 deletions(-) delete mode 100644 packages/schema/parse/src/extract/directives.ts delete mode 100644 packages/schema/parse/src/extract/functions.ts delete mode 100644 packages/schema/parse/src/extract/index.ts delete mode 100644 packages/schema/parse/src/extract/object.ts delete mode 100644 packages/schema/parse/src/extract/types.ts delete mode 100644 packages/schema/parse/src/extract/utils.ts diff --git a/packages/schema/parse/src/extract/directives.ts b/packages/schema/parse/src/extract/directives.ts deleted file mode 100644 index 1bc68715c4..0000000000 --- a/packages/schema/parse/src/extract/directives.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { DirectiveNode, FieldDefinitionNode } from "graphql"; -import { MapType, UniqueDefKind } from "../definitions"; -import { parseMapString } from "./utils"; - -export const parseDirectivesInField = (node: FieldDefinitionNode, uniqueDefs: Map) => { - let map: MapType | undefined; - - if (node.directives) { - for (const dir of node.directives) { - switch (dir.name.value) { - case "annotate": - map = parseAnnotateDirective(dir, uniqueDefs); - break; - } - } - } - - return { - map, - } -} - -export const parseAnnotateDirective = (node: DirectiveNode, uniqueDefs: Map): MapType => { - const mapStringValue = node.arguments?.find((arg) => arg.name.value === "type")?.value; - - if (!mapStringValue || mapStringValue.kind !== "StringValue") { - throw new Error( - `Annotate directive: ${node.name.value} has invalid arguments` - ); - } - - const mapString = mapStringValue.value; - - return parseMapString(mapString, uniqueDefs) -} diff --git a/packages/schema/parse/src/extract/functions.ts b/packages/schema/parse/src/extract/functions.ts deleted file mode 100644 index df485b3734..0000000000 --- a/packages/schema/parse/src/extract/functions.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { ASTVisitor, FieldDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; -import { isModuleType } from "../abi"; -import { Abi, UniqueDefKind, ResultDef, FunctionDef, ArgumentDef } from "../definitions"; -import { parseAnnotateDirective, parseDirectivesInField } from "./directives"; -import { VisitorBuilder } from "./types"; -import { extractType } from "./utils"; - -export class FunctionsVisitorBuilder implements VisitorBuilder { - constructor(protected readonly uniqueDefs: Map) { } - - private extractMethodFromFieldDefNode(node: FieldDefinitionNode): FunctionDef { - const { map } = parseDirectivesInField(node, this.uniqueDefs); - - const resultDef: ResultDef = { - kind: "Result", - required: node.type.kind === "NonNullType", - type: map ?? extractType(node.type, this.uniqueDefs) - } - - const args: ArgumentDef[] = node.arguments?.map(inputNode => { - if (inputNode.directives) { - for (const dir of inputNode.directives) { - if (dir.name.value === "annotate") { - const map = parseAnnotateDirective(dir, this.uniqueDefs); - - return { - kind: "Argument", - required: inputNode.type.kind === "NonNullType", - name: inputNode.name.value, - type: map - } - } - } - } - - return { - kind: "Argument", - name: inputNode.name.value, - required: inputNode.type.kind === "NonNullType", - type: extractType(inputNode.type, this.uniqueDefs) - } - }) ?? []; - - const method: FunctionDef = { - kind: "Function", - result: resultDef, - args, - name: node.name.value - } - - return method; - } - - build(abi: Abi): ASTVisitor { - return { - enter: { - ObjectTypeDefinition: (objectDefNode: ObjectTypeDefinitionNode) => { - const nodeName = objectDefNode.name.value; - - if (!isModuleType(nodeName)) { - return; - } - - const methods = objectDefNode.fields?.map(node => this.extractMethodFromFieldDefNode(node)) - - abi.functions = methods ?? [] - }, - }, - }; - } -} \ No newline at end of file diff --git a/packages/schema/parse/src/extract/index.ts b/packages/schema/parse/src/extract/index.ts deleted file mode 100644 index f33f7ecb6a..0000000000 --- a/packages/schema/parse/src/extract/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./object"; -export * from "./enum"; -export * from "./functions"; \ No newline at end of file diff --git a/packages/schema/parse/src/extract/object.ts b/packages/schema/parse/src/extract/object.ts deleted file mode 100644 index 46a15f77eb..0000000000 --- a/packages/schema/parse/src/extract/object.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { ASTVisitor, FieldDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; -import { isModuleType } from "../abi"; -import { Abi, ObjectDef, PropertyDef, UniqueDefKind } from "../definitions"; -import { parseDirectivesInField } from "./directives"; -import { VisitorBuilder } from "./types"; -import { extractType } from "./utils"; - -export class ObjectVisitorBuilder implements VisitorBuilder { - constructor(protected readonly uniqueDefs: Map) { } - - protected extractPropertyDef(node: FieldDefinitionNode, uniqueDefs: Map): PropertyDef { - const { map } = parseDirectivesInField(node, uniqueDefs) - - return { - kind: "Property", - name: node.name.value, - required: node.type.kind === "NonNullType", - type: map ?? extractType(node.type, uniqueDefs) - } - } - - build(abi: Abi): ASTVisitor { - return { - enter: { - ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { - const typeName = node.name.value; - - // Skip non-custom types - if (isModuleType(typeName)) { - return; - } - - // Create a new TypeDefinition - const def: ObjectDef = { - kind: "Object", - name: typeName, - props: node.fields?.map(fieldNode => this.extractPropertyDef(fieldNode, this.uniqueDefs)) ?? [] - }; - - abi.objects = abi.objects ? [...abi.objects, def] : [def]; - }, - }, - }; - } -} \ No newline at end of file diff --git a/packages/schema/parse/src/extract/types.ts b/packages/schema/parse/src/extract/types.ts deleted file mode 100644 index 236e6c36f8..0000000000 --- a/packages/schema/parse/src/extract/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ASTVisitor } from "graphql"; -import { Abi, UniqueDefKind } from "../definitions"; - -export interface VisitorBuilder { - build(abi: Abi): ASTVisitor -} - -export type ExternalVisitorBuilder = (abi: Abi, uniqueDefs?: Map) => ASTVisitor \ No newline at end of file diff --git a/packages/schema/parse/src/extract/utils.ts b/packages/schema/parse/src/extract/utils.ts deleted file mode 100644 index bed23d725d..0000000000 --- a/packages/schema/parse/src/extract/utils.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { DocumentNode, TypeNode, visit } from "graphql"; -import { isModuleType, isScalarType } from "../abi/utils"; -import { UniqueDefKind, RefType, AnyType, MapKeyTypeName, mapKeyTypeSet, MapType, ScalarTypeName, ArrayType, ScalarType } from "../definitions"; - -export const extractType = (node: TypeNode, uniqueDefs: Map): AnyType => { - switch (node.kind) { - case "NonNullType": - return extractType(node.type, uniqueDefs) - case "ListType": - return { - kind: "Array", - item: { - required: node.type.kind === "NonNullType", - type: extractType(node.type, uniqueDefs) - } - } - case "NamedType": - if (isScalarType(node.name.value)) { - return { - kind: "Scalar", - scalar: node.name.value as ScalarTypeName - } - } - - return parseRef(node.name.value, uniqueDefs) - } -} - -export const parseRef = (refName: string, uniqueDefs: Map): RefType => { - const kind = uniqueDefs.get(refName); - - if (!kind) { - throw new Error(`Found ref to unknown definition '${refName}'`) - } - - return { - kind: "Ref", - ref_kind: kind, - ref_name: refName - } -} - -// TODO: Make sure map also works for imported types and modules - -const extractRequired = (typeString: string): { required: boolean, innerString: string } => { - const required = typeString.endsWith("!"); - - return { - required: required, - innerString: required ? typeString.slice(0, -1) : typeString - } -} - -const parseScalarString = (scalarString: string): ScalarType => { - return { - kind: "Scalar", - scalar: scalarString as ScalarTypeName, - } -} - -const parseRefString = (refString: string, uniqueDefs: Map): RefType => { - const ref_kind = uniqueDefs.get(refString); - - if (!ref_kind) { - throw new Error(`Found ref to unknown definition '${refString}'`) - } - - return { - kind: "Ref", - ref_kind, - ref_name: refString - } -} - -export const parseArrayString = (arrayString: string, uniqueDefs: Map): ArrayType => { - if (!arrayString.startsWith("[") || !arrayString.endsWith("]")) { - throw new Error(`Invalid array type: ${arrayString}`); - } - - const { required: isInnerTypeRequired, innerString: innerTypeString } = extractRequired(arrayString.slice(1, -1)); - - const trimmedInnerTypeString = innerTypeString.trim(); - let innerType: AnyType; - - if (isArray(trimmedInnerTypeString)) { - innerType = parseArrayString(trimmedInnerTypeString, uniqueDefs) - } else if (isMap(trimmedInnerTypeString)) { - innerType = parseMapString(trimmedInnerTypeString, uniqueDefs) - } else if (isScalarType(trimmedInnerTypeString)) { - innerType = parseScalarString(trimmedInnerTypeString) - } else { - innerType = parseRefString(trimmedInnerTypeString, uniqueDefs) - } - - const item = { - required: isInnerTypeRequired, - type: innerType - } - - return { - kind: "Array", - item - } -} - -export const parseMapString = (mapString: string, uniqueDefs: Map): MapType => { - if (!mapString.startsWith("Map<") || !mapString.endsWith(">")) { - throw new Error(`Invalid map value type: ${mapString}`); - } - - const innerMapString = mapString.slice(4, -1); - const trimmedInnerMapString = innerMapString.trim(); - - if (!trimmedInnerMapString.includes(",")) { - throw new Error(`Invalid map value type: ${mapString}`); - } - - const mapStringSplit = trimmedInnerMapString.split(","); - const trimmedKeyString = mapStringSplit[0].trim(); - const trimmedValString = mapStringSplit.slice(1).join(",").trim(); - - // If key contains !, remove it. It will always be required anyways - const innerKeyString = trimmedKeyString.endsWith("!") ? trimmedKeyString.slice(0, -1) : trimmedKeyString; - - if (!isMapKey(innerKeyString)) { - throw new Error( - `Found invalid map key type: ${innerKeyString} while parsing ${mapString}` - ); - } - - const key = { - kind: "Scalar" as const, - scalar: innerKeyString as MapKeyTypeName - } - - const { required: isValueRequired, innerString: innerValueString } = extractRequired(trimmedValString); - const trimmedInnerValueString = innerValueString.trim(); - - let valueType: AnyType; - - if (isArray(trimmedInnerValueString)) { - valueType = parseArrayString(trimmedInnerValueString, uniqueDefs) - } else if (isMap(trimmedInnerValueString)) { - valueType = parseMapString(trimmedInnerValueString, uniqueDefs) - } else if (isScalarType(trimmedInnerValueString)) { - valueType = parseScalarString(trimmedInnerValueString) - } else { - valueType = parseRef(trimmedInnerValueString, uniqueDefs) - } - - const value = { - type: valueType, - required: isValueRequired, - } - - return { - kind: "Map", - key, - value - } -} - -const isMap = (typeName: string): boolean => { - return typeName.startsWith("Map<") -} - -const isMapKey = (typeName: string): boolean => { - return typeName in mapKeyTypeSet; -} - -const isArray = (typeName: string): boolean => { - return typeName.startsWith("[") -} - -export const extractUniqueDefinitionNames = (document: DocumentNode): Map => { - const uniqueDefs = new Map(); - - visit(document, { - ObjectTypeDefinition: (node) => { - const name = node.name.value; - - if (!isModuleType(name)) { - uniqueDefs.set(name, "Object") - } - }, - EnumTypeDefinition: (node) => { - uniqueDefs.set(node.name.value, "Enum") - } - }); - - return uniqueDefs; -} diff --git a/packages/schema/parse/src/index.ts b/packages/schema/parse/src/index.ts index 02cb677217..91f292ea87 100644 --- a/packages/schema/parse/src/index.ts +++ b/packages/schema/parse/src/index.ts @@ -8,13 +8,13 @@ import { ExternalVisitorBuilder, VisitorBuilder } from "./extract/types"; import { ObjectVisitorBuilder } from "./extract"; import { EnumVisitorBuilder } from "./extract"; import { FunctionsVisitorBuilder } from "./extract"; -import { extractUniqueDefinitionNames } from "./extract/utils"; -import { getImportedAbisRegistry } from "./extract/imports/registry"; +import { SchemaParser } from "./types"; export * from "./abi"; export * from "./transform"; export * from "./validate"; export * from "./header"; +export * from "./types"; interface ParserOptions { // Disable schema validation @@ -27,22 +27,22 @@ interface ParserOptions { transforms?: AbiTransforms[]; } -export const parseSchemaAndImports = async (schema: string, schemaPath: string): Promise<{ abi: Abi, imports: Map }> => { - const importsRegistry = await getImportedAbisRegistry(schema, schemaPath); +export const parseSchemaAndImports = async (schema: string, schemaPath: string, parser: SchemaParser): Promise<{ abi: Abi, imports: Map }> => { + const importsRegistry = await parser.getImportedSchemasTable(schema, schemaPath); let allUniqueDefinitions = new Map(); for (const importedAbi of importsRegistry.values()) { - allUniqueDefinitions = new Map([...allUniqueDefinitions, ...extractUniqueDefinitionNames(importedAbi)]); + allUniqueDefinitions = new Map([...allUniqueDefinitions, ...parser.getUniqueDefinitionsTable(importedAbi)]); } const importedAbis = new Map(); for (const [importPath, importedAbi] of importsRegistry.entries()) { - importedAbis.set(importPath, transformSchemaToAbi(importedAbi, allUniqueDefinitions)); + importedAbis.set(importPath, parser.parse(importedAbi, allUniqueDefinitions)); } return { - abi: transformSchemaToAbi(parse(schema), allUniqueDefinitions), + abi: parser.parse(schema, allUniqueDefinitions), imports: importedAbis } } diff --git a/packages/schema/parse/src/validate/types.ts b/packages/schema/parse/src/validate/types.ts index 908bbfd9c2..653968ab97 100644 --- a/packages/schema/parse/src/validate/types.ts +++ b/packages/schema/parse/src/validate/types.ts @@ -1,4 +1,4 @@ -import { isScalarType, isModuleType, scalarTypeNames } from ".."; +import { isScalarType, isModuleType } from ".."; import { SchemaValidator } from "./"; import { @@ -15,6 +15,7 @@ import { UnionTypeDefinitionNode, } from "graphql"; import { getSchemaCycles } from "@dorgjelli/graphql-schema-cycles"; +import { scalarTypeSet } from "../definitions"; const operationTypeNames = ["Mutation", "Subscription", "Query"]; @@ -63,7 +64,7 @@ export const getTypeDefinitionsValidator = (): SchemaValidator => { ScalarTypeDefinition: (node: ScalarTypeDefinitionNode) => { if (node.name.value !== "Map" && !isScalarType(node.name.value)) { throw Error( - `Custom scalar types are not supported. Found: "${node.name.value}". Supported scalars: ${scalarTypeNames}` + `Custom scalar types are not supported. Found: "${node.name.value}". Supported scalars: ${Object.keys(scalarTypeSet).join(", ")}` ); } }, From 72a8e8c1b3055138caee03651ad21dc174305a0e Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Sun, 12 Feb 2023 00:54:41 +0100 Subject: [PATCH 30/90] (chore): migrated GraphQL AST validation and tests outside --- .../parse/src/__tests__/extractor.spec.ts | 381 ------------------ packages/schema/parse/src/index.ts | 80 +--- .../parse/src/transform/toGraphQLType.ts | 44 -- .../schema/parse/src/validate/directives.ts | 265 ------------ packages/schema/parse/src/validate/index.ts | 18 - packages/schema/parse/src/validate/types.ts | 236 ----------- 6 files changed, 8 insertions(+), 1016 deletions(-) delete mode 100644 packages/schema/parse/src/__tests__/extractor.spec.ts delete mode 100644 packages/schema/parse/src/transform/toGraphQLType.ts delete mode 100644 packages/schema/parse/src/validate/directives.ts delete mode 100644 packages/schema/parse/src/validate/index.ts delete mode 100644 packages/schema/parse/src/validate/types.ts diff --git a/packages/schema/parse/src/__tests__/extractor.spec.ts b/packages/schema/parse/src/__tests__/extractor.spec.ts deleted file mode 100644 index cd74149d81..0000000000 --- a/packages/schema/parse/src/__tests__/extractor.spec.ts +++ /dev/null @@ -1,381 +0,0 @@ -import { parse, visit, visitInParallel } from "graphql" -import { Abi, EnumDef, FunctionDef, ObjectDef } from "../definitions" -import { EnumVisitorBuilder, FunctionsVisitorBuilder, ObjectVisitorBuilder } from "../extract" - -describe("Extractors", () => { - describe("Enums", () => { - it("Extracts simple enum defs", () => { - const schema = ` - enum Color { - RED - GREEN - BLUE - } - - enum Size { - SMALL - MEDIUM - } - ` - - const astNode = parse(schema); - const abi: Abi = { - version: "0.2" - } - const enumExtractor = new EnumVisitorBuilder().build(abi); - - const expected: EnumDef[] = [ - { - name: "Color", - kind: "Enum", - constants: ["RED", "GREEN", "BLUE"] - }, - { - name: "Size", - kind: "Enum", - constants: ["SMALL", "MEDIUM"] - } - ] - - visit(astNode, visitInParallel([enumExtractor])) - - expect(abi.enums).toMatchObject(expected) - }) - }) - describe("Objects", () => { - it("Extracts simple object defs", () => { - const schema = ` - type Nested { - prop: UInt8 - } - ` - - const astNode = parse(schema); - const abi: Abi = { - version: "0.2" - } - - const objectExtractor = new ObjectVisitorBuilder(new Map()).build(abi); - - const expected: ObjectDef[] = [ - { - kind: "Object", - name: "Nested", - props: [ - { - kind: "Property", - name: "prop", - type: { - kind: "Scalar", - scalar: "UInt8" - }, - required: false - }, - ] - } - ] - - visit(astNode, visitInParallel([objectExtractor])) - - expect(abi.objects).toMatchObject(expected) - }) - - it("Extracts objects with ref types", () => { - const schema = ` - type Custom { - prop1: String! - nested: Nested - bar: Bar - } - - type Nested { - prop: UInt8 - } - ` - - const astNode = parse(schema); - const abi: Abi = { - version: "0.2" - } - const uniqueDefs = new Map([ - ["Custom", "Object" as const], - ["Nested", "Object" as const], - ["Bar", "Enum" as const] - ]) - const objectExtractor = new ObjectVisitorBuilder(uniqueDefs).build(abi); - - const expected: ObjectDef[] = [ - { - kind: "Object", - name: "Custom", - props: [ - { - kind: "Property", - name: "prop1", - type: { - kind: "Scalar", - scalar: "String" - }, - required: true - }, - { - kind: "Property", - name: "nested", - type: { - kind: "Ref", - ref_name: "Nested", - ref_kind: "Object" - }, - required: false - }, - { - kind: "Property", - name: "bar", - type: { - kind: "Ref", - ref_name: "Bar", - ref_kind: "Enum" - }, - required: false - } - ] - }, - { - kind: "Object", - name: "Nested", - props: [ - { - kind: "Property", - name: "prop", - type: { - kind: "Scalar", - scalar: "UInt8" - }, - required: false - }, - ] - } - ] - - visit(astNode, visitInParallel([objectExtractor])) - - expect(abi.objects).toMatchObject(expected) - }) - - it("Extracts objects with array types", () => { - const schema = ` - type Custom { - prop1: String! - nested: [Nested!]! - } - - type Nested { - prop: [UInt8] - } - ` - - const astNode = parse(schema); - const abi: Abi = { - version: "0.2" - } - const uniqueDefs = new Map([ - ["Custom", "Object" as const], - ["Nested", "Object" as const] - ]) - const objectExtractor = new ObjectVisitorBuilder(uniqueDefs).build(abi); - - const expected: ObjectDef[] = [ - { - kind: "Object", - name: "Custom", - props: [ - { - kind: "Property", - name: "prop1", - type: { - kind: "Scalar", - scalar: "String" - }, - required: true - }, - { - kind: "Property", - name: "nested", - type: { - kind: "Array", - item: { - required: true, - type: { - kind: "Ref", - ref_name: "Nested", - ref_kind: "Object" - } - } - }, - required: true - } - ] - }, - { - kind: "Object", - name: "Nested", - props: [ - { - kind: "Property", - name: "prop", - type: { - kind: "Array", - item: { - required: false, - type: { - kind: "Scalar", - scalar: "UInt8" - } - - } - }, - required: false - } - ] - } - ] - - visit(astNode, visitInParallel([objectExtractor])) - - expect(abi.objects).toMatchObject(expected) - }) - - it("Extracts objects with map types", () => { - const schema = ` - type Custom { - nested: Map! @annotate(type: "Map") - } - - type Nested { - prop: Map @annotate(type: "Map") - } - ` - - const astNode = parse(schema); - const abi: Abi = { - version: "0.2" - } - const uniqueDefs = new Map([ - ["Custom", "Object" as const], - ["Nested", "Object" as const] - ]) - const objectExtractor = new ObjectVisitorBuilder(uniqueDefs).build(abi); - - const expected: ObjectDef[] = [ - { - kind: "Object", - name: "Custom", - props: [ - { - kind: "Property", - name: "nested", - type: { - kind: "Map", - key: { - kind: "Scalar", - scalar: "String" - }, - value: { - required: true, - type: { - kind: "Array", - item: { - required: true, - type: { - kind: "Ref", - ref_name: "Nested", - ref_kind: "Object" - } - } - } - } - }, - required: true - } - ] - }, - { - kind: "Object", - name: "Nested", - props: [ - { - kind: "Property", - name: "prop", - type: { - kind: "Map", - key: { - kind: "Scalar", - scalar: "UInt8" - }, - value: { - required: false, - type: { - kind: "Scalar", - scalar: "UInt8" - } - } - }, - required: false - } - ] - } - ] - - visit(astNode, visitInParallel([objectExtractor])) - - expect(abi.objects).toMatchObject(expected) - }) - }) - describe("Functions", () => { - it("Extract simple functions defs", () => { - const schema = ` - type Module { - fooFunc( - arg: String! - ): String! - } - ` - - const astNode = parse(schema); - const abi: Abi = { - version: "0.2" - } - const functionsExtractor = new FunctionsVisitorBuilder(new Map()).build(abi); - - const expected: FunctionDef[] = [ - { - kind: "Function", - name: "fooFunc", - args: [ - { - kind: "Argument", - name: "arg", - type: { - kind: "Scalar", - scalar: "String" - }, - required: true, - } - ], - result: { - kind: "Result", - type: { - kind: "Scalar", - scalar: "String" - }, - required: true - } - } - ] - - visit(astNode, visitInParallel([functionsExtractor])) - - expect(abi.functions).toMatchObject(expected) - }) - }) -}) \ No newline at end of file diff --git a/packages/schema/parse/src/index.ts b/packages/schema/parse/src/index.ts index 91f292ea87..1c7b74321b 100644 --- a/packages/schema/parse/src/index.ts +++ b/packages/schema/parse/src/index.ts @@ -1,33 +1,21 @@ -import { createAbi } from "./abi"; import { AbiTransforms, transformAbi } from "./transform"; -import { validators, SchemaValidatorBuilder } from "./validate"; -import { ASTVisitor, DocumentNode, parse, visit, visitInParallel } from "graphql"; import { Abi, UniqueDefKind } from "./definitions"; -import { ExternalVisitorBuilder, VisitorBuilder } from "./extract/types"; -import { ObjectVisitorBuilder } from "./extract"; -import { EnumVisitorBuilder } from "./extract"; -import { FunctionsVisitorBuilder } from "./extract"; import { SchemaParser } from "./types"; export * from "./abi"; export * from "./transform"; -export * from "./validate"; export * from "./header"; export * from "./types"; interface ParserOptions { // Disable schema validation noValidate?: boolean; - // Use custom validators - validators?: SchemaValidatorBuilder[]; - // Use custom extractors - extractors?: ExternalVisitorBuilder[]; // Use custom transformations transforms?: AbiTransforms[]; } -export const parseSchemaAndImports = async (schema: string, schemaPath: string, parser: SchemaParser): Promise<{ abi: Abi, imports: Map }> => { +export const parseSchemaAndImports = async (schema: string, schemaPath: string, parser: SchemaParser, options: ParserOptions = {}): Promise<{ abi: Abi, imports: Map }> => { const importsRegistry = await parser.getImportedSchemasTable(schema, schemaPath); let allUniqueDefinitions = new Map(); @@ -41,71 +29,19 @@ export const parseSchemaAndImports = async (schema: string, schemaPath: string, importedAbis.set(importPath, parser.parse(importedAbi, allUniqueDefinitions)); } - return { - abi: parser.parse(schema, allUniqueDefinitions), - imports: importedAbis - } -} - -function transformSchemaToAbi( - astNode: DocumentNode, - uniqueDefinitions: Map, - options: ParserOptions = {} -): Abi { - - const defaultExtractors: VisitorBuilder[] = [ - new ObjectVisitorBuilder(uniqueDefinitions), - new EnumVisitorBuilder(), - new FunctionsVisitorBuilder(uniqueDefinitions) - ] - - // Validate GraphQL Schema - if (!options.noValidate) { - const validates = options.validators || validators; - validate(astNode, validates); - } - - // Extract & Build Abi - let info = createAbi(); - - const extracts = options.extractors?.map(extractorBuilder => extractorBuilder(info, uniqueDefinitions)) ?? defaultExtractors.map(e => e.build(info)); - extract(astNode, extracts); + // TODO: should this happen before or after linking? + // TODO: where does validation happen? + let abi = parser.parse(schema, allUniqueDefinitions); if (options && options.transforms) { for (const transform of options.transforms) { - info = transformAbi(info, transform); + abi = transformAbi(abi, transform); } } return { - version: "0.2", - objects: info.objects?.length ? info.objects : undefined, - functions: info.functions?.length ? info.functions : undefined, - enums: info.enums?.length ? info.enums : undefined, - imports: info.imports?.length ? info.imports : undefined, - }; -} - -const validate = ( - astNode: DocumentNode, - validators: SchemaValidatorBuilder[] -) => { - const allValidators = validators.map((getValidator) => getValidator()); - const allVisitors = allValidators.map((x) => x.visitor); - const allCleanup = allValidators.map((x) => x.cleanup); - - visit(astNode, visitInParallel(allVisitors)); - - for (const cleanup of allCleanup) { - if (cleanup) { - cleanup(astNode); - } + abi, + imports: importedAbis } -}; +} -const extract = ( - astNode: DocumentNode, - extractors: ASTVisitor[] -) => { - visit(astNode, visitInParallel(extractors)); -}; diff --git a/packages/schema/parse/src/transform/toGraphQLType.ts b/packages/schema/parse/src/transform/toGraphQLType.ts deleted file mode 100644 index 7227606526..0000000000 --- a/packages/schema/parse/src/transform/toGraphQLType.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { AnyType, ArgumentDef, EnumDef, FunctionDef, ObjectDef, PropertyDef } from "../definitions"; - -function applyRequired(type: string, required: boolean | undefined): string { - return `${type}${required ? "!" : ""}`; -} - -export function toGraphQL(def: ObjectDef | FunctionDef | ArgumentDef | EnumDef | PropertyDef | AnyType, required: boolean): string { - switch (def.kind) { - case "Object": - return applyRequired(def.name, required); - case "Scalar": - return applyRequired(def.scalar, required); - case "Enum": - return applyRequired(def.name, required);; - case "Property": - return toGraphQL(def.type, def.required); - case "Array": - return applyRequired( - `[${toGraphQL(def.item.type, def.item.required)}]`, - required - ); - case "Map": { - return `Map<${def.key}!, ${toGraphQL(def.value.type, def.value.required)}>` - } - case "Function": { - - const result = `${def.name}( - ${(def.args || []) - .map((arg) => `${arg.name}: ${toGraphQL(arg.type, arg.required)}`) - .join("\n ")} -): ${toGraphQL(def.result.type, def.result.required)}`; - return result; - } - default: - throw Error( - `toGraphQL: Unrecognized Definition or Type.\n${JSON.stringify( - def, - null, - 2 - )}` - ); - } -} - diff --git a/packages/schema/parse/src/validate/directives.ts b/packages/schema/parse/src/validate/directives.ts deleted file mode 100644 index 7d1d9d556a..0000000000 --- a/packages/schema/parse/src/validate/directives.ts +++ /dev/null @@ -1,265 +0,0 @@ -import { SchemaValidator } from "."; -import { isImportedModuleType, isModuleType } from ".."; - -import { DirectiveNode, ASTNode, ObjectTypeDefinitionNode } from "graphql"; -import { ImportedDefinition } from "@polywrap/wrap-manifest-types-js"; - -export const getSupportedDirectivesValidator = (): SchemaValidator => { - const supportedDirectives = [ - "imported", - "imports", - "annotate", - ]; - const unsupportedUsages: string[] = []; - - return { - visitor: { - enter: { - Directive: (node: DirectiveNode) => { - const name = node.name.value; - - if (!supportedDirectives.includes(name)) { - unsupportedUsages.push(name); - } - }, - }, - }, - cleanup: () => { - if (unsupportedUsages.length) { - throw new Error( - `Found the following usages of unsupported directives:${unsupportedUsages.map( - (u) => `\n@${u}` - )}` - ); - } - }, - }; -}; - -export const getEnvDirectiveValidator = (): SchemaValidator => { - let currentType: string | undefined; - - return { - visitor: { - enter: { - ObjectTypeDefinition: (node) => { - currentType = node.name.value; - }, - FieldDefinition: (node) => { - const envDirective = node.directives?.find( - (d) => d.name.value === "env" - ); - - if ( - envDirective && - currentType && - !isModuleType(currentType) && - !isImportedModuleType(currentType) - ) { - throw new Error( - `@env directive should only be used on Module method definitions. Found on field '${node.name.value}' of type '${currentType}'` - ); - } - }, - }, - leave: { - ObjectTypeDefinition: () => { - currentType = undefined; - }, - }, - }, - }; -}; - -export const getImportsDirectiveValidator = (): SchemaValidator => { - let isInsideObjectTypeDefinition = false; - - const ObjectTypeDefinition = (node: ObjectTypeDefinitionNode) => { - isInsideObjectTypeDefinition = true; - const badUsageLocations: string[] = []; - - const importsAllowedObjectTypes = ["Module"]; - const directives = - node.directives && - node.directives.map((directive) => directive.name.value); - - if ( - directives && - directives.includes("imports") && - !importsAllowedObjectTypes.includes(node.name.value) - ) { - badUsageLocations.push(node.name.value); - } - - if (badUsageLocations.length) { - throw new Error( - `@imports directive should only be used on Module type definitions, ` + - `but it is being used on the following ObjectTypeDefinitions:${badUsageLocations.map( - (b) => `\n${b}` - )}` - ); - } - }; - - const Directive = ( - node: DirectiveNode, - key: string | number | undefined, - parent: ASTNode | undefined, - path: ReadonlyArray - ) => { - if (node.name.value !== "imports") { - return; - } - - if (!isInsideObjectTypeDefinition) { - throw new Error( - `@imports directive should only be used on Module type definitions, ` + - `but it is being used in the following location: ${path.join(" -> ")}` - ); - } - - const args = node.arguments || []; - const typesArgument = args.find((arg) => arg.name.value === "types"); - - if (!args.length || !typesArgument) { - throw new Error( - `@imports directive requires argument 'types' of type [String!]!` - ); - } - - if (args.length > 1) { - throw new Error( - `@imports directive takes only one argument 'types', but found: ${args - .filter((arg) => arg.name.value !== "types") - .map((arg) => `\n- ${arg.name.value}`)}` - ); - } - - if (typesArgument.value.kind === "ListValue") { - const values = typesArgument.value.values; - - if (!values.length) { - throw new Error( - `@imports directive's 'types' argument of type [String!]! requires at least one value` - ); - } - - const nonStringValues = values.filter( - (value) => value.kind !== "StringValue" - ); - - if (nonStringValues.length) { - throw new Error( - `@imports directive's 'types' List values must be of type String, but found: \n${nonStringValues.map( - (nonStringValue) => `\n -${nonStringValue.kind}` - )}` - ); - } - } - }; - - return { - visitor: { - enter: ( - node: ASTNode, - key: string | number | undefined, - parent: ASTNode | undefined, - path: ReadonlyArray - ) => { - if (node.kind === "ObjectTypeDefinition") { - ObjectTypeDefinition(node as ObjectTypeDefinitionNode); - } else if (node.kind === "Directive") { - Directive(node as DirectiveNode, key, parent, path); - } else if ( - node.kind !== "NamedType" && - node.kind !== "Name" && - node.kind !== "StringValue" - ) { - isInsideObjectTypeDefinition = false; - } - }, - }, - }; -}; - -export const getImportedDirectiveValidator = (): SchemaValidator => { - let isInsideObjectOrEnumTypeDefinition = false; - - const Directive = ( - node: DirectiveNode, - key: string | number | undefined, - parent: ASTNode | undefined, - path: ReadonlyArray - ) => { - if (node.name.value !== "imported") { - return; - } - - if (!isInsideObjectOrEnumTypeDefinition) { - throw new Error( - `@imported directive should only be used on object or enum type definitions, ` + - `but it is being used in the following location: ${path.join(" -> ")}` - ); - } - - const imported: ImportedDefinition = { - uri: "", - namespace: "", - nativeType: "", - }; - - const args = node.arguments || []; - const expectedArguments = Object.keys(imported); - const actualArguments = args.map((arg) => arg.name.value); - - const missingArguments = expectedArguments.filter( - (expected) => !actualArguments.includes(expected) - ); - - if (missingArguments.length) { - throw new Error( - `@imported directive is missing the following arguments:${missingArguments.map( - (arg) => `\n- ${arg}` - )}` - ); - } - - const extraArguments = actualArguments.filter( - (actual) => !expectedArguments.includes(actual) - ); - - if (extraArguments.length) { - throw new Error( - `@imported directive takes only 3 arguments: ${expectedArguments.join( - ", " - )}. But found:${extraArguments.map((arg) => `\n- ${arg}`)}` - ); - } - }; - - return { - visitor: { - enter: ( - node: ASTNode, - key: string | number | undefined, - parent: ASTNode | undefined, - path: ReadonlyArray - ) => { - if (node.kind === "Directive") { - Directive(node as DirectiveNode, key, parent, path); - } else if ( - node.kind === "ObjectTypeDefinition" || - node.kind === "EnumTypeDefinition" - ) { - isInsideObjectOrEnumTypeDefinition = true; - } else if ( - node.kind !== "NamedType" && - node.kind !== "Name" && - node.kind !== "StringValue" - ) { - isInsideObjectOrEnumTypeDefinition = false; - } - }, - }, - }; -}; diff --git a/packages/schema/parse/src/validate/index.ts b/packages/schema/parse/src/validate/index.ts deleted file mode 100644 index b901a1e2a9..0000000000 --- a/packages/schema/parse/src/validate/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as directiveValidators from "./directives"; -import * as typeValidators from "./types"; - -import { ASTVisitor, DocumentNode } from "graphql"; - -export type SchemaValidator = { - visitor: ASTVisitor; - cleanup?: (documentNode: DocumentNode) => void; -}; - -export type SchemaValidatorBuilder = () => SchemaValidator; - -export const validators: SchemaValidatorBuilder[] = [ - ...Object.values(directiveValidators).filter((x) => typeof x === "function"), - ...Object.values(typeValidators).filter((x) => typeof x === "function"), -]; - -export { directiveValidators, typeValidators }; diff --git a/packages/schema/parse/src/validate/types.ts b/packages/schema/parse/src/validate/types.ts deleted file mode 100644 index 653968ab97..0000000000 --- a/packages/schema/parse/src/validate/types.ts +++ /dev/null @@ -1,236 +0,0 @@ -import { isScalarType, isModuleType } from ".."; -import { SchemaValidator } from "./"; - -import { - DirectiveNode, - DocumentNode, - EnumTypeDefinitionNode, - InputObjectTypeDefinitionNode, - InputValueDefinitionNode, - InterfaceTypeDefinitionNode, - NamedTypeNode, - ObjectTypeDefinitionNode, - ScalarTypeDefinitionNode, - StringValueNode, - UnionTypeDefinitionNode, -} from "graphql"; -import { getSchemaCycles } from "@dorgjelli/graphql-schema-cycles"; -import { scalarTypeSet } from "../definitions"; - -const operationTypeNames = ["Mutation", "Subscription", "Query"]; - -export const getTypeDefinitionsValidator = (): SchemaValidator => { - const objectTypes: Record = {}; - - return { - visitor: { - enter: { - // No Interfaces - InterfaceTypeDefinition: (node: InterfaceTypeDefinitionNode) => { - throw Error( - "Interface type definitions are not supported.\n" + - `Found: interface ${node.name.value} { ... }\n` + - `Please Use: type ${node.name.value} { ... }` - ); - }, - // No Inputs - InputObjectTypeDefinition: (node: InputObjectTypeDefinitionNode) => { - throw Error( - "Input type definitions are not supported.\n" + - `Found: input ${node.name.value} { ... }\n` + - `Please Use: type ${node.name.value} { ... }` - ); - }, - ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { - // No Operation types - if (operationTypeNames.includes(node.name.value)) { - throw Error( - `OperationType names (${operationTypeNames.join( - ", " - )}) are not allowed.` - ); - } - - // No duplicates - if (objectTypes[node.name.value]) { - throw Error( - `Duplicate object type definition found: ${node.name.value}` - ); - } - - objectTypes[node.name.value] = true; - }, - // No New Scalars - ScalarTypeDefinition: (node: ScalarTypeDefinitionNode) => { - if (node.name.value !== "Map" && !isScalarType(node.name.value)) { - throw Error( - `Custom scalar types are not supported. Found: "${node.name.value}". Supported scalars: ${Object.keys(scalarTypeSet).join(", ")}` - ); - } - }, - // No Unions - UnionTypeDefinition: (node: UnionTypeDefinitionNode) => { - throw Error( - "Union type definitions are not supported.\n" + - `Found: union ${node.name.value}` - ); - }, - }, - }, - }; -}; - -export const getPropertyTypesValidator = (): SchemaValidator => { - let currentObject: string | undefined; - let currentImportType: string | undefined; - let currentField: string | undefined; - const objectTypes: Record = {}; - const enumTypes: Record = {}; - const duplicateFields: Record> = {}; - const fieldTypes: { - object: string; - field: string; - type: string; - }[] = []; - - return { - visitor: { - enter: { - ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { - currentObject = node.name.value; - objectTypes[node.name.value] = true; - - if (node.fields) { - const fields: Record = {}; - - for (const field of node.fields) { - if (fields[field.name.value]) { - if (!duplicateFields[node.name.value]) { - duplicateFields[node.name.value] = {}; - } - - duplicateFields[node.name.value][field.name.value] = true; - } - - fields[field.name.value] = true; - } - } - }, - EnumTypeDefinition: (node: EnumTypeDefinitionNode) => { - enumTypes[node.name.value] = true; - }, - Directive: (node: DirectiveNode) => { - if (node.name.value === "imported") { - // save the imported native type name - if (node.arguments) { - const nativeType = node.arguments.find( - (arg) => arg.name.value === "nativeType" - ); - - if (nativeType) { - currentImportType = (nativeType.value as StringValueNode).value; - } - } - } - }, - FieldDefinition: (node) => { - currentField = node.name.value; - }, - NamedType: (node: NamedTypeNode) => { - if (currentObject && currentField) { - const namedType = node.name.value; - - fieldTypes.push({ - object: currentObject, - field: currentField, - type: namedType, - }); - } - }, - InputValueDefinition: (node: InputValueDefinitionNode) => { - const typeName = currentImportType - ? currentImportType - : currentObject; - if (typeName && !isModuleType(typeName)) { - // Arguments not supported on non-module types - throw Error( - `Methods can only be defined on module types (Module).\n` + - `Found: type ${typeName} { ${currentField}(${node.name.value}) }` - ); - } - }, - }, - leave: { - ObjectTypeDefinition: () => { - currentObject = undefined; - currentImportType = undefined; - }, - FieldDefinition: () => { - currentField = undefined; - }, - }, - }, - cleanup: () => { - // Ensure all property types are either a - // supported scalar, enum or an object type definition - for (const field of fieldTypes) { - if ( - !isScalarType(field.type) && - !objectTypes[field.type] && - !enumTypes[field.type] && - field.type !== "Map" - ) { - throw Error( - `Unknown property type found: type ${field.object} { ${field.field}: ${field.type} }` - ); - } - } - - const objectTypeNames = Object.keys(duplicateFields); - - if (objectTypeNames.length) { - throw new Error( - `Found duplicate fields in the following objects:${objectTypeNames.map( - (object) => - `\ntype ${object} => ${JSON.stringify( - Object.keys(duplicateFields[object]) - )}` - )}` - ); - } - }, - }; -}; - -export function getCircularDefinitionsValidator(): SchemaValidator { - const ignoreTypeNames: string[] = []; - - return { - visitor: { - enter: { - ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { - if ( - node.name.value === "Module" || - node.name.value.endsWith("_Module") - ) { - ignoreTypeNames.push(node.name.value); - } - }, - }, - }, - cleanup: (documentNode: DocumentNode) => { - const { cycleStrings, foundCycle } = getSchemaCycles(documentNode, { - ignoreTypeNames, - allowOnNullableFields: true, - }); - - if (foundCycle) { - throw Error( - `Graphql cycles are not supported. \nFound: ${cycleStrings.map( - (cycle) => `\n- ${cycle}` - )}` - ); - } - }, - }; -} From fec106d661233dc85ed0a9e625fc5175b61464d3 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Sun, 12 Feb 2023 00:54:56 +0100 Subject: [PATCH 31/90] (chore): completely removed GraphQL dependency from schema package --- packages/schema/parse/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/schema/parse/package.json b/packages/schema/parse/package.json index 18bf0a8823..1c7682318d 100644 --- a/packages/schema/parse/package.json +++ b/packages/schema/parse/package.json @@ -20,8 +20,7 @@ }, "dependencies": { "@dorgjelli/graphql-schema-cycles": "1.1.4", - "@polywrap/wrap-manifest-types-js": "0.10.0-pre.5", - "graphql": "15.5.0" + "@polywrap/wrap-manifest-types-js": "0.10.0-pre.5" }, "devDependencies": { "@polywrap/test-cases": "0.10.0-pre.5", From 9f16d622c98bcdeedfb42b266bcce8d510ea2c77 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 13 Feb 2023 10:52:52 +0100 Subject: [PATCH 32/90] (chore): renamed compose -> linking --- packages/schema/{compose => linking}/README.md | 0 packages/schema/{compose => linking}/jest.config.js | 0 packages/schema/{compose => linking}/package.json | 2 +- .../{compose => linking}/src/__tests__/index.ts | 0 .../src/__tests__/test-cases.spec.ts | 2 +- packages/schema/{compose => linking}/src/index.ts | 0 packages/schema/{compose => linking}/src/render.ts | 0 packages/schema/{compose => linking}/src/resolve.ts | 0 packages/schema/{compose => linking}/src/resolver.ts | 0 .../src/templates/header.mustache.ts | 0 .../src/templates/schema.mustache.ts | 0 packages/schema/{compose => linking}/src/types.ts | 11 +---------- .../schema/{compose => linking}/tsconfig.build.json | 0 packages/schema/{compose => linking}/tsconfig.json | 0 14 files changed, 3 insertions(+), 12 deletions(-) rename packages/schema/{compose => linking}/README.md (100%) rename packages/schema/{compose => linking}/jest.config.js (100%) rename packages/schema/{compose => linking}/package.json (96%) rename packages/schema/{compose => linking}/src/__tests__/index.ts (100%) rename packages/schema/{compose => linking}/src/__tests__/test-cases.spec.ts (96%) rename packages/schema/{compose => linking}/src/index.ts (100%) rename packages/schema/{compose => linking}/src/render.ts (100%) rename packages/schema/{compose => linking}/src/resolve.ts (100%) rename packages/schema/{compose => linking}/src/resolver.ts (100%) rename packages/schema/{compose => linking}/src/templates/header.mustache.ts (100%) rename packages/schema/{compose => linking}/src/templates/schema.mustache.ts (100%) rename packages/schema/{compose => linking}/src/types.ts (66%) rename packages/schema/{compose => linking}/tsconfig.build.json (100%) rename packages/schema/{compose => linking}/tsconfig.json (100%) diff --git a/packages/schema/compose/README.md b/packages/schema/linking/README.md similarity index 100% rename from packages/schema/compose/README.md rename to packages/schema/linking/README.md diff --git a/packages/schema/compose/jest.config.js b/packages/schema/linking/jest.config.js similarity index 100% rename from packages/schema/compose/jest.config.js rename to packages/schema/linking/jest.config.js diff --git a/packages/schema/compose/package.json b/packages/schema/linking/package.json similarity index 96% rename from packages/schema/compose/package.json rename to packages/schema/linking/package.json index 239afe0b6d..4b031b89bc 100644 --- a/packages/schema/compose/package.json +++ b/packages/schema/linking/package.json @@ -1,5 +1,5 @@ { - "name": "@polywrap/schema-compose", + "name": "@polywrap/schema-linking", "description": "Polywrap Schema Composition", "version": "0.10.0-pre.5", "license": "MIT", diff --git a/packages/schema/compose/src/__tests__/index.ts b/packages/schema/linking/src/__tests__/index.ts similarity index 100% rename from packages/schema/compose/src/__tests__/index.ts rename to packages/schema/linking/src/__tests__/index.ts diff --git a/packages/schema/compose/src/__tests__/test-cases.spec.ts b/packages/schema/linking/src/__tests__/test-cases.spec.ts similarity index 96% rename from packages/schema/compose/src/__tests__/test-cases.spec.ts rename to packages/schema/linking/src/__tests__/test-cases.spec.ts index b6d9797022..7e61453153 100644 --- a/packages/schema/compose/src/__tests__/test-cases.spec.ts +++ b/packages/schema/linking/src/__tests__/test-cases.spec.ts @@ -1,4 +1,4 @@ -import { composeSchema, renderSchema } from "../"; +import { composeSchema, renderSchema } from ".."; import { fetchTestCases } from "./index"; import { diff } from "jest-diff"; diff --git a/packages/schema/compose/src/index.ts b/packages/schema/linking/src/index.ts similarity index 100% rename from packages/schema/compose/src/index.ts rename to packages/schema/linking/src/index.ts diff --git a/packages/schema/compose/src/render.ts b/packages/schema/linking/src/render.ts similarity index 100% rename from packages/schema/compose/src/render.ts rename to packages/schema/linking/src/render.ts diff --git a/packages/schema/compose/src/resolve.ts b/packages/schema/linking/src/resolve.ts similarity index 100% rename from packages/schema/compose/src/resolve.ts rename to packages/schema/linking/src/resolve.ts diff --git a/packages/schema/compose/src/resolver.ts b/packages/schema/linking/src/resolver.ts similarity index 100% rename from packages/schema/compose/src/resolver.ts rename to packages/schema/linking/src/resolver.ts diff --git a/packages/schema/compose/src/templates/header.mustache.ts b/packages/schema/linking/src/templates/header.mustache.ts similarity index 100% rename from packages/schema/compose/src/templates/header.mustache.ts rename to packages/schema/linking/src/templates/header.mustache.ts diff --git a/packages/schema/compose/src/templates/schema.mustache.ts b/packages/schema/linking/src/templates/schema.mustache.ts similarity index 100% rename from packages/schema/compose/src/templates/schema.mustache.ts rename to packages/schema/linking/src/templates/schema.mustache.ts diff --git a/packages/schema/compose/src/types.ts b/packages/schema/linking/src/types.ts similarity index 66% rename from packages/schema/compose/src/types.ts rename to packages/schema/linking/src/types.ts index 379e46de30..fab23e4c2b 100644 --- a/packages/schema/compose/src/types.ts +++ b/packages/schema/linking/src/types.ts @@ -1,4 +1,4 @@ -import { Abi, CapabilityType } from "@polywrap/schema-parse"; +import { Abi } from "@polywrap/schema-parse"; export interface SchemaFile { schema: string; @@ -13,12 +13,3 @@ export interface AbiResolvers { local: SchemaResolver; } - - -export interface Use { - usedTypes: CapabilityType[]; - namespace: string; -} - - - diff --git a/packages/schema/compose/tsconfig.build.json b/packages/schema/linking/tsconfig.build.json similarity index 100% rename from packages/schema/compose/tsconfig.build.json rename to packages/schema/linking/tsconfig.build.json diff --git a/packages/schema/compose/tsconfig.json b/packages/schema/linking/tsconfig.json similarity index 100% rename from packages/schema/compose/tsconfig.json rename to packages/schema/linking/tsconfig.json From 2b56d22eaabef67968fc70655ee6190c773683b8 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 13 Feb 2023 10:53:12 +0100 Subject: [PATCH 33/90] (feat): started linking implementation --- packages/schema/linking/src/linking.ts | 118 +++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 packages/schema/linking/src/linking.ts diff --git a/packages/schema/linking/src/linking.ts b/packages/schema/linking/src/linking.ts new file mode 100644 index 0000000000..64e2598e2f --- /dev/null +++ b/packages/schema/linking/src/linking.ts @@ -0,0 +1,118 @@ +import { ImportStatement } from "@polywrap/schema-parse"; +import { Abi, AnyType, EnumDef, FunctionDef, ImportRefType, ObjectDef, RefType } from "@polywrap/schema-parse/build/definitions"; + +interface LinkImportsAbiArgs { + importStatements: ImportStatement[]; + abi: Abi; + imports: Map; +} + +type DefsWithDependencies = ObjectDef | FunctionDef; +type DefsThatCanBeReferenced = ObjectDef | EnumDef; +const KINDS_WITH_DEPENDENCIES = ["Object", "Function"] as const; +const REFERENCEABLE_KINDS = ["Object", "Enum"] as const; + +const findReferencedDefintionByName = (abi: Abi, ref_name: string): DefsThatCanBeReferenced | undefined => { + const object = abi.objects?.find((object) => object.name === ref_name); + if (object) { + return object; + } + + const enumDef = abi.enums?.find((enumDef) => enumDef.name === ref_name); + if (enumDef) { + return enumDef; + } + + return undefined; +} + +const extractRefFromAnyType = (type: AnyType): RefType | ImportRefType | undefined => { + switch (type.kind) { + case "Ref": + case "ImportRef": + return type; + case "Scalar": + return undefined; + case "Array": + return extractRefFromAnyType(type.item.type); + case "Map": + return extractRefFromAnyType(type.value.type); + } +} + +const extractDefFromRef = (abi: Abi, ref: RefType | ImportRefType): DefsThatCanBeReferenced => { + const name = ref.ref_name; + const definition = findReferencedDefintionByName(abi, name); + + if (!definition) { + throw new Error(`Found reference to '${name}'. But no definition found with this name.`) + } + + if (REFERENCEABLE_KINDS.includes(definition.kind)) { + throw new Error(`Found reference to '${name}'. But this is a '${definition.kind}' definition which can't be referenced.`) + } + + return definition; +} + +const extractReferencedDefs = (abi: Abi, depdendency: DefsWithDependencies): DefsThatCanBeReferenced[] => { + switch (depdendency.kind) { + case "Object": { + const dependencies: DefsThatCanBeReferenced[] = []; + + for (const property of depdendency.props) { + const ref = extractRefFromAnyType(property.type); + + if (ref) { + const definition = extractDefFromRef(abi, ref); + dependencies.push(definition); + + if (definition.kind in KINDS_WITH_DEPENDENCIES) { + dependencies.push(...extractReferencedDefs(abi, definition as DefsWithDependencies)); + } + } + // TODO: ImportRef handling + }; + + return dependencies; + } + case "Function": { + let dependencies: DefsThatCanBeReferenced[] = []; + + for (const arg of depdendency.args) { + const ref = extractRefFromAnyType(arg.type); + + if (ref) { + const definition = extractDefFromRef(abi, ref); + dependencies.push(definition); + + if (definition.kind in KINDS_WITH_DEPENDENCIES) { + dependencies.push(...extractReferencedDefs(abi, definition as DefsWithDependencies)); + } + } + } + + const ref = extractRefFromAnyType(depdendency.result.type); + + if (ref) { + const definition = extractDefFromRef(abi, ref); + dependencies.push(definition); + + if (definition.kind in KINDS_WITH_DEPENDENCIES) { + dependencies.push(...extractReferencedDefs(abi, definition as DefsWithDependencies)); + } + } + + return dependencies; + } + } +} + +export const linkAbiImports = ({ importStatements, abi, imports }: LinkImportsAbiArgs): Abi => { + importStatements.forEach((importStatement) => { + const { importedTypes, uriOrPath } = importStatement; + const importedAbi = imports.get(uriOrPath); + + importedAbi?.objects?.filter + }) +} \ No newline at end of file From 14c8ea26416017b03cd2456da46d01dde8f4db6a Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 13 Feb 2023 14:10:04 +0100 Subject: [PATCH 34/90] (chore): implemented new ABI visitor --- packages/schema/linking/src/visitor.ts | 188 +++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 packages/schema/linking/src/visitor.ts diff --git a/packages/schema/linking/src/visitor.ts b/packages/schema/linking/src/visitor.ts new file mode 100644 index 0000000000..bdda105f8a --- /dev/null +++ b/packages/schema/linking/src/visitor.ts @@ -0,0 +1,188 @@ +import { Abi, ImportedAbi, FunctionDef, ArgumentDef, ResultDef, ObjectDef, PropertyDef, EnumDef, ScalarType, RefType, ImportRefType, ArrayType, MapType, AnyType } from "@polywrap/schema-parse/build/definitions"; + +interface IAbiVisitor { + Abi?: (node: Abi) => void; + ImportedAbi?: (node: ImportedAbi) => void; + FunctionDef?: (node: FunctionDef) => void; + ArgumentDef?: (node: ArgumentDef) => void; + ResultDef?: (node: ResultDef) => void; + ObjectDef?: (node: ObjectDef) => void; + PropertyDef?: (node: PropertyDef) => void; + EnumDef?: (node: EnumDef) => void; + ScalarType?: (node: ScalarType) => void; + RefType?: (node: RefType) => void; + ImportRefType?: (node: ImportRefType) => void; + ArrayType?: (node: ArrayType) => void; + MapType?: (node: MapType) => void; + AnyType?: (node: AnyType) => void; +} + +export class AbiVisitor { + constructor(private readonly visitor: IAbiVisitor) {} + + private _Abi(node: Abi) { + if (this.visitor.Abi) { + this.visitor.Abi(node); + } + + if (node.imports) { + for (const importNode of node.imports) { + this._ImportedAbi(importNode); + } + } + + if (node.functions) { + for (const functionNode of node.functions) { + this._FunctionDef(functionNode); + } + } + + if (node.objects) { + for (const objectNode of node.objects) { + this._ObjectDef(objectNode); + } + } + + if (node.enums) { + for (const enumNode of node.enums) { + this._EnumDef(enumNode); + } + } + } + + private _ImportedAbi(node: ImportedAbi) { + if (this.visitor.ImportedAbi) { + this.visitor.ImportedAbi(node); + } + + if (node.functions) { + for (const functionNode of node.functions) { + this._FunctionDef(functionNode); + } + } + + if (node.objects) { + for (const objectNode of node.objects) { + this._ObjectDef(objectNode); + } + } + + if (node.enums) { + for (const enumNode of node.enums) { + this._EnumDef(enumNode); + } + } + } + + private _FunctionDef(node: FunctionDef) { + if (this.visitor.FunctionDef) { + this.visitor.FunctionDef(node); + } + + if (node.args) { + for (const argumentNode of node.args) { + this._ArgumentDef(argumentNode); + } + } + + this._ResultDef(node.result); + } + + private _ArgumentDef(node: ArgumentDef) { + if (this.visitor.ArgumentDef) { + this.visitor.ArgumentDef(node); + } + this._AnyType(node.type); + } + + private _ResultDef(node: ResultDef) { + if (this.visitor.ResultDef) { + this.visitor.ResultDef(node); + } + this._AnyType(node.type); + } + + private _ObjectDef(node: ObjectDef) { + if (this.visitor.ObjectDef) { + this.visitor.ObjectDef(node); + } + if (node.props) { + for (const propertyNode of node.props) { + this._PropertyDef(propertyNode); + } + } + } + + private _PropertyDef(node: PropertyDef) { + if (this.visitor.PropertyDef) { + this.visitor.PropertyDef(node); + } + this._AnyType(node.type); + } + + private _EnumDef(node: EnumDef) { + if (this.visitor.EnumDef) { + this.visitor.EnumDef(node); + } + } + + private _AnyType(node: AnyType) { + if (this.visitor.AnyType) { + this.visitor.AnyType(node); + } + + switch (node.kind) { + case "Scalar": + this._ScalarType(node); + break; + case "Array": + this._ArrayType(node); + break; + case "Map": + this._MapType(node); + break; + case "Ref": + this._RefType(node); + break; + case "ImportRef": + this._ImportRefType(node); + break; + } + } + + private _ScalarType(node: ScalarType) { + if (this.visitor.ScalarType) { + this.visitor.ScalarType(node); + } + } + + private _RefType(node: RefType) { + if (this.visitor.RefType) { + this.visitor.RefType(node); + } + } + + private _ImportRefType(node: ImportRefType) { + if (this.visitor.ImportRefType) { + this.visitor.ImportRefType(node); + } + } + + private _ArrayType(node: ArrayType) { + if (this.visitor.ArrayType) { + this.visitor.ArrayType(node); + } + this._AnyType(node.item.type); + } + + private _MapType(node: MapType) { + if (this.visitor.MapType) { + this.visitor.MapType(node); + } + this._AnyType(node.value.type); + } + + visit(node: Abi) { + this._Abi(node); + } +} \ No newline at end of file From 90994b6c2c0d3cb958e653975f45b71a91b696e5 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 13 Feb 2023 14:35:13 +0100 Subject: [PATCH 35/90] (chore): first full implementation of linking --- packages/schema/linking/src/linking.ts | 183 +++++++++++++++++++------ 1 file changed, 142 insertions(+), 41 deletions(-) diff --git a/packages/schema/linking/src/linking.ts b/packages/schema/linking/src/linking.ts index 64e2598e2f..ff3774513f 100644 --- a/packages/schema/linking/src/linking.ts +++ b/packages/schema/linking/src/linking.ts @@ -1,15 +1,16 @@ -import { ImportStatement } from "@polywrap/schema-parse"; +import { ExternalImportStatement, ImportStatement, LocalImportStatement, MODULE_NAME } from "@polywrap/schema-parse"; import { Abi, AnyType, EnumDef, FunctionDef, ImportRefType, ObjectDef, RefType } from "@polywrap/schema-parse/build/definitions"; +import { AbiVisitor } from "./visitor"; interface LinkImportsAbiArgs { - importStatements: ImportStatement[]; + importStatements: (ExternalImportStatement | LocalImportStatement)[]; abi: Abi; imports: Map; } -type DefsWithDependencies = ObjectDef | FunctionDef; +type DefsWithRefs = ObjectDef | FunctionDef; type DefsThatCanBeReferenced = ObjectDef | EnumDef; -const KINDS_WITH_DEPENDENCIES = ["Object", "Function"] as const; +const KINDS_WITH_REFS = ["Object", "Function"] as const; const REFERENCEABLE_KINDS = ["Object", "Enum"] as const; const findReferencedDefintionByName = (abi: Abi, ref_name: string): DefsThatCanBeReferenced | undefined => { @@ -55,22 +56,29 @@ const extractDefFromRef = (abi: Abi, ref: RefType | ImportRefType): DefsThatCanB return definition; } -const extractReferencedDefs = (abi: Abi, depdendency: DefsWithDependencies): DefsThatCanBeReferenced[] => { - switch (depdendency.kind) { - case "Object": { - const dependencies: DefsThatCanBeReferenced[] = []; +const extractReferencedDefs = (abi: Abi, defWithRefs: DefsWithRefs): DefsThatCanBeReferenced[] => { + const extractRefsFromAnyType = (type: AnyType): DefsThatCanBeReferenced[] => { + const dependencies: DefsThatCanBeReferenced[] = []; + const ref = extractRefFromAnyType(type); - for (const property of depdendency.props) { - const ref = extractRefFromAnyType(property.type); + if (ref) { + const definition = extractDefFromRef(abi, ref); + dependencies.push(definition); - if (ref) { - const definition = extractDefFromRef(abi, ref); - dependencies.push(definition); + if (definition.kind in KINDS_WITH_REFS) { + dependencies.push(...extractReferencedDefs(abi, definition as DefsWithRefs)); + } + } - if (definition.kind in KINDS_WITH_DEPENDENCIES) { - dependencies.push(...extractReferencedDefs(abi, definition as DefsWithDependencies)); - } - } + return dependencies; + } + + switch (defWithRefs.kind) { + case "Object": { + const dependencies: DefsThatCanBeReferenced[] = []; + + for (const property of defWithRefs.props) { + dependencies.push(...extractRefsFromAnyType(property.type)); // TODO: ImportRef handling }; @@ -79,40 +87,133 @@ const extractReferencedDefs = (abi: Abi, depdendency: DefsWithDependencies): Def case "Function": { let dependencies: DefsThatCanBeReferenced[] = []; - for (const arg of depdendency.args) { - const ref = extractRefFromAnyType(arg.type); + for (const arg of defWithRefs.args) { + dependencies.push(...extractRefsFromAnyType(arg.type)); + } + dependencies.push(...extractRefsFromAnyType(defWithRefs.result.type)); + + return dependencies; + } + } +} + +const treeShakeImportedAbi = (importedAbi: Abi, importStatement: ImportStatement): Abi => { + const treeShakenAbi: Abi = { + version: "0.2" + }; - if (ref) { - const definition = extractDefFromRef(abi, ref); - dependencies.push(definition); + const { importedTypes } = importStatement; - if (definition.kind in KINDS_WITH_DEPENDENCIES) { - dependencies.push(...extractReferencedDefs(abi, definition as DefsWithDependencies)); - } - } - } + const importedObjects = importedAbi?.objects?.filter((object) => importedTypes.includes(object.name)) ?? []; + const importedEnums = importedAbi?.enums?.filter((enumDef) => importedTypes.includes(enumDef.name)) ?? []; + const importedFunctions = (importedTypes.includes(MODULE_NAME) ? importedAbi?.functions : []) ?? [] - const ref = extractRefFromAnyType(depdendency.result.type); + const refsFromObjects = importedObjects.flatMap((object) => extractReferencedDefs(importedAbi, object)); + const referencedEnumsFromObjects = refsFromObjects.filter((ref) => ref.kind === "Enum") as EnumDef[]; + const referencedObjectsFromObjects = refsFromObjects.filter((ref) => ref.kind === "Object") as ObjectDef[]; - if (ref) { - const definition = extractDefFromRef(abi, ref); - dependencies.push(definition); + const refsFromFunctions = importedFunctions.flatMap((func) => extractReferencedDefs(importedAbi, func)); + const referencedEnumsFromFunctions = refsFromFunctions.filter((ref) => ref.kind === "Enum") as EnumDef[]; + const referencedObjectsFromFunctions = refsFromFunctions.filter((ref) => ref.kind === "Object") as ObjectDef[]; - if (definition.kind in KINDS_WITH_DEPENDENCIES) { - dependencies.push(...extractReferencedDefs(abi, definition as DefsWithDependencies)); - } - } + treeShakenAbi.enums = [...importedEnums, ...referencedEnumsFromObjects, ...referencedEnumsFromFunctions]; + treeShakenAbi.objects = [...importedObjects, ...referencedObjectsFromObjects, ...referencedObjectsFromFunctions]; + treeShakenAbi.functions = importedFunctions; - return dependencies; - } - } + return treeShakenAbi; +} + +const mergeAbis = (abis: Abi[]): Abi => { + const mergedAbi: Abi = { + version: "0.2" + }; + + // TODO: imports? + + const enums = abis.flatMap((abi) => abi.enums ?? []); + const objects = abis.flatMap((abi) => abi.objects ?? []); + const functions = abis.flatMap((abi) => abi.functions ?? []); + + mergedAbi.enums = enums; + mergedAbi.objects = objects; + mergedAbi.functions = functions; + + return mergedAbi; } export const linkAbiImports = ({ importStatements, abi, imports }: LinkImportsAbiArgs): Abi => { + const treeShakenImports = new Map(); + let abiClone: Abi = JSON.parse(JSON.stringify(abi)); + + // Tree shake imported ABIs importStatements.forEach((importStatement) => { - const { importedTypes, uriOrPath } = importStatement; + const { uriOrPath } = importStatement; + const importedAbi = imports.get(uriOrPath); - importedAbi?.objects?.filter - }) + if (!importedAbi) { + throw new Error(`Imported ABI not found for '${uriOrPath}'`); + } + + const treeShakenAbi = treeShakeImportedAbi(importedAbi, importStatement); + treeShakenImports.set(uriOrPath, treeShakenAbi); + }); + + if (treeShakenImports.size && !abiClone.imports) { + abiClone.imports = []; + } + + const externalImportsStatements = importStatements.filter((importStatement) => importStatement.kind === "external") as ExternalImportStatement[]; + const localImportsStatements = importStatements.filter((importStatement) => importStatement.kind === "local") as LocalImportStatement[]; + + // Merge local imports + + localImportsStatements.forEach((localImportStatement) => { + const { uriOrPath } = localImportStatement; + const treeShakenAbi = treeShakenImports.get(uriOrPath); + + if (!treeShakenAbi) { + throw new Error(`Tree shaken ABI not found for '${uriOrPath}'`); + } + + abiClone = mergeAbis([abiClone, treeShakenAbi]); + }); + + // Embed external imports + + externalImportsStatements.forEach((importStatement, i) => { + const { uriOrPath, importedTypes } = importStatement; + const treeShakenAbi = treeShakenImports.get(uriOrPath); + + if (!treeShakenAbi) { + throw new Error(`Tree shaken ABI not found for '${uriOrPath}'`); + } + + // TODO: better ID generation + const id = i.toString() + + abiClone.imports!.push({ + uri: uriOrPath, + namespace: importStatement.namespace, + id, + // TODO: Where do I get this from? + type: "wasm", + ...treeShakenAbi + }) + + // Link ImportRefs to external imports + + const importRefVisitor = new AbiVisitor({ + ImportRefType: (importRefType) => { + const { ref_name } = importRefType; + + // TODO: Imports of imports? + if (importedTypes.includes(ref_name)) { + importRefType.import_id = id; + } + } + }) + + importRefVisitor.visit(abiClone); + }); } \ No newline at end of file From fbccf17fd148e8a59067bafa1ce734ff0a831159 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 13 Feb 2023 17:42:09 +0100 Subject: [PATCH 36/90] (chore): removed unused legacy code --- packages/schema/linking/src/index.ts | 234 +++++- packages/schema/linking/src/linking.ts | 219 ------ packages/schema/linking/src/resolve.ts | 922 ------------------------ packages/schema/linking/src/resolver.ts | 238 ------ 4 files changed, 215 insertions(+), 1398 deletions(-) delete mode 100644 packages/schema/linking/src/linking.ts delete mode 100644 packages/schema/linking/src/resolve.ts delete mode 100644 packages/schema/linking/src/resolver.ts diff --git a/packages/schema/linking/src/index.ts b/packages/schema/linking/src/index.ts index 5760e55887..e980e2c320 100644 --- a/packages/schema/linking/src/index.ts +++ b/packages/schema/linking/src/index.ts @@ -1,25 +1,221 @@ -import { SchemaFile } from "./types"; -import { resolveImportsAndParseSchemas } from "./resolve"; -import { renderSchema } from "./render"; +import { ExternalImportStatement, ImportStatement, LocalImportStatement, MODULE_NAME } from "@polywrap/schema-parse"; +import { Abi, AnyType, EnumDef, FunctionDef, ImportRefType, ObjectDef, RefType } from "@polywrap/schema-parse/build/definitions"; +import { AbiVisitor } from "./visitor"; -import { WrapAbi } from "@polywrap/wrap-manifest-types-js"; +interface LinkImportsAbiArgs { + importStatements: (ExternalImportStatement | LocalImportStatement)[]; + abi: Abi; + imports: Map; +} + +type DefsWithRefs = ObjectDef | FunctionDef; +type DefsThatCanBeReferenced = ObjectDef | EnumDef; +const KINDS_WITH_REFS = ["Object", "Function"] as const; +const REFERENCEABLE_KINDS = ["Object", "Enum"] as const; + +const findReferencedDefintionByName = (abi: Abi, ref_name: string): DefsThatCanBeReferenced | undefined => { + const object = abi.objects?.find((object) => object.name === ref_name); + if (object) { + return object; + } + + const enumDef = abi.enums?.find((enumDef) => enumDef.name === ref_name); + if (enumDef) { + return enumDef; + } + + return undefined; +} + +const extractRefFromAnyType = (type: AnyType): RefType | ImportRefType | undefined => { + switch (type.kind) { + case "Ref": + case "ImportRef": + return type; + case "Scalar": + return undefined; + case "Array": + return extractRefFromAnyType(type.item.type); + case "Map": + return extractRefFromAnyType(type.value.type); + } +} + +const extractDefFromRef = (abi: Abi, ref: RefType | ImportRefType): DefsThatCanBeReferenced => { + const name = ref.ref_name; + const definition = findReferencedDefintionByName(abi, name); + + if (!definition) { + throw new Error(`Found reference to '${name}'. But no definition found with this name.`) + } + + if (REFERENCEABLE_KINDS.includes(definition.kind)) { + throw new Error(`Found reference to '${name}'. But this is a '${definition.kind}' definition which can't be referenced.`) + } + + return definition; +} -export * from "./types"; -export { renderSchema }; +const extractReferencedDefs = (abi: Abi, defWithRefs: DefsWithRefs): DefsThatCanBeReferenced[] => { + const extractRefsFromAnyType = (type: AnyType): DefsThatCanBeReferenced[] => { + const dependencies: DefsThatCanBeReferenced[] = []; + const ref = extractRefFromAnyType(type); -export interface ComposerOptions { - schema: SchemaFile; - abis: Map; - schemas: Map; + if (ref) { + const definition = extractDefFromRef(abi, ref); + dependencies.push(definition); + + if (definition.kind in KINDS_WITH_REFS) { + dependencies.push(...extractReferencedDefs(abi, definition as DefsWithRefs)); + } + } + + return dependencies; + } + + switch (defWithRefs.kind) { + case "Object": { + const dependencies: DefsThatCanBeReferenced[] = []; + + for (const property of defWithRefs.props) { + dependencies.push(...extractRefsFromAnyType(property.type)); + // TODO: ImportRef handling + }; + + return dependencies; + } + case "Function": { + let dependencies: DefsThatCanBeReferenced[] = []; + + for (const arg of defWithRefs.args) { + dependencies.push(...extractRefsFromAnyType(arg.type)); + } + dependencies.push(...extractRefsFromAnyType(defWithRefs.result.type)); + + return dependencies; + } + } } -export async function composeSchema( - options: ComposerOptions -): Promise { - return await resolveImportsAndParseSchemas( - options.schema.schema, - options.schema.absolutePath, - options.schemas, - options.abis - ); +const treeShakeImportedAbi = (importedAbi: Abi, importStatement: ImportStatement): Abi => { + const treeShakenAbi: Abi = { + version: "0.2" + }; + + const { importedTypes } = importStatement; + + const importedObjects = importedAbi?.objects?.filter((object) => importedTypes.includes(object.name)) ?? []; + const importedEnums = importedAbi?.enums?.filter((enumDef) => importedTypes.includes(enumDef.name)) ?? []; + const importedFunctions = (importedTypes.includes(MODULE_NAME) ? importedAbi?.functions : []) ?? [] + + const refsFromObjects = importedObjects.flatMap((object) => extractReferencedDefs(importedAbi, object)); + const referencedEnumsFromObjects = refsFromObjects.filter((ref) => ref.kind === "Enum") as EnumDef[]; + const referencedObjectsFromObjects = refsFromObjects.filter((ref) => ref.kind === "Object") as ObjectDef[]; + + const refsFromFunctions = importedFunctions.flatMap((func) => extractReferencedDefs(importedAbi, func)); + const referencedEnumsFromFunctions = refsFromFunctions.filter((ref) => ref.kind === "Enum") as EnumDef[]; + const referencedObjectsFromFunctions = refsFromFunctions.filter((ref) => ref.kind === "Object") as ObjectDef[]; + + treeShakenAbi.enums = [...importedEnums, ...referencedEnumsFromObjects, ...referencedEnumsFromFunctions]; + treeShakenAbi.objects = [...importedObjects, ...referencedObjectsFromObjects, ...referencedObjectsFromFunctions]; + treeShakenAbi.functions = importedFunctions; + + return treeShakenAbi; } + +const mergeAbis = (abis: Abi[]): Abi => { + const mergedAbi: Abi = { + version: "0.2" + }; + + // TODO: imports? + + const enums = abis.flatMap((abi) => abi.enums ?? []); + const objects = abis.flatMap((abi) => abi.objects ?? []); + const functions = abis.flatMap((abi) => abi.functions ?? []); + + mergedAbi.enums = enums; + mergedAbi.objects = objects; + mergedAbi.functions = functions; + + return mergedAbi; +} + +export const linkAbiImports = ({ importStatements, abi, imports }: LinkImportsAbiArgs): Abi => { + const treeShakenImports = new Map(); + let abiClone: Abi = JSON.parse(JSON.stringify(abi)); + + // Tree shake imported ABIs + importStatements.forEach((importStatement) => { + const { uriOrPath } = importStatement; + + const importedAbi = imports.get(uriOrPath); + + if (!importedAbi) { + throw new Error(`Imported ABI not found for '${uriOrPath}'`); + } + + const treeShakenAbi = treeShakeImportedAbi(importedAbi, importStatement); + treeShakenImports.set(uriOrPath, treeShakenAbi); + }); + + if (treeShakenImports.size && !abiClone.imports) { + abiClone.imports = []; + } + + const externalImportsStatements = importStatements.filter((importStatement) => importStatement.kind === "external") as ExternalImportStatement[]; + const localImportsStatements = importStatements.filter((importStatement) => importStatement.kind === "local") as LocalImportStatement[]; + + // Merge local imports + + localImportsStatements.forEach((localImportStatement) => { + const { uriOrPath } = localImportStatement; + const treeShakenAbi = treeShakenImports.get(uriOrPath); + + if (!treeShakenAbi) { + throw new Error(`Tree shaken ABI not found for '${uriOrPath}'`); + } + + abiClone = mergeAbis([abiClone, treeShakenAbi]); + }); + + // Embed external imports + + externalImportsStatements.forEach((importStatement, i) => { + const { uriOrPath, importedTypes } = importStatement; + const treeShakenAbi = treeShakenImports.get(uriOrPath); + + if (!treeShakenAbi) { + throw new Error(`Tree shaken ABI not found for '${uriOrPath}'`); + } + + // TODO: better ID generation + const id = i.toString() + + abiClone.imports!.push({ + uri: uriOrPath, + namespace: importStatement.namespace, + id, + // TODO: Where do I get this from? + type: "wasm", + ...treeShakenAbi + }) + + // Link ImportRefs to external imports + + const importRefVisitor = new AbiVisitor({ + ImportRefType: (importRefType) => { + const { ref_name } = importRefType; + + // TODO: Imports of imports? + if (importedTypes.includes(ref_name)) { + importRefType.import_id = id; + } + } + }) + + importRefVisitor.visit(abiClone); + }); + + return abiClone; +} \ No newline at end of file diff --git a/packages/schema/linking/src/linking.ts b/packages/schema/linking/src/linking.ts deleted file mode 100644 index ff3774513f..0000000000 --- a/packages/schema/linking/src/linking.ts +++ /dev/null @@ -1,219 +0,0 @@ -import { ExternalImportStatement, ImportStatement, LocalImportStatement, MODULE_NAME } from "@polywrap/schema-parse"; -import { Abi, AnyType, EnumDef, FunctionDef, ImportRefType, ObjectDef, RefType } from "@polywrap/schema-parse/build/definitions"; -import { AbiVisitor } from "./visitor"; - -interface LinkImportsAbiArgs { - importStatements: (ExternalImportStatement | LocalImportStatement)[]; - abi: Abi; - imports: Map; -} - -type DefsWithRefs = ObjectDef | FunctionDef; -type DefsThatCanBeReferenced = ObjectDef | EnumDef; -const KINDS_WITH_REFS = ["Object", "Function"] as const; -const REFERENCEABLE_KINDS = ["Object", "Enum"] as const; - -const findReferencedDefintionByName = (abi: Abi, ref_name: string): DefsThatCanBeReferenced | undefined => { - const object = abi.objects?.find((object) => object.name === ref_name); - if (object) { - return object; - } - - const enumDef = abi.enums?.find((enumDef) => enumDef.name === ref_name); - if (enumDef) { - return enumDef; - } - - return undefined; -} - -const extractRefFromAnyType = (type: AnyType): RefType | ImportRefType | undefined => { - switch (type.kind) { - case "Ref": - case "ImportRef": - return type; - case "Scalar": - return undefined; - case "Array": - return extractRefFromAnyType(type.item.type); - case "Map": - return extractRefFromAnyType(type.value.type); - } -} - -const extractDefFromRef = (abi: Abi, ref: RefType | ImportRefType): DefsThatCanBeReferenced => { - const name = ref.ref_name; - const definition = findReferencedDefintionByName(abi, name); - - if (!definition) { - throw new Error(`Found reference to '${name}'. But no definition found with this name.`) - } - - if (REFERENCEABLE_KINDS.includes(definition.kind)) { - throw new Error(`Found reference to '${name}'. But this is a '${definition.kind}' definition which can't be referenced.`) - } - - return definition; -} - -const extractReferencedDefs = (abi: Abi, defWithRefs: DefsWithRefs): DefsThatCanBeReferenced[] => { - const extractRefsFromAnyType = (type: AnyType): DefsThatCanBeReferenced[] => { - const dependencies: DefsThatCanBeReferenced[] = []; - const ref = extractRefFromAnyType(type); - - if (ref) { - const definition = extractDefFromRef(abi, ref); - dependencies.push(definition); - - if (definition.kind in KINDS_WITH_REFS) { - dependencies.push(...extractReferencedDefs(abi, definition as DefsWithRefs)); - } - } - - return dependencies; - } - - switch (defWithRefs.kind) { - case "Object": { - const dependencies: DefsThatCanBeReferenced[] = []; - - for (const property of defWithRefs.props) { - dependencies.push(...extractRefsFromAnyType(property.type)); - // TODO: ImportRef handling - }; - - return dependencies; - } - case "Function": { - let dependencies: DefsThatCanBeReferenced[] = []; - - for (const arg of defWithRefs.args) { - dependencies.push(...extractRefsFromAnyType(arg.type)); - } - dependencies.push(...extractRefsFromAnyType(defWithRefs.result.type)); - - return dependencies; - } - } -} - -const treeShakeImportedAbi = (importedAbi: Abi, importStatement: ImportStatement): Abi => { - const treeShakenAbi: Abi = { - version: "0.2" - }; - - const { importedTypes } = importStatement; - - const importedObjects = importedAbi?.objects?.filter((object) => importedTypes.includes(object.name)) ?? []; - const importedEnums = importedAbi?.enums?.filter((enumDef) => importedTypes.includes(enumDef.name)) ?? []; - const importedFunctions = (importedTypes.includes(MODULE_NAME) ? importedAbi?.functions : []) ?? [] - - const refsFromObjects = importedObjects.flatMap((object) => extractReferencedDefs(importedAbi, object)); - const referencedEnumsFromObjects = refsFromObjects.filter((ref) => ref.kind === "Enum") as EnumDef[]; - const referencedObjectsFromObjects = refsFromObjects.filter((ref) => ref.kind === "Object") as ObjectDef[]; - - const refsFromFunctions = importedFunctions.flatMap((func) => extractReferencedDefs(importedAbi, func)); - const referencedEnumsFromFunctions = refsFromFunctions.filter((ref) => ref.kind === "Enum") as EnumDef[]; - const referencedObjectsFromFunctions = refsFromFunctions.filter((ref) => ref.kind === "Object") as ObjectDef[]; - - treeShakenAbi.enums = [...importedEnums, ...referencedEnumsFromObjects, ...referencedEnumsFromFunctions]; - treeShakenAbi.objects = [...importedObjects, ...referencedObjectsFromObjects, ...referencedObjectsFromFunctions]; - treeShakenAbi.functions = importedFunctions; - - return treeShakenAbi; -} - -const mergeAbis = (abis: Abi[]): Abi => { - const mergedAbi: Abi = { - version: "0.2" - }; - - // TODO: imports? - - const enums = abis.flatMap((abi) => abi.enums ?? []); - const objects = abis.flatMap((abi) => abi.objects ?? []); - const functions = abis.flatMap((abi) => abi.functions ?? []); - - mergedAbi.enums = enums; - mergedAbi.objects = objects; - mergedAbi.functions = functions; - - return mergedAbi; -} - -export const linkAbiImports = ({ importStatements, abi, imports }: LinkImportsAbiArgs): Abi => { - const treeShakenImports = new Map(); - let abiClone: Abi = JSON.parse(JSON.stringify(abi)); - - // Tree shake imported ABIs - importStatements.forEach((importStatement) => { - const { uriOrPath } = importStatement; - - const importedAbi = imports.get(uriOrPath); - - if (!importedAbi) { - throw new Error(`Imported ABI not found for '${uriOrPath}'`); - } - - const treeShakenAbi = treeShakeImportedAbi(importedAbi, importStatement); - treeShakenImports.set(uriOrPath, treeShakenAbi); - }); - - if (treeShakenImports.size && !abiClone.imports) { - abiClone.imports = []; - } - - const externalImportsStatements = importStatements.filter((importStatement) => importStatement.kind === "external") as ExternalImportStatement[]; - const localImportsStatements = importStatements.filter((importStatement) => importStatement.kind === "local") as LocalImportStatement[]; - - // Merge local imports - - localImportsStatements.forEach((localImportStatement) => { - const { uriOrPath } = localImportStatement; - const treeShakenAbi = treeShakenImports.get(uriOrPath); - - if (!treeShakenAbi) { - throw new Error(`Tree shaken ABI not found for '${uriOrPath}'`); - } - - abiClone = mergeAbis([abiClone, treeShakenAbi]); - }); - - // Embed external imports - - externalImportsStatements.forEach((importStatement, i) => { - const { uriOrPath, importedTypes } = importStatement; - const treeShakenAbi = treeShakenImports.get(uriOrPath); - - if (!treeShakenAbi) { - throw new Error(`Tree shaken ABI not found for '${uriOrPath}'`); - } - - // TODO: better ID generation - const id = i.toString() - - abiClone.imports!.push({ - uri: uriOrPath, - namespace: importStatement.namespace, - id, - // TODO: Where do I get this from? - type: "wasm", - ...treeShakenAbi - }) - - // Link ImportRefs to external imports - - const importRefVisitor = new AbiVisitor({ - ImportRefType: (importRefType) => { - const { ref_name } = importRefType; - - // TODO: Imports of imports? - if (importedTypes.includes(ref_name)) { - importRefType.import_id = id; - } - } - }) - - importRefVisitor.visit(abiClone); - }); -} \ No newline at end of file diff --git a/packages/schema/linking/src/resolve.ts b/packages/schema/linking/src/resolve.ts deleted file mode 100644 index 2ed25851c8..0000000000 --- a/packages/schema/linking/src/resolve.ts +++ /dev/null @@ -1,922 +0,0 @@ -/* eslint-disable no-useless-escape */ -/* eslint-disable @typescript-eslint/naming-convention */ -/* eslint-disable @typescript-eslint/ban-types */ - -import { - ExternalImport, - LocalImport, - SYNTAX_REFERENCE, -} from "./types"; -import { parseExternalImports, parseLocalImports, parseUse } from "./parse"; -import { renderSchema } from "./render"; -import { addHeader } from "./templates/header.mustache"; - -import { - WrapAbi, - ObjectDefinition, - ImportedObjectDefinition, - ModuleDefinition, - ImportedModuleDefinition, - ImportedEnumDefinition, - EnumDefinition, - GenericDefinition, - InterfaceImplementedDefinition, - ObjectRef, - EnumRef, -} from "@polywrap/wrap-manifest-types-js"; -import { - parseSchema, - AbiTransforms, - visitObjectDefinition, - visitModuleDefinition, - DefinitionKind, - visitImportedModuleDefinition, - visitImportedObjectDefinition, - visitEnumDefinition, - visitImportedEnumDefinition, - isKind, - isImportedModuleType, - header, - createImportedObjectDefinition, - createImportedEnumDefinition, - createImportedModuleDefinition, - createInterfaceDefinition, - createCapability, - ModuleCapability, - createModuleDefinition, -} from "@polywrap/schema-parse"; - -type ImplementationWithInterfaces = { - typeName: string; - interfaces: string[]; -}; - -const TYPE_NAME_REGEX = `[a-zA-Z0-9_]+`; - -type UriStr = string; -type SchemaPath = string; - - - -interface Namespaced { - __namespaced?: boolean; -} - -type ImportMap = Record< - string, - ( - | ImportedObjectDefinition - | ImportedModuleDefinition - | ImportedEnumDefinition - ) & - Namespaced ->; - -type EnumOrObject = ObjectDefinition | EnumDefinition; -type ImportedEnumOrObject = - | ImportedObjectDefinition - | ImportedEnumDefinition - -// A transformation that converts all object definitions into -// imported object definitions -const extractObjectImportDependencies = ( - importsFound: ImportMap, - rootAbi: WrapAbi, - namespace: string, - uri: string -): AbiTransforms => { - const findImport = ( - type: string, - namespaceType: string, - rootTypes: EnumOrObject[], - importedTypes: ImportedEnumOrObject[], - kind: DefinitionKind - ): ImportedEnumOrObject & Namespaced => { - // Find this type's ObjectDefinition in the root type info - let idx = rootTypes.findIndex((obj) => obj.type === type); - let obj = undefined; - - if (idx === -1) { - idx = importedTypes.findIndex((obj) => obj.type === type); - } else { - obj = rootTypes[idx]; - } - - if (idx === -1) { - throw Error( - `extractObjectImportDependencies: Cannot find the dependent type within the root type info.\n` + - `Type: ${type}\nAbi: ${JSON.stringify( - rootAbi - )}\n${namespace}\n${JSON.stringify(Object.keys(importsFound))}` - ); - } else if (obj === undefined) { - obj = importedTypes[idx]; - } - - // Create the new ImportedObjectDefinition - return { - ...obj, - type: namespaceType, - __namespaced: true, - kind, - uri, - namespace, - nativeType: type, - }; - }; - - return { - enter: { - ObjectRef: (def: ObjectRef & Namespaced) => { - if (def.__namespaced) { - return def; - } - - const type = def.type; - - const namespaceType = appendNamespace(namespace, type); - - if (!importsFound[namespaceType]) { - const importFound = findImport( - type, - namespaceType, - rootAbi.objectTypes || [], - rootAbi.importedObjectTypes || [], - DefinitionKind.ImportedObject - ) as ImportedObjectDefinition; - - // Keep track of it - importsFound[importFound.type] = importFound; - - // Traverse this newly added object - visitObjectDefinition(importFound, { - ...extractObjectImportDependencies( - importsFound, - rootAbi, - namespace, - uri - ), - }); - } - - return def; - }, - InterfaceImplementedDefinition: ( - def: InterfaceImplementedDefinition & Namespaced - ) => { - if (def.__namespaced) { - return def; - } - - const type = def.type; - - const namespaceType = appendNamespace(namespace, type); - - if (!importsFound[namespaceType]) { - const importFound = findImport( - type, - namespaceType, - rootAbi.objectTypes || [], - rootAbi.importedObjectTypes || [], - DefinitionKind.ImportedObject - ) as ImportedObjectDefinition; - - // Keep track of it - importsFound[importFound.type] = importFound; - - // Traverse this newly added object - visitObjectDefinition(importFound, { - ...extractObjectImportDependencies( - importsFound, - rootAbi, - namespace, - uri - ), - }); - } - - return def; - }, - EnumRef: (def: EnumRef & Namespaced) => { - if (def.__namespaced) { - return def; - } - - const namespaceType = appendNamespace(namespace, def.type); - if (!importsFound[namespaceType]) { - // Find the import - const importFound = findImport( - def.type, - namespaceType, - rootAbi.enumTypes || [], - rootAbi.importedEnumTypes || [], - DefinitionKind.ImportedEnum - ) as ImportedEnumDefinition; - - // Keep track of it - importsFound[importFound.type] = importFound; - } - - return def; - }, - }, - }; -}; - -const namespaceTypes = (namespace: string): AbiTransforms => ({ - enter: { - ObjectRef: (def: ObjectRef & Namespaced) => { - if (def.__namespaced) { - return def; - } - - return { - ...def, - type: appendNamespace(namespace, def.type), - __namespaced: true, - }; - }, - InterfaceImplementedDefinition: ( - def: InterfaceImplementedDefinition & Namespaced - ) => { - if (def.__namespaced) { - return def; - } - - return { - ...def, - type: appendNamespace(namespace, def.type), - __namespaced: true, - }; - }, - EnumRef: (def: EnumRef & Namespaced) => { - if (def.__namespaced) { - return def; - } - - return { - ...def, - type: appendNamespace(namespace, def.type), - __namespaced: true, - }; - }, - }, -}); - -function appendNamespace(namespace: string, str: string) { - return `${namespace}_${str}`; -} - -function addModuleImportsDirective( - schema: string, - externalImports: string[] -): string { - if (!externalImports.length) { - return schema; - } - - let result = schema; - - const modifySchema = () => { - // Append the @imports(...) directive to the module type - const typeCapture = /type\s+Module\s+([^{]*)\s*{/g; - - const importedTypes = `${externalImports - .map((type) => `\"${type}\"`) - .join(",\n ")}`; - - const replacementModuleStr = `type Module $1@imports( - types: [ - ${importedTypes} - ] - ) {`; - - return result.replace(typeCapture, replacementModuleStr); - }; - - result = modifySchema(); - - return result; -} - -function addCapabilityDirective( - schema: string, - capabilities: ModuleCapability[] -): string { - if (!capabilities.length) { - return schema; - } - - capabilities.forEach((capability) => { - const typeCapture = /type[ \n\t]+Module[ \n\t]+([^{]*)[ \n\t]*{/g; - const replacementModuleStr = `type Module $1@capability( -type: "${capability.type}", -uri: "${capability.uri}", -namespace: "${capability.namespace}" -) {`; - - schema = schema.replace(typeCapture, replacementModuleStr); - }); - - return schema; -} - -function parseInterfaces( - implementInterfaceStatments: RegExpMatchArray[] -): ImplementationWithInterfaces[] { - const implementationsWithInterfaces: ImplementationWithInterfaces[] = []; - - for (const implementMatch of implementInterfaceStatments) { - const implementStr = implementMatch[1].trim(); - const typeCapture = new RegExp(`type\\s+(${TYPE_NAME_REGEX})\\s+`, "g"); - - const typeNameMatches = typeCapture.exec(implementMatch[0]); - - if (!typeNameMatches) { - continue; - } - - const typeName = typeNameMatches[1]; - - const interfaces = [ - ...implementStr.matchAll(new RegExp(`(${TYPE_NAME_REGEX})(&\s+)*`, "g")), - ].map((x) => x[0]); - - implementationsWithInterfaces.push({ - typeName, - interfaces, - }); - } - - return implementationsWithInterfaces; -} - -function resolveInterfaces( - schema: string, - implementationsWithInterfaces: ImplementationWithInterfaces[] -): string { - const removeComments = (body: string) => { - const bodyWithoutComments = body.replace(/"""[^"]*"""\s*/g, ""); - return bodyWithoutComments; - }; - - if (!implementationsWithInterfaces.length) { - return schema; - } - - const getAllUniqueInterfaces = (): string[] => { - const allIntefaces = implementationsWithInterfaces - .map((x) => x.interfaces) - .reduce((acc, x) => acc.concat(x), []); - - return [...new Set(allIntefaces)]; - }; - - const allInterfaces = getAllUniqueInterfaces(); - const interfacesWithBodies: { name: string; body: string }[] = []; - - const typeCapture = new RegExp( - `type\\s+(${TYPE_NAME_REGEX})[^{]+{([^}]*)}`, - "g" - ); - const typeMatches = [...schema.matchAll(typeCapture)]; - - for (const interfaceName of allInterfaces) { - const match = typeMatches.find((x) => x[1] === interfaceName); - - if (!match) { - continue; - } - - let body = match[2]; - if (!body) { - continue; - } - - body = removeComments(body); - - interfacesWithBodies.push({ - name: interfaceName, - body: body, - }); - } - - for (const implementationWithInterfaces of implementationsWithInterfaces) { - const implementationTypeCapture = new RegExp( - `(type\\s+${implementationWithInterfaces.typeName}\\s+[^{]*){([^}]*)}` - ); - - const bodiesOfInterfaces = implementationWithInterfaces.interfaces.map( - (interfaceName) => { - return interfacesWithBodies - .find((iwb) => iwb.name === interfaceName) - ?.body.trim(); - } - ); - - const bodiesOfInterfacesStr = bodiesOfInterfaces - .filter((x) => x) - .reduce((acc: string, x: string) => acc + "\n" + x, ""); - - schema = schema.replace( - implementationTypeCapture, - `$1{$2${bodiesOfInterfacesStr}\n}` - ); - } - - return schema; -} - -async function resolveExternalImports( - importsToResolve: ExternalImport[], - externalAbisMap: Map, - abi: WrapAbi -): Promise { - // Keep track of all imported object type names - const typesToImport: ImportMap = {}; - - for (const importToResolve of importsToResolve) { - const { uri, namespace, importedTypes } = importToResolve; - - // Resolve the schema - // const extAbi = await resolveAbi(uri); - const extAbi = externalAbisMap.get(uri) - - if (!extAbi) { - throw Error(`Unable to resolve abi at "${uri}"`); - } - - const extTypesToImport = importedTypes; - const starIdx = extTypesToImport.indexOf("*"); - - // If the importedTypes array contains the catch-all "*" - // go ahead and add all extAbi types to the importedTypes array - if (starIdx > -1) { - extTypesToImport.splice(starIdx, 1); - if (extAbi.objectTypes) { - extTypesToImport.push(...extAbi.objectTypes.map((x) => x.type)); - } - if (extAbi.enumTypes) { - extTypesToImport.push(...extAbi.enumTypes.map((x) => x.type)); - } - - if (extAbi.moduleType) { - extTypesToImport.push(extAbi.moduleType.type); - } - } - - // For each imported type to resolve - for (const importedType of extTypesToImport) { - let extTypes: - | (ModuleDefinition | ObjectDefinition | EnumDefinition)[] - | undefined; - let visitorFunc: Function | undefined; - let trueType: - | ImportedModuleDefinition - | ImportedObjectDefinition - | ImportedEnumDefinition - | undefined; - - // If it's a module type - if (importedType === "Module") { - if (!extAbi.moduleType) { - extAbi.moduleType = createModuleDefinition({}); - } - - extTypes = [extAbi.moduleType as ModuleDefinition]; - visitorFunc = visitModuleDefinition; - const type = extAbi.moduleType as ModuleDefinition; - trueType = { - ...createImportedModuleDefinition({ - ...type, - required: undefined, - uri, - nativeType: type.type, - namespace, - }), - methods: type.methods, - }; - } else if (isImportedModuleType(importedType)) { - throw Error( - `Cannot import an import's imported module type. Tried to import ${importedType} from ${uri}.` - ); - } else { - const objIdx = extAbi.objectTypes - ? extAbi.objectTypes.findIndex((def) => def.type === importedType) - : -1; - const impObjIdx = - objIdx === -1 && extAbi.importedObjectTypes - ? extAbi.importedObjectTypes.findIndex( - (def) => def.type === importedType - ) - : -1; - const enumIdx = - impObjIdx === -1 && extAbi.enumTypes - ? extAbi.enumTypes.findIndex((def) => def.type === importedType) - : -1; - const impEnumIdx = - enumIdx === -1 && extAbi.importedEnumTypes - ? extAbi.importedEnumTypes.findIndex( - (def) => def.type === importedType - ) - : -1; - - if (objIdx > -1) { - extTypes = extAbi.objectTypes; - visitorFunc = visitObjectDefinition; - if (!extAbi.objectTypes || !extAbi.objectTypes.length) { - throw new Error( - "Expected objectTypes to be an array got undefined" - ); - } - const type = extAbi.objectTypes[objIdx]; - trueType = { - ...createImportedObjectDefinition({ - ...type, - type: appendNamespace(namespace, importedType), - name: undefined, - required: undefined, - uri, - nativeType: type.type, - namespace, - }), - properties: type.properties, - }; - } else if (impObjIdx > -1) { - extTypes = extAbi.importedObjectTypes; - visitorFunc = visitObjectDefinition; - if ( - !extAbi.importedObjectTypes || - !extAbi.importedObjectTypes.length - ) { - throw new Error( - "Expected importedObjectTypes to be an array got undefined" - ); - } - const type = extAbi.importedObjectTypes[impObjIdx]; - trueType = { - ...createImportedObjectDefinition({ - ...type, - type: appendNamespace(namespace, importedType), - name: undefined, - required: undefined, - uri, - nativeType: type.type, - namespace, - }), - properties: type.properties, - }; - } else if (enumIdx > -1) { - extTypes = extAbi.enumTypes; - visitorFunc = visitEnumDefinition; - if (!extAbi.enumTypes || !extAbi.enumTypes.length) { - throw new Error("Expected enumTypes to be an array got undefined"); - } - const type = extAbi.enumTypes[enumIdx]; - trueType = createImportedEnumDefinition({ - ...type, - type: appendNamespace(namespace, importedType), - uri, - nativeType: type.type, - namespace, - }); - } else if (impEnumIdx > -1) { - extTypes = extAbi.importedEnumTypes; - visitorFunc = visitEnumDefinition; - if (!extAbi.importedEnumTypes || !extAbi.importedEnumTypes.length) { - throw new Error( - "Expected importedEnumTypes to be an array got undefined" - ); - } - const type = extAbi.importedEnumTypes[impEnumIdx]; - trueType = createImportedEnumDefinition({ - ...type, - type: appendNamespace(namespace, importedType), - uri, - nativeType: type.type, - namespace, - }); - } - } - - if (!trueType) { - throw Error( - `Cannot find type "${importedType}" in the schema at ${uri}.\nFound: ${extTypes && JSON.stringify(extTypes.map((type) => type.type)) - }` - ); - } - - if (!visitorFunc) { - throw Error(`visitorFunc has not been set, this should never happen.`); - } - - const namespacedType = appendNamespace(namespace, importedType); - - // Continue if we've already imported this type - if (typesToImport[namespacedType]) { - continue; - } - - // Append the base type to our Abi - typesToImport[namespacedType] = { - ...trueType, - __namespaced: true, - }; - - // Extract all object dependencies - visitorFunc( - trueType, - extractObjectImportDependencies(typesToImport, extAbi, namespace, uri) - ); - } - - // Add all imported types into the aggregate Abi - for (const importName of Object.keys(typesToImport)) { - const importType = typesToImport[importName]; - let destArray: - | ImportedObjectDefinition[] - | ImportedModuleDefinition[] - | ImportedEnumDefinition[] - | undefined; - let append; - - if (importType.kind === DefinitionKind.ImportedObject) { - destArray = abi.importedObjectTypes; - append = () => { - const importDef = importType as ImportedObjectDefinition; - abi.importedObjectTypes && - abi.importedObjectTypes.push( - visitImportedObjectDefinition( - importDef, - namespaceTypes(namespace) - ) - ); - }; - } else if (importType.kind === DefinitionKind.ImportedModule) { - destArray = abi.importedModuleTypes; - append = () => { - const importDef = importType as ImportedModuleDefinition; - abi.importedModuleTypes && - abi.importedModuleTypes.push( - visitImportedModuleDefinition( - importDef, - namespaceTypes(namespace) - ) - ); - }; - } else if (importType.kind === DefinitionKind.ImportedEnum) { - destArray = abi.importedEnumTypes; - append = () => { - abi.importedEnumTypes && - abi.importedEnumTypes.push( - visitImportedEnumDefinition( - importType as ImportedEnumDefinition, - namespaceTypes(namespace) - ) - ); - }; - } else { - throw Error( - `resolveExternalImports: This should never happen, unknown kind.\n${JSON.stringify( - importType, - null, - 2 - )}` - ); - } - - const found = - destArray !== undefined && - destArray.findIndex( - ( - def: - | ImportedObjectDefinition - | ImportedModuleDefinition - | ImportedEnumDefinition - ) => def.type === importType.type - ) > -1; - - if (!found) { - append(); - } - } - } - - return Promise.resolve(Object.keys(typesToImport)); -} - -async function resolveLocalImports( - importsToResolve: LocalImport[], - importSchemas: Map, - abi: WrapAbi, - importAbis: Map -): Promise { - for (const importToResolve of importsToResolve) { - const { importedTypes, path } = importToResolve; - - // Resolve the schema - let schema = importSchemas.get(path); - - if (!schema) { - throw Error(`Unable to resolve schema at "${path}"`); - } - - // Make sure the schema has the Polywrap header - if (schema.indexOf("### Polywrap Header START ###") === -1) { - schema = addHeader(schema); - } - - // Parse the schema into Abi - const localAbi = await resolveImportsAndParseSchemas( - schema, - path, - importSchemas, - importAbis, - true - ); - - const extTypesToImport = importedTypes; - const starIdx = extTypesToImport.indexOf("*"); - - // If the importedTypes array contains the catch-all "*" - // go ahead and add all extAbi types to the importedTypes array - if (starIdx > -1) { - extTypesToImport.splice(starIdx, 1); - if (localAbi.objectTypes) { - extTypesToImport.push(...localAbi.objectTypes.map((x) => x.type)); - } - if (localAbi.enumTypes) { - extTypesToImport.push(...localAbi.enumTypes.map((x) => x.type)); - } - - if (localAbi.moduleType) { - extTypesToImport.push(localAbi.moduleType.type); - } - } - - // Keep track of all imported type names - const typesToImport: Record = {}; - - for (const importedType of extTypesToImport) { - if (importedType === "Module") { - throw Error( - `Importing module types from local schemas is prohibited. Tried to import from ${path}.` - ); - } - - let type: ObjectDefinition | EnumDefinition | undefined; - let visitorFunc: Function; - - const objectIdx = localAbi.objectTypes - ? localAbi.objectTypes.findIndex((type) => type.type === importedType) - : -1; - - const enumIdx = - objectIdx === -1 && localAbi.enumTypes - ? localAbi.enumTypes.findIndex((type) => type.type === importedType) - : -1; - - if (objectIdx > -1) { - visitorFunc = visitObjectDefinition; - type = localAbi.objectTypes && localAbi.objectTypes[objectIdx]; - } else if (enumIdx > -1) { - visitorFunc = visitEnumDefinition; - type = - localAbi.enumTypes && - localAbi.enumTypes.find((type) => type.type === importedType); - } - - if (!type) { - throw Error( - `Cannot find type "${importedType}" in the schema at ${path}.\nFound: [ ${localAbi.objectTypes && - localAbi.objectTypes.map((type) => type.type + " ") - }]` - ); - } - - typesToImport[type.type] = type; - - const findImport = ( - def: GenericDefinition, - rootTypes: EnumOrObject[] - ) => { - // Skip objects that we've already processed - if (typesToImport[def.type]) { - return def; - } - - // Find the ObjectDefinition - const idx = rootTypes.findIndex((obj) => obj.type === def.type); - - if (idx === -1) { - throw Error( - `resolveLocalImports: Cannot find the requested type within the Abi.\n` + - `Type: ${def.type}\nAbi: ${JSON.stringify(localAbi)}` - ); - } - - const objectDefinition = rootTypes[idx]; - - if (!visitedTypes[objectDefinition.type]) { - if (objectDefinition.kind !== DefinitionKind.Enum) { - visitedTypes[objectDefinition.type] = true; - visitType(objectDefinition); - } - } - - typesToImport[def.type] = { - ...objectDefinition, - }; - return def; - }; - - const visitedTypes: Record = {}; - - const visitType = (type: GenericDefinition) => { - visitorFunc(type, { - enter: { - ObjectRef: (def: ObjectRef) => { - const allObjectTypes = []; - if (localAbi.objectTypes) { - allObjectTypes.push(...localAbi.objectTypes); - } - if (localAbi.importedObjectTypes) { - allObjectTypes.push(...localAbi.importedObjectTypes); - } - return findImport(def, allObjectTypes); - }, - EnumRef: (def: EnumRef) => { - const allEnumTypes = []; - if (localAbi.enumTypes) { - allEnumTypes.push(...localAbi.enumTypes); - } - if (localAbi.importedEnumTypes) { - allEnumTypes.push(...localAbi.importedEnumTypes); - } - return findImport(def, allEnumTypes); - }, - InterfaceImplementedDefinition: ( - def: InterfaceImplementedDefinition - ) => { - const allObjectTypes = []; - if (localAbi.objectTypes) { - allObjectTypes.push(...localAbi.objectTypes); - } - if (localAbi.importedObjectTypes) { - allObjectTypes.push(...localAbi.importedObjectTypes); - } - return findImport(def, allObjectTypes); - }, - }, - }); - }; - - visitedTypes[type.type] = true; - visitType(type); - } - - // Add all imported types into the aggregate Abi - for (const importType of Object.keys(typesToImport)) { - if ( - isKind(typesToImport[importType], DefinitionKind.ImportedObject) - ) { - if ( - abi.importedObjectTypes && - abi.importedObjectTypes.findIndex( - (def) => def.type === importType - ) === -1 - ) { - abi.importedObjectTypes.push( - typesToImport[importType] as ImportedObjectDefinition - ); - } - } else if (isKind(typesToImport[importType], DefinitionKind.Object)) { - if ( - abi.objectTypes && - abi.objectTypes.findIndex((def) => def.type === importType) === -1 - ) { - abi.objectTypes.push(typesToImport[importType] as ObjectDefinition); - } - } else if ( - isKind(typesToImport[importType], DefinitionKind.ImportedEnum) - ) { - if ( - abi.importedEnumTypes && - abi.importedEnumTypes.findIndex((def) => def.type === importType) === - -1 - ) { - abi.importedEnumTypes.push( - typesToImport[importType] as ImportedEnumDefinition - ); - } - } else if (isKind(typesToImport[importType], DefinitionKind.Enum)) { - if ( - abi.enumTypes && - abi.enumTypes.findIndex((def) => def.type === importType) === -1 - ) { - abi.enumTypes.push(typesToImport[importType] as EnumDefinition); - } - } - } - } -} diff --git a/packages/schema/linking/src/resolver.ts b/packages/schema/linking/src/resolver.ts deleted file mode 100644 index 00390ef008..0000000000 --- a/packages/schema/linking/src/resolver.ts +++ /dev/null @@ -1,238 +0,0 @@ -import { createImportedEnumDefinition, createImportedModuleDefinition, createImportedObjectDefinition, DefinitionKind, visitEnumDefinition, visitModuleDefinition, visitObjectDefinition } from "@polywrap/schema-parse"; -import { EnumDefinition, GenericDefinition, ImportedEnumDefinition, ImportedModuleDefinition, ImportedObjectDefinition, ModuleDefinition, ObjectDefinition, WrapAbi } from "@polywrap/wrap-manifest-types-js"; - -type UriStr = string; -interface ImportStatement { - typeNames: string[] -} - -type ExternalImportStatement = ImportStatement & Namespaced - -interface Namespaced { - __namespaced?: boolean; - uri: UriStr; - namespace: string; -} - -type ResolvedType = (ImportedModuleDefinition | ImportedObjectDefinition | ImportedEnumDefinition) & Namespaced; - -type ImportMap = Map< - string, ResolvedType ->; - -type ImportDefWithKind = { - extDefinition: ImportedModuleDefinition, - kind: "ImportedModule" -} | { - extDefinition: ImportedObjectDefinition, - kind: "ImportedObject" -} | { - extDefinition: ImportedEnumDefinition, - kind: "ImportedEnum" -} - -type LocalDefWithKind = { - extDefinition: ModuleDefinition, - kind: "Module" -} | { - extDefinition: ObjectDefinition, - kind: "Object" -} | { - extDefinition: EnumDefinition, - kind: "Enum" -} - -type ExternalDefinitionWithKind = ImportDefWithKind | LocalDefWithKind - - -export abstract class ImportsResolver { - constructor(protected mainAbi: WrapAbi, protected imports: TImportStatement[], protected importAbis: Map) { } - - protected determineKind(importAbi: WrapAbi, importType: string): ExternalDefinitionWithKind { - const extObj = importAbi.objectTypes?.find(def => def.type == importType) - if (extObj) { - return { extDefinition: extObj, kind: "Object" } - } - - const extImportObj = importAbi.importedObjectTypes?.find(def => def.type == importType); - if (extImportObj) { - return { extDefinition: extImportObj, kind: "ImportedObject" } - } - - const extEnum = importAbi.enumTypes?.find(def => def.type == importType); - if (extEnum) { - return { extDefinition: extEnum, kind: "Enum" } - } - - const extImportEnum = importAbi.importedEnumTypes?.find(def => def.type == importType); - if (extImportEnum) { - return { extDefinition: extImportEnum, kind: "ImportedEnum" } - } - - const extModule = importAbi.moduleType; - if (extModule && extModule.type == importType) { - return { extDefinition: extModule, kind: "Module" } - } - - const extModuleImport = importAbi.importedModuleTypes?.find(def => def.type == importType); - if (extModuleImport) { - return { extDefinition: extModuleImport, kind: "ImportedModule" } - } - - throw new Error(`Could not determine kind of imported type '${importType}'`) - } - - // private copyAllDefinitions(importAbi: WrapAbi) { - // if (importAbi) { - // this.mainAbi.importedEnumTypes = this.mainAbi.importedEnumTypes?.concat(importAbi.enumTypes ?? []) - // } - // } - - protected namespaceResolvedType(unnamespacedResolvedType: ResolvedType, uri: UriStr, namespace: string): ResolvedType { - // TODO: more performant way of doing it? - const objectCopy = JSON.parse(JSON.stringify(unnamespacedResolvedType)) as ResolvedType; - - objectCopy.namespace = namespace; - objectCopy.uri = uri; - objectCopy.type = `${namespace}_${objectCopy.type}`; - - return objectCopy - } - - protected _resolveImport(importAbi: WrapAbi, importType: string): { unnamespacedResolvedType: ResolvedType; visitor: Function } { - const extDefWithKind = this.determineKind(importAbi, importType) - // TODO: Namespace and Uri in TrueType - - switch (extDefWithKind.kind) { - case "Object": - case "ImportedObject": - return { - unnamespacedResolvedType: { - ...createImportedObjectDefinition({ - ...extDefWithKind.extDefinition, - type: importType, - name: undefined, - required: undefined, - nativeType: extDefWithKind.extDefinition.type, - uri: "", - namespace: "" - }), - properties: extDefWithKind.extDefinition.properties, - }, - - visitor: visitObjectDefinition - }; - case "Enum": - case "ImportedEnum": - return { - unnamespacedResolvedType: createImportedEnumDefinition({ - ...extDefWithKind.extDefinition, - type: importType, - name: undefined, - required: undefined, - nativeType: extDefWithKind.extDefinition.type, - uri: "", - namespace: "" - }), - visitor: visitEnumDefinition - }; - case "Module": - return { - unnamespacedResolvedType: { - ...createImportedModuleDefinition({ - ...extDefWithKind.extDefinition, - required: undefined, - nativeType: extDefWithKind.extDefinition.type, - uri: "", - namespace: "", - }), - methods: extDefWithKind.extDefinition.methods, - }, - visitor: visitModuleDefinition - }; - case "ImportedModule": - throw new Error(`Cannot import an import's imported ${extDefWithKind.kind}. Tried to import ${importType}`) - } - } - - - abstract resolveImportsStatement(importStatement: TImportStatement): ImportMap; - - - // TODO: Add all imported types into the aggregate Abi -} - -export class ExternalImportsResolver extends ImportsResolver { - protected extractDependencyTypes(importAbi: WrapAbi, genericDefinition: GenericDefinition): GenericDefinition[] { - - switch (genericDefinition.kind) { - case DefinitionKind.Scalar: - return []; - case DefinitionKind.ObjectRef: - return [] - case DefinitionKind.EnumRef: - return [genericDefinition]; - case DefinitionKind.Object: - case DefinitionKind.ImportedObject: - return [genericDefinition, ...(genericDefinition as ObjectDefinition).properties?.map(p => this.extractDependencyTypes(importAbi, p))] - } - } - - protected extractDeps(resolvedType: ResolvedType, extracted: ResolvedType[] = []) { - switch (resolvedType.kind) { - case DefinitionKind.Scalar: - return [] - case DefinitionKind.Object: - case DefinitionKind.ImportedObject: - const abi = this.importAbis.get(resolvedType.uri); - - (resolvedType as ObjectDefinition).properties?.forEach(property => { - - }) - - const objectDef = abi?.objectTypes?.find(obj => obj.type === resolvedType.nativeType); - objectDef?.properties?.forEach(property => { - - }) - - if - - const importObjectDef = abi?.importedObjectTypes?.find(obj => obj.type === resolvedType.nativeType) - - if (importObjectDef) { - this.extractDeps(importObjectDef) - } - } - } - - resolveImportsStatement(importStatatement: ExternalImportStatement): ImportMap { - const { uri, namespace, typeNames } = importStatatement; - const importAbi = this.importAbis.get(uri); - const typesToImport: ImportMap = new Map() - - if (!importAbi) { - throw Error(`Unable to resolve abi at "${uri}"`); - } - - if (typeNames.includes("*")) { - // TODO - } - - for (const typeName of typeNames) { - const { visitor, unnamespacedResolvedType } = this._resolveImport(importAbi, typeName) - - // TODO: EXTERNAL IMPORTS ONLY - - const namespacedResolvedType = this.namespaceResolvedType(unnamespacedResolvedType, uri, namespace) - - - //TODO: continue if we already imported it? - - typesToImport.set(namespacedResolvedType.type, namespacedResolvedType) - - visitor(namespacedResolvedType, extractImportDependencies()) - } - - return typesToImport; - } -} \ No newline at end of file From 5151f53200c4cbb76cf172887ceaa529b1260c0e Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 13 Feb 2023 17:42:22 +0100 Subject: [PATCH 37/90] (chore): made visitor support enter and leave --- packages/schema/linking/src/visitor.ts | 117 +++++++++++++++++++------ 1 file changed, 88 insertions(+), 29 deletions(-) diff --git a/packages/schema/linking/src/visitor.ts b/packages/schema/linking/src/visitor.ts index bdda105f8a..b2a66c8b2e 100644 --- a/packages/schema/linking/src/visitor.ts +++ b/packages/schema/linking/src/visitor.ts @@ -17,12 +17,17 @@ interface IAbiVisitor { AnyType?: (node: AnyType) => void; } +interface IAbiVisitorEnterAndLeave { + enter?: IAbiVisitor; + leave?: IAbiVisitor; +} + export class AbiVisitor { - constructor(private readonly visitor: IAbiVisitor) {} + constructor(private readonly visitor: IAbiVisitorEnterAndLeave) {} private _Abi(node: Abi) { - if (this.visitor.Abi) { - this.visitor.Abi(node); + if (this.visitor.enter?.Abi) { + this.visitor.enter.Abi(node); } if (node.imports) { @@ -48,11 +53,15 @@ export class AbiVisitor { this._EnumDef(enumNode); } } + + if (this.visitor.leave?.Abi) { + this.visitor.leave.Abi(node); + } } private _ImportedAbi(node: ImportedAbi) { - if (this.visitor.ImportedAbi) { - this.visitor.ImportedAbi(node); + if (this.visitor.enter?.ImportedAbi) { + this.visitor.enter.ImportedAbi(node); } if (node.functions) { @@ -72,11 +81,15 @@ export class AbiVisitor { this._EnumDef(enumNode); } } + + if (this.visitor.leave?.ImportedAbi) { + this.visitor.leave.ImportedAbi(node); + } } private _FunctionDef(node: FunctionDef) { - if (this.visitor.FunctionDef) { - this.visitor.FunctionDef(node); + if (this.visitor.enter?.FunctionDef) { + this.visitor.enter.FunctionDef(node); } if (node.args) { @@ -86,49 +99,71 @@ export class AbiVisitor { } this._ResultDef(node.result); + + if (this.visitor.leave?.FunctionDef) { + this.visitor.leave.FunctionDef(node); + } } private _ArgumentDef(node: ArgumentDef) { - if (this.visitor.ArgumentDef) { - this.visitor.ArgumentDef(node); + if (this.visitor.enter?.ArgumentDef) { + this.visitor.enter.ArgumentDef(node); } this._AnyType(node.type); + + if (this.visitor.leave?.ArgumentDef) { + this.visitor.leave.ArgumentDef(node); + } } private _ResultDef(node: ResultDef) { - if (this.visitor.ResultDef) { - this.visitor.ResultDef(node); + if (this.visitor.enter?.ResultDef) { + this.visitor.enter.ResultDef(node); } this._AnyType(node.type); + + if (this.visitor.leave?.ResultDef) { + this.visitor.leave.ResultDef(node); + } } private _ObjectDef(node: ObjectDef) { - if (this.visitor.ObjectDef) { - this.visitor.ObjectDef(node); + if (this.visitor.enter?.ObjectDef) { + this.visitor.enter.ObjectDef(node); } if (node.props) { for (const propertyNode of node.props) { this._PropertyDef(propertyNode); } } + if (this.visitor.leave?.ObjectDef) { + this.visitor.leave.ObjectDef(node); + } } private _PropertyDef(node: PropertyDef) { - if (this.visitor.PropertyDef) { - this.visitor.PropertyDef(node); + if (this.visitor.enter?.PropertyDef) { + this.visitor.enter.PropertyDef(node); } this._AnyType(node.type); + if (this.visitor.leave?.PropertyDef) { + this.visitor.leave?.PropertyDef(node); + } } private _EnumDef(node: EnumDef) { - if (this.visitor.EnumDef) { - this.visitor.EnumDef(node); + if (this.visitor.enter?.EnumDef) { + this.visitor.enter.EnumDef(node); + } + + if (this.visitor.leave?.EnumDef) { + this.visitor.leave.EnumDef(node); } } private _AnyType(node: AnyType) { - if (this.visitor.AnyType) { - this.visitor.AnyType(node); + if (this.visitor.enter?.AnyType) { + this.visitor.enter.AnyType(node); } switch (node.kind) { @@ -148,38 +183,62 @@ export class AbiVisitor { this._ImportRefType(node); break; } + + if (this.visitor.leave?.AnyType) { + this.visitor.leave.AnyType(node); + } } private _ScalarType(node: ScalarType) { - if (this.visitor.ScalarType) { - this.visitor.ScalarType(node); + if (this.visitor.enter?.ScalarType) { + this.visitor.enter.ScalarType(node); + } + + if (this.visitor.leave?.ScalarType) { + this.visitor.leave.ScalarType(node); } } private _RefType(node: RefType) { - if (this.visitor.RefType) { - this.visitor.RefType(node); + if (this.visitor.enter?.RefType) { + this.visitor.enter.RefType(node); + } + + if (this.visitor.leave?.RefType) { + this.visitor.leave.RefType(node); } } private _ImportRefType(node: ImportRefType) { - if (this.visitor.ImportRefType) { - this.visitor.ImportRefType(node); + if (this.visitor.enter?.ImportRefType) { + this.visitor.enter.ImportRefType(node); + } + + if (this.visitor.leave?.ImportRefType) { + this.visitor.leave.ImportRefType(node); } } private _ArrayType(node: ArrayType) { - if (this.visitor.ArrayType) { - this.visitor.ArrayType(node); + if (this.visitor.enter?.ArrayType) { + this.visitor.enter.ArrayType(node); } this._AnyType(node.item.type); + + if (this.visitor.leave?.ArrayType) { + this.visitor.leave.ArrayType(node); + } } private _MapType(node: MapType) { - if (this.visitor.MapType) { - this.visitor.MapType(node); + if (this.visitor.enter?.MapType) { + this.visitor.enter.MapType(node); } this._AnyType(node.value.type); + + if (this.visitor.leave?.MapType) { + this.visitor.leave.MapType(node); + } } visit(node: Abi) { From 01450f3ed9890c412d0a1c74cb34f6aa68deee44 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Tue, 14 Feb 2023 20:58:50 +0100 Subject: [PATCH 38/90] (feat): imports parser --- packages/schema/parse/src/DependencyTree.ts | 39 ++++ .../parse/src/__tests__/imports.spec.ts | 7 + packages/schema/parse/src/header.ts | 29 --- packages/schema/parse/src/imports.ts | 83 +++++++ .../parse/src/transform/addAnnotations.ts | 18 -- packages/schema/parse/src/transform/index.ts | 212 ------------------ packages/schema/parse/src/types.ts | 28 +++ .../schema/{linking => parse}/src/visitor.ts | 2 +- 8 files changed, 158 insertions(+), 260 deletions(-) create mode 100644 packages/schema/parse/src/DependencyTree.ts create mode 100644 packages/schema/parse/src/__tests__/imports.spec.ts delete mode 100644 packages/schema/parse/src/header.ts create mode 100644 packages/schema/parse/src/imports.ts delete mode 100644 packages/schema/parse/src/transform/addAnnotations.ts delete mode 100644 packages/schema/parse/src/transform/index.ts create mode 100644 packages/schema/parse/src/types.ts rename packages/schema/{linking => parse}/src/visitor.ts (99%) diff --git a/packages/schema/parse/src/DependencyTree.ts b/packages/schema/parse/src/DependencyTree.ts new file mode 100644 index 0000000000..03ce662f8e --- /dev/null +++ b/packages/schema/parse/src/DependencyTree.ts @@ -0,0 +1,39 @@ +export class DependencyTree { + private nodes: {[id: string]: TNodeContent} = {}; + private edges: {[id: string]: string[]} = {}; + + addNode(id: string, content: TNodeContent) { + this.nodes[id] = content; + this.edges[id] = []; + } + + addEdge(from: string, to: string) { + this.edges[from].push(to); + } + + hasNode(nodeId: string) { + return this.nodes.hasOwnProperty(nodeId); + } + + getNodeProperties(nodeId: string) { + return this.nodes[nodeId]; + } + + getNodeDependencies(nodeId: string) { + return this.edges[nodeId]; + } + + getAllDependencies(nodeIds: string[]): string[] { + const visited = new Set(); + const queue = [...nodeIds]; + while (queue.length > 0) { + const nodeId = queue.shift(); + if (nodeId && !visited.has(nodeId)) { + visited.add(nodeId); + const dependencies = this.getNodeDependencies(nodeId); + queue.push(...dependencies); + } + } + return Array.from(visited); + } +} \ No newline at end of file diff --git a/packages/schema/parse/src/__tests__/imports.spec.ts b/packages/schema/parse/src/__tests__/imports.spec.ts new file mode 100644 index 0000000000..3699746ba3 --- /dev/null +++ b/packages/schema/parse/src/__tests__/imports.spec.ts @@ -0,0 +1,7 @@ +import { ImportsParser } from "../imports" + +describe("Imports parser", () => { + it("Works", async () => { + const importsParser = new ImportsParser() + }) +}) \ No newline at end of file diff --git a/packages/schema/parse/src/header.ts b/packages/schema/parse/src/header.ts deleted file mode 100644 index 3f3d7a9413..0000000000 --- a/packages/schema/parse/src/header.ts +++ /dev/null @@ -1,29 +0,0 @@ -export const header = `### Polywrap Header START ### -scalar UInt -scalar UInt8 -scalar UInt16 -scalar UInt32 -scalar Int -scalar Int8 -scalar Int16 -scalar Int32 -scalar Bytes -scalar BigInt -scalar BigNumber -scalar JSON -scalar Map - -directive @imported( - uri: String! - namespace: String! - nativeType: String! -) on OBJECT | ENUM - -directive @imports( - types: [String!]! -) on OBJECT - -directive @annotate(type: String!) on FIELD - -### Polywrap Header END ### -`; diff --git a/packages/schema/parse/src/imports.ts b/packages/schema/parse/src/imports.ts new file mode 100644 index 0000000000..425f095d52 --- /dev/null +++ b/packages/schema/parse/src/imports.ts @@ -0,0 +1,83 @@ +import { DependencyTree } from "./DependencyTree"; +import { ExternalImportStatement, SchemaParser } from "./types"; +import { AbiVisitor } from "./visitor"; + +export class ImportsParser { + private _schemaDependencyTree = new DependencyTree() + private _defintionDependencyTree = new DependencyTree() + + constructor(private _schemaParser: SchemaParser, private _fetchers: { + external: (uri: string) => Promise + }) { } + + private async _getTransitiveDependencies(extImportStatements: ExternalImportStatement[]) { + for await (const externalImportStatement of extImportStatements) { + const extImportUri = externalImportStatement.uriOrPath + const externalSchema = await this._fetchers.external(extImportUri) + const importedTypes = externalImportStatement.importedTypes; + + this._schemaDependencyTree.addNode(extImportUri, externalSchema) + externalImportStatement.importedTypes.forEach(importedType => this._defintionDependencyTree.addNode(importedType, extImportUri)) + + const externalAbi = await this._schemaParser.parse(externalSchema) + const transitiveImports = await this._schemaParser.parseExternalImportStatements(externalSchema) + const additionalImports = new Set<[string, string]>() + const transitiveImportDeps = new Set() + + const state: { currentObject?: string } = {} + const externalAbiVisitor = new AbiVisitor({ + enter: { + ObjectDef: (def) => { + if (importedTypes.includes(def.name)) { + state.currentObject = def.name + } + }, + RefType: (ref) => { + if (state.currentObject && + !importedTypes.includes(ref.ref_name)) { + additionalImports.add([state.currentObject, ref.ref_name]) + } + }, + ImportRefType: (ref) => { + if (state.currentObject) { + const transitiveDependency = transitiveImports.find(t => t.importedTypes.includes(ref.ref_name)) + + if (!transitiveDependency) { + throw new Error(`Found import reference to '${ref.ref_name}' which isn't an imported definition`) + } + + transitiveImportDeps.add(transitiveDependency) + } + } + }, + leave: { + ObjectDef: () => { + state.currentObject = undefined + } + } + }) + externalAbiVisitor.visit(externalAbi) + + additionalImports.forEach(([dependentType, dependencyType]) => { + this._defintionDependencyTree.addNode(dependencyType, extImportUri) + this._defintionDependencyTree.addEdge(dependentType, dependencyType) + }) + + transitiveImportDeps.forEach(transitiveDep => { + this._schemaDependencyTree.addEdge(extImportUri, transitiveDep.uriOrPath) + }) + + await this._getTransitiveDependencies([...transitiveImportDeps]) + } + + return { + definitionDependencyTree: this._defintionDependencyTree, + schemaDependencyTree: this._schemaDependencyTree + } + } + + async getImports(rootSchema: string, parser: SchemaParser) { + const externalImportStatements = await parser.parseExternalImportStatements(rootSchema); + return this._getTransitiveDependencies(externalImportStatements) + } +} diff --git a/packages/schema/parse/src/transform/addAnnotations.ts b/packages/schema/parse/src/transform/addAnnotations.ts deleted file mode 100644 index 242ecfb09e..0000000000 --- a/packages/schema/parse/src/transform/addAnnotations.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { AbiTransforms } from ".."; -import { PropertyDef } from "../definitions"; -import { toGraphQL } from "./toGraphQLType"; - -export const addAnnotations: AbiTransforms = { - enter: { - PropertyDefinition: (def) => { - const typeInProperty = def.type; - if (typeInProperty.kind !== "Map") return def; - - return { - ...def, - toGraphQLType: (): string => - `Map${def.required ? "!" : ""} @annotate(type: "${toGraphQL(typeInProperty, def.required)}")`, - } as PropertyDef; - }, - }, -}; diff --git a/packages/schema/parse/src/transform/index.ts b/packages/schema/parse/src/transform/index.ts deleted file mode 100644 index 8162476c71..0000000000 --- a/packages/schema/parse/src/transform/index.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { Abi, AnyType, AnyTypeOrDef, ArgumentDef, ArrayType, Def, EnumDef, FunctionDef, ImportRefType, MapKeyTypeName, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType, Type } from "../definitions"; - -export interface AbiTransforms { - enter?: AbiTransformer; - leave?: AbiTransformer; -} - -export interface AbiTransformer { - FunctionDefinition?: (def: FunctionDef) => FunctionDef - ObjectDefinition?: (def: ObjectDef) => ObjectDef - EnumDefinition?: (def: EnumDef) => EnumDef - - ArgumentDefinition?: (def: ArgumentDef) => ArgumentDef - ResultDefinition?: (def: ResultDef) => ResultDef - PropertyDefinition?: (def: PropertyDef) => PropertyDef - - ScalarType?: (type: ScalarType) => ScalarType - ArrayType?: (type: ArrayType) => ArrayType - MapType?: (type: MapType) => MapType - RefType?: (type: RefType) => RefType - ImportRefType?: (type: ImportRefType) => ImportRefType - - DefinitionOrType?: (def: T) => T - Abi?: (abi: Abi) => Abi -} - -export const transformAbi = (abi: Abi, transforms: AbiTransforms): Abi => { - let result = Object.assign({}, abi); - - if (transforms.enter && transforms.enter.Abi) { - result = transforms.enter.Abi(result); - } - - result.enums = result.enums?.map(def => visitEnumDefinition(def, transforms)) - result.objects = result.objects?.map(def => visitObjectDefinition(def, transforms)) - result.functions = result.functions?.map(def => visitFunctionDefinition(def, transforms)) - - if (transforms.leave && transforms.leave.Abi) { - result = transforms.leave.Abi(result); - } - - return result; -} - -const transformType = (type: T, transform?: AbiTransformer): T => { - if (!transform) { - return type; - } - - let result: T = Object.assign({}, type); - const { - FunctionDefinition, - ObjectDefinition, - EnumDefinition, - ArgumentDefinition, - ResultDefinition, - PropertyDefinition, - ScalarType, - ArrayType, - MapType, - RefType, - DefinitionOrType, - ImportRefType, - } = transform; - - if (DefinitionOrType) { - result = Object.assign(result, DefinitionOrType(result)) - } - - if (FunctionDefinition && result.kind === "Function") { - result = Object.assign(result, FunctionDefinition(result as FunctionDef)) - } - - if (ObjectDefinition && result.kind === "Object") { - result = Object.assign(result, ObjectDefinition(result as ObjectDef)) - } - - if (EnumDefinition && result.kind === "Enum") { - result = Object.assign(result, EnumDefinition(result as EnumDef)) - } - - if (ArgumentDefinition && result.kind === "Argument") { - result = Object.assign(result, ArgumentDefinition(result as ArgumentDef)) - } - - if (ResultDefinition && result.kind === "Result") { - result = Object.assign(result, ResultDefinition(result as ResultDef)) - } - - if (PropertyDefinition && result.kind === "Property") { - result = Object.assign(result, PropertyDefinition(result as PropertyDef)) - } - - if (ScalarType && result.kind === "Scalar") { - result = Object.assign(result, ScalarType(result as ScalarType)) - } - - if (ArrayType && result.kind === "Array") { - result = Object.assign(result, ArrayType(result as ArrayType)) - } - - if (MapType && result.kind === "Map") { - result = Object.assign(result, MapType(result as MapType)) - } - - if (RefType && result.kind === "Ref") { - result = Object.assign(result, RefType(result as RefType)) - } - - if (ImportRefType && result.kind === "ImportRef") { - result = Object.assign(result, ImportRefType(result as ImportRefType)) - } - - return result; -} - -const visitRefType = (type: RefType, transforms: AbiTransforms) => { - let result = Object.assign({}, type); - result = transformType(result, transforms.enter) - - return transformType(result, transforms.leave) -} - -const visitImportRefType = (type: ImportRefType, transforms: AbiTransforms) => { - let result = Object.assign({}, type); - result = transformType(result, transforms.enter) - - return transformType(result, transforms.leave) -} - -const visitScalarType = (type: ScalarType, transforms: AbiTransforms) => { - let result = Object.assign({}, type); - result = transformType(result, transforms.enter) - - return transformType(result, transforms.leave); -} - -const visitArrayType = (type: ArrayType, transforms: AbiTransforms) => { - let result = Object.assign({}, type); - result = transformType(result, transforms.enter) - - result.item.type = visitAnyType(result.item.type, transforms) - return transformType(result, transforms.leave); -} - -const visitMapType = (type: MapType, transforms: AbiTransforms) => { - let result = Object.assign({}, type); - result = transformType(result, transforms.enter) - - result.key = visitScalarType(result.key, transforms) as ScalarType - result.value.type = visitAnyType(result.value.type, transforms) - return transformType(result, transforms.leave); -} - -const visitAnyType = (type: AnyType, transforms: AbiTransforms) => { - switch (type.kind) { - case "Ref": return visitRefType(type, transforms); - case "Array": return visitArrayType(type, transforms); - case "Scalar": return visitScalarType(type, transforms); - case "Map": return visitMapType(type, transforms); - case "ImportRef": return visitImportRefType(type, transforms); - } -} - -const visitPropertyDefinition = (def: PropertyDef, transforms: AbiTransforms) => { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter) - result.type = visitAnyType(result.type, transforms); - - return transformType(result, transforms.leave) -} - -const visitObjectDefinition = (def: ObjectDef, transforms: AbiTransforms) => { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - - result.props.forEach((prop, i) => { - result.props[i] = visitPropertyDefinition(prop, transforms) - }) - - return transformType(result, transforms.leave) -} - -const visitEnumDefinition = (def: EnumDef, transforms: AbiTransforms) => { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - return transformType(result, transforms.leave) -} - -const visitArgumentDefinition = (def: ArgumentDef, transforms: AbiTransforms) => { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - result.type = visitAnyType(result.type, transforms); - return transformType(result, transforms.leave) -} - -const visitResultDefinition = (def: ResultDef, transforms: AbiTransforms) => { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - result.type = visitAnyType(result.type, transforms); - return transformType(result, transforms.leave) -} - -const visitFunctionDefinition = (def: FunctionDef, transforms: AbiTransforms) => { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - - result.args = result.args.map(arg => visitArgumentDefinition(arg, transforms)) - result.result = visitResultDefinition(result.result, transforms) - - return transformType(result, transforms.leave) -} \ No newline at end of file diff --git a/packages/schema/parse/src/types.ts b/packages/schema/parse/src/types.ts new file mode 100644 index 0000000000..6ccb54ff5d --- /dev/null +++ b/packages/schema/parse/src/types.ts @@ -0,0 +1,28 @@ +import { UniqueDefKind, Abi } from "./definitions"; + +export interface ImportStatement { + kind: "local" | "external"; + importedTypes: string[]; + uriOrPath: string; +} + +export interface ExternalImportStatement extends ImportStatement { + kind: "external"; + namespace: string; +} + +export interface LocalImportStatement extends ImportStatement { + kind: "local"; +} + +export interface SchemaParser { + parseExternalImportStatements: (schema: string) => Promise + getImportStatements: (schema: string) => Promise + getImportedSchemasTable: (schema: string, schemaPath: string) => Promise> + getUniqueDefinitionsTable: (schema: string) => Promise> + parse: (schema: string) => Promise +} + +export interface ParserOptions { + noValidate?: boolean; +} \ No newline at end of file diff --git a/packages/schema/linking/src/visitor.ts b/packages/schema/parse/src/visitor.ts similarity index 99% rename from packages/schema/linking/src/visitor.ts rename to packages/schema/parse/src/visitor.ts index b2a66c8b2e..f96264e284 100644 --- a/packages/schema/linking/src/visitor.ts +++ b/packages/schema/parse/src/visitor.ts @@ -23,7 +23,7 @@ interface IAbiVisitorEnterAndLeave { } export class AbiVisitor { - constructor(private readonly visitor: IAbiVisitorEnterAndLeave) {} + constructor(private readonly visitor: IAbiVisitorEnterAndLeave) { } private _Abi(node: Abi) { if (this.visitor.enter?.Abi) { From 4943c6ae2af68cebc97f5666914eb3cbbcb02a04 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Wed, 15 Feb 2023 23:19:49 +0100 Subject: [PATCH 39/90] (feat): unlinked defs --- packages/schema/parse/src/UnlinkedDefs.ts | 81 +++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 packages/schema/parse/src/UnlinkedDefs.ts diff --git a/packages/schema/parse/src/UnlinkedDefs.ts b/packages/schema/parse/src/UnlinkedDefs.ts new file mode 100644 index 0000000000..1f8c4ed6fd --- /dev/null +++ b/packages/schema/parse/src/UnlinkedDefs.ts @@ -0,0 +1,81 @@ +import { Def, EnumDef, MapKeyTypeName, NamedDef, RefType, ScalarType } from "./definitions"; + +export interface UnlinkedAbiDefs { + functions?: UnlinkedFunctionDef[]; + objects?: UnlinkedObjectDef[]; + enums?: EnumDef[]; +} + +export type UnlinkedAnyDef = + | UnlinkedFunctionDef + | UnlinkedObjectDef + | EnumDef + | UnlinkedPropertyDef + | UnlinkedArgumentDef + | UnlinkedResultDef; + +export interface UnlinkedInlinedTypeDef extends Def, UnlinkedOptionalType { } + +export interface UnlinkedNamedTypeDef extends NamedDef, UnlinkedInlinedTypeDef { } + +export interface UnlinkedFunctionDef extends NamedDef { + kind: "Function"; + args: UnlinkedArgumentDef[]; + result: UnlinkedResultDef; +} + +export interface UnlinkedArgumentDef extends UnlinkedNamedTypeDef { + kind: "Argument"; +} + +export interface UnlinkedResultDef extends UnlinkedInlinedTypeDef { + kind: "Result"; +} + +export interface UnlinkedObjectDef extends NamedDef { + kind: "Object"; + props: UnlinkedPropertyDef[]; +} + +export interface UnlinkedPropertyDef extends UnlinkedNamedTypeDef { + kind: "Property"; +} + +export type UnlinkedAnyType = + | ScalarType + | UnlinkedArrayType + | UnlinkedMapType + | RefType + | UnlinkedImportRefType; + +export type UnlinkedTypeKind = + | "Scalar" + | "Array" + | "Map" + | "Ref" + | "UnlinkedImportRef"; + +export interface UnlinkedType { + kind: UnlinkedTypeKind; +} + +export interface UnlinkedArrayType extends UnlinkedType { + kind: "Array"; + item: UnlinkedOptionalType; +} + +export interface UnlinkedMapType extends UnlinkedType { + kind: "Map"; + key: ScalarType; + value: UnlinkedOptionalType; +} + +export interface UnlinkedImportRefType extends UnlinkedType { + kind: "UnlinkedImportRef"; + ref_name: string; +} + +export interface UnlinkedOptionalType { + required: boolean; + type: UnlinkedAnyType; +} From 236199f3fdb23e7008bd03b016abc46feec7394a Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Wed, 15 Feb 2023 23:45:07 +0100 Subject: [PATCH 40/90] (feat): test for imports resolution --- .../parse/src/__tests__/imports.spec.ts | 157 +++++++++++++++++- 1 file changed, 156 insertions(+), 1 deletion(-) diff --git a/packages/schema/parse/src/__tests__/imports.spec.ts b/packages/schema/parse/src/__tests__/imports.spec.ts index 3699746ba3..29f3e8cc9a 100644 --- a/packages/schema/parse/src/__tests__/imports.spec.ts +++ b/packages/schema/parse/src/__tests__/imports.spec.ts @@ -1,7 +1,162 @@ +import { UniqueDefKind } from "../definitions" import { ImportsParser } from "../imports" +import { ExternalImportStatement, SchemaParser } from "../types" +import { UnlinkedAbiDefs } from "../UnlinkedDefs" describe("Imports parser", () => { it("Works", async () => { - const importsParser = new ImportsParser() + + const rootSchema = ` + import { Foo } into NamespaceFoo from "foo" + + type Baz { + foo: NamespaceFoo_Foo! + prop: String! + } + ` + + const fooSchema = ` + import { Bar } into NamespaceBar from "bar" + + type Some { + propSome: String! + } + + type Additional { + bar: NamespaceBar_Bar! + } + + type Foo { + propFoo: Additional! + } + ` + + const barSchema = ` + type Bar { + propBar: String! + } + ` + + const fetchers = { + external: async (uri: string): Promise => { + switch (uri) { + case "foo": return fooSchema + case "bar": return barSchema + case "root": return rootSchema + default: throw new Error(`Unknown URI: ${uri}`) + } + } + } + + const mockSchemaParser: SchemaParser = { + getImportedSchemasTable: (schema: string, schemaPath: string): Promise> => { + throw new Error("Unimplemented getImportedSchemasTable") + }, + getUniqueDefinitionsTable: (schema: string): Promise> => { + throw new Error("Unimplemented getUniqueDefinitionsTable") + }, + getImportStatements: async (schema: string): Promise => { + throw new Error("Unimplemented getImportStatements") + }, + parse: async (schema: string): Promise => { + switch (schema) { + case rootSchema: return { + objects: [{ + kind: "Object", + props: [{ + kind: "Property", + name: "foo", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "Foo", + } + }], + name: "Baz", + }] + }; + case fooSchema: return { + objects: [{ + kind: "Object", + name: "Some", + props: [{ + kind: "Property", + name: "propFoo", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + }] + }, { + kind: "Object", + name: "Additional", + props: [{ + kind: "Property", + name: "bar", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "Bar", + } + }] + }, { + kind: "Object", + name: "Foo", + props: [{ + kind: "Property", + name: "propFoo", + required: true, + type: { + kind: "Ref", + ref_kind: "Object", + ref_name: "Additional", + } + }] + }] + } + case barSchema: return { + objects: [{ + kind: "Object", + name: "Bar", + props: [{ + kind: "Property", + name: "propBar", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + }] + }] + } + default: throw new Error(`Unknown schema: ${schema}`) + } + }, + parseExternalImportStatements: async (schema: string): Promise => { + switch (schema) { + case rootSchema: return [{ + kind: "external", + uriOrPath: "foo", + importedTypes: ["Foo"], + namespace: "NamespaceFoo" + }] + case fooSchema: return [{ + kind: "external", + uriOrPath: "bar", + importedTypes: ["Bar"], + namespace: "NamespaceBar" + }] + case barSchema: return []; + default: throw new Error(`Unknown schema: ${schema}`) + } + } + } + + const importsParser = new ImportsParser(mockSchemaParser, fetchers) + const { definitionDependencyTree, schemaDependencyTree } = await importsParser.getImports(rootSchema, mockSchemaParser) + + console.log(JSON.stringify(definitionDependencyTree, null, 2)) + console.log(JSON.stringify(schemaDependencyTree, null, 2)) }) }) \ No newline at end of file From a4c7f6ea3bc197841b79604dc22c4966960d4d38 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Wed, 15 Feb 2023 23:46:09 +0100 Subject: [PATCH 41/90] (chore): wip unlinked defs parsing --- packages/schema/parse/src/imports.ts | 4 +- packages/schema/parse/src/index.ts | 65 +++++++--------- packages/schema/parse/src/types.ts | 5 +- packages/schema/parse/src/visitor.ts | 107 +++++++++++---------------- 4 files changed, 73 insertions(+), 108 deletions(-) diff --git a/packages/schema/parse/src/imports.ts b/packages/schema/parse/src/imports.ts index 425f095d52..2128ce3944 100644 --- a/packages/schema/parse/src/imports.ts +++ b/packages/schema/parse/src/imports.ts @@ -1,6 +1,6 @@ import { DependencyTree } from "./DependencyTree"; import { ExternalImportStatement, SchemaParser } from "./types"; -import { AbiVisitor } from "./visitor"; +import { UnlinkedAbiVisitor } from "./visitor"; export class ImportsParser { private _schemaDependencyTree = new DependencyTree() @@ -25,7 +25,7 @@ export class ImportsParser { const transitiveImportDeps = new Set() const state: { currentObject?: string } = {} - const externalAbiVisitor = new AbiVisitor({ + const externalAbiVisitor = new UnlinkedAbiVisitor({ enter: { ObjectDef: (def) => { if (importedTypes.includes(def.name)) { diff --git a/packages/schema/parse/src/index.ts b/packages/schema/parse/src/index.ts index 1c7b74321b..a83a80031d 100644 --- a/packages/schema/parse/src/index.ts +++ b/packages/schema/parse/src/index.ts @@ -1,47 +1,34 @@ -import { AbiTransforms, transformAbi } from "./transform"; - -import { Abi, UniqueDefKind } from "./definitions"; -import { SchemaParser } from "./types"; +import { Abi } from "./definitions"; +import { ParserOptions, SchemaParser } from "./types"; export * from "./abi"; -export * from "./transform"; -export * from "./header"; export * from "./types"; -interface ParserOptions { - // Disable schema validation - noValidate?: boolean; - // Use custom transformations - transforms?: AbiTransforms[]; -} - export const parseSchemaAndImports = async (schema: string, schemaPath: string, parser: SchemaParser, options: ParserOptions = {}): Promise<{ abi: Abi, imports: Map }> => { - const importsRegistry = await parser.getImportedSchemasTable(schema, schemaPath); - let allUniqueDefinitions = new Map(); + throw new Error("Unimplemented") - for (const importedAbi of importsRegistry.values()) { - allUniqueDefinitions = new Map([...allUniqueDefinitions, ...parser.getUniqueDefinitionsTable(importedAbi)]); - } - - const importedAbis = new Map(); - - for (const [importPath, importedAbi] of importsRegistry.entries()) { - importedAbis.set(importPath, parser.parse(importedAbi, allUniqueDefinitions)); - } - - // TODO: should this happen before or after linking? - // TODO: where does validation happen? - let abi = parser.parse(schema, allUniqueDefinitions); - - if (options && options.transforms) { - for (const transform of options.transforms) { - abi = transformAbi(abi, transform); - } - } - - return { - abi, - imports: importedAbis - } + // const importsRegistry = await parser.getImportedSchemasTable(schema, schemaPath); + // let allUniqueDefinitions = new Map(); + + // for await (const importedAbi of importsRegistry.values()) { + // const importedAbiUniqueDefs = await parser.getUniqueDefinitionsTable(importedAbi); + // allUniqueDefinitions = new Map([...allUniqueDefinitions, ...importedAbiUniqueDefs]); + // } + + // const importedAbis = new Map(); + + // for await (const [importPath, importedSchemaString] of importsRegistry.entries()) { + // const importedAbi = await parser.parse(importedSchemaString, allUniqueDefinitions); + // importedAbis.set(importPath, importedAbi); + // } + + // // TODO: should this happen before or after linking? + // // TODO: where does validation happen? + // let abi = await parser.parse(schema, allUniqueDefinitions); + + // return { + // abi, + // imports: importedAbis + // } } diff --git a/packages/schema/parse/src/types.ts b/packages/schema/parse/src/types.ts index 6ccb54ff5d..eebd1944e7 100644 --- a/packages/schema/parse/src/types.ts +++ b/packages/schema/parse/src/types.ts @@ -1,4 +1,5 @@ -import { UniqueDefKind, Abi } from "./definitions"; +import { UniqueDefKind } from "./definitions"; +import { UnlinkedAbiDefs } from "./UnlinkedDefs"; export interface ImportStatement { kind: "local" | "external"; @@ -20,7 +21,7 @@ export interface SchemaParser { getImportStatements: (schema: string) => Promise getImportedSchemasTable: (schema: string, schemaPath: string) => Promise> getUniqueDefinitionsTable: (schema: string) => Promise> - parse: (schema: string) => Promise + parse: (schema: string) => Promise } export interface ParserOptions { diff --git a/packages/schema/parse/src/visitor.ts b/packages/schema/parse/src/visitor.ts index f96264e284..a57282bf7f 100644 --- a/packages/schema/parse/src/visitor.ts +++ b/packages/schema/parse/src/visitor.ts @@ -1,41 +1,46 @@ -import { Abi, ImportedAbi, FunctionDef, ArgumentDef, ResultDef, ObjectDef, PropertyDef, EnumDef, ScalarType, RefType, ImportRefType, ArrayType, MapType, AnyType } from "@polywrap/schema-parse/build/definitions"; - -interface IAbiVisitor { - Abi?: (node: Abi) => void; - ImportedAbi?: (node: ImportedAbi) => void; - FunctionDef?: (node: FunctionDef) => void; - ArgumentDef?: (node: ArgumentDef) => void; - ResultDef?: (node: ResultDef) => void; - ObjectDef?: (node: ObjectDef) => void; - PropertyDef?: (node: PropertyDef) => void; +import { EnumDef, ScalarType, RefType } from "./definitions"; +import { + UnlinkedAbiDefs, + UnlinkedFunctionDef, + UnlinkedArgumentDef, + UnlinkedResultDef, + UnlinkedObjectDef, + UnlinkedPropertyDef, + UnlinkedImportRefType, + UnlinkedAnyType, + UnlinkedArrayType, + UnlinkedMapType +} from "./UnlinkedDefs"; + +interface IUnlinkedAbiVisitor { + Abi?: (node: UnlinkedAbiDefs) => void; + FunctionDef?: (node: UnlinkedFunctionDef) => void; + ArgumentDef?: (node: UnlinkedArgumentDef) => void; + ResultDef?: (node: UnlinkedResultDef) => void; + ObjectDef?: (node: UnlinkedObjectDef) => void; + PropertyDef?: (node: UnlinkedPropertyDef) => void; EnumDef?: (node: EnumDef) => void; ScalarType?: (node: ScalarType) => void; RefType?: (node: RefType) => void; - ImportRefType?: (node: ImportRefType) => void; - ArrayType?: (node: ArrayType) => void; - MapType?: (node: MapType) => void; - AnyType?: (node: AnyType) => void; + ImportRefType?: (node: UnlinkedImportRefType) => void; + ArrayType?: (node: UnlinkedArrayType) => void; + MapType?: (node: UnlinkedMapType) => void; + AnyType?: (node: UnlinkedAnyType) => void; } -interface IAbiVisitorEnterAndLeave { - enter?: IAbiVisitor; - leave?: IAbiVisitor; +interface IUnlinkedAbiVisitorEnterAndLeave { + enter?: IUnlinkedAbiVisitor; + leave?: IUnlinkedAbiVisitor; } -export class AbiVisitor { - constructor(private readonly visitor: IAbiVisitorEnterAndLeave) { } +export class UnlinkedAbiVisitor { + constructor(private readonly visitor: IUnlinkedAbiVisitorEnterAndLeave) { } - private _Abi(node: Abi) { + private _Abi(node: UnlinkedAbiDefs) { if (this.visitor.enter?.Abi) { this.visitor.enter.Abi(node); } - if (node.imports) { - for (const importNode of node.imports) { - this._ImportedAbi(importNode); - } - } - if (node.functions) { for (const functionNode of node.functions) { this._FunctionDef(functionNode); @@ -59,35 +64,7 @@ export class AbiVisitor { } } - private _ImportedAbi(node: ImportedAbi) { - if (this.visitor.enter?.ImportedAbi) { - this.visitor.enter.ImportedAbi(node); - } - - if (node.functions) { - for (const functionNode of node.functions) { - this._FunctionDef(functionNode); - } - } - - if (node.objects) { - for (const objectNode of node.objects) { - this._ObjectDef(objectNode); - } - } - - if (node.enums) { - for (const enumNode of node.enums) { - this._EnumDef(enumNode); - } - } - - if (this.visitor.leave?.ImportedAbi) { - this.visitor.leave.ImportedAbi(node); - } - } - - private _FunctionDef(node: FunctionDef) { + private _FunctionDef(node: UnlinkedFunctionDef) { if (this.visitor.enter?.FunctionDef) { this.visitor.enter.FunctionDef(node); } @@ -105,7 +82,7 @@ export class AbiVisitor { } } - private _ArgumentDef(node: ArgumentDef) { + private _ArgumentDef(node: UnlinkedArgumentDef) { if (this.visitor.enter?.ArgumentDef) { this.visitor.enter.ArgumentDef(node); } @@ -116,7 +93,7 @@ export class AbiVisitor { } } - private _ResultDef(node: ResultDef) { + private _ResultDef(node: UnlinkedResultDef) { if (this.visitor.enter?.ResultDef) { this.visitor.enter.ResultDef(node); } @@ -127,7 +104,7 @@ export class AbiVisitor { } } - private _ObjectDef(node: ObjectDef) { + private _ObjectDef(node: UnlinkedObjectDef) { if (this.visitor.enter?.ObjectDef) { this.visitor.enter.ObjectDef(node); } @@ -141,7 +118,7 @@ export class AbiVisitor { } } - private _PropertyDef(node: PropertyDef) { + private _PropertyDef(node: UnlinkedPropertyDef) { if (this.visitor.enter?.PropertyDef) { this.visitor.enter.PropertyDef(node); } @@ -161,7 +138,7 @@ export class AbiVisitor { } } - private _AnyType(node: AnyType) { + private _AnyType(node: UnlinkedAnyType) { if (this.visitor.enter?.AnyType) { this.visitor.enter.AnyType(node); } @@ -179,7 +156,7 @@ export class AbiVisitor { case "Ref": this._RefType(node); break; - case "ImportRef": + case "UnlinkedImportRef": this._ImportRefType(node); break; } @@ -209,7 +186,7 @@ export class AbiVisitor { } } - private _ImportRefType(node: ImportRefType) { + private _ImportRefType(node: UnlinkedImportRefType) { if (this.visitor.enter?.ImportRefType) { this.visitor.enter.ImportRefType(node); } @@ -219,7 +196,7 @@ export class AbiVisitor { } } - private _ArrayType(node: ArrayType) { + private _ArrayType(node: UnlinkedArrayType) { if (this.visitor.enter?.ArrayType) { this.visitor.enter.ArrayType(node); } @@ -230,7 +207,7 @@ export class AbiVisitor { } } - private _MapType(node: MapType) { + private _MapType(node: UnlinkedMapType) { if (this.visitor.enter?.MapType) { this.visitor.enter.MapType(node); } @@ -241,7 +218,7 @@ export class AbiVisitor { } } - visit(node: Abi) { + visit(node: UnlinkedAbiDefs) { this._Abi(node); } } \ No newline at end of file From 6718a21ecf541d86de2626ecb4bbd5f5af8cac85 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Thu, 16 Feb 2023 01:00:09 +0100 Subject: [PATCH 42/90] (chore): imports resolution algorithm working --- .../parse/src/__tests__/imports.spec.ts | 21 +++- packages/schema/parse/src/imports.ts | 116 +++++++++++------- 2 files changed, 91 insertions(+), 46 deletions(-) diff --git a/packages/schema/parse/src/__tests__/imports.spec.ts b/packages/schema/parse/src/__tests__/imports.spec.ts index 29f3e8cc9a..ff6b1181ae 100644 --- a/packages/schema/parse/src/__tests__/imports.spec.ts +++ b/packages/schema/parse/src/__tests__/imports.spec.ts @@ -33,7 +33,11 @@ describe("Imports parser", () => { const barSchema = ` type Bar { - propBar: String! + propBar: Transitive! + } + + type Transitive { + propTransitive: String! } ` @@ -123,6 +127,19 @@ describe("Imports parser", () => { kind: "Property", name: "propBar", required: true, + type: { + kind: "Ref", + ref_kind: "Object", + ref_name: "Transitive" + } + }] + }, { + kind: "Object", + name: "Transitive", + props: [{ + kind: "Property", + name: "propTransitive", + required: true, type: { kind: "Scalar", scalar: "String" @@ -158,5 +175,7 @@ describe("Imports parser", () => { console.log(JSON.stringify(definitionDependencyTree, null, 2)) console.log(JSON.stringify(schemaDependencyTree, null, 2)) + + console.log(definitionDependencyTree.getAllDependencies(["Foo"])) }) }) \ No newline at end of file diff --git a/packages/schema/parse/src/imports.ts b/packages/schema/parse/src/imports.ts index 2128ce3944..7b03ea6fed 100644 --- a/packages/schema/parse/src/imports.ts +++ b/packages/schema/parse/src/imports.ts @@ -1,5 +1,6 @@ import { DependencyTree } from "./DependencyTree"; import { ExternalImportStatement, SchemaParser } from "./types"; +import { UnlinkedAbiDefs } from "./UnlinkedDefs"; import { UnlinkedAbiVisitor } from "./visitor"; export class ImportsParser { @@ -10,6 +11,73 @@ export class ImportsParser { external: (uri: string) => Promise }) { } + private _extractAdditionaAndTransitiveDeps(typesToVisit: string[], transitiveImports: ExternalImportStatement[], abiToVisitUri: string, abiToVisit: UnlinkedAbiDefs, typesToSkip: string[] = []): { + typesToImport: Set<[string, string]>, + transitiveImportDeps: Set, + } { + typesToVisit = typesToVisit.filter(t => !typesToSkip.includes(t)) + const typesToImport = new Set<[string, string]>() + const transitiveImportDeps = new Set() + + if (!typesToVisit.length) { + return { + typesToImport, + transitiveImportDeps + } + } + + const state: { currentObject?: string } = {} + const externalAbiVisitor = new UnlinkedAbiVisitor({ + enter: { + ObjectDef: (def) => { + if (typesToVisit.includes(def.name)) { + state.currentObject = def.name + } + }, + RefType: (ref) => { + if (state.currentObject && + !typesToVisit.includes(ref.ref_name)) { + typesToImport.add([state.currentObject, ref.ref_name]) + + this._defintionDependencyTree.addNode(ref.ref_name, abiToVisitUri) + this._defintionDependencyTree.addEdge(state.currentObject, ref.ref_name) + } + }, + ImportRefType: (ref) => { + if (state.currentObject) { + const transitiveDependency = transitiveImports.find(t => t.importedTypes.includes(ref.ref_name)) + + if (!transitiveDependency) { + throw new Error(`Found import reference to '${ref.ref_name}' which isn't an imported definition`) + } + + transitiveImportDeps.add(transitiveDependency) + + this._defintionDependencyTree.addNode(ref.ref_name, abiToVisitUri) + this._defintionDependencyTree.addEdge(state.currentObject, ref.ref_name) + this._schemaDependencyTree.addEdge(abiToVisitUri, transitiveDependency.uriOrPath) + } + } + }, + leave: { + ObjectDef: () => { + state.currentObject = undefined + } + } + }) + externalAbiVisitor.visit(abiToVisit) + + const { + typesToImport: resultingTypesToImport, + transitiveImportDeps: resultingTransitiveImportDeps, + } = this._extractAdditionaAndTransitiveDeps([...typesToImport.values()].map(([_, dep]) => dep), transitiveImports, abiToVisitUri, abiToVisit, [...typesToSkip, ...typesToVisit]) + + return { + typesToImport: new Set([...typesToImport, ...resultingTypesToImport]), + transitiveImportDeps: new Set([...transitiveImportDeps, ...resultingTransitiveImportDeps]), + } + } + private async _getTransitiveDependencies(extImportStatements: ExternalImportStatement[]) { for await (const externalImportStatement of extImportStatements) { const extImportUri = externalImportStatement.uriOrPath @@ -21,51 +89,9 @@ export class ImportsParser { const externalAbi = await this._schemaParser.parse(externalSchema) const transitiveImports = await this._schemaParser.parseExternalImportStatements(externalSchema) - const additionalImports = new Set<[string, string]>() - const transitiveImportDeps = new Set() - - const state: { currentObject?: string } = {} - const externalAbiVisitor = new UnlinkedAbiVisitor({ - enter: { - ObjectDef: (def) => { - if (importedTypes.includes(def.name)) { - state.currentObject = def.name - } - }, - RefType: (ref) => { - if (state.currentObject && - !importedTypes.includes(ref.ref_name)) { - additionalImports.add([state.currentObject, ref.ref_name]) - } - }, - ImportRefType: (ref) => { - if (state.currentObject) { - const transitiveDependency = transitiveImports.find(t => t.importedTypes.includes(ref.ref_name)) - - if (!transitiveDependency) { - throw new Error(`Found import reference to '${ref.ref_name}' which isn't an imported definition`) - } - - transitiveImportDeps.add(transitiveDependency) - } - } - }, - leave: { - ObjectDef: () => { - state.currentObject = undefined - } - } - }) - externalAbiVisitor.visit(externalAbi) - - additionalImports.forEach(([dependentType, dependencyType]) => { - this._defintionDependencyTree.addNode(dependencyType, extImportUri) - this._defintionDependencyTree.addEdge(dependentType, dependencyType) - }) - - transitiveImportDeps.forEach(transitiveDep => { - this._schemaDependencyTree.addEdge(extImportUri, transitiveDep.uriOrPath) - }) + const { + transitiveImportDeps, + } = this._extractAdditionaAndTransitiveDeps(importedTypes, transitiveImports, extImportUri, externalAbi) await this._getTransitiveDependencies([...transitiveImportDeps]) } From 8256987dbe2bafa1847078019d8f65b5ec8eb164 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Thu, 16 Feb 2023 14:45:43 +0100 Subject: [PATCH 43/90] (chore): added circular deps detection and visual repr for dep tree --- packages/schema/parse/src/DependencyTree.ts | 76 +++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/packages/schema/parse/src/DependencyTree.ts b/packages/schema/parse/src/DependencyTree.ts index 03ce662f8e..221b12c127 100644 --- a/packages/schema/parse/src/DependencyTree.ts +++ b/packages/schema/parse/src/DependencyTree.ts @@ -36,4 +36,80 @@ export class DependencyTree { } return Array.from(visited); } + + getRootNodes(): string[] { + const allNodes = Object.keys(this.nodes); + const dependentNodes = new Set(); + for (const from in this.edges) { + const toNodes = this.edges[from]; + for (const to of toNodes) { + dependentNodes.add(to); + } + } + const rootNodes = allNodes.filter(nodeId => !dependentNodes.has(nodeId)); + return rootNodes; + } + + printVisualTree(rootNodeId: string, prefix = '', isLast = true, output = ''): string { + const branchChar = isLast ? '└── ' : '├── '; + const currentNode = `${prefix}${branchChar}${rootNodeId}\n`; + output += currentNode; + const dependencies = this.getNodeDependencies(rootNodeId); + const numDeps = dependencies.length; + for (let i = 0; i < numDeps; i++) { + const depNodeId = dependencies[i]; + const newPrefix = prefix + (isLast ? ' ' : '│ '); + const isLastDep = i === numDeps - 1; + output = this.printVisualTree(depNodeId, newPrefix, isLastDep, output); + } + return output; + } + + findCircularDependencies(): { detected: true; cycle: string; } | { detected: false } { + const visited = new Set(); + const inStack = new Set(); + const cycle: string[] = []; + + const dfs = (nodeId: string) => { + visited.add(nodeId); + inStack.add(nodeId); + const dependencies = this.getNodeDependencies(nodeId); + for (const depNodeId of dependencies) { + if (!visited.has(depNodeId)) { + dfs(depNodeId); + } else if (inStack.has(depNodeId)) { + cycle.unshift(depNodeId); + cycle.unshift(nodeId); + return; + } + } + inStack.delete(nodeId); + }; + + for (const nodeId in this.nodes) { + if (!visited.has(nodeId)) { + dfs(nodeId); + if (cycle.length > 0) { + break; + } + } + } + + if (cycle.length > 0) { + const cycleStart = cycle[0]; + const cycleEnd = cycle[cycle.length - 1]; + const cycleIndex = cycle.indexOf(cycleStart); + const cycleLength = cycle.length - cycleIndex; + const cycleSlice = cycle.slice(cycleIndex, cycleIndex + cycleLength); + const cycleString = cycleSlice.join(' -> '); + return { + detected: true, + cycle: `Circular dependency detected: ${cycleString} -> ${cycleEnd}` + }; + } else { + return { + detected: false, + }; + } + } } \ No newline at end of file From 588962d55c02cd13a1c840a4bc685214843ceb31 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Thu, 16 Feb 2023 14:46:17 +0100 Subject: [PATCH 44/90] (chore): imports extraction test working --- .../parse/src/__tests__/imports.spec.ts | 15 ++++-- packages/schema/parse/src/imports.ts | 47 +++++++++++++++++-- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/packages/schema/parse/src/__tests__/imports.spec.ts b/packages/schema/parse/src/__tests__/imports.spec.ts index ff6b1181ae..aa18d1ae09 100644 --- a/packages/schema/parse/src/__tests__/imports.spec.ts +++ b/packages/schema/parse/src/__tests__/imports.spec.ts @@ -28,6 +28,7 @@ describe("Imports parser", () => { type Foo { propFoo: Additional! + prop2: Some! } ` @@ -116,6 +117,15 @@ describe("Imports parser", () => { ref_kind: "Object", ref_name: "Additional", } + }, { + kind: "Property", + name: "prop2", + required: true, + type: { + kind: "Ref", + ref_kind: "Object", + ref_name: "Some", + } }] }] } @@ -172,10 +182,5 @@ describe("Imports parser", () => { const importsParser = new ImportsParser(mockSchemaParser, fetchers) const { definitionDependencyTree, schemaDependencyTree } = await importsParser.getImports(rootSchema, mockSchemaParser) - - console.log(JSON.stringify(definitionDependencyTree, null, 2)) - console.log(JSON.stringify(schemaDependencyTree, null, 2)) - - console.log(definitionDependencyTree.getAllDependencies(["Foo"])) }) }) \ No newline at end of file diff --git a/packages/schema/parse/src/imports.ts b/packages/schema/parse/src/imports.ts index 7b03ea6fed..1b6518c6fc 100644 --- a/packages/schema/parse/src/imports.ts +++ b/packages/schema/parse/src/imports.ts @@ -4,7 +4,7 @@ import { UnlinkedAbiDefs } from "./UnlinkedDefs"; import { UnlinkedAbiVisitor } from "./visitor"; export class ImportsParser { - private _schemaDependencyTree = new DependencyTree() + private _schemaDependencyTree = new DependencyTree() private _defintionDependencyTree = new DependencyTree() constructor(private _schemaParser: SchemaParser, private _fetchers: { @@ -83,11 +83,11 @@ export class ImportsParser { const extImportUri = externalImportStatement.uriOrPath const externalSchema = await this._fetchers.external(extImportUri) const importedTypes = externalImportStatement.importedTypes; + const externalAbi = await this._schemaParser.parse(externalSchema) - this._schemaDependencyTree.addNode(extImportUri, externalSchema) + this._schemaDependencyTree.addNode(extImportUri, externalAbi) externalImportStatement.importedTypes.forEach(importedType => this._defintionDependencyTree.addNode(importedType, extImportUri)) - const externalAbi = await this._schemaParser.parse(externalSchema) const transitiveImports = await this._schemaParser.parseExternalImportStatements(externalSchema) const { transitiveImportDeps, @@ -103,7 +103,48 @@ export class ImportsParser { } async getImports(rootSchema: string, parser: SchemaParser) { + const rootAbi = await parser.parse(rootSchema) + this._schemaDependencyTree.addNode("root", rootAbi) + + const state: { currentObject?: string; currentFunction?: string } = {} const externalImportStatements = await parser.parseExternalImportStatements(rootSchema); + + const rootAbiVisitor = new UnlinkedAbiVisitor({ + enter: { + ObjectDef: (def) => { + state.currentObject = def.name + }, + FunctionDef: (def) => { + state.currentFunction = def.name + }, + ImportRefType: (ref) => { + if (!state.currentObject && !state.currentFunction) { + throw new Error(`Found import reference to '${ref.ref_name}' outside of an object or function definition`) + } + const containingDefName = state.currentObject || state.currentFunction as string + const correspondingAbiImport = externalImportStatements.find(t => t.importedTypes.includes(ref.ref_name)) + + if (!correspondingAbiImport) { + throw new Error(`Found import reference to '${ref.ref_name}' which isn't an imported definition`) + } + + this._defintionDependencyTree.addNode(containingDefName, "root") + this._defintionDependencyTree.addEdge(containingDefName, ref.ref_name) + this._schemaDependencyTree.addEdge("root", correspondingAbiImport.uriOrPath) + } + }, + leave: { + ObjectDef: () => { + state.currentObject = undefined + }, + FunctionDef: () => { + state.currentFunction = undefined + } + } + }) + + rootAbiVisitor.visit(rootAbi) + return this._getTransitiveDependencies(externalImportStatements) } } From 1767b86d82981b9badc5bb96c32eb9fb35b75d60 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Thu, 16 Feb 2023 23:17:45 +0100 Subject: [PATCH 45/90] (chore): started local import parsing --- packages/schema/parse/src/imports.ts | 152 +++++++++++++++++++++++---- 1 file changed, 132 insertions(+), 20 deletions(-) diff --git a/packages/schema/parse/src/imports.ts b/packages/schema/parse/src/imports.ts index 1b6518c6fc..185b9318d6 100644 --- a/packages/schema/parse/src/imports.ts +++ b/packages/schema/parse/src/imports.ts @@ -1,21 +1,28 @@ import { DependencyTree } from "./DependencyTree"; -import { ExternalImportStatement, SchemaParser } from "./types"; -import { UnlinkedAbiDefs } from "./UnlinkedDefs"; +import { AbiMerger } from "./AbiMerger"; +import { ExternalImportStatement, LocalImportStatement, SchemaParser } from "./types"; +import { UnlinkedAbiDefs, UnlinkedFunctionDef, UnlinkedObjectDef } from "./UnlinkedDefs"; import { UnlinkedAbiVisitor } from "./visitor"; +import { EnumDef, RefType } from "./definitions"; export class ImportsParser { private _schemaDependencyTree = new DependencyTree() private _defintionDependencyTree = new DependencyTree() constructor(private _schemaParser: SchemaParser, private _fetchers: { - external: (uri: string) => Promise - }) { } + external: (uri: string) => Promise; + local: (path: string) => Promise; + }, private _abiMerger: AbiMerger) { } - private _extractAdditionaAndTransitiveDeps(typesToVisit: string[], transitiveImports: ExternalImportStatement[], abiToVisitUri: string, abiToVisit: UnlinkedAbiDefs, typesToSkip: string[] = []): { + private _extractAdditionaAndTransitiveDeps(abiToVisit: { + uri: string, + abi: UnlinkedAbiDefs, + extImports: ExternalImportStatement[], + }, typesToVisit: string[], typesVisited: string[] = []): { typesToImport: Set<[string, string]>, transitiveImportDeps: Set, } { - typesToVisit = typesToVisit.filter(t => !typesToSkip.includes(t)) + typesToVisit = typesToVisit.filter(t => !typesVisited.includes(t)) const typesToImport = new Set<[string, string]>() const transitiveImportDeps = new Set() @@ -39,13 +46,13 @@ export class ImportsParser { !typesToVisit.includes(ref.ref_name)) { typesToImport.add([state.currentObject, ref.ref_name]) - this._defintionDependencyTree.addNode(ref.ref_name, abiToVisitUri) + this._defintionDependencyTree.addNode(ref.ref_name, abiToVisit.uri) this._defintionDependencyTree.addEdge(state.currentObject, ref.ref_name) } }, ImportRefType: (ref) => { if (state.currentObject) { - const transitiveDependency = transitiveImports.find(t => t.importedTypes.includes(ref.ref_name)) + const transitiveDependency = abiToVisit.extImports.find(t => t.importedTypes.includes(ref.ref_name)) if (!transitiveDependency) { throw new Error(`Found import reference to '${ref.ref_name}' which isn't an imported definition`) @@ -53,9 +60,9 @@ export class ImportsParser { transitiveImportDeps.add(transitiveDependency) - this._defintionDependencyTree.addNode(ref.ref_name, abiToVisitUri) + this._defintionDependencyTree.addNode(ref.ref_name, abiToVisit.uri) this._defintionDependencyTree.addEdge(state.currentObject, ref.ref_name) - this._schemaDependencyTree.addEdge(abiToVisitUri, transitiveDependency.uriOrPath) + this._schemaDependencyTree.addEdge(abiToVisit.uri, transitiveDependency.uriOrPath) } } }, @@ -65,12 +72,12 @@ export class ImportsParser { } } }) - externalAbiVisitor.visit(abiToVisit) + externalAbiVisitor.visit(abiToVisit.abi) const { typesToImport: resultingTypesToImport, transitiveImportDeps: resultingTransitiveImportDeps, - } = this._extractAdditionaAndTransitiveDeps([...typesToImport.values()].map(([_, dep]) => dep), transitiveImports, abiToVisitUri, abiToVisit, [...typesToSkip, ...typesToVisit]) + } = this._extractAdditionaAndTransitiveDeps(abiToVisit, Array.from(typesToImport).map(([_, dep]) => dep), [...typesVisited, ...typesToVisit]) return { typesToImport: new Set([...typesToImport, ...resultingTypesToImport]), @@ -91,24 +98,129 @@ export class ImportsParser { const transitiveImports = await this._schemaParser.parseExternalImportStatements(externalSchema) const { transitiveImportDeps, - } = this._extractAdditionaAndTransitiveDeps(importedTypes, transitiveImports, extImportUri, externalAbi) + } = this._extractAdditionaAndTransitiveDeps({ + uri: extImportUri, + extImports: transitiveImports, + abi: externalAbi, + }, importedTypes) await this._getTransitiveDependencies([...transitiveImportDeps]) } + } + + findDefFromRef(abi: UnlinkedAbiDefs, ref: RefType): UnlinkedObjectDef | EnumDef { + let result: UnlinkedObjectDef | EnumDef | undefined; + + switch (ref.ref_kind) { + case "Object": + result = abi.objects?.find(o => o.name === ref.ref_name) + break; + case "Enum": + result = abi.enums?.find(o => o.name === ref.ref_name) + break; + default: + throw new Error(`Unknown kind ${ref.kind}`) + } + + if (!result) { + throw new Error(`Could not find ${ref.ref_kind} ${ref.ref_name}`) + } + + return result + } + + async extractLocalDefs(localImportStatements: LocalImportStatement[]): Promise<{ + locallyImportedDefs: UnlinkedAbiDefs, + additionalExternalImports: ExternalImportStatement[] + }> { + const objectsToMerge = new Set() + const enumsToMerge = new Set() + const functionsToMerge = new Set() + const additionalExternalImports = new Set() + + for await (const localImportStatement of localImportStatements) { + const localSchema = await this._fetchers.local(localImportStatement.uriOrPath); + const localAbi = await this._schemaParser.parse(localSchema) + const transitiveExtImports = await this._schemaParser.parseExternalImportStatements(localSchema) + + const state: { currentObject?: string; currentFunction?: string } = {} + const localAbiVisitor = new UnlinkedAbiVisitor({ + enter: { + ObjectDef: (def) => { + if (localImportStatement.importedTypes.includes(def.name)) { + state.currentObject = def.name + objectsToMerge.add(def) + } + }, + EnumDef: (def) => { + if (localImportStatement.importedTypes.includes(def.name)) { + enumsToMerge.add(def) + } + }, + FunctionDef: (def) => { + if (localImportStatement.importedTypes.includes(def.name)) { + state.currentFunction = def.name + functionsToMerge.add(def) + } + }, + RefType: (ref) => { + const containingDefName = state.currentObject || state.currentFunction as string + if (containingDefName && !localImportStatement.importedTypes.includes(ref.ref_name)) { + + const referencedDef = this.findDefFromRef(localAbi, ref) + if (referencedDef.kind === "Object") { + objectsToMerge.add(referencedDef) + } else { + enumsToMerge.add(referencedDef) + } + } + }, + ImportRefType: (ref) => { + const containingDefName = state.currentObject || state.currentFunction as string + // TODO: namespaced re-exported ext import? + if (containingDefName) { + const referencedTransitiveImport = transitiveExtImports.find(t => t.importedTypes.includes(ref.ref_name)) + + if (!referencedTransitiveImport) { + throw new Error(`Could not find transitive import for ${ref.ref_name}`) + } + additionalExternalImports.add(referencedTransitiveImport) + } + } + }, + leave: { + ObjectDef: () => { + state.currentObject = undefined + }, + FunctionDef: () => { + state.currentFunction = undefined + } + } + }) + + localAbiVisitor.visit(localAbi) + } return { - definitionDependencyTree: this._defintionDependencyTree, - schemaDependencyTree: this._schemaDependencyTree + locallyImportedDefs: {objects: [...objectsToMerge], enums: [...enumsToMerge], functions: [...functionsToMerge]}, + additionalExternalImports: [...additionalExternalImports] } } - async getImports(rootSchema: string, parser: SchemaParser) { - const rootAbi = await parser.parse(rootSchema) - this._schemaDependencyTree.addNode("root", rootAbi) + async getImports(rootSchema: string) { + const rootAbi = await this._schemaParser.parse(rootSchema) + const localImportStatements = await this._schemaParser.parseLocalImportStatements(rootSchema); - const state: { currentObject?: string; currentFunction?: string } = {} - const externalImportStatements = await parser.parseExternalImportStatements(rootSchema); + const { locallyImportedDefs, additionalExternalImports } = await this.extractLocalDefs(localImportStatements) + + const externalImportStatementsFromRoot = await this._schemaParser.parseExternalImportStatements(rootSchema); + const externalImportStatements = [...externalImportStatementsFromRoot, ...additionalExternalImports] + const mergedRootAbi = this._abiMerger.merge([rootAbi, locallyImportedDefs]) + + this._schemaDependencyTree.addNode("root", mergedRootAbi) + + const state: { currentObject?: string; currentFunction?: string } = {} const rootAbiVisitor = new UnlinkedAbiVisitor({ enter: { ObjectDef: (def) => { From 4da5788837ff5a9600c122f3404e1581ee9d81a2 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Fri, 17 Feb 2023 19:02:48 +0100 Subject: [PATCH 46/90] (feat): local imports: merging + flattening + linking --- packages/schema/parse/src/AbiMerger.ts | 11 + packages/schema/parse/src/UnlinkedDefs.ts | 6 +- .../parse/src/__tests__/imports.spec.ts | 219 ++++++++++++++-- packages/schema/parse/src/imports.ts | 245 ++++++------------ packages/schema/parse/src/types.ts | 5 +- 5 files changed, 288 insertions(+), 198 deletions(-) create mode 100644 packages/schema/parse/src/AbiMerger.ts diff --git a/packages/schema/parse/src/AbiMerger.ts b/packages/schema/parse/src/AbiMerger.ts new file mode 100644 index 0000000000..fa814b3761 --- /dev/null +++ b/packages/schema/parse/src/AbiMerger.ts @@ -0,0 +1,11 @@ +import { UnlinkedAbiDefs } from "./UnlinkedDefs"; + +export class AbiMerger { + merge(abis: UnlinkedAbiDefs[]): UnlinkedAbiDefs { + return { + objects: abis.reduce((acc, abi) => [...acc, ...(abi.objects ?? [])], []), + enums: abis.reduce((acc, abi) => [...acc, ...(abi.enums ?? [])], []), + functions: abis.reduce((acc, abi) => [...acc, ...(abi.functions ?? [])], []), + } + } +} \ No newline at end of file diff --git a/packages/schema/parse/src/UnlinkedDefs.ts b/packages/schema/parse/src/UnlinkedDefs.ts index 1f8c4ed6fd..b19382ad8c 100644 --- a/packages/schema/parse/src/UnlinkedDefs.ts +++ b/packages/schema/parse/src/UnlinkedDefs.ts @@ -1,9 +1,9 @@ import { Def, EnumDef, MapKeyTypeName, NamedDef, RefType, ScalarType } from "./definitions"; export interface UnlinkedAbiDefs { - functions?: UnlinkedFunctionDef[]; - objects?: UnlinkedObjectDef[]; - enums?: EnumDef[]; + functions: UnlinkedFunctionDef[]; + objects: UnlinkedObjectDef[]; + enums: EnumDef[]; } export type UnlinkedAnyDef = diff --git a/packages/schema/parse/src/__tests__/imports.spec.ts b/packages/schema/parse/src/__tests__/imports.spec.ts index aa18d1ae09..2974d8dff7 100644 --- a/packages/schema/parse/src/__tests__/imports.spec.ts +++ b/packages/schema/parse/src/__tests__/imports.spec.ts @@ -1,25 +1,28 @@ -import { UniqueDefKind } from "../definitions" -import { ImportsParser } from "../imports" -import { ExternalImportStatement, SchemaParser } from "../types" +import { AbiMerger } from "../AbiMerger" +import { Abi } from "../definitions" +import { Parser } from "../imports" +import { ExternalImportStatement, LocalImportStatement, SchemaParser } from "../types" import { UnlinkedAbiDefs } from "../UnlinkedDefs" describe("Imports parser", () => { it("Works", async () => { const rootSchema = ` - import { Foo } into NamespaceFoo from "foo" + import { Foo } from "foo" + import { ExtFoo } into Ext from "extfoo" type Baz { - foo: NamespaceFoo_Foo! - prop: String! + extfoo: Ext_ExtFoo! + foo: Foo! } ` const fooSchema = ` - import { Bar } into NamespaceBar from "bar" + import { Bar } from "bar" + import { OutBar } into Out from "outbar" type Some { - propSome: String! + propSome: Out_OutBar! } type Additional { @@ -43,32 +46,69 @@ describe("Imports parser", () => { ` const fetchers = { - external: async (uri: string): Promise => { + external: async (uri: string): Promise => { switch (uri) { + case "extfoo": return { + version: "0.2", + objects: [{ + kind: "Object", + name: "ExtFoo", + props: [{ + kind: "Property", + name: "propExtFoo", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + }] + }] + } + case "outbar": return { + version: "0.2", + objects: [{ + kind: "Object", + name: "OutBar", + props: [{ + kind: "Property", + name: "propOutBar", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + }] + }] + } + default: throw new Error(`Unknown URI: ${uri}`) + } + }, + local: async (path: string): Promise => { + switch (path) { case "foo": return fooSchema case "bar": return barSchema - case "root": return rootSchema - default: throw new Error(`Unknown URI: ${uri}`) + default: throw new Error(`Unknown path: ${path}`) } } } const mockSchemaParser: SchemaParser = { - getImportedSchemasTable: (schema: string, schemaPath: string): Promise> => { - throw new Error("Unimplemented getImportedSchemasTable") - }, - getUniqueDefinitionsTable: (schema: string): Promise> => { - throw new Error("Unimplemented getUniqueDefinitionsTable") - }, - getImportStatements: async (schema: string): Promise => { - throw new Error("Unimplemented getImportStatements") - }, parse: async (schema: string): Promise => { switch (schema) { case rootSchema: return { + functions: [], + enums: [], objects: [{ kind: "Object", props: [{ + kind: "Property", + name: "extfoo", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "ExtFoo", + } + }, { kind: "Property", name: "foo", required: true, @@ -81,16 +121,18 @@ describe("Imports parser", () => { }] }; case fooSchema: return { + functions: [], + enums: [], objects: [{ kind: "Object", name: "Some", props: [{ kind: "Property", - name: "propFoo", + name: "propSome", required: true, type: { - kind: "Scalar", - scalar: "String" + kind: "UnlinkedImportRef", + ref_name: "OutBar", } }] }, { @@ -130,6 +172,8 @@ describe("Imports parser", () => { }] } case barSchema: return { + functions: [], + enums: [], objects: [{ kind: "Object", name: "Bar", @@ -164,15 +208,31 @@ describe("Imports parser", () => { switch (schema) { case rootSchema: return [{ kind: "external", + uriOrPath: "extfoo", + importedTypes: ["ExtFoo"], + namespace: "Ext" + }] + case fooSchema: return [{ + kind: "external", + uriOrPath: "outbar", + importedTypes: ["OutBar"], + namespace: "Out" + }] + case barSchema: return []; + default: throw new Error(`Unknown schema: ${schema}`) + } + }, + parseLocalImportStatements: async (schema: string): Promise => { + switch (schema) { + case rootSchema: return [{ + kind: "local", uriOrPath: "foo", importedTypes: ["Foo"], - namespace: "NamespaceFoo" }] case fooSchema: return [{ - kind: "external", + kind: "local", uriOrPath: "bar", importedTypes: ["Bar"], - namespace: "NamespaceBar" }] case barSchema: return []; default: throw new Error(`Unknown schema: ${schema}`) @@ -180,7 +240,110 @@ describe("Imports parser", () => { } } - const importsParser = new ImportsParser(mockSchemaParser, fetchers) - const { definitionDependencyTree, schemaDependencyTree } = await importsParser.getImports(rootSchema, mockSchemaParser) + const importsParser = new Parser(mockSchemaParser, fetchers, new AbiMerger()) + const { abi, externalImportStatements } = await importsParser.parse(rootSchema) + + const expectedAbi: UnlinkedAbiDefs = { + functions: [], + enums: [], + objects: [{ + kind: "Object", + props: [{ + kind: "Property", + name: "extfoo", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "ExtFoo", + } + }, { + kind: "Property", + name: "foo", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "Foo", + } + }], + name: "Baz", + }, + { + kind: "Object", + name: "Foo", + props: [{ + kind: "Property", + name: "propFoo", + required: true, + type: { + kind: "Ref", + ref_kind: "Object", + ref_name: "Additional", + } + }, { + kind: "Property", + name: "prop2", + required: true, + type: { + kind: "Ref", + ref_kind: "Object", + ref_name: "Some", + } + }] + }, { + kind: "Object", + name: "Additional", + props: [{ + kind: "Property", + name: "bar", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "Bar", + } + }] + }, { + kind: "Object", + name: "Some", + props: [{ + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "OutBar", + } + }] + }, + { + kind: "Object", + name: "Bar", + props: [{ + kind: "Property", + name: "propBar", + required: true, + type: { + kind: "Ref", + ref_kind: "Object", + ref_name: "Transitive" + } + }] + }, { + kind: "Object", + name: "Transitive", + props: [{ + kind: "Property", + name: "propTransitive", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + }] + } + ] + } + + expect(abi).toEqual(expectedAbi) + console.log(externalImportStatements) }) }) \ No newline at end of file diff --git a/packages/schema/parse/src/imports.ts b/packages/schema/parse/src/imports.ts index 185b9318d6..ec6aa880c9 100644 --- a/packages/schema/parse/src/imports.ts +++ b/packages/schema/parse/src/imports.ts @@ -1,146 +1,96 @@ -import { DependencyTree } from "./DependencyTree"; import { AbiMerger } from "./AbiMerger"; import { ExternalImportStatement, LocalImportStatement, SchemaParser } from "./types"; -import { UnlinkedAbiDefs, UnlinkedFunctionDef, UnlinkedObjectDef } from "./UnlinkedDefs"; +import { UnlinkedAbiDefs, UnlinkedObjectDef } from "./UnlinkedDefs"; import { UnlinkedAbiVisitor } from "./visitor"; -import { EnumDef, RefType } from "./definitions"; +import { Abi, EnumDef, RefType, UniqueDefKind } from "./definitions"; -export class ImportsParser { - private _schemaDependencyTree = new DependencyTree() - private _defintionDependencyTree = new DependencyTree() +type ReferenceableDefKind = Exclude +export class Parser { constructor(private _schemaParser: SchemaParser, private _fetchers: { - external: (uri: string) => Promise; + external: (uri: string) => Promise; local: (path: string) => Promise; }, private _abiMerger: AbiMerger) { } - private _extractAdditionaAndTransitiveDeps(abiToVisit: { - uri: string, - abi: UnlinkedAbiDefs, - extImports: ExternalImportStatement[], - }, typesToVisit: string[], typesVisited: string[] = []): { - typesToImport: Set<[string, string]>, - transitiveImportDeps: Set, - } { - typesToVisit = typesToVisit.filter(t => !typesVisited.includes(t)) - const typesToImport = new Set<[string, string]>() - const transitiveImportDeps = new Set() + findDefinitionFromReference(abi: UnlinkedAbiDefs, ref: RefType): UnlinkedObjectDef | EnumDef { + let result: UnlinkedObjectDef | EnumDef | undefined; - if (!typesToVisit.length) { - return { - typesToImport, - transitiveImportDeps - } + switch (ref.ref_kind) { + case "Object": + result = abi.objects?.find(o => o.name === ref.ref_name) + break; + case "Enum": + result = abi.enums?.find(o => o.name === ref.ref_name) + break; + default: + throw new Error(`Unknown kind ${ref.kind}`) + } + + if (!result) { + throw new Error(`Could not find ${ref.ref_kind} ${ref.ref_name}`) } - const state: { currentObject?: string } = {} - const externalAbiVisitor = new UnlinkedAbiVisitor({ + return result + } + + extractLocalReferenceableDefs(abi: UnlinkedAbiDefs): Map { + const result = new Map() + + const visitor = new UnlinkedAbiVisitor({ enter: { ObjectDef: (def) => { - if (typesToVisit.includes(def.name)) { - state.currentObject = def.name - } + result.set(def.name, "Object") }, - RefType: (ref) => { - if (state.currentObject && - !typesToVisit.includes(ref.ref_name)) { - typesToImport.add([state.currentObject, ref.ref_name]) - - this._defintionDependencyTree.addNode(ref.ref_name, abiToVisit.uri) - this._defintionDependencyTree.addEdge(state.currentObject, ref.ref_name) - } + EnumDef: (def) => { + result.set(def.name, "Enum") }, - ImportRefType: (ref) => { - if (state.currentObject) { - const transitiveDependency = abiToVisit.extImports.find(t => t.importedTypes.includes(ref.ref_name)) - - if (!transitiveDependency) { - throw new Error(`Found import reference to '${ref.ref_name}' which isn't an imported definition`) - } - transitiveImportDeps.add(transitiveDependency) - - this._defintionDependencyTree.addNode(ref.ref_name, abiToVisit.uri) - this._defintionDependencyTree.addEdge(state.currentObject, ref.ref_name) - this._schemaDependencyTree.addEdge(abiToVisit.uri, transitiveDependency.uriOrPath) - } - } - }, - leave: { - ObjectDef: () => { - state.currentObject = undefined - } } }) - externalAbiVisitor.visit(abiToVisit.abi) - const { - typesToImport: resultingTypesToImport, - transitiveImportDeps: resultingTransitiveImportDeps, - } = this._extractAdditionaAndTransitiveDeps(abiToVisit, Array.from(typesToImport).map(([_, dep]) => dep), [...typesVisited, ...typesToVisit]) + visitor.visit(abi) - return { - typesToImport: new Set([...typesToImport, ...resultingTypesToImport]), - transitiveImportDeps: new Set([...transitiveImportDeps, ...resultingTransitiveImportDeps]), - } + return result } - private async _getTransitiveDependencies(extImportStatements: ExternalImportStatement[]) { - for await (const externalImportStatement of extImportStatements) { - const extImportUri = externalImportStatement.uriOrPath - const externalSchema = await this._fetchers.external(extImportUri) - const importedTypes = externalImportStatement.importedTypes; - const externalAbi = await this._schemaParser.parse(externalSchema) - - this._schemaDependencyTree.addNode(extImportUri, externalAbi) - externalImportStatement.importedTypes.forEach(importedType => this._defintionDependencyTree.addNode(importedType, extImportUri)) - - const transitiveImports = await this._schemaParser.parseExternalImportStatements(externalSchema) - const { - transitiveImportDeps, - } = this._extractAdditionaAndTransitiveDeps({ - uri: extImportUri, - extImports: transitiveImports, - abi: externalAbi, - }, importedTypes) - - await this._getTransitiveDependencies([...transitiveImportDeps]) - } - } + linkLocalReferences(abi: UnlinkedAbiDefs, referenceableDefs: Map): UnlinkedAbiDefs { + const result: UnlinkedAbiDefs = JSON.parse(JSON.stringify(abi)) - findDefFromRef(abi: UnlinkedAbiDefs, ref: RefType): UnlinkedObjectDef | EnumDef { - let result: UnlinkedObjectDef | EnumDef | undefined; + const visitor = new UnlinkedAbiVisitor({ + enter: { + RefType: (ref) => { + const defKind = referenceableDefs.get(ref.ref_name) + if (!defKind) { + throw new Error(`Could not find ${ref.ref_name}`) + } - switch (ref.ref_kind) { - case "Object": - result = abi.objects?.find(o => o.name === ref.ref_name) - break; - case "Enum": - result = abi.enums?.find(o => o.name === ref.ref_name) - break; - default: - throw new Error(`Unknown kind ${ref.kind}`) - } + ref.ref_kind = defKind + } + } + }) - if (!result) { - throw new Error(`Could not find ${ref.ref_kind} ${ref.ref_name}`) - } + visitor.visit(result) return result } - async extractLocalDefs(localImportStatements: LocalImportStatement[]): Promise<{ - locallyImportedDefs: UnlinkedAbiDefs, - additionalExternalImports: ExternalImportStatement[] + async mergeLocalImports(rootAbi: UnlinkedAbiDefs, localImportStatements: LocalImportStatement[]): Promise<{ + abi: UnlinkedAbiDefs, + transitiveExternalImports: ExternalImportStatement[] }> { - const objectsToMerge = new Set() - const enumsToMerge = new Set() - const functionsToMerge = new Set() - const additionalExternalImports = new Set() + let mergedAbi: UnlinkedAbiDefs = JSON.parse(JSON.stringify(rootAbi)) + const transitiveExternalImports: ExternalImportStatement[] = [] for await (const localImportStatement of localImportStatements) { + const treeShakenLocalAbi: UnlinkedAbiDefs = { + objects: [], + enums: [], + functions: [], + } + const localSchema = await this._fetchers.local(localImportStatement.uriOrPath); const localAbi = await this._schemaParser.parse(localSchema) + const transitiveLocalImports = await this._schemaParser.parseLocalImportStatements(localSchema) const transitiveExtImports = await this._schemaParser.parseExternalImportStatements(localSchema) const state: { currentObject?: string; currentFunction?: string } = {} @@ -149,29 +99,29 @@ export class ImportsParser { ObjectDef: (def) => { if (localImportStatement.importedTypes.includes(def.name)) { state.currentObject = def.name - objectsToMerge.add(def) + treeShakenLocalAbi.objects.push(def) } }, EnumDef: (def) => { if (localImportStatement.importedTypes.includes(def.name)) { - enumsToMerge.add(def) + treeShakenLocalAbi.enums.push(def) } }, FunctionDef: (def) => { if (localImportStatement.importedTypes.includes(def.name)) { state.currentFunction = def.name - functionsToMerge.add(def) + treeShakenLocalAbi.functions.push(def) } }, RefType: (ref) => { const containingDefName = state.currentObject || state.currentFunction as string if (containingDefName && !localImportStatement.importedTypes.includes(ref.ref_name)) { - - const referencedDef = this.findDefFromRef(localAbi, ref) + + const referencedDef = this.findDefinitionFromReference(localAbi, ref) if (referencedDef.kind === "Object") { - objectsToMerge.add(referencedDef) + treeShakenLocalAbi.objects.push(referencedDef) } else { - enumsToMerge.add(referencedDef) + treeShakenLocalAbi.enums.push(referencedDef) } } }, @@ -184,7 +134,7 @@ export class ImportsParser { if (!referencedTransitiveImport) { throw new Error(`Could not find transitive import for ${ref.ref_name}`) } - additionalExternalImports.add(referencedTransitiveImport) + transitiveExternalImports.push(referencedTransitiveImport) } } }, @@ -199,64 +149,33 @@ export class ImportsParser { }) localAbiVisitor.visit(localAbi) + + const subResult = await this.mergeLocalImports(treeShakenLocalAbi, transitiveLocalImports) + mergedAbi = this._abiMerger.merge([mergedAbi, subResult.abi]) + transitiveExternalImports.push(...subResult.transitiveExternalImports) } return { - locallyImportedDefs: {objects: [...objectsToMerge], enums: [...enumsToMerge], functions: [...functionsToMerge]}, - additionalExternalImports: [...additionalExternalImports] + abi: mergedAbi, + transitiveExternalImports } } - async getImports(rootSchema: string) { + async parse(rootSchema: string): Promise<{ abi: UnlinkedAbiDefs; externalImportStatements: ExternalImportStatement[] }> { const rootAbi = await this._schemaParser.parse(rootSchema) - const localImportStatements = await this._schemaParser.parseLocalImportStatements(rootSchema); - - const { locallyImportedDefs, additionalExternalImports } = await this.extractLocalDefs(localImportStatements) - const externalImportStatementsFromRoot = await this._schemaParser.parseExternalImportStatements(rootSchema); - const externalImportStatements = [...externalImportStatementsFromRoot, ...additionalExternalImports] - - const mergedRootAbi = this._abiMerger.merge([rootAbi, locallyImportedDefs]) - - this._schemaDependencyTree.addNode("root", mergedRootAbi) - - const state: { currentObject?: string; currentFunction?: string } = {} - const rootAbiVisitor = new UnlinkedAbiVisitor({ - enter: { - ObjectDef: (def) => { - state.currentObject = def.name - }, - FunctionDef: (def) => { - state.currentFunction = def.name - }, - ImportRefType: (ref) => { - if (!state.currentObject && !state.currentFunction) { - throw new Error(`Found import reference to '${ref.ref_name}' outside of an object or function definition`) - } - const containingDefName = state.currentObject || state.currentFunction as string - const correspondingAbiImport = externalImportStatements.find(t => t.importedTypes.includes(ref.ref_name)) + const localImportStatementsFromRoot = await this._schemaParser.parseLocalImportStatements(rootSchema); - if (!correspondingAbiImport) { - throw new Error(`Found import reference to '${ref.ref_name}' which isn't an imported definition`) - } + const { abi, transitiveExternalImports } = await this.mergeLocalImports(rootAbi, localImportStatementsFromRoot) - this._defintionDependencyTree.addNode(containingDefName, "root") - this._defintionDependencyTree.addEdge(containingDefName, ref.ref_name) - this._schemaDependencyTree.addEdge("root", correspondingAbiImport.uriOrPath) - } - }, - leave: { - ObjectDef: () => { - state.currentObject = undefined - }, - FunctionDef: () => { - state.currentFunction = undefined - } - } - }) + const extractLocalReferenceableDefs = this.extractLocalReferenceableDefs(abi) + const locallyLinkedAbi = this.linkLocalReferences(abi, extractLocalReferenceableDefs) - rootAbiVisitor.visit(rootAbi) + const externalImportStatements = [...externalImportStatementsFromRoot, ...transitiveExternalImports] - return this._getTransitiveDependencies(externalImportStatements) + return { + abi: locallyLinkedAbi, + externalImportStatements, + } } } diff --git a/packages/schema/parse/src/types.ts b/packages/schema/parse/src/types.ts index eebd1944e7..488a4218cf 100644 --- a/packages/schema/parse/src/types.ts +++ b/packages/schema/parse/src/types.ts @@ -1,4 +1,3 @@ -import { UniqueDefKind } from "./definitions"; import { UnlinkedAbiDefs } from "./UnlinkedDefs"; export interface ImportStatement { @@ -18,9 +17,7 @@ export interface LocalImportStatement extends ImportStatement { export interface SchemaParser { parseExternalImportStatements: (schema: string) => Promise - getImportStatements: (schema: string) => Promise - getImportedSchemasTable: (schema: string, schemaPath: string) => Promise> - getUniqueDefinitionsTable: (schema: string) => Promise> + parseLocalImportStatements: (schema: string) => Promise parse: (schema: string) => Promise } From c2b1e9fd5b270990738b848c51eaa87c58fcf20a Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Fri, 17 Feb 2023 19:27:29 +0100 Subject: [PATCH 47/90] (fix): fixed faulty tree shaking of ext imports --- packages/schema/parse/src/imports.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/packages/schema/parse/src/imports.ts b/packages/schema/parse/src/imports.ts index ec6aa880c9..bf61b9bd20 100644 --- a/packages/schema/parse/src/imports.ts +++ b/packages/schema/parse/src/imports.ts @@ -92,7 +92,9 @@ export class Parser { const localAbi = await this._schemaParser.parse(localSchema) const transitiveLocalImports = await this._schemaParser.parseLocalImportStatements(localSchema) const transitiveExtImports = await this._schemaParser.parseExternalImportStatements(localSchema) + transitiveExternalImports.push(...transitiveExtImports) + // TODO: not tree shaking transitive local imports yet const state: { currentObject?: string; currentFunction?: string } = {} const localAbiVisitor = new UnlinkedAbiVisitor({ enter: { @@ -116,7 +118,6 @@ export class Parser { RefType: (ref) => { const containingDefName = state.currentObject || state.currentFunction as string if (containingDefName && !localImportStatement.importedTypes.includes(ref.ref_name)) { - const referencedDef = this.findDefinitionFromReference(localAbi, ref) if (referencedDef.kind === "Object") { treeShakenLocalAbi.objects.push(referencedDef) @@ -125,18 +126,6 @@ export class Parser { } } }, - ImportRefType: (ref) => { - const containingDefName = state.currentObject || state.currentFunction as string - // TODO: namespaced re-exported ext import? - if (containingDefName) { - const referencedTransitiveImport = transitiveExtImports.find(t => t.importedTypes.includes(ref.ref_name)) - - if (!referencedTransitiveImport) { - throw new Error(`Could not find transitive import for ${ref.ref_name}`) - } - transitiveExternalImports.push(referencedTransitiveImport) - } - } }, leave: { ObjectDef: () => { From 92bb4f5d146932f21b4a94cf6fa1b692fe34eeef Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 20 Feb 2023 13:50:28 +0100 Subject: [PATCH 48/90] (chore): rmeoved concept of UnlinkedDefs as separate from the Abi defs --- .../parse/src/__tests__/imports.spec.ts | 15 ++- packages/schema/parse/src/definitions.ts | 11 +- packages/schema/parse/src/imports.ts | 108 ++++++------------ 3 files changed, 54 insertions(+), 80 deletions(-) diff --git a/packages/schema/parse/src/__tests__/imports.spec.ts b/packages/schema/parse/src/__tests__/imports.spec.ts index 2974d8dff7..71345c2d38 100644 --- a/packages/schema/parse/src/__tests__/imports.spec.ts +++ b/packages/schema/parse/src/__tests__/imports.spec.ts @@ -1,8 +1,7 @@ import { AbiMerger } from "../AbiMerger" import { Abi } from "../definitions" -import { Parser } from "../imports" +import { LocalImportsLinker } from "../imports" import { ExternalImportStatement, LocalImportStatement, SchemaParser } from "../types" -import { UnlinkedAbiDefs } from "../UnlinkedDefs" describe("Imports parser", () => { it("Works", async () => { @@ -93,9 +92,10 @@ describe("Imports parser", () => { } const mockSchemaParser: SchemaParser = { - parse: async (schema: string): Promise => { + parse: async (schema: string): Promise => { switch (schema) { case rootSchema: return { + version: "0.2", functions: [], enums: [], objects: [{ @@ -121,6 +121,7 @@ describe("Imports parser", () => { }] }; case fooSchema: return { + version: "0.2", functions: [], enums: [], objects: [{ @@ -172,6 +173,7 @@ describe("Imports parser", () => { }] } case barSchema: return { + version: "0.2", functions: [], enums: [], objects: [{ @@ -240,10 +242,11 @@ describe("Imports parser", () => { } } - const importsParser = new Parser(mockSchemaParser, fetchers, new AbiMerger()) - const { abi, externalImportStatements } = await importsParser.parse(rootSchema) + const importsParser = new LocalImportsLinker(mockSchemaParser, fetchers, new AbiMerger()) + const { abi, externalImportStatements } = await importsParser.link(rootSchema) - const expectedAbi: UnlinkedAbiDefs = { + const expectedAbi: Abi = { + version: "0.2", functions: [], enums: [], objects: [{ diff --git a/packages/schema/parse/src/definitions.ts b/packages/schema/parse/src/definitions.ts index 76d3ded194..a7b369dfea 100644 --- a/packages/schema/parse/src/definitions.ts +++ b/packages/schema/parse/src/definitions.ts @@ -85,14 +85,16 @@ export type AnyType = | ArrayType | MapType | RefType - | ImportRefType; + | ImportRefType + | UnlinkedImportRefType; export type TypeKind = | "Scalar" | "Array" | "Map" | "Ref" - | "ImportRef"; + | "ImportRef" + | "UnlinkedImportRef" export interface Type { kind: TypeKind; @@ -129,6 +131,11 @@ export interface ImportRefType extends Type { ref_name: string; } +export interface UnlinkedImportRefType extends Type { + kind: "UnlinkedImportRef"; + ref_name: string; +} + export interface OptionalType { required: boolean; type: AnyType; diff --git a/packages/schema/parse/src/imports.ts b/packages/schema/parse/src/imports.ts index bf61b9bd20..90ab86bcd6 100644 --- a/packages/schema/parse/src/imports.ts +++ b/packages/schema/parse/src/imports.ts @@ -1,42 +1,20 @@ import { AbiMerger } from "./AbiMerger"; import { ExternalImportStatement, LocalImportStatement, SchemaParser } from "./types"; -import { UnlinkedAbiDefs, UnlinkedObjectDef } from "./UnlinkedDefs"; -import { UnlinkedAbiVisitor } from "./visitor"; -import { Abi, EnumDef, RefType, UniqueDefKind } from "./definitions"; +import { AbiVisitor } from "./visitors"; +import { Abi, EnumDef, ObjectDef, RefType, UniqueDefKind } from "./definitions"; type ReferenceableDefKind = Exclude -export class Parser { - constructor(private _schemaParser: SchemaParser, private _fetchers: { +abstract class ImportsLinker { + constructor(protected _schemaParser: SchemaParser, protected _fetchers: { external: (uri: string) => Promise; local: (path: string) => Promise; - }, private _abiMerger: AbiMerger) { } - - findDefinitionFromReference(abi: UnlinkedAbiDefs, ref: RefType): UnlinkedObjectDef | EnumDef { - let result: UnlinkedObjectDef | EnumDef | undefined; - - switch (ref.ref_kind) { - case "Object": - result = abi.objects?.find(o => o.name === ref.ref_name) - break; - case "Enum": - result = abi.enums?.find(o => o.name === ref.ref_name) - break; - default: - throw new Error(`Unknown kind ${ref.kind}`) - } - - if (!result) { - throw new Error(`Could not find ${ref.ref_kind} ${ref.ref_name}`) - } + }, protected _abiMerger: AbiMerger) { } - return result - } - - extractLocalReferenceableDefs(abi: UnlinkedAbiDefs): Map { + extractReferenceableDefs(abi: Abi): Map { const result = new Map() - const visitor = new UnlinkedAbiVisitor({ + const visitor = new AbiVisitor({ enter: { ObjectDef: (def) => { result.set(def.name, "Object") @@ -44,7 +22,6 @@ export class Parser { EnumDef: (def) => { result.set(def.name, "Enum") }, - } }) @@ -52,37 +29,26 @@ export class Parser { return result } +} - linkLocalReferences(abi: UnlinkedAbiDefs, referenceableDefs: Map): UnlinkedAbiDefs { - const result: UnlinkedAbiDefs = JSON.parse(JSON.stringify(abi)) - - const visitor = new UnlinkedAbiVisitor({ - enter: { - RefType: (ref) => { - const defKind = referenceableDefs.get(ref.ref_name) - if (!defKind) { - throw new Error(`Could not find ${ref.ref_name}`) - } - - ref.ref_kind = defKind - } - } - }) - - visitor.visit(result) +export class LocalImportsLinker extends ImportsLinker { + async handleExternalImports(extImportStatements: ExternalImportStatement[]) { + for await (const extImportStatement of extImportStatements) { + const externalSchema = await this._fetchers.external(extImportStatement.uriOrPath); - return result + } } - async mergeLocalImports(rootAbi: UnlinkedAbiDefs, localImportStatements: LocalImportStatement[]): Promise<{ - abi: UnlinkedAbiDefs, + async mergeLocalImports(rootAbi: Abi, localImportStatements: LocalImportStatement[]): Promise<{ + abi: Abi, transitiveExternalImports: ExternalImportStatement[] }> { - let mergedAbi: UnlinkedAbiDefs = JSON.parse(JSON.stringify(rootAbi)) + let mergedAbi: Abi = JSON.parse(JSON.stringify(rootAbi)) const transitiveExternalImports: ExternalImportStatement[] = [] for await (const localImportStatement of localImportStatements) { - const treeShakenLocalAbi: UnlinkedAbiDefs = { + const treeShakenLocalAbi: Abi = { + version: "0.2", objects: [], enums: [], functions: [], @@ -90,29 +56,26 @@ export class Parser { const localSchema = await this._fetchers.local(localImportStatement.uriOrPath); const localAbi = await this._schemaParser.parse(localSchema) - const transitiveLocalImports = await this._schemaParser.parseLocalImportStatements(localSchema) - const transitiveExtImports = await this._schemaParser.parseExternalImportStatements(localSchema) - transitiveExternalImports.push(...transitiveExtImports) // TODO: not tree shaking transitive local imports yet const state: { currentObject?: string; currentFunction?: string } = {} - const localAbiVisitor = new UnlinkedAbiVisitor({ + const localAbiVisitor = new AbiVisitor({ enter: { ObjectDef: (def) => { if (localImportStatement.importedTypes.includes(def.name)) { state.currentObject = def.name - treeShakenLocalAbi.objects.push(def) + treeShakenLocalAbi.objects!.push(def) } }, EnumDef: (def) => { if (localImportStatement.importedTypes.includes(def.name)) { - treeShakenLocalAbi.enums.push(def) + treeShakenLocalAbi.enums!.push(def) } }, FunctionDef: (def) => { if (localImportStatement.importedTypes.includes(def.name)) { state.currentFunction = def.name - treeShakenLocalAbi.functions.push(def) + treeShakenLocalAbi.functions!.push(def) } }, RefType: (ref) => { @@ -120,10 +83,10 @@ export class Parser { if (containingDefName && !localImportStatement.importedTypes.includes(ref.ref_name)) { const referencedDef = this.findDefinitionFromReference(localAbi, ref) if (referencedDef.kind === "Object") { - treeShakenLocalAbi.objects.push(referencedDef) + treeShakenLocalAbi.objects!.push(referencedDef) } else { - treeShakenLocalAbi.enums.push(referencedDef) - } + treeShakenLocalAbi.enums!.push(referencedDef) + } } }, }, @@ -139,6 +102,10 @@ export class Parser { localAbiVisitor.visit(localAbi) + const transitiveLocalImports = await this._schemaParser.parseLocalImportStatements(localSchema) + const transitiveExtImports = await this._schemaParser.parseExternalImportStatements(localSchema) + transitiveExternalImports.push(...transitiveExtImports) + const subResult = await this.mergeLocalImports(treeShakenLocalAbi, transitiveLocalImports) mergedAbi = this._abiMerger.merge([mergedAbi, subResult.abi]) transitiveExternalImports.push(...subResult.transitiveExternalImports) @@ -150,20 +117,17 @@ export class Parser { } } - async parse(rootSchema: string): Promise<{ abi: UnlinkedAbiDefs; externalImportStatements: ExternalImportStatement[] }> { - const rootAbi = await this._schemaParser.parse(rootSchema) - const externalImportStatementsFromRoot = await this._schemaParser.parseExternalImportStatements(rootSchema); - const localImportStatementsFromRoot = await this._schemaParser.parseLocalImportStatements(rootSchema); - + async link(rootAbi: Abi, importStatements?: { + local?: LocalImportStatement[]; + external?: ExternalImportStatement[]; + }): Promise<{ abi: Abi; externalImportStatements: ExternalImportStatement[] }> { + const localImportStatementsFromRoot = importStatements?.local || [] + const externalImportStatementsFromRoot = importStatements?.external || [] const { abi, transitiveExternalImports } = await this.mergeLocalImports(rootAbi, localImportStatementsFromRoot) - - const extractLocalReferenceableDefs = this.extractLocalReferenceableDefs(abi) - const locallyLinkedAbi = this.linkLocalReferences(abi, extractLocalReferenceableDefs) - const externalImportStatements = [...externalImportStatementsFromRoot, ...transitiveExternalImports] return { - abi: locallyLinkedAbi, + abi, externalImportStatements, } } From d6e4c994fe143c597fbff7185918912415df9fba Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 20 Feb 2023 13:50:40 +0100 Subject: [PATCH 49/90] (feat): abi visitor --- .../parse/src/{visitor.ts => visitors/abi.ts} | 147 ++++++++++-------- packages/schema/parse/src/visitors/index.ts | 1 + 2 files changed, 86 insertions(+), 62 deletions(-) rename packages/schema/parse/src/{visitor.ts => visitors/abi.ts} (55%) create mode 100644 packages/schema/parse/src/visitors/index.ts diff --git a/packages/schema/parse/src/visitor.ts b/packages/schema/parse/src/visitors/abi.ts similarity index 55% rename from packages/schema/parse/src/visitor.ts rename to packages/schema/parse/src/visitors/abi.ts index a57282bf7f..8ae7324439 100644 --- a/packages/schema/parse/src/visitor.ts +++ b/packages/schema/parse/src/visitors/abi.ts @@ -1,61 +1,55 @@ -import { EnumDef, ScalarType, RefType } from "./definitions"; -import { - UnlinkedAbiDefs, - UnlinkedFunctionDef, - UnlinkedArgumentDef, - UnlinkedResultDef, - UnlinkedObjectDef, - UnlinkedPropertyDef, - UnlinkedImportRefType, - UnlinkedAnyType, - UnlinkedArrayType, - UnlinkedMapType -} from "./UnlinkedDefs"; - -interface IUnlinkedAbiVisitor { - Abi?: (node: UnlinkedAbiDefs) => void; - FunctionDef?: (node: UnlinkedFunctionDef) => void; - ArgumentDef?: (node: UnlinkedArgumentDef) => void; - ResultDef?: (node: UnlinkedResultDef) => void; - ObjectDef?: (node: UnlinkedObjectDef) => void; - PropertyDef?: (node: UnlinkedPropertyDef) => void; +import { Abi, AnyType, ArgumentDef, ArrayType, EnumDef, FunctionDef, ImportedAbi, ImportRefType, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType } from "../definitions"; + +export interface IAbiVisitor { + Abi?: (node: Abi) => void; + Imports?: (node: ImportedAbi[]) => void; + Import?: (node: ImportedAbi) => void; + FunctionDef?: (node: FunctionDef) => void; + ArgumentDef?: (node: ArgumentDef) => void; + ResultDef?: (node: ResultDef) => void; + ObjectDef?: (node: ObjectDef) => void; + PropertyDef?: (node: PropertyDef) => void; EnumDef?: (node: EnumDef) => void; ScalarType?: (node: ScalarType) => void; RefType?: (node: RefType) => void; - ImportRefType?: (node: UnlinkedImportRefType) => void; - ArrayType?: (node: UnlinkedArrayType) => void; - MapType?: (node: UnlinkedMapType) => void; - AnyType?: (node: UnlinkedAnyType) => void; + ImportRefType?: (node: ImportRefType) => void; + ArrayType?: (node: ArrayType) => void; + MapType?: (node: MapType) => void; + AnyType?: (node: AnyType) => void; } -interface IUnlinkedAbiVisitorEnterAndLeave { - enter?: IUnlinkedAbiVisitor; - leave?: IUnlinkedAbiVisitor; +interface IAbiVisitorEnterAndLeave { + enter?: IAbiVisitor; + leave?: IAbiVisitor; } -export class UnlinkedAbiVisitor { - constructor(private readonly visitor: IUnlinkedAbiVisitorEnterAndLeave) { } +export class AbiVisitor implements IAbiVisitor { + constructor(private readonly visitor: IAbiVisitorEnterAndLeave) { } - private _Abi(node: UnlinkedAbiDefs) { + Abi(node: Abi) { if (this.visitor.enter?.Abi) { this.visitor.enter.Abi(node); } + if (node.imports) { + this.Imports(node.imports); + } + if (node.functions) { for (const functionNode of node.functions) { - this._FunctionDef(functionNode); + this.FunctionDef(functionNode); } } if (node.objects) { for (const objectNode of node.objects) { - this._ObjectDef(objectNode); + this.ObjectDef(objectNode); } } if (node.enums) { for (const enumNode of node.enums) { - this._EnumDef(enumNode); + this.EnumDef(enumNode); } } @@ -64,53 +58,82 @@ export class UnlinkedAbiVisitor { } } - private _FunctionDef(node: UnlinkedFunctionDef) { + Imports(node: ImportedAbi[]) { + if (this.visitor.enter?.Imports) { + this.visitor.enter.Imports(node); + } + + for (const importNode of node) { + this.Import(importNode); + } + + if (this.visitor.leave?.Imports) { + this.visitor.leave.Imports(node); + } + } + + Import(node: ImportedAbi) { + if (this.visitor.enter?.Import) { + this.visitor.enter.Import(node); + } + + this.Abi({ + version: "0.2", + ...node + }); + + if (this.visitor.leave?.Import) { + this.visitor.leave.Import(node); + } + } + + FunctionDef(node: FunctionDef) { if (this.visitor.enter?.FunctionDef) { this.visitor.enter.FunctionDef(node); } if (node.args) { for (const argumentNode of node.args) { - this._ArgumentDef(argumentNode); + this.ArgumentDef(argumentNode); } } - this._ResultDef(node.result); + this.ResultDef(node.result); if (this.visitor.leave?.FunctionDef) { this.visitor.leave.FunctionDef(node); } } - private _ArgumentDef(node: UnlinkedArgumentDef) { + ArgumentDef(node: ArgumentDef) { if (this.visitor.enter?.ArgumentDef) { this.visitor.enter.ArgumentDef(node); } - this._AnyType(node.type); + this.AnyType(node.type); if (this.visitor.leave?.ArgumentDef) { this.visitor.leave.ArgumentDef(node); } } - private _ResultDef(node: UnlinkedResultDef) { + ResultDef(node: ResultDef) { if (this.visitor.enter?.ResultDef) { this.visitor.enter.ResultDef(node); } - this._AnyType(node.type); + this.AnyType(node.type); if (this.visitor.leave?.ResultDef) { this.visitor.leave.ResultDef(node); } } - private _ObjectDef(node: UnlinkedObjectDef) { + ObjectDef(node: ObjectDef) { if (this.visitor.enter?.ObjectDef) { this.visitor.enter.ObjectDef(node); } if (node.props) { for (const propertyNode of node.props) { - this._PropertyDef(propertyNode); + this.PropertyDef(propertyNode); } } if (this.visitor.leave?.ObjectDef) { @@ -118,17 +141,17 @@ export class UnlinkedAbiVisitor { } } - private _PropertyDef(node: UnlinkedPropertyDef) { + PropertyDef(node: PropertyDef) { if (this.visitor.enter?.PropertyDef) { this.visitor.enter.PropertyDef(node); } - this._AnyType(node.type); + this.AnyType(node.type); if (this.visitor.leave?.PropertyDef) { this.visitor.leave?.PropertyDef(node); } } - private _EnumDef(node: EnumDef) { + EnumDef(node: EnumDef) { if (this.visitor.enter?.EnumDef) { this.visitor.enter.EnumDef(node); } @@ -138,26 +161,26 @@ export class UnlinkedAbiVisitor { } } - private _AnyType(node: UnlinkedAnyType) { + AnyType(node: AnyType) { if (this.visitor.enter?.AnyType) { this.visitor.enter.AnyType(node); } switch (node.kind) { case "Scalar": - this._ScalarType(node); + this.ScalarType(node); break; case "Array": - this._ArrayType(node); + this.ArrayType(node); break; case "Map": - this._MapType(node); + this.MapType(node); break; case "Ref": - this._RefType(node); + this.RefType(node); break; - case "UnlinkedImportRef": - this._ImportRefType(node); + case "ImportRef": + this.ImportRefType(node); break; } @@ -166,7 +189,7 @@ export class UnlinkedAbiVisitor { } } - private _ScalarType(node: ScalarType) { + ScalarType(node: ScalarType) { if (this.visitor.enter?.ScalarType) { this.visitor.enter.ScalarType(node); } @@ -176,7 +199,7 @@ export class UnlinkedAbiVisitor { } } - private _RefType(node: RefType) { + RefType(node: RefType) { if (this.visitor.enter?.RefType) { this.visitor.enter.RefType(node); } @@ -186,7 +209,7 @@ export class UnlinkedAbiVisitor { } } - private _ImportRefType(node: UnlinkedImportRefType) { + ImportRefType(node: ImportRefType) { if (this.visitor.enter?.ImportRefType) { this.visitor.enter.ImportRefType(node); } @@ -196,29 +219,29 @@ export class UnlinkedAbiVisitor { } } - private _ArrayType(node: UnlinkedArrayType) { + ArrayType(node: ArrayType) { if (this.visitor.enter?.ArrayType) { this.visitor.enter.ArrayType(node); } - this._AnyType(node.item.type); + this.AnyType(node.item.type); if (this.visitor.leave?.ArrayType) { this.visitor.leave.ArrayType(node); } } - private _MapType(node: UnlinkedMapType) { + MapType(node: MapType) { if (this.visitor.enter?.MapType) { this.visitor.enter.MapType(node); } - this._AnyType(node.value.type); + this.AnyType(node.value.type); if (this.visitor.leave?.MapType) { this.visitor.leave.MapType(node); } } - visit(node: UnlinkedAbiDefs) { - this._Abi(node); + visit(node: Abi) { + this.Abi(node); } } \ No newline at end of file diff --git a/packages/schema/parse/src/visitors/index.ts b/packages/schema/parse/src/visitors/index.ts new file mode 100644 index 0000000000..348be25344 --- /dev/null +++ b/packages/schema/parse/src/visitors/index.ts @@ -0,0 +1 @@ +export * from "./abi"; \ No newline at end of file From 1750b4df1eb23306aa5fd02b15fc75e23d1f7387 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 20 Feb 2023 13:50:52 +0100 Subject: [PATCH 50/90] (feat): ABI tree shaker --- packages/schema/parse/src/AbiTreeShaker.ts | 188 +++++++++++++++++++++ packages/schema/parse/src/UnlinkedDefs.ts | 81 --------- 2 files changed, 188 insertions(+), 81 deletions(-) create mode 100644 packages/schema/parse/src/AbiTreeShaker.ts delete mode 100644 packages/schema/parse/src/UnlinkedDefs.ts diff --git a/packages/schema/parse/src/AbiTreeShaker.ts b/packages/schema/parse/src/AbiTreeShaker.ts new file mode 100644 index 0000000000..5d0ac9d4ff --- /dev/null +++ b/packages/schema/parse/src/AbiTreeShaker.ts @@ -0,0 +1,188 @@ +import { Abi, AbiDefs, EnumDef, ImportRefType, ObjectDef, RefType } from "./definitions"; +import { AbiVisitor } from "./visitors"; + +type ReferenceableDef = ObjectDef | EnumDef; + +export class AbiTreeShaker { + findReferencedDefinition(abi: Abi, ref: RefType): ReferenceableDef | undefined { + // TODO: implement a stop to the search if the definition is found + let found: ReferenceableDef | undefined = undefined; + const visitor = new AbiVisitor({ + enter: { + ObjectDef: (def) => { + if (def.name === ref.ref_name) { + found = def; + } + }, + EnumDef: (def) => { + if (def.name === ref.ref_name) { + found = def; + } + } + } + }); + + visitor.visit(abi); + + return found; + } + + extractNeededDefinitions(abi: Abi, neededDefNames: string[]): AbiDefs { + const result: AbiDefs = {}; + const state: { currentObject?: string; currentFunction?: string } = {} + const abiVisitor = new AbiVisitor({ + enter: { + ObjectDef: (def) => { + if (neededDefNames.includes(def.name)) { + state.currentObject = def.name + result.objects = result.objects ? [...result.objects, def] : [def] + } + }, + EnumDef: (def) => { + if (neededDefNames.includes(def.name)) { + result.enums = result.enums ? [...result.enums, def] : [def] + } + }, + FunctionDef: (def) => { + if (neededDefNames.includes(def.name)) { + state.currentFunction = def.name + result.functions = result.functions ? [...result.functions, def] : [def] + } + }, + }, + leave: { + ObjectDef: () => { + state.currentObject = undefined + }, + FunctionDef: () => { + state.currentFunction = undefined + }, + } + }); + + abiVisitor.visit(abi); + + return result; + } + + extractImportReferences(abi: Abi): ImportRefType[] { + const result: ImportRefType[] = []; + const abiVisitor = new AbiVisitor({ + enter: { + ImportRefType: (ref) => { + result.push(ref); + } + } + }); + + abiVisitor.visit(abi); + + return result; + } + + extractReferencedSiblingDefinitions(abi: Abi, defs: AbiDefs): AbiDefs { + const result: AbiDefs = {}; + const objectDefNames = defs.objects?.map((def) => def.name) || []; + const functionDefNames = defs.functions?.map((def) => def.name) || []; + const state: { currentObject?: string; currentFunction?: string } = {} + const abiVisitor = new AbiVisitor({ + enter: { + ObjectDef: (def) => { + if (objectDefNames.includes(def.name)) { + state.currentObject = def.name + } + }, + FunctionDef: (def) => { + if (functionDefNames.includes(def.name)) { + state.currentFunction = def.name + } + }, + RefType: (ref) => { + const containingDefName = state.currentObject || state.currentFunction as string + + if(!containingDefName) { + return; + } + + if (containingDefName) { + const referencedDef = this.findReferencedDefinition(abi, ref) + + if (!referencedDef) { + throw new Error(`Could not find referenced definition ${ref.ref_name} in ${containingDefName}`) + } + + if (referencedDef.kind === "Object") { + result.objects = result.objects ? [...result.objects, referencedDef] : [referencedDef] + } else { + result.enums = result.enums ? [...result.enums, referencedDef] : [referencedDef] + } + } + }, + }, + leave: { + ObjectDef: () => { + state.currentObject = undefined + }, + FunctionDef: () => { + state.currentFunction = undefined + }, + } + }); + + abiVisitor.visit(abi); + + return result; + } + + shakeTree(abi: Abi, neededDefNames: string[]): Abi { + const neededDefs = this.extractNeededDefinitions(abi, neededDefNames); + const referencedDefs = this.extractReferencedSiblingDefinitions(abi, neededDefs); + const shakenTree = { + version: abi.version, + ...neededDefs, + ...referencedDefs, + } + + const neededImports = this.extractImportReferences(shakenTree); + + const state: { currentDepth: number; lastDepth: number, currentId: string } = { + currentId: "", + currentDepth: 0, + lastDepth: 0, + } + + const importsVisitor = new AbiVisitor({ + enter: { + Imports: () => { + state.currentDepth += 1 + }, + Import: (importDef) => { + // TODO: this logic works but could be improved + if (state.currentDepth > state.lastDepth) { + state.currentId = `${state.currentId}.${importDef.id}` + } else if (state.currentDepth < state.lastDepth) { + state.currentId = state.currentId.split(".").slice(0, state.currentDepth - 1).join(".") + state.currentId = `${state.currentId}.${importDef.id}` + } else { + state.currentId = state.currentId.split(".").slice(0, state.currentDepth - 1).join(".") + state.currentId = `${state.currentId}.${importDef.id}` + } + } + + }, + leave: { + Imports: () => { + state.currentDepth -= 1 + } + } + }); + + + + return { + version: abi.version, + ...neededDefs, + ...referencedDefs, + }; + } +} \ No newline at end of file diff --git a/packages/schema/parse/src/UnlinkedDefs.ts b/packages/schema/parse/src/UnlinkedDefs.ts deleted file mode 100644 index b19382ad8c..0000000000 --- a/packages/schema/parse/src/UnlinkedDefs.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Def, EnumDef, MapKeyTypeName, NamedDef, RefType, ScalarType } from "./definitions"; - -export interface UnlinkedAbiDefs { - functions: UnlinkedFunctionDef[]; - objects: UnlinkedObjectDef[]; - enums: EnumDef[]; -} - -export type UnlinkedAnyDef = - | UnlinkedFunctionDef - | UnlinkedObjectDef - | EnumDef - | UnlinkedPropertyDef - | UnlinkedArgumentDef - | UnlinkedResultDef; - -export interface UnlinkedInlinedTypeDef extends Def, UnlinkedOptionalType { } - -export interface UnlinkedNamedTypeDef extends NamedDef, UnlinkedInlinedTypeDef { } - -export interface UnlinkedFunctionDef extends NamedDef { - kind: "Function"; - args: UnlinkedArgumentDef[]; - result: UnlinkedResultDef; -} - -export interface UnlinkedArgumentDef extends UnlinkedNamedTypeDef { - kind: "Argument"; -} - -export interface UnlinkedResultDef extends UnlinkedInlinedTypeDef { - kind: "Result"; -} - -export interface UnlinkedObjectDef extends NamedDef { - kind: "Object"; - props: UnlinkedPropertyDef[]; -} - -export interface UnlinkedPropertyDef extends UnlinkedNamedTypeDef { - kind: "Property"; -} - -export type UnlinkedAnyType = - | ScalarType - | UnlinkedArrayType - | UnlinkedMapType - | RefType - | UnlinkedImportRefType; - -export type UnlinkedTypeKind = - | "Scalar" - | "Array" - | "Map" - | "Ref" - | "UnlinkedImportRef"; - -export interface UnlinkedType { - kind: UnlinkedTypeKind; -} - -export interface UnlinkedArrayType extends UnlinkedType { - kind: "Array"; - item: UnlinkedOptionalType; -} - -export interface UnlinkedMapType extends UnlinkedType { - kind: "Map"; - key: ScalarType; - value: UnlinkedOptionalType; -} - -export interface UnlinkedImportRefType extends UnlinkedType { - kind: "UnlinkedImportRef"; - ref_name: string; -} - -export interface UnlinkedOptionalType { - required: boolean; - type: UnlinkedAnyType; -} From 586635c4614c3260a18e4af5c63694da8d5eb6e7 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 20 Feb 2023 14:47:17 +0100 Subject: [PATCH 51/90] (feat): recursive imports shaking --- packages/schema/parse/src/AbiTreeShaker.ts | 82 ++++++++++++++++------ 1 file changed, 59 insertions(+), 23 deletions(-) diff --git a/packages/schema/parse/src/AbiTreeShaker.ts b/packages/schema/parse/src/AbiTreeShaker.ts index 5d0ac9d4ff..7da75e36eb 100644 --- a/packages/schema/parse/src/AbiTreeShaker.ts +++ b/packages/schema/parse/src/AbiTreeShaker.ts @@ -26,7 +26,7 @@ export class AbiTreeShaker { return found; } - + extractNeededDefinitions(abi: Abi, neededDefNames: string[]): AbiDefs { const result: AbiDefs = {}; const state: { currentObject?: string; currentFunction?: string } = {} @@ -99,18 +99,18 @@ export class AbiTreeShaker { }, RefType: (ref) => { const containingDefName = state.currentObject || state.currentFunction as string - - if(!containingDefName) { + + if (!containingDefName) { return; } - + if (containingDefName) { const referencedDef = this.findReferencedDefinition(abi, ref) if (!referencedDef) { throw new Error(`Could not find referenced definition ${ref.ref_name} in ${containingDefName}`) } - + if (referencedDef.kind === "Object") { result.objects = result.objects ? [...result.objects, referencedDef] : [referencedDef] } else { @@ -134,22 +134,13 @@ export class AbiTreeShaker { return result; } - shakeTree(abi: Abi, neededDefNames: string[]): Abi { - const neededDefs = this.extractNeededDefinitions(abi, neededDefNames); - const referencedDefs = this.extractReferencedSiblingDefinitions(abi, neededDefs); - const shakenTree = { - version: abi.version, - ...neededDefs, - ...referencedDefs, - } + shakeImports(abi: Abi, neededImports: ImportRefType[], state: { currentDepth: number; lastDepth: number, currentId: string } = { + currentId: "", + currentDepth: 0, + lastDepth: 0, + }): Abi { - const neededImports = this.extractImportReferences(shakenTree); - - const state: { currentDepth: number; lastDepth: number, currentId: string } = { - currentId: "", - currentDepth: 0, - lastDepth: 0, - } + let abiClone = JSON.parse(JSON.stringify(abi)); const importsVisitor = new AbiVisitor({ enter: { @@ -167,8 +158,42 @@ export class AbiTreeShaker { state.currentId = state.currentId.split(".").slice(0, state.currentDepth - 1).join(".") state.currentId = `${state.currentId}.${importDef.id}` } + + state.lastDepth = state.currentDepth + + const neededFromThisImport = neededImports + .filter((neededImport) => neededImport.import_id === state.currentId) + .map((neededImport) => neededImport.ref_name) + const neededDefsFromThisImport = this.extractNeededDefinitions({ + version: abiClone.version, + ...importDef + }, neededFromThisImport); + const neededSiblingDefs = this.extractReferencedSiblingDefinitions({ + version: abiClone.version, + ...importDef + }, neededDefsFromThisImport); + + importDef = { + id: importDef.id, + uri: importDef.uri, + namespace: importDef.namespace, + type: importDef.type, + ...neededDefsFromThisImport, + ...neededSiblingDefs, + } + + const transitiveImports = this.extractImportReferences({ + version: abiClone.version, + ...importDef + }); + + abiClone = this.shakeImports({ + version: abiClone.version, + ...importDef + }, transitiveImports, state) + } - + }, leave: { Imports: () => { @@ -177,12 +202,23 @@ export class AbiTreeShaker { } }); + importsVisitor.visit(abiClone); + return abiClone; + } - return { + shakeTree(abi: Abi, neededDefNames: string[]): Abi { + const neededDefs = this.extractNeededDefinitions(abi, neededDefNames); + const referencedDefs = this.extractReferencedSiblingDefinitions(abi, neededDefs); + const shakenTree = { version: abi.version, ...neededDefs, ...referencedDefs, - }; + } + + const neededImports = this.extractImportReferences(shakenTree); + const shakenTreeWithShakenImports = this.shakeImports(shakenTree, neededImports); + + return shakenTreeWithShakenImports; } } \ No newline at end of file From 4ef1be69ab64f6d40f880fdf45fd92a9ca19fd89 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Tue, 21 Feb 2023 12:45:08 +0100 Subject: [PATCH 52/90] (chore): remove duplicated local shake logic. Finish first impl --- packages/schema/parse/src/imports.ts | 114 ++++++--------------------- 1 file changed, 26 insertions(+), 88 deletions(-) diff --git a/packages/schema/parse/src/imports.ts b/packages/schema/parse/src/imports.ts index 90ab86bcd6..e4d59e87ef 100644 --- a/packages/schema/parse/src/imports.ts +++ b/packages/schema/parse/src/imports.ts @@ -1,42 +1,31 @@ import { AbiMerger } from "./AbiMerger"; import { ExternalImportStatement, LocalImportStatement, SchemaParser } from "./types"; -import { AbiVisitor } from "./visitors"; -import { Abi, EnumDef, ObjectDef, RefType, UniqueDefKind } from "./definitions"; +import { Abi, ImportedAbi } from "./definitions"; +import { AbiTreeShaker } from "./AbiTreeShaker"; -type ReferenceableDefKind = Exclude - -abstract class ImportsLinker { +export class ImportsLinker { constructor(protected _schemaParser: SchemaParser, protected _fetchers: { external: (uri: string) => Promise; local: (path: string) => Promise; - }, protected _abiMerger: AbiMerger) { } - - extractReferenceableDefs(abi: Abi): Map { - const result = new Map() - - const visitor = new AbiVisitor({ - enter: { - ObjectDef: (def) => { - result.set(def.name, "Object") - }, - EnumDef: (def) => { - result.set(def.name, "Enum") - }, - } - }) - - visitor.visit(abi) + }, protected _abiMerger: AbiMerger, protected _abiTreeShaker: AbiTreeShaker) { } - return result - } -} + async embedExternalImports(rootAbi: Abi, extImportStatements: ExternalImportStatement[]) { + let abiClone: Abi = JSON.parse(JSON.stringify(rootAbi)); -export class LocalImportsLinker extends ImportsLinker { - async handleExternalImports(extImportStatements: ExternalImportStatement[]) { for await (const extImportStatement of extImportStatements) { const externalSchema = await this._fetchers.external(extImportStatement.uriOrPath); - + const importedAbi: ImportedAbi = { + ...externalSchema, + namespace: extImportStatement.namespace, + id: extImportStatement.namespace, + uri: extImportStatement.uriOrPath, + // TODO: how to get this? + type: "wasm" + } + abiClone.imports = abiClone.imports ? [...abiClone.imports, importedAbi] : [importedAbi] } + + return abiClone } async mergeLocalImports(rootAbi: Abi, localImportStatements: LocalImportStatement[]): Promise<{ @@ -47,66 +36,15 @@ export class LocalImportsLinker extends ImportsLinker { const transitiveExternalImports: ExternalImportStatement[] = [] for await (const localImportStatement of localImportStatements) { - const treeShakenLocalAbi: Abi = { - version: "0.2", - objects: [], - enums: [], - functions: [], - } - const localSchema = await this._fetchers.local(localImportStatement.uriOrPath); const localAbi = await this._schemaParser.parse(localSchema) + const localShakenAbi = await this._abiTreeShaker.shakeTree(localAbi, localImportStatement.importedTypes) - // TODO: not tree shaking transitive local imports yet - const state: { currentObject?: string; currentFunction?: string } = {} - const localAbiVisitor = new AbiVisitor({ - enter: { - ObjectDef: (def) => { - if (localImportStatement.importedTypes.includes(def.name)) { - state.currentObject = def.name - treeShakenLocalAbi.objects!.push(def) - } - }, - EnumDef: (def) => { - if (localImportStatement.importedTypes.includes(def.name)) { - treeShakenLocalAbi.enums!.push(def) - } - }, - FunctionDef: (def) => { - if (localImportStatement.importedTypes.includes(def.name)) { - state.currentFunction = def.name - treeShakenLocalAbi.functions!.push(def) - } - }, - RefType: (ref) => { - const containingDefName = state.currentObject || state.currentFunction as string - if (containingDefName && !localImportStatement.importedTypes.includes(ref.ref_name)) { - const referencedDef = this.findDefinitionFromReference(localAbi, ref) - if (referencedDef.kind === "Object") { - treeShakenLocalAbi.objects!.push(referencedDef) - } else { - treeShakenLocalAbi.enums!.push(referencedDef) - } - } - }, - }, - leave: { - ObjectDef: () => { - state.currentObject = undefined - }, - FunctionDef: () => { - state.currentFunction = undefined - } - } - }) - - localAbiVisitor.visit(localAbi) - - const transitiveLocalImports = await this._schemaParser.parseLocalImportStatements(localSchema) const transitiveExtImports = await this._schemaParser.parseExternalImportStatements(localSchema) transitiveExternalImports.push(...transitiveExtImports) - const subResult = await this.mergeLocalImports(treeShakenLocalAbi, transitiveLocalImports) + const transitiveLocalImports = await this._schemaParser.parseLocalImportStatements(localSchema) + const subResult = await this.mergeLocalImports(localShakenAbi, transitiveLocalImports) mergedAbi = this._abiMerger.merge([mergedAbi, subResult.abi]) transitiveExternalImports.push(...subResult.transitiveExternalImports) } @@ -120,15 +58,15 @@ export class LocalImportsLinker extends ImportsLinker { async link(rootAbi: Abi, importStatements?: { local?: LocalImportStatement[]; external?: ExternalImportStatement[]; - }): Promise<{ abi: Abi; externalImportStatements: ExternalImportStatement[] }> { + }): Promise { const localImportStatementsFromRoot = importStatements?.local || [] const externalImportStatementsFromRoot = importStatements?.external || [] - const { abi, transitiveExternalImports } = await this.mergeLocalImports(rootAbi, localImportStatementsFromRoot) + + const { abi: abiWithLocalImports, transitiveExternalImports } = await this.mergeLocalImports(rootAbi, localImportStatementsFromRoot) + const externalImportStatements = [...externalImportStatementsFromRoot, ...transitiveExternalImports] + const abiWithExtImports = await this.embedExternalImports(abiWithLocalImports, externalImportStatements) - return { - abi, - externalImportStatements, - } + return abiWithExtImports } } From f7b3603d601b215a1a314160980278715d6b5b7f Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Tue, 21 Feb 2023 12:55:37 +0100 Subject: [PATCH 53/90] (feat): depend on interfaces, not implementations --- packages/schema/parse/src/AbiMerger.ts | 13 ++++++++++--- packages/schema/parse/src/AbiTreeShaker.ts | 9 ++++++++- .../parse/src/{imports.ts => ImportsLinker.ts} | 15 +++++++++++---- packages/schema/parse/src/types.ts | 4 ++-- 4 files changed, 31 insertions(+), 10 deletions(-) rename packages/schema/parse/src/{imports.ts => ImportsLinker.ts} (87%) diff --git a/packages/schema/parse/src/AbiMerger.ts b/packages/schema/parse/src/AbiMerger.ts index fa814b3761..30e28df216 100644 --- a/packages/schema/parse/src/AbiMerger.ts +++ b/packages/schema/parse/src/AbiMerger.ts @@ -1,11 +1,18 @@ -import { UnlinkedAbiDefs } from "./UnlinkedDefs"; +import { Abi } from "./definitions"; -export class AbiMerger { - merge(abis: UnlinkedAbiDefs[]): UnlinkedAbiDefs { +export interface IAbiMerger { + merge(abis: Abi[]): Abi +} + +export class AbiMerger implements IAbiMerger { + merge(abis: Abi[]): Abi { return { + // TODO: handle different versions? + version: "0.2", objects: abis.reduce((acc, abi) => [...acc, ...(abi.objects ?? [])], []), enums: abis.reduce((acc, abi) => [...acc, ...(abi.enums ?? [])], []), functions: abis.reduce((acc, abi) => [...acc, ...(abi.functions ?? [])], []), + imports: abis.reduce((acc, abi) => [...acc, ...(abi.imports ?? [])], []) } } } \ No newline at end of file diff --git a/packages/schema/parse/src/AbiTreeShaker.ts b/packages/schema/parse/src/AbiTreeShaker.ts index 7da75e36eb..682071e267 100644 --- a/packages/schema/parse/src/AbiTreeShaker.ts +++ b/packages/schema/parse/src/AbiTreeShaker.ts @@ -3,7 +3,14 @@ import { AbiVisitor } from "./visitors"; type ReferenceableDef = ObjectDef | EnumDef; -export class AbiTreeShaker { +export interface IAbiTreeShaker { + findReferencedDefinition(abi: Abi, ref: RefType): ReferenceableDef | undefined + shakeTree(abi: Abi, neededDefNames: string[]): Abi + // TODO: revisit this method's signature + shakeImports(abi: Abi, neededImports: ImportRefType[], state: { currentDepth: number; lastDepth: number, currentId: string }): Abi +} + +export class AbiTreeShaker implements IAbiTreeShaker { findReferencedDefinition(abi: Abi, ref: RefType): ReferenceableDef | undefined { // TODO: implement a stop to the search if the definition is found let found: ReferenceableDef | undefined = undefined; diff --git a/packages/schema/parse/src/imports.ts b/packages/schema/parse/src/ImportsLinker.ts similarity index 87% rename from packages/schema/parse/src/imports.ts rename to packages/schema/parse/src/ImportsLinker.ts index e4d59e87ef..70ac72a153 100644 --- a/packages/schema/parse/src/imports.ts +++ b/packages/schema/parse/src/ImportsLinker.ts @@ -1,13 +1,20 @@ -import { AbiMerger } from "./AbiMerger"; +import { IAbiMerger } from "./AbiMerger"; import { ExternalImportStatement, LocalImportStatement, SchemaParser } from "./types"; import { Abi, ImportedAbi } from "./definitions"; -import { AbiTreeShaker } from "./AbiTreeShaker"; +import { IAbiTreeShaker } from "./AbiTreeShaker"; -export class ImportsLinker { +export interface IImportsLinker { + link: (rootAbi: Abi, importStatements?: { + local?: LocalImportStatement[]; + external?: ExternalImportStatement[]; + }) => Promise +} + +export class ImportsLinker implements IImportsLinker { constructor(protected _schemaParser: SchemaParser, protected _fetchers: { external: (uri: string) => Promise; local: (path: string) => Promise; - }, protected _abiMerger: AbiMerger, protected _abiTreeShaker: AbiTreeShaker) { } + }, protected _abiMerger: IAbiMerger, protected _abiTreeShaker: IAbiTreeShaker) { } async embedExternalImports(rootAbi: Abi, extImportStatements: ExternalImportStatement[]) { let abiClone: Abi = JSON.parse(JSON.stringify(rootAbi)); diff --git a/packages/schema/parse/src/types.ts b/packages/schema/parse/src/types.ts index 488a4218cf..0845f6cac6 100644 --- a/packages/schema/parse/src/types.ts +++ b/packages/schema/parse/src/types.ts @@ -1,4 +1,4 @@ -import { UnlinkedAbiDefs } from "./UnlinkedDefs"; +import { Abi } from "./definitions"; export interface ImportStatement { kind: "local" | "external"; @@ -18,7 +18,7 @@ export interface LocalImportStatement extends ImportStatement { export interface SchemaParser { parseExternalImportStatements: (schema: string) => Promise parseLocalImportStatements: (schema: string) => Promise - parse: (schema: string) => Promise + parse: (schema: string) => Promise } export interface ParserOptions { From d2645750110b5412b872cb44173a844081aa2c23 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Tue, 21 Feb 2023 14:24:22 +0100 Subject: [PATCH 54/90] (chore): removed unnecessary method from interface. Removed dep tree --- packages/schema/parse/src/AbiTreeShaker.ts | 4 +- packages/schema/parse/src/DependencyTree.ts | 115 -------------------- 2 files changed, 1 insertion(+), 118 deletions(-) delete mode 100644 packages/schema/parse/src/DependencyTree.ts diff --git a/packages/schema/parse/src/AbiTreeShaker.ts b/packages/schema/parse/src/AbiTreeShaker.ts index 682071e267..50b4d30f98 100644 --- a/packages/schema/parse/src/AbiTreeShaker.ts +++ b/packages/schema/parse/src/AbiTreeShaker.ts @@ -1,13 +1,11 @@ import { Abi, AbiDefs, EnumDef, ImportRefType, ObjectDef, RefType } from "./definitions"; -import { AbiVisitor } from "./visitors"; +import { AbiVisitor } from "./AbiVisitor"; type ReferenceableDef = ObjectDef | EnumDef; export interface IAbiTreeShaker { findReferencedDefinition(abi: Abi, ref: RefType): ReferenceableDef | undefined shakeTree(abi: Abi, neededDefNames: string[]): Abi - // TODO: revisit this method's signature - shakeImports(abi: Abi, neededImports: ImportRefType[], state: { currentDepth: number; lastDepth: number, currentId: string }): Abi } export class AbiTreeShaker implements IAbiTreeShaker { diff --git a/packages/schema/parse/src/DependencyTree.ts b/packages/schema/parse/src/DependencyTree.ts deleted file mode 100644 index 221b12c127..0000000000 --- a/packages/schema/parse/src/DependencyTree.ts +++ /dev/null @@ -1,115 +0,0 @@ -export class DependencyTree { - private nodes: {[id: string]: TNodeContent} = {}; - private edges: {[id: string]: string[]} = {}; - - addNode(id: string, content: TNodeContent) { - this.nodes[id] = content; - this.edges[id] = []; - } - - addEdge(from: string, to: string) { - this.edges[from].push(to); - } - - hasNode(nodeId: string) { - return this.nodes.hasOwnProperty(nodeId); - } - - getNodeProperties(nodeId: string) { - return this.nodes[nodeId]; - } - - getNodeDependencies(nodeId: string) { - return this.edges[nodeId]; - } - - getAllDependencies(nodeIds: string[]): string[] { - const visited = new Set(); - const queue = [...nodeIds]; - while (queue.length > 0) { - const nodeId = queue.shift(); - if (nodeId && !visited.has(nodeId)) { - visited.add(nodeId); - const dependencies = this.getNodeDependencies(nodeId); - queue.push(...dependencies); - } - } - return Array.from(visited); - } - - getRootNodes(): string[] { - const allNodes = Object.keys(this.nodes); - const dependentNodes = new Set(); - for (const from in this.edges) { - const toNodes = this.edges[from]; - for (const to of toNodes) { - dependentNodes.add(to); - } - } - const rootNodes = allNodes.filter(nodeId => !dependentNodes.has(nodeId)); - return rootNodes; - } - - printVisualTree(rootNodeId: string, prefix = '', isLast = true, output = ''): string { - const branchChar = isLast ? '└── ' : '├── '; - const currentNode = `${prefix}${branchChar}${rootNodeId}\n`; - output += currentNode; - const dependencies = this.getNodeDependencies(rootNodeId); - const numDeps = dependencies.length; - for (let i = 0; i < numDeps; i++) { - const depNodeId = dependencies[i]; - const newPrefix = prefix + (isLast ? ' ' : '│ '); - const isLastDep = i === numDeps - 1; - output = this.printVisualTree(depNodeId, newPrefix, isLastDep, output); - } - return output; - } - - findCircularDependencies(): { detected: true; cycle: string; } | { detected: false } { - const visited = new Set(); - const inStack = new Set(); - const cycle: string[] = []; - - const dfs = (nodeId: string) => { - visited.add(nodeId); - inStack.add(nodeId); - const dependencies = this.getNodeDependencies(nodeId); - for (const depNodeId of dependencies) { - if (!visited.has(depNodeId)) { - dfs(depNodeId); - } else if (inStack.has(depNodeId)) { - cycle.unshift(depNodeId); - cycle.unshift(nodeId); - return; - } - } - inStack.delete(nodeId); - }; - - for (const nodeId in this.nodes) { - if (!visited.has(nodeId)) { - dfs(nodeId); - if (cycle.length > 0) { - break; - } - } - } - - if (cycle.length > 0) { - const cycleStart = cycle[0]; - const cycleEnd = cycle[cycle.length - 1]; - const cycleIndex = cycle.indexOf(cycleStart); - const cycleLength = cycle.length - cycleIndex; - const cycleSlice = cycle.slice(cycleIndex, cycleIndex + cycleLength); - const cycleString = cycleSlice.join(' -> '); - return { - detected: true, - cycle: `Circular dependency detected: ${cycleString} -> ${cycleEnd}` - }; - } else { - return { - detected: false, - }; - } - } -} \ No newline at end of file From d476856d9a4a1d3c369318bbc6ca43636ebb6e54 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Tue, 21 Feb 2023 14:25:21 +0100 Subject: [PATCH 55/90] (chore): added main entrypoint function + fetcher types --- .../src/{visitors/abi.ts => AbiVisitor.ts} | 2 +- packages/schema/parse/src/ImportsLinker.ts | 14 +++--- packages/schema/parse/src/index.ts | 48 +++++++++---------- packages/schema/parse/src/types.ts | 8 ++++ packages/schema/parse/src/visitors/index.ts | 1 - 5 files changed, 40 insertions(+), 33 deletions(-) rename packages/schema/parse/src/{visitors/abi.ts => AbiVisitor.ts} (98%) delete mode 100644 packages/schema/parse/src/visitors/index.ts diff --git a/packages/schema/parse/src/visitors/abi.ts b/packages/schema/parse/src/AbiVisitor.ts similarity index 98% rename from packages/schema/parse/src/visitors/abi.ts rename to packages/schema/parse/src/AbiVisitor.ts index 8ae7324439..a05236cf16 100644 --- a/packages/schema/parse/src/visitors/abi.ts +++ b/packages/schema/parse/src/AbiVisitor.ts @@ -1,4 +1,4 @@ -import { Abi, AnyType, ArgumentDef, ArrayType, EnumDef, FunctionDef, ImportedAbi, ImportRefType, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType } from "../definitions"; +import { Abi, AnyType, ArgumentDef, ArrayType, EnumDef, FunctionDef, ImportedAbi, ImportRefType, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType } from "./definitions"; export interface IAbiVisitor { Abi?: (node: Abi) => void; diff --git a/packages/schema/parse/src/ImportsLinker.ts b/packages/schema/parse/src/ImportsLinker.ts index 70ac72a153..8199402a92 100644 --- a/packages/schema/parse/src/ImportsLinker.ts +++ b/packages/schema/parse/src/ImportsLinker.ts @@ -1,26 +1,26 @@ import { IAbiMerger } from "./AbiMerger"; -import { ExternalImportStatement, LocalImportStatement, SchemaParser } from "./types"; +import { ExternalImportStatement, ExternalSchemaFetcher, LocalImportStatement, LocalSchemaFetcher, SchemaParser } from "./types"; import { Abi, ImportedAbi } from "./definitions"; import { IAbiTreeShaker } from "./AbiTreeShaker"; export interface IImportsLinker { link: (rootAbi: Abi, importStatements?: { - local?: LocalImportStatement[]; - external?: ExternalImportStatement[]; + local: LocalImportStatement[]; + external: ExternalImportStatement[]; }) => Promise } export class ImportsLinker implements IImportsLinker { constructor(protected _schemaParser: SchemaParser, protected _fetchers: { - external: (uri: string) => Promise; - local: (path: string) => Promise; + external: ExternalSchemaFetcher; + local: LocalSchemaFetcher; }, protected _abiMerger: IAbiMerger, protected _abiTreeShaker: IAbiTreeShaker) { } async embedExternalImports(rootAbi: Abi, extImportStatements: ExternalImportStatement[]) { let abiClone: Abi = JSON.parse(JSON.stringify(rootAbi)); for await (const extImportStatement of extImportStatements) { - const externalSchema = await this._fetchers.external(extImportStatement.uriOrPath); + const externalSchema = await this._fetchers.external.fetch(extImportStatement.uriOrPath); const importedAbi: ImportedAbi = { ...externalSchema, namespace: extImportStatement.namespace, @@ -43,7 +43,7 @@ export class ImportsLinker implements IImportsLinker { const transitiveExternalImports: ExternalImportStatement[] = [] for await (const localImportStatement of localImportStatements) { - const localSchema = await this._fetchers.local(localImportStatement.uriOrPath); + const localSchema = await this._fetchers.local.fetch(localImportStatement.uriOrPath); const localAbi = await this._schemaParser.parse(localSchema) const localShakenAbi = await this._abiTreeShaker.shakeTree(localAbi, localImportStatement.importedTypes) diff --git a/packages/schema/parse/src/index.ts b/packages/schema/parse/src/index.ts index a83a80031d..72813c989b 100644 --- a/packages/schema/parse/src/index.ts +++ b/packages/schema/parse/src/index.ts @@ -1,34 +1,34 @@ +import { AbiMerger } from "./AbiMerger"; +import { AbiTreeShaker } from "./AbiTreeShaker"; import { Abi } from "./definitions"; -import { ParserOptions, SchemaParser } from "./types"; +import { ImportsLinker } from "./ImportsLinker"; +import { ExternalSchemaFetcher, LocalSchemaFetcher, SchemaParser } from "./types"; export * from "./abi"; export * from "./types"; -export const parseSchemaAndImports = async (schema: string, schemaPath: string, parser: SchemaParser, options: ParserOptions = {}): Promise<{ abi: Abi, imports: Map }> => { - throw new Error("Unimplemented") - - // const importsRegistry = await parser.getImportedSchemasTable(schema, schemaPath); - // let allUniqueDefinitions = new Map(); - - // for await (const importedAbi of importsRegistry.values()) { - // const importedAbiUniqueDefs = await parser.getUniqueDefinitionsTable(importedAbi); - // allUniqueDefinitions = new Map([...allUniqueDefinitions, ...importedAbiUniqueDefs]); - // } - - // const importedAbis = new Map(); +interface Args { + schema: string + parser: SchemaParser + fetchers: { + external: ExternalSchemaFetcher; + local: LocalSchemaFetcher; + } +} - // for await (const [importPath, importedSchemaString] of importsRegistry.entries()) { - // const importedAbi = await parser.parse(importedSchemaString, allUniqueDefinitions); - // importedAbis.set(importPath, importedAbi); - // } +export const parseAndLinkSchema = async ({ schema, parser, fetchers }: Args): Promise => { + const abi = await parser.parse(schema) + const externalImportStatements = await parser.parseExternalImportStatements(schema) + const localImportStatements = await parser.parseLocalImportStatements(schema) - // // TODO: should this happen before or after linking? - // // TODO: where does validation happen? - // let abi = await parser.parse(schema, allUniqueDefinitions); + const merger = new AbiMerger() + const shaker = new AbiTreeShaker() + const linker = new ImportsLinker(parser, fetchers, merger, shaker) + const linkedAbi = await linker.link(abi, { + external: externalImportStatements, + local: localImportStatements + }) - // return { - // abi, - // imports: importedAbis - // } + return linkedAbi } diff --git a/packages/schema/parse/src/types.ts b/packages/schema/parse/src/types.ts index 0845f6cac6..b6e93b5c2f 100644 --- a/packages/schema/parse/src/types.ts +++ b/packages/schema/parse/src/types.ts @@ -23,4 +23,12 @@ export interface SchemaParser { export interface ParserOptions { noValidate?: boolean; +} + +export interface ExternalSchemaFetcher { + fetch: (uri: string) => Promise; +} + +export interface LocalSchemaFetcher { + fetch: (path: string) => Promise; } \ No newline at end of file diff --git a/packages/schema/parse/src/visitors/index.ts b/packages/schema/parse/src/visitors/index.ts deleted file mode 100644 index 348be25344..0000000000 --- a/packages/schema/parse/src/visitors/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./abi"; \ No newline at end of file From 5eee0d98d1c7620163baf10b3b04d44c21264c74 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Tue, 21 Feb 2023 16:55:39 +0100 Subject: [PATCH 56/90] (chore): eliminated linking package --- packages/schema/linking/README.md | 41 ---- packages/schema/linking/jest.config.js | 12 - packages/schema/linking/package.json | 43 ---- .../schema/linking/src/__tests__/index.ts | 129 ---------- .../linking/src/__tests__/test-cases.spec.ts | 60 ----- packages/schema/linking/src/index.ts | 221 ------------------ packages/schema/linking/src/types.ts | 15 -- packages/schema/linking/tsconfig.build.json | 9 - packages/schema/linking/tsconfig.json | 18 -- .../{ImportsLinker.ts => AbiImportsLinker.ts} | 4 +- packages/schema/parse/src/index.ts | 4 +- packages/schema/parse/src/types.ts | 4 + 12 files changed, 8 insertions(+), 552 deletions(-) delete mode 100644 packages/schema/linking/README.md delete mode 100644 packages/schema/linking/jest.config.js delete mode 100644 packages/schema/linking/package.json delete mode 100644 packages/schema/linking/src/__tests__/index.ts delete mode 100644 packages/schema/linking/src/__tests__/test-cases.spec.ts delete mode 100644 packages/schema/linking/src/index.ts delete mode 100644 packages/schema/linking/src/types.ts delete mode 100644 packages/schema/linking/tsconfig.build.json delete mode 100644 packages/schema/linking/tsconfig.json rename packages/schema/parse/src/{ImportsLinker.ts => AbiImportsLinker.ts} (97%) diff --git a/packages/schema/linking/README.md b/packages/schema/linking/README.md deleted file mode 100644 index c1acb2167f..0000000000 --- a/packages/schema/linking/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Polywrap Schema Compose (@polywrap/schema-compose) - -Composes GraphQL schema source and typings for passing to schema-bind. - -## Usage - -``` typescript -import path from "path"; -import { readFileSync } from "fs"; -import { ComposerOptions, ComposeFilter, composeSchema } from "@polywrap/schema-compose"; -import { TypeInfo } from "@polywrap/schema-parse"; - -const schemaPath = "input/module.graphql" - -const schema = readFileSync(schemaPath); - -const resolveExternal = (uri: string): Promise => { - return Promise.resolve(readFileSync(`imports-ext/${uri}/schema.graphql`) || ""); -}; - -const resolveLocal = (path: string): Promise => { - return Promise.resolve(readFileSync(path) || ""); -}; - -const input: ComposerOptions = { - schemas: [{ - schema, - absolutePath, - }], - resolvers: { - external: resolveExternal, - local: resolveLocal, - }, - output: ComposerFilter.All -}; - -const output: ComposerOutput = composeSchema(input); - -const { schema: string, typeInfo: TypeInfo } = output; - -``` diff --git a/packages/schema/linking/jest.config.js b/packages/schema/linking/jest.config.js deleted file mode 100644 index 1d548add36..0000000000 --- a/packages/schema/linking/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - collectCoverage: true, - preset: 'ts-jest', - testEnvironment: 'node', - testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"], - modulePathIgnorePatterns: ['./src/__tests__/cases'], - globals: { - 'ts-jest': { - diagnostics: false - } - } -}; diff --git a/packages/schema/linking/package.json b/packages/schema/linking/package.json deleted file mode 100644 index 4b031b89bc..0000000000 --- a/packages/schema/linking/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "@polywrap/schema-linking", - "description": "Polywrap Schema Composition", - "version": "0.10.0-pre.5", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/polywrap/monorepo.git" - }, - "main": "build/index.js", - "files": [ - "build" - ], - "scripts": { - "build": "rimraf ./build && tsc --project tsconfig.build.json", - "lint": "eslint --color -c ../../../.eslintrc.js src/", - "test": "jest --passWithNoTests --runInBand --verbose", - "test:ci": "jest --passWithNoTests --runInBand --verbose", - "test:watch": "jest --watch --passWithNoTests --verbose" - }, - "dependencies": { - "@polywrap/schema-parse": "0.10.0-pre.5", - "@polywrap/wrap-manifest-types-js": "0.10.0-pre.5", - "graphql": "15.5.0", - "mustache": "4.0.1" - }, - "devDependencies": { - "@polywrap/test-cases": "0.10.0-pre.5", - "@types/jest": "26.0.8", - "@types/mustache": "4.0.1", - "@types/prettier": "2.6.0", - "jest": "26.6.3", - "jest-diff": "28.1.3", - "rimraf": "3.0.2", - "ts-jest": "26.5.4", - "ts-node": "8.10.2", - "typescript": "4.1.6" - }, - "gitHead": "7346adaf5adb7e6bbb70d9247583e995650d390a", - "publishConfig": { - "access": "public" - } -} diff --git a/packages/schema/linking/src/__tests__/index.ts b/packages/schema/linking/src/__tests__/index.ts deleted file mode 100644 index 2ec585d1e0..0000000000 --- a/packages/schema/linking/src/__tests__/index.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { ComposerOptions } from ".."; - -import path from "path"; -import { readdirSync, Dirent } from "fs"; - -import { Abi, createAbi } from "@polywrap/schema-parse"; -import { - GetPathToComposeTestFiles, - readFileIfExists, - readNamedExportIfExists, -} from "@polywrap/test-cases" - -const root = GetPathToComposeTestFiles(); - -export interface TestCase { - name: string; - input: ComposerOptions; - abi: Abi; - schema: string | undefined; -} - -type TestCases = { - promise: Promise; - name: string; -}[]; - -export function fetchTestCases(): TestCases { - const testCases: TestCases = []; - - readdirSync(root, { withFileTypes: true }).forEach( - (dirent: Dirent) => { - buildTestCases( - path.join(root, dirent.name), - dirent.name, - testCases - ); - } - ); - - return testCases; -} - -function buildTestCases( - directory: string, - name: string, - testCases: TestCases -): void { - const items = readdirSync(directory, { withFileTypes: true }); - - if ( - items.some(x => x.name.startsWith("input")) && - items.some(x => x.name.startsWith("output")) - ) { - testCases.push({ - promise: importCase(directory, name), - name: name - }); - } else { - for (const item of items) { - buildTestCases( - path.join(directory, item.name), - name + " " + item.name, - testCases - ); - } - } -} - -async function importCase( - directory: string, - name: string, -): Promise { - // Fetch the input schemas - const moduleInput = readFileIfExists("input/module.graphql", directory); - - // Fetch the output abi - const moduleAbi = await readNamedExportIfExists("abi", "output/module.ts", directory); - - // Fetch the output schema - const moduleSchema = readFileIfExists("output/module.graphql", directory); - - const resolveExternal = async (uri: string): Promise => { - let abi = createAbi() - const generatedAbi = await readNamedExportIfExists("abi", `imports-ext/${uri}/module.ts`, directory) - if (generatedAbi) { - abi = generatedAbi - } - return Promise.resolve(abi); - }; - - const resolveLocal = (path: string): Promise => { - return Promise.resolve(readFileIfExists(path, directory, true) || ""); - }; - - if (!moduleInput) { - throw new Error("Expected input schema.graphql file to Exist") - } - - const input: ComposerOptions = { - schema: { - schema: moduleInput, - absolutePath: path.join( - directory, - "input/module.graphql" - ), - }, - resolvers: { - external: resolveExternal, - local: resolveLocal, - }, - }; - - if (moduleInput) { - input.schema = { - schema: moduleInput, - absolutePath: path.join( - directory, - "input/module.graphql" - ), - }; - } - - return { - name, - input, - abi: moduleAbi as Abi, - schema: moduleSchema - }; -} diff --git a/packages/schema/linking/src/__tests__/test-cases.spec.ts b/packages/schema/linking/src/__tests__/test-cases.spec.ts deleted file mode 100644 index 7e61453153..0000000000 --- a/packages/schema/linking/src/__tests__/test-cases.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { composeSchema, renderSchema } from ".."; -import { fetchTestCases } from "./index"; - -import { diff } from "jest-diff"; - -function sanitizeObj(obj: unknown) { - if (typeof obj !== "object") { - return; - } - - for (var i in obj) { - if (!obj.hasOwnProperty(i)) continue; - - const prop = (obj as any)[i]; - const typeOf = typeof prop; - - if (typeOf === "object") { - sanitizeObj(prop); - } else if (typeOf === "function") { - delete (obj as any)[i]; - } else if (typeOf === "undefined") { - delete (obj as any)[i]; - } - } - - return obj; -} - -describe("Polywrap Schema Composer Test Cases", () => { - let cases = fetchTestCases(); - for (const test of cases) { - it(`Case: ${test.name}`, async () => { - const testCase = await test.promise; - - if (!testCase) { - return; - } - - const result = await composeSchema(testCase.input); - - // Check result with output ABI - sanitizeObj(result); - sanitizeObj(testCase.abi); - expect(result).toMatchObject(testCase.abi); - - // Check rendered result schema with output schema - const resultSchema = renderSchema(result, true); - - if (testCase.schema) { - expect(diff(testCase.schema, resultSchema)) - .toContain("Compared values have no visual difference"); - } - - // Check rendering between result ABI and output ABI - const testCaseSchema = renderSchema(testCase.abi, true); - expect(diff(testCaseSchema, resultSchema)) - .toContain("Compared values have no visual difference"); - }); - } -}); diff --git a/packages/schema/linking/src/index.ts b/packages/schema/linking/src/index.ts deleted file mode 100644 index e980e2c320..0000000000 --- a/packages/schema/linking/src/index.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { ExternalImportStatement, ImportStatement, LocalImportStatement, MODULE_NAME } from "@polywrap/schema-parse"; -import { Abi, AnyType, EnumDef, FunctionDef, ImportRefType, ObjectDef, RefType } from "@polywrap/schema-parse/build/definitions"; -import { AbiVisitor } from "./visitor"; - -interface LinkImportsAbiArgs { - importStatements: (ExternalImportStatement | LocalImportStatement)[]; - abi: Abi; - imports: Map; -} - -type DefsWithRefs = ObjectDef | FunctionDef; -type DefsThatCanBeReferenced = ObjectDef | EnumDef; -const KINDS_WITH_REFS = ["Object", "Function"] as const; -const REFERENCEABLE_KINDS = ["Object", "Enum"] as const; - -const findReferencedDefintionByName = (abi: Abi, ref_name: string): DefsThatCanBeReferenced | undefined => { - const object = abi.objects?.find((object) => object.name === ref_name); - if (object) { - return object; - } - - const enumDef = abi.enums?.find((enumDef) => enumDef.name === ref_name); - if (enumDef) { - return enumDef; - } - - return undefined; -} - -const extractRefFromAnyType = (type: AnyType): RefType | ImportRefType | undefined => { - switch (type.kind) { - case "Ref": - case "ImportRef": - return type; - case "Scalar": - return undefined; - case "Array": - return extractRefFromAnyType(type.item.type); - case "Map": - return extractRefFromAnyType(type.value.type); - } -} - -const extractDefFromRef = (abi: Abi, ref: RefType | ImportRefType): DefsThatCanBeReferenced => { - const name = ref.ref_name; - const definition = findReferencedDefintionByName(abi, name); - - if (!definition) { - throw new Error(`Found reference to '${name}'. But no definition found with this name.`) - } - - if (REFERENCEABLE_KINDS.includes(definition.kind)) { - throw new Error(`Found reference to '${name}'. But this is a '${definition.kind}' definition which can't be referenced.`) - } - - return definition; -} - -const extractReferencedDefs = (abi: Abi, defWithRefs: DefsWithRefs): DefsThatCanBeReferenced[] => { - const extractRefsFromAnyType = (type: AnyType): DefsThatCanBeReferenced[] => { - const dependencies: DefsThatCanBeReferenced[] = []; - const ref = extractRefFromAnyType(type); - - if (ref) { - const definition = extractDefFromRef(abi, ref); - dependencies.push(definition); - - if (definition.kind in KINDS_WITH_REFS) { - dependencies.push(...extractReferencedDefs(abi, definition as DefsWithRefs)); - } - } - - return dependencies; - } - - switch (defWithRefs.kind) { - case "Object": { - const dependencies: DefsThatCanBeReferenced[] = []; - - for (const property of defWithRefs.props) { - dependencies.push(...extractRefsFromAnyType(property.type)); - // TODO: ImportRef handling - }; - - return dependencies; - } - case "Function": { - let dependencies: DefsThatCanBeReferenced[] = []; - - for (const arg of defWithRefs.args) { - dependencies.push(...extractRefsFromAnyType(arg.type)); - } - dependencies.push(...extractRefsFromAnyType(defWithRefs.result.type)); - - return dependencies; - } - } -} - -const treeShakeImportedAbi = (importedAbi: Abi, importStatement: ImportStatement): Abi => { - const treeShakenAbi: Abi = { - version: "0.2" - }; - - const { importedTypes } = importStatement; - - const importedObjects = importedAbi?.objects?.filter((object) => importedTypes.includes(object.name)) ?? []; - const importedEnums = importedAbi?.enums?.filter((enumDef) => importedTypes.includes(enumDef.name)) ?? []; - const importedFunctions = (importedTypes.includes(MODULE_NAME) ? importedAbi?.functions : []) ?? [] - - const refsFromObjects = importedObjects.flatMap((object) => extractReferencedDefs(importedAbi, object)); - const referencedEnumsFromObjects = refsFromObjects.filter((ref) => ref.kind === "Enum") as EnumDef[]; - const referencedObjectsFromObjects = refsFromObjects.filter((ref) => ref.kind === "Object") as ObjectDef[]; - - const refsFromFunctions = importedFunctions.flatMap((func) => extractReferencedDefs(importedAbi, func)); - const referencedEnumsFromFunctions = refsFromFunctions.filter((ref) => ref.kind === "Enum") as EnumDef[]; - const referencedObjectsFromFunctions = refsFromFunctions.filter((ref) => ref.kind === "Object") as ObjectDef[]; - - treeShakenAbi.enums = [...importedEnums, ...referencedEnumsFromObjects, ...referencedEnumsFromFunctions]; - treeShakenAbi.objects = [...importedObjects, ...referencedObjectsFromObjects, ...referencedObjectsFromFunctions]; - treeShakenAbi.functions = importedFunctions; - - return treeShakenAbi; -} - -const mergeAbis = (abis: Abi[]): Abi => { - const mergedAbi: Abi = { - version: "0.2" - }; - - // TODO: imports? - - const enums = abis.flatMap((abi) => abi.enums ?? []); - const objects = abis.flatMap((abi) => abi.objects ?? []); - const functions = abis.flatMap((abi) => abi.functions ?? []); - - mergedAbi.enums = enums; - mergedAbi.objects = objects; - mergedAbi.functions = functions; - - return mergedAbi; -} - -export const linkAbiImports = ({ importStatements, abi, imports }: LinkImportsAbiArgs): Abi => { - const treeShakenImports = new Map(); - let abiClone: Abi = JSON.parse(JSON.stringify(abi)); - - // Tree shake imported ABIs - importStatements.forEach((importStatement) => { - const { uriOrPath } = importStatement; - - const importedAbi = imports.get(uriOrPath); - - if (!importedAbi) { - throw new Error(`Imported ABI not found for '${uriOrPath}'`); - } - - const treeShakenAbi = treeShakeImportedAbi(importedAbi, importStatement); - treeShakenImports.set(uriOrPath, treeShakenAbi); - }); - - if (treeShakenImports.size && !abiClone.imports) { - abiClone.imports = []; - } - - const externalImportsStatements = importStatements.filter((importStatement) => importStatement.kind === "external") as ExternalImportStatement[]; - const localImportsStatements = importStatements.filter((importStatement) => importStatement.kind === "local") as LocalImportStatement[]; - - // Merge local imports - - localImportsStatements.forEach((localImportStatement) => { - const { uriOrPath } = localImportStatement; - const treeShakenAbi = treeShakenImports.get(uriOrPath); - - if (!treeShakenAbi) { - throw new Error(`Tree shaken ABI not found for '${uriOrPath}'`); - } - - abiClone = mergeAbis([abiClone, treeShakenAbi]); - }); - - // Embed external imports - - externalImportsStatements.forEach((importStatement, i) => { - const { uriOrPath, importedTypes } = importStatement; - const treeShakenAbi = treeShakenImports.get(uriOrPath); - - if (!treeShakenAbi) { - throw new Error(`Tree shaken ABI not found for '${uriOrPath}'`); - } - - // TODO: better ID generation - const id = i.toString() - - abiClone.imports!.push({ - uri: uriOrPath, - namespace: importStatement.namespace, - id, - // TODO: Where do I get this from? - type: "wasm", - ...treeShakenAbi - }) - - // Link ImportRefs to external imports - - const importRefVisitor = new AbiVisitor({ - ImportRefType: (importRefType) => { - const { ref_name } = importRefType; - - // TODO: Imports of imports? - if (importedTypes.includes(ref_name)) { - importRefType.import_id = id; - } - } - }) - - importRefVisitor.visit(abiClone); - }); - - return abiClone; -} \ No newline at end of file diff --git a/packages/schema/linking/src/types.ts b/packages/schema/linking/src/types.ts deleted file mode 100644 index fab23e4c2b..0000000000 --- a/packages/schema/linking/src/types.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Abi } from "@polywrap/schema-parse"; - -export interface SchemaFile { - schema: string; - absolutePath: string; -} - -export type AbiResolver = (uri: string) => Promise; -export type SchemaResolver = (path: string) => Promise; - -export interface AbiResolvers { - external: AbiResolver; - local: SchemaResolver; -} - diff --git a/packages/schema/linking/tsconfig.build.json b/packages/schema/linking/tsconfig.build.json deleted file mode 100644 index 77aadfdd2f..0000000000 --- a/packages/schema/linking/tsconfig.build.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": [ - "./src/**/*.ts" - ], - "exclude": [ - "./src/**/__tests__" - ] -} diff --git a/packages/schema/linking/tsconfig.json b/packages/schema/linking/tsconfig.json deleted file mode 100644 index 1b7ca8ec90..0000000000 --- a/packages/schema/linking/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "../../../tsconfig", - "compilerOptions": { - "lib": [ - "es2020.string", - "es2015", - "es5", - "dom" - ], - "downlevelIteration": true, - "outDir": "build" - }, - "include": [ - "./src/**/*.ts" - ], - "exclude": [] -} - diff --git a/packages/schema/parse/src/ImportsLinker.ts b/packages/schema/parse/src/AbiImportsLinker.ts similarity index 97% rename from packages/schema/parse/src/ImportsLinker.ts rename to packages/schema/parse/src/AbiImportsLinker.ts index 8199402a92..efb9d14ed2 100644 --- a/packages/schema/parse/src/ImportsLinker.ts +++ b/packages/schema/parse/src/AbiImportsLinker.ts @@ -3,14 +3,14 @@ import { ExternalImportStatement, ExternalSchemaFetcher, LocalImportStatement, L import { Abi, ImportedAbi } from "./definitions"; import { IAbiTreeShaker } from "./AbiTreeShaker"; -export interface IImportsLinker { +export interface IAbiImportsLinker { link: (rootAbi: Abi, importStatements?: { local: LocalImportStatement[]; external: ExternalImportStatement[]; }) => Promise } -export class ImportsLinker implements IImportsLinker { +export class AbiImportsLinker implements IAbiImportsLinker { constructor(protected _schemaParser: SchemaParser, protected _fetchers: { external: ExternalSchemaFetcher; local: LocalSchemaFetcher; diff --git a/packages/schema/parse/src/index.ts b/packages/schema/parse/src/index.ts index 72813c989b..f41b90a059 100644 --- a/packages/schema/parse/src/index.ts +++ b/packages/schema/parse/src/index.ts @@ -1,7 +1,7 @@ import { AbiMerger } from "./AbiMerger"; import { AbiTreeShaker } from "./AbiTreeShaker"; import { Abi } from "./definitions"; -import { ImportsLinker } from "./ImportsLinker"; +import { AbiImportsLinker } from "./AbiImportsLinker"; import { ExternalSchemaFetcher, LocalSchemaFetcher, SchemaParser } from "./types"; export * from "./abi"; @@ -23,7 +23,7 @@ export const parseAndLinkSchema = async ({ schema, parser, fetchers }: Args): Pr const merger = new AbiMerger() const shaker = new AbiTreeShaker() - const linker = new ImportsLinker(parser, fetchers, merger, shaker) + const linker = new AbiImportsLinker(parser, fetchers, merger, shaker) const linkedAbi = await linker.link(abi, { external: externalImportStatements, local: localImportStatements diff --git a/packages/schema/parse/src/types.ts b/packages/schema/parse/src/types.ts index b6e93b5c2f..9653622450 100644 --- a/packages/schema/parse/src/types.ts +++ b/packages/schema/parse/src/types.ts @@ -21,6 +21,10 @@ export interface SchemaParser { parse: (schema: string) => Promise } +export interface SchemaRenderer { + render: (abi: Abi) => Promise +} + export interface ParserOptions { noValidate?: boolean; } From 760bb8e7f7b9eff9c6a28ce5115d1463c1dfd9c9 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Tue, 21 Feb 2023 23:04:21 +0100 Subject: [PATCH 57/90] (feat): renamed package to schema-abi. Removed transforms --- packages/schema/{parse => abi}/.eslintrc.js | 0 packages/schema/{parse => abi}/README.md | 0 packages/schema/{parse => abi}/jest.config.js | 0 packages/schema/{parse => abi}/package.json | 4 +- .../{parse => abi}/src/AbiImportsLinker.ts | 3 +- .../schema/{parse => abi}/src/AbiMerger.ts | 0 .../{parse => abi}/src/AbiTreeShaker.ts | 16 +- .../schema/{parse => abi}/src/AbiVisitor.ts | 0 .../src/__tests__/imports.spec.ts | 4 +- .../{parse => abi}/src/__tests__/index.ts | 0 .../src/__tests__/parse-map-type.spec.ts | 0 .../src/__tests__/test-cases.spec.ts | 2 +- .../src/__tests__/transforms.spec.ts | 2 +- .../src/__tests__/utils.spec.ts | 0 .../src/__tests__/validate-directives.spec.ts | 0 .../src/__tests__/validate-types.spec.ts | 0 .../schema/{parse => abi}/src/definitions.ts | 0 packages/schema/{parse => abi}/src/index.ts | 10 +- packages/schema/{parse => abi}/src/types.ts | 0 .../schema/{parse => abi}/tsconfig.build.json | 0 packages/schema/{parse => abi}/tsconfig.json | 0 packages/schema/linking/src/render.ts | 43 ----- .../linking/src/templates/header.mustache.ts | 14 -- .../linking/src/templates/schema.mustache.ts | 160 ------------------ packages/schema/parse/src/abi/index.ts | 10 -- packages/schema/parse/src/abi/utils.ts | 19 --- .../parse/src/transform/addFirstLast.ts | 46 ----- .../schema/parse/src/transform/extendType.ts | 20 --- .../schema/parse/src/transform/hasImports.ts | 10 -- 29 files changed, 28 insertions(+), 335 deletions(-) rename packages/schema/{parse => abi}/.eslintrc.js (100%) rename packages/schema/{parse => abi}/README.md (100%) rename packages/schema/{parse => abi}/jest.config.js (100%) rename packages/schema/{parse => abi}/package.json (92%) rename packages/schema/{parse => abi}/src/AbiImportsLinker.ts (96%) rename packages/schema/{parse => abi}/src/AbiMerger.ts (100%) rename packages/schema/{parse => abi}/src/AbiTreeShaker.ts (94%) rename packages/schema/{parse => abi}/src/AbiVisitor.ts (100%) rename packages/schema/{parse => abi}/src/__tests__/imports.spec.ts (98%) rename packages/schema/{parse => abi}/src/__tests__/index.ts (100%) rename packages/schema/{parse => abi}/src/__tests__/parse-map-type.spec.ts (100%) rename packages/schema/{parse => abi}/src/__tests__/test-cases.spec.ts (95%) rename packages/schema/{parse => abi}/src/__tests__/transforms.spec.ts (99%) rename packages/schema/{parse => abi}/src/__tests__/utils.spec.ts (100%) rename packages/schema/{parse => abi}/src/__tests__/validate-directives.spec.ts (100%) rename packages/schema/{parse => abi}/src/__tests__/validate-types.spec.ts (100%) rename packages/schema/{parse => abi}/src/definitions.ts (100%) rename packages/schema/{parse => abi}/src/index.ts (84%) rename packages/schema/{parse => abi}/src/types.ts (100%) rename packages/schema/{parse => abi}/tsconfig.build.json (100%) rename packages/schema/{parse => abi}/tsconfig.json (100%) delete mode 100644 packages/schema/linking/src/render.ts delete mode 100644 packages/schema/linking/src/templates/header.mustache.ts delete mode 100644 packages/schema/linking/src/templates/schema.mustache.ts delete mode 100644 packages/schema/parse/src/abi/index.ts delete mode 100644 packages/schema/parse/src/abi/utils.ts delete mode 100644 packages/schema/parse/src/transform/addFirstLast.ts delete mode 100644 packages/schema/parse/src/transform/extendType.ts delete mode 100644 packages/schema/parse/src/transform/hasImports.ts diff --git a/packages/schema/parse/.eslintrc.js b/packages/schema/abi/.eslintrc.js similarity index 100% rename from packages/schema/parse/.eslintrc.js rename to packages/schema/abi/.eslintrc.js diff --git a/packages/schema/parse/README.md b/packages/schema/abi/README.md similarity index 100% rename from packages/schema/parse/README.md rename to packages/schema/abi/README.md diff --git a/packages/schema/parse/jest.config.js b/packages/schema/abi/jest.config.js similarity index 100% rename from packages/schema/parse/jest.config.js rename to packages/schema/abi/jest.config.js diff --git a/packages/schema/parse/package.json b/packages/schema/abi/package.json similarity index 92% rename from packages/schema/parse/package.json rename to packages/schema/abi/package.json index 1c7682318d..a27f0f5fd0 100644 --- a/packages/schema/parse/package.json +++ b/packages/schema/abi/package.json @@ -1,6 +1,6 @@ { - "name": "@polywrap/schema-parse", - "description": "Polywrap Schema Parsing", + "name": "@polywrap/schema-abi", + "description": "Polywrap ABI Parsing and Linking", "version": "0.10.0-pre.5", "license": "MIT", "repository": { diff --git a/packages/schema/parse/src/AbiImportsLinker.ts b/packages/schema/abi/src/AbiImportsLinker.ts similarity index 96% rename from packages/schema/parse/src/AbiImportsLinker.ts rename to packages/schema/abi/src/AbiImportsLinker.ts index efb9d14ed2..f3db69cf4c 100644 --- a/packages/schema/parse/src/AbiImportsLinker.ts +++ b/packages/schema/abi/src/AbiImportsLinker.ts @@ -73,7 +73,8 @@ export class AbiImportsLinker implements IAbiImportsLinker { const externalImportStatements = [...externalImportStatementsFromRoot, ...transitiveExternalImports] const abiWithExtImports = await this.embedExternalImports(abiWithLocalImports, externalImportStatements) + const abiWithShakenExtImports = await this._abiTreeShaker.shakeImports(abiWithExtImports) - return abiWithExtImports + return abiWithShakenExtImports } } diff --git a/packages/schema/parse/src/AbiMerger.ts b/packages/schema/abi/src/AbiMerger.ts similarity index 100% rename from packages/schema/parse/src/AbiMerger.ts rename to packages/schema/abi/src/AbiMerger.ts diff --git a/packages/schema/parse/src/AbiTreeShaker.ts b/packages/schema/abi/src/AbiTreeShaker.ts similarity index 94% rename from packages/schema/parse/src/AbiTreeShaker.ts rename to packages/schema/abi/src/AbiTreeShaker.ts index 50b4d30f98..430b6244c3 100644 --- a/packages/schema/parse/src/AbiTreeShaker.ts +++ b/packages/schema/abi/src/AbiTreeShaker.ts @@ -6,6 +6,7 @@ type ReferenceableDef = ObjectDef | EnumDef; export interface IAbiTreeShaker { findReferencedDefinition(abi: Abi, ref: RefType): ReferenceableDef | undefined shakeTree(abi: Abi, neededDefNames: string[]): Abi + shakeImports(abi: Abi): Abi } export class AbiTreeShaker implements IAbiTreeShaker { @@ -139,7 +140,7 @@ export class AbiTreeShaker implements IAbiTreeShaker { return result; } - shakeImports(abi: Abi, neededImports: ImportRefType[], state: { currentDepth: number; lastDepth: number, currentId: string } = { + private _shakeImports(abi: Abi, neededImports: ImportRefType[], state: { currentDepth: number; lastDepth: number, currentId: string } = { currentId: "", currentDepth: 0, lastDepth: 0, @@ -192,7 +193,7 @@ export class AbiTreeShaker implements IAbiTreeShaker { ...importDef }); - abiClone = this.shakeImports({ + abiClone = this._shakeImports({ version: abiClone.version, ...importDef }, transitiveImports, state) @@ -212,6 +213,13 @@ export class AbiTreeShaker implements IAbiTreeShaker { return abiClone; } + shakeImports(abi: Abi): Abi { + const neededImports = this.extractImportReferences(abi); + const treeWithShakenImports = this._shakeImports(abi, neededImports); + + return treeWithShakenImports + } + shakeTree(abi: Abi, neededDefNames: string[]): Abi { const neededDefs = this.extractNeededDefinitions(abi, neededDefNames); const referencedDefs = this.extractReferencedSiblingDefinitions(abi, neededDefs); @@ -221,9 +229,7 @@ export class AbiTreeShaker implements IAbiTreeShaker { ...referencedDefs, } - const neededImports = this.extractImportReferences(shakenTree); - const shakenTreeWithShakenImports = this.shakeImports(shakenTree, neededImports); - + const shakenTreeWithShakenImports = this.shakeImports(shakenTree); return shakenTreeWithShakenImports; } } \ No newline at end of file diff --git a/packages/schema/parse/src/AbiVisitor.ts b/packages/schema/abi/src/AbiVisitor.ts similarity index 100% rename from packages/schema/parse/src/AbiVisitor.ts rename to packages/schema/abi/src/AbiVisitor.ts diff --git a/packages/schema/parse/src/__tests__/imports.spec.ts b/packages/schema/abi/src/__tests__/imports.spec.ts similarity index 98% rename from packages/schema/parse/src/__tests__/imports.spec.ts rename to packages/schema/abi/src/__tests__/imports.spec.ts index 71345c2d38..4fa02eb7a0 100644 --- a/packages/schema/parse/src/__tests__/imports.spec.ts +++ b/packages/schema/abi/src/__tests__/imports.spec.ts @@ -1,6 +1,6 @@ import { AbiMerger } from "../AbiMerger" import { Abi } from "../definitions" -import { LocalImportsLinker } from "../imports" +import { ImportsLinker } from "../AbiImportsLinker" import { ExternalImportStatement, LocalImportStatement, SchemaParser } from "../types" describe("Imports parser", () => { @@ -242,7 +242,7 @@ describe("Imports parser", () => { } } - const importsParser = new LocalImportsLinker(mockSchemaParser, fetchers, new AbiMerger()) + const importsParser = new ImportsLinker(mockSchemaParser, fetchers, new AbiMerger()) const { abi, externalImportStatements } = await importsParser.link(rootSchema) const expectedAbi: Abi = { diff --git a/packages/schema/parse/src/__tests__/index.ts b/packages/schema/abi/src/__tests__/index.ts similarity index 100% rename from packages/schema/parse/src/__tests__/index.ts rename to packages/schema/abi/src/__tests__/index.ts diff --git a/packages/schema/parse/src/__tests__/parse-map-type.spec.ts b/packages/schema/abi/src/__tests__/parse-map-type.spec.ts similarity index 100% rename from packages/schema/parse/src/__tests__/parse-map-type.spec.ts rename to packages/schema/abi/src/__tests__/parse-map-type.spec.ts diff --git a/packages/schema/parse/src/__tests__/test-cases.spec.ts b/packages/schema/abi/src/__tests__/test-cases.spec.ts similarity index 95% rename from packages/schema/parse/src/__tests__/test-cases.spec.ts rename to packages/schema/abi/src/__tests__/test-cases.spec.ts index a0b0775b55..f723512f1c 100644 --- a/packages/schema/parse/src/__tests__/test-cases.spec.ts +++ b/packages/schema/abi/src/__tests__/test-cases.spec.ts @@ -1,4 +1,4 @@ -import { parseSchema } from "../"; +import { parseSchema } from ".."; import { fetchTestCases } from "./index"; describe("Polywrap Schema Parser Test Cases", () => { diff --git a/packages/schema/parse/src/__tests__/transforms.spec.ts b/packages/schema/abi/src/__tests__/transforms.spec.ts similarity index 99% rename from packages/schema/parse/src/__tests__/transforms.spec.ts rename to packages/schema/abi/src/__tests__/transforms.spec.ts index 0883968e9d..f24dd53c9a 100644 --- a/packages/schema/parse/src/__tests__/transforms.spec.ts +++ b/packages/schema/abi/src/__tests__/transforms.spec.ts @@ -1,4 +1,4 @@ -import { parseSchema } from "../"; +import { parseSchema } from ".."; import { addFirstLast, extendType } from "../transform"; import { createObjectDefinition, diff --git a/packages/schema/parse/src/__tests__/utils.spec.ts b/packages/schema/abi/src/__tests__/utils.spec.ts similarity index 100% rename from packages/schema/parse/src/__tests__/utils.spec.ts rename to packages/schema/abi/src/__tests__/utils.spec.ts diff --git a/packages/schema/parse/src/__tests__/validate-directives.spec.ts b/packages/schema/abi/src/__tests__/validate-directives.spec.ts similarity index 100% rename from packages/schema/parse/src/__tests__/validate-directives.spec.ts rename to packages/schema/abi/src/__tests__/validate-directives.spec.ts diff --git a/packages/schema/parse/src/__tests__/validate-types.spec.ts b/packages/schema/abi/src/__tests__/validate-types.spec.ts similarity index 100% rename from packages/schema/parse/src/__tests__/validate-types.spec.ts rename to packages/schema/abi/src/__tests__/validate-types.spec.ts diff --git a/packages/schema/parse/src/definitions.ts b/packages/schema/abi/src/definitions.ts similarity index 100% rename from packages/schema/parse/src/definitions.ts rename to packages/schema/abi/src/definitions.ts diff --git a/packages/schema/parse/src/index.ts b/packages/schema/abi/src/index.ts similarity index 84% rename from packages/schema/parse/src/index.ts rename to packages/schema/abi/src/index.ts index f41b90a059..c78e64a47c 100644 --- a/packages/schema/parse/src/index.ts +++ b/packages/schema/abi/src/index.ts @@ -4,8 +4,11 @@ import { Abi } from "./definitions"; import { AbiImportsLinker } from "./AbiImportsLinker"; import { ExternalSchemaFetcher, LocalSchemaFetcher, SchemaParser } from "./types"; -export * from "./abi"; export * from "./types"; +export * from "./AbiImportsLinker" +export * from "./AbiMerger" +export * from "./AbiTreeShaker" +export * from "./AbiVisitor" interface Args { schema: string @@ -32,3 +35,8 @@ export const parseAndLinkSchema = async ({ schema, parser, fetchers }: Args): Pr return linkedAbi } +export function createAbi(): Abi { + return { + version: "0.2" + }; +} diff --git a/packages/schema/parse/src/types.ts b/packages/schema/abi/src/types.ts similarity index 100% rename from packages/schema/parse/src/types.ts rename to packages/schema/abi/src/types.ts diff --git a/packages/schema/parse/tsconfig.build.json b/packages/schema/abi/tsconfig.build.json similarity index 100% rename from packages/schema/parse/tsconfig.build.json rename to packages/schema/abi/tsconfig.build.json diff --git a/packages/schema/parse/tsconfig.json b/packages/schema/abi/tsconfig.json similarity index 100% rename from packages/schema/parse/tsconfig.json rename to packages/schema/abi/tsconfig.json diff --git a/packages/schema/linking/src/render.ts b/packages/schema/linking/src/render.ts deleted file mode 100644 index 3f43a07e56..0000000000 --- a/packages/schema/linking/src/render.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any */ -import { template as schemaTemplate } from "./templates/schema.mustache"; -import { addHeader } from "./templates/header.mustache"; - -import Mustache from "mustache"; -import { - addFirstLast, - toGraphQLType, - transformAbi, - moduleCapabilities, - addAnnotations, -} from "@polywrap/schema-parse"; -import { GenericDefinition, WrapAbi } from "@polywrap/wrap-manifest-types-js"; - -// Remove mustache's built-in HTML escaping -Mustache.escape = (value) => value; - -export function renderSchema(abi: WrapAbi, header: boolean): string { - // Prepare the Abi for the renderer - abi = transformAbi(abi, addFirstLast); - abi = transformAbi(abi, toGraphQLType); - abi = transformAbi(abi, moduleCapabilities()); - abi = transformAbi(abi, addAnnotations); - abi = transformAbi(abi, { - enter: { - GenericDefinition: (def: GenericDefinition) => { - const comment = (def as any).comment || null; - return { ...def, comment }; - }, - }, - }); - - let schema = Mustache.render(schemaTemplate, { - abi, - }); - - if (header) { - schema = addHeader(schema); - } - - // Remove needless whitespace - return schema.replace(/[\n]{2,}/gm, "\n\n"); -} diff --git a/packages/schema/linking/src/templates/header.mustache.ts b/packages/schema/linking/src/templates/header.mustache.ts deleted file mode 100644 index 7c70a2164a..0000000000 --- a/packages/schema/linking/src/templates/header.mustache.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { header } from "@polywrap/schema-parse"; -import Mustache from "mustache"; - -// Remove mustache's built-in HTML escaping -Mustache.escape = (value) => value; - -export const template = `${header} - -{{schema}} -`; - -export function addHeader(schema: string): string { - return Mustache.render(template, { schema }); -} diff --git a/packages/schema/linking/src/templates/schema.mustache.ts b/packages/schema/linking/src/templates/schema.mustache.ts deleted file mode 100644 index 6419a43c7f..0000000000 --- a/packages/schema/linking/src/templates/schema.mustache.ts +++ /dev/null @@ -1,160 +0,0 @@ -const template = ` -{{#abi}} -{{#moduleType}}{{#comment}} -""" -{{comment}} -""" -{{/comment}} -type {{type}}{{#interfaces.length}} implements{{#interfaces}} {{type}}{{^last}} &{{/last}}{{/interfaces}}{{/interfaces.length}}{{#imports.length}} @imports( - types: [ - {{#imports}} - "{{type}}"{{^last}},{{/last}} - {{/imports}} - ] -){{/imports.length}}{{#capabilities.length}}{{#capabilities}} @capability( - type: "{{type}}", - uri: "{{uri}}", - namespace: "{{namespace}}" -){{/capabilities}}{{/capabilities.length}}{{#methods.length}} { - {{#methods}}{{#comment}} - """ - {{comment}} - """ - {{/comment}} - {{name}}{{#arguments.length}}( - {{#arguments}}{{#comment}} - """ - {{comment}} - """ - {{/comment}} - {{name}}: {{toGraphQLType}} - {{/arguments}} - ){{/arguments.length}}: {{#return}}{{toGraphQLType}}{{/return}}{{#env}} @env(required: {{required}}){{/env}} - {{^last}} - - {{/last}} - {{/methods}} -}{{/methods.length}}{{^methods.length}} { }{{/methods.length}} - -{{/moduleType}} -{{#envType}}{{#comment}} -""" -{{comment}} -""" -{{/comment}} -type {{type}}{{#interfaces.length}} implements{{#interfaces}} {{type}}{{^last}} &{{/last}}{{/interfaces}}{{/interfaces.length}}{{#properties.length}} { - {{#properties}}{{#comment}} - """ - {{comment}} - """ - {{/comment}} - {{name}}: {{toGraphQLType}} - {{/properties}} -}{{/properties.length}} - -{{/envType}} -{{#objectTypes}}{{#comment}} -""" -{{comment}} -""" -{{/comment}} -type {{type}}{{#interfaces.length}} implements{{#interfaces}} {{type}}{{^last}} &{{/last}}{{/interfaces}}{{/interfaces.length}}{{#properties.length}} { - {{#properties}}{{#comment}} - """ - {{comment}} - """ - {{/comment}} - {{name}}: {{toGraphQLType}} - {{/properties}} -}{{/properties.length}}{{^properties.length}} { }{{/properties.length}} - -{{/objectTypes}} -{{#enumTypes}}{{#comment}} -""" -{{comment}} -""" -{{/comment}} -enum {{type}} { - {{#constants}} - {{.}} - {{/constants}} -} - -{{/enumTypes}} -### Imported Modules START ### - -{{#importedModuleTypes}}{{#comment}} -""" -{{comment}} -""" -{{/comment}} -type {{type}}{{#interfaces.length}} implements{{#interfaces}} {{type}}{{^last}} &{{/last}}{{/interfaces}}{{/interfaces.length}} @imported( - uri: "{{uri}}", - namespace: "{{namespace}}", - nativeType: "{{nativeType}}" -){{#isInterface}} @enabled_interface{{/isInterface}}{{#methods.length}} { - {{#methods}}{{#comment}} - """ - {{comment}} - """ - {{/comment}} - {{name}}{{#arguments.length}}( - {{#arguments}}{{#comment}} - """ - {{comment}} - """ - {{/comment}} - {{name}}: {{toGraphQLType}} - {{/arguments}} - ){{/arguments.length}}: {{#return}}{{toGraphQLType}}{{/return}} - {{^last}} - - {{/last}} - {{/methods}} -}{{/methods.length}}{{^methods.length}} { }{{/methods.length}} - -{{/importedModuleTypes}} -### Imported Modules END ### - -### Imported Objects START ### - -{{#importedObjectTypes}}{{#comment}} -""" -{{comment}} -""" -{{/comment}} -type {{type}}{{#interfaces.length}} implements{{#interfaces}} {{type}}{{^last}} &{{/last}}{{/interfaces}}{{/interfaces.length}} @imported( - uri: "{{uri}}", - namespace: "{{namespace}}", - nativeType: "{{nativeType}}" -){{#properties.length}} { - {{#properties}}{{#comment}} - """ - {{comment}} - """ - {{/comment}} - {{name}}: {{toGraphQLType}} - {{/properties}} -}{{/properties.length}}{{^properties.length}} { }{{/properties.length}} - -{{/importedObjectTypes}} - -{{#importedEnumTypes}}{{#comment}} -""" -{{comment}} -""" -{{/comment}} -enum {{type}} @imported( - uri: "{{uri}}", - namespace: "{{namespace}}", - nativeType: "{{nativeType}}" -) { - {{#constants}} - {{.}} - {{/constants}} -} - -{{/importedEnumTypes}} -### Imported Objects END ###{{/abi}}`; - -export { template }; diff --git a/packages/schema/parse/src/abi/index.ts b/packages/schema/parse/src/abi/index.ts deleted file mode 100644 index 471cb0cc17..0000000000 --- a/packages/schema/parse/src/abi/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Abi } from "../definitions"; - -export * from "@polywrap/wrap-manifest-types-js"; -export * from "./utils"; - -export function createAbi(): Abi { - return { - version: "0.2" - }; -} diff --git a/packages/schema/parse/src/abi/utils.ts b/packages/schema/parse/src/abi/utils.ts deleted file mode 100644 index 9e896c3cea..0000000000 --- a/packages/schema/parse/src/abi/utils.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { mapKeyTypeSet, scalarTypeSet } from "../definitions"; - -export function isMapKeyType(type: string): boolean { - return type in mapKeyTypeSet; -} - -export const MODULE_NAME = "Module"; - -export function isModuleType(type: string): boolean { - return type === MODULE_NAME; -} - -export function isImportedModuleType(type: string): boolean { - return type.endsWith(`_${MODULE_NAME}`); -} - -export function isScalarType(type: string): boolean { - return type in scalarTypeSet; -} diff --git a/packages/schema/parse/src/transform/addFirstLast.ts b/packages/schema/parse/src/transform/addFirstLast.ts deleted file mode 100644 index b678ea8717..0000000000 --- a/packages/schema/parse/src/transform/addFirstLast.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { AbiTransforms } from "."; - -export const addFirstLast: AbiTransforms = { - enter: { - DefinitionOrType: (def) => { - const arrays: Record = {}; - - for (const key of Object.keys(def)) { - const value = ((def as unknown) as Record)[key]; - - if (Array.isArray(value)) { - arrays[key] = setFirstLast(value); - } - } - - return { - ...def, - ...arrays, - }; - }, - Abi: (abi) => ({ - ...abi, - objectTypes: setFirstLast(abi.objects), - // TODO: why only local objects? - // importedObjectTypes: setFirstLast(abi.importedObjectTypes), - // importedModuleTypes: setFirstLast(abi.importedModuleTypes), - // importedEnvTypes: setFirstLast(abi.importedEnvTypes), - }), - }, -}; - -function setFirstLast(array: T[] | undefined): T[] { - return array - ? array.map((item, index) => { - if (typeof item === "object") { - return { - ...item, - first: index === 0 ? true : null, - last: index === array.length - 1 ? true : null, - }; - } else { - return item; - } - }) - : []; -} diff --git a/packages/schema/parse/src/transform/extendType.ts b/packages/schema/parse/src/transform/extendType.ts deleted file mode 100644 index ccdcf2b098..0000000000 --- a/packages/schema/parse/src/transform/extendType.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { AbiTransforms } from "."; - -// TODO: where is this one used? - -// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types -export function extendType(extension: any): AbiTransforms { - return { - enter: { - Abi: (abi) => ({ - ...abi, - extension, - }), - // eslint-disable-next-line @typescript-eslint/naming-convention - DefinitionOrType: (def) => ({ - ...def, - ...extension, - }), - }, - }; -} diff --git a/packages/schema/parse/src/transform/hasImports.ts b/packages/schema/parse/src/transform/hasImports.ts deleted file mode 100644 index dc8ae537cc..0000000000 --- a/packages/schema/parse/src/transform/hasImports.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { AbiTransforms } from "."; - -export const hasImports: AbiTransforms = { - enter: { - Abi: (abi) => ({ - ...abi, - hasImports: () => abi.imports && abi.imports.length, - }), - }, -}; From 97e5d1b67a4e5970606af7d30417450b0b847ac4 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Wed, 22 Feb 2023 16:54:32 +0100 Subject: [PATCH 58/90] (feat): extracted ABI types into separate packages --- packages/schema/abi-types/.eslintrc.js | 95 +++++++++++++++++++ packages/schema/abi-types/README.md | 68 +++++++++++++ packages/schema/abi-types/jest.config.js | 12 +++ packages/schema/abi-types/package.json | 36 +++++++ .../{abi => abi-types}/src/definitions.ts | 0 .../src/types.ts => abi-types/src/index.ts} | 2 + packages/schema/abi-types/tsconfig.build.json | 9 ++ packages/schema/abi-types/tsconfig.json | 10 ++ packages/schema/abi/package.json | 2 +- packages/schema/abi/src/AbiImportsLinker.ts | 4 +- packages/schema/abi/src/AbiMerger.ts | 2 +- packages/schema/abi/src/AbiTreeShaker.ts | 2 +- packages/schema/abi/src/AbiVisitor.ts | 2 +- packages/schema/abi/src/index.ts | 6 +- 14 files changed, 240 insertions(+), 10 deletions(-) create mode 100644 packages/schema/abi-types/.eslintrc.js create mode 100644 packages/schema/abi-types/README.md create mode 100644 packages/schema/abi-types/jest.config.js create mode 100644 packages/schema/abi-types/package.json rename packages/schema/{abi => abi-types}/src/definitions.ts (100%) rename packages/schema/{abi/src/types.ts => abi-types/src/index.ts} (96%) create mode 100644 packages/schema/abi-types/tsconfig.build.json create mode 100644 packages/schema/abi-types/tsconfig.json diff --git a/packages/schema/abi-types/.eslintrc.js b/packages/schema/abi-types/.eslintrc.js new file mode 100644 index 0000000000..b315b11479 --- /dev/null +++ b/packages/schema/abi-types/.eslintrc.js @@ -0,0 +1,95 @@ + +module.exports = { + extends: "../../../.eslintrc.js", + overrides: [ + { + files: ["*.ts"], + parser: "@typescript-eslint/parser", + parserOptions: { + project: ["tsconfig.json"], + }, + plugins: [ + "eslint-plugin-import", + "@typescript-eslint", + "prettier" + ], + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "prettier", + ], + rules: { + "prettier/prettier": ["error"], + "@typescript-eslint/naming-convention": [ + "error", + {selector: "default", format: ["camelCase"]}, + { + selector: [ + "classProperty", "parameterProperty", + "classMethod", "parameter" + ], + format: ["camelCase"], + leadingUnderscore: "allow" + }, + //wrapper host methods doesn"t satisfy neither camel or snake + {selector: ["objectLiteralMethod", "typeMethod"], filter: {regex: "^_wrap_.*", match: true}, format: null}, + //variable must be in camel or upper case + {selector: "variable", format: ["camelCase", "PascalCase", "UPPER_CASE"], leadingUnderscore: "allow"}, + //classes and types must be in PascalCase + {selector: ["typeLike", "enum"], format: ["PascalCase"]}, + {selector: "enumMember", format: null}, + //ignore rule for quoted stuff + { + selector: [ + "classProperty", + "objectLiteralProperty", + "typeProperty", + "classMethod", + "objectLiteralMethod", + "typeMethod", + "accessor", + "enumMember" + ], + format: null, + modifiers: ["requiresQuotes"] + }, + //ignore rules on destructured params + { + selector: "variable", + modifiers: ["destructured"], + format: null + }, + { + selector: [ + "objectLiteralProperty", + "objectLiteralMethod", + ], + format: ["PascalCase", "camelCase"] + }, + ], + "@typescript-eslint/explicit-module-boundary-types": "error", + "@typescript-eslint/member-ordering": "error", + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-require-imports": "error", + "@typescript-eslint/no-unused-vars": ["error", { + "varsIgnorePattern": "^_", + "argsIgnorePattern": "^_", + }], + "@typescript-eslint/no-floating-promises": "error", + "import/no-extraneous-dependencies": ["error", { + "devDependencies": false, + "optionalDependencies": true, + "peerDependencies": false + }], + "import/order": [ + "error", + { + "groups": [["index", "sibling", "parent", "internal"], ["external", "builtin"], "object"], + "newlines-between": "always" + } + ], + }, + }, + ], +}; diff --git a/packages/schema/abi-types/README.md b/packages/schema/abi-types/README.md new file mode 100644 index 0000000000..d0e8400831 --- /dev/null +++ b/packages/schema/abi-types/README.md @@ -0,0 +1,68 @@ +# @polywrap/schema-parse + +Parse & validate WRAP schemas, converting them into a WRAP ABI structure. Optionally perform transformations upon the WRAP ABI. + +## Usage +```typescript +import { + Abi, + parseSchema, + ParserOptions +} from "@polywrap/schema-parse"; + +const schema = readFileSync("module.graphql", "utf-8"); +const options: ParserOptions = { }; + +const abi: Abi = parseSchema(schema, options); +``` + +### Options +```typescript +interface ParserOptions { + // Disable schema validation + noValidate?: boolean; + // Use custom validators + validators?: SchemaValidatorBuilder[]; + // Use custom extractors + extractors?: SchemaExtractorBuilder[]; + // Use custom transformations + transforms?: AbiTransforms[]; +} +``` + +### ABI Transforms +ABI transformations can be used to modify the ABI structure. A variety of pre-defined transformations can be found in the [./src/transform/](./src/transform/) directory. + +Example: +```typescript +import { + Abi, + AbiTransforms, + GenericDefinition, + parseSchema +} from "@polywrap/schema-parse"; + +function extendType(extension: any): AbiTransforms { + return { + enter: { + Abi: (abi: Abi) => ({ + ...abi, + extension, + }), + GenericDefinition: (def: GenericDefinition) => ({ + ...def, + ...extension, + }), + }, + }; +} +``` + +Usage: +```typescript +parseSchema(schema, { + transforms: [ + extendType({ newProp: "foo" }) + ] +}); +``` diff --git a/packages/schema/abi-types/jest.config.js b/packages/schema/abi-types/jest.config.js new file mode 100644 index 0000000000..1d548add36 --- /dev/null +++ b/packages/schema/abi-types/jest.config.js @@ -0,0 +1,12 @@ +module.exports = { + collectCoverage: true, + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"], + modulePathIgnorePatterns: ['./src/__tests__/cases'], + globals: { + 'ts-jest': { + diagnostics: false + } + } +}; diff --git a/packages/schema/abi-types/package.json b/packages/schema/abi-types/package.json new file mode 100644 index 0000000000..7d86762b0d --- /dev/null +++ b/packages/schema/abi-types/package.json @@ -0,0 +1,36 @@ +{ + "name": "@polywrap/abi-types", + "description": "Polywrap ABI Core Types", + "version": "0.10.0-pre.5", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/polywrap/monorepo.git" + }, + "main": "build/index.js", + "files": [ + "build" + ], + "scripts": { + "build": "rimraf ./build && tsc --project tsconfig.build.json", + "lint": "eslint --color src/", + "test": "jest --passWithNoTests --runInBand --verbose", + "test:ci": "jest --passWithNoTests --runInBand --verbose", + "test:watch": "jest --watch --passWithNoTests --verbose" + }, + "devDependencies": { + "@polywrap/test-cases": "0.10.0-pre.5", + "@types/deep-equal": "1.0.1", + "@types/jest": "26.0.8", + "@types/prettier": "2.6.0", + "jest": "26.6.3", + "rimraf": "3.0.2", + "ts-jest": "26.5.4", + "ts-node": "8.10.2", + "typescript": "4.1.6" + }, + "gitHead": "7346adaf5adb7e6bbb70d9247583e995650d390a", + "publishConfig": { + "access": "public" + } +} diff --git a/packages/schema/abi/src/definitions.ts b/packages/schema/abi-types/src/definitions.ts similarity index 100% rename from packages/schema/abi/src/definitions.ts rename to packages/schema/abi-types/src/definitions.ts diff --git a/packages/schema/abi/src/types.ts b/packages/schema/abi-types/src/index.ts similarity index 96% rename from packages/schema/abi/src/types.ts rename to packages/schema/abi-types/src/index.ts index 9653622450..9dcc01fc73 100644 --- a/packages/schema/abi/src/types.ts +++ b/packages/schema/abi-types/src/index.ts @@ -1,5 +1,7 @@ import { Abi } from "./definitions"; +export * from "./definitions"; + export interface ImportStatement { kind: "local" | "external"; importedTypes: string[]; diff --git a/packages/schema/abi-types/tsconfig.build.json b/packages/schema/abi-types/tsconfig.build.json new file mode 100644 index 0000000000..77aadfdd2f --- /dev/null +++ b/packages/schema/abi-types/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "include": [ + "./src/**/*.ts" + ], + "exclude": [ + "./src/**/__tests__" + ] +} diff --git a/packages/schema/abi-types/tsconfig.json b/packages/schema/abi-types/tsconfig.json new file mode 100644 index 0000000000..c199f55b7d --- /dev/null +++ b/packages/schema/abi-types/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../../tsconfig", + "compilerOptions": { + "outDir": "build" + }, + "include": [ + "./src/**/*.ts" + ], + "exclude": [] +} \ No newline at end of file diff --git a/packages/schema/abi/package.json b/packages/schema/abi/package.json index a27f0f5fd0..6032f5c18d 100644 --- a/packages/schema/abi/package.json +++ b/packages/schema/abi/package.json @@ -19,7 +19,7 @@ "test:watch": "jest --watch --passWithNoTests --verbose" }, "dependencies": { - "@dorgjelli/graphql-schema-cycles": "1.1.4", + "@polywrap/abi-types": "0.10.0-pre.5", "@polywrap/wrap-manifest-types-js": "0.10.0-pre.5" }, "devDependencies": { diff --git a/packages/schema/abi/src/AbiImportsLinker.ts b/packages/schema/abi/src/AbiImportsLinker.ts index f3db69cf4c..abc92d2724 100644 --- a/packages/schema/abi/src/AbiImportsLinker.ts +++ b/packages/schema/abi/src/AbiImportsLinker.ts @@ -1,8 +1,8 @@ import { IAbiMerger } from "./AbiMerger"; -import { ExternalImportStatement, ExternalSchemaFetcher, LocalImportStatement, LocalSchemaFetcher, SchemaParser } from "./types"; -import { Abi, ImportedAbi } from "./definitions"; import { IAbiTreeShaker } from "./AbiTreeShaker"; +import { Abi, ExternalImportStatement, ExternalSchemaFetcher, ImportedAbi, LocalImportStatement, LocalSchemaFetcher, SchemaParser } from "@polywrap/abi-types" + export interface IAbiImportsLinker { link: (rootAbi: Abi, importStatements?: { local: LocalImportStatement[]; diff --git a/packages/schema/abi/src/AbiMerger.ts b/packages/schema/abi/src/AbiMerger.ts index 30e28df216..a8bc437e7b 100644 --- a/packages/schema/abi/src/AbiMerger.ts +++ b/packages/schema/abi/src/AbiMerger.ts @@ -1,4 +1,4 @@ -import { Abi } from "./definitions"; +import { Abi } from "@polywrap/abi-types"; export interface IAbiMerger { merge(abis: Abi[]): Abi diff --git a/packages/schema/abi/src/AbiTreeShaker.ts b/packages/schema/abi/src/AbiTreeShaker.ts index 430b6244c3..5791872536 100644 --- a/packages/schema/abi/src/AbiTreeShaker.ts +++ b/packages/schema/abi/src/AbiTreeShaker.ts @@ -1,4 +1,4 @@ -import { Abi, AbiDefs, EnumDef, ImportRefType, ObjectDef, RefType } from "./definitions"; +import { Abi, AbiDefs, EnumDef, ImportRefType, ObjectDef, RefType } from "@polywrap/abi-types"; import { AbiVisitor } from "./AbiVisitor"; type ReferenceableDef = ObjectDef | EnumDef; diff --git a/packages/schema/abi/src/AbiVisitor.ts b/packages/schema/abi/src/AbiVisitor.ts index a05236cf16..fc243b5ff6 100644 --- a/packages/schema/abi/src/AbiVisitor.ts +++ b/packages/schema/abi/src/AbiVisitor.ts @@ -1,4 +1,4 @@ -import { Abi, AnyType, ArgumentDef, ArrayType, EnumDef, FunctionDef, ImportedAbi, ImportRefType, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType } from "./definitions"; +import { Abi, AnyType, ArgumentDef, ArrayType, EnumDef, FunctionDef, ImportedAbi, ImportRefType, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType } from "@polywrap/abi-types"; export interface IAbiVisitor { Abi?: (node: Abi) => void; diff --git a/packages/schema/abi/src/index.ts b/packages/schema/abi/src/index.ts index c78e64a47c..86012c8928 100644 --- a/packages/schema/abi/src/index.ts +++ b/packages/schema/abi/src/index.ts @@ -1,10 +1,8 @@ import { AbiMerger } from "./AbiMerger"; -import { AbiTreeShaker } from "./AbiTreeShaker"; -import { Abi } from "./definitions"; +import { AbiTreeShaker } from "./AbiTreeShaker";; import { AbiImportsLinker } from "./AbiImportsLinker"; -import { ExternalSchemaFetcher, LocalSchemaFetcher, SchemaParser } from "./types"; +import { ExternalSchemaFetcher, LocalSchemaFetcher, SchemaParser, Abi } from "@polywrap/abi-types"; -export * from "./types"; export * from "./AbiImportsLinker" export * from "./AbiMerger" export * from "./AbiTreeShaker" From 5d9de93f7be47775f0adcb50bf213a7d5fb2daf9 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Thu, 23 Feb 2023 22:21:20 +0100 Subject: [PATCH 59/90] (chore): moved createABI to the core types package --- packages/schema/abi-types/src/index.ts | 8 +++++++- packages/schema/abi/package.json | 3 ++- packages/schema/abi/src/index.ts | 6 ------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/schema/abi-types/src/index.ts b/packages/schema/abi-types/src/index.ts index 9dcc01fc73..ec02a9a686 100644 --- a/packages/schema/abi-types/src/index.ts +++ b/packages/schema/abi-types/src/index.ts @@ -37,4 +37,10 @@ export interface ExternalSchemaFetcher { export interface LocalSchemaFetcher { fetch: (path: string) => Promise; -} \ No newline at end of file +} + +export function createAbi(): Abi { + return { + version: "0.2" + }; +} diff --git a/packages/schema/abi/package.json b/packages/schema/abi/package.json index 6032f5c18d..a02e67b6d8 100644 --- a/packages/schema/abi/package.json +++ b/packages/schema/abi/package.json @@ -20,7 +20,8 @@ }, "dependencies": { "@polywrap/abi-types": "0.10.0-pre.5", - "@polywrap/wrap-manifest-types-js": "0.10.0-pre.5" + "@polywrap/wrap-manifest-types-js": "0.10.0-pre.5", + "@polywrap/graphql-schema-parser": "0.10.0-pre.5" }, "devDependencies": { "@polywrap/test-cases": "0.10.0-pre.5", diff --git a/packages/schema/abi/src/index.ts b/packages/schema/abi/src/index.ts index 86012c8928..c6fc2cbabf 100644 --- a/packages/schema/abi/src/index.ts +++ b/packages/schema/abi/src/index.ts @@ -32,9 +32,3 @@ export const parseAndLinkSchema = async ({ schema, parser, fetchers }: Args): Pr return linkedAbi } - -export function createAbi(): Abi { - return { - version: "0.2" - }; -} From da274e5563f42aa2be70991f62a4758ba32b69cd Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Thu, 23 Feb 2023 22:21:37 +0100 Subject: [PATCH 60/90] (feat): implemented circular dependency detection --- .../abi/src/CircularDependencyValidator.ts | 80 ++++++++++++ packages/schema/abi/src/DependencyTree.ts | 115 ++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 packages/schema/abi/src/CircularDependencyValidator.ts create mode 100644 packages/schema/abi/src/DependencyTree.ts diff --git a/packages/schema/abi/src/CircularDependencyValidator.ts b/packages/schema/abi/src/CircularDependencyValidator.ts new file mode 100644 index 0000000000..a858b18461 --- /dev/null +++ b/packages/schema/abi/src/CircularDependencyValidator.ts @@ -0,0 +1,80 @@ +import { Abi } from "@polywrap/abi-types"; +import { AbiVisitor } from "."; +import { DependencyTree } from "./DependencyTree"; + +export class CircularDependencyValidator { + private definitionsTree: DependencyTree + private importsTree: DependencyTree + + private traverseAbi(abi: Abi) { + const state: { currentDepth: number; lastDepth: number, currentId: string; currentObject?: string } = { + currentId: "", + currentDepth: 0, + lastDepth: 0, + currentObject: undefined, + } + + const importsVisitor = new AbiVisitor({ + enter: { + Imports: () => { + state.currentDepth += 1 + }, + Import: (importDef) => { + // TODO: this logic works but could be improved + if (state.currentDepth > state.lastDepth) { + state.currentId = `${state.currentId}.${importDef.id}` + } else if (state.currentDepth < state.lastDepth) { + state.currentId = state.currentId.split(".").slice(0, state.currentDepth - 1).join(".") + state.currentId = `${state.currentId}.${importDef.id}` + } else { + state.currentId = state.currentId.split(".").slice(0, state.currentDepth - 1).join(".") + state.currentId = `${state.currentId}.${importDef.id}` + } + + this.importsTree.addNode(state.currentId) + state.lastDepth = state.currentDepth + }, + ObjectDef: (objectDef) => { + state.currentObject = objectDef.name + this.definitionsTree.addNode(`${state.currentId}.${objectDef.name}`) + }, + RefType: (refType) => { + if (refType.ref_kind === "Object") { + this.definitionsTree.addEdge(`${state.currentId}.${state.currentObject}`, `${state.currentId}.${refType.ref_name}`) + } + }, + ImportRefType: (importRefType) => { + this.importsTree.addEdge(state.currentId, `${state.currentId}.${importRefType.import_id}`) + + if (importRefType.ref_kind === "Object") { + this.definitionsTree.addEdge(`${state.currentId}.${state.currentObject}`, `${state.currentId}.${importRefType.import_id}.${importRefType.ref_name}`) + } + } + }, + leave: { + Imports: () => { + state.currentDepth -= 1 + }, + ObjectDef: () => { + state.currentObject = undefined + }, + } + }); + + importsVisitor.visit(abi); + } + + detectCircularDependencies(abi: Abi) { + this.definitionsTree = new DependencyTree(); + this.importsTree = new DependencyTree(); + this.traverseAbi(abi); + + const circularDependencies = this.definitionsTree.findCircularDependencies(); + const circularImports = this.importsTree.findCircularDependencies(); + + return { + circularDependencies, + circularImports, + } + } +} \ No newline at end of file diff --git a/packages/schema/abi/src/DependencyTree.ts b/packages/schema/abi/src/DependencyTree.ts new file mode 100644 index 0000000000..f4846c5f79 --- /dev/null +++ b/packages/schema/abi/src/DependencyTree.ts @@ -0,0 +1,115 @@ +export class DependencyTree { + private nodes: {[id: string]: undefined} = {}; + private edges: {[id: string]: string[]} = {}; + + addNode(id: string) { + this.nodes[id] = undefined; + this.edges[id] = []; + } + + addEdge(from: string, to: string) { + this.edges[from].push(to); + } + + hasNode(nodeId: string) { + return this.nodes.hasOwnProperty(nodeId); + } + + getNodeProperties(nodeId: string) { + return this.nodes[nodeId]; + } + + getNodeDependencies(nodeId: string) { + return this.edges[nodeId]; + } + + getAllDependencies(nodeIds: string[]): string[] { + const visited = new Set(); + const queue = [...nodeIds]; + while (queue.length > 0) { + const nodeId = queue.shift(); + if (nodeId && !visited.has(nodeId)) { + visited.add(nodeId); + const dependencies = this.getNodeDependencies(nodeId); + queue.push(...dependencies); + } + } + return Array.from(visited); + } + + getRootNodes(): string[] { + const allNodes = Object.keys(this.nodes); + const dependentNodes = new Set(); + for (const from in this.edges) { + const toNodes = this.edges[from]; + for (const to of toNodes) { + dependentNodes.add(to); + } + } + const rootNodes = allNodes.filter(nodeId => !dependentNodes.has(nodeId)); + return rootNodes; + } + + printVisualTree(rootNodeId: string, prefix = '', isLast = true, output = ''): string { + const branchChar = isLast ? '└── ' : '├── '; + const currentNode = `${prefix}${branchChar}${rootNodeId}\n`; + output += currentNode; + const dependencies = this.getNodeDependencies(rootNodeId); + const numDeps = dependencies.length; + for (let i = 0; i < numDeps; i++) { + const depNodeId = dependencies[i]; + const newPrefix = prefix + (isLast ? ' ' : '│ '); + const isLastDep = i === numDeps - 1; + output = this.printVisualTree(depNodeId, newPrefix, isLastDep, output); + } + return output; + } + + findCircularDependencies(): { detected: true; cycle: string; } | { detected: false } { + const visited = new Set(); + const inStack = new Set(); + const cycle: string[] = []; + + const dfs = (nodeId: string) => { + visited.add(nodeId); + inStack.add(nodeId); + const dependencies = this.getNodeDependencies(nodeId); + for (const depNodeId of dependencies) { + if (!visited.has(depNodeId)) { + dfs(depNodeId); + } else if (inStack.has(depNodeId)) { + cycle.unshift(depNodeId); + cycle.unshift(nodeId); + return; + } + } + inStack.delete(nodeId); + }; + + for (const nodeId in this.nodes) { + if (!visited.has(nodeId)) { + dfs(nodeId); + if (cycle.length > 0) { + break; + } + } + } + + if (cycle.length > 0) { + const cycleStart = cycle[0]; + const cycleEnd = cycle[cycle.length - 1]; + const cycleIndex = cycle.indexOf(cycleStart); + const cycleLength = cycle.length - cycleIndex; + const cycleSlice = cycle.slice(cycleIndex, cycleIndex + cycleLength); + const cycleString = cycleSlice.join(' -> '); + return { + detected: true, + cycle: `Circular dependency detected: ${cycleString} -> ${cycleEnd}` + }; + } else { + return { + detected: false, + }; + } + } +} \ No newline at end of file From f2b13afcaa4f21869e16673741e4a6e064623664 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Fri, 24 Feb 2023 21:48:41 +0100 Subject: [PATCH 61/90] (feat): made ABI visitor mutable with GraphQL's AST visitor devexp --- packages/schema/abi/src/AbiVisitor.ts | 285 +++++++++++++++----------- 1 file changed, 165 insertions(+), 120 deletions(-) diff --git a/packages/schema/abi/src/AbiVisitor.ts b/packages/schema/abi/src/AbiVisitor.ts index fc243b5ff6..57c854fc0f 100644 --- a/packages/schema/abi/src/AbiVisitor.ts +++ b/packages/schema/abi/src/AbiVisitor.ts @@ -1,244 +1,289 @@ -import { Abi, AnyType, ArgumentDef, ArrayType, EnumDef, FunctionDef, ImportedAbi, ImportRefType, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType } from "@polywrap/abi-types"; +import { Abi, AnyType, ArgumentDef, ArrayType, EnumDef, FunctionDef, ImportedAbi, ImportRefType, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType, UnlinkedImportRefType } from "@polywrap/abi-types"; + +type VisitorFunction = (node: T) => T | void; export interface IAbiVisitor { - Abi?: (node: Abi) => void; - Imports?: (node: ImportedAbi[]) => void; - Import?: (node: ImportedAbi) => void; - FunctionDef?: (node: FunctionDef) => void; - ArgumentDef?: (node: ArgumentDef) => void; - ResultDef?: (node: ResultDef) => void; - ObjectDef?: (node: ObjectDef) => void; - PropertyDef?: (node: PropertyDef) => void; - EnumDef?: (node: EnumDef) => void; - ScalarType?: (node: ScalarType) => void; - RefType?: (node: RefType) => void; - ImportRefType?: (node: ImportRefType) => void; - ArrayType?: (node: ArrayType) => void; - MapType?: (node: MapType) => void; - AnyType?: (node: AnyType) => void; + Abi?: VisitorFunction; + ImportedAbi?: VisitorFunction; + Import?: VisitorFunction; + FunctionDef?: VisitorFunction; + ArgumentDef?: VisitorFunction; + ResultDef?: VisitorFunction; + ObjectDef?: VisitorFunction; + PropertyDef?: VisitorFunction; + EnumDef?: VisitorFunction; + ScalarType?: VisitorFunction; + RefType?: VisitorFunction; + ImportRefType?: VisitorFunction; + UnlinkedImportRefType?: (node: UnlinkedImportRefType) => UnlinkedImportRefType; + ArrayType?: VisitorFunction; + MapType?: VisitorFunction; + AnyType?: VisitorFunction; } -interface IAbiVisitorEnterAndLeave { +export interface IAbiVisitorEnterAndLeave { enter?: IAbiVisitor; leave?: IAbiVisitor; } export class AbiVisitor implements IAbiVisitor { - constructor(private readonly visitor: IAbiVisitorEnterAndLeave) { } - - Abi(node: Abi) { - if (this.visitor.enter?.Abi) { - this.visitor.enter.Abi(node); - } + constructor(protected readonly visitor: IAbiVisitorEnterAndLeave) { } - if (node.imports) { - this.Imports(node.imports); - } + private coerceVoidToUndefined(value: T | void): T | undefined { + return value === undefined ? undefined : value; + } - if (node.functions) { - for (const functionNode of node.functions) { - this.FunctionDef(functionNode); - } - } + Abi(node: Abi): Abi { + let mutatedNode = node; - if (node.objects) { - for (const objectNode of node.objects) { - this.ObjectDef(objectNode); - } + if (this.visitor.enter?.Abi) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.Abi(node)) ?? mutatedNode; } - if (node.enums) { - for (const enumNode of node.enums) { - this.EnumDef(enumNode); - } - } + mutatedNode.imports = mutatedNode.imports?.map((importNode) => this.Import(importNode)); + mutatedNode.functions = mutatedNode.functions?.map((functionNode) => this.FunctionDef(functionNode)); + mutatedNode.objects = mutatedNode.objects?.map((objectNode) => this.ObjectDef(objectNode)); + mutatedNode.enums = mutatedNode.enums?.map((enumNode) => this.EnumDef(enumNode)); if (this.visitor.leave?.Abi) { - this.visitor.leave.Abi(node); - } - } - - Imports(node: ImportedAbi[]) { - if (this.visitor.enter?.Imports) { - this.visitor.enter.Imports(node); - } - - for (const importNode of node) { - this.Import(importNode); + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.Abi(node)) ?? mutatedNode; } - if (this.visitor.leave?.Imports) { - this.visitor.leave.Imports(node); - } + return mutatedNode; } - Import(node: ImportedAbi) { + Import(node: ImportedAbi): ImportedAbi { + let mutatedNode = node; + if (this.visitor.enter?.Import) { - this.visitor.enter.Import(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.Import(mutatedNode)) ?? mutatedNode; } - this.Abi({ - version: "0.2", - ...node - }); + mutatedNode.imports = mutatedNode.imports?.map((importNode) => this.Import(importNode)); + mutatedNode.functions = mutatedNode.functions?.map((functionNode) => this.FunctionDef(functionNode)); + mutatedNode.objects = mutatedNode.objects?.map((objectNode) => this.ObjectDef(objectNode)); + mutatedNode.enums = mutatedNode.enums?.map((enumNode) => this.EnumDef(enumNode)); if (this.visitor.leave?.Import) { - this.visitor.leave.Import(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.Import(mutatedNode)) ?? mutatedNode; } + + return mutatedNode; } - FunctionDef(node: FunctionDef) { - if (this.visitor.enter?.FunctionDef) { - this.visitor.enter.FunctionDef(node); - } + FunctionDef(node: FunctionDef): FunctionDef { + let mutatedNode = node; - if (node.args) { - for (const argumentNode of node.args) { - this.ArgumentDef(argumentNode); - } + if (this.visitor.enter?.FunctionDef) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.FunctionDef(mutatedNode)) ?? mutatedNode; } - this.ResultDef(node.result); + mutatedNode.args = mutatedNode.args?.map((argumentNode) => this.ArgumentDef(argumentNode)); + mutatedNode.result = this.ResultDef(mutatedNode.result); if (this.visitor.leave?.FunctionDef) { - this.visitor.leave.FunctionDef(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.FunctionDef(mutatedNode)) ?? mutatedNode; } + + return mutatedNode; } - ArgumentDef(node: ArgumentDef) { + ArgumentDef(node: ArgumentDef): ArgumentDef { + let mutatedNode = node; + if (this.visitor.enter?.ArgumentDef) { - this.visitor.enter.ArgumentDef(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.ArgumentDef(mutatedNode)) ?? mutatedNode; } - this.AnyType(node.type); + + mutatedNode.type = this.AnyType(mutatedNode.type); if (this.visitor.leave?.ArgumentDef) { - this.visitor.leave.ArgumentDef(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.ArgumentDef(mutatedNode)) ?? mutatedNode; } + + return mutatedNode; } - ResultDef(node: ResultDef) { + ResultDef(node: ResultDef): ResultDef { + let mutatedNode = node; if (this.visitor.enter?.ResultDef) { - this.visitor.enter.ResultDef(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.ResultDef(mutatedNode)) ?? mutatedNode; } - this.AnyType(node.type); + mutatedNode.type = this.AnyType(mutatedNode.type); if (this.visitor.leave?.ResultDef) { - this.visitor.leave.ResultDef(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.ResultDef(mutatedNode)) ?? mutatedNode; } + + return mutatedNode; } - ObjectDef(node: ObjectDef) { + ObjectDef(node: ObjectDef): ObjectDef { + let mutatedNode = node; if (this.visitor.enter?.ObjectDef) { - this.visitor.enter.ObjectDef(node); - } - if (node.props) { - for (const propertyNode of node.props) { - this.PropertyDef(propertyNode); - } + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.ObjectDef(mutatedNode)) ?? mutatedNode; } + + mutatedNode.props = mutatedNode.props?.map((propertyNode) => this.PropertyDef(propertyNode)); + if (this.visitor.leave?.ObjectDef) { - this.visitor.leave.ObjectDef(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.ObjectDef(mutatedNode)) ?? mutatedNode; } + + return mutatedNode; } - PropertyDef(node: PropertyDef) { + PropertyDef(node: PropertyDef): PropertyDef { + let mutatedNode = node; + if (this.visitor.enter?.PropertyDef) { - this.visitor.enter.PropertyDef(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.PropertyDef(mutatedNode)) ?? mutatedNode; } - this.AnyType(node.type); + + mutatedNode.type = this.AnyType(mutatedNode.type); + if (this.visitor.leave?.PropertyDef) { - this.visitor.leave?.PropertyDef(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave?.PropertyDef(mutatedNode)) ?? mutatedNode; } + + return mutatedNode; } - EnumDef(node: EnumDef) { + EnumDef(node: EnumDef): EnumDef { + let mutatedNode = node; + if (this.visitor.enter?.EnumDef) { - this.visitor.enter.EnumDef(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.EnumDef(mutatedNode)) ?? mutatedNode; } if (this.visitor.leave?.EnumDef) { - this.visitor.leave.EnumDef(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.EnumDef(mutatedNode)) ?? mutatedNode; } + + return mutatedNode; } - AnyType(node: AnyType) { + AnyType(node: AnyType): AnyType { + let mutatedNode = node; + if (this.visitor.enter?.AnyType) { - this.visitor.enter.AnyType(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.AnyType(mutatedNode)) ?? mutatedNode; } - switch (node.kind) { + switch (mutatedNode.kind) { case "Scalar": - this.ScalarType(node); + this.ScalarType(mutatedNode); break; case "Array": - this.ArrayType(node); + this.ArrayType(mutatedNode); break; case "Map": - this.MapType(node); + this.MapType(mutatedNode); break; case "Ref": - this.RefType(node); + this.RefType(mutatedNode); break; case "ImportRef": - this.ImportRefType(node); + this.ImportRefType(mutatedNode); + break; + case "UnlinkedImportRef": + this.UnlinkedImportRefType(mutatedNode); break; } if (this.visitor.leave?.AnyType) { - this.visitor.leave.AnyType(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.AnyType(mutatedNode)) ?? mutatedNode; } + + return mutatedNode; } - ScalarType(node: ScalarType) { + ScalarType(node: ScalarType): ScalarType { + let mutatedNode = node; + if (this.visitor.enter?.ScalarType) { - this.visitor.enter.ScalarType(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.ScalarType(mutatedNode)) ?? mutatedNode; } if (this.visitor.leave?.ScalarType) { - this.visitor.leave.ScalarType(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.ScalarType(mutatedNode)) ?? mutatedNode; } + + return mutatedNode; } - RefType(node: RefType) { + RefType(node: RefType): RefType { + let mutatedNode = node; + if (this.visitor.enter?.RefType) { - this.visitor.enter.RefType(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.RefType(mutatedNode)) ?? mutatedNode; } if (this.visitor.leave?.RefType) { - this.visitor.leave.RefType(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.RefType(mutatedNode)) ?? mutatedNode; } + + return mutatedNode; } - ImportRefType(node: ImportRefType) { + ImportRefType(node: ImportRefType): ImportRefType { + let mutatedNode = node; + if (this.visitor.enter?.ImportRefType) { - this.visitor.enter.ImportRefType(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.ImportRefType(mutatedNode)) ?? mutatedNode; } if (this.visitor.leave?.ImportRefType) { - this.visitor.leave.ImportRefType(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.ImportRefType(mutatedNode)) ?? mutatedNode; } + + return mutatedNode; } - ArrayType(node: ArrayType) { + UnlinkedImportRefType(node: UnlinkedImportRefType): UnlinkedImportRefType { + let mutatedNode = node; + + if (this.visitor.enter?.UnlinkedImportRefType) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.UnlinkedImportRefType(mutatedNode)) ?? mutatedNode; + } + + if (this.visitor.leave?.UnlinkedImportRefType) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.UnlinkedImportRefType(mutatedNode)) ?? mutatedNode; + } + + return mutatedNode; + } + + ArrayType(node: ArrayType): ArrayType { + let mutatedNode = node; + if (this.visitor.enter?.ArrayType) { - this.visitor.enter.ArrayType(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.ArrayType(mutatedNode)) ?? mutatedNode; } - this.AnyType(node.item.type); + + mutatedNode.item.type = this.AnyType(mutatedNode.item.type); if (this.visitor.leave?.ArrayType) { - this.visitor.leave.ArrayType(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.ArrayType(mutatedNode)) ?? mutatedNode; } + + return mutatedNode; } - MapType(node: MapType) { + MapType(node: MapType): MapType { + let mutatedNode = node; + if (this.visitor.enter?.MapType) { - this.visitor.enter.MapType(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.MapType(mutatedNode)) ?? mutatedNode; } - this.AnyType(node.value.type); + + mutatedNode.value.type = this.AnyType(mutatedNode.value.type); if (this.visitor.leave?.MapType) { - this.visitor.leave.MapType(node); + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.MapType(mutatedNode)) ?? mutatedNode; } + + return mutatedNode; } visit(node: Abi) { From fa1db4906480d8514e71fec804fd1a2c2104c7b2 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Fri, 24 Feb 2023 21:49:04 +0100 Subject: [PATCH 62/90] (chore): changed currentIDPath algorithm --- packages/schema/abi/src/AbiTreeShaker.ts | 43 ++++++------------- .../abi/src/CircularDependencyValidator.ts | 41 +++++++----------- 2 files changed, 29 insertions(+), 55 deletions(-) diff --git a/packages/schema/abi/src/AbiTreeShaker.ts b/packages/schema/abi/src/AbiTreeShaker.ts index 5791872536..6f28ee4082 100644 --- a/packages/schema/abi/src/AbiTreeShaker.ts +++ b/packages/schema/abi/src/AbiTreeShaker.ts @@ -1,27 +1,27 @@ -import { Abi, AbiDefs, EnumDef, ImportRefType, ObjectDef, RefType } from "@polywrap/abi-types"; +import { Abi, AbiDefs, EnumDef, ImportRefType, ObjectDef } from "@polywrap/abi-types"; import { AbiVisitor } from "./AbiVisitor"; type ReferenceableDef = ObjectDef | EnumDef; export interface IAbiTreeShaker { - findReferencedDefinition(abi: Abi, ref: RefType): ReferenceableDef | undefined + findReferencedDefinition(abi: Abi, ref: string): ReferenceableDef | undefined shakeTree(abi: Abi, neededDefNames: string[]): Abi shakeImports(abi: Abi): Abi } export class AbiTreeShaker implements IAbiTreeShaker { - findReferencedDefinition(abi: Abi, ref: RefType): ReferenceableDef | undefined { + findReferencedDefinition(abi: Abi, refName: string): ReferenceableDef | undefined { // TODO: implement a stop to the search if the definition is found let found: ReferenceableDef | undefined = undefined; const visitor = new AbiVisitor({ enter: { ObjectDef: (def) => { - if (def.name === ref.ref_name) { + if (def.name === refName) { found = def; } }, EnumDef: (def) => { - if (def.name === ref.ref_name) { + if (def.name === refName) { found = def; } } @@ -111,7 +111,7 @@ export class AbiTreeShaker implements IAbiTreeShaker { } if (containingDefName) { - const referencedDef = this.findReferencedDefinition(abi, ref) + const referencedDef = this.findReferencedDefinition(abi, ref.ref_name) if (!referencedDef) { throw new Error(`Could not find referenced definition ${ref.ref_name} in ${containingDefName}`) @@ -140,35 +140,20 @@ export class AbiTreeShaker implements IAbiTreeShaker { return result; } - private _shakeImports(abi: Abi, neededImports: ImportRefType[], state: { currentDepth: number; lastDepth: number, currentId: string } = { - currentId: "", - currentDepth: 0, - lastDepth: 0, + private _shakeImports(abi: Abi, neededImports: ImportRefType[], state: { currentIdPath: string[] } = { + currentIdPath: [] }): Abi { let abiClone = JSON.parse(JSON.stringify(abi)); const importsVisitor = new AbiVisitor({ enter: { - Imports: () => { - state.currentDepth += 1 - }, Import: (importDef) => { - // TODO: this logic works but could be improved - if (state.currentDepth > state.lastDepth) { - state.currentId = `${state.currentId}.${importDef.id}` - } else if (state.currentDepth < state.lastDepth) { - state.currentId = state.currentId.split(".").slice(0, state.currentDepth - 1).join(".") - state.currentId = `${state.currentId}.${importDef.id}` - } else { - state.currentId = state.currentId.split(".").slice(0, state.currentDepth - 1).join(".") - state.currentId = `${state.currentId}.${importDef.id}` - } - - state.lastDepth = state.currentDepth + state.currentIdPath.push(importDef.id) + const currentId = state.currentIdPath.join(".") const neededFromThisImport = neededImports - .filter((neededImport) => neededImport.import_id === state.currentId) + .filter((neededImport) => neededImport.import_id === currentId) .map((neededImport) => neededImport.ref_name) const neededDefsFromThisImport = this.extractNeededDefinitions({ version: abiClone.version, @@ -197,13 +182,13 @@ export class AbiTreeShaker implements IAbiTreeShaker { version: abiClone.version, ...importDef }, transitiveImports, state) - + } }, leave: { - Imports: () => { - state.currentDepth -= 1 + Import: (importAbi) => { + state.currentIdPath.pop() } } }); diff --git a/packages/schema/abi/src/CircularDependencyValidator.ts b/packages/schema/abi/src/CircularDependencyValidator.ts index a858b18461..ab20c868a7 100644 --- a/packages/schema/abi/src/CircularDependencyValidator.ts +++ b/packages/schema/abi/src/CircularDependencyValidator.ts @@ -7,53 +7,42 @@ export class CircularDependencyValidator { private importsTree: DependencyTree private traverseAbi(abi: Abi) { - const state: { currentDepth: number; lastDepth: number, currentId: string; currentObject?: string } = { - currentId: "", - currentDepth: 0, - lastDepth: 0, - currentObject: undefined, + const state: { currentIdPath: string[]; currentObject?: string } = { + currentIdPath: [], + currentObject: undefined } const importsVisitor = new AbiVisitor({ enter: { - Imports: () => { - state.currentDepth += 1 - }, Import: (importDef) => { - // TODO: this logic works but could be improved - if (state.currentDepth > state.lastDepth) { - state.currentId = `${state.currentId}.${importDef.id}` - } else if (state.currentDepth < state.lastDepth) { - state.currentId = state.currentId.split(".").slice(0, state.currentDepth - 1).join(".") - state.currentId = `${state.currentId}.${importDef.id}` - } else { - state.currentId = state.currentId.split(".").slice(0, state.currentDepth - 1).join(".") - state.currentId = `${state.currentId}.${importDef.id}` - } + state.currentIdPath.push(importDef.id) + const currentId = state.currentIdPath.join(".") - this.importsTree.addNode(state.currentId) - state.lastDepth = state.currentDepth + this.importsTree.addNode(currentId) }, ObjectDef: (objectDef) => { state.currentObject = objectDef.name - this.definitionsTree.addNode(`${state.currentId}.${objectDef.name}`) + const currentId = state.currentIdPath.join(".") + this.definitionsTree.addNode(`${currentId}.${objectDef.name}`) }, RefType: (refType) => { if (refType.ref_kind === "Object") { - this.definitionsTree.addEdge(`${state.currentId}.${state.currentObject}`, `${state.currentId}.${refType.ref_name}`) + const currentId = state.currentIdPath.join(".") + this.definitionsTree.addEdge(`${currentId}.${state.currentObject}`, `${currentId}.${refType.ref_name}`) } }, ImportRefType: (importRefType) => { - this.importsTree.addEdge(state.currentId, `${state.currentId}.${importRefType.import_id}`) + const currentId = state.currentIdPath.join(".") + this.importsTree.addEdge(currentId, `${currentId}.${importRefType.import_id}`) if (importRefType.ref_kind === "Object") { - this.definitionsTree.addEdge(`${state.currentId}.${state.currentObject}`, `${state.currentId}.${importRefType.import_id}.${importRefType.ref_name}`) + this.definitionsTree.addEdge(`${currentId}.${state.currentObject}`, `${currentId}.${importRefType.import_id}.${importRefType.ref_name}`) } } }, leave: { - Imports: () => { - state.currentDepth -= 1 + Import: () => { + state.currentIdPath.pop() }, ObjectDef: () => { state.currentObject = undefined From 598f5a5c7b8d0f2f2c7edc4e02ed786d47bb3e8e Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Fri, 24 Feb 2023 21:49:12 +0100 Subject: [PATCH 63/90] (chore): linking references --- packages/schema/abi/src/AbiImportsLinker.ts | 115 +++++++++++++++++++- 1 file changed, 112 insertions(+), 3 deletions(-) diff --git a/packages/schema/abi/src/AbiImportsLinker.ts b/packages/schema/abi/src/AbiImportsLinker.ts index abc92d2724..0f55da7407 100644 --- a/packages/schema/abi/src/AbiImportsLinker.ts +++ b/packages/schema/abi/src/AbiImportsLinker.ts @@ -1,7 +1,8 @@ import { IAbiMerger } from "./AbiMerger"; import { IAbiTreeShaker } from "./AbiTreeShaker"; -import { Abi, ExternalImportStatement, ExternalSchemaFetcher, ImportedAbi, LocalImportStatement, LocalSchemaFetcher, SchemaParser } from "@polywrap/abi-types" +import { Abi, ExternalImportStatement, ExternalSchemaFetcher, ImportedAbi, ImportRefType, LocalImportStatement, LocalSchemaFetcher, RefType, SchemaParser, UniqueDefKind } from "@polywrap/abi-types" +import { AbiVisitor } from "./AbiVisitor"; export interface IAbiImportsLinker { link: (rootAbi: Abi, importStatements?: { @@ -62,6 +63,113 @@ export class AbiImportsLinker implements IAbiImportsLinker { } } + mapImportsToNamespacePaths(rootAbi: Abi): Map { + const importMap = new Map() + const state: { currentNamespacePath: string[]; currentIdPath: string[] } = { + currentNamespacePath: [], + currentIdPath: [] + } + + const importVisitor = new AbiVisitor({ + enter: { + Import: (importAbi) => { + state.currentNamespacePath.push(importAbi.namespace) + state.currentIdPath.push(importAbi.id) + importMap.set(state.currentNamespacePath.join("_"), { abi: importAbi, absoluteIdPath: state.currentIdPath.join(".") }) + return importAbi + } + }, + leave: { + Import: (importAbi) => { + state.currentNamespacePath.pop() + state.currentIdPath.pop() + return importAbi + }, + } + }) + + importVisitor.visit(rootAbi) + return importMap + } + + getUniqueDefinitionsMap(abi: Abi): Map { + const uniqueDefinitionsMap = new Map() + + const uniqueDefVisitor = new AbiVisitor({ + enter: { + EnumDef: (enumDef) => { + uniqueDefinitionsMap.set(enumDef.name, "Enum") + return enumDef + }, + ObjectDef: (objectDef) => { + uniqueDefinitionsMap.set(objectDef.name, "Object") + return objectDef + }, + FunctionDef: (functionDef) => { + uniqueDefinitionsMap.set(functionDef.name, "Function") + return functionDef + } + } + }) + + uniqueDefVisitor.visit(abi) + return uniqueDefinitionsMap + } + + linkImportReferences(rootAbi: Abi): Abi { + const abiClone = JSON.parse(JSON.stringify(rootAbi)) + const rootAbiUniqueDefsMap = this.getUniqueDefinitionsMap(rootAbi) + const importMap = this.mapImportsToNamespacePaths(rootAbi) + + const linkVisitor = new AbiVisitor({ + enter: { + UnlinkedImportRefType: (refType) => { + const nameSplit = refType.ref_name.split("_"); + + if (nameSplit.length < 2) { + const foundDefinitionKind = rootAbiUniqueDefsMap.get(refType.ref_name) + + if (!foundDefinitionKind) { + throw new Error(`Could not find definition for ${refType.ref_name}`) + } + + // TODO: this is a hack around immutability, we should have a better way to do this + (refType as unknown as RefType).kind = "Ref"; + (refType as unknown as RefType).ref_name = refType.ref_name; + (refType as unknown as RefType).ref_kind = foundDefinitionKind; + } else { + // if Foo_Bar_Baz_SomeDef, then namespace path is ["Baz", "Bar", "Foo"] + const namespacePath = nameSplit.slice(0, -1).reverse() + const refName = nameSplit.slice(-1)[0] + const importAbi = importMap.get(namespacePath.join("_")) + + if (!importAbi) { + throw new Error(`Could not find import for ${refType.ref_name}`) + } + + const foundDefinition = this._abiTreeShaker.findReferencedDefinition({ + objects: importAbi.abi.objects, + enums: importAbi.abi.enums, + version: "0.2" + }, refType.ref_name) + + if (!foundDefinition) { + throw new Error(`Could not find definition for ${refType.ref_name}`) + } + + // TODO: this is a hack around immutability, we should have a better way to do this + (refType as unknown as ImportRefType).kind = "ImportRef"; + (refType as unknown as ImportRefType).ref_name = refName; + (refType as unknown as ImportRefType).import_id = importAbi.absoluteIdPath; + } + } + }, + }) + + linkVisitor.visit(abiClone) + return abiClone + } + async link(rootAbi: Abi, importStatements?: { local?: LocalImportStatement[]; external?: ExternalImportStatement[]; @@ -73,8 +181,9 @@ export class AbiImportsLinker implements IAbiImportsLinker { const externalImportStatements = [...externalImportStatementsFromRoot, ...transitiveExternalImports] const abiWithExtImports = await this.embedExternalImports(abiWithLocalImports, externalImportStatements) - const abiWithShakenExtImports = await this._abiTreeShaker.shakeImports(abiWithExtImports) + const abiWithLinkedRefs = this.linkImportReferences(abiWithExtImports) + const linkedAbiWithShakenExtImports = await this._abiTreeShaker.shakeImports(abiWithLinkedRefs) - return abiWithShakenExtImports + return linkedAbiWithShakenExtImports } } From 20d914e74e092d309ffd6d9cf1739595d23dc9ce Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Sat, 25 Feb 2023 20:57:04 +0100 Subject: [PATCH 64/90] (feat): implemented Linker Visitor to mutate and link UnlinkedRefs --- packages/schema/abi/src/AbiVisitor.ts | 22 +-------- packages/schema/abi/src/LinkerVisitor.ts | 58 ++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 20 deletions(-) create mode 100644 packages/schema/abi/src/LinkerVisitor.ts diff --git a/packages/schema/abi/src/AbiVisitor.ts b/packages/schema/abi/src/AbiVisitor.ts index 57c854fc0f..9cb38a6c7a 100644 --- a/packages/schema/abi/src/AbiVisitor.ts +++ b/packages/schema/abi/src/AbiVisitor.ts @@ -1,4 +1,4 @@ -import { Abi, AnyType, ArgumentDef, ArrayType, EnumDef, FunctionDef, ImportedAbi, ImportRefType, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType, UnlinkedImportRefType } from "@polywrap/abi-types"; +import { Abi, AnyType, ArgumentDef, ArrayType, EnumDef, FunctionDef, ImportedAbi, ImportRefType, MapType, ObjectDef, PropertyDef, RefType, ResultDef, ScalarType } from "@polywrap/abi-types"; type VisitorFunction = (node: T) => T | void; @@ -15,7 +15,6 @@ export interface IAbiVisitor { ScalarType?: VisitorFunction; RefType?: VisitorFunction; ImportRefType?: VisitorFunction; - UnlinkedImportRefType?: (node: UnlinkedImportRefType) => UnlinkedImportRefType; ArrayType?: VisitorFunction; MapType?: VisitorFunction; AnyType?: VisitorFunction; @@ -29,7 +28,7 @@ export interface IAbiVisitorEnterAndLeave { export class AbiVisitor implements IAbiVisitor { constructor(protected readonly visitor: IAbiVisitorEnterAndLeave) { } - private coerceVoidToUndefined(value: T | void): T | undefined { + protected coerceVoidToUndefined(value: T | void): T | undefined { return value === undefined ? undefined : value; } @@ -186,9 +185,6 @@ export class AbiVisitor implements IAbiVisitor { case "ImportRef": this.ImportRefType(mutatedNode); break; - case "UnlinkedImportRef": - this.UnlinkedImportRefType(mutatedNode); - break; } if (this.visitor.leave?.AnyType) { @@ -240,20 +236,6 @@ export class AbiVisitor implements IAbiVisitor { return mutatedNode; } - UnlinkedImportRefType(node: UnlinkedImportRefType): UnlinkedImportRefType { - let mutatedNode = node; - - if (this.visitor.enter?.UnlinkedImportRefType) { - mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.UnlinkedImportRefType(mutatedNode)) ?? mutatedNode; - } - - if (this.visitor.leave?.UnlinkedImportRefType) { - mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.UnlinkedImportRefType(mutatedNode)) ?? mutatedNode; - } - - return mutatedNode; - } - ArrayType(node: ArrayType): ArrayType { let mutatedNode = node; diff --git a/packages/schema/abi/src/LinkerVisitor.ts b/packages/schema/abi/src/LinkerVisitor.ts new file mode 100644 index 0000000000..b412d77bd7 --- /dev/null +++ b/packages/schema/abi/src/LinkerVisitor.ts @@ -0,0 +1,58 @@ +import { AnyType, ImportRefType, RefType, UnlinkedImportRefType } from "@polywrap/abi-types"; +import { AbiVisitor, IAbiVisitor } from "./AbiVisitor"; + +export interface ILinkerVisitorEnterAndLeave { + enter: IAbiVisitor & { UnlinkedImportRefType: (unlinkedRefType: UnlinkedImportRefType) => RefType | ImportRefType }; + leave?: IAbiVisitor & { UnlinkedImportRefType: (linkedRefType: T) => T }; +} + +export class LinkerVisitor extends AbiVisitor { + constructor(protected readonly visitor: ILinkerVisitorEnterAndLeave) { + super(visitor); + } + + AnyType(node: AnyType): AnyType { + let mutatedNode = node; + + if (this.visitor.enter?.AnyType) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.AnyType(mutatedNode)) ?? mutatedNode; + } + + switch (mutatedNode.kind) { + case "Scalar": + this.ScalarType(mutatedNode); + break; + case "Array": + this.ArrayType(mutatedNode); + break; + case "Map": + this.MapType(mutatedNode); + break; + case "Ref": + this.RefType(mutatedNode); + break; + case "ImportRef": + this.ImportRefType(mutatedNode); + break; + case "UnlinkedImportRef": + this.UnlinkedImportRefType(mutatedNode); + break; + } + + if (this.visitor.leave?.AnyType) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.AnyType(mutatedNode)) ?? mutatedNode; + } + + return mutatedNode; + } + + UnlinkedImportRefType(node: UnlinkedImportRefType): RefType | ImportRefType { + let linkedRef = this.visitor.enter.UnlinkedImportRefType(node); + + if (this.visitor.leave?.UnlinkedImportRefType) { + linkedRef = this.visitor.leave.UnlinkedImportRefType(linkedRef); + } + + return linkedRef; + } +} \ No newline at end of file From 5c8e8a126fa6d263bf243e4a633c63df93f525be Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Sat, 25 Feb 2023 20:57:26 +0100 Subject: [PATCH 65/90] (chore): linking unlinked refs using visitor --- packages/schema/abi/src/AbiImportsLinker.ts | 24 ++++++++++++--------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/schema/abi/src/AbiImportsLinker.ts b/packages/schema/abi/src/AbiImportsLinker.ts index 0f55da7407..d92c890696 100644 --- a/packages/schema/abi/src/AbiImportsLinker.ts +++ b/packages/schema/abi/src/AbiImportsLinker.ts @@ -1,8 +1,9 @@ import { IAbiMerger } from "./AbiMerger"; import { IAbiTreeShaker } from "./AbiTreeShaker"; -import { Abi, ExternalImportStatement, ExternalSchemaFetcher, ImportedAbi, ImportRefType, LocalImportStatement, LocalSchemaFetcher, RefType, SchemaParser, UniqueDefKind } from "@polywrap/abi-types" +import { Abi, ExternalImportStatement, ExternalSchemaFetcher, ImportedAbi, LocalImportStatement, LocalSchemaFetcher, SchemaParser, UniqueDefKind } from "@polywrap/abi-types" import { AbiVisitor } from "./AbiVisitor"; +import { LinkerVisitor } from "./UnlinkedRefVisitor"; export interface IAbiImportsLinker { link: (rootAbi: Abi, importStatements?: { @@ -121,7 +122,7 @@ export class AbiImportsLinker implements IAbiImportsLinker { const rootAbiUniqueDefsMap = this.getUniqueDefinitionsMap(rootAbi) const importMap = this.mapImportsToNamespacePaths(rootAbi) - const linkVisitor = new AbiVisitor({ + const linkVisitor = new LinkerVisitor({ enter: { UnlinkedImportRefType: (refType) => { const nameSplit = refType.ref_name.split("_"); @@ -133,10 +134,11 @@ export class AbiImportsLinker implements IAbiImportsLinker { throw new Error(`Could not find definition for ${refType.ref_name}`) } - // TODO: this is a hack around immutability, we should have a better way to do this - (refType as unknown as RefType).kind = "Ref"; - (refType as unknown as RefType).ref_name = refType.ref_name; - (refType as unknown as RefType).ref_kind = foundDefinitionKind; + return { + kind: "Ref", + ref_name: refType.ref_name, + ref_kind: foundDefinitionKind + } } else { // if Foo_Bar_Baz_SomeDef, then namespace path is ["Baz", "Bar", "Foo"] const namespacePath = nameSplit.slice(0, -1).reverse() @@ -157,10 +159,12 @@ export class AbiImportsLinker implements IAbiImportsLinker { throw new Error(`Could not find definition for ${refType.ref_name}`) } - // TODO: this is a hack around immutability, we should have a better way to do this - (refType as unknown as ImportRefType).kind = "ImportRef"; - (refType as unknown as ImportRefType).ref_name = refName; - (refType as unknown as ImportRefType).import_id = importAbi.absoluteIdPath; + return { + kind: "ImportRef", + ref_name: refName, + ref_kind: foundDefinition.kind, + import_id: importAbi.absoluteIdPath + } } } }, From 680e0e367f8667c779aedf86f5fc3a3048a97b42 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Sat, 25 Feb 2023 22:42:23 +0100 Subject: [PATCH 66/90] (wip): ABI Imports Linker tests added --- packages/schema/abi/src/AbiImportsLinker.ts | 2 +- packages/schema/abi/src/AbiMerger.ts | 22 +- .../src/__tests__/AbiImportsLinker.spec.ts | 272 ++++++++++++++++++ 3 files changed, 293 insertions(+), 3 deletions(-) create mode 100644 packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts diff --git a/packages/schema/abi/src/AbiImportsLinker.ts b/packages/schema/abi/src/AbiImportsLinker.ts index d92c890696..0654bd9ad1 100644 --- a/packages/schema/abi/src/AbiImportsLinker.ts +++ b/packages/schema/abi/src/AbiImportsLinker.ts @@ -3,7 +3,7 @@ import { IAbiTreeShaker } from "./AbiTreeShaker"; import { Abi, ExternalImportStatement, ExternalSchemaFetcher, ImportedAbi, LocalImportStatement, LocalSchemaFetcher, SchemaParser, UniqueDefKind } from "@polywrap/abi-types" import { AbiVisitor } from "./AbiVisitor"; -import { LinkerVisitor } from "./UnlinkedRefVisitor"; +import { LinkerVisitor } from "./LinkerVisitor"; export interface IAbiImportsLinker { link: (rootAbi: Abi, importStatements?: { diff --git a/packages/schema/abi/src/AbiMerger.ts b/packages/schema/abi/src/AbiMerger.ts index a8bc437e7b..c70e9b2f2d 100644 --- a/packages/schema/abi/src/AbiMerger.ts +++ b/packages/schema/abi/src/AbiMerger.ts @@ -1,4 +1,4 @@ -import { Abi } from "@polywrap/abi-types"; +import { Abi, ImportedAbi } from "@polywrap/abi-types"; export interface IAbiMerger { merge(abis: Abi[]): Abi @@ -6,13 +6,31 @@ export interface IAbiMerger { export class AbiMerger implements IAbiMerger { merge(abis: Abi[]): Abi { - return { + return this._removeEmptyArrays({ // TODO: handle different versions? version: "0.2", objects: abis.reduce((acc, abi) => [...acc, ...(abi.objects ?? [])], []), enums: abis.reduce((acc, abi) => [...acc, ...(abi.enums ?? [])], []), functions: abis.reduce((acc, abi) => [...acc, ...(abi.functions ?? [])], []), imports: abis.reduce((acc, abi) => [...acc, ...(abi.imports ?? [])], []) + }) as Abi + } + + private _removeEmptyArrays(abi: Abi | ImportedAbi): Abi | ImportedAbi { + const processed = { + objects: abi.objects?.length ? abi.objects : undefined, + enums: abi.enums?.length ? abi.enums : undefined, + functions: abi.functions?.length ? abi.functions : undefined, + imports: abi.imports?.length ? abi.imports.map(this._removeEmptyArrays) as ImportedAbi[] : undefined } + + if ((abi as Abi).version) { + return { + version: (abi as Abi).version, + ...processed + } + } + + return processed as ImportedAbi } } \ No newline at end of file diff --git a/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts new file mode 100644 index 0000000000..6f3fec634d --- /dev/null +++ b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts @@ -0,0 +1,272 @@ +import { Abi, LocalImportStatement, SchemaParser } from "@polywrap/abi-types" +import { AbiImportsLinker } from "../AbiImportsLinker" +import { AbiMerger } from "../AbiMerger" +import { AbiTreeShaker } from "../AbiTreeShaker" +import { mockFetchers, mockSchemaParser } from "./mocks" + +describe("AbiImportsLinker", () => { + it("Should extract unique definitions", () => { + const abi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "Some", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "OutBar", + } + } + ] + }, + { + kind: "Object", + name: "Some2", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "OutBar", + } + } + ] + } + ], + enums: [ + { + kind: "Enum", + name: "Foo", + constants: ["ONE", "TWO"] + } + ], + } + + const linker = new AbiImportsLinker(mockSchemaParser(), mockFetchers(), new AbiMerger(), new AbiTreeShaker()) + const uniqueDefinitions = linker.getUniqueDefinitionsMap(abi) + const expectedUniqueDefinitions = new Map([ + ["Some", "Object"], + ["Some2", "Object"], + ["Foo", "Enum"] + ]) + + expect(uniqueDefinitions).toEqual(expectedUniqueDefinitions) + }) + + it("Should map imports to namespace paths", async () => { + const abi: Abi = { + version: "0.2", + imports: [ + { + id: "0", + uri: "foo.eth", + type: "wasm", + namespace: "FOO", + imports: [ + { + id: "1", + uri: "bar.eth", + type: "wasm", + namespace: "BAR", + imports: [ + { + id: "2", + uri: "baz.eth", + type: "wasm", + namespace: "BAZ", + } + ], + } + ], + } + ] + } + + const fooImport = { + id: "0", + uri: "foo.eth", + type: "wasm", + namespace: "FOO", + imports: [ + { + id: "1", + uri: "bar.eth", + type: "wasm", + namespace: "BAR", + imports: [ + { + id: "2", + uri: "baz.eth", + type: "wasm", + namespace: "BAZ", + } + ], + } + ], + }; + + const barImport = { + id: "1", + uri: "bar.eth", + type: "wasm", + namespace: "BAR", + imports: [ + { + id: "2", + uri: "baz.eth", + type: "wasm", + namespace: "BAZ", + } + ], + }; + + const bazImport = { + id: "2", + uri: "baz.eth", + type: "wasm", + namespace: "BAZ", + } + + const expectedMapping = new Map([ + ["FOO", { abi: fooImport, absoluteIdPath: "0" }], + ["FOO_BAR", { abi: barImport, absoluteIdPath: "0.1" }], + ["FOO_BAR_BAZ", { abi: bazImport, absoluteIdPath: "0.1.2" }] + ]) + + const linker = new AbiImportsLinker(mockSchemaParser(), mockFetchers(), new AbiMerger(), new AbiTreeShaker()) + const mapping = linker.mapImportsToNamespacePaths(abi); + + expect(mapping).toEqual(expectedMapping) + }) + + it("Should merge local imports", async () => { + const abi: Abi = { + version: "0.2", + } + + const localAbi1: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "Some", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "Scalar", + scalar: "String", + } + } + ] + }, + { + kind: "Object", + name: "WillBeShakenObj", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "Scalar", + scalar: "String", + } + } + ] + } + ] + } + + const localAbi2: Abi = { + version: "0.2", + enums: [ + { + kind: "Enum", + name: "Foo", + constants: ["ONE", "TWO"] + }, + { + kind: "Enum", + name: "WillBeShakenEnum", + constants: ["THREE", "FOUR"] + } + ] + } + + const expectedAbi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "Some", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "Scalar", + scalar: "String", + } + } + ] + } + ], + enums: [ + { + kind: "Enum", + name: "Foo", + constants: ["ONE", "TWO"] + } + ] + } + + const localImportStatements: LocalImportStatement[] = [ + { + kind: "local", + uriOrPath: "local1", + importedTypes: ["Some"] + }, + { + kind: "local", + uriOrPath: "local2", + importedTypes: ["Foo"] + }, + ] + + const fetchers = { + external: { + fetch: jest.fn(), + }, + local: { + fetch: (uriOrPath: string) => Promise.resolve(uriOrPath) + } + } + + const parser: SchemaParser = { + parse: (uriOrPath: string) => { + if (uriOrPath === "local1") { + return Promise.resolve(localAbi1) + } else { + return Promise.resolve(localAbi2) + } + }, + parseLocalImportStatements: (_: string) => Promise.resolve([]), + parseExternalImportStatements: (_: string) => Promise.resolve([]), + } + const linker = new AbiImportsLinker(parser, fetchers, new AbiMerger(), new AbiTreeShaker()) + const mergedAbi = await linker.mergeLocalImports(abi, localImportStatements) + + expect(mergedAbi.abi).toEqual(expectedAbi) + }) +}) \ No newline at end of file From a9d750f774f00d1bef12880cde5f15896c1336b5 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Sun, 26 Feb 2023 00:26:32 +0100 Subject: [PATCH 67/90] (chore): added local imports merging tests and mocks --- .../src/__tests__/AbiImportsLinker.spec.ts | 254 ++++++++++++++++++ packages/schema/abi/src/__tests__/mocks.ts | 34 +++ 2 files changed, 288 insertions(+) create mode 100644 packages/schema/abi/src/__tests__/mocks.ts diff --git a/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts index 6f3fec634d..60f10c3e48 100644 --- a/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts +++ b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts @@ -269,4 +269,258 @@ describe("AbiImportsLinker", () => { expect(mergedAbi.abi).toEqual(expectedAbi) }) + + it("Should merge local transitive imports", async () => { + const abi: Abi = { + version: "0.2", + } + + const localAbi1: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "Some", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "Scalar", + scalar: "String", + } + } + ] + }, + { + kind: "Object", + name: "WillBeShakenObj", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "Scalar", + scalar: "String", + } + } + ] + } + ] + } + + const localAbi2: Abi = { + version: "0.2", + enums: [ + { + kind: "Enum", + name: "Foo", + constants: ["ONE", "TWO"] + }, + { + kind: "Enum", + name: "WillBeShakenEnum", + constants: ["THREE", "FOUR"] + } + ] + } + + const expectedAbi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "Some", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "Scalar", + scalar: "String", + } + } + ] + } + ], + enums: [ + { + kind: "Enum", + name: "Foo", + constants: ["ONE", "TWO"] + } + ] + } + + const localImportStatements: LocalImportStatement[] = [ + { + kind: "local", + uriOrPath: "local1", + importedTypes: ["Some"] + } + ] + + const fetchers = { + external: { + fetch: jest.fn(), + }, + local: { + fetch: (uriOrPath: string) => Promise.resolve(uriOrPath) + } + } + + const parser: SchemaParser = { + parse: (uriOrPath: string) => { + if (uriOrPath === "local1") { + return Promise.resolve(localAbi1) + } else { + return Promise.resolve(localAbi2) + } + }, + parseLocalImportStatements: (uriOrPath: string) => { + if (uriOrPath === "local1") { + return Promise.resolve([ + { + kind: "local", + uriOrPath: "local2", + importedTypes: ["Foo"] + } + ]) + } + return Promise.resolve([]) + }, + parseExternalImportStatements: (_: string) => Promise.resolve([]), + } + const linker = new AbiImportsLinker(parser, fetchers, new AbiMerger(), new AbiTreeShaker()) + const mergedAbi = await linker.mergeLocalImports(abi, localImportStatements) + + expect(mergedAbi.abi).toEqual(expectedAbi) + }) + + it("Should embed external imports", async () => { + const abi: Abi = { + version: "0.2", + } + + const localAbi1: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "Some", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "Scalar", + scalar: "String", + } + } + ] + }, + { + kind: "Object", + name: "WillBeShakenObj", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "Scalar", + scalar: "String", + } + } + ] + } + ] + } + + const localAbi2: Abi = { + version: "0.2", + enums: [ + { + kind: "Enum", + name: "Foo", + constants: ["ONE", "TWO"] + }, + { + kind: "Enum", + name: "WillBeShakenEnum", + constants: ["THREE", "FOUR"] + } + ] + } + + const expectedAbi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "Some", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "Scalar", + scalar: "String", + } + } + ] + } + ], + enums: [ + { + kind: "Enum", + name: "Foo", + constants: ["ONE", "TWO"] + } + ] + } + + const localImportStatements: LocalImportStatement[] = [ + { + kind: "local", + uriOrPath: "local1", + importedTypes: ["Some"] + }, + { + kind: "local", + uriOrPath: "local2", + importedTypes: ["Foo"] + }, + ] + + const fetchers = { + external: { + fetch: jest.fn(), + }, + local: { + fetch: (uriOrPath: string) => Promise.resolve(uriOrPath) + } + } + + const parser: SchemaParser = { + parse: (uriOrPath: string) => { + if (uriOrPath === "local1") { + return Promise.resolve(localAbi1) + } else { + return Promise.resolve(localAbi2) + } + }, + parseLocalImportStatements: (_: string) => Promise.resolve([]), + parseExternalImportStatements: (_: string) => Promise.resolve([]), + } + const linker = new AbiImportsLinker(parser, fetchers, new AbiMerger(), new AbiTreeShaker()) + const mergedAbi = await linker.mergeLocalImports(abi, localImportStatements) + + expect(mergedAbi.abi).toEqual(expectedAbi) + }) }) \ No newline at end of file diff --git a/packages/schema/abi/src/__tests__/mocks.ts b/packages/schema/abi/src/__tests__/mocks.ts new file mode 100644 index 0000000000..819e9a36b0 --- /dev/null +++ b/packages/schema/abi/src/__tests__/mocks.ts @@ -0,0 +1,34 @@ +import { SchemaParser, Abi, ExternalImportStatement, LocalImportStatement } from "@polywrap/abi-types" + +export const mockSchemaParser = (): SchemaParser => { + return { + parse: async (schema: string): Promise => { + return { + version: "0.2", + } + }, + parseExternalImportStatements: async (schema: string): Promise => { + return [] + }, + parseLocalImportStatements: async (schema: string): Promise => { + return [] + } + } +} + +export const mockFetchers = () => { + return { + external: { + fetch: async (url: string): Promise => { + return { + version: "0.2", + } + } + }, + local: { + fetch: async (path: string): Promise => { + return "" + } + }, + } +} \ No newline at end of file From 45376aebdd1ce1771b1fbb132971693c8d6dfd4c Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Sun, 26 Feb 2023 00:45:19 +0100 Subject: [PATCH 68/90] (chore): added external imports embedding tests --- .../src/__tests__/AbiImportsLinker.spec.ts | 94 ++++++++++--------- 1 file changed, 49 insertions(+), 45 deletions(-) diff --git a/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts index 60f10c3e48..aa430b0018 100644 --- a/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts +++ b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts @@ -1,4 +1,4 @@ -import { Abi, LocalImportStatement, SchemaParser } from "@polywrap/abi-types" +import { Abi, ExternalImportStatement, LocalImportStatement, SchemaParser } from "@polywrap/abi-types" import { AbiImportsLinker } from "../AbiImportsLinker" import { AbiMerger } from "../AbiMerger" import { AbiTreeShaker } from "../AbiTreeShaker" @@ -405,8 +405,23 @@ describe("AbiImportsLinker", () => { version: "0.2", } - const localAbi1: Abi = { + const extAbi1: Abi = { version: "0.2", + imports: [ + { + id: "0", + type: "wasm", + uri: "ext1", + namespace: "BAR", + enums: [ + { + kind: "Enum", + name: "Bar", + constants: ["ONE", "TWO"] + } + ] + } + ], objects: [ { kind: "Object", @@ -441,7 +456,7 @@ describe("AbiImportsLinker", () => { ] } - const localAbi2: Abi = { + const extAbi2: Abi = { version: "0.2", enums: [ { @@ -459,68 +474,57 @@ describe("AbiImportsLinker", () => { const expectedAbi: Abi = { version: "0.2", - objects: [ + imports: [ { - kind: "Object", - name: "Some", - props: [ - { - kind: "Property", - name: "propSome", - required: true, - type: { - kind: "Scalar", - scalar: "String", - } - } - ] - } - ], - enums: [ + id: "EXT1", + type: "wasm", + uri: "ext1", + namespace: "EXT1", + ...extAbi1 + }, { - kind: "Enum", - name: "Foo", - constants: ["ONE", "TWO"] - } + id: "EXT2", + type: "wasm", + uri: "ext2", + namespace: "EXT2", + ...extAbi2 + }, ] } - const localImportStatements: LocalImportStatement[] = [ + const externalImportStatements: ExternalImportStatement[] = [ { - kind: "local", - uriOrPath: "local1", + kind: "external", + uriOrPath: "ext1", + namespace: "EXT1", importedTypes: ["Some"] }, { - kind: "local", - uriOrPath: "local2", + kind: "external", + uriOrPath: "ext2", + namespace: "EXT2", importedTypes: ["Foo"] }, ] const fetchers = { external: { - fetch: jest.fn(), + fetch: (uriOrPath: string) => { + if (uriOrPath === "ext1") { + return Promise.resolve(extAbi1) + } else { + return Promise.resolve(extAbi2) + } + }, }, local: { - fetch: (uriOrPath: string) => Promise.resolve(uriOrPath) + fetch: jest.fn() } } - const parser: SchemaParser = { - parse: (uriOrPath: string) => { - if (uriOrPath === "local1") { - return Promise.resolve(localAbi1) - } else { - return Promise.resolve(localAbi2) - } - }, - parseLocalImportStatements: (_: string) => Promise.resolve([]), - parseExternalImportStatements: (_: string) => Promise.resolve([]), - } - const linker = new AbiImportsLinker(parser, fetchers, new AbiMerger(), new AbiTreeShaker()) - const mergedAbi = await linker.mergeLocalImports(abi, localImportStatements) + const linker = new AbiImportsLinker(mockSchemaParser(), fetchers, new AbiMerger(), new AbiTreeShaker()) + const abiWithExports = await linker.embedExternalImports(abi, externalImportStatements) - expect(mergedAbi.abi).toEqual(expectedAbi) + expect(abiWithExports).toEqual(expectedAbi) }) }) \ No newline at end of file From 89c7b17116304b0e828a640539a248aa2483d4bc Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Sun, 26 Feb 2023 23:47:29 +0100 Subject: [PATCH 69/90] (feat): reference linking tests --- .../src/__tests__/AbiImportsLinker.spec.ts | 603 ++++++++++++++++++ 1 file changed, 603 insertions(+) diff --git a/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts index aa430b0018..e3b8b7c084 100644 --- a/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts +++ b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts @@ -527,4 +527,607 @@ describe("AbiImportsLinker", () => { expect(abiWithExports).toEqual(expectedAbi) }) + + it("Link unlinked local and external import references", async () => { + const abi: Abi = { + version: "0.2", + imports: [ + { + id: "0", + type: "wasm", + uri: "ext", + namespace: "EXT", + enums: [ + { + kind: "Enum", + name: "Foo", + constants: ["ONE", "TWO"] + } + ] + } + ], + enums: [ + { + kind: "Enum", + name: "Bar", + constants: ["ONE", "TWO"] + } + ], + objects: [ + { + kind: "Object", + name: "Some", + props: [ + { + kind: "Property", + name: "extProp", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "EXT_Foo", + } + }, + { + kind: "Property", + name: "extBar", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "Bar", + } + } + ] + } + ] + } + + const expectedAbi: Abi = { + version: "0.2", + imports: [ + { + id: "0", + type: "wasm", + uri: "ext", + namespace: "EXT", + enums: [ + { + kind: "Enum", + name: "Foo", + constants: ["ONE", "TWO"] + } + ] + } + ], + enums: [ + { + kind: "Enum", + name: "Bar", + constants: ["ONE", "TWO"] + } + ], + objects: [ + { + kind: "Object", + name: "Some", + props: [ + { + kind: "Property", + name: "extProp", + required: true, + type: { + kind: "ImportRef", + ref_name: "Foo", + ref_kind: "Enum", + import_id: "0", + } + }, + { + kind: "Property", + name: "extBar", + required: true, + type: { + kind: "Ref", + ref_name: "Bar", + ref_kind: "Enum", + } + } + ] + } + ] + } + + const linker = new AbiImportsLinker(mockSchemaParser(), mockFetchers(), new AbiMerger(), new AbiTreeShaker()) + const linkedAbi = await linker.linkImportReferences(abi) + + expect(linkedAbi).toEqual(expectedAbi) + }) + + it("Link external nested/transitive import references", async () => { + const abi: Abi = { + version: "0.2", + imports: [ + { + id: "0", + type: "wasm", + uri: "ext1", + namespace: "EXT1", + enums: [ + { + kind: "Enum", + name: "Foo", + constants: ["ONE", "TWO"] + } + ], + imports: [ + { + id: "1", + type: "wasm", + uri: "ext2", + namespace: "EXT2", + enums: [ + { + kind: "Enum", + name: "Bar", + constants: ["ONE", "TWO"] + } + ], + imports: [ + { + id: "2", + type: "wasm", + uri: "ext3", + namespace: "EXT3", + enums: [ + { + kind: "Enum", + name: "Baz", + constants: ["ONE", "TWO"] + } + ], + } + ] + } + ] + } + ], + objects: [ + { + kind: "Object", + name: "Some", + props: [ + { + kind: "Property", + name: "extProp", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "EXT1_Foo", + } + }, + { + kind: "Property", + name: "extProp2", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "EXT1_EXT2_Bar", + } + }, + { + kind: "Property", + name: "extProp3", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "EXT1_EXT2_EXT3_Baz", + } + }, + ] + } + ] + } + + const expectedAbi: Abi = { + version: "0.2", + imports: [ + { + id: "0", + type: "wasm", + uri: "ext1", + namespace: "EXT1", + enums: [ + { + kind: "Enum", + name: "Foo", + constants: ["ONE", "TWO"] + } + ], + imports: [ + { + id: "1", + type: "wasm", + uri: "ext2", + namespace: "EXT2", + enums: [ + { + kind: "Enum", + name: "Bar", + constants: ["ONE", "TWO"] + } + ], + imports: [ + { + id: "2", + type: "wasm", + uri: "ext3", + namespace: "EXT3", + enums: [ + { + kind: "Enum", + name: "Baz", + constants: ["ONE", "TWO"] + } + ], + } + ] + } + ] + } + ], + objects: [ + { + kind: "Object", + name: "Some", + props: [ + { + kind: "Property", + name: "extProp", + required: true, + type: { + kind: "ImportRef", + ref_name: "Foo", + ref_kind: "Enum", + import_id: "0", + } + }, + { + kind: "Property", + name: "extProp2", + required: true, + type: { + kind: "ImportRef", + ref_name: "Bar", + ref_kind: "Enum", + import_id: "0.1", + } + }, + { + kind: "Property", + name: "extProp3", + required: true, + type: { + kind: "ImportRef", + ref_name: "Baz", + ref_kind: "Enum", + import_id: "0.1.2", + } + }, + ] + } + ] + } + + const linker = new AbiImportsLinker(mockSchemaParser(), mockFetchers(), new AbiMerger(), new AbiTreeShaker()) + const linkedAbi = await linker.linkImportReferences(abi) + + expect(linkedAbi).toEqual(expectedAbi) + }) + + it("Link ABI", async () => { + const extAbi: Abi = { + version: "0.2", + imports: [ + { + id: "0", + type: "wasm", + uri: "ext2", + namespace: "BAR", + enums: [ + { + kind: "Enum", + name: "Bar", + constants: ["ONE", "TWO"] + } + ] + } + ], + objects: [ + { + kind: "Object", + name: "Foo", + props: [{ + kind: "Property", + name: "transitiveProp", + required: true, + type: { + kind: "ImportRef", + ref_name: "Bar", + ref_kind: "Enum", + import_id: "0" + } + }] + } + ] + } + + const transitiveLocalAbi: Abi = { + version: "0.2", + enums: [ + { + kind: "Enum", + name: "TransitiveFoo", + constants: ["ONE", "TWO"] + } + ] + } + + const localAbi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "Some", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "Ref", + ref_kind: "Enum", + ref_name: "LocalFoo", + } + }, + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "TransitiveFoo", + } + } + ] + } + ], + enums: [ + { + kind: "Enum", + name: "LocalFoo", + constants: ["ONE", "TWO"] + } + ] + } + + const rootAbi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "RefObj", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "EXT1_Foo", + } + }, + { + kind: "Property", + name: "propSome2", + required: true, + type: { + kind: "UnlinkedImportRef", + ref_name: "Some", + } + } + ] + } + ] + } + + const expectedAbi: Abi = { + version: "0.2", + imports: [ + { + id: "EXT1", + type: "wasm", + uri: "ext1", + namespace: "EXT1", + imports: [ + { + id: "0", + type: "wasm", + uri: "ext2", + namespace: "BAR", + enums: [ + { + kind: "Enum", + name: "Bar", + constants: ["ONE", "TWO"] + } + ] + } + ], + objects: [ + { + kind: "Object", + name: "Foo", + props: [{ + kind: "Property", + name: "transitiveProp", + required: true, + type: { + kind: "ImportRef", + ref_name: "Bar", + ref_kind: "Enum", + import_id: "1" + } + }] + } + ] + } + ], + objects: [ + { + kind: "Object", + name: "RefObj", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "ImportRef", + ref_kind: "Object", + ref_name: "Foo", + import_id: "EXT", + } + }, + { + kind: "Property", + name: "propSome2", + required: true, + type: { + kind: "Ref", + ref_kind: "Object", + ref_name: "Some", + } + } + ] + }, + { + kind: "Object", + name: "Some", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "Ref", + ref_kind: "Enum", + ref_name: "LocalFoo", + } + }, + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "Ref", + ref_kind: "Object", + ref_name: "TransitiveFoo", + } + } + ] + } + ], + enums: [ + { + kind: "Enum", + name: "LocalFoo", + constants: ["ONE", "TWO"] + }, + { + kind: "Enum", + name: "TransitiveFoo", + constants: ["ONE", "TWO"] + } + ] + } + + const parser: SchemaParser = { + parse: async (schemaUri: string): Promise => { + switch (schemaUri) { + case "local": + return Promise.resolve(localAbi) + case "transitiveLocal": + return Promise.resolve(transitiveLocalAbi) + } + + throw new Error("Unknown schema") + }, + parseExternalImportStatements: async (schemaUri: string): Promise => { + if (schemaUri === "ext1") { + return Promise.resolve([ + { + kind: "external", + namespace: "EXT1", + importedTypes: ["Foo"], + uriOrPath: "ext1", + } + ]) + } + + return Promise.resolve([]) + }, + parseLocalImportStatements: async (schemaUri: string): Promise => { + console.log("SCHEMA URI ", schemaUri) + if (schemaUri === "local") { + return Promise.resolve([ + { + kind: "local", + importedTypes: ["TransitiveFoo"], + uriOrPath: "transitiveLocal", + } + ]) + } + return Promise.resolve([]) + } + } + + const fetchers = { + external: { + fetch: async (uri: string): Promise => { + return extAbi + } + }, + local: { + fetch: async (path: string): Promise => { + return path + } + } + } + + const linker = new AbiImportsLinker(parser, fetchers, new AbiMerger(), new AbiTreeShaker()) + + const linkedAbi = await linker.link(rootAbi, { + local: [ + { + kind: "local", + importedTypes: ["Some"], + uriOrPath: "local", + }, + { + kind: "local", + importedTypes: ["TransitiveFoo"], + uriOrPath: "transitiveLocal", + } + ], + external: [ + { + kind: "external", + namespace: "EXT1", + importedTypes: ["Foo"], + uriOrPath: "ext1", + } + ] + }) + + expect(linkedAbi).toEqual(expectedAbi) + }) }) \ No newline at end of file From 01eb66ad2bca5f294dc74095939c0f4f6c767083 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Sun, 26 Feb 2023 23:47:53 +0100 Subject: [PATCH 70/90] (fix): Linker visitor mutation fix --- packages/schema/abi/src/AbiImportsLinker.ts | 16 +++++++--------- packages/schema/abi/src/LinkerVisitor.ts | 12 ++++++------ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/schema/abi/src/AbiImportsLinker.ts b/packages/schema/abi/src/AbiImportsLinker.ts index 0654bd9ad1..9ae1341fc2 100644 --- a/packages/schema/abi/src/AbiImportsLinker.ts +++ b/packages/schema/abi/src/AbiImportsLinker.ts @@ -131,7 +131,7 @@ export class AbiImportsLinker implements IAbiImportsLinker { const foundDefinitionKind = rootAbiUniqueDefsMap.get(refType.ref_name) if (!foundDefinitionKind) { - throw new Error(`Could not find definition for ${refType.ref_name}`) + throw new Error(`Could not find local definition for ${refType.ref_name}`) } return { @@ -140,8 +140,8 @@ export class AbiImportsLinker implements IAbiImportsLinker { ref_kind: foundDefinitionKind } } else { - // if Foo_Bar_Baz_SomeDef, then namespace path is ["Baz", "Bar", "Foo"] - const namespacePath = nameSplit.slice(0, -1).reverse() + // if Foo_Bar_Baz_SomeDef, then namespace path is ["Foo", "Bar", "Baz"] + const namespacePath = nameSplit.slice(0, -1) const refName = nameSplit.slice(-1)[0] const importAbi = importMap.get(namespacePath.join("_")) @@ -150,13 +150,12 @@ export class AbiImportsLinker implements IAbiImportsLinker { } const foundDefinition = this._abiTreeShaker.findReferencedDefinition({ - objects: importAbi.abi.objects, - enums: importAbi.abi.enums, - version: "0.2" - }, refType.ref_name) + version: "0.2", + ...importAbi.abi + }, refName) if (!foundDefinition) { - throw new Error(`Could not find definition for ${refType.ref_name}`) + throw new Error(`Could not find imported definition for ${refType.ref_name}`) } return { @@ -180,7 +179,6 @@ export class AbiImportsLinker implements IAbiImportsLinker { }): Promise { const localImportStatementsFromRoot = importStatements?.local || [] const externalImportStatementsFromRoot = importStatements?.external || [] - const { abi: abiWithLocalImports, transitiveExternalImports } = await this.mergeLocalImports(rootAbi, localImportStatementsFromRoot) const externalImportStatements = [...externalImportStatementsFromRoot, ...transitiveExternalImports] diff --git a/packages/schema/abi/src/LinkerVisitor.ts b/packages/schema/abi/src/LinkerVisitor.ts index b412d77bd7..fab6d12abc 100644 --- a/packages/schema/abi/src/LinkerVisitor.ts +++ b/packages/schema/abi/src/LinkerVisitor.ts @@ -20,22 +20,22 @@ export class LinkerVisitor extends AbiVisitor { switch (mutatedNode.kind) { case "Scalar": - this.ScalarType(mutatedNode); + mutatedNode = this.ScalarType(mutatedNode); break; case "Array": - this.ArrayType(mutatedNode); + mutatedNode = this.ArrayType(mutatedNode); break; case "Map": - this.MapType(mutatedNode); + mutatedNode = this.MapType(mutatedNode); break; case "Ref": - this.RefType(mutatedNode); + mutatedNode = this.RefType(mutatedNode); break; case "ImportRef": - this.ImportRefType(mutatedNode); + mutatedNode = this.ImportRefType(mutatedNode); break; case "UnlinkedImportRef": - this.UnlinkedImportRefType(mutatedNode); + mutatedNode = this.UnlinkedImportRefType(mutatedNode); break; } From bac928ab217001ec9d929c37bca620f5f541e284 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 27 Feb 2023 16:55:11 +0100 Subject: [PATCH 71/90] (fix): fixed link imports test --- packages/schema/abi/src/AbiImportsLinker.ts | 4 ++-- .../schema/abi/src/__tests__/AbiImportsLinker.spec.ts | 11 +++-------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/schema/abi/src/AbiImportsLinker.ts b/packages/schema/abi/src/AbiImportsLinker.ts index 9ae1341fc2..81e63b05b6 100644 --- a/packages/schema/abi/src/AbiImportsLinker.ts +++ b/packages/schema/abi/src/AbiImportsLinker.ts @@ -184,8 +184,8 @@ export class AbiImportsLinker implements IAbiImportsLinker { const externalImportStatements = [...externalImportStatementsFromRoot, ...transitiveExternalImports] const abiWithExtImports = await this.embedExternalImports(abiWithLocalImports, externalImportStatements) const abiWithLinkedRefs = this.linkImportReferences(abiWithExtImports) - const linkedAbiWithShakenExtImports = await this._abiTreeShaker.shakeImports(abiWithLinkedRefs) + // const linkedAbiWithShakenExtImports = await this._abiTreeShaker.shakeImports(abiWithLinkedRefs) - return linkedAbiWithShakenExtImports + return abiWithLinkedRefs } } diff --git a/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts index e3b8b7c084..8c542db34c 100644 --- a/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts +++ b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts @@ -974,7 +974,7 @@ describe("AbiImportsLinker", () => { kind: "ImportRef", ref_name: "Bar", ref_kind: "Enum", - import_id: "1" + import_id: "0" } }] } @@ -994,7 +994,7 @@ describe("AbiImportsLinker", () => { kind: "ImportRef", ref_kind: "Object", ref_name: "Foo", - import_id: "EXT", + import_id: "EXT1", } }, { @@ -1029,7 +1029,7 @@ describe("AbiImportsLinker", () => { required: true, type: { kind: "Ref", - ref_kind: "Object", + ref_kind: "Enum", ref_name: "TransitiveFoo", } } @@ -1111,11 +1111,6 @@ describe("AbiImportsLinker", () => { kind: "local", importedTypes: ["Some"], uriOrPath: "local", - }, - { - kind: "local", - importedTypes: ["TransitiveFoo"], - uriOrPath: "transitiveLocal", } ], external: [ From b4d166395049126d613e8d3b88e1f1363cdd8145 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 27 Feb 2023 23:09:07 +0100 Subject: [PATCH 72/90] (feat): ABI shaker unit tests --- .../abi/src/__tests__/AbiTreeShaker.spec.ts | 264 ++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 packages/schema/abi/src/__tests__/AbiTreeShaker.spec.ts diff --git a/packages/schema/abi/src/__tests__/AbiTreeShaker.spec.ts b/packages/schema/abi/src/__tests__/AbiTreeShaker.spec.ts new file mode 100644 index 0000000000..b2b96d8406 --- /dev/null +++ b/packages/schema/abi/src/__tests__/AbiTreeShaker.spec.ts @@ -0,0 +1,264 @@ +import { Abi } from "@polywrap/abi-types"; +import { AbiTreeShaker } from ".."; +import { AbiMerger } from "../AbiMerger"; +import { AbiSanitizer } from "../AbiSanitizer"; + +describe("AbiTreeShaker", () => { + it("Shakes local definitions based on needed types list", async () => { + const merger = new AbiMerger(); + const abiTreeShaker = new AbiTreeShaker(merger); + + const abi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "Foo", + props: [ + { + kind: "Property", + required: true, + name: "bar", + type: { + kind: "Ref", + ref_name: "Bar", + ref_kind: "Enum" + } + } + ] + }, + { + kind: "Object", + name: "Baz", + props: [ + { + kind: "Property", + name: "bar", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + } + ] + } + ], + enums: [ + { + kind: "Enum", + name: "Bar", + constants: ["ONE", "TWO"] + }, + { + kind: "Enum", + name: "Some", + constants: ["ONE", "TWO"] + } + ] + } + + const shakenAbi = abiTreeShaker.shakeTree(abi, ["Foo"]) + const expectedAbi: Abi = { + version: "0.2", + objects: [{ + kind: "Object", + name: "Foo", + props: [ + { + kind: "Property", + required: true, + name: "bar", + type: { + kind: "Ref", + ref_name: "Bar", + ref_kind: "Enum" + } + } + ] + }], + enums: [{ + kind: "Enum", + name: "Bar", + constants: ["ONE", "TWO"] + }] + } + const sanitizer = new AbiSanitizer(); + expect(sanitizer.sanitizeAbi(shakenAbi)).toEqual(expectedAbi) + }) + + it("Shakes unneeded definitions from imported abis", async () => { + const merger = new AbiMerger(); + const abiTreeShaker = new AbiTreeShaker(merger); + + const abi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "Foo", + props: [ + { + kind: "Property", + required: true, + name: "bar", + type: { + kind: "ImportRef", + ref_name: "Bar", + ref_kind: "Enum", + import_id: "2.1" + } + }, + { + kind: "Property", + required: true, + name: "baz", + type: { + kind: "ImportRef", + ref_name: "Baz", + ref_kind: "Enum", + import_id: "2" + } + } + ] + } + ], + imports: [ + { + namespace: "EXT2", + uri: "uri2", + id: "2", + type: "wasm", + objects: [ + { + kind: "Object", + name: "BazObj", + props: [ + { + kind: "Property", + required: true, + name: "bar", + type: { + kind: "Scalar", + scalar: "String" + } + } + ] + } + ], + enums: [ + { + kind: "Enum", + name: "Baz", + constants: ["ONE", "TWO"] + }, + { + kind: "Enum", + name: "BazEnum", + constants: ["ONE", "TWO"] + } + ], + imports: [ + { + namespace: "EXT1", + uri: "uri1", + id: "1", + type: "wasm", + objects: [ + { + kind: "Object", + name: "SomeObj", + props: [ + { + kind: "Property", + required: true, + name: "bar", + type: { + kind: "Scalar", + scalar: "String" + } + } + ] + } + ], + enums: [ + { + kind: "Enum", + name: "Bar", + constants: ["ONE", "TWO"] + } + ] + } + ] + } + ] + } + + const expectedAbi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "Foo", + props: [ + { + kind: "Property", + required: true, + name: "bar", + type: { + kind: "ImportRef", + ref_name: "Bar", + ref_kind: "Enum", + import_id: "2.1" + } + }, + { + kind: "Property", + required: true, + name: "baz", + type: { + kind: "ImportRef", + ref_name: "Baz", + ref_kind: "Enum", + import_id: "2" + } + } + ] + } + ], + imports: [ + { + namespace: "EXT2", + uri: "uri2", + id: "2", + type: "wasm", + enums: [ + { + kind: "Enum", + name: "Baz", + constants: ["ONE", "TWO"] + }, + ], + imports: [ + { + namespace: "EXT1", + uri: "uri1", + id: "1", + type: "wasm", + enums: [ + { + kind: "Enum", + name: "Bar", + constants: ["ONE", "TWO"] + } + ] + } + ] + } + ] + } + + const shakenAbi = abiTreeShaker.shakeImports(abi) + const sanitizer = new AbiSanitizer(); + expect(sanitizer.sanitizeAbi(shakenAbi)).toEqual(expectedAbi) + }); +}) \ No newline at end of file From e86a6a06dab6465370987e5e476baeb7665a7e5e Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 27 Feb 2023 23:11:08 +0100 Subject: [PATCH 73/90] (feat): ABI sanitizer added --- packages/schema/abi/src/AbiSanitizer.ts | 50 +++++++++++++++++++ .../src/__tests__/AbiImportsLinker.spec.ts | 39 +++++++++------ 2 files changed, 75 insertions(+), 14 deletions(-) create mode 100644 packages/schema/abi/src/AbiSanitizer.ts diff --git a/packages/schema/abi/src/AbiSanitizer.ts b/packages/schema/abi/src/AbiSanitizer.ts new file mode 100644 index 0000000000..c596f2bb44 --- /dev/null +++ b/packages/schema/abi/src/AbiSanitizer.ts @@ -0,0 +1,50 @@ +import { Abi, AbiDefs, ImportedAbi } from "@polywrap/abi-types"; + +export interface IAbiSanitizer { + sanitizeAbi(abi: Abi | ImportedAbi): Abi | ImportedAbi + sanitizeDefs(abiDefs: AbiDefs): AbiDefs +} + +export class AbiSanitizer implements IAbiSanitizer { + sanitizeAbi(abi: Abi | ImportedAbi): Abi | ImportedAbi { + const sanitizedAbi = this._removeEmptyArrays(abi) + + return sanitizedAbi + } + + sanitizeDefs(abiDefs: AbiDefs): AbiDefs { + const sanitizedDefs = this._removeEmptyArraysFromDefs(abiDefs) + + return sanitizedDefs + } + + private _removeEmptyArraysFromDefs(abiDefs: AbiDefs): AbiDefs { + if (!abiDefs.objects || abiDefs.objects.length === 0) { + delete abiDefs.objects + } + + if (!abiDefs.enums || abiDefs.enums.length === 0) { + delete abiDefs.enums + } + + if (!abiDefs.functions || abiDefs.functions.length === 0) { + delete abiDefs.functions + } + + return abiDefs + } + + private _removeEmptyArrays(abi: Abi | ImportedAbi): Abi | ImportedAbi { + this._removeEmptyArraysFromDefs(abi) + + if (!abi.imports || abi.imports.length === 0) { + delete abi.imports + } else { + abi.imports.forEach((importedAbi) => { + this._removeEmptyArrays(importedAbi) + }) + } + + return abi + } +} \ No newline at end of file diff --git a/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts index 8c542db34c..de7283e9c5 100644 --- a/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts +++ b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts @@ -1,10 +1,15 @@ import { Abi, ExternalImportStatement, LocalImportStatement, SchemaParser } from "@polywrap/abi-types" import { AbiImportsLinker } from "../AbiImportsLinker" import { AbiMerger } from "../AbiMerger" +import { AbiSanitizer } from "../AbiSanitizer" import { AbiTreeShaker } from "../AbiTreeShaker" import { mockFetchers, mockSchemaParser } from "./mocks" describe("AbiImportsLinker", () => { + const merger = new AbiMerger() + const shaker = new AbiTreeShaker(merger) + const sanitizer = new AbiSanitizer() + it("Should extract unique definitions", () => { const abi: Abi = { version: "0.2", @@ -49,7 +54,7 @@ describe("AbiImportsLinker", () => { ], } - const linker = new AbiImportsLinker(mockSchemaParser(), mockFetchers(), new AbiMerger(), new AbiTreeShaker()) + const linker = new AbiImportsLinker(mockSchemaParser(), mockFetchers(), merger, shaker) const uniqueDefinitions = linker.getUniqueDefinitionsMap(abi) const expectedUniqueDefinitions = new Map([ ["Some", "Object"], @@ -140,7 +145,7 @@ describe("AbiImportsLinker", () => { ["FOO_BAR_BAZ", { abi: bazImport, absoluteIdPath: "0.1.2" }] ]) - const linker = new AbiImportsLinker(mockSchemaParser(), mockFetchers(), new AbiMerger(), new AbiTreeShaker()) + const linker = new AbiImportsLinker(mockSchemaParser(), mockFetchers(), merger, shaker) const mapping = linker.mapImportsToNamespacePaths(abi); expect(mapping).toEqual(expectedMapping) @@ -264,10 +269,10 @@ describe("AbiImportsLinker", () => { parseLocalImportStatements: (_: string) => Promise.resolve([]), parseExternalImportStatements: (_: string) => Promise.resolve([]), } - const linker = new AbiImportsLinker(parser, fetchers, new AbiMerger(), new AbiTreeShaker()) + const linker = new AbiImportsLinker(parser, fetchers, merger, shaker) const mergedAbi = await linker.mergeLocalImports(abi, localImportStatements) - expect(mergedAbi.abi).toEqual(expectedAbi) + expect(sanitizer.sanitizeAbi(mergedAbi.abi)).toEqual(expectedAbi) }) it("Should merge local transitive imports", async () => { @@ -394,10 +399,10 @@ describe("AbiImportsLinker", () => { }, parseExternalImportStatements: (_: string) => Promise.resolve([]), } - const linker = new AbiImportsLinker(parser, fetchers, new AbiMerger(), new AbiTreeShaker()) + const linker = new AbiImportsLinker(parser, fetchers, merger, shaker) const mergedAbi = await linker.mergeLocalImports(abi, localImportStatements) - expect(mergedAbi.abi).toEqual(expectedAbi) + expect(sanitizer.sanitizeAbi(mergedAbi.abi)).toEqual(expectedAbi) }) it("Should embed external imports", async () => { @@ -480,14 +485,20 @@ describe("AbiImportsLinker", () => { type: "wasm", uri: "ext1", namespace: "EXT1", - ...extAbi1 + objects: extAbi1.objects, + enums: extAbi1.enums, + functions: extAbi1.functions, + imports: extAbi1.imports }, { id: "EXT2", type: "wasm", uri: "ext2", namespace: "EXT2", - ...extAbi2 + objects: extAbi2.objects, + enums: extAbi2.enums, + functions: extAbi2.functions, + imports: extAbi2.imports }, ] } @@ -522,10 +533,10 @@ describe("AbiImportsLinker", () => { } } - const linker = new AbiImportsLinker(mockSchemaParser(), fetchers, new AbiMerger(), new AbiTreeShaker()) + const linker = new AbiImportsLinker(mockSchemaParser(), fetchers, merger, shaker) const abiWithExports = await linker.embedExternalImports(abi, externalImportStatements) - expect(abiWithExports).toEqual(expectedAbi) + expect(sanitizer.sanitizeAbi(abiWithExports)).toEqual(expectedAbi) }) it("Link unlinked local and external import references", async () => { @@ -636,7 +647,7 @@ describe("AbiImportsLinker", () => { ] } - const linker = new AbiImportsLinker(mockSchemaParser(), mockFetchers(), new AbiMerger(), new AbiTreeShaker()) + const linker = new AbiImportsLinker(mockSchemaParser(), mockFetchers(), merger, shaker) const linkedAbi = await linker.linkImportReferences(abi) expect(linkedAbi).toEqual(expectedAbi) @@ -817,7 +828,7 @@ describe("AbiImportsLinker", () => { ] } - const linker = new AbiImportsLinker(mockSchemaParser(), mockFetchers(), new AbiMerger(), new AbiTreeShaker()) + const linker = new AbiImportsLinker(mockSchemaParser(), mockFetchers(), merger, shaker) const linkedAbi = await linker.linkImportReferences(abi) expect(linkedAbi).toEqual(expectedAbi) @@ -1103,7 +1114,7 @@ describe("AbiImportsLinker", () => { } } - const linker = new AbiImportsLinker(parser, fetchers, new AbiMerger(), new AbiTreeShaker()) + const linker = new AbiImportsLinker(parser, fetchers, merger, shaker) const linkedAbi = await linker.link(rootAbi, { local: [ @@ -1123,6 +1134,6 @@ describe("AbiImportsLinker", () => { ] }) - expect(linkedAbi).toEqual(expectedAbi) + expect(sanitizer.sanitizeAbi(linkedAbi)).toEqual(expectedAbi) }) }) \ No newline at end of file From 47c90a822b0ba3ad6998990f8d9ac02906c7a45d Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 27 Feb 2023 23:11:21 +0100 Subject: [PATCH 74/90] (chore): fixed mutating ABI visitor --- packages/schema/abi/src/AbiVisitor.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/schema/abi/src/AbiVisitor.ts b/packages/schema/abi/src/AbiVisitor.ts index 9cb38a6c7a..b005e019e2 100644 --- a/packages/schema/abi/src/AbiVisitor.ts +++ b/packages/schema/abi/src/AbiVisitor.ts @@ -3,7 +3,7 @@ import { Abi, AnyType, ArgumentDef, ArrayType, EnumDef, FunctionDef, ImportedAbi type VisitorFunction = (node: T) => T | void; export interface IAbiVisitor { - Abi?: VisitorFunction; + Abi?: VisitorFunction; ImportedAbi?: VisitorFunction; Import?: VisitorFunction; FunctionDef?: VisitorFunction; @@ -32,7 +32,7 @@ export class AbiVisitor implements IAbiVisitor { return value === undefined ? undefined : value; } - Abi(node: Abi): Abi { + Abi(node: Abi | ImportedAbi): Abi | ImportedAbi { let mutatedNode = node; if (this.visitor.enter?.Abi) { @@ -171,19 +171,19 @@ export class AbiVisitor implements IAbiVisitor { switch (mutatedNode.kind) { case "Scalar": - this.ScalarType(mutatedNode); + mutatedNode = this.ScalarType(mutatedNode); break; case "Array": - this.ArrayType(mutatedNode); + mutatedNode = this.ArrayType(mutatedNode); break; case "Map": - this.MapType(mutatedNode); + mutatedNode = this.MapType(mutatedNode); break; case "Ref": - this.RefType(mutatedNode); + mutatedNode = this.RefType(mutatedNode); break; case "ImportRef": - this.ImportRefType(mutatedNode); + mutatedNode = this.ImportRefType(mutatedNode); break; } @@ -268,7 +268,7 @@ export class AbiVisitor implements IAbiVisitor { return mutatedNode; } - visit(node: Abi) { + visit(node: Abi | ImportedAbi) { this.Abi(node); } } \ No newline at end of file From c1ec52574deffc0f478e7e8dff95c02e847995c8 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Mon, 27 Feb 2023 23:12:06 +0100 Subject: [PATCH 75/90] (fix): all tests passing --- packages/schema/abi/src/AbiImportsLinker.ts | 27 +++---- packages/schema/abi/src/AbiMerger.ts | 40 ++++------ packages/schema/abi/src/AbiTreeShaker.ts | 87 +++++++++------------ packages/schema/abi/src/index.ts | 9 ++- 4 files changed, 71 insertions(+), 92 deletions(-) diff --git a/packages/schema/abi/src/AbiImportsLinker.ts b/packages/schema/abi/src/AbiImportsLinker.ts index 81e63b05b6..50b657df3e 100644 --- a/packages/schema/abi/src/AbiImportsLinker.ts +++ b/packages/schema/abi/src/AbiImportsLinker.ts @@ -18,13 +18,16 @@ export class AbiImportsLinker implements IAbiImportsLinker { local: LocalSchemaFetcher; }, protected _abiMerger: IAbiMerger, protected _abiTreeShaker: IAbiTreeShaker) { } - async embedExternalImports(rootAbi: Abi, extImportStatements: ExternalImportStatement[]) { - let abiClone: Abi = JSON.parse(JSON.stringify(rootAbi)); + async embedExternalImports(rootAbi: Abi | ImportedAbi, extImportStatements: ExternalImportStatement[]) { + let abiClone: Abi | ImportedAbi = JSON.parse(JSON.stringify(rootAbi)); for await (const extImportStatement of extImportStatements) { - const externalSchema = await this._fetchers.external.fetch(extImportStatement.uriOrPath); + const externalAbi = await this._fetchers.external.fetch(extImportStatement.uriOrPath); const importedAbi: ImportedAbi = { - ...externalSchema, + objects: externalAbi.objects, + enums: externalAbi.enums, + functions: externalAbi.functions, + imports: externalAbi.imports, namespace: extImportStatement.namespace, id: extImportStatement.namespace, uri: extImportStatement.uriOrPath, @@ -37,11 +40,11 @@ export class AbiImportsLinker implements IAbiImportsLinker { return abiClone } - async mergeLocalImports(rootAbi: Abi, localImportStatements: LocalImportStatement[]): Promise<{ - abi: Abi, + async mergeLocalImports(rootAbi: Abi | ImportedAbi, localImportStatements: LocalImportStatement[]): Promise<{ + abi: Abi | ImportedAbi, transitiveExternalImports: ExternalImportStatement[] }> { - let mergedAbi: Abi = JSON.parse(JSON.stringify(rootAbi)) + let mergedAbi: Abi | ImportedAbi = JSON.parse(JSON.stringify(rootAbi)) const transitiveExternalImports: ExternalImportStatement[] = [] for await (const localImportStatement of localImportStatements) { @@ -54,7 +57,7 @@ export class AbiImportsLinker implements IAbiImportsLinker { const transitiveLocalImports = await this._schemaParser.parseLocalImportStatements(localSchema) const subResult = await this.mergeLocalImports(localShakenAbi, transitiveLocalImports) - mergedAbi = this._abiMerger.merge([mergedAbi, subResult.abi]) + mergedAbi = this._abiMerger.merge(mergedAbi as Abi, [subResult.abi]) transitiveExternalImports.push(...subResult.transitiveExternalImports) } @@ -149,10 +152,7 @@ export class AbiImportsLinker implements IAbiImportsLinker { throw new Error(`Could not find import for ${refType.ref_name}`) } - const foundDefinition = this._abiTreeShaker.findReferencedDefinition({ - version: "0.2", - ...importAbi.abi - }, refName) + const foundDefinition = this._abiTreeShaker.findReferencedDefinition(importAbi.abi, refName) if (!foundDefinition) { throw new Error(`Could not find imported definition for ${refType.ref_name}`) @@ -183,8 +183,7 @@ export class AbiImportsLinker implements IAbiImportsLinker { const externalImportStatements = [...externalImportStatementsFromRoot, ...transitiveExternalImports] const abiWithExtImports = await this.embedExternalImports(abiWithLocalImports, externalImportStatements) - const abiWithLinkedRefs = this.linkImportReferences(abiWithExtImports) - // const linkedAbiWithShakenExtImports = await this._abiTreeShaker.shakeImports(abiWithLinkedRefs) + const abiWithLinkedRefs = this.linkImportReferences(abiWithExtImports as Abi) return abiWithLinkedRefs } diff --git a/packages/schema/abi/src/AbiMerger.ts b/packages/schema/abi/src/AbiMerger.ts index c70e9b2f2d..daeac36b67 100644 --- a/packages/schema/abi/src/AbiMerger.ts +++ b/packages/schema/abi/src/AbiMerger.ts @@ -1,36 +1,24 @@ -import { Abi, ImportedAbi } from "@polywrap/abi-types"; +import { Abi, AbiDefs, ImportedAbi } from "@polywrap/abi-types"; export interface IAbiMerger { - merge(abis: Abi[]): Abi + merge(rootAbi: Abi, abisToMerge: (Abi | ImportedAbi)[]): Abi + mergeDefs(defs: AbiDefs[]): AbiDefs } export class AbiMerger implements IAbiMerger { - merge(abis: Abi[]): Abi { - return this._removeEmptyArrays({ - // TODO: handle different versions? - version: "0.2", - objects: abis.reduce((acc, abi) => [...acc, ...(abi.objects ?? [])], []), - enums: abis.reduce((acc, abi) => [...acc, ...(abi.enums ?? [])], []), - functions: abis.reduce((acc, abi) => [...acc, ...(abi.functions ?? [])], []), - imports: abis.reduce((acc, abi) => [...acc, ...(abi.imports ?? [])], []) - }) as Abi - } - - private _removeEmptyArrays(abi: Abi | ImportedAbi): Abi | ImportedAbi { - const processed = { - objects: abi.objects?.length ? abi.objects : undefined, - enums: abi.enums?.length ? abi.enums : undefined, - functions: abi.functions?.length ? abi.functions : undefined, - imports: abi.imports?.length ? abi.imports.map(this._removeEmptyArrays) as ImportedAbi[] : undefined + merge(rootAbi: Abi, abisToMerge: (Abi | ImportedAbi)[]): Abi { + return { + ...rootAbi, + ...this.mergeDefs([rootAbi, ...abisToMerge]), + imports: [rootAbi, ...abisToMerge].reduce((acc, abi) => [...acc, ...(abi.imports ?? [])], []) } + } - if ((abi as Abi).version) { - return { - version: (abi as Abi).version, - ...processed - } + mergeDefs(defs: AbiDefs[]): AbiDefs { + return { + objects: defs.reduce((acc, def) => [...acc, ...(def.objects ?? [])], []), + enums: defs.reduce((acc, def) => [...acc, ...(def.enums ?? [])], []), + functions: defs.reduce((acc, def) => [...acc, ...(def.functions ?? [])], []) } - - return processed as ImportedAbi } } \ No newline at end of file diff --git a/packages/schema/abi/src/AbiTreeShaker.ts b/packages/schema/abi/src/AbiTreeShaker.ts index 6f28ee4082..ed5a36f995 100644 --- a/packages/schema/abi/src/AbiTreeShaker.ts +++ b/packages/schema/abi/src/AbiTreeShaker.ts @@ -1,16 +1,19 @@ -import { Abi, AbiDefs, EnumDef, ImportRefType, ObjectDef } from "@polywrap/abi-types"; +import { Abi, AbiDefs, EnumDef, ImportedAbi, ImportRefType, ObjectDef } from "@polywrap/abi-types"; +import { IAbiMerger } from "."; import { AbiVisitor } from "./AbiVisitor"; type ReferenceableDef = ObjectDef | EnumDef; export interface IAbiTreeShaker { - findReferencedDefinition(abi: Abi, ref: string): ReferenceableDef | undefined - shakeTree(abi: Abi, neededDefNames: string[]): Abi - shakeImports(abi: Abi): Abi + findReferencedDefinition(abi: Abi | ImportedAbi, ref: string): ReferenceableDef | undefined + shakeTree(abi: Abi | ImportedAbi, neededDefNames: string[]): Abi | ImportedAbi + shakeImports(abi: Abi | ImportedAbi): Abi | ImportedAbi } export class AbiTreeShaker implements IAbiTreeShaker { - findReferencedDefinition(abi: Abi, refName: string): ReferenceableDef | undefined { + constructor(private _abiMerger: IAbiMerger) { } + + findReferencedDefinition(abi: Abi | ImportedAbi, refName: string): ReferenceableDef | undefined { // TODO: implement a stop to the search if the definition is found let found: ReferenceableDef | undefined = undefined; const visitor = new AbiVisitor({ @@ -33,7 +36,7 @@ export class AbiTreeShaker implements IAbiTreeShaker { return found; } - extractNeededDefinitions(abi: Abi, neededDefNames: string[]): AbiDefs { + extractNeededDefinitions(abi: Abi | ImportedAbi, neededDefNames: string[]): AbiDefs { const result: AbiDefs = {}; const state: { currentObject?: string; currentFunction?: string } = {} const abiVisitor = new AbiVisitor({ @@ -71,7 +74,7 @@ export class AbiTreeShaker implements IAbiTreeShaker { return result; } - extractImportReferences(abi: Abi): ImportRefType[] { + extractImportReferences(abi: Abi | ImportedAbi): ImportRefType[] { const result: ImportRefType[] = []; const abiVisitor = new AbiVisitor({ enter: { @@ -86,20 +89,20 @@ export class AbiTreeShaker implements IAbiTreeShaker { return result; } - extractReferencedSiblingDefinitions(abi: Abi, defs: AbiDefs): AbiDefs { + extractReferencedSiblingDefinitions(abi: Abi | ImportedAbi, defs: AbiDefs): AbiDefs { const result: AbiDefs = {}; - const objectDefNames = defs.objects?.map((def) => def.name) || []; - const functionDefNames = defs.functions?.map((def) => def.name) || []; + const objectDefNames = defs.objects?.map((def) => def.name); + const functionDefNames = defs.functions?.map((def) => def.name); const state: { currentObject?: string; currentFunction?: string } = {} const abiVisitor = new AbiVisitor({ enter: { ObjectDef: (def) => { - if (objectDefNames.includes(def.name)) { + if (objectDefNames?.includes(def.name)) { state.currentObject = def.name } }, FunctionDef: (def) => { - if (functionDefNames.includes(def.name)) { + if (functionDefNames?.includes(def.name)) { state.currentFunction = def.name } }, @@ -140,54 +143,34 @@ export class AbiTreeShaker implements IAbiTreeShaker { return result; } - private _shakeImports(abi: Abi, neededImports: ImportRefType[], state: { currentIdPath: string[] } = { - currentIdPath: [] - }): Abi { + private _shakeImports(abi: Abi | ImportedAbi, neededImports: ImportRefType[]): Abi | ImportedAbi { let abiClone = JSON.parse(JSON.stringify(abi)); + const state = { + currentIdPath: [] as string[], + neededImports + } + const importsVisitor = new AbiVisitor({ enter: { Import: (importDef) => { state.currentIdPath.push(importDef.id) const currentId = state.currentIdPath.join(".") - const neededFromThisImport = neededImports + const neededFromThisImport = state.neededImports .filter((neededImport) => neededImport.import_id === currentId) .map((neededImport) => neededImport.ref_name) - const neededDefsFromThisImport = this.extractNeededDefinitions({ - version: abiClone.version, - ...importDef - }, neededFromThisImport); - const neededSiblingDefs = this.extractReferencedSiblingDefinitions({ - version: abiClone.version, - ...importDef - }, neededDefsFromThisImport); - - importDef = { - id: importDef.id, - uri: importDef.uri, - namespace: importDef.namespace, - type: importDef.type, - ...neededDefsFromThisImport, - ...neededSiblingDefs, - } - const transitiveImports = this.extractImportReferences({ - version: abiClone.version, - ...importDef - }); + const shakenImportDef = this.shakeLocalDefinitions(importDef, neededFromThisImport) as ImportedAbi + state.neededImports.push(...this.extractImportReferences(importDef)); - abiClone = this._shakeImports({ - version: abiClone.version, - ...importDef - }, transitiveImports, state) - + return shakenImportDef } }, leave: { - Import: (importAbi) => { + Import: () => { state.currentIdPath.pop() } } @@ -198,22 +181,28 @@ export class AbiTreeShaker implements IAbiTreeShaker { return abiClone; } - shakeImports(abi: Abi): Abi { + shakeImports(abi: Abi | ImportedAbi): Abi | ImportedAbi { const neededImports = this.extractImportReferences(abi); const treeWithShakenImports = this._shakeImports(abi, neededImports); return treeWithShakenImports } - shakeTree(abi: Abi, neededDefNames: string[]): Abi { + shakeLocalDefinitions(abi: Abi | ImportedAbi, neededDefNames: string[]): Abi | ImportedAbi { const neededDefs = this.extractNeededDefinitions(abi, neededDefNames); const referencedDefs = this.extractReferencedSiblingDefinitions(abi, neededDefs); - const shakenTree = { - version: abi.version, - ...neededDefs, - ...referencedDefs, + const shakenDefs = this._abiMerger.mergeDefs([neededDefs, referencedDefs]) + + return { + ...abi, + objects: shakenDefs.objects, + enums: shakenDefs.enums, + functions: shakenDefs.functions, } + } + shakeTree(abi: Abi | ImportedAbi, neededDefNames: string[]): Abi | ImportedAbi { + const shakenTree = this.shakeLocalDefinitions(abi, neededDefNames); const shakenTreeWithShakenImports = this.shakeImports(shakenTree); return shakenTreeWithShakenImports; } diff --git a/packages/schema/abi/src/index.ts b/packages/schema/abi/src/index.ts index c6fc2cbabf..db99b927e9 100644 --- a/packages/schema/abi/src/index.ts +++ b/packages/schema/abi/src/index.ts @@ -2,6 +2,7 @@ import { AbiMerger } from "./AbiMerger"; import { AbiTreeShaker } from "./AbiTreeShaker";; import { AbiImportsLinker } from "./AbiImportsLinker"; import { ExternalSchemaFetcher, LocalSchemaFetcher, SchemaParser, Abi } from "@polywrap/abi-types"; +import { AbiSanitizer } from "./AbiSanitizer"; export * from "./AbiImportsLinker" export * from "./AbiMerger" @@ -23,12 +24,14 @@ export const parseAndLinkSchema = async ({ schema, parser, fetchers }: Args): Pr const localImportStatements = await parser.parseLocalImportStatements(schema) const merger = new AbiMerger() - const shaker = new AbiTreeShaker() + const sanitizer = new AbiSanitizer() + const shaker = new AbiTreeShaker(merger) const linker = new AbiImportsLinker(parser, fetchers, merger, shaker) const linkedAbi = await linker.link(abi, { external: externalImportStatements, local: localImportStatements }) - - return linkedAbi + const shakenAbi = await shaker.shakeImports(linkedAbi) + const sanitizedAbi = sanitizer.sanitizeAbi(shakenAbi) + return sanitizedAbi as Abi } From 4bc531826036ff55e39bce6200989ae7d31b694e Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Tue, 28 Feb 2023 00:39:02 +0100 Subject: [PATCH 76/90] (fix): more tests to shaker. Tested deep transitive imports --- packages/schema/abi/src/AbiTreeShaker.ts | 35 ++- .../src/__tests__/AbiImportsLinker.spec.ts | 1 - .../abi/src/__tests__/AbiTreeShaker.spec.ts | 235 ++++++++++++++++++ 3 files changed, 260 insertions(+), 11 deletions(-) diff --git a/packages/schema/abi/src/AbiTreeShaker.ts b/packages/schema/abi/src/AbiTreeShaker.ts index ed5a36f995..f4ed0fb36d 100644 --- a/packages/schema/abi/src/AbiTreeShaker.ts +++ b/packages/schema/abi/src/AbiTreeShaker.ts @@ -74,19 +74,36 @@ export class AbiTreeShaker implements IAbiTreeShaker { return result; } - extractImportReferences(abi: Abi | ImportedAbi): ImportRefType[] { - const result: ImportRefType[] = []; + extractImportReferences(abi: Abi): ImportRefType[] { + const refsWithAbsIds: ImportRefType[] = []; + const state: { currentId: string[] } = { currentId: [] }; const abiVisitor = new AbiVisitor({ enter: { + Import: (importDef) => { + state.currentId.push(importDef.id); + }, ImportRefType: (ref) => { - result.push(ref); + state.currentId.push(ref.import_id); + + refsWithAbsIds.push({ + ...ref, + import_id: state.currentId.join("."), + }); + }, + }, + leave: { + Import: () => { + state.currentId.pop(); + }, + ImportRefType: () => { + state.currentId.pop(); } } }); abiVisitor.visit(abi); - return result; + return refsWithAbsIds; } extractReferencedSiblingDefinitions(abi: Abi | ImportedAbi, defs: AbiDefs): AbiDefs { @@ -143,7 +160,7 @@ export class AbiTreeShaker implements IAbiTreeShaker { return result; } - private _shakeImports(abi: Abi | ImportedAbi, neededImports: ImportRefType[]): Abi | ImportedAbi { + private _shakeImports(abi: Abi, neededImports: ImportRefType[]): Abi { let abiClone = JSON.parse(JSON.stringify(abi)); @@ -161,9 +178,7 @@ export class AbiTreeShaker implements IAbiTreeShaker { const neededFromThisImport = state.neededImports .filter((neededImport) => neededImport.import_id === currentId) .map((neededImport) => neededImport.ref_name) - const shakenImportDef = this.shakeLocalDefinitions(importDef, neededFromThisImport) as ImportedAbi - state.neededImports.push(...this.extractImportReferences(importDef)); return shakenImportDef } @@ -181,7 +196,7 @@ export class AbiTreeShaker implements IAbiTreeShaker { return abiClone; } - shakeImports(abi: Abi | ImportedAbi): Abi | ImportedAbi { + shakeImports(abi: Abi): Abi { const neededImports = this.extractImportReferences(abi); const treeWithShakenImports = this._shakeImports(abi, neededImports); @@ -201,9 +216,9 @@ export class AbiTreeShaker implements IAbiTreeShaker { } } - shakeTree(abi: Abi | ImportedAbi, neededDefNames: string[]): Abi | ImportedAbi { + shakeTree(abi: Abi, neededDefNames: string[]): Abi | ImportedAbi { const shakenTree = this.shakeLocalDefinitions(abi, neededDefNames); - const shakenTreeWithShakenImports = this.shakeImports(shakenTree); + const shakenTreeWithShakenImports = this.shakeImports(shakenTree as Abi); return shakenTreeWithShakenImports; } } \ No newline at end of file diff --git a/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts index de7283e9c5..56c49a59b0 100644 --- a/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts +++ b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts @@ -1087,7 +1087,6 @@ describe("AbiImportsLinker", () => { return Promise.resolve([]) }, parseLocalImportStatements: async (schemaUri: string): Promise => { - console.log("SCHEMA URI ", schemaUri) if (schemaUri === "local") { return Promise.resolve([ { diff --git a/packages/schema/abi/src/__tests__/AbiTreeShaker.spec.ts b/packages/schema/abi/src/__tests__/AbiTreeShaker.spec.ts index b2b96d8406..d55f986ac9 100644 --- a/packages/schema/abi/src/__tests__/AbiTreeShaker.spec.ts +++ b/packages/schema/abi/src/__tests__/AbiTreeShaker.spec.ts @@ -261,4 +261,239 @@ describe("AbiTreeShaker", () => { const sanitizer = new AbiSanitizer(); expect(sanitizer.sanitizeAbi(shakenAbi)).toEqual(expectedAbi) }); + + it("Detects and keeps transitive import definitions", async () => { + const merger = new AbiMerger(); + const abiTreeShaker = new AbiTreeShaker(merger); + + const abi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "Foo", + props: [ + { + kind: "Property", + required: true, + name: "baz", + type: { + kind: "ImportRef", + ref_name: "Baz", + ref_kind: "Object", + import_id: "2" + } + } + ] + } + ], + imports: [ + { + namespace: "EXT2", + uri: "uri2", + id: "2", + type: "wasm", + objects: [ + { + kind: "Object", + name: "Baz", + props: [ + { + kind: "Property", + required: true, + name: "bar", + type: { + kind: "ImportRef", + ref_name: "Bar", + ref_kind: "Enum", + import_id: "1" + } + }, + { + kind: "Property", + required: true, + name: "bar2", + type: { + kind: "Ref", + ref_name: "BazEnum", + ref_kind: "Enum" + } + }, + { + kind: "Property", + required: true, + name: "bar2", + type: { + kind: "ImportRef", + import_id: "1.3", + ref_name: "FooBar", + ref_kind: "Enum" + } + } + ] + } + ], + enums: [ + { + kind: "Enum", + name: "BazEnum", + constants: ["ONE", "TWO"] + }, + { + kind: "Enum", + name: "BazEnum2", + constants: ["ONE", "TWO"] + } + ], + imports: [ + { + namespace: "EXT1", + uri: "uri1", + id: "1", + type: "wasm", + enums: [ + { + kind: "Enum", + name: "Bar", + constants: ["ONE", "TWO"] + } + ], + imports: [ + { + namespace: "EXT3", + uri: "uri3", + id: "3", + type: "wasm", + enums: [ + { + kind: "Enum", + name: "FooBar", + constants: ["ONE", "TWO"] + }, + { + kind: "Enum", + name: "FooBar2", + constants: ["ONE", "TWO"] + } + ] + } + ] + } + ] + } + ] + } + + const expectedAbi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "Foo", + props: [ + { + kind: "Property", + required: true, + name: "baz", + type: { + kind: "ImportRef", + ref_name: "Baz", + ref_kind: "Object", + import_id: "2" + } + } + ] + } + ], + imports: [ + { + namespace: "EXT2", + uri: "uri2", + id: "2", + type: "wasm", + objects: [ + { + kind: "Object", + name: "Baz", + props: [ + { + kind: "Property", + required: true, + name: "bar", + type: { + kind: "ImportRef", + ref_name: "Bar", + ref_kind: "Enum", + import_id: "1" + } + }, + { + kind: "Property", + required: true, + name: "bar2", + type: { + kind: "Ref", + ref_name: "BazEnum", + ref_kind: "Enum" + } + }, + { + kind: "Property", + required: true, + name: "bar2", + type: { + kind: "ImportRef", + import_id: "1.3", + ref_name: "FooBar", + ref_kind: "Enum" + } + } + ] + } + ], + enums: [ + { + kind: "Enum", + name: "BazEnum", + constants: ["ONE", "TWO"] + } + ], + imports: [ + { + namespace: "EXT1", + uri: "uri1", + id: "1", + type: "wasm", + enums: [ + { + kind: "Enum", + name: "Bar", + constants: ["ONE", "TWO"] + } + ], + imports: [ + { + namespace: "EXT3", + uri: "uri3", + id: "3", + type: "wasm", + enums: [ + { + kind: "Enum", + name: "FooBar", + constants: ["ONE", "TWO"] + } + ] + } + ] + } + ] + } + ] + } + + const shakenAbi = abiTreeShaker.shakeImports(abi) + const sanitizer = new AbiSanitizer(); + expect(sanitizer.sanitizeAbi(shakenAbi)).toEqual(expectedAbi) + }); }) \ No newline at end of file From b11fc79a929a25c3901618b89cca0c98bc4057b2 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Tue, 28 Feb 2023 01:20:57 +0100 Subject: [PATCH 77/90] (chore): added visitor tests --- packages/schema/abi/src/AbiVisitor.ts | 2 +- .../abi/src/__tests__/AbiVisitor.spec.ts | 241 ++++++++++++++++++ 2 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 packages/schema/abi/src/__tests__/AbiVisitor.spec.ts diff --git a/packages/schema/abi/src/AbiVisitor.ts b/packages/schema/abi/src/AbiVisitor.ts index b005e019e2..13b04fafdc 100644 --- a/packages/schema/abi/src/AbiVisitor.ts +++ b/packages/schema/abi/src/AbiVisitor.ts @@ -2,9 +2,9 @@ import { Abi, AnyType, ArgumentDef, ArrayType, EnumDef, FunctionDef, ImportedAbi type VisitorFunction = (node: T) => T | void; +// NOTE: does not visit map keys export interface IAbiVisitor { Abi?: VisitorFunction; - ImportedAbi?: VisitorFunction; Import?: VisitorFunction; FunctionDef?: VisitorFunction; ArgumentDef?: VisitorFunction; diff --git a/packages/schema/abi/src/__tests__/AbiVisitor.spec.ts b/packages/schema/abi/src/__tests__/AbiVisitor.spec.ts new file mode 100644 index 0000000000..aebda40228 --- /dev/null +++ b/packages/schema/abi/src/__tests__/AbiVisitor.spec.ts @@ -0,0 +1,241 @@ +import { Abi } from "@polywrap/abi-types" +import { AbiVisitor } from ".." + +describe("AbiVisitor", () => { + it("Visits all definitions and types", () => { + const visits = { + Abi: 0, + Import: 0, + FunctionDef: 0, + ArgumentDef: 0, + ResultDef: 0, + ObjectDef: 0, + PropertyDef: 0, + EnumDef: 0, + ScalarType: 0, + RefType: 0, + ImportRefType: 0, + ArrayType: 0, + MapType: 0, + AnyType: 0, + } + + const expectedvisits = { + Abi: 2, + Import: 2, + FunctionDef: 2, + ArgumentDef: 2, + ResultDef: 2, + ObjectDef: 4, + PropertyDef: 6, + EnumDef: 2, + ScalarType: 6, + RefType: 2, + ImportRefType: 2, + ArrayType: 2, + MapType: 2, + AnyType: 14, + } + + const visitor = new AbiVisitor({ + enter: { + Abi: () => { + visits.Abi++ + }, + Import: () => { + visits.Import++ + }, + FunctionDef: () => { + visits.FunctionDef++ + }, + ArgumentDef: () => { + visits.ArgumentDef++ + }, + ResultDef: () => { + visits.ResultDef++ + }, + ObjectDef: () => { + visits.ObjectDef++ + }, + PropertyDef: () => { + visits.PropertyDef++ + }, + EnumDef: () => { + visits.EnumDef++ + }, + ScalarType: () => { + visits.ScalarType++ + }, + RefType: () => { + visits.RefType++ + }, + ImportRefType: () => { + visits.ImportRefType++ + }, + ArrayType: () => { + visits.ArrayType++ + }, + MapType: () => { + visits.MapType++ + }, + AnyType: () => { + visits.AnyType++ + }, + }, + leave: { + Abi: () => { + visits.Abi++ + }, + Import: () => { + visits.Import++ + }, + FunctionDef: () => { + visits.FunctionDef++ + }, + ArgumentDef: () => { + visits.ArgumentDef++ + }, + ResultDef: () => { + visits.ResultDef++ + }, + ObjectDef: () => { + visits.ObjectDef++ + }, + PropertyDef: () => { + visits.PropertyDef++ + }, + EnumDef: () => { + visits.EnumDef++ + }, + ScalarType: () => { + visits.ScalarType++ + }, + RefType: () => { + visits.RefType++ + }, + ImportRefType: () => { + visits.ImportRefType++ + }, + ArrayType: () => { + visits.ArrayType++ + }, + MapType: () => { + visits.MapType++ + }, + AnyType: () => { + visits.AnyType++ + }, + } + }) + + const abi: Abi = { + version: "0.2", + imports: [ + { + id: "0", + uri: "https://example.com/abi.json", + namespace: "Example", + type: "wasm", + objects: [ + { + kind: "Object", + name: "ExampleObject", + props: [ + { + kind: "Property", + name: "exampleProperty", + required: true, + type: { + kind: "Scalar", + scalar: "String", + }, + } + ] + } + ] + } + ], + objects: [ + { + kind: "Object", + name: "ExampleObject2", + props: [ + { + kind: "Property", + name: "exampleProperty", + required: true, + type: { + kind: "Ref", + ref_name: "RefName", + ref_kind: "Object", + }, + }, + { + kind: "Property", + name: "exampleProperty", + required: true, + type: { + import_id: "0", + kind: "ImportRef", + ref_name: "RefName2", + ref_kind: "Object", + }, + } + ] + } + ], + enums: [ + { + kind: "Enum", + name: "ExampleEnum", + constants: ["CONSTANT1", "CONSTANT2"], + } + ], + functions: [ + { + kind: "Function", + name: "ExampleFunction", + args: [ + { + kind: "Argument", + name: "exampleArgument", + required: true, + type: { + kind: "Array", + item: { + required: true, + type: { + kind: "Scalar", + scalar: "String", + } + } + } + } + ], + result: { + kind: "Result", + required: true, + type: { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String", + }, + value: { + required: true, + type: { + kind: "Scalar", + scalar: "String", + } + } + } + } + } + ] + } + + visitor.visit(abi) + + expect(visits).toEqual(expectedvisits) + }) +}) \ No newline at end of file From 851f96d2624a2443c819bb7d37e39c5d21918e22 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Tue, 28 Feb 2023 01:21:30 +0100 Subject: [PATCH 78/90] (chore): removed old tests --- .../schema/abi/src/__tests__/imports.spec.ts | 352 ------------------ packages/schema/abi/src/__tests__/index.ts | 92 ----- .../abi/src/__tests__/parse-map-type.spec.ts | 188 ---------- .../abi/src/__tests__/test-cases.spec.ts | 36 -- .../abi/src/__tests__/transforms.spec.ts | 325 ---------------- .../schema/abi/src/__tests__/utils.spec.ts | 224 ----------- .../src/__tests__/validate-directives.spec.ts | 146 -------- .../abi/src/__tests__/validate-types.spec.ts | 340 ----------------- 8 files changed, 1703 deletions(-) delete mode 100644 packages/schema/abi/src/__tests__/imports.spec.ts delete mode 100644 packages/schema/abi/src/__tests__/index.ts delete mode 100644 packages/schema/abi/src/__tests__/parse-map-type.spec.ts delete mode 100644 packages/schema/abi/src/__tests__/test-cases.spec.ts delete mode 100644 packages/schema/abi/src/__tests__/transforms.spec.ts delete mode 100644 packages/schema/abi/src/__tests__/utils.spec.ts delete mode 100644 packages/schema/abi/src/__tests__/validate-directives.spec.ts delete mode 100644 packages/schema/abi/src/__tests__/validate-types.spec.ts diff --git a/packages/schema/abi/src/__tests__/imports.spec.ts b/packages/schema/abi/src/__tests__/imports.spec.ts deleted file mode 100644 index 4fa02eb7a0..0000000000 --- a/packages/schema/abi/src/__tests__/imports.spec.ts +++ /dev/null @@ -1,352 +0,0 @@ -import { AbiMerger } from "../AbiMerger" -import { Abi } from "../definitions" -import { ImportsLinker } from "../AbiImportsLinker" -import { ExternalImportStatement, LocalImportStatement, SchemaParser } from "../types" - -describe("Imports parser", () => { - it("Works", async () => { - - const rootSchema = ` - import { Foo } from "foo" - import { ExtFoo } into Ext from "extfoo" - - type Baz { - extfoo: Ext_ExtFoo! - foo: Foo! - } - ` - - const fooSchema = ` - import { Bar } from "bar" - import { OutBar } into Out from "outbar" - - type Some { - propSome: Out_OutBar! - } - - type Additional { - bar: NamespaceBar_Bar! - } - - type Foo { - propFoo: Additional! - prop2: Some! - } - ` - - const barSchema = ` - type Bar { - propBar: Transitive! - } - - type Transitive { - propTransitive: String! - } - ` - - const fetchers = { - external: async (uri: string): Promise => { - switch (uri) { - case "extfoo": return { - version: "0.2", - objects: [{ - kind: "Object", - name: "ExtFoo", - props: [{ - kind: "Property", - name: "propExtFoo", - required: true, - type: { - kind: "Scalar", - scalar: "String" - } - }] - }] - } - case "outbar": return { - version: "0.2", - objects: [{ - kind: "Object", - name: "OutBar", - props: [{ - kind: "Property", - name: "propOutBar", - required: true, - type: { - kind: "Scalar", - scalar: "String" - } - }] - }] - } - default: throw new Error(`Unknown URI: ${uri}`) - } - }, - local: async (path: string): Promise => { - switch (path) { - case "foo": return fooSchema - case "bar": return barSchema - default: throw new Error(`Unknown path: ${path}`) - } - } - } - - const mockSchemaParser: SchemaParser = { - parse: async (schema: string): Promise => { - switch (schema) { - case rootSchema: return { - version: "0.2", - functions: [], - enums: [], - objects: [{ - kind: "Object", - props: [{ - kind: "Property", - name: "extfoo", - required: true, - type: { - kind: "UnlinkedImportRef", - ref_name: "ExtFoo", - } - }, { - kind: "Property", - name: "foo", - required: true, - type: { - kind: "UnlinkedImportRef", - ref_name: "Foo", - } - }], - name: "Baz", - }] - }; - case fooSchema: return { - version: "0.2", - functions: [], - enums: [], - objects: [{ - kind: "Object", - name: "Some", - props: [{ - kind: "Property", - name: "propSome", - required: true, - type: { - kind: "UnlinkedImportRef", - ref_name: "OutBar", - } - }] - }, { - kind: "Object", - name: "Additional", - props: [{ - kind: "Property", - name: "bar", - required: true, - type: { - kind: "UnlinkedImportRef", - ref_name: "Bar", - } - }] - }, { - kind: "Object", - name: "Foo", - props: [{ - kind: "Property", - name: "propFoo", - required: true, - type: { - kind: "Ref", - ref_kind: "Object", - ref_name: "Additional", - } - }, { - kind: "Property", - name: "prop2", - required: true, - type: { - kind: "Ref", - ref_kind: "Object", - ref_name: "Some", - } - }] - }] - } - case barSchema: return { - version: "0.2", - functions: [], - enums: [], - objects: [{ - kind: "Object", - name: "Bar", - props: [{ - kind: "Property", - name: "propBar", - required: true, - type: { - kind: "Ref", - ref_kind: "Object", - ref_name: "Transitive" - } - }] - }, { - kind: "Object", - name: "Transitive", - props: [{ - kind: "Property", - name: "propTransitive", - required: true, - type: { - kind: "Scalar", - scalar: "String" - } - }] - }] - } - default: throw new Error(`Unknown schema: ${schema}`) - } - }, - parseExternalImportStatements: async (schema: string): Promise => { - switch (schema) { - case rootSchema: return [{ - kind: "external", - uriOrPath: "extfoo", - importedTypes: ["ExtFoo"], - namespace: "Ext" - }] - case fooSchema: return [{ - kind: "external", - uriOrPath: "outbar", - importedTypes: ["OutBar"], - namespace: "Out" - }] - case barSchema: return []; - default: throw new Error(`Unknown schema: ${schema}`) - } - }, - parseLocalImportStatements: async (schema: string): Promise => { - switch (schema) { - case rootSchema: return [{ - kind: "local", - uriOrPath: "foo", - importedTypes: ["Foo"], - }] - case fooSchema: return [{ - kind: "local", - uriOrPath: "bar", - importedTypes: ["Bar"], - }] - case barSchema: return []; - default: throw new Error(`Unknown schema: ${schema}`) - } - } - } - - const importsParser = new ImportsLinker(mockSchemaParser, fetchers, new AbiMerger()) - const { abi, externalImportStatements } = await importsParser.link(rootSchema) - - const expectedAbi: Abi = { - version: "0.2", - functions: [], - enums: [], - objects: [{ - kind: "Object", - props: [{ - kind: "Property", - name: "extfoo", - required: true, - type: { - kind: "UnlinkedImportRef", - ref_name: "ExtFoo", - } - }, { - kind: "Property", - name: "foo", - required: true, - type: { - kind: "UnlinkedImportRef", - ref_name: "Foo", - } - }], - name: "Baz", - }, - { - kind: "Object", - name: "Foo", - props: [{ - kind: "Property", - name: "propFoo", - required: true, - type: { - kind: "Ref", - ref_kind: "Object", - ref_name: "Additional", - } - }, { - kind: "Property", - name: "prop2", - required: true, - type: { - kind: "Ref", - ref_kind: "Object", - ref_name: "Some", - } - }] - }, { - kind: "Object", - name: "Additional", - props: [{ - kind: "Property", - name: "bar", - required: true, - type: { - kind: "UnlinkedImportRef", - ref_name: "Bar", - } - }] - }, { - kind: "Object", - name: "Some", - props: [{ - kind: "Property", - name: "propSome", - required: true, - type: { - kind: "UnlinkedImportRef", - ref_name: "OutBar", - } - }] - }, - { - kind: "Object", - name: "Bar", - props: [{ - kind: "Property", - name: "propBar", - required: true, - type: { - kind: "Ref", - ref_kind: "Object", - ref_name: "Transitive" - } - }] - }, { - kind: "Object", - name: "Transitive", - props: [{ - kind: "Property", - name: "propTransitive", - required: true, - type: { - kind: "Scalar", - scalar: "String" - } - }] - } - ] - } - - expect(abi).toEqual(expectedAbi) - console.log(externalImportStatements) - }) -}) \ No newline at end of file diff --git a/packages/schema/abi/src/__tests__/index.ts b/packages/schema/abi/src/__tests__/index.ts deleted file mode 100644 index c2cc127c06..0000000000 --- a/packages/schema/abi/src/__tests__/index.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { WrapAbi } from "../abi"; - -import path from "path"; -import { readdirSync, Dirent } from "fs"; - -import { - GetPathToParseTestFiles, - readFileIfExists, - readNamedExportIfExists -} from "@polywrap/test-cases" - -const root = GetPathToParseTestFiles(); - -export type TestCase = { - name: string; - input: string; - output: WrapAbi; -}; - -export type TestCases = { - promise: Promise; - name: string; -}[]; - -export function fetchTestCases(): TestCases { - const testCases: TestCases = []; - - readdirSync(root, { withFileTypes: true }).forEach( - (dirent: Dirent) => { - buildTestCases( - path.join(root, dirent.name), - dirent.name, - testCases - ); - } - ); - - return testCases; -} - -function buildTestCases( - directory: string, - name: string, - testCases: TestCases -): void { - const items = readdirSync(directory, { withFileTypes: true }); - - if ( - items.some(x => x.name.startsWith("input")) && - items.some(x => x.name.startsWith("output")) - ) { - testCases.push({ - promise: importCase(directory, name), - name: name - }); - } else { - for (const item of items) { - buildTestCases( - path.join(directory, item.name), - item.name, - testCases - ); - } - } -} - -async function importCase( - directory: string, - name: string, -): Promise { - // Fetch the input schema - const input = readFileIfExists("input.graphql", directory); - - // Fetch the output Abi - const output = await readNamedExportIfExists("abi", "output.ts", directory); - - if (!input) { - console.error(`Missing input file "input.graphql" for test case "${name}" at ${directory}`); - return undefined; - } - - if (!output) { - console.error(`Missing output file "output.ts" for test case "${name}" at ${directory}`); - return undefined; - } - - return { - name, - input, - output - }; -} diff --git a/packages/schema/abi/src/__tests__/parse-map-type.spec.ts b/packages/schema/abi/src/__tests__/parse-map-type.spec.ts deleted file mode 100644 index 65fd989623..0000000000 --- a/packages/schema/abi/src/__tests__/parse-map-type.spec.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { - createMapDefinition, - createMapKeyDefinition, - createScalarDefinition, - createUnresolvedObjectOrEnumRef, -} from "../abi"; -import { - parseCurrentType, - parseMapType, - toGraphQLType, -} from "../extract/utils/map-utils"; - -describe("parseMapType", () => { - test("Map", () => { - const result = parseMapType("Map"); - expect(result).toMatchObject( - createMapDefinition({ - type: "Map", - key: createMapKeyDefinition({ type: "String", required: true }), - value: createScalarDefinition({ type: "Int" }), - }) - ); - }); - - test("Map", () => { - const result = parseMapType("Map"); - expect(result).toMatchObject( - createMapDefinition({ - type: "Map", - key: createMapKeyDefinition({ - type: "String", - required: true, - }), - value: createUnresolvedObjectOrEnumRef({ - type: "CustomType", - required: true, - }), - }) - ); - }); - - test("Map!>", () => { - const result = parseMapType("Map!>", "customMap"); - expect(result).toMatchObject({ - name: "customMap", - type: "Map", - key: { - name: "customMap", - type: "Int", - }, - value: { - name: "customMap", - type: "[String]", - item: { - type: "String", - }, - required: true, - }, - }); - }); - - test("Map", () => { - const result = parseMapType("Map"); - expect(result).toMatchObject({ - type: "Map", - key: { - type: "Int", - }, - value: { - type: "[String]", - item: { - type: "String", - }, - required: true, - }, - }); - }); - - test("Map!>!", () => { - const result = parseMapType("Map!>!"); - expect(result).toMatchObject({ - type: "Map>", - key: { - type: "String", - }, - value: { - type: "Map", - key: { - type: "String", - }, - value: { - type: "Int", - required: true, - }, - required: true, - }, - required: true, - }); - }); - - test("Map", () => { - expect(() => parseMapType("Map")).toThrow( - "Found invalid map key type: CustomType while parsing Map" - ); - }); -}); - -describe("toGraphQLType", () => { - test("Map", () => { - const result = toGraphQLType("Map"); - expect(result).toBe("Map"); - }); - - test("Map", () => { - const result = toGraphQLType("Map"); - expect(result).toBe("Map"); - }); - - test("Map!>", () => { - const result = toGraphQLType("Map!>"); - expect(result).toBe("Map"); - }); - - test("Array!", () => { - const result = toGraphQLType("Array!"); - expect(result).toBe("[String]"); - }); -}); - -describe("parseCurrentType", () => { - test("Map", () => { - const result = parseCurrentType("Map"); - expect(result).toMatchObject({ - currentType: "Map", - subType: "String, Int", - }); - }); - - test("Map", () => { - const result = parseCurrentType("Map"); - expect(result).toMatchObject({ - currentType: "Map", - subType: "String, CustomType!", - }); - }); - - test("Map!>!", () => { - const result = parseCurrentType("Map!>!"); - expect(result).toMatchObject({ - currentType: "Map", - subType: "Int, Array!", - required: true, - }); - }); - - test("Array!", () => { - const result = parseCurrentType("Array!"); - expect(result).toMatchObject({ - currentType: "Array", - subType: "String!", - required: true, - }); - }); - - test("Map!>!", () => { - const result = parseCurrentType("Map!>!"); - expect(result).toMatchObject({ - currentType: "Map", - subType: "String!, Map!", - required: true, - }); - }); - - test("CustomType!", () => { - const result = parseCurrentType("CustomType!"); - expect(result).toMatchObject({ - currentType: "CustomType", - required: true, - }); - }); - - test("String", () => { - const result = parseCurrentType("String"); - expect(result).toMatchObject({ - currentType: "String", - }); - }); -}); diff --git a/packages/schema/abi/src/__tests__/test-cases.spec.ts b/packages/schema/abi/src/__tests__/test-cases.spec.ts deleted file mode 100644 index f723512f1c..0000000000 --- a/packages/schema/abi/src/__tests__/test-cases.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { parseSchema } from ".."; -import { fetchTestCases } from "./index"; - -describe("Polywrap Schema Parser Test Cases", () => { - const cases = fetchTestCases(); - - for (const test of cases) { - it(`Case: ${test.name}`, async () => { - const testCase = await test.promise; - - if (!testCase) { - return; - } - - const result = parseSchema(testCase.input); - - const sort = (obj: any) => Object - .keys(obj) - .sort() - .reduce((map: any, key) => { - if (typeof obj[key] === "object") { - map[key] = sort(obj[key]); - - if (Array.isArray(obj[key])) { - map[key] = Object.values(map[key]); - } - } else { - map[key] = obj[key]; - } - return map - }, {}); - - expect(sort(result)).toMatchObject(sort(testCase.output)); - }); - } -}); diff --git a/packages/schema/abi/src/__tests__/transforms.spec.ts b/packages/schema/abi/src/__tests__/transforms.spec.ts deleted file mode 100644 index f24dd53c9a..0000000000 --- a/packages/schema/abi/src/__tests__/transforms.spec.ts +++ /dev/null @@ -1,325 +0,0 @@ -import { parseSchema } from ".."; -import { addFirstLast, extendType } from "../transform"; -import { - createObjectDefinition, - createScalarPropertyDefinition, - createModuleDefinition, - createMethodDefinition, - createImportedModuleDefinition, -} from "../abi"; -import { - WrapAbi, - ImportedModuleDefinition, - MethodDefinition, - ModuleDefinition, - ObjectDefinition, - PropertyDefinition, -} from "@polywrap/wrap-manifest-types-js"; - -const schema1 = ` -type MyType { - prop1: String! - prop2: String -} - -type AnotherType { - prop: String! -} - -type Module { - method1( - arg1: String! - arg2: String - arg3: Boolean - ): String! - - method2( - arg1: String! - ): String - - method3( - arg1: String! - ): Boolean -} - -type TestImport_Module @imported( - uri: "testimport.uri.eth", - namespace: "TestImport", - nativeType: "Module" -) { - importedMethod( - str: String! - ): String! - - anotherMethod( - str: String! - ): String! -} -`; - -const schema2 = ` -type MyType { - prop1: String! - prop2: String -} - -type AnotherType { - prop: String! -} -`; - -describe("Polywrap Schema Abi Transformations", () => { - it("addFirstLast", () => { - const abi = parseSchema(schema1, { - transforms: [addFirstLast], - }); - const expected: WrapAbi = { - version: "0.1", - objectTypes: [ - { - ...createObjectDefinition({ type: "MyType" }), - properties: [ - { - ...createScalarPropertyDefinition({ - name: "prop1", - type: "String", - required: true - }), - first: true, - last: null, - } as PropertyDefinition, - { - ...createScalarPropertyDefinition({ - name: "prop2", - type: "String" - }), - first: null, - last: true, - }, - ], - first: true, - last: null, - } as ObjectDefinition, - { - ...createObjectDefinition({ type: "AnotherType" }), - properties: [ - { - ...createScalarPropertyDefinition({ - name: "prop", - type: "String", - required: true - }), - first: true, - last: true, - } as PropertyDefinition, - ], - first: null, - last: true, - } as ObjectDefinition, - ], - moduleType: - { - ...createModuleDefinition({}), - methods: [ - { - ...createMethodDefinition({ - name: "method1", - arguments: [ - { - ...createScalarPropertyDefinition({ - type: "String", - name: "arg1", - required: true, - }), - first: true, - last: null - } as PropertyDefinition, - { - ...createScalarPropertyDefinition({ - type: "String", - name: "arg2", - }), - first: null, - last: null - } as PropertyDefinition, - { - ...createScalarPropertyDefinition({ - type: "Boolean", - name: "arg3", - }), - first: null, - last: true - } as PropertyDefinition, - ], - return: createScalarPropertyDefinition({ - type: "String", - name: "method1", - required: true - }) - }), - first: true, - last: null - } as MethodDefinition, - { - ...createMethodDefinition({ - name: "method2", - arguments: [ - { - ...createScalarPropertyDefinition({ - type: "String", - name: "arg1", - required: true, - }), - first: true, - last: true - } as PropertyDefinition, - ], - return: createScalarPropertyDefinition({ - type: "String", - name: "method2" - }) - }), - first: null, - last: null - }, - { - ...createMethodDefinition({ - name: "method3", - arguments: [ - { - ...createScalarPropertyDefinition({ - type: "String", - name: "arg1", - required: true, - }), - first: true, - last: true - } as PropertyDefinition, - ], - return: createScalarPropertyDefinition({ - type: "Boolean", - name: "method3", - }) - }), - first: null, - last: true - } as MethodDefinition, - ], - } as ModuleDefinition, - importedModuleTypes: [ - { - ...createImportedModuleDefinition({ - uri: "testimport.uri.eth", - namespace: "TestImport", - nativeType: "Module", - isInterface: false, - }), - methods: [ - { - ...createMethodDefinition({ - name: "importedMethod", - arguments: [ - { - ...createScalarPropertyDefinition({ - type: "String", - name: "str", - required: true, - }), - first: true, - last: true - } as PropertyDefinition - ], - return: createScalarPropertyDefinition({ - type: "String", - name: "importedMethod", - required: true - }) - }), - first: true, - last: null - } as MethodDefinition, - { - ...createMethodDefinition({ - name: "anotherMethod", - arguments: [ - { - ...createScalarPropertyDefinition({ - type: "String", - name: "str", - required: true, - }), - first: true, - last: true - } as PropertyDefinition - ], - return: createScalarPropertyDefinition({ - type: "String", - name: "anotherMethod", - required: true - }) - }), - first: null, - last: true - } as MethodDefinition, - ], - first: true, - last: true - } as ImportedModuleDefinition, - ], - }; - - expect(abi).toMatchObject(expected); - }); - - it("extendType", () => { - const abi = parseSchema(schema2, { - transforms: [ - extendType({ - foo: "bar", - }), - ], - }); - const expected: WrapAbi = { - version: "0.1", - objectTypes: [ - { - ...createObjectDefinition({ type: "MyType" }), - properties: [ - { - ...createScalarPropertyDefinition({ - name: "prop1", - type: "String", - required: true - }), - foo: "bar", - } as PropertyDefinition, - { - ...createScalarPropertyDefinition({ - name: "prop2", - type: "String" - }), - foo: "bar", - }, - ], - foo: "bar", - } as ObjectDefinition, - { - ...createObjectDefinition({ type: "AnotherType" }), - properties: [ - { - ...createScalarPropertyDefinition({ - name: "prop", - type: "String", - required: true - }), - foo: "bar", - } as PropertyDefinition, - ], - foo: "bar", - } as ObjectDefinition, - ], - }; - - expect(abi).toMatchObject(expected); - }); -}); diff --git a/packages/schema/abi/src/__tests__/utils.spec.ts b/packages/schema/abi/src/__tests__/utils.spec.ts deleted file mode 100644 index e2ccd6eb0b..0000000000 --- a/packages/schema/abi/src/__tests__/utils.spec.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { ArrayType, MapType } from "../definitions" -import { parseArrayString, parseMapString } from "../extract/utils" - -describe("Polywrap Schema Abi Utils", () => { - describe("parseArrayString", () => { - it("Parses a simple array", () => { - const arrayString = "[String!]" - const expected: ArrayType = { - kind: "Array", - item: { - required: true, - type: { - kind: "Scalar", - scalar: "String" - } - } - } - - const result = parseArrayString(arrayString, new Map()) - expect(result).toMatchObject(expected) - }) - - it("Parses a ref value", () => { - const arrayString = "[Foo!]" - const expected: ArrayType = { - kind: "Array", - item: { - required: true, - type: { - kind: "Ref", - ref_kind: "Enum", - ref_name: "Foo" - } - } - } - - const result = parseArrayString(arrayString, new Map([["Foo", "Enum"]])) - expect(result).toMatchObject(expected) - }) - - it("Parses a multi-level nested array", () => { - const arrayString = "[[[Foo!]]!]" - const expected: ArrayType = { - kind: "Array", - item: { - required: true, - type: { - kind: "Array", - item: { - required: false, - type: { - kind: "Array", - item: { - required: true, - type: { - kind: "Ref", - ref_kind: "Enum", - ref_name: "Foo" - } - } - } - } - } - } - } - - const result = parseArrayString(arrayString, new Map([["Foo", "Enum"]])) - expect(result).toMatchObject(expected) - }) - }) - - describe("parseMapString", () => { - it("Without a explicitly required key", () => { - const mapString = "Map" - const expected: MapType = { - kind: "Map", - key: { - kind: "Scalar", - scalar: "String" - }, - value: { - required: true, - type: { - kind: "Array", - item: { - required: false, - type: { - kind: "Scalar", - scalar: "String" - } - } - } - } - } - - const result = parseMapString(mapString, new Map()) - expect(result).toMatchObject(expected) - }) - - it("With a explicitly required key", () => { - const mapString = "Map" - const expected: MapType = { - kind: "Map", - key: { - kind: "Scalar", - scalar: "String" - }, - value: { - required: true, - type: { - kind: "Array", - item: { - required: false, - type: { - kind: "Scalar", - scalar: "String" - } - } - } - } - } - - const result = parseMapString(mapString, new Map()) - expect(result).toMatchObject(expected) - }) - - it("With a explicitly required key", () => { - const mapString = "Map" - const expected: MapType = { - kind: "Map", - key: { - kind: "Scalar", - scalar: "String" - }, - value: { - required: true, - type: { - kind: "Array", - item: { - required: false, - type: { - kind: "Scalar", - scalar: "String" - } - } - } - } - } - - const result = parseMapString(mapString, new Map()) - expect(result).toMatchObject(expected) - }) - - it("Trims inner values", () => { - const mapString = "Map< String! , [String]! >" - const expected: MapType = { - kind: "Map", - key: { - kind: "Scalar", - scalar: "String" - }, - value: { - required: true, - type: { - kind: "Array", - item: { - required: false, - type: { - kind: "Scalar", - scalar: "String" - } - } - } - } - } - - const result = parseMapString(mapString, new Map()) - expect(result).toMatchObject(expected) - }) - - it("Parses a complex nested Map", () => { - const mapString = "Map!>" - const expected: MapType = { - kind: "Map", - key: { - kind: "Scalar", - scalar: "String" - }, - value: { - required: true, - type: { - kind: "Map", - key: { - kind: "Scalar", - scalar: "String" - }, - value: { - required: true, - type: { - kind: "Array", - item: { - required: false, - type: { - kind: "Ref", - ref_kind: "Object", - ref_name: "Foo" - } - } - } - } - } - } - } - - const result = parseMapString(mapString, new Map([["Foo", "Object"]])) - expect(result).toMatchObject(expected) - }) - - it("Throws if Map string is invalid", () => { - const mapString = "" - expect(() => parseMapString(mapString, new Map())).toThrow() - }) - }) -}) \ No newline at end of file diff --git a/packages/schema/abi/src/__tests__/validate-directives.spec.ts b/packages/schema/abi/src/__tests__/validate-directives.spec.ts deleted file mode 100644 index 2418bc5d37..0000000000 --- a/packages/schema/abi/src/__tests__/validate-directives.spec.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { parseSchema } from ".."; -import { directiveValidators } from "../validate"; - -const supportedDirectivesSchema = ` -type Module @imports( - types: ["Hey"] -) { - func( - prop: String! - ): String! -} - -type Namespace_Object @imported( - uri: "uri", - namespace: "Namespace", - nativeType: "Object" -) { - prop: String! -} - -type Foo @unknown { - prop: Boolean! @anotherUnknown -} -`; - -const importsDirectiveSchema1 = ` -type Object @imports( - types: ["Hey"] -) { - prop: String! -} -`; - -const importsDirectiveSchema2 = ` -type Object { - prop: String! @imports( - types: ["Hey"] - ) -} -`; - -const importsDirectiveSchema3 = ` -type Module @imports( - typees: ["Hey"] -) { - prop: String! -} -`; - -const importedDirectiveSchema1 = ` -type Namespace_Object @imported( - urri: "uri", - namespace: "Namespace", - nativeType: "Object" -) { - prop: String! -} -`; - -const importedDirectiveSchema2 = ` -type Namespace_Object { - prop: String! @imported( - uri: "uri", - namespace: "Namespace", - nativeType: "Object" - ) -} -`; - -const envDirectiveSchema = ` -type Object { - prop: String! @env(required: true) -} -`; - -describe("Polywrap Schema Directives Validation", () => { - it("supportedDirectives", () => { - expect(() => parseSchema(supportedDirectivesSchema, { - validators: [ - directiveValidators.getSupportedDirectivesValidator - ] - })).toThrow( - /Found the following usages of unsupported directives:\n@unknown,\n@anotherUnknown/gm - ); - }); - - it("importsDirective: Module Object Only", () => { - expect(() => parseSchema(importsDirectiveSchema1, { - validators: [ - directiveValidators.getImportsDirectiveValidator - ] - })).toThrow( - /@imports directive should only be used on Module type definitions, but it is being used on the following ObjectTypeDefinitions:\nObject/gm - ); - }); - - it("importsDirective: Improper Placement", () => { - expect(() => parseSchema(importsDirectiveSchema2, { - validators: [ - directiveValidators.getImportsDirectiveValidator - ] - })).toThrow( - /@imports directive should only be used on Module type definitions, but it is being used in the following location: definitions -> 0 -> fields -> 0 -> directives -> 0/gm - ); - }); - - it("importsDirective: Incorrect Arguments", () => { - expect(() => parseSchema(importsDirectiveSchema3, { - validators: [ - directiveValidators.getImportsDirectiveValidator - ] - })).toThrow( - /@imports directive requires argument 'types' of type \[String!\]!/ - ); - }); - - it("importedDirective: Incorrect Arguments", () => { - expect(() => parseSchema(importedDirectiveSchema1, { - validators: [ - directiveValidators.getImportedDirectiveValidator - ] - })).toThrow( - /@imported directive is missing the following arguments:\n- uri/gm - ); - }); - - it("importedDirective: Improper Placement", () => { - expect(() => parseSchema(importedDirectiveSchema2, { - validators: [ - directiveValidators.getImportedDirectiveValidator - ] - })).toThrow( - /@imported directive should only be used on object or enum type definitions, but it is being used in the following location: definitions -> 0 -> fields -> 0 -> directives -> 0/gm - ); - }); - - it("envDirective: Improper Placement", () => { - expect(() => parseSchema(envDirectiveSchema, { - validators: [ - directiveValidators.getEnvDirectiveValidator - ] - })).toThrow( - /@env directive should only be used on Module method definitions. Found on field \'prop\' of type \'Object\'/gm - ); - }); -}); diff --git a/packages/schema/abi/src/__tests__/validate-types.spec.ts b/packages/schema/abi/src/__tests__/validate-types.spec.ts deleted file mode 100644 index b1f1936bb3..0000000000 --- a/packages/schema/abi/src/__tests__/validate-types.spec.ts +++ /dev/null @@ -1,340 +0,0 @@ -import { parseSchema } from ".."; -import { typeValidators } from "../validate"; - -const typeDefinitions1 = ` -type Subscription { - prop: String! -} -`; - -const typeDefinitions2 = ` -input Custom { - prop: String! -} -`; - -const typeDefinitions3 = ` -interface Custom { - prop: String! -} -`; - -const typeDefinitions4 = ` -type Bar { - prop: String! -} - -type Foo { - foo: String -} - -union FooBar = Bar | Foo -`; - - -const typeDefinitions5 = ` -type Bar { - prop: String! -} - -type Bar { - other: String! -} -`; - -const propertyTypes1 = ` -type Custom { - prop: String! - other: Stringg! -} -`; - -const propertyTypes2 = ` -type Custom { - prop: Bar! - other: Barr! -} - -type Bar { - prop: Int! -} -`; - -const propertyTypes3 = ` -type Custom { - prop: [[Bar]]! - other: [[Barr]]! -} - -type Bar { - prop: Int! -} -`; - -const propertyTypes4 = ` -type Custom { - prop: Bar! -} - -type Bar { - prop: Intt! -} -`; - -const propertyTypes5 = ` -type Module { - method( - prop: Bar! - other: Barr! - ): String! -} - -type Bar { - prop: Int! -} -`; - -const propertyTypes6 = ` -type Module { - method( - prop: Bar! - ): Barr! -} - -type Bar { - prop: Int! -} -`; - -const propertyTypes7 = ` -type Modul { - method( - prop: Bar! - ): String! -} - -type Bar { - prop: Int! -} -`; - -const circularTypes1 = ` -type A { - prop: B! -} - -type B { - prop: A! -} -` - -const circularTypes2 = ` -type A { - prop: B! -} - -type B { - prop: C! -} - -type C { - prop: A! -} -` - -const circularTypes3 = ` -type A { - prop: B! - root: D! -} - -type B { - prop: C! -} - -type C { - prop: A! - root: D! -} - -type D { - prop: B! - root: A! -} -` - -const circularTypes4 = ` -type Module { - method1( - arg1: String! - arg2: String - arg3: Boolean - ): String! -} - -type TestImport_Module @imported( - uri: "testimport.uri.eth", - namespace: "TestImport", - nativeType: "Module" -) { - importedMethod( - str: String! - ): String! - - anotherMethod( - str: String! - ): String! -} -` - -const circularTypes5 = ` -type TestImport_Object @imported( - uri: "testimport.uri.eth", - namespace: "TestImport", - nativeType: "Object" -) { - prop: String! - nested: TestImport_NestedObject! -} - -type TestImport_NestedObject @imported( - uri: "testimport.uri.eth", - namespace: "TestImport", - nativeType: "NestedObject" -) { - foo: [String!]! - circular: TestImport_Object! -} -` - -const circularTypes6 = ` -type A { - prop: B! -} - -type B { - prop: A -} -` - -const circularTypes7 = ` -type A { - prop: A -} -` - -const circularTypes8 = ` -type A { - prop: [A!]! -} -` - -const circularTypes9 = ` -type A { - prop: [A!] -} -` - -const circularTypes10 = ` -type A { - prop: [A] -} -` - -describe("Polywrap Schema Type Validation", () => { - it("typeDefinitions", () => { - const exec = (schema: string) => () => parseSchema(schema, { - validators: [typeValidators.getTypeDefinitionsValidator] - }); - - expect(exec(typeDefinitions1)).toThrow( - /OperationType names \(Mutation, Subscription, Query\) are not allowed./gm - ); - - expect(exec(typeDefinitions2)).toThrow( - /Input type definitions are not supported.\nFound: input Custom {/gm - ); - - expect(exec(typeDefinitions3)).toThrow( - /Interface type definitions are not supported.\nFound: interface Custom {/gm - ); - - expect(exec(typeDefinitions4)).toThrow( - /Union type definitions are not supported.\nFound: union FooBar/gm - ); - - expect(exec(typeDefinitions5)).toThrow( - /Duplicate object type definition found: Bar/gm - ); - }); - - it("propertyTypes", () => { - const exec = (schema: string) => () => parseSchema(schema, { - validators: [typeValidators.getPropertyTypesValidator] - }); - - expect(exec(propertyTypes1)).toThrow( - /Unknown property type found: type Custom { other: Stringg }/gm - ); - - expect(exec(propertyTypes2)).toThrow( - /Unknown property type found: type Custom { other: Barr }/gm - ); - - expect(exec(propertyTypes3)).toThrow( - /Unknown property type found: type Custom { other: Barr }/gm - ); - - expect(exec(propertyTypes4)).toThrow( - /Unknown property type found: type Bar { prop: Intt }/gm - ); - - expect(exec(propertyTypes5)).toThrow( - /Unknown property type found: type Module { method: Barr }/gm - ); - - expect(exec(propertyTypes6)).toThrow( - /Unknown property type found: type Module { method: Barr }/gm - ); - - expect(exec(propertyTypes7)).toThrow( - /Methods can only be defined on module types \(Module\)\.\nFound: type Modul { method\(prop\) }/gm - ); - }) - - it("Circular type definitions", () => { - const exec = (schema: string) => () => parseSchema(schema, { - validators: [typeValidators.getCircularDefinitionsValidator] - }) - - expect(exec(circularTypes1)).toThrow( - /Graphql cycles are not supported. \nFound: \n- { B -\[prop\]-> A -\[prop\]-> B }/gm - ) - - expect(exec(circularTypes2)).toThrow( - /Graphql cycles are not supported. \nFound: \n- { C -\[prop\]-> A -\[prop\]-> B -\[prop\]-> C }/gm - ) - - expect(exec(circularTypes3)).toThrow( - /Graphql cycles are not supported. \nFound: \n- { D -\[prop\]-> B -\[prop\]-> C -\[prop\]-> A -\[root\]-> D }/gm - ) - - //Should ignore Operation Types - expect(exec(circularTypes4)).not.toThrow() - - expect(exec(circularTypes5)).toThrow( - /Graphql cycles are not supported. \nFound: \n- { TestImport_NestedObject -\[circular\]-> TestImport_Object -\[nested\]-> TestImport_NestedObject }/gm - ) - - //Should allow circular references on nullable fields - expect(exec(circularTypes6)).not.toThrow() - - //Should allow recursive reference on nullable fields - expect(exec(circularTypes7)).not.toThrow() - - //Should allow array of recursive references - expect(exec(circularTypes8)).not.toThrow() - expect(exec(circularTypes9)).not.toThrow() - expect(exec(circularTypes10)).not.toThrow() - }) -}); From c360dcc7967b1733508307b9ab68c0d24a3af397 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Tue, 28 Feb 2023 17:07:13 +0100 Subject: [PATCH 79/90] (chore): explicitly stating UnlinkedImportRefs have namespaced names --- packages/schema/abi-types/src/definitions.ts | 2 +- packages/schema/abi/src/AbiImportsLinker.ts | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/schema/abi-types/src/definitions.ts b/packages/schema/abi-types/src/definitions.ts index a7b369dfea..a00a2fed1d 100644 --- a/packages/schema/abi-types/src/definitions.ts +++ b/packages/schema/abi-types/src/definitions.ts @@ -133,7 +133,7 @@ export interface ImportRefType extends Type { export interface UnlinkedImportRefType extends Type { kind: "UnlinkedImportRef"; - ref_name: string; + namespaced_ref_name: string; } export interface OptionalType { diff --git a/packages/schema/abi/src/AbiImportsLinker.ts b/packages/schema/abi/src/AbiImportsLinker.ts index 50b657df3e..9b44195e3f 100644 --- a/packages/schema/abi/src/AbiImportsLinker.ts +++ b/packages/schema/abi/src/AbiImportsLinker.ts @@ -128,18 +128,19 @@ export class AbiImportsLinker implements IAbiImportsLinker { const linkVisitor = new LinkerVisitor({ enter: { UnlinkedImportRefType: (refType) => { - const nameSplit = refType.ref_name.split("_"); + const nameSplit = refType.namespaced_ref_name.split("_"); + // if name isn't namespaced, then it's a local reference if (nameSplit.length < 2) { - const foundDefinitionKind = rootAbiUniqueDefsMap.get(refType.ref_name) + const foundDefinitionKind = rootAbiUniqueDefsMap.get(refType.namespaced_ref_name) if (!foundDefinitionKind) { - throw new Error(`Could not find local definition for ${refType.ref_name}`) + throw new Error(`Could not find local definition for ${refType.namespaced_ref_name}`) } return { kind: "Ref", - ref_name: refType.ref_name, + ref_name: refType.namespaced_ref_name, ref_kind: foundDefinitionKind } } else { @@ -149,13 +150,13 @@ export class AbiImportsLinker implements IAbiImportsLinker { const importAbi = importMap.get(namespacePath.join("_")) if (!importAbi) { - throw new Error(`Could not find import for ${refType.ref_name}`) + throw new Error(`Could not find import for ${refType.namespaced_ref_name}`) } const foundDefinition = this._abiTreeShaker.findReferencedDefinition(importAbi.abi, refName) if (!foundDefinition) { - throw new Error(`Could not find imported definition for ${refType.ref_name}`) + throw new Error(`Could not find imported definition for ${refType.namespaced_ref_name}`) } return { From 943e7d6ec6cb38dc89acec22996930aba0b821c7 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Tue, 28 Feb 2023 17:21:20 +0100 Subject: [PATCH 80/90] (feat): Linker visitor tests --- .../src/__tests__/AbiImportsLinker.spec.ts | 54 ++++++++++++--- .../abi/src/__tests__/LinkerVisitor.spec.ts | 67 +++++++++++++++++++ packages/schema/abi/src/__tests__/mocks.ts | 34 ---------- 3 files changed, 110 insertions(+), 45 deletions(-) create mode 100644 packages/schema/abi/src/__tests__/LinkerVisitor.spec.ts delete mode 100644 packages/schema/abi/src/__tests__/mocks.ts diff --git a/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts index 56c49a59b0..a07d803099 100644 --- a/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts +++ b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts @@ -3,7 +3,39 @@ import { AbiImportsLinker } from "../AbiImportsLinker" import { AbiMerger } from "../AbiMerger" import { AbiSanitizer } from "../AbiSanitizer" import { AbiTreeShaker } from "../AbiTreeShaker" -import { mockFetchers, mockSchemaParser } from "./mocks" + +export const mockSchemaParser = (): SchemaParser => { + return { + parse: async (schema: string): Promise => { + return { + version: "0.2", + } + }, + parseExternalImportStatements: async (schema: string): Promise => { + return [] + }, + parseLocalImportStatements: async (schema: string): Promise => { + return [] + } + } +} + +export const mockFetchers = () => { + return { + external: { + fetch: async (url: string): Promise => { + return { + version: "0.2", + } + } + }, + local: { + fetch: async (path: string): Promise => { + return "" + } + }, + } +} describe("AbiImportsLinker", () => { const merger = new AbiMerger() @@ -24,7 +56,7 @@ describe("AbiImportsLinker", () => { required: true, type: { kind: "UnlinkedImportRef", - ref_name: "OutBar", + namespaced_ref_name: "OutBar", } } ] @@ -39,7 +71,7 @@ describe("AbiImportsLinker", () => { required: true, type: { kind: "UnlinkedImportRef", - ref_name: "OutBar", + namespaced_ref_name: "OutBar", } } ] @@ -575,7 +607,7 @@ describe("AbiImportsLinker", () => { required: true, type: { kind: "UnlinkedImportRef", - ref_name: "EXT_Foo", + namespaced_ref_name: "EXT_Foo", } }, { @@ -584,7 +616,7 @@ describe("AbiImportsLinker", () => { required: true, type: { kind: "UnlinkedImportRef", - ref_name: "Bar", + namespaced_ref_name: "Bar", } } ] @@ -712,7 +744,7 @@ describe("AbiImportsLinker", () => { required: true, type: { kind: "UnlinkedImportRef", - ref_name: "EXT1_Foo", + namespaced_ref_name: "EXT1_Foo", } }, { @@ -721,7 +753,7 @@ describe("AbiImportsLinker", () => { required: true, type: { kind: "UnlinkedImportRef", - ref_name: "EXT1_EXT2_Bar", + namespaced_ref_name: "EXT1_EXT2_Bar", } }, { @@ -730,7 +762,7 @@ describe("AbiImportsLinker", () => { required: true, type: { kind: "UnlinkedImportRef", - ref_name: "EXT1_EXT2_EXT3_Baz", + namespaced_ref_name: "EXT1_EXT2_EXT3_Baz", } }, ] @@ -905,7 +937,7 @@ describe("AbiImportsLinker", () => { required: true, type: { kind: "UnlinkedImportRef", - ref_name: "TransitiveFoo", + namespaced_ref_name: "TransitiveFoo", } } ] @@ -933,7 +965,7 @@ describe("AbiImportsLinker", () => { required: true, type: { kind: "UnlinkedImportRef", - ref_name: "EXT1_Foo", + namespaced_ref_name: "EXT1_Foo", } }, { @@ -942,7 +974,7 @@ describe("AbiImportsLinker", () => { required: true, type: { kind: "UnlinkedImportRef", - ref_name: "Some", + namespaced_ref_name: "Some", } } ] diff --git a/packages/schema/abi/src/__tests__/LinkerVisitor.spec.ts b/packages/schema/abi/src/__tests__/LinkerVisitor.spec.ts new file mode 100644 index 0000000000..5f928095d6 --- /dev/null +++ b/packages/schema/abi/src/__tests__/LinkerVisitor.spec.ts @@ -0,0 +1,67 @@ +import { Abi } from "@polywrap/abi-types"; +import { LinkerVisitor } from "../LinkerVisitor" + +describe("LinkerVisitor", () => { + it("Mutates unlinked import references into import references", () => { + const abi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "TestObject", + props: [ + { + kind: "Property", + name: "testProperty", + required: true, + type: { + kind: "UnlinkedImportRef", + namespaced_ref_name: "TestEnum" + } + } + ] + } + ] + } + + const expectedAbi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "TestObject", + props: [ + { + kind: "Property", + name: "testProperty", + required: true, + type: { + kind: "ImportRef", + ref_name: "TestEnum", + ref_kind: "Enum", + import_id: "0" + } + } + ] + } + ] + } + + const visitor = new LinkerVisitor({ + enter: { + UnlinkedImportRefType: (unlinkedRefType) => { + return { + kind: "ImportRef", + ref_name: unlinkedRefType.namespaced_ref_name, + ref_kind: "Enum", + import_id: "0" + } + } + } + }); + + visitor.visit(abi) + + expect(abi).toEqual(expectedAbi) + }) +}) \ No newline at end of file diff --git a/packages/schema/abi/src/__tests__/mocks.ts b/packages/schema/abi/src/__tests__/mocks.ts deleted file mode 100644 index 819e9a36b0..0000000000 --- a/packages/schema/abi/src/__tests__/mocks.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { SchemaParser, Abi, ExternalImportStatement, LocalImportStatement } from "@polywrap/abi-types" - -export const mockSchemaParser = (): SchemaParser => { - return { - parse: async (schema: string): Promise => { - return { - version: "0.2", - } - }, - parseExternalImportStatements: async (schema: string): Promise => { - return [] - }, - parseLocalImportStatements: async (schema: string): Promise => { - return [] - } - } -} - -export const mockFetchers = () => { - return { - external: { - fetch: async (url: string): Promise => { - return { - version: "0.2", - } - } - }, - local: { - fetch: async (path: string): Promise => { - return "" - } - }, - } -} \ No newline at end of file From 714f73d7b56ebfc8325963392563a2b7455d0f92 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Thu, 2 Mar 2023 00:20:48 +0100 Subject: [PATCH 81/90] (feat): add first last transform --- .../schema/abi/src/transforms/addFirstLast.ts | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 packages/schema/abi/src/transforms/addFirstLast.ts diff --git a/packages/schema/abi/src/transforms/addFirstLast.ts b/packages/schema/abi/src/transforms/addFirstLast.ts new file mode 100644 index 0000000000..84ef42e42d --- /dev/null +++ b/packages/schema/abi/src/transforms/addFirstLast.ts @@ -0,0 +1,77 @@ +import { Abi, EnumDef, FunctionDef, ImportedAbi, ObjectDef } from "@polywrap/abi-types"; +import { IAbiVisitor } from "../AbiVisitor"; + +const addFirstLastProps = (items: T[]): T[] => { + return items.map((item, index) => { + const isFirst = index === 0; + const isLast = index === items.length - 1; + + return { + ...item, + isFirst, + isLast, + }; + }); +} + +export const addFirstLast: IAbiVisitor = { + Abi: (abi: Abi): Abi => { + if (abi.functions) { + abi.functions = addFirstLastProps(abi.functions); + } + + if (abi.objects) { + abi.objects = addFirstLastProps(abi.objects); + } + + if (abi.enums) { + abi.enums = addFirstLastProps(abi.enums); + } + + if (abi.imports) { + abi.imports = addFirstLastProps(abi.imports); + } + + return abi; + }, + Import: (abi: ImportedAbi): ImportedAbi => { + if (abi.functions) { + abi.functions = addFirstLastProps(abi.functions); + } + + if (abi.objects) { + abi.objects = addFirstLastProps(abi.objects); + } + + if (abi.enums) { + abi.enums = addFirstLastProps(abi.enums); + } + + if (abi.imports) { + abi.imports = addFirstLastProps(abi.imports); + } + + return abi; + }, + ObjectDef: (objectDef: ObjectDef): ObjectDef => { + if (objectDef.props) { + objectDef.props = addFirstLastProps(objectDef.props); + } + + return objectDef; + }, + EnumDef: (enumDef: EnumDef): EnumDef => { + if (enumDef.constants) { + enumDef.constants = addFirstLastProps(enumDef.constants); + } + + return enumDef; + }, + FunctionDef: (functionDef: FunctionDef): FunctionDef => { + if (functionDef.args) { + functionDef.args = addFirstLastProps(functionDef.args); + } + + return functionDef; + } +} \ No newline at end of file From c6b219c352685de9df7ae2fdb4255753e61c07d9 Mon Sep 17 00:00:00 2001 From: Nestor Amesty Date: Thu, 2 Mar 2023 01:04:58 +0100 Subject: [PATCH 82/90] (chore): moved transforms to bind package --- packages/schema/abi/src/index.ts | 1 + .../schema/abi/src/transforms/addFirstLast.ts | 77 ------------------ packages/schema/bind/package.json | 3 +- .../src/bindings/assemblyscript/wasm/index.ts | 17 +++- .../bind/src/transforms/addFirstLast.ts | 79 +++++++++++++++++++ .../schema/bind/src/transforms/hasImports.ts | 37 +++++++++ packages/schema/bind/src/transforms/index.ts | 2 + 7 files changed, 135 insertions(+), 81 deletions(-) delete mode 100644 packages/schema/abi/src/transforms/addFirstLast.ts create mode 100644 packages/schema/bind/src/transforms/addFirstLast.ts create mode 100644 packages/schema/bind/src/transforms/hasImports.ts create mode 100644 packages/schema/bind/src/transforms/index.ts diff --git a/packages/schema/abi/src/index.ts b/packages/schema/abi/src/index.ts index db99b927e9..bba899204e 100644 --- a/packages/schema/abi/src/index.ts +++ b/packages/schema/abi/src/index.ts @@ -8,6 +8,7 @@ export * from "./AbiImportsLinker" export * from "./AbiMerger" export * from "./AbiTreeShaker" export * from "./AbiVisitor" +export * from "./AbiSanitizer" interface Args { schema: string diff --git a/packages/schema/abi/src/transforms/addFirstLast.ts b/packages/schema/abi/src/transforms/addFirstLast.ts deleted file mode 100644 index 84ef42e42d..0000000000 --- a/packages/schema/abi/src/transforms/addFirstLast.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Abi, EnumDef, FunctionDef, ImportedAbi, ObjectDef } from "@polywrap/abi-types"; -import { IAbiVisitor } from "../AbiVisitor"; - -const addFirstLastProps = (items: T[]): T[] => { - return items.map((item, index) => { - const isFirst = index === 0; - const isLast = index === items.length - 1; - - return { - ...item, - isFirst, - isLast, - }; - }); -} - -export const addFirstLast: IAbiVisitor = { - Abi: (abi: Abi): Abi => { - if (abi.functions) { - abi.functions = addFirstLastProps(abi.functions); - } - - if (abi.objects) { - abi.objects = addFirstLastProps(abi.objects); - } - - if (abi.enums) { - abi.enums = addFirstLastProps(abi.enums); - } - - if (abi.imports) { - abi.imports = addFirstLastProps(abi.imports); - } - - return abi; - }, - Import: (abi: ImportedAbi): ImportedAbi => { - if (abi.functions) { - abi.functions = addFirstLastProps(abi.functions); - } - - if (abi.objects) { - abi.objects = addFirstLastProps(abi.objects); - } - - if (abi.enums) { - abi.enums = addFirstLastProps(abi.enums); - } - - if (abi.imports) { - abi.imports = addFirstLastProps(abi.imports); - } - - return abi; - }, - ObjectDef: (objectDef: ObjectDef): ObjectDef => { - if (objectDef.props) { - objectDef.props = addFirstLastProps(objectDef.props); - } - - return objectDef; - }, - EnumDef: (enumDef: EnumDef): EnumDef => { - if (enumDef.constants) { - enumDef.constants = addFirstLastProps(enumDef.constants); - } - - return enumDef; - }, - FunctionDef: (functionDef: FunctionDef): FunctionDef => { - if (functionDef.args) { - functionDef.args = addFirstLastProps(functionDef.args); - } - - return functionDef; - } -} \ No newline at end of file diff --git a/packages/schema/bind/package.json b/packages/schema/bind/package.json index 1c4fe4ce08..30889fbd77 100644 --- a/packages/schema/bind/package.json +++ b/packages/schema/bind/package.json @@ -21,7 +21,8 @@ }, "dependencies": { "@polywrap/os-js": "0.10.0-pre.5", - "@polywrap/schema-parse": "0.10.0-pre.5", + "@polywrap/schema-abi": "0.10.0-pre.5", + "@polywrap/abi-types": "0.10.0-pre.5", "@polywrap/wrap-manifest-types-js": "0.10.0-pre.5", "mustache": "4.0.1" }, diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts b/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts index 41105bbe47..1793de5397 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts @@ -4,11 +4,12 @@ import { loadSubTemplates } from "../../utils"; import { BindOptions, BindOutput } from "../../.."; import { - transformAbi, addFirstLast, extendType, toPrefixedGraphQLType, } from "@polywrap/schema-parse"; +import { AbiVisitor, IAbiVisitorEnterAndLeave } from "@polywrap/schema-abi"; +import { Abi } from "@polywrap/abi-types"; import { OutputEntry, readDirectorySync } from "@polywrap/os-js"; import path from "path"; import { WrapAbi } from "@polywrap/wrap-manifest-types-js/src"; @@ -153,8 +154,17 @@ export const generateBinding: GenerateBindingFn = ( return result; }; -function applyTransforms(abi: WrapAbi): WrapAbi { - const transforms = [ +const transformAbi = (abi: Abi, transform: IAbiVisitorEnterAndLeave) => { + const abiClone: Abi = JSON.parse(JSON.stringify(abi)); + const visitor = new AbiVisitor(transform); + + visitor.visit(abiClone); + + return abiClone; +} + +function applyTransforms(abi: Abi): Abi { + const transforms: IAbiVisitorEnterAndLeave[] = [ extendType(Functions), addFirstLast, toPrefixedGraphQLType, @@ -163,5 +173,6 @@ function applyTransforms(abi: WrapAbi): WrapAbi { for (const transform of transforms) { abi = transformAbi(abi, transform); } + return abi; } diff --git a/packages/schema/bind/src/transforms/addFirstLast.ts b/packages/schema/bind/src/transforms/addFirstLast.ts new file mode 100644 index 0000000000..2578d4245f --- /dev/null +++ b/packages/schema/bind/src/transforms/addFirstLast.ts @@ -0,0 +1,79 @@ +import { Abi, EnumDef, FunctionDef, ImportedAbi, ObjectDef } from "@polywrap/abi-types"; +import { IAbiVisitorEnterAndLeave } from "@polywrap/schema-abi"; + +const addFirstLastProps = (items: T[]): T[] => { + return items.map((item, index) => { + const isFirst = index === 0; + const isLast = index === items.length - 1; + + return { + ...item, + isFirst, + isLast, + }; + }); +} + +export const addFirstLast: IAbiVisitorEnterAndLeave = { + enter: { + Abi: (abi: Abi): Abi => { + if (abi.functions) { + abi.functions = addFirstLastProps(abi.functions); + } + + if (abi.objects) { + abi.objects = addFirstLastProps(abi.objects); + } + + if (abi.enums) { + abi.enums = addFirstLastProps(abi.enums); + } + + if (abi.imports) { + abi.imports = addFirstLastProps(abi.imports); + } + + return abi; + }, + Import: (abi: ImportedAbi): ImportedAbi => { + if (abi.functions) { + abi.functions = addFirstLastProps(abi.functions); + } + + if (abi.objects) { + abi.objects = addFirstLastProps(abi.objects); + } + + if (abi.enums) { + abi.enums = addFirstLastProps(abi.enums); + } + + if (abi.imports) { + abi.imports = addFirstLastProps(abi.imports); + } + + return abi; + }, + ObjectDef: (objectDef: ObjectDef): ObjectDef => { + if (objectDef.props) { + objectDef.props = addFirstLastProps(objectDef.props); + } + + return objectDef; + }, + EnumDef: (enumDef: EnumDef): EnumDef => { + if (enumDef.constants) { + enumDef.constants = addFirstLastProps(enumDef.constants); + } + + return enumDef; + }, + FunctionDef: (functionDef: FunctionDef): FunctionDef => { + if (functionDef.args) { + functionDef.args = addFirstLastProps(functionDef.args); + } + + return functionDef; + } + } +} \ No newline at end of file diff --git a/packages/schema/bind/src/transforms/hasImports.ts b/packages/schema/bind/src/transforms/hasImports.ts new file mode 100644 index 0000000000..54f2b3eb86 --- /dev/null +++ b/packages/schema/bind/src/transforms/hasImports.ts @@ -0,0 +1,37 @@ +import { Abi, ImportedAbi } from "@polywrap/abi-types"; +import { IAbiVisitor, IAbiVisitorEnterAndLeave } from "@polywrap/schema-abi"; + +type HasImportsAbi = Abi & { hasImports: boolean }; +type HasImportsImportedAbi = ImportedAbi & { hasImports: boolean }; + +interface HasImportsAbiVisitor extends IAbiVisitor { + Abi: (abi: HasImportsAbi) => HasImportsAbi; + Import: (abi: HasImportsImportedAbi) => HasImportsImportedAbi; +} + +interface IHasImportsAbiVisitorEnterAndLeave extends IAbiVisitorEnterAndLeave { + enter: HasImportsAbiVisitor; +} + +export const hasImports: IHasImportsAbiVisitorEnterAndLeave = { + enter: { + Abi: (abi: HasImportsAbi): HasImportsAbi => { + if (abi.imports && abi.imports.length > 0) { + abi.hasImports = true; + } else { + abi.hasImports = false; + } + + return abi; + }, + Import: (abi: HasImportsImportedAbi): HasImportsImportedAbi => { + if (abi.imports && abi.imports.length > 0) { + abi.hasImports = true; + } else { + abi.hasImports = false; + } + + return abi; + }, + } +} \ No newline at end of file diff --git a/packages/schema/bind/src/transforms/index.ts b/packages/schema/bind/src/transforms/index.ts new file mode 100644 index 0000000000..d782e25e44 --- /dev/null +++ b/packages/schema/bind/src/transforms/index.ts @@ -0,0 +1,2 @@ +export * from "./addFirstLast"; +export * from "./hasImports"; \ No newline at end of file From e49c34e915f4a2aabfc44b7d2eab6d1b4e3340a5 Mon Sep 17 00:00:00 2001 From: namesty Date: Wed, 5 Apr 2023 20:52:48 +0200 Subject: [PATCH 83/90] (chore): updated top level AS schema bindings --- packages/schema/abi/package.json | 3 +- .../src/bindings/assemblyscript/wasm/index.ts | 160 +++++++++++------- .../wasm/templates/entry-ts.mustache | 16 +- .../templates/enum-type/index-ts.mustache | 22 +-- .../imported/enum-type/index-ts.mustache | 22 +-- .../wasm/templates/imported/index-ts.mustache | 14 +- .../imported/module-type/index-ts.mustache | 18 +- .../imported/object-type/index-ts.mustache | 26 +-- .../object-type/serialization-ts.mustache | 28 +-- .../wasm/templates/index-ts.mustache | 73 ++++---- .../interface-type/index-ts.mustache | 23 --- .../templates/module-type/index-ts.mustache | 12 +- .../module-type/serialization-ts.mustache | 4 +- .../templates/module-type/wrapped-ts.mustache | 16 +- .../templates/object-type/index-ts.mustache | 26 +-- .../object-type/serialization-ts.mustache | 28 +-- 16 files changed, 247 insertions(+), 244 deletions(-) delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/interface-type/index-ts.mustache diff --git a/packages/schema/abi/package.json b/packages/schema/abi/package.json index a02e67b6d8..6032f5c18d 100644 --- a/packages/schema/abi/package.json +++ b/packages/schema/abi/package.json @@ -20,8 +20,7 @@ }, "dependencies": { "@polywrap/abi-types": "0.10.0-pre.5", - "@polywrap/wrap-manifest-types-js": "0.10.0-pre.5", - "@polywrap/graphql-schema-parser": "0.10.0-pre.5" + "@polywrap/wrap-manifest-types-js": "0.10.0-pre.5" }, "devDependencies": { "@polywrap/test-cases": "0.10.0-pre.5", diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts b/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts index 1793de5397..bc8150a50c 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts @@ -9,16 +9,66 @@ import { toPrefixedGraphQLType, } from "@polywrap/schema-parse"; import { AbiVisitor, IAbiVisitorEnterAndLeave } from "@polywrap/schema-abi"; -import { Abi } from "@polywrap/abi-types"; +import { Abi, EnumDef, FunctionDef, ObjectDef } from "@polywrap/abi-types"; import { OutputEntry, readDirectorySync } from "@polywrap/os-js"; import path from "path"; -import { WrapAbi } from "@polywrap/wrap-manifest-types-js/src"; const templatesDir = readDirectorySync(path.join(__dirname, "./templates")); const subTemplates = loadSubTemplates(templatesDir.entries); const templatePath = (subpath: string) => path.join(__dirname, "./templates", subpath); +const getImportedObjects = (abi: Abi) => { + const results: ObjectDef[] = [] + + abi.imports?.forEach(importedAbi => { + importedAbi.objects?.forEach(obj => { + results.push(obj) + }) + + getImportedObjects({ + ...importedAbi, + version: "0.2" + }) + }) + + return results +} + +const getImportedEnums = (abi: Abi) => { + const results: EnumDef[] = [] + + abi.imports?.forEach(importedAbi => { + importedAbi.enums?.forEach(obj => { + results.push(obj) + }) + + getImportedEnums({ + ...importedAbi, + version: "0.2" + }) + }) + + return results +} + +const getImportedFuncs = (abi: Abi) => { + const results: FunctionDef[] = [] + + abi.imports?.forEach(importedAbi => { + importedAbi.functions?.forEach(obj => { + results.push(obj) + }) + + getImportedFuncs({ + ...importedAbi, + version: "0.2" + }) + }) + + return results +} + export const generateBinding: GenerateBindingFn = ( options: BindOptions ): BindOutput => { @@ -32,11 +82,11 @@ export const generateBinding: GenerateBindingFn = ( const abi = applyTransforms(options.abi); // Generate object type folders - if (abi.objectTypes) { - for (const objectType of abi.objectTypes) { + if (abi.objects) { + for (const objectType of abi.objects) { output.entries.push({ type: "Directory", - name: objectType.type, + name: objectType.name, data: renderTemplates( templatePath("object-type"), objectType, @@ -48,50 +98,47 @@ export const generateBinding: GenerateBindingFn = ( // Generate imported folder const importEntries: OutputEntry[] = []; + const importedObjects = getImportedObjects(abi) + const importedEnums = getImportedEnums(abi) + const importedFuncs = getImportedFuncs(abi) // Generate imported module type folders - if (abi.importedModuleTypes) { - for (const importedModuleType of abi.importedModuleTypes) { - importEntries.push({ - type: "Directory", - name: importedModuleType.type, - data: renderTemplates( - templatePath("imported/module-type"), - importedModuleType, - subTemplates - ), - }); - } + for (const importedModuleType of importedFuncs) { + importEntries.push({ + type: "Directory", + name: importedModuleType.name, + data: renderTemplates( + templatePath("imported/module-type"), + importedModuleType, + subTemplates + ), + }); } // Generate imported enum type folders - if (abi.importedEnumTypes) { - for (const importedEnumType of abi.importedEnumTypes) { - importEntries.push({ - type: "Directory", - name: importedEnumType.type, - data: renderTemplates( - templatePath("imported/enum-type"), - importedEnumType, - subTemplates - ), - }); - } + for (const importedEnumType of importedEnums) { + importEntries.push({ + type: "Directory", + name: importedEnumType.name, + data: renderTemplates( + templatePath("imported/enum-type"), + importedEnumType, + subTemplates + ), + }); } // Generate imported object type folders - if (abi.importedObjectTypes) { - for (const importedObectType of abi.importedObjectTypes) { - importEntries.push({ - type: "Directory", - name: importedObectType.type, - data: renderTemplates( - templatePath("imported/object-type"), - importedObectType, - subTemplates - ), - }); - } + for (const importedObectType of importedObjects) { + importEntries.push({ + type: "Directory", + name: importedObectType.name, + data: renderTemplates( + templatePath("imported/object-type"), + importedObectType, + subTemplates + ), + }); } if (importEntries.length) { @@ -105,40 +152,27 @@ export const generateBinding: GenerateBindingFn = ( }); } - // Generate interface type folders - if (abi.interfaceTypes) { - for (const interfaceType of abi.interfaceTypes) { + // Generate module type folders + if (abi.functions) { + for (const functionType of abi.functions) { output.entries.push({ type: "Directory", - name: interfaceType.type, + name: functionType.name, data: renderTemplates( - templatePath("interface-type"), - interfaceType, + templatePath("module-type"), + functionType, subTemplates ), }); } } - // Generate module type folders - if (abi.moduleType) { - output.entries.push({ - type: "Directory", - name: abi.moduleType.type, - data: renderTemplates( - templatePath("module-type"), - abi.moduleType, - subTemplates - ), - }); - } - // Generate enum type folders - if (abi.enumTypes) { - for (const enumType of abi.enumTypes) { + if (abi.enums) { + for (const enumType of abi.enums) { output.entries.push({ type: "Directory", - name: enumType.type, + name: enumType.name, data: renderTemplates( templatePath("enum-type"), enumType, diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.mustache index e44fc81f68..c50d2654e1 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.mustache @@ -5,15 +5,13 @@ import { InvokeArgs } from "@polywrap/wasm-as"; -{{#moduleType}} -{{#methods.length}} +{{#functions.length}} import { - {{#methods}} + {{#functions}} {{name}}Wrapped{{^last}},{{/last}} - {{/methods}} + {{/functions}} } from "./{{type}}/wrapped"; -{{/methods.length}} -{{/moduleType}} +{{/functions.length}} export function _wrap_invoke(method_size: u32, args_size: u32): bool { const args: InvokeArgs = wrap_invoke_args( @@ -21,13 +19,11 @@ export function _wrap_invoke(method_size: u32, args_size: u32): bool { args_size ); - {{#moduleType}} - {{#methods}} + {{#functions}} {{^first}}else {{/first}}if (args.method == "{{name}}") { return wrap_invoke(args, {{name}}Wrapped); } - {{/methods}} - {{/moduleType}} + {{/functions}} else { return wrap_invoke(args, null); } diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/enum-type/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/enum-type/index-ts.mustache index c738d94e84..40d000fd72 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/enum-type/index-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/enum-type/index-ts.mustache @@ -1,35 +1,35 @@ -export enum {{#detectKeyword}}{{type}}{{/detectKeyword}} { +export enum {{#detectKeyword}}{{name}}{{/detectKeyword}} { {{#constants}} {{#detectKeyword}}{{.}}{{/detectKeyword}}, {{/constants}} _MAX_ } -export function sanitize{{type}}Value(value: i32): void { - const valid = value >= 0 && value < {{#detectKeyword}}{{type}}{{/detectKeyword}}._MAX_; +export function sanitize{{name}}Value(value: i32): void { + const valid = value >= 0 && value < {{#detectKeyword}}{{name}}{{/detectKeyword}}._MAX_; if (!valid) { - throw new Error("Invalid value for enum '{{#detectKeyword}}{{type}}{{/detectKeyword}}': " + value.toString()); + throw new Error("Invalid value for enum '{{#detectKeyword}}{{name}}{{/detectKeyword}}': " + value.toString()); } } -export function get{{type}}Value(key: string): {{#detectKeyword}}{{type}}{{/detectKeyword}} { +export function get{{name}}Value(key: string): {{#detectKeyword}}{{name}}{{/detectKeyword}} { {{#constants}} if (key == "{{#detectKeyword}}{{.}}{{/detectKeyword}}") { - return {{#detectKeyword}}{{type}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}; + return {{#detectKeyword}}{{name}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}; } {{/constants}} - throw new Error("Invalid key for enum '{{#detectKeyword}}{{type}}{{/detectKeyword}}': " + key); + throw new Error("Invalid key for enum '{{#detectKeyword}}{{name}}{{/detectKeyword}}': " + key); } -export function get{{type}}Key(value: {{#detectKeyword}}{{type}}{{/detectKeyword}}): string { - sanitize{{type}}Value(value); +export function get{{name}}Key(value: {{#detectKeyword}}{{name}}{{/detectKeyword}}): string { + sanitize{{name}}Value(value); switch (value) { {{#constants}} - case {{#detectKeyword}}{{type}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}: return "{{#detectKeyword}}{{.}}{{/detectKeyword}}"; + case {{#detectKeyword}}{{name}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}: return "{{#detectKeyword}}{{.}}{{/detectKeyword}}"; {{/constants}} default: - throw new Error("Invalid value for enum '{{#detectKeyword}}{{type}}{{/detectKeyword}}': " + value.toString()); + throw new Error("Invalid value for enum '{{#detectKeyword}}{{name}}{{/detectKeyword}}': " + value.toString()); } } diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/enum-type/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/enum-type/index-ts.mustache index c738d94e84..40d000fd72 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/enum-type/index-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/enum-type/index-ts.mustache @@ -1,35 +1,35 @@ -export enum {{#detectKeyword}}{{type}}{{/detectKeyword}} { +export enum {{#detectKeyword}}{{name}}{{/detectKeyword}} { {{#constants}} {{#detectKeyword}}{{.}}{{/detectKeyword}}, {{/constants}} _MAX_ } -export function sanitize{{type}}Value(value: i32): void { - const valid = value >= 0 && value < {{#detectKeyword}}{{type}}{{/detectKeyword}}._MAX_; +export function sanitize{{name}}Value(value: i32): void { + const valid = value >= 0 && value < {{#detectKeyword}}{{name}}{{/detectKeyword}}._MAX_; if (!valid) { - throw new Error("Invalid value for enum '{{#detectKeyword}}{{type}}{{/detectKeyword}}': " + value.toString()); + throw new Error("Invalid value for enum '{{#detectKeyword}}{{name}}{{/detectKeyword}}': " + value.toString()); } } -export function get{{type}}Value(key: string): {{#detectKeyword}}{{type}}{{/detectKeyword}} { +export function get{{name}}Value(key: string): {{#detectKeyword}}{{name}}{{/detectKeyword}} { {{#constants}} if (key == "{{#detectKeyword}}{{.}}{{/detectKeyword}}") { - return {{#detectKeyword}}{{type}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}; + return {{#detectKeyword}}{{name}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}; } {{/constants}} - throw new Error("Invalid key for enum '{{#detectKeyword}}{{type}}{{/detectKeyword}}': " + key); + throw new Error("Invalid key for enum '{{#detectKeyword}}{{name}}{{/detectKeyword}}': " + key); } -export function get{{type}}Key(value: {{#detectKeyword}}{{type}}{{/detectKeyword}}): string { - sanitize{{type}}Value(value); +export function get{{name}}Key(value: {{#detectKeyword}}{{name}}{{/detectKeyword}}): string { + sanitize{{name}}Value(value); switch (value) { {{#constants}} - case {{#detectKeyword}}{{type}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}: return "{{#detectKeyword}}{{.}}{{/detectKeyword}}"; + case {{#detectKeyword}}{{name}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}: return "{{#detectKeyword}}{{.}}{{/detectKeyword}}"; {{/constants}} default: - throw new Error("Invalid value for enum '{{#detectKeyword}}{{type}}{{/detectKeyword}}': " + value.toString()); + throw new Error("Invalid value for enum '{{#detectKeyword}}{{name}}{{/detectKeyword}}': " + value.toString()); } } diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.mustache index 6f31291e32..4b89d71758 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.mustache @@ -1,9 +1,11 @@ -{{#importedModuleTypes}} +{{#imports}} +{{#functions}} export * from "./{{type}}"; -{{/importedModuleTypes}} -{{#importedObjectTypes}} +{{/functions}} +{{#objects}} export * from "./{{type}}"; -{{/importedObjectTypes}} -{{#importedEnumTypes}} +{{/objects}} +{{#enums}} export * from "./{{type}}"; -{{/importedEnumTypes}} +{{/enums}} +{{/imports}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/index-ts.mustache index a1b0abaa22..989b562264 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/index-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/index-ts.mustache @@ -7,23 +7,23 @@ import { JSON, Result } from "@polywrap/wasm-as"; -{{#methods.length}} +{{#functions.length}} import { - {{#methods}} + {{#functions}} serialize{{name}}Args, deserialize{{name}}Result, Args_{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/methods}} + {{/functions}} } from "./serialization"; -{{/methods.length}} +{{/functions.length}} import * as Types from "../.."; {{^isInterface}} -export class {{#detectKeyword}}{{type}}{{/detectKeyword}} { +export class {{#detectKeyword}}{{name}}{{/detectKeyword}} { public static uri: string = "{{uri}}"; - {{#methods}} + {{#functions}} public static {{name}}( args: Args_{{#detectKeyword}}{{name}}{{/detectKeyword}} ): Result<{{#return}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/return}}, string> { @@ -47,7 +47,7 @@ export class {{#detectKeyword}}{{type}}{{/detectKeyword}} { {{^last}} {{/last}} - {{/methods}} + {{/functions}} } {{/isInterface}} {{#isInterface}} @@ -61,7 +61,7 @@ export class {{#detectKeyword}}{{type}}{{/detectKeyword}} { this.uri = uri; } - {{#methods}} + {{#functions}} public {{name}}( args: Args_{{#detectKeyword}}{{name}}{{/detectKeyword}} ): Result<{{#return}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/return}}, string> { @@ -86,6 +86,6 @@ export class {{#detectKeyword}}{{type}}{{/detectKeyword}} { {{^last}} {{/last}} - {{/methods}} + {{/functions}} } {{/isInterface}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/index-ts.mustache index 3d914e5ae1..3692269327 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/index-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/index-ts.mustache @@ -7,14 +7,14 @@ import { JSON } from "@polywrap/wasm-as"; import { - serialize{{type}}, - deserialize{{type}}, - write{{type}}, - read{{type}} + serialize{{name}}, + deserialize{{name}}, + write{{name}}, + read{{name}} } from "./serialization"; import * as Types from "../.."; -export class {{#detectKeyword}}{{type}}{{/detectKeyword}} { +export class {{#detectKeyword}}{{name}}{{/detectKeyword}} { public static uri: string = "{{uri}}"; @@ -22,19 +22,19 @@ export class {{#detectKeyword}}{{type}}{{/detectKeyword}} { {{#detectKeyword}}{{name}}{{/detectKeyword}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}}; {{/properties}} - static toBuffer(type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): ArrayBuffer { - return serialize{{type}}(type); + static toBuffer(type: {{#detectKeyword}}{{name}}{{/detectKeyword}}): ArrayBuffer { + return serialize{{name}}(type); } - static fromBuffer(buffer: ArrayBuffer): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - return deserialize{{type}}(buffer); + static fromBuffer(buffer: ArrayBuffer): {{#detectKeyword}}{{name}}{{/detectKeyword}} { + return deserialize{{name}}(buffer); } - static write(writer: Write, type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): void { - write{{type}}(writer, type); + static write(writer: Write, type: {{#detectKeyword}}{{name}}{{/detectKeyword}}): void { + write{{name}}(writer, type); } - static read(reader: Read): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - return read{{type}}(reader); + static read(reader: Read): {{#detectKeyword}}{{name}}{{/detectKeyword}} { + return read{{name}}(reader); } } diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/serialization-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/serialization-ts.mustache index c2b2bb40ec..4e6ca0ae10 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/serialization-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/serialization-ts.mustache @@ -1,19 +1,19 @@ {{> serialization_imports}} -import { {{#detectKeyword}}{{type}}{{/detectKeyword}} } from "./"; +import { {{#detectKeyword}}{{name}}{{/detectKeyword}} } from "./"; import * as Types from "../.."; -export function serialize{{type}}(type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): ArrayBuffer { - const sizerContext: Context = new Context("Serializing (sizing) imported object-type: {{type}}"); +export function serialize{{name}}(type: {{#detectKeyword}}{{name}}{{/detectKeyword}}): ArrayBuffer { + const sizerContext: Context = new Context("Serializing (sizing) imported object-type: {{name}}"); const sizer = new WriteSizer(sizerContext); - write{{type}}(sizer, type); + write{{name}}(sizer, type); const buffer = new ArrayBuffer(sizer.length); - const encoderContext: Context = new Context("Serializing (encoding) import object-type: {{type}}"); + const encoderContext: Context = new Context("Serializing (encoding) import object-type: {{name}}"); const encoder = new WriteEncoder(buffer, sizer, encoderContext); - write{{type}}(encoder, type); + write{{name}}(encoder, type); return buffer; } -export function write{{type}}(writer: Write, type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): void { +export function write{{name}}(writer: Write, type: {{#detectKeyword}}{{name}}{{/detectKeyword}}): void { {{#properties.length}} writer.writeMapLength({{properties.length}}); {{/properties.length}} @@ -40,11 +40,11 @@ export function write{{type}}(writer: Write, type: {{#detectKeyword}}{{type}}{{/ {{/map}} {{#object}} {{#required}} - Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}.write(writer, type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); + Types.{{#detectKeyword}}{{name}}{{/detectKeyword}}.write(writer, type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); {{/required}} {{^required}} if (type.{{#detectKeyword}}{{name}}{{/detectKeyword}}) { - Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}.write(writer, type.{{#detectKeyword}}{{name}}{{/detectKeyword}} as Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}); + Types.{{#detectKeyword}}{{name}}{{/detectKeyword}}.write(writer, type.{{#detectKeyword}}{{name}}{{/detectKeyword}} as Types.{{#detectKeyword}}{{name}}{{/detectKeyword}}); } else { writer.writeNil(); } @@ -62,13 +62,13 @@ export function write{{type}}(writer: Write, type: {{#detectKeyword}}{{type}}{{/ {{/properties}} } -export function deserialize{{type}}(buffer: ArrayBuffer): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - const context: Context = new Context("Deserializing imported object-type {{type}}"); +export function deserialize{{name}}(buffer: ArrayBuffer): {{#detectKeyword}}{{name}}{{/detectKeyword}} { + const context: Context = new Context("Deserializing imported object-type {{name}}"); const reader = new ReadDecoder(buffer, context); - return read{{type}}(reader); + return read{{name}}(reader); } -export function read{{type}}(reader: Read): {{#detectKeyword}}{{type}}{{/detectKeyword}} { +export function read{{name}}(reader: Read): {{#detectKeyword}}{{name}}{{/detectKeyword}} { let numFields = reader.readMapLength(); {{#properties}} @@ -136,7 +136,7 @@ export function read{{type}}(reader: Read): {{#detectKeyword}}{{type}}{{/detectK {{#object}} if (!_{{name}} || !_{{name}}Set) { {{/object}} - throw new Error(reader.context().printWithContext("Missing required property: '{{name}}: {{type}}'")); + throw new Error(reader.context().printWithContext("Missing required property: '{{name}}: {{name}}'")); } {{/required}} {{/properties}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.mustache index be38308fd7..54e5b2ce96 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.mustache @@ -1,46 +1,41 @@ -{{#moduleType}} -{{#methods.length}} +{{#functions.length}} import { - {{#methods}} + {{#functions}} Args_{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/methods}} -} from "./{{type}}"; -{{/methods.length}} -{{/moduleType}} -{{#moduleType}} -{{#methods.length}} + {{/functions}} +} from "./{{name}}"; +{{/functions.length}} +{{#functions.length}} export { - {{#methods}} + {{#functions}} Args_{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/methods}} + {{/functions}} }; -{{/methods.length}} -{{/moduleType}} -{{#objectTypes}} -export { {{#detectKeyword}}{{type}}{{/detectKeyword}} } from "./{{type}}"; -{{/objectTypes}} -{{#enumTypes}} +{{/functions.length}} +{{#objects}} +export { {{#detectKeyword}}{{name}}{{/detectKeyword}} } from "./{{name}}"; +{{/objects}} +{{#enums}} export { - {{#detectKeyword}}{{type}}{{/detectKeyword}}, - get{{type}}Key, - get{{type}}Value, - sanitize{{type}}Value -} from "./{{type}}"; -{{/enumTypes}} -{{#importedModuleTypes}} -export { {{#detectKeyword}}{{type}}{{/detectKeyword}} } from "./imported/{{type}}"; -{{/importedModuleTypes}} -{{#importedObjectTypes}} -export { {{#detectKeyword}}{{type}}{{/detectKeyword}} } from "./imported/{{type}}"; -{{/importedObjectTypes}} -{{#importedEnumTypes}} + {{#detectKeyword}}{{name}}{{/detectKeyword}}, + get{{name}}Key, + get{{name}}Value, + sanitize{{name}}Value +} from "./{{name}}"; +{{/enums}} +{{#imports}} +{{#functions}} +export { {{#detectKeyword}}{{name}}{{/detectKeyword}} } from "./imported/{{name}}"; +{{/functions}} +{{#objects}} +export { {{#detectKeyword}}{{name}}{{/detectKeyword}} } from "./imported/{{name}}"; +{{/objects}} +{{#enums}} export { - {{#detectKeyword}}{{type}}{{/detectKeyword}}, - get{{type}}Key, - get{{type}}Value, - sanitize{{type}}Value -} from "./imported/{{type}}"; -{{/importedEnumTypes}} -{{#interfaceTypes}} -export { {{#detectKeyword}}{{namespace}}{{/detectKeyword}} } from "./{{namespace}}"; -{{/interfaceTypes}} + {{#detectKeyword}}{{name}}{{/detectKeyword}}, + get{{name}}Key, + get{{name}}Value, + sanitize{{name}}Value +} from "./imported/{{name}}"; +{{/enums}} +{{/imports}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/interface-type/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/interface-type/index-ts.mustache deleted file mode 100644 index 25239251f4..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/interface-type/index-ts.mustache +++ /dev/null @@ -1,23 +0,0 @@ -{{#capabilities}} -{{#getImplementations}} -{{#enabled}} -import { - wrap_getImplementations -} from "@polywrap/wasm-as"; -{{/enabled}} -{{/getImplementations}} -{{/capabilities}} - -export class {{#detectKeyword}}{{namespace}}{{/detectKeyword}} { - static uri: string = "{{uri}}" - - {{#capabilities}} - {{#getImplementations}} - {{#enabled}} - public static getImplementations(): string[] { - return wrap_getImplementations(this.uri); - } - {{/enabled}} - {{/getImplementations}} - {{/capabilities}} -} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/index-ts.mustache index cadfcf855f..3b41b0fb64 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/index-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/index-ts.mustache @@ -1,13 +1,13 @@ -{{#methods.length}} +{{#functions.length}} import { - {{#methods}} + {{#functions}} Args_{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/methods}} + {{/functions}} } from "./serialization"; export { - {{#methods}} + {{#functions}} Args_{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/methods}} + {{/functions}} }; -{{/methods.length}} \ No newline at end of file +{{/functions.length}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/serialization-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/serialization-ts.mustache index 415a33a2fb..0399298afe 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/serialization-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/serialization-ts.mustache @@ -1,7 +1,7 @@ {{> serialization_imports}} import * as Types from ".."; -{{#methods}} +{{#functions}} export class Args_{{#detectKeyword}}{{name}}{{/detectKeyword}} { {{#arguments}} {{#detectKeyword}}{{name}}{{/detectKeyword}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}}; @@ -245,4 +245,4 @@ export function deserialize{{name}}Result(buffer: ArrayBuffer): {{#return}}{{#to {{^last}} {{/last}} -{{/methods}} +{{/functions}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.mustache index c7fa81c62e..b45f7dcab7 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.mustache @@ -1,19 +1,19 @@ -{{#methods.length}} +{{#functions.length}} import { - {{#methods}} + {{#functions}} {{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/methods}} + {{/functions}} } from "../../index"; import { - {{#methods}} + {{#functions}} deserialize{{name}}Args, serialize{{name}}Result{{^last}},{{/last}} - {{/methods}} + {{/functions}} } from "./serialization"; -{{/methods.length}} +{{/functions.length}} import * as Types from ".."; -{{#methods}} +{{#functions}} export function {{name}}Wrapped(argsBuf: ArrayBuffer): ArrayBuffer { {{#arguments.length}} const args = deserialize{{name}}Args(argsBuf); @@ -31,4 +31,4 @@ export function {{name}}Wrapped(argsBuf: ArrayBuffer): ArrayBuffer { {{^last}} {{/last}} -{{/methods}} +{{/functions}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.mustache index 8199149c0d..cc49d95ac4 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.mustache @@ -7,31 +7,31 @@ import { JSON } from "@polywrap/wasm-as"; import { - serialize{{type}}, - deserialize{{type}}, - write{{type}}, - read{{type}} + serialize{{name}}, + deserialize{{name}}, + write{{name}}, + read{{name}} } from "./serialization"; import * as Types from ".."; -export class {{#detectKeyword}}{{type}}{{/detectKeyword}} { +export class {{#detectKeyword}}{{name}}{{/detectKeyword}} { {{#properties}} {{#detectKeyword}}{{name}}{{/detectKeyword}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}}; {{/properties}} - static toBuffer(type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): ArrayBuffer { - return serialize{{type}}(type); + static toBuffer(type: {{#detectKeyword}}{{name}}{{/detectKeyword}}): ArrayBuffer { + return serialize{{name}}(type); } - static fromBuffer(buffer: ArrayBuffer): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - return deserialize{{type}}(buffer); + static fromBuffer(buffer: ArrayBuffer): {{#detectKeyword}}{{name}}{{/detectKeyword}} { + return deserialize{{name}}(buffer); } - static write(writer: Write, type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): void { - write{{type}}(writer, type); + static write(writer: Write, type: {{#detectKeyword}}{{name}}{{/detectKeyword}}): void { + write{{name}}(writer, type); } - static read(reader: Read): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - return read{{type}}(reader); + static read(reader: Read): {{#detectKeyword}}{{name}}{{/detectKeyword}} { + return read{{name}}(reader); } } diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/serialization-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/serialization-ts.mustache index 831b72a573..a088c024db 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/serialization-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/serialization-ts.mustache @@ -1,19 +1,19 @@ {{> serialization_imports}} -import { {{#detectKeyword}}{{type}}{{/detectKeyword}} } from "./"; +import { {{#detectKeyword}}{{name}}{{/detectKeyword}} } from "./"; import * as Types from ".."; -export function serialize{{type}}(type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): ArrayBuffer { - const sizerContext: Context = new Context("Serializing (sizing) object-type: {{type}}"); +export function serialize{{name}}(type: {{#detectKeyword}}{{name}}{{/detectKeyword}}): ArrayBuffer { + const sizerContext: Context = new Context("Serializing (sizing) object-type: {{name}}"); const sizer = new WriteSizer(sizerContext); - write{{type}}(sizer, type); + write{{name}}(sizer, type); const buffer = new ArrayBuffer(sizer.length); - const encoderContext: Context = new Context("Serializing (encoding) object-type: {{type}}"); + const encoderContext: Context = new Context("Serializing (encoding) object-type: {{name}}"); const encoder = new WriteEncoder(buffer, sizer, encoderContext); - write{{type}}(encoder, type); + write{{name}}(encoder, type); return buffer; } -export function write{{type}}(writer: Write, type: {{#detectKeyword}}{{type}}{{/detectKeyword}}): void { +export function write{{name}}(writer: Write, type: {{#detectKeyword}}{{name}}{{/detectKeyword}}): void { {{#properties.length}} writer.writeMapLength({{properties.length}}); {{/properties.length}} @@ -40,11 +40,11 @@ export function write{{type}}(writer: Write, type: {{#detectKeyword}}{{type}}{{/ {{/map}} {{#object}} {{#required}} - Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}.write(writer, type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); + Types.{{#detectKeyword}}{{name}}{{/detectKeyword}}.write(writer, type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); {{/required}} {{^required}} if (type.{{#detectKeyword}}{{name}}{{/detectKeyword}}) { - Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}.write(writer, type.{{#detectKeyword}}{{name}}{{/detectKeyword}} as Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}); + Types.{{#detectKeyword}}{{name}}{{/detectKeyword}}.write(writer, type.{{#detectKeyword}}{{name}}{{/detectKeyword}} as Types.{{#detectKeyword}}{{name}}{{/detectKeyword}}); } else { writer.writeNil(); } @@ -62,13 +62,13 @@ export function write{{type}}(writer: Write, type: {{#detectKeyword}}{{type}}{{/ {{/properties}} } -export function deserialize{{type}}(buffer: ArrayBuffer): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - const context: Context = new Context("Deserializing object-type {{type}}"); +export function deserialize{{name}}(buffer: ArrayBuffer): {{#detectKeyword}}{{name}}{{/detectKeyword}} { + const context: Context = new Context("Deserializing object-type {{name}}"); const reader = new ReadDecoder(buffer, context); - return read{{type}}(reader); + return read{{name}}(reader); } -export function read{{type}}(reader: Read): {{#detectKeyword}}{{type}}{{/detectKeyword}} { +export function read{{name}}(reader: Read): {{#detectKeyword}}{{name}}{{/detectKeyword}} { let numFields = reader.readMapLength(); {{#properties}} @@ -136,7 +136,7 @@ export function read{{type}}(reader: Read): {{#detectKeyword}}{{type}}{{/detectK {{#object}} if (!_{{name}} || !_{{name}}Set) { {{/object}} - throw new Error(reader.context().printWithContext("Missing required property: '{{name}}: {{type}}'")); + throw new Error(reader.context().printWithContext("Missing required property: '{{name}}: {{name}}'")); } {{/required}} {{/properties}} From a19e838e71954721d3cdbf1622b9de1cde98728e Mon Sep 17 00:00:00 2001 From: namesty Date: Thu, 6 Apr 2023 05:49:13 +0200 Subject: [PATCH 84/90] (feat): changed Mustache templates for Handlebars --- packages/schema/bind/package.json | 3 +- .../wasm/templates/deserialize_array.hbs | 23 +++ .../wasm/templates/deserialize_array.mustache | 23 --- .../wasm/templates/deserialize_enum.hbs | 25 +++ .../wasm/templates/deserialize_enum.mustache | 26 ---- .../wasm/templates/deserialize_map_value.hbs | 23 +++ .../templates/deserialize_map_value.mustache | 23 --- .../wasm/templates/deserialize_object.hbs | 8 + .../templates/deserialize_object.mustache | 9 -- .../{entry-ts.mustache => entry-ts.hbs} | 27 ++-- .../wasm/templates/enum-type/index-ts.hbs | 35 +++++ .../templates/enum-type/index-ts.mustache | 35 ----- .../templates/imported/enum-type/index-ts.hbs | 35 +++++ .../imported/enum-type/index-ts.mustache | 35 ----- .../wasm/templates/imported/index-ts.hbs | 11 ++ .../wasm/templates/imported/index-ts.mustache | 11 -- .../{index-ts.mustache => index-ts.hbs} | 36 ++--- ...ation-ts.mustache => serialization-ts.hbs} | 0 .../imported/object-type/index-ts.hbs | 40 +++++ .../imported/object-type/index-ts.mustache | 40 ----- .../wasm/templates/index-ts.hbs | 56 +++++++ .../wasm/templates/index-ts.mustache | 41 ----- .../wasm/templates/module-type/index-ts.hbs | 13 ++ .../templates/module-type/index-ts.mustache | 13 -- ...ation-ts.mustache => serialization-ts.hbs} | 0 .../wasm/templates/module-type/wrapped-ts.hbs | 34 ++++ .../templates/module-type/wrapped-ts.mustache | 34 ---- .../wasm/templates/object-type/index-ts.hbs | 37 +++++ .../templates/object-type/index-ts.mustache | 37 ----- ...ation-ts.mustache => serialization-ts.hbs} | 0 ...rts.mustache => serialization_imports.hbs} | 0 .../wasm/templates/serialize_array.hbs | 21 +++ .../wasm/templates/serialize_array.mustache | 21 --- .../wasm/templates/serialize_enum.hbs | 5 + .../wasm/templates/serialize_enum.mustache | 6 - .../wasm/templates/serialize_map_value.hbs | 33 ++++ .../templates/serialize_map_value.mustache | 35 ----- .../wasm/templates/serialize_object.hbs | 9 ++ .../wasm/templates/serialize_object.mustache | 10 -- packages/schema/bind/src/index.ts | 4 +- .../bind/src/transforms/toGraphQLType.ts | 145 ++++++++++++++++++ 41 files changed, 586 insertions(+), 436 deletions(-) create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_array.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_array.mustache create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_enum.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_enum.mustache create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_map_value.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_map_value.mustache create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_object.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_object.mustache rename packages/schema/bind/src/bindings/assemblyscript/wasm/templates/{entry-ts.mustache => entry-ts.hbs} (60%) create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/enum-type/index-ts.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/enum-type/index-ts.mustache create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/enum-type/index-ts.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/enum-type/index-ts.mustache create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.mustache rename packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/{index-ts.mustache => index-ts.hbs} (75%) rename packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/{serialization-ts.mustache => serialization-ts.hbs} (100%) create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/index-ts.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/index-ts.mustache create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.mustache create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/index-ts.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/index-ts.mustache rename packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/{serialization-ts.mustache => serialization-ts.hbs} (100%) create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.mustache create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.mustache rename packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/{serialization-ts.mustache => serialization-ts.hbs} (100%) rename packages/schema/bind/src/bindings/assemblyscript/wasm/templates/{serialization_imports.mustache => serialization_imports.hbs} (100%) create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_array.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_array.mustache create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_enum.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_enum.mustache create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_map_value.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_map_value.mustache create mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_object.hbs delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_object.mustache create mode 100644 packages/schema/bind/src/transforms/toGraphQLType.ts diff --git a/packages/schema/bind/package.json b/packages/schema/bind/package.json index 30889fbd77..08d9566360 100644 --- a/packages/schema/bind/package.json +++ b/packages/schema/bind/package.json @@ -24,13 +24,12 @@ "@polywrap/schema-abi": "0.10.0-pre.5", "@polywrap/abi-types": "0.10.0-pre.5", "@polywrap/wrap-manifest-types-js": "0.10.0-pre.5", - "mustache": "4.0.1" + "handlebars": "4.7.7" }, "devDependencies": { "@polywrap/test-cases": "0.10.0-pre.5", "@types/jest": "26.0.8", "@types/lodash": "4.14.178", - "@types/mustache": "4.0.1", "@types/prettier": "2.6.0", "copyfiles": "2.4.1", "jest": "26.6.3", diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_array.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_array.hbs new file mode 100644 index 0000000000..45d7acdded --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_array.hbs @@ -0,0 +1,23 @@ +{{#if scalar}} + return reader.read{{toMsgPack}}{{toGraphQLType}}(); +{{/if}} +{{#if array}} + return reader.read{{toMsgPack}}{{toGraphQLType}}((reader: Read): {{#item}}{{toWasm}}{{toGraphQLType}}{{/item}} => { + {{> deserialize_array}} + }); +{{/if}} +{{#if map}} + return reader.read{{toMsgPack}}{{toGraphQLType}}((reader: Read): {{#key}}{{toWasm}}{{toGraphQLType}}{{/key}} => { + return reader.read{{toMsgPack}}{{toGraphQLType}}(); + }, (reader: Read): {{#value}}{{toWasm}}{{toGraphQLType}}{{/value}} => { + {{> deserialize_map_value}} + }); +{{/if}} +{{#if enum}} + {{> deserialize_enum}} + return value; +{{/if}} +{{#if object}} + {{> deserialize_object}} + return object; +{{/if}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_array.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_array.mustache deleted file mode 100644 index 5ee4a8a43b..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_array.mustache +++ /dev/null @@ -1,23 +0,0 @@ -{{#scalar}} -return reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(); -{{/scalar}} -{{#array}} -return reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}((reader: Read): {{#item}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/item}} => { - {{> deserialize_array}} -}); -{{/array}} -{{#map}} -return reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}((reader: Read): {{#key}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/key}} => { - return reader.read{{#key}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/key}}(); -}, (reader: Read): {{#value}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/value}} => { - {{> deserialize_map_value}} -}); -{{/map}} -{{#enum}} -{{> deserialize_enum}} -return value; -{{/enum}} -{{#object}} -{{> deserialize_object}} -return object; -{{/object}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_enum.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_enum.hbs new file mode 100644 index 0000000000..d0bd7ad094 --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_enum.hbs @@ -0,0 +1,25 @@ +{{#if required}} + let value: Types.{{detectKeyword type}}; + if (reader.isNextString()) { + value = Types.get{{type}}Value(reader.readString()); + } else { + value = reader.readInt32(); + Types.sanitize{{type}}Value(value); + } +{{else}} + let value: Box | null; + if (!reader.isNextNil()) { + if (reader.isNextString()) { + value = Box.from( + Types.get{{type}}Value(reader.readString()) + ); + } else { + value = Box.from( + reader.readInt32() + ); + Types.sanitize{{type}}Value(value.unwrap()); + } + } else { + value = null; + } +{{/if}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_enum.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_enum.mustache deleted file mode 100644 index 6702368f21..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_enum.mustache +++ /dev/null @@ -1,26 +0,0 @@ -{{#required}} -let value: Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}; -if (reader.isNextString()) { - value = Types.get{{type}}Value(reader.readString()); -} else { - value = reader.readInt32(); - Types.sanitize{{type}}Value(value); -} -{{/required}} -{{^required}} -let value: Box | null; -if (!reader.isNextNil()) { - if (reader.isNextString()) { - value = Box.from( - Types.get{{type}}Value(reader.readString()) - ); - } else { - value = Box.from( - reader.readInt32() - ); - Types.sanitize{{type}}Value(value.unwrap()); - } -} else { - value = null; -} -{{/required}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_map_value.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_map_value.hbs new file mode 100644 index 0000000000..45d7acdded --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_map_value.hbs @@ -0,0 +1,23 @@ +{{#if scalar}} + return reader.read{{toMsgPack}}{{toGraphQLType}}(); +{{/if}} +{{#if array}} + return reader.read{{toMsgPack}}{{toGraphQLType}}((reader: Read): {{#item}}{{toWasm}}{{toGraphQLType}}{{/item}} => { + {{> deserialize_array}} + }); +{{/if}} +{{#if map}} + return reader.read{{toMsgPack}}{{toGraphQLType}}((reader: Read): {{#key}}{{toWasm}}{{toGraphQLType}}{{/key}} => { + return reader.read{{toMsgPack}}{{toGraphQLType}}(); + }, (reader: Read): {{#value}}{{toWasm}}{{toGraphQLType}}{{/value}} => { + {{> deserialize_map_value}} + }); +{{/if}} +{{#if enum}} + {{> deserialize_enum}} + return value; +{{/if}} +{{#if object}} + {{> deserialize_object}} + return object; +{{/if}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_map_value.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_map_value.mustache deleted file mode 100644 index 5ee4a8a43b..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_map_value.mustache +++ /dev/null @@ -1,23 +0,0 @@ -{{#scalar}} -return reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(); -{{/scalar}} -{{#array}} -return reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}((reader: Read): {{#item}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/item}} => { - {{> deserialize_array}} -}); -{{/array}} -{{#map}} -return reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}((reader: Read): {{#key}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/key}} => { - return reader.read{{#key}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/key}}(); -}, (reader: Read): {{#value}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/value}} => { - {{> deserialize_map_value}} -}); -{{/map}} -{{#enum}} -{{> deserialize_enum}} -return value; -{{/enum}} -{{#object}} -{{> deserialize_object}} -return object; -{{/object}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_object.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_object.hbs new file mode 100644 index 0000000000..c26fb4f9c7 --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_object.hbs @@ -0,0 +1,8 @@ +{{#if required}} + const object = Types.{{detectKeyword type}}.read(reader); +{{else}} + let object: {{toWasm}}{{toGraphQLType}} | null = null; + if (!reader.isNextNil()) { + object = Types.{{detectKeyword type}}.read(reader); + } +{{/if}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_object.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_object.mustache deleted file mode 100644 index 18f27d1c1e..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/deserialize_object.mustache +++ /dev/null @@ -1,9 +0,0 @@ -{{#required}} -const object = Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}.read(reader); -{{/required}} -{{^required}} -let object: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} = null; -if (!reader.isNextNil()) { - object = Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}.read(reader); -} -{{/required}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.hbs similarity index 60% rename from packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.mustache rename to packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.hbs index c50d2654e1..eab7f5e7b5 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.hbs @@ -5,13 +5,13 @@ import { InvokeArgs } from "@polywrap/wasm-as"; -{{#functions.length}} +{{#if functions.length}} import { - {{#functions}} - {{name}}Wrapped{{^last}},{{/last}} - {{/functions}} + {{#each functions}} + {{name}}Wrapped{{#unless @last}},{{/unless}} + {{/each}} } from "./{{type}}/wrapped"; -{{/functions.length}} +{{/if}} export function _wrap_invoke(method_size: u32, args_size: u32): bool { const args: InvokeArgs = wrap_invoke_args( @@ -19,14 +19,13 @@ export function _wrap_invoke(method_size: u32, args_size: u32): bool { args_size ); - {{#functions}} - {{^first}}else {{/first}}if (args.method == "{{name}}") { - return wrap_invoke(args, {{name}}Wrapped); - } - {{/functions}} - else { - return wrap_invoke(args, null); - } + {{#each functions}} + {{#unless @first}}else {{/unless}}if (args.method == "{{name}}") { + return wrap_invoke(args, {{name}}Wrapped); + } + {{/each}} + + return wrap_invoke(args, null); } export function wrapAbort( @@ -41,4 +40,4 @@ export function wrapAbort( line, column ); -} +} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/enum-type/index-ts.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/enum-type/index-ts.hbs new file mode 100644 index 0000000000..f66d28b005 --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/enum-type/index-ts.hbs @@ -0,0 +1,35 @@ +export enum {{detectKeyword name}} { + {{#each constants}} + {{detectKeyword this}}, + {{/each}} + _MAX_ +} + +export function sanitize{{name}}Value(value: i32): void { + const valid = value >= 0 && value < {{detectKeyword name}}._MAX_; + if (!valid) { + throw new Error("Invalid value for enum '{{detectKeyword name}}': " + value.toString()); + } +} + +export function get{{name}}Value(key: string): {{detectKeyword name}} { + {{#each constants}} + if (key == "{{detectKeyword this}}") { + return {{detectKeyword name}}.{{detectKeyword this}}; + } + {{/each}} + + throw new Error("Invalid key for enum '{{detectKeyword name}}': " + key); +} + +export function get{{name}}Key(value: {{detectKeyword name}}): string { + sanitize{{name}}Value(value); + + switch (value) { + {{#each constants}} + case {{detectKeyword name}}.{{detectKeyword this}}: return "{{detectKeyword this}}"; + {{/each}} + default: + throw new Error("Invalid value for enum '{{detectKeyword name}}': " + value.toString()); + } +} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/enum-type/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/enum-type/index-ts.mustache deleted file mode 100644 index 40d000fd72..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/enum-type/index-ts.mustache +++ /dev/null @@ -1,35 +0,0 @@ -export enum {{#detectKeyword}}{{name}}{{/detectKeyword}} { - {{#constants}} - {{#detectKeyword}}{{.}}{{/detectKeyword}}, - {{/constants}} - _MAX_ -} - -export function sanitize{{name}}Value(value: i32): void { - const valid = value >= 0 && value < {{#detectKeyword}}{{name}}{{/detectKeyword}}._MAX_; - if (!valid) { - throw new Error("Invalid value for enum '{{#detectKeyword}}{{name}}{{/detectKeyword}}': " + value.toString()); - } -} - -export function get{{name}}Value(key: string): {{#detectKeyword}}{{name}}{{/detectKeyword}} { - {{#constants}} - if (key == "{{#detectKeyword}}{{.}}{{/detectKeyword}}") { - return {{#detectKeyword}}{{name}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}; - } - {{/constants}} - - throw new Error("Invalid key for enum '{{#detectKeyword}}{{name}}{{/detectKeyword}}': " + key); -} - -export function get{{name}}Key(value: {{#detectKeyword}}{{name}}{{/detectKeyword}}): string { - sanitize{{name}}Value(value); - - switch (value) { - {{#constants}} - case {{#detectKeyword}}{{name}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}: return "{{#detectKeyword}}{{.}}{{/detectKeyword}}"; - {{/constants}} - default: - throw new Error("Invalid value for enum '{{#detectKeyword}}{{name}}{{/detectKeyword}}': " + value.toString()); - } -} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/enum-type/index-ts.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/enum-type/index-ts.hbs new file mode 100644 index 0000000000..f66d28b005 --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/enum-type/index-ts.hbs @@ -0,0 +1,35 @@ +export enum {{detectKeyword name}} { + {{#each constants}} + {{detectKeyword this}}, + {{/each}} + _MAX_ +} + +export function sanitize{{name}}Value(value: i32): void { + const valid = value >= 0 && value < {{detectKeyword name}}._MAX_; + if (!valid) { + throw new Error("Invalid value for enum '{{detectKeyword name}}': " + value.toString()); + } +} + +export function get{{name}}Value(key: string): {{detectKeyword name}} { + {{#each constants}} + if (key == "{{detectKeyword this}}") { + return {{detectKeyword name}}.{{detectKeyword this}}; + } + {{/each}} + + throw new Error("Invalid key for enum '{{detectKeyword name}}': " + key); +} + +export function get{{name}}Key(value: {{detectKeyword name}}): string { + sanitize{{name}}Value(value); + + switch (value) { + {{#each constants}} + case {{detectKeyword name}}.{{detectKeyword this}}: return "{{detectKeyword this}}"; + {{/each}} + default: + throw new Error("Invalid value for enum '{{detectKeyword name}}': " + value.toString()); + } +} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/enum-type/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/enum-type/index-ts.mustache deleted file mode 100644 index 40d000fd72..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/enum-type/index-ts.mustache +++ /dev/null @@ -1,35 +0,0 @@ -export enum {{#detectKeyword}}{{name}}{{/detectKeyword}} { - {{#constants}} - {{#detectKeyword}}{{.}}{{/detectKeyword}}, - {{/constants}} - _MAX_ -} - -export function sanitize{{name}}Value(value: i32): void { - const valid = value >= 0 && value < {{#detectKeyword}}{{name}}{{/detectKeyword}}._MAX_; - if (!valid) { - throw new Error("Invalid value for enum '{{#detectKeyword}}{{name}}{{/detectKeyword}}': " + value.toString()); - } -} - -export function get{{name}}Value(key: string): {{#detectKeyword}}{{name}}{{/detectKeyword}} { - {{#constants}} - if (key == "{{#detectKeyword}}{{.}}{{/detectKeyword}}") { - return {{#detectKeyword}}{{name}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}; - } - {{/constants}} - - throw new Error("Invalid key for enum '{{#detectKeyword}}{{name}}{{/detectKeyword}}': " + key); -} - -export function get{{name}}Key(value: {{#detectKeyword}}{{name}}{{/detectKeyword}}): string { - sanitize{{name}}Value(value); - - switch (value) { - {{#constants}} - case {{#detectKeyword}}{{name}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}: return "{{#detectKeyword}}{{.}}{{/detectKeyword}}"; - {{/constants}} - default: - throw new Error("Invalid value for enum '{{#detectKeyword}}{{name}}{{/detectKeyword}}': " + value.toString()); - } -} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.hbs new file mode 100644 index 0000000000..00db5278fe --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.hbs @@ -0,0 +1,11 @@ +{{#each imports}} + {{#each functions}} + export * from "./{{type}}"; + {{/each}} + {{#each objects}} + export * from "./{{type}}"; + {{/each}} + {{#each enums}} + export * from "./{{type}}"; + {{/each}} +{{/each}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.mustache deleted file mode 100644 index 4b89d71758..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.mustache +++ /dev/null @@ -1,11 +0,0 @@ -{{#imports}} -{{#functions}} -export * from "./{{type}}"; -{{/functions}} -{{#objects}} -export * from "./{{type}}"; -{{/objects}} -{{#enums}} -export * from "./{{type}}"; -{{/enums}} -{{/imports}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/index-ts.hbs similarity index 75% rename from packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/index-ts.mustache rename to packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/index-ts.hbs index 989b562264..e97758f164 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/index-ts.mustache +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/index-ts.hbs @@ -7,25 +7,25 @@ import { JSON, Result } from "@polywrap/wasm-as"; -{{#functions.length}} +{{#if functions.length}} import { - {{#functions}} + {{#each functions}} serialize{{name}}Args, deserialize{{name}}Result, - Args_{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/functions}} + Args_{{detectKeyword name}}{{^@last}},{{/@last}} + {{/each}} } from "./serialization"; -{{/functions.length}} +{{/if}} import * as Types from "../.."; {{^isInterface}} -export class {{#detectKeyword}}{{name}}{{/detectKeyword}} { +export class {{detectKeyword name}} { public static uri: string = "{{uri}}"; - {{#functions}} + {{#each functions}} public static {{name}}( - args: Args_{{#detectKeyword}}{{name}}{{/detectKeyword}} + args: Args_{{detectKeyword name}} ): Result<{{#return}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/return}}, string> { const argsBuf = serialize{{name}}Args(args); const result = wrap_subinvoke( @@ -44,14 +44,14 @@ export class {{#detectKeyword}}{{name}}{{/detectKeyword}} { deserialize{{name}}Result(result.unwrap()) ); } - {{^last}} + {{^@last}} - {{/last}} - {{/functions}} + {{/@last}} + {{/each}} } {{/isInterface}} {{#isInterface}} -export class {{#detectKeyword}}{{type}}{{/detectKeyword}} { +export class {{detectKeyword type}} { public static interfaceUri: string = "{{uri}}"; @@ -61,9 +61,9 @@ export class {{#detectKeyword}}{{type}}{{/detectKeyword}} { this.uri = uri; } - {{#functions}} + {{#each functions}} public {{name}}( - args: Args_{{#detectKeyword}}{{name}}{{/detectKeyword}} + args: Args_{{detectKeyword name}} ): Result<{{#return}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/return}}, string> { const argsBuf = serialize{{name}}Args(args); const result = wrap_subinvokeImplementation( @@ -83,9 +83,9 @@ export class {{#detectKeyword}}{{type}}{{/detectKeyword}} { deserialize{{name}}Result(result.unwrap()) ); } - {{^last}} + {{^@last}} - {{/last}} - {{/functions}} + {{/@last}} + {{/each}} } -{{/isInterface}} +{{/isInterface}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/serialization-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/serialization-ts.hbs similarity index 100% rename from packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/serialization-ts.mustache rename to packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/module-type/serialization-ts.hbs diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/index-ts.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/index-ts.hbs new file mode 100644 index 0000000000..04d80291de --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/index-ts.hbs @@ -0,0 +1,40 @@ +import { + Read, + Write, + Box, + BigInt, + BigNumber, + JSON +} from "@polywrap/wasm-as"; +import { + serialize{{name}}, + deserialize{{name}}, + write{{name}}, + read{{name}} +} from "./serialization"; +import * as Types from "../.."; + +export class {{#detectKeyword name}}{{name}}{{/detectKeyword}} { + + public static uri: string = "{{uri}}"; + + {{#properties}} + {{#detectKeyword name}}{{name}}{{/detectKeyword}}: {{#toWasm toGraphQLType}}{{/toWasm}}; + {{/properties}} + + static toBuffer(type: {{#detectKeyword name}}{{name}}{{/detectKeyword}}): ArrayBuffer { + return serialize{{name}}(type); + } + + static fromBuffer(buffer: ArrayBuffer): {{#detectKeyword name}}{{name}}{{/detectKeyword}} { + return deserialize{{name}}(buffer); + } + + static write(writer: Write, type: {{#detectKeyword name}}{{name}}{{/detectKeyword}}): void { + write{{name}}(writer, type); + } + + static read(reader: Read): {{#detectKeyword name}}{{name}}{{/detectKeyword}} { + return read{{name}}(reader); + } +} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/index-ts.mustache deleted file mode 100644 index 3692269327..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/object-type/index-ts.mustache +++ /dev/null @@ -1,40 +0,0 @@ -import { - Read, - Write, - Box, - BigInt, - BigNumber, - JSON -} from "@polywrap/wasm-as"; -import { - serialize{{name}}, - deserialize{{name}}, - write{{name}}, - read{{name}} -} from "./serialization"; -import * as Types from "../.."; - -export class {{#detectKeyword}}{{name}}{{/detectKeyword}} { - - public static uri: string = "{{uri}}"; - - {{#properties}} - {{#detectKeyword}}{{name}}{{/detectKeyword}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}}; - {{/properties}} - - static toBuffer(type: {{#detectKeyword}}{{name}}{{/detectKeyword}}): ArrayBuffer { - return serialize{{name}}(type); - } - - static fromBuffer(buffer: ArrayBuffer): {{#detectKeyword}}{{name}}{{/detectKeyword}} { - return deserialize{{name}}(buffer); - } - - static write(writer: Write, type: {{#detectKeyword}}{{name}}{{/detectKeyword}}): void { - write{{name}}(writer, type); - } - - static read(reader: Read): {{#detectKeyword}}{{name}}{{/detectKeyword}} { - return read{{name}}(reader); - } -} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.hbs new file mode 100644 index 0000000000..87f556cacd --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.hbs @@ -0,0 +1,56 @@ +{{#if functions.length}} +import { + {{#each functions}} + Args_{{detectKeyword name}}{{#unless @last}},{{/unless}} + {{/each}} +} from "./{{name}}"; +{{/if}} +{{#if functions.length}} +export { + {{#each functions}} + Args_{{detectKeyword name}}{{#unless @last}},{{/unless}} + {{/each}} +}; +{{/if}} +{{#each objects}} +export { {{detectKeyword name}} } from "./{{name}}"; +{{/each}} +{{#each enums}} +export { + {{detectKeyword name}}, + get{{name}}Key, + get{{name}}Value, + sanitize{{name}}Value +} from "./{{name}}"; +{{/each}} +{{#if imports}} +{{#each imports}} + {{#if functions}} + export { + {{#each functions}} + {{detectKeyword name}} + {{#unless @last}},{{/unless}} + {{/each}} + } from "./imported/{{name}}"; + {{/if}} + {{#if objects}} + export { + {{#each objects}} + {{detectKeyword name}} + {{#unless @last}},{{/unless}} + {{/each}} + } from "./imported/{{name}}"; + {{/if}} + {{#if enums}} + export { + {{#each enums}} + {{detectKeyword name}}, + get{{name}}Key, + get{{name}}Value, + sanitize{{name}}Value + {{#unless @last}},{{/unless}} + {{/each}} + } from "./imported/{{name}}"; + {{/if}} +{{/each}} +{{/if}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.mustache deleted file mode 100644 index 54e5b2ce96..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.mustache +++ /dev/null @@ -1,41 +0,0 @@ -{{#functions.length}} -import { - {{#functions}} - Args_{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/functions}} -} from "./{{name}}"; -{{/functions.length}} -{{#functions.length}} -export { - {{#functions}} - Args_{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/functions}} -}; -{{/functions.length}} -{{#objects}} -export { {{#detectKeyword}}{{name}}{{/detectKeyword}} } from "./{{name}}"; -{{/objects}} -{{#enums}} -export { - {{#detectKeyword}}{{name}}{{/detectKeyword}}, - get{{name}}Key, - get{{name}}Value, - sanitize{{name}}Value -} from "./{{name}}"; -{{/enums}} -{{#imports}} -{{#functions}} -export { {{#detectKeyword}}{{name}}{{/detectKeyword}} } from "./imported/{{name}}"; -{{/functions}} -{{#objects}} -export { {{#detectKeyword}}{{name}}{{/detectKeyword}} } from "./imported/{{name}}"; -{{/objects}} -{{#enums}} -export { - {{#detectKeyword}}{{name}}{{/detectKeyword}}, - get{{name}}Key, - get{{name}}Value, - sanitize{{name}}Value -} from "./imported/{{name}}"; -{{/enums}} -{{/imports}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/index-ts.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/index-ts.hbs new file mode 100644 index 0000000000..5c1133805b --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/index-ts.hbs @@ -0,0 +1,13 @@ +{{#if functions.length}} +import { + {{#each functions}} + Args_{{detectKeyword name}}{{#unless @last}},{{/unless}} + {{/each}} +} from "./serialization"; + +export { + {{#each functions}} + Args_{{detectKeyword name}}{{#unless @last}},{{/unless}} + {{/each}} +}; +{{/if}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/index-ts.mustache deleted file mode 100644 index 3b41b0fb64..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/index-ts.mustache +++ /dev/null @@ -1,13 +0,0 @@ -{{#functions.length}} -import { - {{#functions}} - Args_{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/functions}} -} from "./serialization"; - -export { - {{#functions}} - Args_{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/functions}} -}; -{{/functions.length}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/serialization-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/serialization-ts.hbs similarity index 100% rename from packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/serialization-ts.mustache rename to packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/serialization-ts.hbs diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.hbs new file mode 100644 index 0000000000..c8de986ec8 --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.hbs @@ -0,0 +1,34 @@ +{{#if functions.length}} +import { +{{#each functions}} +{{#detectKeyword}}{{name}}{{/detectKeyword}}{{#unless @last}}, {{/unless}} +{{/each}} +} from "../../index"; +import { +{{#each functions}} +deserialize{{name}}Args, +serialize{{name}}Result{{#unless @last}}, {{/unless}} +{{/each}} +} from "./serialization"; +{{/if}} +import * as Types from ".."; + +{{#each functions}} +export function {{name}}Wrapped(argsBuf: ArrayBuffer): ArrayBuffer { +{{#if arguments.length}} +const args = deserialize{{name}}Args(argsBuf); +{{/if}} + +const result = {{name}}({{#if arguments.length}} +{ +{{#each arguments}} +{{#detectKeyword}}{{name}}{{/detectKeyword}}: args.{{#detectKeyword}}{{name}}{{/detectKeyword}}{{#unless @last}}, +{{/unless}} +{{/each}} +}{{/if}}{{#unless arguments.length}}{}{{/unless}}); +return serialize{{name}}Result(result); +} +{{#unless @last}} + +{{/unless}} +{{/each}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.mustache deleted file mode 100644 index b45f7dcab7..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.mustache +++ /dev/null @@ -1,34 +0,0 @@ -{{#functions.length}} -import { - {{#functions}} - {{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/functions}} -} from "../../index"; -import { - {{#functions}} - deserialize{{name}}Args, - serialize{{name}}Result{{^last}},{{/last}} - {{/functions}} -} from "./serialization"; -{{/functions.length}} -import * as Types from ".."; - -{{#functions}} -export function {{name}}Wrapped(argsBuf: ArrayBuffer): ArrayBuffer { - {{#arguments.length}} - const args = deserialize{{name}}Args(argsBuf); - {{/arguments.length}} - - const result = {{#detectKeyword}}{{name}}{{/detectKeyword}}({{#arguments.length}} - { - {{#arguments}} - {{#detectKeyword}}{{name}}{{/detectKeyword}}: args.{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/arguments}} - }{{/arguments.length}}{{^arguments.length}}{}{{/arguments.length}} - ); - return serialize{{name}}Result(result); -} -{{^last}} - -{{/last}} -{{/functions}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.hbs new file mode 100644 index 0000000000..e0c6e86359 --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.hbs @@ -0,0 +1,37 @@ +import { + Read, + Write, + Box, + BigInt, + BigNumber, + JSON +} from "@polywrap/wasm-as"; +import { + serialize{{name}}, + deserialize{{name}}, + write{{name}}, + read{{name}} +} from "./serialization"; +import * as Types from ".."; + +export class {{detectKeyword name}} { + {{#each properties}} + {{detectKeyword this.name}}: {{toWasm this.type}}; + {{/each}} + + static toBuffer(type: {{detectKeyword name}}): ArrayBuffer { + return serialize{{name}}(type); + } + + static fromBuffer(buffer: ArrayBuffer): {{detectKeyword name}} { + return deserialize{{name}}(buffer); + } + + static write(writer: Write, type: {{detectKeyword name}}): void { + write{{name}}(writer, type); + } + + static read(reader: Read): {{detectKeyword name}} { + return read{{name}}(reader); + } +} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.mustache deleted file mode 100644 index cc49d95ac4..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.mustache +++ /dev/null @@ -1,37 +0,0 @@ -import { - Read, - Write, - Box, - BigInt, - BigNumber, - JSON -} from "@polywrap/wasm-as"; -import { - serialize{{name}}, - deserialize{{name}}, - write{{name}}, - read{{name}} -} from "./serialization"; -import * as Types from ".."; - -export class {{#detectKeyword}}{{name}}{{/detectKeyword}} { - {{#properties}} - {{#detectKeyword}}{{name}}{{/detectKeyword}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}}; - {{/properties}} - - static toBuffer(type: {{#detectKeyword}}{{name}}{{/detectKeyword}}): ArrayBuffer { - return serialize{{name}}(type); - } - - static fromBuffer(buffer: ArrayBuffer): {{#detectKeyword}}{{name}}{{/detectKeyword}} { - return deserialize{{name}}(buffer); - } - - static write(writer: Write, type: {{#detectKeyword}}{{name}}{{/detectKeyword}}): void { - write{{name}}(writer, type); - } - - static read(reader: Read): {{#detectKeyword}}{{name}}{{/detectKeyword}} { - return read{{name}}(reader); - } -} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/serialization-ts.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/serialization-ts.hbs similarity index 100% rename from packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/serialization-ts.mustache rename to packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/serialization-ts.hbs diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialization_imports.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialization_imports.hbs similarity index 100% rename from packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialization_imports.mustache rename to packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialization_imports.hbs diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_array.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_array.hbs new file mode 100644 index 0000000000..174040683d --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_array.hbs @@ -0,0 +1,21 @@ +{{#if scalar}} + writer.write{{toMsgPack}}{{toGraphQLType}}(item); +{{/if}} +{{#if array}} + writer.write{{toMsgPack}}{{toGraphQLType}}(item, (writer: Write, item: {{#item}}{{toWasm}}{{toGraphQLType}}{{/item}}) => { + {{> serialize_array}} + }); +{{/if}} +{{#if map}} + writer.write{{toMsgPack}}{{toGraphQLType}}(item, (writer: Write, key: {{#key}}{{toWasm}}{{toGraphQLType}}{{/key}}) => { + writer.write{{toMsgPack}}{{toGraphQLType}}(key); + }, (writer: Write, value: {{#value}}{{toWasm}}{{toGraphQLType}}{{/value}}) => { + {{> serialize_map_value}} + }); +{{/if}} +{{#if enum}} + {{> serialize_enum}} +{{/if}} +{{#if object}} + {{> serialize_object}} +{{/if}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_array.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_array.mustache deleted file mode 100644 index a32a8fbfb0..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_array.mustache +++ /dev/null @@ -1,21 +0,0 @@ -{{#scalar}} -writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(item); -{{/scalar}} -{{#array}} -writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(item, (writer: Write, item: {{#item}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/item}}): void => { - {{> serialize_array}} -}); -{{/array}} -{{#map}} -writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(item, (writer: Write, key: {{#key}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/key}}) => { - writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(key); -}, (writer: Write, value: {{#value}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/value}}): void => { - {{> serialize_map_value}} -}); -{{/map}} -{{#enum}} -{{> serialize_enum}} -{{/enum}} -{{#object}} -{{> serialize_object}} -{{/object}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_enum.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_enum.hbs new file mode 100644 index 0000000000..b7400181b9 --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_enum.hbs @@ -0,0 +1,5 @@ +{{#if required}} + writer.writeInt32(item); +{{else}} + writer.writeOptionalInt32(item); +{{/if}} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_enum.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_enum.mustache deleted file mode 100644 index 36c482f6ce..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_enum.mustache +++ /dev/null @@ -1,6 +0,0 @@ -{{#required}} -writer.writeInt32(item); -{{/required}} -{{^required}} -writer.writeOptionalInt32(item); -{{/required}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_map_value.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_map_value.hbs new file mode 100644 index 0000000000..997e723383 --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_map_value.hbs @@ -0,0 +1,33 @@ +{{#if (eq kind "Scalar")}} + writer.write{{toMsgPack toGraphQLType}}(value); +{{/if}} +{{#if (eq kind "Array")}} + writer.write{{toMsgPack toGraphQLType}}(value, (writer: Write, item: {{#item}}{{toWasm toGraphQLType}}{{/item}}) => { + {{> serialize_array}} + }); +{{/if}} +{{#if (eq kind "Map")}} + writer.write{{toMsgPack toGraphQLType}}(value, (writer: Write, key: {{#key}}{{toWasm toGraphQLType}}{{/key}}) => { + writer.write{{toMsgPack toGraphQLType}}(key); + }, (writer: Write, value: {{#value}}{{toWasm toGraphQLType}}{{/value}}) => { + {{> serialize_map_value}} + }); +{{/if}} +{{#if (eq kind "Enum")}} + {{#if required}} + writer.writeInt32(value); + {{else}} + writer.writeOptionalInt32(value); + {{/if}} +{{/if}} +{{#if (eq kind "Object")}} + {{#if required}} + Types.{{detectKeyword type}}.write(writer, value); + {{else}} + {{#if value}} + Types.{{detectKeyword type}}.write(writer, value as Types.{{detectKeyword type}}); + {{else}} + writer.writeNil(); + {{/if}} + {{/if}} +{{/if}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_map_value.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_map_value.mustache deleted file mode 100644 index 4dc36f8422..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_map_value.mustache +++ /dev/null @@ -1,35 +0,0 @@ -{{#scalar}} -writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(value); -{{/scalar}} -{{#array}} -writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(value, (writer: Write, item: {{#item}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/item}}): void => { - {{> serialize_array}} -}); -{{/array}} -{{#map}} -writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(value, (writer: Write, key: {{#key}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/key}}) => { - writer.write{{#key}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/key}}(key); -}, (writer: Write, value: {{#value}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/value}}): void => { - {{> serialize_map_value}} -}); -{{/map}} -{{#enum}} -{{#required}} -writer.writeInt32(value); -{{/required}} -{{^required}} -writer.writeOptionalInt32(value); -{{/required}} -{{/enum}} -{{#object}} -{{#required}} -Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}.write(writer, value); -{{/required}} -{{^required}} -if (value) { - Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}.write(writer, value as Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}); -} else { - writer.writeNil(); -} -{{/required}} -{{/object}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_object.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_object.hbs new file mode 100644 index 0000000000..3cc9c654c7 --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_object.hbs @@ -0,0 +1,9 @@ +{{#if required}} + Types.{{detectKeyword type}}.write(writer, item); +{{else}} + if (item) { + Types.{{detectKeyword type}}.write(writer, item as Types.{{detectKeyword type}}); + } else { + writer.writeNil(); + } +{{/if}} diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_object.mustache b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_object.mustache deleted file mode 100644 index 10a177af22..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/serialize_object.mustache +++ /dev/null @@ -1,10 +0,0 @@ -{{#required}} -Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}.write(writer, item); -{{/required}} -{{^required}} -if (item) { - Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}.write(writer, item as Types.{{#detectKeyword}}{{type}}{{/detectKeyword}}); -} else { - writer.writeNil(); -} -{{/required}} diff --git a/packages/schema/bind/src/index.ts b/packages/schema/bind/src/index.ts index ecdf3c5f15..f50a39b0d7 100644 --- a/packages/schema/bind/src/index.ts +++ b/packages/schema/bind/src/index.ts @@ -1,10 +1,8 @@ import { BindOptions, BindOutput } from "./types"; import { getGenerateBindingFn } from "./bindings"; -import Mustache from "mustache"; - // Remove mustache's built-in HTML escaping -Mustache.escape = (value) => value; +// Handlebars.escape = (value) => value; export * from "./types"; export * from "./bindings"; diff --git a/packages/schema/bind/src/transforms/toGraphQLType.ts b/packages/schema/bind/src/transforms/toGraphQLType.ts new file mode 100644 index 0000000000..e24bb21279 --- /dev/null +++ b/packages/schema/bind/src/transforms/toGraphQLType.ts @@ -0,0 +1,145 @@ +import { AnyDef, AnyTypeOrDef } from "@polywrap/abi-types"; + +function applyRequired(type: string, required: boolean | undefined): string { + return `${type}${required ? "!" : ""}`; +} + +function anyToGraphQL(any: AnyTypeOrDef, prefixed: boolean): string { + switch (any.kind) { + case "Object": + return toGraphQL(any.object, prefixed); + case "Array": + return toGraphQL(any.array, prefixed); + case "Scalar": + return toGraphQL(any.scalar, prefixed); + case "Enum": + return toGraphQL(any.enum, prefixed); + case "Map": + return toGraphQL(any.map, prefixed); + default: + throw Error( + `anyToGraphQL: Any type is invalid.\n${JSON.stringify(any, null, 2)}` + ); + } +} + +export function toGraphQL(def: GenericDefinition, prefixed = false): string { + switch (def.kind) { + case DefinitionKind.Object: + case DefinitionKind.ObjectRef: + case DefinitionKind.Scalar: + case DefinitionKind.ImportedObject: + return applyRequired(def.type, def.required); + case DefinitionKind.Enum: + case DefinitionKind.EnumRef: + case DefinitionKind.ImportedEnum: + if (prefixed) { + return applyRequired(`Enum_${def.type}`, def.required); + } + + return applyRequired(def.type, def.required); + case DefinitionKind.Any: + case DefinitionKind.Property: + return anyToGraphQL(def as AnyDefinition, prefixed); + case DefinitionKind.Array: { + const array = def as ArrayDefinition; + + if (!array.item) { + throw Error( + `toGraphQL: ArrayDefinition's item type is undefined.\n${JSON.stringify( + array, + null, + 2 + )}` + ); + } + + return applyRequired( + `[${toGraphQL(array.item, prefixed)}]`, + array.required + ); + } + case DefinitionKind.Map: { + const map = def as MapDefinition; + if (!map.key) { + throw Error( + `toGraphQL: MapDefinition's key type is undefined.\n${JSON.stringify( + map, + null, + 2 + )}` + ); + } + if (!map.value) { + throw Error( + `toGraphQL: MapDefinition's value type is undefined.\n${JSON.stringify( + map, + null, + 2 + )}` + ); + } + return applyRequired( + `Map<${toGraphQL(map.key, prefixed)}, ${toGraphQL( + map.value, + prefixed + )}>`, + map.required + ); + } + case DefinitionKind.Method: { + const method = def as MethodDefinition; + + if (!method.return) { + throw Error( + `toGraphQL: MethodDefinition's return type is undefined.\n${JSON.stringify( + method, + null, + 2 + )}` + ); + } + + const result = `${method.name}( + ${(method.arguments || []) + .map((arg) => `${arg.name}: ${toGraphQL(arg, prefixed)}`) + .join("\n ")} +): ${toGraphQL(method.return, prefixed)}`; + return result; + } + case DefinitionKind.Module: + return def.type; + case DefinitionKind.ImportedModule: + return def.type; + default: + throw Error( + `toGraphQL: Unrecognized DefinitionKind.\n${JSON.stringify( + def, + null, + 2 + )}` + ); + } +} + +export function toPrefixedGraphQL(def: GenericDefinition): string { + return toGraphQL(def, true); +} + +export const toPrefixedGraphQLType: AbiTransforms = { + enter: { + GenericDefinition: (def: GenericDefinition) => ({ + ...def, + toGraphQLType: () => toPrefixedGraphQL(def), + }), + }, +}; + +export const toGraphQLType: AbiTransforms = { + enter: { + GenericDefinition: (def: GenericDefinition) => ({ + ...def, + toGraphQLType: () => toGraphQL(def), + }), + }, +}; \ No newline at end of file From 1bb49d7ba752910fc5cf0e7412156ed645d2b811 Mon Sep 17 00:00:00 2001 From: namesty Date: Fri, 7 Apr 2023 01:28:19 +0200 Subject: [PATCH 85/90] (feat): bindings WasmRenderer --- packages/schema/bind/src/WasmRenderer.ts | 103 +++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 packages/schema/bind/src/WasmRenderer.ts diff --git a/packages/schema/bind/src/WasmRenderer.ts b/packages/schema/bind/src/WasmRenderer.ts new file mode 100644 index 0000000000..5e759f6c20 --- /dev/null +++ b/packages/schema/bind/src/WasmRenderer.ts @@ -0,0 +1,103 @@ +import { AnyType, ArrayType, ImportRefType, MapType, RefType, ScalarType } from "@polywrap/abi-types"; +import { isBaseType, isKeyword } from "./bindings/assemblyscript/types"; + +export class WasmRenderer { + private escapeKeyword(typeName: string): string { + return isKeyword(typeName) ? `_${typeName}`: typeName + } + + private applyOptional(type: string, required: boolean, isEnum: boolean): string { + if (!required) { + if ( + type.indexOf("Array") === 0 || + type.indexOf("string") === 0 || + (!isEnum && !isBaseType(type)) + ) { + return `${type} | null`; + } else { + return `Box<${type}> | null`; + } + } else { + return type; + } + } + + renderAnyType(type: AnyType, required: boolean): string { + switch (type.kind) { + case "Array": return this.renderArray(type, required) + case "Map": return this.renderMap(type, required) + case "Scalar": return this.renderScalar(type, required) + case "Ref": return this.renderRef(type, required) + case "ImportRef": return this.renderRef(type, required) + case "UnlinkedImportRef": throw new Error(`Cannot render Unlinked Import refs. Attempted to render: ${type}`) + } + } + + renderRef(type: RefType | ImportRefType, required: boolean) { + const escapedName = this.escapeKeyword(type.ref_name) + return this.applyOptional(`Types.${escapedName}`, required, type.ref_kind === "Enum") + } + + renderMap(type: MapType, required: boolean): string { + const value = this.renderAnyType(type.value.type, type.value.required) + const key = this.renderScalar(type.key, true) + + return this.applyOptional(`Map<${key}, ${value}>`, required, false) + } + + renderArray(type: ArrayType, required: boolean): string { + const item = this.renderAnyType(type.item.type, type.item.required) + return this.applyOptional(`Array<${item}>`, required, false) + } + + renderScalar(type: ScalarType, required: boolean): string { + let scalar: string + + switch (type.scalar) { + case "Int": + scalar = "i32"; + break; + case "Int8": + scalar = "i8"; + break; + case "Int16": + scalar = "i16"; + break; + case "Int32": + scalar = "i32"; + break; + case "UInt": + scalar = "u32"; + break; + case "UInt32": + scalar = "u32"; + break; + case "UInt8": + scalar = "u8"; + break; + case "UInt16": + scalar = "u16"; + break; + case "String": + scalar = "string"; + break; + case "Boolean": + scalar = "bool"; + break; + case "Bytes": + scalar = "ArrayBuffer"; + break; + case "BigInt": + scalar = "BigInt"; + break; + case "BigNumber": + scalar = "BigNumber"; + break; + case "JSON": + scalar = "JSON.Value"; + break; + } + + return this.applyOptional(scalar, required, false) + } +} \ No newline at end of file From ddc610ef4686ca82a03ce8c5835dbb622b31bf28 Mon Sep 17 00:00:00 2001 From: namesty Date: Fri, 7 Apr 2023 03:27:43 +0200 Subject: [PATCH 86/90] (feat): Wasm initial values renderer added --- .../bind/src/WasmInitialValuesRenderer.ts | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 packages/schema/bind/src/WasmInitialValuesRenderer.ts diff --git a/packages/schema/bind/src/WasmInitialValuesRenderer.ts b/packages/schema/bind/src/WasmInitialValuesRenderer.ts new file mode 100644 index 0000000000..2cce05925e --- /dev/null +++ b/packages/schema/bind/src/WasmInitialValuesRenderer.ts @@ -0,0 +1,96 @@ +import { AnyType, RefType, ImportRefType, MapType, ArrayType, ScalarType } from "@polywrap/abi-types"; +import { isKeyword } from "./bindings/assemblyscript/types"; +import { WasmRenderer } from "./WasmRenderer"; + +export class WasmInitRenderer { + + constructor(protected wasmRenderer: WasmRenderer) {} + + private escapeKeyword(typeName: string): string { + return isKeyword(typeName) ? `_${typeName}`: typeName + } + + renderAnyType(type: AnyType, required: boolean): string { + switch (type.kind) { + case "Array": return this.renderArray(type, required) + case "Map": return this.renderMap(type, required) + case "Scalar": return this.renderScalar(type, required) + case "Ref": return this.renderRef(type, required) + case "ImportRef": return this.renderRef(type, required) + case "UnlinkedImportRef": throw new Error(`Cannot render Unlinked Import refs. Attempted to render: ${type}`) + } + } + + renderRef(type: RefType | ImportRefType, required: boolean) { + if (!required) { + const renderedType = this.wasmRenderer.renderAnyType(type, required) + + if (renderedType.endsWith("| null")) { + return "null" + } + } + + if (type.ref_kind === "Enum") { + return "0" + } + + const escapedName = this.escapeKeyword(type.ref_name) + + return `new Types.${escapedName}()` + } + + renderMap(type: MapType, required: boolean): string { + if (!required) { + const renderedType = this.wasmRenderer.renderAnyType(type, required) + + if (renderedType.endsWith("| null")) { + return "null" + } + } + + const value = this.wasmRenderer.renderAnyType(type.value.type, type.value.required) + const key = this.wasmRenderer.renderScalar(type.key, true) + + return `new Map<${key}, ${value}>()` + } + + renderArray(type: ArrayType, required: boolean): string { + if (!required) { + const renderedType = this.wasmRenderer.renderAnyType(type, required) + + if (renderedType.endsWith("| null")) { + return "null" + } + } + + return "[]" + } + + renderScalar(type: ScalarType, required: boolean): string { + if (!required) { + const renderedType = this.wasmRenderer.renderAnyType(type, required) + + if (renderedType.endsWith("| null")) { + return "null" + } + } + + switch (type.scalar) { + case "Int": + case "Int8": + case "Int16": + case "Int32": + case "UInt": + case "UInt32": + case "UInt8": + case "UInt16": + return "0" + case "String": return `""` + case "Boolean": return "false"; + case "Bytes": return `new ArrayBuffer(0)`; + case "BigInt": return `BigInt.fromUInt16(0)`; + case "BigNumber": return `new BigNumber(BigInt.fromUInt16(0), 0, 0)`; + case "JSON": return `JSON.Value.Null()`; + } + } +} \ No newline at end of file From 47cdca618491e7c0f5b9afa5c72cbb0e6b0806bb Mon Sep 17 00:00:00 2001 From: namesty Date: Fri, 7 Apr 2023 16:38:02 +0200 Subject: [PATCH 87/90] (chore): removed unnecessary transforms --- .../bind/src/transforms/addFirstLast.ts | 79 ---------- .../schema/bind/src/transforms/hasImports.ts | 37 ----- packages/schema/bind/src/transforms/index.ts | 2 - .../bind/src/transforms/toGraphQLType.ts | 145 ------------------ 4 files changed, 263 deletions(-) delete mode 100644 packages/schema/bind/src/transforms/addFirstLast.ts delete mode 100644 packages/schema/bind/src/transforms/hasImports.ts delete mode 100644 packages/schema/bind/src/transforms/index.ts delete mode 100644 packages/schema/bind/src/transforms/toGraphQLType.ts diff --git a/packages/schema/bind/src/transforms/addFirstLast.ts b/packages/schema/bind/src/transforms/addFirstLast.ts deleted file mode 100644 index 2578d4245f..0000000000 --- a/packages/schema/bind/src/transforms/addFirstLast.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { Abi, EnumDef, FunctionDef, ImportedAbi, ObjectDef } from "@polywrap/abi-types"; -import { IAbiVisitorEnterAndLeave } from "@polywrap/schema-abi"; - -const addFirstLastProps = (items: T[]): T[] => { - return items.map((item, index) => { - const isFirst = index === 0; - const isLast = index === items.length - 1; - - return { - ...item, - isFirst, - isLast, - }; - }); -} - -export const addFirstLast: IAbiVisitorEnterAndLeave = { - enter: { - Abi: (abi: Abi): Abi => { - if (abi.functions) { - abi.functions = addFirstLastProps(abi.functions); - } - - if (abi.objects) { - abi.objects = addFirstLastProps(abi.objects); - } - - if (abi.enums) { - abi.enums = addFirstLastProps(abi.enums); - } - - if (abi.imports) { - abi.imports = addFirstLastProps(abi.imports); - } - - return abi; - }, - Import: (abi: ImportedAbi): ImportedAbi => { - if (abi.functions) { - abi.functions = addFirstLastProps(abi.functions); - } - - if (abi.objects) { - abi.objects = addFirstLastProps(abi.objects); - } - - if (abi.enums) { - abi.enums = addFirstLastProps(abi.enums); - } - - if (abi.imports) { - abi.imports = addFirstLastProps(abi.imports); - } - - return abi; - }, - ObjectDef: (objectDef: ObjectDef): ObjectDef => { - if (objectDef.props) { - objectDef.props = addFirstLastProps(objectDef.props); - } - - return objectDef; - }, - EnumDef: (enumDef: EnumDef): EnumDef => { - if (enumDef.constants) { - enumDef.constants = addFirstLastProps(enumDef.constants); - } - - return enumDef; - }, - FunctionDef: (functionDef: FunctionDef): FunctionDef => { - if (functionDef.args) { - functionDef.args = addFirstLastProps(functionDef.args); - } - - return functionDef; - } - } -} \ No newline at end of file diff --git a/packages/schema/bind/src/transforms/hasImports.ts b/packages/schema/bind/src/transforms/hasImports.ts deleted file mode 100644 index 54f2b3eb86..0000000000 --- a/packages/schema/bind/src/transforms/hasImports.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Abi, ImportedAbi } from "@polywrap/abi-types"; -import { IAbiVisitor, IAbiVisitorEnterAndLeave } from "@polywrap/schema-abi"; - -type HasImportsAbi = Abi & { hasImports: boolean }; -type HasImportsImportedAbi = ImportedAbi & { hasImports: boolean }; - -interface HasImportsAbiVisitor extends IAbiVisitor { - Abi: (abi: HasImportsAbi) => HasImportsAbi; - Import: (abi: HasImportsImportedAbi) => HasImportsImportedAbi; -} - -interface IHasImportsAbiVisitorEnterAndLeave extends IAbiVisitorEnterAndLeave { - enter: HasImportsAbiVisitor; -} - -export const hasImports: IHasImportsAbiVisitorEnterAndLeave = { - enter: { - Abi: (abi: HasImportsAbi): HasImportsAbi => { - if (abi.imports && abi.imports.length > 0) { - abi.hasImports = true; - } else { - abi.hasImports = false; - } - - return abi; - }, - Import: (abi: HasImportsImportedAbi): HasImportsImportedAbi => { - if (abi.imports && abi.imports.length > 0) { - abi.hasImports = true; - } else { - abi.hasImports = false; - } - - return abi; - }, - } -} \ No newline at end of file diff --git a/packages/schema/bind/src/transforms/index.ts b/packages/schema/bind/src/transforms/index.ts deleted file mode 100644 index d782e25e44..0000000000 --- a/packages/schema/bind/src/transforms/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./addFirstLast"; -export * from "./hasImports"; \ No newline at end of file diff --git a/packages/schema/bind/src/transforms/toGraphQLType.ts b/packages/schema/bind/src/transforms/toGraphQLType.ts deleted file mode 100644 index e24bb21279..0000000000 --- a/packages/schema/bind/src/transforms/toGraphQLType.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { AnyDef, AnyTypeOrDef } from "@polywrap/abi-types"; - -function applyRequired(type: string, required: boolean | undefined): string { - return `${type}${required ? "!" : ""}`; -} - -function anyToGraphQL(any: AnyTypeOrDef, prefixed: boolean): string { - switch (any.kind) { - case "Object": - return toGraphQL(any.object, prefixed); - case "Array": - return toGraphQL(any.array, prefixed); - case "Scalar": - return toGraphQL(any.scalar, prefixed); - case "Enum": - return toGraphQL(any.enum, prefixed); - case "Map": - return toGraphQL(any.map, prefixed); - default: - throw Error( - `anyToGraphQL: Any type is invalid.\n${JSON.stringify(any, null, 2)}` - ); - } -} - -export function toGraphQL(def: GenericDefinition, prefixed = false): string { - switch (def.kind) { - case DefinitionKind.Object: - case DefinitionKind.ObjectRef: - case DefinitionKind.Scalar: - case DefinitionKind.ImportedObject: - return applyRequired(def.type, def.required); - case DefinitionKind.Enum: - case DefinitionKind.EnumRef: - case DefinitionKind.ImportedEnum: - if (prefixed) { - return applyRequired(`Enum_${def.type}`, def.required); - } - - return applyRequired(def.type, def.required); - case DefinitionKind.Any: - case DefinitionKind.Property: - return anyToGraphQL(def as AnyDefinition, prefixed); - case DefinitionKind.Array: { - const array = def as ArrayDefinition; - - if (!array.item) { - throw Error( - `toGraphQL: ArrayDefinition's item type is undefined.\n${JSON.stringify( - array, - null, - 2 - )}` - ); - } - - return applyRequired( - `[${toGraphQL(array.item, prefixed)}]`, - array.required - ); - } - case DefinitionKind.Map: { - const map = def as MapDefinition; - if (!map.key) { - throw Error( - `toGraphQL: MapDefinition's key type is undefined.\n${JSON.stringify( - map, - null, - 2 - )}` - ); - } - if (!map.value) { - throw Error( - `toGraphQL: MapDefinition's value type is undefined.\n${JSON.stringify( - map, - null, - 2 - )}` - ); - } - return applyRequired( - `Map<${toGraphQL(map.key, prefixed)}, ${toGraphQL( - map.value, - prefixed - )}>`, - map.required - ); - } - case DefinitionKind.Method: { - const method = def as MethodDefinition; - - if (!method.return) { - throw Error( - `toGraphQL: MethodDefinition's return type is undefined.\n${JSON.stringify( - method, - null, - 2 - )}` - ); - } - - const result = `${method.name}( - ${(method.arguments || []) - .map((arg) => `${arg.name}: ${toGraphQL(arg, prefixed)}`) - .join("\n ")} -): ${toGraphQL(method.return, prefixed)}`; - return result; - } - case DefinitionKind.Module: - return def.type; - case DefinitionKind.ImportedModule: - return def.type; - default: - throw Error( - `toGraphQL: Unrecognized DefinitionKind.\n${JSON.stringify( - def, - null, - 2 - )}` - ); - } -} - -export function toPrefixedGraphQL(def: GenericDefinition): string { - return toGraphQL(def, true); -} - -export const toPrefixedGraphQLType: AbiTransforms = { - enter: { - GenericDefinition: (def: GenericDefinition) => ({ - ...def, - toGraphQLType: () => toPrefixedGraphQL(def), - }), - }, -}; - -export const toGraphQLType: AbiTransforms = { - enter: { - GenericDefinition: (def: GenericDefinition) => ({ - ...def, - toGraphQLType: () => toGraphQL(def), - }), - }, -}; \ No newline at end of file From cde565d737274ca8de9af6034446b5098bbf50d6 Mon Sep 17 00:00:00 2001 From: namesty Date: Fri, 7 Apr 2023 16:38:48 +0200 Subject: [PATCH 88/90] (feat): better assemblyscript helpers and renderers --- packages/schema/bind/src/MsgPackRenderer.ts | 103 +++++++ .../src/bindings/assemblyscript/functions.ts | 254 ------------------ .../src/bindings/assemblyscript/helpers.ts | 18 ++ .../renderers/MsgPackRenderer.ts | 39 +++ .../renderers}/WasmInitialValuesRenderer.ts | 8 +- .../assemblyscript/renderers}/WasmRenderer.ts | 4 +- .../assemblyscript/renderers/types.ts | 9 + 7 files changed, 175 insertions(+), 260 deletions(-) create mode 100644 packages/schema/bind/src/MsgPackRenderer.ts delete mode 100644 packages/schema/bind/src/bindings/assemblyscript/functions.ts create mode 100644 packages/schema/bind/src/bindings/assemblyscript/helpers.ts create mode 100644 packages/schema/bind/src/bindings/assemblyscript/renderers/MsgPackRenderer.ts rename packages/schema/bind/src/{ => bindings/assemblyscript/renderers}/WasmInitialValuesRenderer.ts (92%) rename packages/schema/bind/src/{ => bindings/assemblyscript/renderers}/WasmRenderer.ts (96%) create mode 100644 packages/schema/bind/src/bindings/assemblyscript/renderers/types.ts diff --git a/packages/schema/bind/src/MsgPackRenderer.ts b/packages/schema/bind/src/MsgPackRenderer.ts new file mode 100644 index 0000000000..d734c63df1 --- /dev/null +++ b/packages/schema/bind/src/MsgPackRenderer.ts @@ -0,0 +1,103 @@ +import { AnyType, ArrayType, ImportRefType, MapType, RefType, ScalarType } from "@polywrap/abi-types"; +import { isBaseType, isKeyword } from "./bindings/assemblyscript/types"; + +export class WasmRenderer { + private escapeKeyword(typeName: string): string { + return isKeyword(typeName) ? `_${typeName}`: typeName + } + + private applyOptional(type: string, required: boolean): string { + if (!required) { + if ( + type.indexOf("Array") === 0 || + type.indexOf("string") === 0 || + (!isEnum && !isBaseType(type)) + ) { + return `${type} | null`; + } else { + return `Box<${type}> | null`; + } + } else { + return type; + } + } + + renderAnyType(type: AnyType, required: boolean): string { + switch (type.kind) { + case "Array": return this.renderArray(type, required) + case "Map": return this.renderMap(type, required) + case "Scalar": return this.renderScalar(type, required) + case "Ref": return this.renderRef(type, required) + case "ImportRef": return this.renderRef(type, required) + case "UnlinkedImportRef": throw new Error(`Cannot render Unlinked Import refs. Attempted to render: ${type}`) + } + } + + renderRef(type: RefType | ImportRefType, required: boolean) { + const escapedName = this.escapeKeyword(type.ref_name) + return this.applyOptional(`Types.${escapedName}`, required) + } + + renderMap(type: MapType, required: boolean): string { + const value = this.renderAnyType(type.value.type, type.value.required) + const key = this.renderScalar(type.key, true) + + return this.applyOptional(`Map<${key}, ${value}>`, required) + } + + renderArray(type: ArrayType, required: boolean): string { + const item = this.renderAnyType(type.item.type, type.item.required) + return this.applyOptional(`Array<${item}>`, required) + } + + renderScalar(type: ScalarType, required: boolean): string { + let scalar: string + + switch (type.scalar) { + case "Int": + scalar = "i32"; + break; + case "Int8": + scalar = "i8"; + break; + case "Int16": + scalar = "i16"; + break; + case "Int32": + scalar = "i32"; + break; + case "UInt": + scalar = "u32"; + break; + case "UInt32": + scalar = "u32"; + break; + case "UInt8": + scalar = "u8"; + break; + case "UInt16": + scalar = "u16"; + break; + case "String": + scalar = "string"; + break; + case "Boolean": + scalar = "bool"; + break; + case "Bytes": + scalar = "ArrayBuffer"; + break; + case "BigInt": + scalar = "BigInt"; + break; + case "BigNumber": + scalar = "BigNumber"; + break; + case "JSON": + scalar = "JSON.Value"; + break; + } + + return this.applyOptional(scalar, required) + } +} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/functions.ts b/packages/schema/bind/src/bindings/assemblyscript/functions.ts deleted file mode 100644 index f58c5d5e12..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/functions.ts +++ /dev/null @@ -1,254 +0,0 @@ -import { MustacheFn } from "../types"; -import { isBaseType, isKeyword } from "./types"; - -// check if any of the keywords match the property name; -// if there's a match, insert `_` at the beginning of the property name. -export const detectKeyword: MustacheFn = () => { - return (value: string, render: (template: string) => string): string => { - const type = render(value); - if (isKeyword(type)) { - return "_" + type; - } - return type; - }; -}; - -export const toMsgPack: MustacheFn = () => { - return (value: string, render: (template: string) => string) => { - let type = render(value); - - let modifier = ""; - if (type[type.length - 1] === "!") { - type = type.substring(0, type.length - 1); - } else { - modifier = "Optional"; - } - - if (type[0] === "[") { - return modifier + "Array"; - } - if (type.startsWith("Map<")) { - return modifier + "ExtGenericMap"; - } - switch (type) { - case "Int": - return modifier + "Int32"; - case "UInt": - return modifier + "UInt32"; - case "Boolean": - return modifier + "Bool"; - default: - return modifier + type; - } - }; -}; - -export const toWasmInit: MustacheFn = () => { - return (value: string, render: (template: string) => string) => { - let type = render(value); - - if (type[type.length - 1] === "!") { - type = type.substring(0, type.length - 1); - } else { - const nullType = toWasm()(value, render); - const nullOptional = "| null"; - - if (nullType.endsWith(nullOptional)) { - return "null"; - } - } - - if (type[0] === "[") { - return "[]"; - } - - if (type.startsWith("Map<")) { - const firstOpenBracketIdx = type.indexOf("<"); - const lastCloseBracketIdx = type.lastIndexOf(">"); - if (firstOpenBracketIdx === -1 || lastCloseBracketIdx === -1) { - throw new Error(`Invalid Map: ${type}`); - } - - const keyValTypes = type.substring( - firstOpenBracketIdx + 1, - lastCloseBracketIdx - ); - - const firstCommaIdx = keyValTypes.indexOf(","); - if (firstCommaIdx === -1) { - throw new Error(`Invalid Map: ${type}`); - } - - const keyType = keyValTypes.substring(0, firstCommaIdx).trim(); - const valType = keyValTypes.substring(firstCommaIdx + 1).trim(); - - const wasmKeyType = toWasm()(keyType, (str) => str); - const wasmValType = toWasm()(valType, (str) => str); - - return `new Map<${wasmKeyType}, ${wasmValType}>()`; - } - - switch (type) { - case "Int": - case "Int8": - case "Int16": - case "Int32": - case "UInt": - case "UInt8": - case "UInt16": - case "UInt32": - return "0"; - case "String": - return `""`; - case "Boolean": - return "false"; - case "Bytes": - return `new ArrayBuffer(0)`; - case "BigInt": - return `BigInt.fromUInt16(0)`; - case "BigNumber": - return `new BigNumber(BigInt.fromUInt16(0), 0, 0)`; - case "JSON": - return `JSON.Value.Null()`; - default: - if (type.includes("Enum_")) { - return "0"; - } else { - type = detectKeyword()(type, (str) => str); - return `new Types.${type}()`; - } - } - }; -}; - -export const toWasm: MustacheFn = () => { - return (value: string, render: (template: string) => string) => { - let type = render(value); - let isEnum = false; - - let optional = false; - if (type[type.length - 1] === "!") { - type = type.substring(0, type.length - 1); - } else { - optional = true; - } - - if (type[0] === "[") { - return toWasmArray(type, optional); - } - - if (type.startsWith("Map<")) { - return toWasmMap(type, optional); - } - - switch (type) { - case "Int": - type = "i32"; - break; - case "Int8": - type = "i8"; - break; - case "Int16": - type = "i16"; - break; - case "Int32": - type = "i32"; - break; - case "UInt": - case "UInt32": - type = "u32"; - break; - case "UInt8": - type = "u8"; - break; - case "UInt16": - type = "u16"; - break; - case "String": - type = "string"; - break; - case "Boolean": - type = "bool"; - break; - case "Bytes": - type = "ArrayBuffer"; - break; - case "BigInt": - type = "BigInt"; - break; - case "BigNumber": - type = "BigNumber"; - break; - case "JSON": - type = "JSON.Value"; - break; - default: - if (type.includes("Enum_")) { - type = type.replace("Enum_", ""); - isEnum = true; - } - type = detectKeyword()(type, (str) => str); - type = `Types.${type}`; - } - - return applyOptional(type, optional, isEnum); - }; -}; - -const toWasmArray = (type: string, optional: boolean): string => { - const result = type.match(/(\[)([[\]A-Za-z0-9_.!]+)(\])/); - - if (!result || result.length !== 4) { - throw Error(`Invalid Array: ${type}`); - } - - const wasmType = toWasm()(result[2], (str) => str); - return applyOptional("Array<" + wasmType + ">", optional, false); -}; - -const toWasmMap = (type: string, optional: boolean): string => { - const firstOpenBracketIdx = type.indexOf("<"); - const lastCloseBracketIdx = type.lastIndexOf(">"); - - if (firstOpenBracketIdx === -1 || lastCloseBracketIdx === -1) { - throw new Error(`Invalid Map: ${type}`); - } - - const keyValTypes = type.substring( - firstOpenBracketIdx + 1, - lastCloseBracketIdx - ); - - const firstCommaIdx = keyValTypes.indexOf(","); - if (firstCommaIdx === -1) { - throw new Error(`Invalid Map: ${type}`); - } - - const keyType = keyValTypes.substring(0, firstCommaIdx).trim(); - const valType = keyValTypes.substring(firstCommaIdx + 1).trim(); - - const wasmKeyType = toWasm()(keyType, (str) => str); - const wasmValType = toWasm()(valType, (str) => str); - - return applyOptional(`Map<${wasmKeyType}, ${wasmValType}>`, optional, false); -}; - -const applyOptional = ( - type: string, - optional: boolean, - isEnum: boolean -): string => { - if (optional) { - if ( - type.indexOf("Array") === 0 || - type.indexOf("string") === 0 || - (!isEnum && !isBaseType(type)) - ) { - return `${type} | null`; - } else { - return `Box<${type}> | null`; - } - } else { - return type; - } -}; diff --git a/packages/schema/bind/src/bindings/assemblyscript/helpers.ts b/packages/schema/bind/src/bindings/assemblyscript/helpers.ts new file mode 100644 index 0000000000..9a0e95f9f1 --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/helpers.ts @@ -0,0 +1,18 @@ +import Handlebars from "handlebars"; +import { isKeyword } from "./types"; +import { AssemblyscriptWasmInitRenderer } from "./renderers/WasmInitialValuesRenderer"; +import { MsgPackRenderer } from "./renderers/MsgPackRenderer"; +import { AssemblyscriptWasmRenderer } from "./renderers/WasmRenderer"; + +const msgPackRenderer = new MsgPackRenderer() +const wasmRenderer = new AssemblyscriptWasmRenderer(); +const wasmInitValueRenderer = new AssemblyscriptWasmInitRenderer(wasmRenderer); + +Handlebars.registerHelper("toMsgPack", msgPackRenderer.renderAnyType) +Handlebars.registerHelper("toWasmInit", wasmInitValueRenderer.renderAnyType) +Handlebars.registerHelper("toWasm", wasmRenderer.renderAnyType) +// check if any of the keywords match the property name; +// if there's a match, insert `_` at the beginning of the property name. +Handlebars.registerHelper("detectKeyword", (typeName: string) => { + return isKeyword(typeName) ? `_${typeName}`: typeName +}) \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/renderers/MsgPackRenderer.ts b/packages/schema/bind/src/bindings/assemblyscript/renderers/MsgPackRenderer.ts new file mode 100644 index 0000000000..fe9e547474 --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/renderers/MsgPackRenderer.ts @@ -0,0 +1,39 @@ +import { AnyType, ArrayType, ImportRefType, MapType, RefType, ScalarType } from "@polywrap/abi-types"; + +export class MsgPackRenderer { + private applyOptional(type: string, required: boolean): string { + return required ? type : `Optional${type}` + } + + renderAnyType(type: AnyType, required: boolean): string { + switch (type.kind) { + case "Array": return this.renderArray(type, required) + case "Map": return this.renderMap(type, required) + case "Scalar": return this.renderScalar(type, required) + case "Ref": return this.renderRef(type, required) + case "ImportRef": return this.renderRef(type, required) + case "UnlinkedImportRef": throw new Error(`Cannot render Unlinked Import refs. Attempted to render: ${type}`) + } + } + + renderRef(type: RefType | ImportRefType, required: boolean) { + return this.applyOptional(type.ref_name, required) + } + + renderMap(_: MapType, required: boolean): string { + return this.applyOptional(`ExtGenericMap`, required) + } + + renderArray(_: ArrayType, required: boolean): string { + return this.applyOptional(`Array`, required) + } + + renderScalar(type: ScalarType, required: boolean): string { + switch (type.scalar) { + case "Int": return this.applyOptional("Int32", required) + case "UInt":return this.applyOptional("UInt32", required) + case "Boolean": return this.applyOptional("Bool", required) + default: return this.applyOptional(type.scalar, required) + } + } +} \ No newline at end of file diff --git a/packages/schema/bind/src/WasmInitialValuesRenderer.ts b/packages/schema/bind/src/bindings/assemblyscript/renderers/WasmInitialValuesRenderer.ts similarity index 92% rename from packages/schema/bind/src/WasmInitialValuesRenderer.ts rename to packages/schema/bind/src/bindings/assemblyscript/renderers/WasmInitialValuesRenderer.ts index 2cce05925e..4f46afc920 100644 --- a/packages/schema/bind/src/WasmInitialValuesRenderer.ts +++ b/packages/schema/bind/src/bindings/assemblyscript/renderers/WasmInitialValuesRenderer.ts @@ -1,10 +1,10 @@ import { AnyType, RefType, ImportRefType, MapType, ArrayType, ScalarType } from "@polywrap/abi-types"; -import { isKeyword } from "./bindings/assemblyscript/types"; -import { WasmRenderer } from "./WasmRenderer"; +import { isKeyword } from "../types"; +import { Renderer } from "./types"; -export class WasmInitRenderer { +export class AssemblyscriptWasmInitRenderer { - constructor(protected wasmRenderer: WasmRenderer) {} + constructor(protected wasmRenderer: Renderer) {} private escapeKeyword(typeName: string): string { return isKeyword(typeName) ? `_${typeName}`: typeName diff --git a/packages/schema/bind/src/WasmRenderer.ts b/packages/schema/bind/src/bindings/assemblyscript/renderers/WasmRenderer.ts similarity index 96% rename from packages/schema/bind/src/WasmRenderer.ts rename to packages/schema/bind/src/bindings/assemblyscript/renderers/WasmRenderer.ts index 5e759f6c20..7f482680ac 100644 --- a/packages/schema/bind/src/WasmRenderer.ts +++ b/packages/schema/bind/src/bindings/assemblyscript/renderers/WasmRenderer.ts @@ -1,7 +1,7 @@ import { AnyType, ArrayType, ImportRefType, MapType, RefType, ScalarType } from "@polywrap/abi-types"; -import { isBaseType, isKeyword } from "./bindings/assemblyscript/types"; +import { isBaseType, isKeyword } from "../types"; -export class WasmRenderer { +export class AssemblyscriptWasmRenderer { private escapeKeyword(typeName: string): string { return isKeyword(typeName) ? `_${typeName}`: typeName } diff --git a/packages/schema/bind/src/bindings/assemblyscript/renderers/types.ts b/packages/schema/bind/src/bindings/assemblyscript/renderers/types.ts new file mode 100644 index 0000000000..12f94cffc9 --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/renderers/types.ts @@ -0,0 +1,9 @@ +import { AnyType, ArrayType, ImportRefType, MapType, RefType, ScalarType } from "@polywrap/abi-types"; + +export interface Renderer { + renderAnyType(type: AnyType, required: boolean): string + renderRef(type: RefType | ImportRefType, required: boolean): string + renderArray(type: ArrayType, required: boolean): string + renderMap(type: MapType, required: boolean): string + renderScalar(type: ScalarType, required: boolean): string +} \ No newline at end of file From d035703d6ae32de4981974c783df05236ad0712b Mon Sep 17 00:00:00 2001 From: namesty Date: Fri, 7 Apr 2023 17:28:41 +0200 Subject: [PATCH 89/90] (chore): removed legacy templating code --- packages/schema/bind/src/MsgPackRenderer.ts | 103 ------------------ .../bind/src/bindings/assemblyscript/index.ts | 2 +- .../src/bindings/assemblyscript/wasm/index.ts | 32 +----- packages/schema/bind/src/bindings/utils.ts | 7 +- packages/schema/bind/src/types.ts | 4 +- 5 files changed, 9 insertions(+), 139 deletions(-) delete mode 100644 packages/schema/bind/src/MsgPackRenderer.ts diff --git a/packages/schema/bind/src/MsgPackRenderer.ts b/packages/schema/bind/src/MsgPackRenderer.ts deleted file mode 100644 index d734c63df1..0000000000 --- a/packages/schema/bind/src/MsgPackRenderer.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { AnyType, ArrayType, ImportRefType, MapType, RefType, ScalarType } from "@polywrap/abi-types"; -import { isBaseType, isKeyword } from "./bindings/assemblyscript/types"; - -export class WasmRenderer { - private escapeKeyword(typeName: string): string { - return isKeyword(typeName) ? `_${typeName}`: typeName - } - - private applyOptional(type: string, required: boolean): string { - if (!required) { - if ( - type.indexOf("Array") === 0 || - type.indexOf("string") === 0 || - (!isEnum && !isBaseType(type)) - ) { - return `${type} | null`; - } else { - return `Box<${type}> | null`; - } - } else { - return type; - } - } - - renderAnyType(type: AnyType, required: boolean): string { - switch (type.kind) { - case "Array": return this.renderArray(type, required) - case "Map": return this.renderMap(type, required) - case "Scalar": return this.renderScalar(type, required) - case "Ref": return this.renderRef(type, required) - case "ImportRef": return this.renderRef(type, required) - case "UnlinkedImportRef": throw new Error(`Cannot render Unlinked Import refs. Attempted to render: ${type}`) - } - } - - renderRef(type: RefType | ImportRefType, required: boolean) { - const escapedName = this.escapeKeyword(type.ref_name) - return this.applyOptional(`Types.${escapedName}`, required) - } - - renderMap(type: MapType, required: boolean): string { - const value = this.renderAnyType(type.value.type, type.value.required) - const key = this.renderScalar(type.key, true) - - return this.applyOptional(`Map<${key}, ${value}>`, required) - } - - renderArray(type: ArrayType, required: boolean): string { - const item = this.renderAnyType(type.item.type, type.item.required) - return this.applyOptional(`Array<${item}>`, required) - } - - renderScalar(type: ScalarType, required: boolean): string { - let scalar: string - - switch (type.scalar) { - case "Int": - scalar = "i32"; - break; - case "Int8": - scalar = "i8"; - break; - case "Int16": - scalar = "i16"; - break; - case "Int32": - scalar = "i32"; - break; - case "UInt": - scalar = "u32"; - break; - case "UInt32": - scalar = "u32"; - break; - case "UInt8": - scalar = "u8"; - break; - case "UInt16": - scalar = "u16"; - break; - case "String": - scalar = "string"; - break; - case "Boolean": - scalar = "bool"; - break; - case "Bytes": - scalar = "ArrayBuffer"; - break; - case "BigInt": - scalar = "BigInt"; - break; - case "BigNumber": - scalar = "BigNumber"; - break; - case "JSON": - scalar = "JSON.Value"; - break; - } - - return this.applyOptional(scalar, required) - } -} \ No newline at end of file diff --git a/packages/schema/bind/src/bindings/assemblyscript/index.ts b/packages/schema/bind/src/bindings/assemblyscript/index.ts index a01310ad9e..88be4e61d7 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/index.ts +++ b/packages/schema/bind/src/bindings/assemblyscript/index.ts @@ -1,3 +1,3 @@ export * as Wasm from "./wasm"; -export * as Functions from "./functions"; +export * as Functions from "./helpers"; export * as Types from "./types"; diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts b/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts index bc8150a50c..332683804a 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts @@ -1,14 +1,7 @@ -import { Functions } from "../"; import { GenerateBindingFn, renderTemplates } from "../.."; import { loadSubTemplates } from "../../utils"; import { BindOptions, BindOutput } from "../../.."; -import { - addFirstLast, - extendType, - toPrefixedGraphQLType, -} from "@polywrap/schema-parse"; -import { AbiVisitor, IAbiVisitorEnterAndLeave } from "@polywrap/schema-abi"; import { Abi, EnumDef, FunctionDef, ObjectDef } from "@polywrap/abi-types"; import { OutputEntry, readDirectorySync } from "@polywrap/os-js"; import path from "path"; @@ -79,7 +72,7 @@ export const generateBinding: GenerateBindingFn = ( outputDirAbs: options.outputDirAbs, }; const output = result.output; - const abi = applyTransforms(options.abi); + const abi = options.abi; // Generate object type folders if (abi.objects) { @@ -187,26 +180,3 @@ export const generateBinding: GenerateBindingFn = ( return result; }; - -const transformAbi = (abi: Abi, transform: IAbiVisitorEnterAndLeave) => { - const abiClone: Abi = JSON.parse(JSON.stringify(abi)); - const visitor = new AbiVisitor(transform); - - visitor.visit(abiClone); - - return abiClone; -} - -function applyTransforms(abi: Abi): Abi { - const transforms: IAbiVisitorEnterAndLeave[] = [ - extendType(Functions), - addFirstLast, - toPrefixedGraphQLType, - ]; - - for (const transform of transforms) { - abi = transformAbi(abi, transform); - } - - return abi; -} diff --git a/packages/schema/bind/src/bindings/utils.ts b/packages/schema/bind/src/bindings/utils.ts index fac191d695..05def36c96 100644 --- a/packages/schema/bind/src/bindings/utils.ts +++ b/packages/schema/bind/src/bindings/utils.ts @@ -1,5 +1,4 @@ import { OutputEntry, readDirectorySync } from "@polywrap/os-js"; -import Mustache from "mustache"; import path from "path"; export function renderTemplates( @@ -21,7 +20,11 @@ export function renderTemplates( // file templates don't contain '_' if (name.indexOf("_") === -1) { - const data = Mustache.render(dirent.data, view, subTemplates); + Object.entries(subTemplates).forEach(([partialName, partialTemplate]) => { + Handlebars.registerPartial(partialName, partialTemplate) + }) + + const data = Handlebars.compile(dirent.data)(view) // If the file isn't empty, add it to the output if (data) { diff --git a/packages/schema/bind/src/types.ts b/packages/schema/bind/src/types.ts index 3ae6aa13dc..6cabd56791 100644 --- a/packages/schema/bind/src/types.ts +++ b/packages/schema/bind/src/types.ts @@ -1,5 +1,5 @@ +import { Abi } from "@polywrap/abi-types"; import { OutputDirectory } from "@polywrap/os-js"; -import { WrapAbi } from "@polywrap/schema-parse"; export type BindLanguage = "wasm-as" | "wasm-rs" | "plugin-ts" | "app-ts"; @@ -11,7 +11,7 @@ export interface BindOutput { export interface BindOptions { projectName: string; bindLanguage: BindLanguage; - abi: WrapAbi; + abi: Abi; config?: Record; outputDirAbs: string; } From 3b6bb0019fa8913be3453caee749c714f05007a5 Mon Sep 17 00:00:00 2001 From: namesty Date: Fri, 14 Apr 2023 19:44:52 +0200 Subject: [PATCH 90/90] (chore): updated object type templates --- .../src/bindings/assemblyscript/wasm/index.ts | 8 +- .../wasm/templates/object-type/index-ts.hbs | 4 +- .../object-type/serialization-ts.hbs | 139 +++++++++--------- 3 files changed, 75 insertions(+), 76 deletions(-) diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts b/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts index 332683804a..7be51e44ef 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts @@ -96,13 +96,15 @@ export const generateBinding: GenerateBindingFn = ( const importedFuncs = getImportedFuncs(abi) // Generate imported module type folders - for (const importedModuleType of importedFuncs) { + if (importedFuncs.length) { importEntries.push({ type: "Directory", - name: importedModuleType.name, + name: "Module", data: renderTemplates( templatePath("imported/module-type"), - importedModuleType, + { + functions: importedFuncs + }, subTemplates ), }); diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.hbs index e0c6e86359..27329d271f 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.hbs +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/index-ts.hbs @@ -15,8 +15,8 @@ import { import * as Types from ".."; export class {{detectKeyword name}} { - {{#each properties}} - {{detectKeyword this.name}}: {{toWasm this.type}}; + {{#each props}} + {{detectKeyword name}}: {{toWasm type}}; {{/each}} static toBuffer(type: {{detectKeyword name}}): ArrayBuffer { diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/serialization-ts.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/serialization-ts.hbs index a088c024db..3ca4f0849d 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/serialization-ts.hbs +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/serialization-ts.hbs @@ -1,8 +1,8 @@ {{> serialization_imports}} -import { {{#detectKeyword}}{{name}}{{/detectKeyword}} } from "./"; +import { {{detectKeyword name}} } from "./"; import * as Types from ".."; -export function serialize{{name}}(type: {{#detectKeyword}}{{name}}{{/detectKeyword}}): ArrayBuffer { +export function serialize{{name}}(type: {{detectKeyword name}}): ArrayBuffer { const sizerContext: Context = new Context("Serializing (sizing) object-type: {{name}}"); const sizer = new WriteSizer(sizerContext); write{{name}}(sizer, type); @@ -13,101 +13,98 @@ export function serialize{{name}}(type: {{#detectKeyword}}{{name}}{{/detectKeywo return buffer; } -export function write{{name}}(writer: Write, type: {{#detectKeyword}}{{name}}{{/detectKeyword}}): void { - {{#properties.length}} - writer.writeMapLength({{properties.length}}); - {{/properties.length}} - {{^properties}} +export function write{{name}}(writer: Write, type: {{detectKeyword name}}): void { + {{#if props.length}} + writer.writeMapLength({{props.length}}); + {{else}} writer.writeMapLength(0); - {{/properties}} - {{#properties}} - writer.context().push("{{name}}", "{{#toWasm}}{{toGraphQLType}}{{/toWasm}}", "writing property"); + {{/if}} + {{#each props}} + writer.context().push("{{name}}", "{{toWasm type}}", "writing property"); writer.writeString("{{name}}"); - {{#scalar}} - writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); - {{/scalar}} - {{#array}} - writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(type.{{#detectKeyword}}{{name}}{{/detectKeyword}}, (writer: Write, item: {{#item}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/item}}): void => { + {{#if (eq type.kind "Scalar") }} + writer.write{{toMsgPack type}}(type.{{detectKeyword name}}); + {{/if}} + {{#if (eq type.kind "Array") }} + writer.write{{toMsgPack type}}(type.{{detectKeyword name}}, (writer: Write, item: {{toWasm type.item}}): void => { {{> serialize_array}} }); - {{/array}} - {{#map}} - writer.write{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(type.{{#detectKeyword}}{{name}}{{/detectKeyword}}, (writer: Write, key: {{#key}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/key}}) => { - writer.write{{#key}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/key}}(key); - }, (writer: Write, value: {{#value}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/value}}): void => { + {{/if}} + {{#if (eq type.kind "Map") }} + writer.write{{toMsgPack type}}(type.{{detectKeyword name}}, (writer: Write, key: {{toWasm type.key}}) => { + writer.write{{toMsgPack type.key}}(key); + }, (writer: Write, value: {{toWasm type.value}}): void => { {{> serialize_map_value}} }); - {{/map}} - {{#object}} - {{#required}} - Types.{{#detectKeyword}}{{name}}{{/detectKeyword}}.write(writer, type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); - {{/required}} - {{^required}} - if (type.{{#detectKeyword}}{{name}}{{/detectKeyword}}) { - Types.{{#detectKeyword}}{{name}}{{/detectKeyword}}.write(writer, type.{{#detectKeyword}}{{name}}{{/detectKeyword}} as Types.{{#detectKeyword}}{{name}}{{/detectKeyword}}); - } else { - writer.writeNil(); - } - {{/required}} - {{/object}} - {{#enum}} - {{#required}} - writer.writeInt32(type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); - {{/required}} - {{^required}} - writer.writeOptionalInt32(type.{{#detectKeyword}}{{name}}{{/detectKeyword}}); - {{/required}} - {{/enum}} + {{/if}} + {{#if (eq type.kind "Ref")}} + {{#if (eq type.ref_kind "Object") }} + {{#if required}} + Types.{{detectKeyword name}}.write(writer, type.{{detectKeyword name}}); + {{else}} + if (type.{{detectKeyword name}}) { + Types.{{detectKeyword name}}.write(writer, type.{{detectKeyword name}} as Types.{{detectKeyword name}}); + } else { + writer.writeNil(); + } + {{/if}} + {{/if}} + {{#if (eq type.ref_kind "Enum") }} + {{#if required}} + writer.writeInt32(type.{{detectKeyword name}}); + {{else}} + writer.writeOptionalInt32(type.{{detectKeyword name}}); + {{/if}} + {{/if}} + {{/if}} writer.context().pop(); - {{/properties}} + {{/each}} } -export function deserialize{{name}}(buffer: ArrayBuffer): {{#detectKeyword}}{{name}}{{/detectKeyword}} { +export function deserialize{{name}}(buffer: ArrayBuffer): {{detectKeyword name}} { const context: Context = new Context("Deserializing object-type {{name}}"); const reader = new ReadDecoder(buffer, context); return read{{name}}(reader); } -export function read{{name}}(reader: Read): {{#detectKeyword}}{{name}}{{/detectKeyword}} { +export function read{{name}}(reader: Read): {{detectKeyword name}} { let numFields = reader.readMapLength(); - {{#properties}} - {{^object}} - let _{{name}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} = {{#toWasmInit}}{{toGraphQLType}}{{/toWasmInit}}; - {{/object}} - {{#object}} - {{#required}} - let _{{name}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} | null = null; - {{/required}} - {{^required}} - let _{{name}}: {{#toWasm}}{{toGraphQLType}}{{/toWasm}} = {{#toWasmInit}}{{toGraphQLType}}{{/toWasmInit}}; - {{/required}} - {{/object}} - {{#required}} + {{#each props}} + {{#if (eq object)}} + {{#if (eq required)}} + let _{{name}}: {{toWasm type}} | null = null; + {{else}} + let _{{name}}: {{toWasm type}} = {{toWasmInit type}}; + {{/if}} + {{else}} + let _{{name}}: {{toWasm type}} = {{toWasmInit type}}; + {{/if}} + {{#if required}} let _{{name}}Set: bool = false; - {{/required}} - {{/properties}} + {{/if}} + {{/each}} while (numFields > 0) { numFields--; const field = reader.readString(); reader.context().push(field, "unknown", "searching for property type"); - {{#properties}} + {{#each props}} {{^first}}else {{/first}}if (field == "{{name}}") { - reader.context().push(field, "{{#toWasm}}{{toGraphQLType}}{{/toWasm}}", "type found, reading property"); + reader.context().push(field, "{{toWasm type}}", "type found, reading property"); {{#scalar}} _{{name}} = reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}(); {{/scalar}} {{#array}} - _{{name}} = reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}((reader: Read): {{#item}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/item}} => { + _{{name}} = reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}((reader: Read): {{#item}}{{toWasm item}}{{/item}} => { {{> deserialize_array}} }); {{/array}} {{#map}} - _{{name}} = reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}((reader: Read): {{#key}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/key}} => { + _{{name}} = reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}((reader: Read): {{#key}}{{toWasm item}}{{/key}} => { return reader.read{{#key}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/key}}(); - }, (reader: Read): {{#value}}{{#toWasm}}{{toGraphQLType}}{{/toWasm}}{{/value}} => { + }, (reader: Read): {{#value}}{{toWasm item}}{{/value}} => { {{> deserialize_map_value}} }); {{/map}} @@ -124,12 +121,12 @@ export function read{{name}}(reader: Read): {{#detectKeyword}}{{name}}{{/detectK {{/required}} reader.context().pop(); } - {{/properties}} + {{/each}} reader.context().pop(); } - {{#properties}} - {{#required}} + {{#each props}} + {{#if (eq required)}} {{^object}} if (!_{{name}}Set) { {{/object}} @@ -139,11 +136,11 @@ export function read{{name}}(reader: Read): {{#detectKeyword}}{{name}}{{/detectK throw new Error(reader.context().printWithContext("Missing required property: '{{name}}: {{name}}'")); } {{/required}} - {{/properties}} + {{/each}} return { - {{#properties}} - {{#detectKeyword}}{{name}}{{/detectKeyword}}: _{{name}}{{^last}},{{/last}} - {{/properties}} + {{#props}} + {{detectKeyword name}}: _{{name}}{{^last}},{{/last}} + {{/props}} }; }