Rust implementation of NextGraph, a Decentralized and local-first web 3.0 ecosystem https://nextgraph.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
nextgraph-rs/sdk/ng-sdk-js/ng-shex-orm/src/schema-converter/converter.ts

105 lines
3.3 KiB

import type { Schema } from "@ldo/traverser-shexj";
import { jsonld2graphobject } from "jsonld2graphobject";
import * as dom from "dts-dom";
import {
ShexJTypingTransformerCompact,
additionalCompactEnumAliases,
} from "./transformers/ShexJTypingTransformer.ts";
import { ShexJSchemaTransformerCompact } from "./transformers/ShexJSchemaTransformer.ts";
import type { Schema as ShapeSchema, Shape } from "../types.ts";
export interface TypingReturn {
typingsString: string;
typings: {
typingString: string;
dts: dom.TopLevelDeclaration;
}[];
}
export async function shexJConverter(
shexj: Schema
): Promise<[TypingReturn, ShapeSchema]> {
// Prepare processed schema (names still rely on context visitor)
const processedShexj: Schema = (await jsonld2graphobject(
{
...shexj,
"@id": "SCHEMA",
"@context": "http://www.w3.org/ns/shex.jsonld",
},
"SCHEMA"
)) as unknown as Schema;
additionalCompactEnumAliases.clear();
const declarations = await ShexJTypingTransformerCompact.transform(
processedShexj,
"Schema",
null
);
const compactSchemaShapesUnflattened =
await ShexJSchemaTransformerCompact.transform(
processedShexj,
"Schema",
null
);
const compactSchema = flattenSchema(compactSchemaShapesUnflattened);
// Append only enum aliases (no interface Id aliases in compact format now)
const hasName = (d: unknown): d is { name: string } =>
typeof (d as { name?: unknown }).name === "string";
additionalCompactEnumAliases.forEach((alias) => {
const exists = declarations.some((d) => hasName(d) && d.name === alias);
if (!exists)
declarations.push(
dom.create.alias(alias, dom.create.namedTypeReference("IRI"))
);
});
const typings = declarations.map((declaration) => ({
typingString: dom
.emit(declaration, {
rootFlags: dom.ContextFlags.InAmbientNamespace,
})
.replace(/\r\n/g, "\n"),
dts: declaration,
}));
const header = `export type IRI = string;\n\n`;
const typingsString =
header + typings.map((t) => `export ${t.typingString}`).join("");
return [{ typingsString, typings }, compactSchema];
}
/** Shapes may be nested. Put all to their root and give nested ones ids. */
function flattenSchema(shapes: Shape[]): ShapeSchema {
let schema: ShapeSchema = {};
for (const shape of shapes) {
schema[shape.iri] = shape;
// Find nested, unflattened (i.e. anonymous) schemas in properties.
const nestedSchemaPredicates = shape.predicates.filter(
(pred) =>
pred.type === "nested" && typeof pred.nestedShape === "object"
);
for (const pred of nestedSchemaPredicates) {
const newId = shape.iri + "||" + pred.iri;
// Recurse
const flattened = flattenSchema([
{
...(pred.nestedShape as Shape),
iri: newId,
},
]);
// Replace the nested schema with its new id.
pred.nestedShape = newId;
schema = { ...schema, ...flattened };
}
// Flatten / Recurse
}
return schema;
}