diff --git a/packages/schema/parse/.eslintrc.js b/packages/schema/abi-types/.eslintrc.js similarity index 100% rename from packages/schema/parse/.eslintrc.js rename to packages/schema/abi-types/.eslintrc.js diff --git a/packages/schema/parse/README.md b/packages/schema/abi-types/README.md similarity index 100% rename from packages/schema/parse/README.md rename to packages/schema/abi-types/README.md diff --git a/packages/schema/compose/jest.config.js b/packages/schema/abi-types/jest.config.js similarity index 100% rename from packages/schema/compose/jest.config.js rename to packages/schema/abi-types/jest.config.js diff --git a/packages/schema/compose/package.json b/packages/schema/abi-types/package.json similarity index 68% rename from packages/schema/compose/package.json rename to packages/schema/abi-types/package.json index 239afe0b6d..7d86762b0d 100644 --- a/packages/schema/compose/package.json +++ b/packages/schema/abi-types/package.json @@ -1,6 +1,6 @@ { - "name": "@polywrap/schema-compose", - "description": "Polywrap Schema Composition", + "name": "@polywrap/abi-types", + "description": "Polywrap ABI Core Types", "version": "0.10.0-pre.5", "license": "MIT", "repository": { @@ -13,24 +13,17 @@ ], "scripts": { "build": "rimraf ./build && tsc --project tsconfig.build.json", - "lint": "eslint --color -c ../../../.eslintrc.js src/", + "lint": "eslint --color 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/deep-equal": "1.0.1", "@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", diff --git a/packages/schema/abi-types/src/definitions.ts b/packages/schema/abi-types/src/definitions.ts new file mode 100644 index 0000000000..a00a2fed1d --- /dev/null +++ b/packages/schema/abi-types/src/definitions.ts @@ -0,0 +1,182 @@ +/// 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 { + id: string; + uri: string; + type: ImportAbiType; + namespace: string; + imports?: ImportedAbi[]; +} + +/// Definitions (user-defined) + +export type AnyDef = FunctionDef | ObjectDef | EnumDef | PropertyDef | ArgumentDef | ResultDef; + +export type UniqueDefKind = + | "Function" + | "Object" + | "Enum" + +export type DefKind = + | UniqueDefKind + | "Argument" + | "Result" + | "Property"; + +export interface Def { + kind: DefKind; +} + +export interface NamedDef extends Def { + name: string; +} + +export interface InlinedTypeDef extends Def, OptionalType { } + +export interface NamedTypeDef extends NamedDef, InlinedTypeDef { } + +export interface FunctionDef extends NamedDef { + kind: "Function"; + args: ArgumentDef[]; + result: ResultDef; +} + +export interface ArgumentDef extends NamedTypeDef { + kind: "Argument"; +} + +export interface ResultDef extends InlinedTypeDef { + kind: "Result"; +} + +export interface ObjectDef extends NamedDef { + kind: "Object"; + props: PropertyDef[]; +} + +export interface PropertyDef extends NamedTypeDef { + kind: "Property"; +} + +export interface EnumDef extends NamedDef { + kind: "Enum"; + constants: string[]; +} + +/// Types (built-ins) + +export type AnyType = + | ScalarType + | ArrayType + | MapType + | RefType + | ImportRefType + | UnlinkedImportRefType; + +export type TypeKind = + | "Scalar" + | "Array" + | "Map" + | "Ref" + | "ImportRef" + | "UnlinkedImportRef" + +export interface Type { + kind: TypeKind; +} + +export interface ScalarType< + TScalarTypeName extends ScalarTypeName = ScalarTypeName +> extends Type { + kind: "Scalar"; + scalar: TScalarTypeName; +} + +export interface ArrayType extends Type { + kind: "Array"; + item: OptionalType; +} + +export interface MapType extends Type { + kind: "Map"; + key: ScalarType; + value: OptionalType; +} + +export interface RefType extends Type { + kind: "Ref"; + ref_kind: UniqueDefKind; + ref_name: string; +} + +export interface ImportRefType extends Type { + kind: "ImportRef"; + import_id: string; + ref_kind: UniqueDefKind; + ref_name: string; +} + +export interface UnlinkedImportRefType extends Type { + kind: "UnlinkedImportRef"; + namespaced_ref_name: string; +} + +export interface OptionalType { + required: boolean; + type: AnyType; +} + +/// 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; + +export type AnyTypeOrDef = AnyType | AnyDef; \ No newline at end of file diff --git a/packages/schema/abi-types/src/index.ts b/packages/schema/abi-types/src/index.ts new file mode 100644 index 0000000000..ec02a9a686 --- /dev/null +++ b/packages/schema/abi-types/src/index.ts @@ -0,0 +1,46 @@ +import { Abi } from "./definitions"; + +export * 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 + parseLocalImportStatements: (schema: string) => Promise + parse: (schema: string) => Promise +} + +export interface SchemaRenderer { + render: (abi: Abi) => Promise +} + +export interface ParserOptions { + noValidate?: boolean; +} + +export interface ExternalSchemaFetcher { + fetch: (uri: string) => Promise; +} + +export interface LocalSchemaFetcher { + fetch: (path: string) => Promise; +} + +export function createAbi(): Abi { + return { + version: "0.2" + }; +} diff --git a/packages/schema/compose/tsconfig.build.json b/packages/schema/abi-types/tsconfig.build.json similarity index 100% rename from packages/schema/compose/tsconfig.build.json rename to packages/schema/abi-types/tsconfig.build.json diff --git a/packages/schema/parse/tsconfig.json b/packages/schema/abi-types/tsconfig.json similarity index 100% rename from packages/schema/parse/tsconfig.json rename to packages/schema/abi-types/tsconfig.json diff --git a/packages/schema/abi/.eslintrc.js b/packages/schema/abi/.eslintrc.js new file mode 100644 index 0000000000..b315b11479 --- /dev/null +++ b/packages/schema/abi/.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/README.md b/packages/schema/abi/README.md new file mode 100644 index 0000000000..d0e8400831 --- /dev/null +++ b/packages/schema/abi/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/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 81% rename from packages/schema/parse/package.json rename to packages/schema/abi/package.json index 18bf0a8823..6032f5c18d 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": { @@ -19,9 +19,8 @@ "test:watch": "jest --watch --passWithNoTests --verbose" }, "dependencies": { - "@dorgjelli/graphql-schema-cycles": "1.1.4", - "@polywrap/wrap-manifest-types-js": "0.10.0-pre.5", - "graphql": "15.5.0" + "@polywrap/abi-types": "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/abi/src/AbiImportsLinker.ts b/packages/schema/abi/src/AbiImportsLinker.ts new file mode 100644 index 0000000000..9b44195e3f --- /dev/null +++ b/packages/schema/abi/src/AbiImportsLinker.ts @@ -0,0 +1,191 @@ +import { IAbiMerger } from "./AbiMerger"; +import { IAbiTreeShaker } from "./AbiTreeShaker"; + +import { Abi, ExternalImportStatement, ExternalSchemaFetcher, ImportedAbi, LocalImportStatement, LocalSchemaFetcher, SchemaParser, UniqueDefKind } from "@polywrap/abi-types" +import { AbiVisitor } from "./AbiVisitor"; +import { LinkerVisitor } from "./LinkerVisitor"; + +export interface IAbiImportsLinker { + link: (rootAbi: Abi, importStatements?: { + local: LocalImportStatement[]; + external: ExternalImportStatement[]; + }) => Promise +} + +export class AbiImportsLinker implements IAbiImportsLinker { + constructor(protected _schemaParser: SchemaParser, protected _fetchers: { + external: ExternalSchemaFetcher; + local: LocalSchemaFetcher; + }, protected _abiMerger: IAbiMerger, protected _abiTreeShaker: IAbiTreeShaker) { } + + async embedExternalImports(rootAbi: Abi | ImportedAbi, extImportStatements: ExternalImportStatement[]) { + let abiClone: Abi | ImportedAbi = JSON.parse(JSON.stringify(rootAbi)); + + for await (const extImportStatement of extImportStatements) { + const externalAbi = await this._fetchers.external.fetch(extImportStatement.uriOrPath); + const importedAbi: ImportedAbi = { + objects: externalAbi.objects, + enums: externalAbi.enums, + functions: externalAbi.functions, + imports: externalAbi.imports, + 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 | ImportedAbi, localImportStatements: LocalImportStatement[]): Promise<{ + abi: Abi | ImportedAbi, + transitiveExternalImports: ExternalImportStatement[] + }> { + let mergedAbi: Abi | ImportedAbi = JSON.parse(JSON.stringify(rootAbi)) + const transitiveExternalImports: ExternalImportStatement[] = [] + + for await (const localImportStatement of localImportStatements) { + 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) + + const transitiveExtImports = await this._schemaParser.parseExternalImportStatements(localSchema) + transitiveExternalImports.push(...transitiveExtImports) + + const transitiveLocalImports = await this._schemaParser.parseLocalImportStatements(localSchema) + const subResult = await this.mergeLocalImports(localShakenAbi, transitiveLocalImports) + mergedAbi = this._abiMerger.merge(mergedAbi as Abi, [subResult.abi]) + transitiveExternalImports.push(...subResult.transitiveExternalImports) + } + + return { + abi: mergedAbi, + transitiveExternalImports + } + } + + 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 LinkerVisitor({ + enter: { + UnlinkedImportRefType: (refType) => { + 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.namespaced_ref_name) + + if (!foundDefinitionKind) { + throw new Error(`Could not find local definition for ${refType.namespaced_ref_name}`) + } + + return { + kind: "Ref", + ref_name: refType.namespaced_ref_name, + ref_kind: foundDefinitionKind + } + } else { + // 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("_")) + + if (!importAbi) { + 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.namespaced_ref_name}`) + } + + return { + kind: "ImportRef", + ref_name: refName, + ref_kind: foundDefinition.kind, + import_id: importAbi.absoluteIdPath + } + } + } + }, + }) + + linkVisitor.visit(abiClone) + return abiClone + } + + async link(rootAbi: Abi, importStatements?: { + local?: LocalImportStatement[]; + external?: ExternalImportStatement[]; + }): Promise { + const localImportStatementsFromRoot = importStatements?.local || [] + const externalImportStatementsFromRoot = importStatements?.external || [] + const { abi: abiWithLocalImports, transitiveExternalImports } = await this.mergeLocalImports(rootAbi, localImportStatementsFromRoot) + + const externalImportStatements = [...externalImportStatementsFromRoot, ...transitiveExternalImports] + const abiWithExtImports = await this.embedExternalImports(abiWithLocalImports, externalImportStatements) + 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 new file mode 100644 index 0000000000..daeac36b67 --- /dev/null +++ b/packages/schema/abi/src/AbiMerger.ts @@ -0,0 +1,24 @@ +import { Abi, AbiDefs, ImportedAbi } from "@polywrap/abi-types"; + +export interface IAbiMerger { + merge(rootAbi: Abi, abisToMerge: (Abi | ImportedAbi)[]): Abi + mergeDefs(defs: AbiDefs[]): AbiDefs +} + +export class AbiMerger implements IAbiMerger { + merge(rootAbi: Abi, abisToMerge: (Abi | ImportedAbi)[]): Abi { + return { + ...rootAbi, + ...this.mergeDefs([rootAbi, ...abisToMerge]), + imports: [rootAbi, ...abisToMerge].reduce((acc, abi) => [...acc, ...(abi.imports ?? [])], []) + } + } + + 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 ?? [])], []) + } + } +} \ No newline at end of file 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/AbiTreeShaker.ts b/packages/schema/abi/src/AbiTreeShaker.ts new file mode 100644 index 0000000000..f4ed0fb36d --- /dev/null +++ b/packages/schema/abi/src/AbiTreeShaker.ts @@ -0,0 +1,224 @@ +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 | ImportedAbi, ref: string): ReferenceableDef | undefined + shakeTree(abi: Abi | ImportedAbi, neededDefNames: string[]): Abi | ImportedAbi + shakeImports(abi: Abi | ImportedAbi): Abi | ImportedAbi +} + +export class AbiTreeShaker implements IAbiTreeShaker { + 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({ + enter: { + ObjectDef: (def) => { + if (def.name === refName) { + found = def; + } + }, + EnumDef: (def) => { + if (def.name === refName) { + found = def; + } + } + } + }); + + visitor.visit(abi); + + return found; + } + + extractNeededDefinitions(abi: Abi | ImportedAbi, 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 refsWithAbsIds: ImportRefType[] = []; + const state: { currentId: string[] } = { currentId: [] }; + const abiVisitor = new AbiVisitor({ + enter: { + Import: (importDef) => { + state.currentId.push(importDef.id); + }, + ImportRefType: (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 refsWithAbsIds; + } + + 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 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.ref_name) + + 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; + } + + private _shakeImports(abi: Abi, neededImports: ImportRefType[]): Abi { + + 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 = state.neededImports + .filter((neededImport) => neededImport.import_id === currentId) + .map((neededImport) => neededImport.ref_name) + const shakenImportDef = this.shakeLocalDefinitions(importDef, neededFromThisImport) as ImportedAbi + + return shakenImportDef + } + + }, + leave: { + Import: () => { + state.currentIdPath.pop() + } + } + }); + + importsVisitor.visit(abiClone); + + return abiClone; + } + + shakeImports(abi: Abi): Abi { + const neededImports = this.extractImportReferences(abi); + const treeWithShakenImports = this._shakeImports(abi, neededImports); + + return treeWithShakenImports + } + + shakeLocalDefinitions(abi: Abi | ImportedAbi, neededDefNames: string[]): Abi | ImportedAbi { + const neededDefs = this.extractNeededDefinitions(abi, neededDefNames); + const referencedDefs = this.extractReferencedSiblingDefinitions(abi, neededDefs); + const shakenDefs = this._abiMerger.mergeDefs([neededDefs, referencedDefs]) + + return { + ...abi, + objects: shakenDefs.objects, + enums: shakenDefs.enums, + functions: shakenDefs.functions, + } + } + + shakeTree(abi: Abi, neededDefNames: string[]): Abi | ImportedAbi { + const shakenTree = this.shakeLocalDefinitions(abi, neededDefNames); + const shakenTreeWithShakenImports = this.shakeImports(shakenTree as Abi); + return shakenTreeWithShakenImports; + } +} \ No newline at end of file diff --git a/packages/schema/abi/src/AbiVisitor.ts b/packages/schema/abi/src/AbiVisitor.ts new file mode 100644 index 0000000000..13b04fafdc --- /dev/null +++ b/packages/schema/abi/src/AbiVisitor.ts @@ -0,0 +1,274 @@ +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; + +// NOTE: does not visit map keys +export interface IAbiVisitor { + Abi?: VisitorFunction; + Import?: VisitorFunction; + FunctionDef?: VisitorFunction; + ArgumentDef?: VisitorFunction; + ResultDef?: VisitorFunction; + ObjectDef?: VisitorFunction; + PropertyDef?: VisitorFunction; + EnumDef?: VisitorFunction; + ScalarType?: VisitorFunction; + RefType?: VisitorFunction; + ImportRefType?: VisitorFunction; + ArrayType?: VisitorFunction; + MapType?: VisitorFunction; + AnyType?: VisitorFunction; +} + +export interface IAbiVisitorEnterAndLeave { + enter?: IAbiVisitor; + leave?: IAbiVisitor; +} + +export class AbiVisitor implements IAbiVisitor { + constructor(protected readonly visitor: IAbiVisitorEnterAndLeave) { } + + protected coerceVoidToUndefined(value: T | void): T | undefined { + return value === undefined ? undefined : value; + } + + Abi(node: Abi | ImportedAbi): Abi | ImportedAbi { + let mutatedNode = node; + + if (this.visitor.enter?.Abi) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.Abi(node)) ?? mutatedNode; + } + + 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) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.Abi(node)) ?? mutatedNode; + } + + return mutatedNode; + } + + Import(node: ImportedAbi): ImportedAbi { + let mutatedNode = node; + + if (this.visitor.enter?.Import) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.Import(mutatedNode)) ?? mutatedNode; + } + + 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) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.Import(mutatedNode)) ?? mutatedNode; + } + + return mutatedNode; + } + + FunctionDef(node: FunctionDef): FunctionDef { + let mutatedNode = node; + + if (this.visitor.enter?.FunctionDef) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.FunctionDef(mutatedNode)) ?? mutatedNode; + } + + mutatedNode.args = mutatedNode.args?.map((argumentNode) => this.ArgumentDef(argumentNode)); + mutatedNode.result = this.ResultDef(mutatedNode.result); + + if (this.visitor.leave?.FunctionDef) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.FunctionDef(mutatedNode)) ?? mutatedNode; + } + + return mutatedNode; + } + + ArgumentDef(node: ArgumentDef): ArgumentDef { + let mutatedNode = node; + + if (this.visitor.enter?.ArgumentDef) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.ArgumentDef(mutatedNode)) ?? mutatedNode; + } + + mutatedNode.type = this.AnyType(mutatedNode.type); + + if (this.visitor.leave?.ArgumentDef) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.ArgumentDef(mutatedNode)) ?? mutatedNode; + } + + return mutatedNode; + } + + ResultDef(node: ResultDef): ResultDef { + let mutatedNode = node; + if (this.visitor.enter?.ResultDef) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.ResultDef(mutatedNode)) ?? mutatedNode; + } + mutatedNode.type = this.AnyType(mutatedNode.type); + + if (this.visitor.leave?.ResultDef) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.ResultDef(mutatedNode)) ?? mutatedNode; + } + + return mutatedNode; + } + + ObjectDef(node: ObjectDef): ObjectDef { + let mutatedNode = node; + if (this.visitor.enter?.ObjectDef) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.ObjectDef(mutatedNode)) ?? mutatedNode; + } + + mutatedNode.props = mutatedNode.props?.map((propertyNode) => this.PropertyDef(propertyNode)); + + if (this.visitor.leave?.ObjectDef) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.ObjectDef(mutatedNode)) ?? mutatedNode; + } + + return mutatedNode; + } + + PropertyDef(node: PropertyDef): PropertyDef { + let mutatedNode = node; + + if (this.visitor.enter?.PropertyDef) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.PropertyDef(mutatedNode)) ?? mutatedNode; + } + + mutatedNode.type = this.AnyType(mutatedNode.type); + + if (this.visitor.leave?.PropertyDef) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave?.PropertyDef(mutatedNode)) ?? mutatedNode; + } + + return mutatedNode; + } + + EnumDef(node: EnumDef): EnumDef { + let mutatedNode = node; + + if (this.visitor.enter?.EnumDef) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.EnumDef(mutatedNode)) ?? mutatedNode; + } + + if (this.visitor.leave?.EnumDef) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.EnumDef(mutatedNode)) ?? mutatedNode; + } + + return mutatedNode; + } + + 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": + mutatedNode = this.ScalarType(mutatedNode); + break; + case "Array": + mutatedNode = this.ArrayType(mutatedNode); + break; + case "Map": + mutatedNode = this.MapType(mutatedNode); + break; + case "Ref": + mutatedNode = this.RefType(mutatedNode); + break; + case "ImportRef": + mutatedNode = this.ImportRefType(mutatedNode); + break; + } + + if (this.visitor.leave?.AnyType) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.AnyType(mutatedNode)) ?? mutatedNode; + } + + return mutatedNode; + } + + ScalarType(node: ScalarType): ScalarType { + let mutatedNode = node; + + if (this.visitor.enter?.ScalarType) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.ScalarType(mutatedNode)) ?? mutatedNode; + } + + if (this.visitor.leave?.ScalarType) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.ScalarType(mutatedNode)) ?? mutatedNode; + } + + return mutatedNode; + } + + RefType(node: RefType): RefType { + let mutatedNode = node; + + if (this.visitor.enter?.RefType) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.RefType(mutatedNode)) ?? mutatedNode; + } + + if (this.visitor.leave?.RefType) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.RefType(mutatedNode)) ?? mutatedNode; + } + + return mutatedNode; + } + + ImportRefType(node: ImportRefType): ImportRefType { + let mutatedNode = node; + + if (this.visitor.enter?.ImportRefType) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.ImportRefType(mutatedNode)) ?? mutatedNode; + } + + if (this.visitor.leave?.ImportRefType) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.ImportRefType(mutatedNode)) ?? mutatedNode; + } + + return mutatedNode; + } + + ArrayType(node: ArrayType): ArrayType { + let mutatedNode = node; + + if (this.visitor.enter?.ArrayType) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.ArrayType(mutatedNode)) ?? mutatedNode; + } + + mutatedNode.item.type = this.AnyType(mutatedNode.item.type); + + if (this.visitor.leave?.ArrayType) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.ArrayType(mutatedNode)) ?? mutatedNode; + } + + return mutatedNode; + } + + MapType(node: MapType): MapType { + let mutatedNode = node; + + if (this.visitor.enter?.MapType) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.enter.MapType(mutatedNode)) ?? mutatedNode; + } + + mutatedNode.value.type = this.AnyType(mutatedNode.value.type); + + if (this.visitor.leave?.MapType) { + mutatedNode = this.coerceVoidToUndefined(this.visitor.leave.MapType(mutatedNode)) ?? mutatedNode; + } + + return mutatedNode; + } + + visit(node: Abi | ImportedAbi) { + this.Abi(node); + } +} \ No newline at end of file diff --git a/packages/schema/abi/src/CircularDependencyValidator.ts b/packages/schema/abi/src/CircularDependencyValidator.ts new file mode 100644 index 0000000000..ab20c868a7 --- /dev/null +++ b/packages/schema/abi/src/CircularDependencyValidator.ts @@ -0,0 +1,69 @@ +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: { currentIdPath: string[]; currentObject?: string } = { + currentIdPath: [], + currentObject: undefined + } + + const importsVisitor = new AbiVisitor({ + enter: { + Import: (importDef) => { + state.currentIdPath.push(importDef.id) + const currentId = state.currentIdPath.join(".") + + this.importsTree.addNode(currentId) + }, + ObjectDef: (objectDef) => { + state.currentObject = objectDef.name + const currentId = state.currentIdPath.join(".") + this.definitionsTree.addNode(`${currentId}.${objectDef.name}`) + }, + RefType: (refType) => { + if (refType.ref_kind === "Object") { + const currentId = state.currentIdPath.join(".") + this.definitionsTree.addEdge(`${currentId}.${state.currentObject}`, `${currentId}.${refType.ref_name}`) + } + }, + ImportRefType: (importRefType) => { + const currentId = state.currentIdPath.join(".") + this.importsTree.addEdge(currentId, `${currentId}.${importRefType.import_id}`) + + if (importRefType.ref_kind === "Object") { + this.definitionsTree.addEdge(`${currentId}.${state.currentObject}`, `${currentId}.${importRefType.import_id}.${importRefType.ref_name}`) + } + } + }, + leave: { + Import: () => { + state.currentIdPath.pop() + }, + 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 diff --git a/packages/schema/abi/src/LinkerVisitor.ts b/packages/schema/abi/src/LinkerVisitor.ts new file mode 100644 index 0000000000..fab6d12abc --- /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": + mutatedNode = this.ScalarType(mutatedNode); + break; + case "Array": + mutatedNode = this.ArrayType(mutatedNode); + break; + case "Map": + mutatedNode = this.MapType(mutatedNode); + break; + case "Ref": + mutatedNode = this.RefType(mutatedNode); + break; + case "ImportRef": + mutatedNode = this.ImportRefType(mutatedNode); + break; + case "UnlinkedImportRef": + mutatedNode = 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 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..a07d803099 --- /dev/null +++ b/packages/schema/abi/src/__tests__/AbiImportsLinker.spec.ts @@ -0,0 +1,1170 @@ +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" + +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() + const shaker = new AbiTreeShaker(merger) + const sanitizer = new AbiSanitizer() + + 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", + namespaced_ref_name: "OutBar", + } + } + ] + }, + { + kind: "Object", + name: "Some2", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "UnlinkedImportRef", + namespaced_ref_name: "OutBar", + } + } + ] + } + ], + enums: [ + { + kind: "Enum", + name: "Foo", + constants: ["ONE", "TWO"] + } + ], + } + + const linker = new AbiImportsLinker(mockSchemaParser(), mockFetchers(), merger, shaker) + 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(), merger, shaker) + 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, merger, shaker) + const mergedAbi = await linker.mergeLocalImports(abi, localImportStatements) + + expect(sanitizer.sanitizeAbi(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, merger, shaker) + const mergedAbi = await linker.mergeLocalImports(abi, localImportStatements) + + expect(sanitizer.sanitizeAbi(mergedAbi.abi)).toEqual(expectedAbi) + }) + + it("Should embed external imports", async () => { + const abi: Abi = { + version: "0.2", + } + + 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", + 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 extAbi2: 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", + imports: [ + { + id: "EXT1", + type: "wasm", + uri: "ext1", + namespace: "EXT1", + objects: extAbi1.objects, + enums: extAbi1.enums, + functions: extAbi1.functions, + imports: extAbi1.imports + }, + { + id: "EXT2", + type: "wasm", + uri: "ext2", + namespace: "EXT2", + objects: extAbi2.objects, + enums: extAbi2.enums, + functions: extAbi2.functions, + imports: extAbi2.imports + }, + ] + } + + const externalImportStatements: ExternalImportStatement[] = [ + { + kind: "external", + uriOrPath: "ext1", + namespace: "EXT1", + importedTypes: ["Some"] + }, + { + kind: "external", + uriOrPath: "ext2", + namespace: "EXT2", + importedTypes: ["Foo"] + }, + ] + + const fetchers = { + external: { + fetch: (uriOrPath: string) => { + if (uriOrPath === "ext1") { + return Promise.resolve(extAbi1) + } else { + return Promise.resolve(extAbi2) + } + }, + }, + local: { + fetch: jest.fn() + } + } + + const linker = new AbiImportsLinker(mockSchemaParser(), fetchers, merger, shaker) + const abiWithExports = await linker.embedExternalImports(abi, externalImportStatements) + + expect(sanitizer.sanitizeAbi(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", + namespaced_ref_name: "EXT_Foo", + } + }, + { + kind: "Property", + name: "extBar", + required: true, + type: { + kind: "UnlinkedImportRef", + namespaced_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(), merger, shaker) + 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", + namespaced_ref_name: "EXT1_Foo", + } + }, + { + kind: "Property", + name: "extProp2", + required: true, + type: { + kind: "UnlinkedImportRef", + namespaced_ref_name: "EXT1_EXT2_Bar", + } + }, + { + kind: "Property", + name: "extProp3", + required: true, + type: { + kind: "UnlinkedImportRef", + namespaced_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(), merger, shaker) + 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", + namespaced_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", + namespaced_ref_name: "EXT1_Foo", + } + }, + { + kind: "Property", + name: "propSome2", + required: true, + type: { + kind: "UnlinkedImportRef", + namespaced_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: "0" + } + }] + } + ] + } + ], + objects: [ + { + kind: "Object", + name: "RefObj", + props: [ + { + kind: "Property", + name: "propSome", + required: true, + type: { + kind: "ImportRef", + ref_kind: "Object", + ref_name: "Foo", + import_id: "EXT1", + } + }, + { + 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: "Enum", + 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 => { + 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, merger, shaker) + + const linkedAbi = await linker.link(rootAbi, { + local: [ + { + kind: "local", + importedTypes: ["Some"], + uriOrPath: "local", + } + ], + external: [ + { + kind: "external", + namespace: "EXT1", + importedTypes: ["Foo"], + uriOrPath: "ext1", + } + ] + }) + + expect(sanitizer.sanitizeAbi(linkedAbi)).toEqual(expectedAbi) + }) +}) \ No newline at end of file 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..d55f986ac9 --- /dev/null +++ b/packages/schema/abi/src/__tests__/AbiTreeShaker.spec.ts @@ -0,0 +1,499 @@ +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) + }); + + 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 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 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/index.ts b/packages/schema/abi/src/index.ts new file mode 100644 index 0000000000..bba899204e --- /dev/null +++ b/packages/schema/abi/src/index.ts @@ -0,0 +1,38 @@ +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" +export * from "./AbiTreeShaker" +export * from "./AbiVisitor" +export * from "./AbiSanitizer" + +interface Args { + schema: string + parser: SchemaParser + fetchers: { + external: ExternalSchemaFetcher; + local: LocalSchemaFetcher; + } +} + +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) + + const merger = new AbiMerger() + 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 + }) + const shakenAbi = await shaker.shakeImports(linkedAbi) + const sanitizedAbi = sanitizer.sanitizeAbi(shakenAbi) + return sanitizedAbi as Abi +} 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/compose/tsconfig.json b/packages/schema/abi/tsconfig.json similarity index 54% rename from packages/schema/compose/tsconfig.json rename to packages/schema/abi/tsconfig.json index 1b7ca8ec90..c199f55b7d 100644 --- a/packages/schema/compose/tsconfig.json +++ b/packages/schema/abi/tsconfig.json @@ -1,18 +1,10 @@ { "extends": "../../../tsconfig", "compilerOptions": { - "lib": [ - "es2020.string", - "es2015", - "es5", - "dom" - ], - "downlevelIteration": true, "outDir": "build" }, "include": [ "./src/**/*.ts" ], "exclude": [] -} - +} \ No newline at end of file diff --git a/packages/schema/bind/package.json b/packages/schema/bind/package.json index 1c4fe4ce08..08d9566360 100644 --- a/packages/schema/bind/package.json +++ b/packages/schema/bind/package.json @@ -21,15 +21,15 @@ }, "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" + "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/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/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/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/bindings/assemblyscript/renderers/WasmInitialValuesRenderer.ts b/packages/schema/bind/src/bindings/assemblyscript/renderers/WasmInitialValuesRenderer.ts new file mode 100644 index 0000000000..4f46afc920 --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/renderers/WasmInitialValuesRenderer.ts @@ -0,0 +1,96 @@ +import { AnyType, RefType, ImportRefType, MapType, ArrayType, ScalarType } from "@polywrap/abi-types"; +import { isKeyword } from "../types"; +import { Renderer } from "./types"; + +export class AssemblyscriptWasmInitRenderer { + + constructor(protected wasmRenderer: Renderer) {} + + 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 diff --git a/packages/schema/bind/src/bindings/assemblyscript/renderers/WasmRenderer.ts b/packages/schema/bind/src/bindings/assemblyscript/renderers/WasmRenderer.ts new file mode 100644 index 0000000000..7f482680ac --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/renderers/WasmRenderer.ts @@ -0,0 +1,103 @@ +import { AnyType, ArrayType, ImportRefType, MapType, RefType, ScalarType } from "@polywrap/abi-types"; +import { isBaseType, isKeyword } from "../types"; + +export class AssemblyscriptWasmRenderer { + 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 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 diff --git a/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts b/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts index 1bfdea57cc..7be51e44ef 100644 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/index.ts @@ -1,23 +1,67 @@ -import { Functions } from "../"; import { GenerateBindingFn, renderTemplates } from "../.."; import { loadSubTemplates } from "../../utils"; import { BindOptions, BindOutput } from "../../.."; -import { - transformAbi, - addFirstLast, - extendType, - toPrefixedGraphQLType, -} from "@polywrap/schema-parse"; +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 => { @@ -28,14 +72,14 @@ 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.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, @@ -47,65 +91,49 @@ 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 - ), - }); - } - } - - // 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 - ), - }); - } + if (importedFuncs.length) { + importEntries.push({ + type: "Directory", + name: "Module", + data: renderTemplates( + templatePath("imported/module-type"), + { + functions: importedFuncs + }, + 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) { @@ -119,40 +147,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, @@ -162,34 +177,8 @@ 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)); return result; }; - -function applyTransforms(abi: WrapAbi): WrapAbi { - const transforms = [ - extendType(Functions), - addFirstLast, - toPrefixedGraphQLType, - ]; - - for (const transform of transforms) { - abi = transformAbi(abi, transform); - } - return abi; -} 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.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.hbs new file mode 100644 index 0000000000..eab7f5e7b5 --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.hbs @@ -0,0 +1,43 @@ +import { + wrap_invoke_args, + wrap_invoke, + wrap_abort, + InvokeArgs +} from "@polywrap/wasm-as"; + +{{#if functions.length}} +import { + {{#each functions}} + {{name}}Wrapped{{#unless @last}},{{/unless}} + {{/each}} +} from "./{{type}}/wrapped"; +{{/if}} + +export function _wrap_invoke(method_size: u32, args_size: u32): bool { + const args: InvokeArgs = wrap_invoke_args( + method_size, + args_size + ); + + {{#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( + msg: string | null, + file: string | null, + line: u32, + column: u32 +): void { + wrap_abort( + msg ? msg : "", + file ? file : "", + line, + column + ); +} \ No newline at end of file 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 deleted file mode 100644 index 0bf55b2b8b..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/entry-ts.mustache +++ /dev/null @@ -1,48 +0,0 @@ -import { - wrap_invoke_args, - wrap_invoke, - wrap_abort, - InvokeArgs -} from "@polywrap/wasm-as"; - -{{#moduleType}} -{{#methods.length}} -import { - {{#methods}} - {{name}}Wrapped{{^last}},{{/last}} - {{/methods}} -} from "./{{type}}/wrapped"; -{{/methods.length}} -{{/moduleType}} - -export function _wrap_invoke(method_size: u32, args_size: u32, env_size: u32): bool { - const args: InvokeArgs = wrap_invoke_args( - method_size, - args_size - ); - - {{#moduleType}} - {{#methods}} - {{^first}}else {{/first}}if (args.method == "{{name}}") { - return wrap_invoke(args, env_size, {{name}}Wrapped); - } - {{/methods}} - {{/moduleType}} - else { - return wrap_invoke(args, env_size, null); - } -} - -export function wrapAbort( - msg: string | null, - file: string | null, - line: u32, - column: u32 -): void { - wrap_abort( - msg ? msg : "", - file ? file : "", - line, - column - ); -} 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 c738d94e84..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/enum-type/index-ts.mustache +++ /dev/null @@ -1,35 +0,0 @@ -export enum {{#detectKeyword}}{{type}}{{/detectKeyword}} { - {{#constants}} - {{#detectKeyword}}{{.}}{{/detectKeyword}}, - {{/constants}} - _MAX_ -} - -export function sanitize{{type}}Value(value: i32): void { - const valid = value >= 0 && value < {{#detectKeyword}}{{type}}{{/detectKeyword}}._MAX_; - if (!valid) { - throw new Error("Invalid value for enum '{{#detectKeyword}}{{type}}{{/detectKeyword}}': " + value.toString()); - } -} - -export function get{{type}}Value(key: string): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - {{#constants}} - if (key == "{{#detectKeyword}}{{.}}{{/detectKeyword}}") { - return {{#detectKeyword}}{{type}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}; - } - {{/constants}} - - throw new Error("Invalid key for enum '{{#detectKeyword}}{{type}}{{/detectKeyword}}': " + key); -} - -export function get{{type}}Key(value: {{#detectKeyword}}{{type}}{{/detectKeyword}}): string { - sanitize{{type}}Value(value); - - switch (value) { - {{#constants}} - case {{#detectKeyword}}{{type}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}: return "{{#detectKeyword}}{{.}}{{/detectKeyword}}"; - {{/constants}} - default: - throw new Error("Invalid value for enum '{{#detectKeyword}}{{type}}{{/detectKeyword}}': " + value.toString()); - } -} 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/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 c738d94e84..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}}{{type}}{{/detectKeyword}} { - {{#constants}} - {{#detectKeyword}}{{.}}{{/detectKeyword}}, - {{/constants}} - _MAX_ -} - -export function sanitize{{type}}Value(value: i32): void { - const valid = value >= 0 && value < {{#detectKeyword}}{{type}}{{/detectKeyword}}._MAX_; - if (!valid) { - throw new Error("Invalid value for enum '{{#detectKeyword}}{{type}}{{/detectKeyword}}': " + value.toString()); - } -} - -export function get{{type}}Value(key: string): {{#detectKeyword}}{{type}}{{/detectKeyword}} { - {{#constants}} - if (key == "{{#detectKeyword}}{{.}}{{/detectKeyword}}") { - return {{#detectKeyword}}{{type}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}; - } - {{/constants}} - - throw new Error("Invalid key for enum '{{#detectKeyword}}{{type}}{{/detectKeyword}}': " + key); -} - -export function get{{type}}Key(value: {{#detectKeyword}}{{type}}{{/detectKeyword}}): string { - sanitize{{type}}Value(value); - - switch (value) { - {{#constants}} - case {{#detectKeyword}}{{type}}{{/detectKeyword}}.{{#detectKeyword}}{{.}}{{/detectKeyword}}: return "{{#detectKeyword}}{{.}}{{/detectKeyword}}"; - {{/constants}} - default: - throw new Error("Invalid value for enum '{{#detectKeyword}}{{type}}{{/detectKeyword}}': " + value.toString()); - } -} 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.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 f37a0641b8..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/imported/index-ts.mustache +++ /dev/null @@ -1,12 +0,0 @@ -{{#importedModuleTypes}} -export * from "./{{type}}"; -{{/importedModuleTypes}} -{{#importedObjectTypes}} -export * from "./{{type}}"; -{{/importedObjectTypes}} -{{#importedEnumTypes}} -export * from "./{{type}}"; -{{/importedEnumTypes}} -{{#importedEnvTypes}} -export * from "./{{type}}"; -{{/importedEnvTypes}} 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 76% 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 a1b0abaa22..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"; -{{#methods.length}} +{{#if functions.length}} import { - {{#methods}} + {{#each functions}} serialize{{name}}Args, deserialize{{name}}Result, - Args_{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/methods}} + Args_{{detectKeyword name}}{{^@last}},{{/@last}} + {{/each}} } from "./serialization"; -{{/methods.length}} +{{/if}} import * as Types from "../.."; {{^isInterface}} -export class {{#detectKeyword}}{{type}}{{/detectKeyword}} { +export class {{detectKeyword name}} { public static uri: string = "{{uri}}"; - {{#methods}} + {{#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}}{{type}}{{/detectKeyword}} { deserialize{{name}}Result(result.unwrap()) ); } - {{^last}} + {{^@last}} - {{/last}} - {{/methods}} + {{/@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; } - {{#methods}} + {{#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}} - {{/methods}} + {{/@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 3d914e5ae1..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{{type}}, - deserialize{{type}}, - write{{type}}, - read{{type}} -} from "./serialization"; -import * as Types from "../.."; - -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); - } -} 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.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 2f2c2f2941..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/index-ts.mustache +++ /dev/null @@ -1,52 +0,0 @@ -{{#moduleType}} -{{#methods.length}} -import { - {{#methods}} - Args_{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/methods}} -} from "./{{type}}"; -{{/methods.length}} -{{/moduleType}} -{{#moduleType}} -{{#methods.length}} -export { - {{#methods}} - Args_{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/methods}} -}; -{{/methods.length}} -{{/moduleType}} -{{#objectTypes}} -export { {{#detectKeyword}}{{type}}{{/detectKeyword}} } from "./{{type}}"; -{{/objectTypes}} -{{#enumTypes}} -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}} -{{#importedEnvTypes}} -export { {{#detectKeyword}}{{type}}{{/detectKeyword}} } from "./imported/{{type}}"; -{{/importedEnvTypes}} -{{#importedEnumTypes}} -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}} -{{#envType}} -export { Env } from "./Env"; -{{/envType}} 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.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 cadfcf855f..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/index-ts.mustache +++ /dev/null @@ -1,13 +0,0 @@ -{{#methods.length}} -import { - {{#methods}} - Args_{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/methods}} -} from "./serialization"; - -export { - {{#methods}} - Args_{{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/methods}} -}; -{{/methods.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 99% 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 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.hbs @@ -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.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 239f0cb479..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/module-type/wrapped-ts.mustache +++ /dev/null @@ -1,53 +0,0 @@ -{{#methods.length}} -import { wrap_load_env } from "@polywrap/wasm-as"; -import { - {{#methods}} - {{#detectKeyword}}{{name}}{{/detectKeyword}}{{^last}},{{/last}} - {{/methods}} -} from "../../index"; -import { - {{#methods}} - deserialize{{name}}Args, - serialize{{name}}Result{{^last}},{{/last}} - {{/methods}} -} from "./serialization"; -{{/methods.length}} -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}} - {{#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}}{{#env}}, - env{{/env}} - ); - return serialize{{name}}Result(result); -} -{{^last}} - -{{/last}} -{{/methods}} 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..27329d271f --- /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 props}} + {{detectKeyword name}}: {{toWasm 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 8199149c0d..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{{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/object-type/serialization-ts.hbs b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/serialization-ts.hbs new file mode 100644 index 0000000000..3ca4f0849d --- /dev/null +++ b/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-type/serialization-ts.hbs @@ -0,0 +1,146 @@ +{{> serialization_imports}} +import { {{detectKeyword name}} } from "./"; +import * as Types from ".."; + +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); + const buffer = new ArrayBuffer(sizer.length); + const encoderContext: Context = new Context("Serializing (encoding) object-type: {{name}}"); + const encoder = new WriteEncoder(buffer, sizer, encoderContext); + write{{name}}(encoder, type); + return buffer; +} + +export function write{{name}}(writer: Write, type: {{detectKeyword name}}): void { + {{#if props.length}} + writer.writeMapLength({{props.length}}); + {{else}} + writer.writeMapLength(0); + {{/if}} + {{#each props}} + writer.context().push("{{name}}", "{{toWasm type}}", "writing property"); + writer.writeString("{{name}}"); + {{#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}} + }); + {{/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}} + }); + {{/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(); + {{/each}} +} + +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}} { + let numFields = reader.readMapLength(); + + {{#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; + {{/if}} + {{/each}} + + while (numFields > 0) { + numFields--; + const field = reader.readString(); + + reader.context().push(field, "unknown", "searching for property type"); + {{#each props}} + {{^first}}else {{/first}}if (field == "{{name}}") { + 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 item}}{{/item}} => { + {{> deserialize_array}} + }); + {{/array}} + {{#map}} + _{{name}} = reader.read{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}((reader: Read): {{#key}}{{toWasm item}}{{/key}} => { + return reader.read{{#key}}{{#toMsgPack}}{{toGraphQLType}}{{/toMsgPack}}{{/key}}(); + }, (reader: Read): {{#value}}{{toWasm item}}{{/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(); + } + {{/each}} + reader.context().pop(); + } + + {{#each props}} + {{#if (eq required)}} + {{^object}} + if (!_{{name}}Set) { + {{/object}} + {{#object}} + if (!_{{name}} || !_{{name}}Set) { + {{/object}} + throw new Error(reader.context().printWithContext("Missing required property: '{{name}}: {{name}}'")); + } + {{/required}} + {{/each}} + + return { + {{#props}} + {{detectKeyword name}}: _{{name}}{{^last}},{{/last}} + {{/props}} + }; +} 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 deleted file mode 100644 index 831b72a573..0000000000 --- a/packages/schema/bind/src/bindings/assemblyscript/wasm/templates/object-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) object-type: {{type}}"); - const sizer = new WriteSizer(sizerContext); - write{{type}}(sizer, type); - const buffer = new ArrayBuffer(sizer.length); - const encoderContext: Context = new Context("Serializing (encoding) object-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 object-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/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/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}} { 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/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/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; } diff --git a/packages/schema/compose/README.md b/packages/schema/compose/README.md deleted file mode 100644 index c1acb2167f..0000000000 --- a/packages/schema/compose/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/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/__tests__/index.ts b/packages/schema/compose/src/__tests__/index.ts deleted file mode 100644 index 2ec585d1e0..0000000000 --- a/packages/schema/compose/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/compose/src/__tests__/test-cases.spec.ts b/packages/schema/compose/src/__tests__/test-cases.spec.ts deleted file mode 100644 index b6d9797022..0000000000 --- a/packages/schema/compose/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/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/index.ts b/packages/schema/compose/src/index.ts deleted file mode 100644 index 57225dda95..0000000000 --- a/packages/schema/compose/src/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { SchemaFile, AbiResolvers } from "./types"; -import { resolveImportsAndParseSchemas } from "./resolve"; -import { renderSchema } from "./render"; - -import { WrapAbi } from "@polywrap/wrap-manifest-types-js"; - -export * from "./types"; -export { renderSchema }; - -export interface ComposerOptions { - schema: SchemaFile; - resolvers: AbiResolvers; -} - -export async function composeSchema( - options: ComposerOptions -): Promise { - return await resolveImportsAndParseSchemas( - options.schema.schema, - options.schema.absolutePath, - options.resolvers - ); -} 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/render.ts b/packages/schema/compose/src/render.ts deleted file mode 100644 index 3f43a07e56..0000000000 --- a/packages/schema/compose/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/compose/src/resolve.ts b/packages/schema/compose/src/resolve.ts deleted file mode 100644 index 70fe6f9c8b..0000000000 --- a/packages/schema/compose/src/resolve.ts +++ /dev/null @@ -1,1197 +0,0 @@ -/* eslint-disable no-useless-escape */ -/* eslint-disable @typescript-eslint/naming-convention */ -/* eslint-disable @typescript-eslint/ban-types */ - -import { - ExternalImport, - LocalImport, - AbiResolvers, - SYNTAX_REFERENCE, - AbiResolver, - SchemaResolver, -} from "./types"; -import { parseExternalImports, parseLocalImports, parseUse } from "./parse"; -import { renderSchema } from "./render"; -import { checkDuplicateEnvProperties } from "./env"; -import { addHeader } from "./templates/header.mustache"; - -import { - WrapAbi, - ObjectDefinition, - ImportedObjectDefinition, - ModuleDefinition, - ImportedModuleDefinition, - ImportedEnumDefinition, - EnumDefinition, - GenericDefinition, - InterfaceImplementedDefinition, - ObjectRef, - EnumRef, - EnvDefinition, - ImportedEnvDefinition, -} from "@polywrap/wrap-manifest-types-js"; -import { - parseSchema, - AbiTransforms, - visitObjectDefinition, - visitModuleDefinition, - visitEnvDefinition, - DefinitionKind, - visitImportedModuleDefinition, - visitImportedObjectDefinition, - visitEnumDefinition, - visitImportedEnumDefinition, - isKind, - isImportedModuleType, - header, - isEnvType, - createImportedObjectDefinition, - createImportedEnumDefinition, - createImportedModuleDefinition, - createInterfaceDefinition, - createCapability, - ModuleCapability, - createEnvDefinition, - createModuleDefinition, - createImportedEnvDefinition, - visitImportedEnvDefinition, - isImportedEnvType, -} from "@polywrap/schema-parse"; - -type ImplementationWithInterfaces = { - typeName: string; - interfaces: string[]; -}; - -const TYPE_NAME_REGEX = `[a-zA-Z0-9_]+`; - -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, - resolvers: AbiResolvers, - 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: [], - importedEnvTypes: [], - }; - - const externalImports = await resolveExternalImports( - externalImportsToResolve, - resolvers.external, - subAbi - ); - - await resolveLocalImports( - localImportsToResolve, - resolvers.local, - subAbi, - resolvers - ); - 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; -} - -type ImportMap = Record< - string, - ( - | ImportedObjectDefinition - | ImportedModuleDefinition - | ImportedEnumDefinition - ) & - Namespaced ->; - -type EnumOrObjectOrEnv = ObjectDefinition | EnumDefinition | EnvDefinition; -type ImportedEnumOrObjectOrEnv = - | ImportedObjectDefinition - | ImportedEnumDefinition - | ImportedEnvDefinition; - -// 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: EnumOrObjectOrEnv[], - importedTypes: ImportedEnumOrObjectOrEnv[], - kind: DefinitionKind - ): ImportedEnumOrObjectOrEnv & 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]) { - 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 - ), - }); - } - } - - return def; - }, - InterfaceImplementedDefinition: ( - def: InterfaceImplementedDefinition & Namespaced - ) => { - if (def.__namespaced) { - return def; - } - - const type = def.type; - - 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 - ), - }); - } - } - - 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[], - resolveAbi: AbiResolver, - 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); - - 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); - } - - if (extAbi.envType) { - extTypesToImport.push(extAbi.envType.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 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) - : -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[] - | 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) { - 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[], - resolveSchema: SchemaResolver, - abi: WrapAbi, - resolvers: AbiResolvers -): Promise { - for (const importToResolve of importsToResolve) { - const { importedTypes, path } = importToResolve; - - // Resolve the schema - let schema = await resolveSchema(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, - resolvers, - 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; - - if (isEnvType(importedType)) { - visitorFunc = visitEnvDefinition; - type = localAbi.envType; - } else { - 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: EnumOrObjectOrEnv[] - ) => { - // 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.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 ( - 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/compose/src/templates/header.mustache.ts b/packages/schema/compose/src/templates/header.mustache.ts deleted file mode 100644 index 7c70a2164a..0000000000 --- a/packages/schema/compose/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/compose/src/templates/schema.mustache.ts b/packages/schema/compose/src/templates/schema.mustache.ts deleted file mode 100644 index 69803ae9ab..0000000000 --- a/packages/schema/compose/src/templates/schema.mustache.ts +++ /dev/null @@ -1,184 +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}}{{#env}} @env(required: {{required}}){{/env}} - {{^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 ### - -### 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}}`; - -export { template }; diff --git a/packages/schema/compose/src/types.ts b/packages/schema/compose/src/types.ts deleted file mode 100644 index a24e15a5b7..0000000000 --- a/packages/schema/compose/src/types.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Abi, CapabilityType } 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; -} - -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" + - `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/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/__tests__/index.ts b/packages/schema/parse/src/__tests__/index.ts deleted file mode 100644 index c2cc127c06..0000000000 --- a/packages/schema/parse/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/parse/src/__tests__/parse-map-type.spec.ts b/packages/schema/parse/src/__tests__/parse-map-type.spec.ts deleted file mode 100644 index 65fd989623..0000000000 --- a/packages/schema/parse/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/parse/src/__tests__/test-cases.spec.ts b/packages/schema/parse/src/__tests__/test-cases.spec.ts deleted file mode 100644 index a0b0775b55..0000000000 --- a/packages/schema/parse/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/parse/src/__tests__/transforms.spec.ts b/packages/schema/parse/src/__tests__/transforms.spec.ts deleted file mode 100644 index 0883968e9d..0000000000 --- a/packages/schema/parse/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/parse/src/__tests__/validate-directives.spec.ts b/packages/schema/parse/src/__tests__/validate-directives.spec.ts deleted file mode 100644 index 2418bc5d37..0000000000 --- a/packages/schema/parse/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/parse/src/__tests__/validate-types.spec.ts b/packages/schema/parse/src/__tests__/validate-types.spec.ts deleted file mode 100644 index b1f1936bb3..0000000000 --- a/packages/schema/parse/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() - }) -}); 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/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/abi/index.ts b/packages/schema/parse/src/abi/index.ts deleted file mode 100644 index 6ad712a5a4..0000000000 --- a/packages/schema/parse/src/abi/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Abi } from "@polywrap/wrap-manifest-types-js"; - -export * from "@polywrap/wrap-manifest-types-js"; -export * from "./definitions"; -export * from "./env"; -export * from "./utils"; - -export function createAbi(): Abi { - return { - version: "0.1", - objectTypes: [], - enumTypes: [], - interfaceTypes: [], - importedObjectTypes: [], - importedModuleTypes: [], - importedEnumTypes: [], - importedEnvTypes: [], - }; -} diff --git a/packages/schema/parse/src/abi/utils.ts b/packages/schema/parse/src/abi/utils.ts deleted file mode 100644 index 74ad904cc1..0000000000 --- a/packages/schema/parse/src/abi/utils.ts +++ /dev/null @@ -1,43 +0,0 @@ -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; - -export function isMapKeyType(type: string): boolean { - return type in MapKeyTypes; -} - -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 ScalarTypes; -} - -export const scalarTypeNames = Object.keys(ScalarTypes); 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/enum-types.ts b/packages/schema/parse/src/extract/enum-types.ts deleted file mode 100644 index a6454ed8ad..0000000000 --- a/packages/schema/parse/src/extract/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/env-types.ts b/packages/schema/parse/src/extract/env-types.ts deleted file mode 100644 index b2c14d1c58..0000000000 --- a/packages/schema/parse/src/extract/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/imported-enum-types.ts b/packages/schema/parse/src/extract/imported-enum-types.ts deleted file mode 100644 index c374735a7b..0000000000 --- a/packages/schema/parse/src/extract/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/imported-env-types.ts b/packages/schema/parse/src/extract/imported-env-types.ts deleted file mode 100644 index 276292d113..0000000000 --- a/packages/schema/parse/src/extract/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/imported-module-types.ts b/packages/schema/parse/src/extract/imported-module-types.ts deleted file mode 100644 index e29e5cf6ec..0000000000 --- a/packages/schema/parse/src/extract/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/imported-object-types.ts b/packages/schema/parse/src/extract/imported-object-types.ts deleted file mode 100644 index 49b4191ca4..0000000000 --- a/packages/schema/parse/src/extract/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/index.ts b/packages/schema/parse/src/extract/index.ts deleted file mode 100644 index 5089ce3278..0000000000 --- a/packages/schema/parse/src/extract/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/module-types.ts b/packages/schema/parse/src/extract/module-types.ts deleted file mode 100644 index 521678346e..0000000000 --- a/packages/schema/parse/src/extract/module-types.ts +++ /dev/null @@ -1,313 +0,0 @@ -import { - createModuleDefinition, - createMethodDefinition, - createPropertyDefinition, - createInterfaceImplementedDefinition, - CapabilityType, - createCapability, - createInterfaceDefinition, - capabilityTypes, -} from ".."; -import { - extractEnvDirective, - extractInputValueDefinition, - extractListType, - extractNamedType, - State, -} from "./utils/module-types-utils"; -import { extractAnnotateDirective } from "./utils/object-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 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 -): { 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/object-types.ts b/packages/schema/parse/src/extract/object-types.ts deleted file mode 100644 index 8ab6b33a4b..0000000000 --- a/packages/schema/parse/src/extract/object-types.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { - createObjectDefinition, - createInterfaceImplementedDefinition, - isEnvType, - isModuleType, -} from ".."; -import { - extractFieldDefinition, - extractListType, - extractNamedType, - State, -} from "./utils/object-types-utils"; - -import { - ObjectTypeDefinitionNode, - NonNullTypeNode, - NamedTypeNode, - ListTypeNode, - FieldDefinitionNode, - DirectiveNode, - ASTVisitor, -} from "graphql"; -import { ObjectDefinition, WrapAbi } from "@polywrap/wrap-manifest-types-js"; - -const visitorEnter = (objectTypes: ObjectDefinition[], state: State) => ({ - 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; - } - - 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, - comment: node.description?.value, - }); - 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), - }; -}; diff --git a/packages/schema/parse/src/extract/utils/imported-types-utils.ts b/packages/schema/parse/src/extract/utils/imported-types-utils.ts deleted file mode 100644 index cdb1290b34..0000000000 --- a/packages/schema/parse/src/extract/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/utils/map-utils.ts b/packages/schema/parse/src/extract/utils/map-utils.ts deleted file mode 100644 index cbc70f0efb..0000000000 --- a/packages/schema/parse/src/extract/utils/map-utils.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { - createArrayDefinition, - createMapDefinition, - createMapKeyDefinition, - createScalarDefinition, - createUnresolvedObjectOrEnumRef, - isMapKeyType, - isScalarType, -} from "../.."; - -import { - GenericDefinition, - MapKeyDefinition, - ScalarDefinition, -} from "@polywrap/wrap-manifest-types-js"; - -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; - } - return { - currentType: "Array", - subType: type.substring(1, closeSquareBracketIdx), - required: required, - }; - } - - 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}`); - } - if (type.endsWith("!")) { - required = true; - } - hasSubType = false; - } - - if (type[closeAngleBracketIdx + 1] === "!") { - required = true; - } - - 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; - } - - 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}` - ); - } -}; - -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, - }); - } - case "Map": { - 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: ${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; - - if (!isMapKeyType(keyType)) { - throw new Error( - `Found invalid map key type: ${keyType} while parsing ${rootType}` - ); - } - - 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, - }); - } - default: - throw new Error(`Invalid map value type: ${type}`); - } -}; - -export function parseCurrentType(type: string): CurrentAbi { - return _parseCurrentType(type, type); -} - -export function parseMapType(type: string, name?: string): GenericDefinition { - return _parseMapType(type, type, name); -} - -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/utils/module-types-utils.ts deleted file mode 100644 index 4605e5e9cf..0000000000 --- a/packages/schema/parse/src/extract/utils/module-types-utils.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { createPropertyDefinition, createArrayDefinition } from "../.."; -import { setPropertyType } from "./property-utils"; -import { extractAnnotateDirective } from "./object-types-utils"; - -import { - BooleanValueNode, - FieldDefinitionNode, - InputValueDefinitionNode, - NamedTypeNode, -} from "graphql"; -import { - ImportedModuleDefinition, - InterfaceDefinition, - MapDefinition, - MethodDefinition, - ModuleDefinition, - PropertyDefinition, -} from "@polywrap/wrap-manifest-types-js"; - -export interface EnvDirDefinition { - required: boolean; -} - -export interface State { - currentModule?: ModuleDefinition; - 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/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, - }); -} diff --git a/packages/schema/parse/src/header.ts b/packages/schema/parse/src/header.ts deleted file mode 100644 index 55b996c26d..0000000000 --- a/packages/schema/parse/src/header.ts +++ /dev/null @@ -1,39 +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 @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/index.ts b/packages/schema/parse/src/index.ts deleted file mode 100644 index 43f84b99e3..0000000000 --- a/packages/schema/parse/src/index.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { createAbi } from "./abi"; -import { extractors, SchemaExtractorBuilder } from "./extract"; -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"; - -export * from "./abi"; -export * from "./extract"; -export * from "./transform"; -export * from "./validate"; -export * from "./header"; - -interface ParserOptions { - // Disable schema validation - noValidate?: boolean; - // Use custom validators - validators?: SchemaValidatorBuilder[]; - // Use custom extractors - extractors?: SchemaExtractorBuilder[]; - // Use custom transformations - transforms?: AbiTransforms[]; -} - -export function parseSchema( - schema: string, - options: ParserOptions = {} -): WrapAbi { - const astNode = parse(schema); - - // Validate GraphQL Schema - if (!options.noValidate) { - const validates = options.validators || validators; - validate(astNode, validates); - } - - // Extract & Build Abi - let info = createAbi(); - - const extracts = options.extractors || extractors; - 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); - } - } - - 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, - }; -} - -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); - } - } -}; - -const extract = ( - astNode: DocumentNode, - abi: WrapAbi, - extractors: SchemaExtractorBuilder[] -) => { - const allVisitors = extractors.map((getVisitor) => getVisitor(abi)); - - visit(astNode, visitInParallel(allVisitors)); -}; diff --git a/packages/schema/parse/src/transform/addAnnotations.ts b/packages/schema/parse/src/transform/addAnnotations.ts deleted file mode 100644 index f64500d433..0000000000 --- a/packages/schema/parse/src/transform/addAnnotations.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { toGraphQL } from "."; -import { AbiTransforms } from ".."; - -import { PropertyDefinition } from "@polywrap/wrap-manifest-types-js"; - -export const addAnnotations: AbiTransforms = { - enter: { - PropertyDefinition: (def: PropertyDefinition): PropertyDefinition => { - if (!def.map) return def; - - return { - ...def, - toGraphQLType: (): string => - `Map${def.required ? "!" : ""} @annotate(type: "${toGraphQL(def)}")`, - } as PropertyDefinition; - }, - }, -}; diff --git a/packages/schema/parse/src/transform/addFirstLast.ts b/packages/schema/parse/src/transform/addFirstLast.ts deleted file mode 100644 index c8ee7a1581..0000000000 --- a/packages/schema/parse/src/transform/addFirstLast.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { AbiTransforms } from "."; - -import { Abi, GenericDefinition } from "@polywrap/wrap-manifest-types-js"; - -export const addFirstLast: AbiTransforms = { - enter: { - GenericDefinition: (def: GenericDefinition): GenericDefinition => { - 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): Abi => ({ - ...abi, - objectTypes: setFirstLast(abi.objectTypes), - 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 092578a42a..0000000000 --- a/packages/schema/parse/src/transform/extendType.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { AbiTransforms } from "."; - -import { GenericDefinition, WrapAbi } from "@polywrap/wrap-manifest-types-js"; - -// 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, - extension, - }), - // eslint-disable-next-line @typescript-eslint/naming-convention - GenericDefinition: (def: GenericDefinition) => ({ - ...def, - ...extension, - }), - }, - }; -} 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/hasImports.ts b/packages/schema/parse/src/transform/hasImports.ts deleted file mode 100644 index d2350d1e05..0000000000 --- a/packages/schema/parse/src/transform/hasImports.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { AbiTransforms } from "."; - -import { WrapAbi } from "@polywrap/wrap-manifest-types-js"; - -export const hasImports: AbiTransforms = { - enter: { - Abi: (abi: WrapAbi) => ({ - ...abi, - hasImports: () => { - return ( - (abi.importedEnumTypes && abi.importedEnumTypes.length) || - (abi.importedObjectTypes && abi.importedObjectTypes.length) || - (abi.importedModuleTypes && abi.importedModuleTypes.length) || - (abi.importedEnvTypes && abi.importedEnvTypes.length) - ); - }, - }), - }, -}; diff --git a/packages/schema/parse/src/transform/index.ts b/packages/schema/parse/src/transform/index.ts deleted file mode 100644 index 8990ae7093..0000000000 --- a/packages/schema/parse/src/transform/index.ts +++ /dev/null @@ -1,507 +0,0 @@ -/* 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"; - -export interface AbiTransforms { - enter?: AbiTransformer; - leave?: AbiTransformer; -} - -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; -} - -export function transformAbi(abi: WrapAbi, transforms: AbiTransforms): WrapAbi { - 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 - ); - } - } - - if (transforms.leave && transforms.leave.Abi) { - result = transforms.leave.Abi(result); - } - - 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 - ); - } - } - - if (result.interfaces) { - for (let i = 0; i < result.interfaces.length; ++i) { - result.interfaces[i] = visitInterfaceImplementedDefinition( - result.interfaces[i], - transforms - ); - } - } - - return transformType(result, transforms.leave); -} - -export function visitObjectRef( - def: ObjectRef, - transforms: AbiTransforms -): ObjectRef { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - - return transformType(result, transforms.leave); -} - -export function visitInterfaceImplementedDefinition( - def: InterfaceImplementedDefinition, - transforms: AbiTransforms -): InterfaceImplementedDefinition { - let result = Object.assign({}, def); - 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; -} - -export function visitScalarDefinition( - def: ScalarDefinition, - transforms: AbiTransforms -): ScalarDefinition { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - 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); -} - -export function visitEnumRef(def: EnumRef, transforms: AbiTransforms): EnumRef { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - 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); - } - - return transformType(result, transforms.leave); -} - -export function visitPropertyDefinition( - def: PropertyDefinition, - transforms: AbiTransforms -): PropertyDefinition { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - - result = visitAnyDefinition(result, transforms); - - return transformType(result, transforms.leave); -} - -export function visitMethodDefinition( - def: MethodDefinition, - transforms: AbiTransforms -): MethodDefinition { - 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 - ); - } - } - - if (result.return) { - result.return = visitPropertyDefinition(result.return, transforms); - } - - return transformType(result, transforms.leave); -} - -export function visitModuleDefinition( - def: ModuleDefinition, - transforms: AbiTransforms -): ModuleDefinition { - 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); -} - -export function visitInterfaceDefinition( - def: InterfaceDefinition, - transforms: AbiTransforms -): InterfaceDefinition { - let result = Object.assign({}, def); - result = transformType(result, transforms.enter); - return transformType(result, transforms.leave); -} - -export function visitImportedModuleDefinition( - def: ImportedModuleDefinition, - transforms: AbiTransforms -): ImportedModuleDefinition { - 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); -} - -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 { - 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); -} - -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; - - 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)); - } - - return result; -} 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/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; - }, - }, - }; -} 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/transform/toGraphQLType.ts b/packages/schema/parse/src/transform/toGraphQLType.ts deleted file mode 100644 index 300b2417d3..0000000000 --- a/packages/schema/parse/src/transform/toGraphQLType.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { AbiTransforms } from "."; -import { DefinitionKind } from ".."; - -import { - GenericDefinition, - AnyDefinition, - ArrayDefinition, - MethodDefinition, - MapDefinition, -} from "@polywrap/wrap-manifest-types-js"; - -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 { - 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), - }), - }, -}; diff --git a/packages/schema/parse/src/validate/directives.ts b/packages/schema/parse/src/validate/directives.ts deleted file mode 100644 index 8d3abcb51a..0000000000 --- a/packages/schema/parse/src/validate/directives.ts +++ /dev/null @@ -1,268 +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", - "capability", - "enabled_interface", - "annotate", - "env", - ]; - 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 908bbfd9c2..0000000000 --- a/packages/schema/parse/src/validate/types.ts +++ /dev/null @@ -1,235 +0,0 @@ -import { isScalarType, isModuleType, scalarTypeNames } 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"; - -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: ${scalarTypeNames}` - ); - } - }, - // 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}` - )}` - ); - } - }, - }; -}