Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 28 additions & 16 deletions packages/cli/src/lib/SchemaComposer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,8 @@ 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);
Expand All @@ -72,16 +70,32 @@ export class SchemaComposer {
this._abi = undefined;
}

private _fetchLocalSchema(schemaPath: string) {
return fs.readFileSync(
path.isAbsolute(schemaPath)
? schemaPath
: path.join(this._config.project.getManifestDir(), schemaPath),
"utf-8"
);
private _abiResolver(
schemaFile: SchemaFile,
importFrom: string,
import_abis?: PolywrapManifest["source"]["import_abis"]
): Promise<WrapAbi | SchemaFile> {
if (Uri.isValidUri(importFrom)) {
return this._resolveUri(importFrom, import_abis);
} 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<WrapAbi> {
Expand Down Expand Up @@ -150,10 +164,8 @@ 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),
});
}

Expand Down
8 changes: 5 additions & 3 deletions packages/schema/compose/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ const input: ComposerOptions = {
schema,
absolutePath,
}],
resolvers: {
external: resolveExternal,
local: resolveLocal,
abiResolver: (
importFrom: string,
schemaFile: SchemaFile
) => Promise<Abi | SchemaFile> => {
...
},
output: ComposerFilter.All
};
Expand Down
1 change: 1 addition & 0 deletions packages/schema/compose/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
},
"devDependencies": {
"@polywrap/test-cases": "0.10.0-pre.11",
"@polywrap/os-js": "0.10.0-pre.11",
"@types/jest": "26.0.8",
"@types/mustache": "4.0.1",
"@types/prettier": "2.6.0",
Expand Down
62 changes: 42 additions & 20 deletions packages/schema/compose/src/__tests__/index.ts
Original file line number Diff line number Diff line change
@@ -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();

Expand Down Expand Up @@ -71,42 +73,62 @@ async function importCase(
name: string,
): Promise<TestCase | undefined> {
// 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>("abi", "output/module.ts", directory);
const moduleAbi = await readNamedExportIfExists<Abi>("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<Abi> => {
const resolveUri = async (uri: string): Promise<Abi> => {
let abi = createAbi()
const generatedAbi = await readNamedExportIfExists<Abi>("abi", `imports-ext/${uri}/module.ts`, directory)
const generatedAbi = await readNamedExportIfExists<Abi>(
"abi",
path.join(directory, `imports-ext/${uri}/module.ts`)
);
if (generatedAbi) {
abi = generatedAbi
}
return Promise.resolve(abi);
};

const resolveLocal = (path: string): Promise<string> => {
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<Abi | SchemaFile> => {
if (Uri.isValidUri(importFrom) || importFrom.endsWith(".eth")) {
return await resolveUri(importFrom);
} else {
return Promise.resolve(resolvePath(
importFrom,
path.dirname(schemaFile.absolutePath)
));
}
},
};

Expand Down
46 changes: 46 additions & 0 deletions packages/schema/compose/src/__tests__/utils.ts
Original file line number Diff line number Diff line change
@@ -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<TExport>(
namedExport: string,
filePath: string
): Promise<TExport | undefined> {
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);
}
}
6 changes: 3 additions & 3 deletions packages/schema/compose/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SchemaFile, AbiResolvers } from "./types";
import { SchemaFile, AbiResolver } from "./types";
import { resolveImportsAndParseSchemas } from "./resolve";
import { renderSchema } from "./render";

Expand All @@ -9,7 +9,7 @@ export { renderSchema };

export interface ComposerOptions {
schema: SchemaFile;
resolvers: AbiResolvers;
abiResolver: AbiResolver;
}

export async function composeSchema(
Expand All @@ -18,6 +18,6 @@ export async function composeSchema(
return await resolveImportsAndParseSchemas(
options.schema.schema,
options.schema.absolutePath,
options.resolvers
options.abiResolver
);
}
23 changes: 9 additions & 14 deletions packages/schema/compose/src/parse.ts
Original file line number Diff line number Diff line change
@@ -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[] {
Expand Down Expand Up @@ -82,7 +81,7 @@ export function parseExternalImports(
externalImports.push({
importedTypes,
namespace,
uri,
importFrom: uri,
});
}

Expand All @@ -96,28 +95,25 @@ export function parseExternalImports(
// Make sure all uris have the same namespace
const uriToNamespace: Record<string, string> = {};
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;
}
}

return externalImports;
}

export function parseLocalImports(
imports: RegExpMatchArray[],
schemaPath: string
): LocalImport[] {
export function parseLocalImports(imports: RegExpMatchArray[]): LocalImport[] {
const localImports: LocalImport[] = [];

for (const importStatement of imports) {
Expand All @@ -134,8 +130,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);
Expand All @@ -147,7 +142,7 @@ export function parseLocalImports(

localImports.push({
importedTypes: importTypes,
path,
importFrom: importFrom,
});
}

Expand Down
Loading