From f403addc468b9fa6af3b6ab15f9650be6516a7e0 Mon Sep 17 00:00:00 2001 From: dOrgJelli Date: Sat, 4 Mar 2023 04:42:00 -0500 Subject: [PATCH 1/3] feat: allow URIs in local schema imports --- packages/cli/src/lib/SchemaComposer.ts | 53 ++++++-- packages/schema/compose/README.md | 5 +- packages/schema/compose/package.json | 1 + .../schema/compose/src/__tests__/index.ts | 62 ++++++--- .../schema/compose/src/__tests__/utils.ts | 46 +++++++ packages/schema/compose/src/index.ts | 6 +- packages/schema/compose/src/parse.ts | 21 ++- packages/schema/compose/src/resolve.ts | 114 +++++++++------- packages/schema/compose/src/types.ts | 21 ++- packages/schema/parse/src/__tests__/index.ts | 15 +-- packages/schema/parse/src/__tests__/utils.ts | 34 +++++ .../016-local-import-uri/expected/output.json | 4 + .../016-local-import-uri/expected/stdout.json | 7 + .../016-local-import-uri/package.json | 15 +++ .../016-local-import-uri/polywrap.build.yaml | 9 ++ .../016-local-import-uri/polywrap.yaml | 9 ++ .../016-local-import-uri/src/index.ts | 13 ++ .../016-local-import-uri/src/schema.graphql | 2 + .../imports-ext/external.eth/module.ts | 125 ++++++++++++++++++ .../04-from-uri/input/module.graphql | 1 + .../04-from-uri/output/module.graphql | 68 ++++++++++ .../04-from-uri/output/module.ts | 72 ++++++++++ packages/test-cases/index.ts | 60 --------- 23 files changed, 586 insertions(+), 177 deletions(-) create mode 100644 packages/schema/compose/src/__tests__/utils.ts create mode 100644 packages/schema/parse/src/__tests__/utils.ts create mode 100644 packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/expected/output.json create mode 100644 packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/expected/stdout.json create mode 100644 packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/package.json create mode 100644 packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/polywrap.build.yaml create mode 100644 packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/polywrap.yaml create mode 100644 packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/src/index.ts create mode 100644 packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/src/schema.graphql create mode 100644 packages/test-cases/cases/compose/001-local-imports/04-from-uri/imports-ext/external.eth/module.ts create mode 100644 packages/test-cases/cases/compose/001-local-imports/04-from-uri/input/module.graphql create mode 100644 packages/test-cases/cases/compose/001-local-imports/04-from-uri/output/module.graphql create mode 100644 packages/test-cases/cases/compose/001-local-imports/04-from-uri/output/module.ts diff --git a/packages/cli/src/lib/SchemaComposer.ts b/packages/cli/src/lib/SchemaComposer.ts index abc6194d4c..e2231019b0 100644 --- a/packages/cli/src/lib/SchemaComposer.ts +++ b/packages/cli/src/lib/SchemaComposer.ts @@ -58,10 +58,14 @@ export class SchemaComposer { const options: ComposerOptions = { schema: schemaFile, - resolvers: { - external: (uri: string) => this._fetchExternalAbi(uri, import_abis), - local: (path: string) => Promise.resolve(this._fetchLocalSchema(path)), - }, + abiResolver: ( + importFrom: string, + schemaFile: SchemaFile + ) => this._abiResolver( + schemaFile, + importFrom, + import_abis + ), }; this._abi = await composeSchema(options); @@ -72,16 +76,36 @@ export class SchemaComposer { this._abi = undefined; } - private _fetchLocalSchema(schemaPath: string) { - return fs.readFileSync( - path.isAbsolute(schemaPath) - ? schemaPath - : path.join(this._config.project.getManifestDir(), schemaPath), + private _abiResolver( + schemaFile: SchemaFile, + importFrom: string, + import_abi?: PolywrapManifest["source"]["import_abis"] + ): Promise { + if (Uri.isValidUri(importFrom)) { + return this._resolveUri(importFrom, import_abi); + } else { + return Promise.resolve(this._resolvePath( + importFrom, + path.dirname(schemaFile.absolutePath) + )); + } + } + + private _resolvePath(importFrom: string, sourceDir: string): SchemaFile { + const schemaPath = path.isAbsolute(importFrom) ? + importFrom : + path.join(sourceDir, importFrom); + const schema = fs.readFileSync( + schemaPath, "utf-8" ); + return { + schema, + absolutePath: schemaPath + }; } - private async _fetchExternalAbi( + private async _resolveUri( uri: string, import_abis?: PolywrapManifest["source"]["import_abis"] ): Promise { @@ -150,10 +174,11 @@ export class SchemaComposer { schema: schema, absolutePath: path, }, - resolvers: { - external: (uri: string) => this._fetchExternalAbi(uri, import_abis), - local: (path: string) => Promise.resolve(this._fetchLocalSchema(path)), - }, + abiResolver: (importFrom, schemaFile) => this._abiResolver( + schemaFile, + importFrom, + import_abis + ), }); } diff --git a/packages/schema/compose/README.md b/packages/schema/compose/README.md index c1acb2167f..f60b1a0722 100644 --- a/packages/schema/compose/README.md +++ b/packages/schema/compose/README.md @@ -27,9 +27,8 @@ const input: ComposerOptions = { schema, absolutePath, }], - resolvers: { - external: resolveExternal, - local: resolveLocal, + abiResolver: (importFrom: string): Promise => { + ... }, output: ComposerFilter.All }; diff --git a/packages/schema/compose/package.json b/packages/schema/compose/package.json index d72a4a7e09..4ffb912788 100644 --- a/packages/schema/compose/package.json +++ b/packages/schema/compose/package.json @@ -26,6 +26,7 @@ }, "devDependencies": { "@polywrap/test-cases": "0.10.0-pre.10", + "@polywrap/os-js": "0.10.0-pre.10", "@types/jest": "26.0.8", "@types/mustache": "4.0.1", "@types/prettier": "2.6.0", diff --git a/packages/schema/compose/src/__tests__/index.ts b/packages/schema/compose/src/__tests__/index.ts index 2ec585d1e0..79ee6fa4fb 100644 --- a/packages/schema/compose/src/__tests__/index.ts +++ b/packages/schema/compose/src/__tests__/index.ts @@ -1,14 +1,16 @@ -import { ComposerOptions } from ".."; +import { ComposerOptions, SchemaFile } from ".."; +import { + readFileIfExists, + getFilePath, + readNamedExportIfExists +} from "./utils"; import path from "path"; import { readdirSync, Dirent } from "fs"; +import { Uri } from "@polywrap/core-js"; import { Abi, createAbi } from "@polywrap/schema-parse"; -import { - GetPathToComposeTestFiles, - readFileIfExists, - readNamedExportIfExists, -} from "@polywrap/test-cases" +import { GetPathToComposeTestFiles } from "@polywrap/test-cases" const root = GetPathToComposeTestFiles(); @@ -71,42 +73,62 @@ async function importCase( name: string, ): Promise { // Fetch the input schemas - const moduleInput = readFileIfExists("input/module.graphql", directory); + const moduleInput = readFileIfExists(path.join(directory, "input/module.graphql")); + const moduleDir = path.join(directory, "input"); // Fetch the output abi - const moduleAbi = await readNamedExportIfExists("abi", "output/module.ts", directory); + const moduleAbi = await readNamedExportIfExists("abi", path.join(directory, "output/module.ts")); // Fetch the output schema - const moduleSchema = readFileIfExists("output/module.graphql", directory); + const moduleSchema = readFileIfExists(path.join(directory, "output/module.graphql")); - const resolveExternal = async (uri: string): Promise => { + const resolveUri = async (uri: string): Promise => { let abi = createAbi() - const generatedAbi = await readNamedExportIfExists("abi", `imports-ext/${uri}/module.ts`, directory) + const generatedAbi = await readNamedExportIfExists( + "abi", + path.join(directory, `imports-ext/${uri}/module.ts`) + ); if (generatedAbi) { abi = generatedAbi } return Promise.resolve(abi); }; - const resolveLocal = (path: string): Promise => { - return Promise.resolve(readFileIfExists(path, directory, true) || ""); + const resolvePath = (importFrom: string, sourceDir: string): SchemaFile => { + const absolutePath = getFilePath(importFrom, sourceDir); + const schema = readFileIfExists(absolutePath); + if (!schema) { + throw Error(`Did not find schema in directory "${sourceDir}" from import "${importFrom}"`); + } + return { + schema, + absolutePath + }; }; if (!moduleInput) { throw new Error("Expected input schema.graphql file to Exist") } + const schemaPath = path.join( + moduleDir, + "module.graphql" + ); + const input: ComposerOptions = { schema: { schema: moduleInput, - absolutePath: path.join( - directory, - "input/module.graphql" - ), + absolutePath: schemaPath, }, - resolvers: { - external: resolveExternal, - local: resolveLocal, + abiResolver: async (importFrom: string, schemaFile: SchemaFile): Promise => { + if (Uri.isValidUri(importFrom) || importFrom.endsWith(".eth")) { + return await resolveUri(importFrom); + } else { + return Promise.resolve(resolvePath( + importFrom, + path.dirname(schemaFile.absolutePath) + )); + } }, }; diff --git a/packages/schema/compose/src/__tests__/utils.ts b/packages/schema/compose/src/__tests__/utils.ts new file mode 100644 index 0000000000..098e70419b --- /dev/null +++ b/packages/schema/compose/src/__tests__/utils.ts @@ -0,0 +1,46 @@ +import path from "path"; +import { readFileSync, existsSync } from "fs"; +import { normalizeLineEndings } from "@polywrap/os-js"; + +export function readFileIfExists( + filePath: string +): string | undefined { + if (existsSync(filePath)) { + return normalizeLineEndings( + readFileSync(filePath, { encoding: "utf-8" }), + "\n" + ); + } else { + return undefined; + } +}; + +export async function readNamedExportIfExists( + namedExport: string, + filePath: string +): Promise { + if (existsSync(filePath)) { + const module = await import(filePath); + + if (!module[namedExport]) { + throw Error( + `Required named export "${namedExport}" is missing in ${filePath}` + ); + } + + return module[namedExport] as TExport; + } else { + return undefined; + } +} + +export function getFilePath( + file: string, + directory: string +): string { + if (path.isAbsolute(file)) { + return file; + } else { + return path.join(directory, file); + } +} diff --git a/packages/schema/compose/src/index.ts b/packages/schema/compose/src/index.ts index 57225dda95..31a2818ef9 100644 --- a/packages/schema/compose/src/index.ts +++ b/packages/schema/compose/src/index.ts @@ -1,4 +1,4 @@ -import { SchemaFile, AbiResolvers } from "./types"; +import { SchemaFile, AbiResolver } from "./types"; import { resolveImportsAndParseSchemas } from "./resolve"; import { renderSchema } from "./render"; @@ -9,7 +9,7 @@ export { renderSchema }; export interface ComposerOptions { schema: SchemaFile; - resolvers: AbiResolvers; + abiResolver: AbiResolver; } export async function composeSchema( @@ -18,6 +18,6 @@ export async function composeSchema( return await resolveImportsAndParseSchemas( options.schema.schema, options.schema.absolutePath, - options.resolvers + options.abiResolver ); } diff --git a/packages/schema/compose/src/parse.ts b/packages/schema/compose/src/parse.ts index bf3dfdc2de..8ddde24e7d 100644 --- a/packages/schema/compose/src/parse.ts +++ b/packages/schema/compose/src/parse.ts @@ -1,7 +1,6 @@ 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[] { @@ -82,7 +81,7 @@ export function parseExternalImports( externalImports.push({ importedTypes, namespace, - uri, + importFrom: uri, }); } @@ -96,18 +95,18 @@ export function parseExternalImports( // 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) { + if (uriToNamespace[ext.importFrom]) { + if (uriToNamespace[ext.importFrom] !== ext.namespace) { throw Error( `Imports from a single URI must be imported into the same namespace.\nURI: ${ - ext.uri + ext.importFrom }\nNamespace 1: ${ext.namespace}\nNamespace 2: ${ - uriToNamespace[ext.uri] + uriToNamespace[ext.importFrom] }` ); } } else { - uriToNamespace[ext.uri] = ext.namespace; + uriToNamespace[ext.importFrom] = ext.namespace; } } @@ -115,8 +114,7 @@ export function parseExternalImports( } export function parseLocalImports( - imports: RegExpMatchArray[], - schemaPath: string + imports: RegExpMatchArray[] ): LocalImport[] { const localImports: LocalImport[] = []; @@ -134,8 +132,7 @@ export function parseLocalImports( .map((str) => str.replace(/(\s+|\{|\})/g, "")) // Remove empty strings .filter(Boolean); - const importPath = importStatement[2]; - const path = Path.join(Path.dirname(schemaPath), importPath); + const importFrom = importStatement[2]; // Make sure the developer does not try to import a dependencies dependency const index = importTypes.findIndex((str) => str.indexOf("_") > -1); @@ -147,7 +144,7 @@ export function parseLocalImports( localImports.push({ importedTypes: importTypes, - path, + importFrom: importFrom, }); } diff --git a/packages/schema/compose/src/resolve.ts b/packages/schema/compose/src/resolve.ts index dc7798f997..c372c1c86e 100644 --- a/packages/schema/compose/src/resolve.ts +++ b/packages/schema/compose/src/resolve.ts @@ -5,16 +5,14 @@ import { ExternalImport, LocalImport, - AbiResolvers, SYNTAX_REFERENCE, AbiResolver, - SchemaResolver, + SchemaFile, } 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, @@ -43,6 +41,7 @@ import { visitImportedEnumDefinition, isKind, isImportedModuleType, + isModuleType, header, isEnvType, createImportedObjectDefinition, @@ -130,9 +129,15 @@ export async function resolveUseStatements( export async function resolveImportsAndParseSchemas( schema: string, schemaPath: string, - resolvers: AbiResolvers, + abiResolver: AbiResolver, noValidate = false ): Promise { + + // Make sure the schema has the Polywrap header + if (schema.indexOf("### Polywrap Header START ###") === -1) { + schema = addHeader(schema); + } + 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; @@ -164,8 +169,7 @@ export async function resolveImportsAndParseSchemas( ); const localImportsToResolve: LocalImport[] = parseLocalImports( - localImportStatements, - schemaPath + localImportStatements ); const subAbi: WrapAbi = { @@ -179,17 +183,23 @@ export async function resolveImportsAndParseSchemas( importedEnvTypes: [], }; + const schemaFile: SchemaFile = { + schema, + absolutePath: schemaPath + } + const externalImports = await resolveExternalImports( + schemaFile, externalImportsToResolve, - resolvers.external, + abiResolver, subAbi ); await resolveLocalImports( + schemaFile, localImportsToResolve, - resolvers.local, - subAbi, - resolvers + abiResolver, + subAbi ); const capabilitiesByModule = await resolveUseStatements( schema, @@ -645,21 +655,36 @@ function resolveInterfaces( } async function resolveExternalImports( + schemaFile: SchemaFile, importsToResolve: ExternalImport[], - resolveAbi: AbiResolver, + abiResolver: 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; + const { importFrom, namespace, importedTypes } = importToResolve; + const uri = importFrom; + const schemaOrAbi = await abiResolver(importFrom, schemaFile); + let extAbi: WrapAbi; + // Resolve the schema - const extAbi = await resolveAbi(uri); + if (typeof (schemaOrAbi as SchemaFile).schema === "string") { + const schemaFile = schemaOrAbi as SchemaFile; + extAbi = await resolveImportsAndParseSchemas( + schemaFile.schema, + schemaFile.absolutePath, + abiResolver, + true + ); + } else { + extAbi = schemaOrAbi as WrapAbi; + } if (!extAbi) { - throw Error(`Unable to resolve abi at "${uri}"`); + throw Error(`Unable to resolve abi at "${importFrom}"`); } const extTypesToImport = importedTypes; @@ -698,7 +723,7 @@ async function resolveExternalImports( | undefined; // If it's a module type - if (importedType === "Module") { + if (isModuleType(importedType)) { if (!extAbi.moduleType) { extAbi.moduleType = createModuleDefinition({}); } @@ -962,34 +987,34 @@ async function resolveExternalImports( } async function resolveLocalImports( + schemaFile: SchemaFile, importsToResolve: LocalImport[], - resolveSchema: SchemaResolver, - abi: WrapAbi, - resolvers: AbiResolvers + abiResolver: AbiResolver, + abi: WrapAbi ): Promise { for (const importToResolve of importsToResolve) { - const { importedTypes, path } = importToResolve; + const { importedTypes, importFrom } = importToResolve; // Resolve the schema - let schema = await resolveSchema(path); - - if (!schema) { - throw Error(`Unable to resolve schema at "${path}"`); + const schemaOrAbi = await abiResolver(importFrom, schemaFile); + let localAbi: WrapAbi; + + if (typeof (schemaOrAbi as SchemaFile).schema === "string") { + const schemaFile = schemaOrAbi as SchemaFile; + localAbi = await resolveImportsAndParseSchemas( + schemaFile.schema, + schemaFile.absolutePath, + abiResolver, + true + ); + } else { + localAbi = schemaOrAbi as WrapAbi; } - // Make sure the schema has the Polywrap header - if (schema.indexOf("### Polywrap Header START ###") === -1) { - schema = addHeader(schema); + if (!localAbi) { + throw Error(`Unable to resolve schema at "${importFrom}"`); } - // Parse the schema into Abi - const localAbi = await resolveImportsAndParseSchemas( - schema, - path, - resolvers, - true - ); - const extTypesToImport = importedTypes; const starIdx = extTypesToImport.indexOf("*"); @@ -1010,19 +1035,16 @@ async function resolveLocalImports( } // Keep track of all imported type names - const typesToImport: Record = {}; + 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 type: ModuleDefinition | ObjectDefinition | EnumDefinition | undefined; let visitorFunc: Function; - if (isEnvType(importedType)) { + if (isModuleType(importedType)) { + visitorFunc = visitModuleDefinition; + type = localAbi.moduleType; + } else if (isEnvType(importedType)) { visitorFunc = visitEnvDefinition; type = localAbi.envType; } else { @@ -1048,7 +1070,7 @@ async function resolveLocalImports( if (!type) { throw Error( - `Cannot find type "${importedType}" in the schema at ${path}.\nFound: [ ${ + `Cannot find type "${importedType}" in the schema at ${importFrom}.\nFound: [ ${ localAbi.objectTypes && localAbi.objectTypes.map((type) => type.type + " ") }]` @@ -1138,7 +1160,9 @@ async function resolveLocalImports( // Add all imported types into the aggregate Abi for (const importType of Object.keys(typesToImport)) { - if (isKind(typesToImport[importType], DefinitionKind.Env)) { + if (isKind(typesToImport[importType], DefinitionKind.Module)) { + abi.moduleType = typesToImport[importType]; + } else if (isKind(typesToImport[importType], DefinitionKind.Env)) { if (!abi.envType) { abi.envType = createEnvDefinition({}); } diff --git a/packages/schema/compose/src/types.ts b/packages/schema/compose/src/types.ts index a24e15a5b7..fa9327d6b1 100644 --- a/packages/schema/compose/src/types.ts +++ b/packages/schema/compose/src/types.ts @@ -5,18 +5,15 @@ export interface SchemaFile { absolutePath: string; } -export type AbiResolver = (uri: string) => Promise; -export type SchemaResolver = (path: string) => Promise; - -export interface AbiResolvers { - external: AbiResolver; - local: SchemaResolver; -} +export type AbiResolver = ( + importFrom: string, + schemaFile: SchemaFile +) => Promise; export interface ExternalImport { importedTypes: string[]; namespace: string; - uri: string; + importFrom: string; } export interface Use { @@ -26,13 +23,13 @@ export interface Use { export interface LocalImport { importedTypes: string[]; - path: string; + importFrom: string; } export const SYNTAX_REFERENCE = "External Import:\n" + - `import { Type, Module } into Namespace from "external.uri"\n` + - `import * into Namespace from "external.uri"\n` + + `import { Type, Module } into Namespace from "wrap://valid/uri"\n` + + `import * into Namespace from "wrap://valid/uri"\n` + "Local Import:\n" + - `import { Type } from "./local/path/file.graphql"\n` + + `import { Type } from "wrap://valid/uri"\n` + `import * from "./local/path/file.graphql"`; diff --git a/packages/schema/parse/src/__tests__/index.ts b/packages/schema/parse/src/__tests__/index.ts index c2cc127c06..b904ccc263 100644 --- a/packages/schema/parse/src/__tests__/index.ts +++ b/packages/schema/parse/src/__tests__/index.ts @@ -1,13 +1,12 @@ import { WrapAbi } from "../abi"; - -import path from "path"; -import { readdirSync, Dirent } from "fs"; - import { - GetPathToParseTestFiles, readFileIfExists, readNamedExportIfExists -} from "@polywrap/test-cases" +} from "./utils"; + +import path from "path"; +import { readdirSync, Dirent } from "fs"; +import { GetPathToParseTestFiles } from "@polywrap/test-cases" const root = GetPathToParseTestFiles(); @@ -69,10 +68,10 @@ async function importCase( name: string, ): Promise { // Fetch the input schema - const input = readFileIfExists("input.graphql", directory); + const input = readFileIfExists(path.join(directory, "input.graphql")); // Fetch the output Abi - const output = await readNamedExportIfExists("abi", "output.ts", directory); + const output = await readNamedExportIfExists("abi", path.join(directory, "output.ts")); if (!input) { console.error(`Missing input file "input.graphql" for test case "${name}" at ${directory}`); diff --git a/packages/schema/parse/src/__tests__/utils.ts b/packages/schema/parse/src/__tests__/utils.ts new file mode 100644 index 0000000000..d1ceee5329 --- /dev/null +++ b/packages/schema/parse/src/__tests__/utils.ts @@ -0,0 +1,34 @@ +import { readFileSync, existsSync } from "fs"; +import { normalizeLineEndings } from "@polywrap/os-js"; + +export function readFileIfExists( + filePath: string +): string | undefined { + if (existsSync(filePath)) { + return normalizeLineEndings( + readFileSync(filePath, { encoding: "utf-8" }), + "\n" + ); + } else { + return undefined; + } +}; + +export async function readNamedExportIfExists( + namedExport: string, + filePath: string +): Promise { + if (existsSync(filePath)) { + const module = await import(filePath); + + if (!module[namedExport]) { + throw Error( + `Required named export "${namedExport}" is missing in ${filePath}` + ); + } + + return module[namedExport] as TExport; + } else { + return undefined; + } +} diff --git a/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/expected/output.json b/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/expected/output.json new file mode 100644 index 0000000000..d91a8e5545 --- /dev/null +++ b/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/expected/output.json @@ -0,0 +1,4 @@ +[ + "wrap.wasm", + "wrap.info" +] \ No newline at end of file diff --git a/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/expected/stdout.json b/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/expected/stdout.json new file mode 100644 index 0000000000..6cbc97b938 --- /dev/null +++ b/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/expected/stdout.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + "Artifacts written to ./build", + "WRAP manifest written in ./build" + ], + "exitCode": 0 +} diff --git a/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/package.json b/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/package.json new file mode 100644 index 0000000000..b42812c813 --- /dev/null +++ b/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/package.json @@ -0,0 +1,15 @@ +{ + "name": "@polywrap/test-project", + "version": "0.1.0", + "license": "MIT", + "private": true, + "scripts": { + "build": "polywrap build" + }, + "dependencies": { + "@polywrap/wasm-as": "../../../../../../../wasm/as" + }, + "devDependencies": { + "assemblyscript": "0.19.23" + } +} diff --git a/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/polywrap.build.yaml b/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/polywrap.build.yaml new file mode 100644 index 0000000000..de1e44ebac --- /dev/null +++ b/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/polywrap.build.yaml @@ -0,0 +1,9 @@ +format: 0.2.0 +strategies: + image: + node_version: "14.16.0" + include: + - ./package.json +linked_packages: + - name: "@polywrap/wasm-as" + path: ../../../../../../../wasm/as \ No newline at end of file diff --git a/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/polywrap.yaml b/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/polywrap.yaml new file mode 100644 index 0000000000..287dcbabc0 --- /dev/null +++ b/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/polywrap.yaml @@ -0,0 +1,9 @@ +format: 0.3.0 +project: + name: test-project + type: wasm/assemblyscript +source: + module: ./src/index.ts + schema: ./src/schema.graphql +extensions: + build: ./polywrap.build.yaml diff --git a/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/src/index.ts b/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/src/index.ts new file mode 100644 index 0000000000..8136a32517 --- /dev/null +++ b/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/src/index.ts @@ -0,0 +1,13 @@ +import { + Connection, + Args_log +} from "./wrap"; + +export function log(args: Args_log): boolean { + // Just to make sure we can use this type + const con: Connection = { + networkNameOrChainId: args.message, + node: null + }; + return con.networkNameOrChainId === args.message; +} diff --git a/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/src/schema.graphql b/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/src/schema.graphql new file mode 100644 index 0000000000..3065617290 --- /dev/null +++ b/packages/test-cases/cases/cli/wasm/build-cmd/assemblyscript/016-local-import-uri/src/schema.graphql @@ -0,0 +1,2 @@ +#import { Connection } from "ens/wraps.eth:ethereum@1.1.0" +#import * from "ens/wraps.eth:logger@1.0.0" diff --git a/packages/test-cases/cases/compose/001-local-imports/04-from-uri/imports-ext/external.eth/module.ts b/packages/test-cases/cases/compose/001-local-imports/04-from-uri/imports-ext/external.eth/module.ts new file mode 100644 index 0000000000..2494fc621e --- /dev/null +++ b/packages/test-cases/cases/compose/001-local-imports/04-from-uri/imports-ext/external.eth/module.ts @@ -0,0 +1,125 @@ +import { WrapAbi } from "@polywrap/schema-parse"; + +export const abi: WrapAbi = { + version: "0.1", + "objectTypes": [ + { + "type": "ExternalType", + "kind": 1, + "properties": [ + { + "type": "String", + "name": "str", + "kind": 34, + "scalar": { + "type": "String", + "name": "str", + "kind": 4 + } + } + ], + "interfaces": [] + } + ], + "enumTypes": [], + "interfaceTypes": [], + "importedObjectTypes": [], + "importedModuleTypes": [], + "importedEnumTypes": [], + "importedEnvTypes": [], + "envType": { + "type": "Env", + "kind": 65536, + "properties": [ + { + "type": "ExternalType", + "name": "externalProp", + "kind": 34, + "object": { + "type": "ExternalType", + "name": "externalProp", + "kind": 8192 + } + } + ], + "interfaces": [] + }, + "moduleType": { + "type": "Module", + "kind": 128, + "methods": [ + { + "type": "Method", + "name": "envMethod", + "required": true, + "kind": 64, + "arguments": [ + { + "type": "String", + "name": "arg", + "required": true, + "kind": 34, + "scalar": { + "type": "String", + "name": "arg", + "required": true, + "kind": 4 + } + } + ], + "return": { + "type": "String", + "name": "envMethod", + "required": true, + "kind": 34, + "scalar": { + "type": "String", + "name": "envMethod", + "required": true, + "kind": 4 + } + }, + "env": { + "required": true + } + }, + { + "type": "Method", + "name": "optEnvMethod", + "required": true, + "kind": 64, + "arguments": [ + { + "type": "String", + "name": "arg", + "required": true, + "kind": 34, + "scalar": { + "type": "String", + "name": "arg", + "required": true, + "kind": 4 + } + } + ], + "return": { + "type": "String", + "name": "optEnvMethod", + "required": true, + "kind": 34, + "scalar": { + "type": "String", + "name": "optEnvMethod", + "required": true, + "kind": 4 + } + }, + "env": { + "required": false + } + } + ], + "imports": [], + "interfaces": [] + } +}; diff --git a/packages/test-cases/cases/compose/001-local-imports/04-from-uri/input/module.graphql b/packages/test-cases/cases/compose/001-local-imports/04-from-uri/input/module.graphql new file mode 100644 index 0000000000..66cf0bd4eb --- /dev/null +++ b/packages/test-cases/cases/compose/001-local-imports/04-from-uri/input/module.graphql @@ -0,0 +1 @@ +#import { ExternalType, Env, Module } from "external.eth" diff --git a/packages/test-cases/cases/compose/001-local-imports/04-from-uri/output/module.graphql b/packages/test-cases/cases/compose/001-local-imports/04-from-uri/output/module.graphql new file mode 100644 index 0000000000..ff06af08d1 --- /dev/null +++ b/packages/test-cases/cases/compose/001-local-imports/04-from-uri/output/module.graphql @@ -0,0 +1,68 @@ +### 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 ### + +type Module { + envMethod( + arg: String! + ): String! @env(required: true) + + optEnvMethod( + arg: String! + ): String! @env(required: false) +} + +type Env { + externalProp: ExternalType +} + +type ExternalType { + str: String +} + +### Imported Modules START ### + +### Imported Modules END ### + +### Imported Objects START ### + +### Imported Objects END ### + +### Imported Envs START ### + +### Imported Envs END ### diff --git a/packages/test-cases/cases/compose/001-local-imports/04-from-uri/output/module.ts b/packages/test-cases/cases/compose/001-local-imports/04-from-uri/output/module.ts new file mode 100644 index 0000000000..0ae917fe19 --- /dev/null +++ b/packages/test-cases/cases/compose/001-local-imports/04-from-uri/output/module.ts @@ -0,0 +1,72 @@ +import { + createMethodDefinition, + createModuleDefinition, + createObjectPropertyDefinition, + createScalarPropertyDefinition, + WrapAbi, + createObjectDefinition, + createEnvDefinition, +} from "@polywrap/schema-parse"; + +export const abi: WrapAbi = { + version: "0.1", + moduleType: createModuleDefinition({ + methods: [ + createMethodDefinition({ + name: "envMethod", + return: createScalarPropertyDefinition({ + name: "envMethod", + type: "String", + required: true, + }), + arguments: [ + createScalarPropertyDefinition({ + name: "arg", + type: "String", + required: true, + }), + ], + env: { + required: true + } + }), + createMethodDefinition({ + name: "optEnvMethod", + return: createScalarPropertyDefinition({ + name: "optEnvMethod", + type: "String", + required: true, + }), + arguments: [ + createScalarPropertyDefinition({ + name: "arg", + type: "String", + required: true, + }), + ], + env: { + required: false + } + }), + ] + }), + objectTypes: [ + createObjectDefinition({ + type: "ExternalType", + properties: [ + createScalarPropertyDefinition({ + name: "str", + type: "String" + }) + ], + }), + ], + envType: createEnvDefinition({ + properties: [ + createObjectPropertyDefinition({ + name: "externalProp", + type: "ExternalType" + }) + ] + }) +}; diff --git a/packages/test-cases/index.ts b/packages/test-cases/index.ts index b86122b1b1..203c8df770 100644 --- a/packages/test-cases/index.ts +++ b/packages/test-cases/index.ts @@ -1,7 +1,3 @@ -import path from "path"; -import { readFileSync, existsSync } from "fs"; - -import { normalizeLineEndings } from "@polywrap/os-js"; import admZip from 'adm-zip'; const axios = require("axios"); const shell = require("shelljs"); @@ -12,62 +8,6 @@ export const GetPathToParseTestFiles = () => `${__dirname}/cases/parse` export const GetPathToTestWrappers = () => `${__dirname}/cases/wrappers` export const GetPathToCliTestFiles = () => `${__dirname}/cases/cli`; -export function readFileIfExists( - file: string, - directory: string, - absolute = false -): string | undefined { - const filePath = getFilePath( - file, - directory, - absolute - ); - - if (existsSync(filePath)) { - return normalizeLineEndings( - readFileSync(filePath, { encoding: "utf-8" }), - "\n" - ); - } else { - return undefined; - } -}; - -export async function readNamedExportIfExists( - namedExport: string, - file: string, - directory: string, - absolute = false -): Promise { - const filePath = getFilePath(file, directory, absolute); - - if (existsSync(filePath)) { - const module = await import(filePath); - - if (!module[namedExport]) { - throw Error( - `Required named export "${namedExport}" is missing in ${filePath}` - ); - } - - return module[namedExport] as TExport; - } else { - return undefined; - } -} - -function getFilePath( - file: string, - directory: string, - absolute = false -): string { - if (absolute) { - return file; - } else { - return path.join(directory, file); - } -} - export async function fetchWrappers(): Promise { // function to fetch file from GitHub release async function fetchFromGithub(url: string) { From 590e11c75d315c6e11b278099dbc1333a8750831 Mon Sep 17 00:00:00 2001 From: dOrgJelli Date: Sat, 4 Mar 2023 04:53:42 -0500 Subject: [PATCH 2/3] chore: lint fix --- packages/cli/src/lib/SchemaComposer.ts | 37 +++++++++----------------- packages/schema/compose/src/parse.ts | 4 +-- packages/schema/compose/src/resolve.ts | 19 ++++++++----- 3 files changed, 26 insertions(+), 34 deletions(-) diff --git a/packages/cli/src/lib/SchemaComposer.ts b/packages/cli/src/lib/SchemaComposer.ts index e2231019b0..8e9ad4dcbf 100644 --- a/packages/cli/src/lib/SchemaComposer.ts +++ b/packages/cli/src/lib/SchemaComposer.ts @@ -58,14 +58,8 @@ export class SchemaComposer { const options: ComposerOptions = { schema: schemaFile, - abiResolver: ( - importFrom: string, - schemaFile: SchemaFile - ) => this._abiResolver( - schemaFile, - importFrom, - import_abis - ), + abiResolver: (importFrom: string, schemaFile: SchemaFile) => + this._abiResolver(schemaFile, importFrom, import_abis), }; this._abi = await composeSchema(options); @@ -84,24 +78,20 @@ export class SchemaComposer { if (Uri.isValidUri(importFrom)) { return this._resolveUri(importFrom, import_abi); } else { - return Promise.resolve(this._resolvePath( - importFrom, - path.dirname(schemaFile.absolutePath) - )); + return Promise.resolve( + this._resolvePath(importFrom, path.dirname(schemaFile.absolutePath)) + ); } } private _resolvePath(importFrom: string, sourceDir: string): SchemaFile { - const schemaPath = path.isAbsolute(importFrom) ? - importFrom : - path.join(sourceDir, importFrom); - const schema = fs.readFileSync( - schemaPath, - "utf-8" - ); + const schemaPath = path.isAbsolute(importFrom) + ? importFrom + : path.join(sourceDir, importFrom); + const schema = fs.readFileSync(schemaPath, "utf-8"); return { schema, - absolutePath: schemaPath + absolutePath: schemaPath, }; } @@ -174,11 +164,8 @@ export class SchemaComposer { schema: schema, absolutePath: path, }, - abiResolver: (importFrom, schemaFile) => this._abiResolver( - schemaFile, - importFrom, - import_abis - ), + abiResolver: (importFrom, schemaFile) => + this._abiResolver(schemaFile, importFrom, import_abis), }); } diff --git a/packages/schema/compose/src/parse.ts b/packages/schema/compose/src/parse.ts index 8ddde24e7d..927445527a 100644 --- a/packages/schema/compose/src/parse.ts +++ b/packages/schema/compose/src/parse.ts @@ -113,9 +113,7 @@ export function parseExternalImports( return externalImports; } -export function parseLocalImports( - imports: RegExpMatchArray[] -): LocalImport[] { +export function parseLocalImports(imports: RegExpMatchArray[]): LocalImport[] { const localImports: LocalImport[] = []; for (const importStatement of imports) { diff --git a/packages/schema/compose/src/resolve.ts b/packages/schema/compose/src/resolve.ts index c372c1c86e..c11de78b3a 100644 --- a/packages/schema/compose/src/resolve.ts +++ b/packages/schema/compose/src/resolve.ts @@ -13,6 +13,7 @@ import { parseExternalImports, parseLocalImports, parseUse } from "./parse"; import { renderSchema } from "./render"; import { checkDuplicateEnvProperties } from "./env"; import { addHeader } from "./templates/header.mustache"; + import { WrapAbi, ObjectDefinition, @@ -132,7 +133,6 @@ export async function resolveImportsAndParseSchemas( abiResolver: AbiResolver, noValidate = false ): Promise { - // Make sure the schema has the Polywrap header if (schema.indexOf("### Polywrap Header START ###") === -1) { schema = addHeader(schema); @@ -185,8 +185,8 @@ export async function resolveImportsAndParseSchemas( const schemaFile: SchemaFile = { schema, - absolutePath: schemaPath - } + absolutePath: schemaPath, + }; const externalImports = await resolveExternalImports( schemaFile, @@ -669,7 +669,7 @@ async function resolveExternalImports( const uri = importFrom; const schemaOrAbi = await abiResolver(importFrom, schemaFile); let extAbi: WrapAbi; - + // Resolve the schema if (typeof (schemaOrAbi as SchemaFile).schema === "string") { const schemaFile = schemaOrAbi as SchemaFile; @@ -1035,10 +1035,17 @@ async function resolveLocalImports( } // Keep track of all imported type names - const typesToImport: Record = {}; + const typesToImport: Record< + string, + ModuleDefinition | ObjectDefinition | EnumDefinition + > = {}; for (const importedType of extTypesToImport) { - let type: ModuleDefinition | ObjectDefinition | EnumDefinition | undefined; + let type: + | ModuleDefinition + | ObjectDefinition + | EnumDefinition + | undefined; let visitorFunc: Function; if (isModuleType(importedType)) { From 18f634d2184e0d04b3c97903c80de42488611d83 Mon Sep 17 00:00:00 2001 From: dOrgJelli Date: Mon, 6 Mar 2023 17:37:23 -0500 Subject: [PATCH 3/3] chore: minor updates --- packages/cli/src/lib/SchemaComposer.ts | 4 ++-- packages/schema/compose/README.md | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/lib/SchemaComposer.ts b/packages/cli/src/lib/SchemaComposer.ts index 8e9ad4dcbf..ae192f3f87 100644 --- a/packages/cli/src/lib/SchemaComposer.ts +++ b/packages/cli/src/lib/SchemaComposer.ts @@ -73,10 +73,10 @@ export class SchemaComposer { private _abiResolver( schemaFile: SchemaFile, importFrom: string, - import_abi?: PolywrapManifest["source"]["import_abis"] + import_abis?: PolywrapManifest["source"]["import_abis"] ): Promise { if (Uri.isValidUri(importFrom)) { - return this._resolveUri(importFrom, import_abi); + return this._resolveUri(importFrom, import_abis); } else { return Promise.resolve( this._resolvePath(importFrom, path.dirname(schemaFile.absolutePath)) diff --git a/packages/schema/compose/README.md b/packages/schema/compose/README.md index f60b1a0722..fd4b7028d7 100644 --- a/packages/schema/compose/README.md +++ b/packages/schema/compose/README.md @@ -27,7 +27,10 @@ const input: ComposerOptions = { schema, absolutePath, }], - abiResolver: (importFrom: string): Promise => { + abiResolver: ( + importFrom: string, + schemaFile: SchemaFile + ) => Promise => { ... }, output: ComposerFilter.All