diff --git a/package-lock.json b/package-lock.json index c93a0eb..32fd313 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16853,294 +16853,6 @@ "node": ">=18" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/netbsd-arm64": { "version": "0.25.4", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", @@ -17157,24 +16869,6 @@ "node": ">=18" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/openbsd-arm64": { "version": "0.25.4", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", @@ -17191,24 +16885,6 @@ "node": ">=18" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/openharmony-arm64": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", @@ -17226,78 +16902,6 @@ "node": ">=18" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -23459,15 +23063,6 @@ "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", "license": "MIT" }, - "node_modules/@types/whatwg-mimetype": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz", - "integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -29671,35 +29266,6 @@ "uglify-js": "^3.1.4" } }, - "node_modules/happy-dom": { - "version": "18.0.1", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-18.0.1.tgz", - "integrity": "sha512-qn+rKOW7KWpVTtgIUi6RVmTBZJSe2k0Db0vh1f7CWrWclkkc7/Q+FrOfkZIb2eiErLyqu5AXEzE7XthO9JVxRA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@types/node": "^20.0.0", - "@types/whatwg-mimetype": "^3.0.2", - "whatwg-mimetype": "^3.0.0" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/happy-dom/node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - } - }, "node_modules/hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -45813,18 +45379,6 @@ "defaults": "^1.0.3" } }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/web-streams-ponyfill": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/web-streams-ponyfill/-/web-streams-ponyfill-1.4.2.tgz", diff --git a/packages/cli/src/build.ts b/packages/cli/src/build.ts index b24852f..984fcf6 100644 --- a/packages/cli/src/build.ts +++ b/packages/cli/src/build.ts @@ -17,6 +17,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url)); interface BuildOptions { input: string; output: string; + format?: "ldo" | "compact"; } export async function build(options: BuildOptions) { @@ -45,26 +46,33 @@ export async function build(options: BuildOptions) { return; } // Convert the content to types - const [typings, context] = await schemaConverterShex(schema); + const format = options.format || "ldo"; + const [typings, context] = await schemaConverterShex(schema, { format }); + const templates: string[] = ["schema", "typings", "shapeTypes"]; + if (format === "ldo") { + templates.unshift("context"); + } await Promise.all( - ["context", "schema", "shapeTypes", "typings"].map( - async (templateName) => { - const finalContent = await renderFile( - path.join(__dirname, "./templates", `${templateName}.ejs`), - { - typings: typings.typings, - fileName, - schema: JSON.stringify(schema, null, 2), - context: JSON.stringify(context, null, 2), - }, - ); - // Save conversion to document - await fs.promises.writeFile( - path.join(options.output, `${fileName}.${templateName}.ts`), - await prettier.format(finalContent, { parser: "typescript" }), - ); - }, - ), + templates.map(async (templateName) => { + const templateFile = + templateName === "shapeTypes" && format === "compact" + ? "shapeTypes.compact" + : templateName; + const finalContent = await renderFile( + path.join(__dirname, "./templates", `${templateFile}.ejs`), + { + typings: typings.typings, + fileName, + schema: JSON.stringify(schema, null, 2), + context: JSON.stringify(context, null, 2), + format, + } + ); + await fs.promises.writeFile( + path.join(options.output, `${fileName}.${templateName}.ts`), + await prettier.format(finalContent, { parser: "typescript" }) + ); + }) ); }); diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 2cf84ea..4f44a12 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -16,6 +16,11 @@ program .description("Build contents of a shex folder into Shape Types") .option("-i, --input ", "Provide the input path", "./.shapes") .option("-o, --output ", "Provide the output path", "./.ldo") + .option( + "-f, --format ", + 'Typings format: "compact" (default) or "ldo"', + "compact" + ) .action(build); program @@ -36,17 +41,17 @@ program .requiredOption( "-p, --project ", "Provide the path to the root project", - "./", + "./" ) .requiredOption( "-s, --shapes ", "Provide the path to the shapes folder", - "./.shapes", + "./.shapes" ) .requiredOption( "-s, --ldo ", "Provide the path to the ldo folder", - "./.ldo", + "./.ldo" ) .action(generateReadme); diff --git a/packages/cli/src/templates/shapeTypes.compact.ejs b/packages/cli/src/templates/shapeTypes.compact.ejs new file mode 100644 index 0000000..4c284b6 --- /dev/null +++ b/packages/cli/src/templates/shapeTypes.compact.ejs @@ -0,0 +1,14 @@ +import { CompactShapeType } from "@ldo/ldo"; +import { <%- fileName %>Schema } from "./<%- fileName %>.schema"; + import { + <% typings.forEach((typing)=> { if (!/Id$/.test(typing.dts.name)) { -%> + <%- typing.dts.name %>, + <% } }); -%>} from "./<%- fileName %>.typings"; + + // Compact ShapeTypes for <%- fileName %> + <% typings.forEach((typing)=> { if (!/Id$/.test(typing.dts.name)) { -%> + export const <%- typing.dts.name %>ShapeType: CompactShapeType<<%- typing.dts.name %>> = { + schema: <%- fileName %>Schema, + shape: "<%- typing.dts.shapeId %>", + }; + <% } }); -%> \ No newline at end of file diff --git a/packages/cli/src/templates/shapeTypes.ejs b/packages/cli/src/templates/shapeTypes.ejs index 5148b30..568e22b 100644 --- a/packages/cli/src/templates/shapeTypes.ejs +++ b/packages/cli/src/templates/shapeTypes.ejs @@ -1,24 +1,16 @@ import { ShapeType } from "@ldo/ldo"; import { <%- fileName %>Schema } from "./<%- fileName %>.schema"; -import { <%- fileName %>Context } from "./<%- fileName %>.context"; -import { -<% typings.forEach((typing) => { -%> - <%- typing.dts.name %>, -<% }); -%>} from "./<%- fileName %>.typings"; + import { <%- fileName %>Context } from "./<%- fileName %>.context"; + import { + <% typings.forEach((typing)=> { if (!/Id$/.test(typing.dts.name)) { -%> + <%- typing.dts.name %>, + <% } }); -%>} from "./<%- fileName %>.typings"; -/** - * ============================================================================= - * LDO ShapeTypes <%- fileName %> - * ============================================================================= - */ -<% typings.forEach((typing) => { -%> - -/** - * <%- typing.dts.name %> ShapeType - */ -export const <%- typing.dts.name %>ShapeType: ShapeType<<%- typing.dts.name %>> = { - schema: <%- fileName %>Schema, - shape: "<%- typing.dts.shapeId %>", - context: <%- fileName %>Context, -}; -<% }); -%> \ No newline at end of file + // LDO ShapeTypes for <%- fileName %> + <% typings.forEach((typing)=> { if (!/Id$/.test(typing.dts.name)) { -%> + export const <%- typing.dts.name %>ShapeType: ShapeType<<%- typing.dts.name %>> = { + schema: <%- fileName %>Schema, + shape: "<%- typing.dts.shapeId %>", + context: <%- fileName %>Context, + }; + <% } }); -%> \ No newline at end of file diff --git a/packages/cli/src/templates/typings.ejs b/packages/cli/src/templates/typings.ejs index 2ec78c8..a88a453 100644 --- a/packages/cli/src/templates/typings.ejs +++ b/packages/cli/src/templates/typings.ejs @@ -1,14 +1,18 @@ -import { LdoJsonldContext, LdSet } from "@ldo/ldo"; +<% if (format==='ldo' ) { -%> + import { LdoJsonldContext, LdSet } from "@ldo/ldo"; + <% } else { -%> + export type IRI = string; + <% } -%> -/** - * ============================================================================= - * Typescript Typings for <%- fileName %> - * ============================================================================= - */ + /** + * ============================================================================= + * Typescript Typings for <%- fileName %> + * ============================================================================= + */ -<% typings.forEach((typing) => { -%> -/** - * <%- typing.dts.name %> Type - */ -export <%- typing.typingString -%> -<% }); -%> \ No newline at end of file + <% typings.forEach((typing)=> { -%> + /** + * <%- typing.dts.name %> Type + */ + export <%- typing.typingString -%> + <% }); -%> \ No newline at end of file diff --git a/packages/ldo/src/LdoBuilder.ts b/packages/ldo/src/LdoBuilder.ts index fdca216..785a955 100644 --- a/packages/ldo/src/LdoBuilder.ts +++ b/packages/ldo/src/LdoBuilder.ts @@ -4,7 +4,7 @@ import type { JsonldDatasetProxyBuilder, LdSet, } from "@ldo/jsonld-dataset-proxy"; -import type { ShapeType } from "./ShapeType.js"; +import type { ShapeType, CompactShapeType, AnyShapeType } from "./ShapeType.js"; import type { LdoBase } from "./util.js"; import { normalizeNodeName, normalizeNodeNames } from "./util.js"; @@ -32,7 +32,7 @@ export class LdoBuilder { * @internal */ protected jsonldDatasetProxyBuilder: JsonldDatasetProxyBuilder; - protected shapeType: ShapeType; + protected shapeType: ShapeType | CompactShapeType; /** * Initializes the LdoBuilder @@ -42,7 +42,7 @@ export class LdoBuilder { */ constructor( jsonldDatasetProxyBuilder: JsonldDatasetProxyBuilder, - shapeType: ShapeType, + shapeType: AnyShapeType, ) { this.jsonldDatasetProxyBuilder = jsonldDatasetProxyBuilder; this.shapeType = shapeType; diff --git a/packages/ldo/src/LdoDataset.ts b/packages/ldo/src/LdoDataset.ts index a8d15c1..8764b19 100644 --- a/packages/ldo/src/LdoDataset.ts +++ b/packages/ldo/src/LdoDataset.ts @@ -2,7 +2,7 @@ import type { Quad } from "@rdfjs/types"; import jsonldDatasetProxy from "@ldo/jsonld-dataset-proxy"; import { SubscribableDataset } from "@ldo/subscribable-dataset"; import { LdoBuilder } from "./LdoBuilder.js"; -import type { ShapeType } from "./ShapeType.js"; +import type { AnyShapeType, ShapeType, CompactShapeType } from "./ShapeType.js"; import type { LdoBase } from "./index.js"; import { LdoTransactionDataset } from "./LdoTransactionDataset.js"; import type { ILdoDataset } from "./types.js"; @@ -35,10 +35,14 @@ export class LdoDataset * @returns A builder for the given type */ public usingType( - shapeType: ShapeType, + shapeType: AnyShapeType, ): LdoBuilder { - const proxyBuilder = jsonldDatasetProxy(this, shapeType.context); - return new LdoBuilder(proxyBuilder, shapeType); + const context = (shapeType as ShapeType).context || {}; + const proxyBuilder = jsonldDatasetProxy(this, context); + return new LdoBuilder( + proxyBuilder, + shapeType as ShapeType | CompactShapeType, + ); } public startTransaction(): LdoTransactionDataset { diff --git a/packages/ldo/src/LdoTransactionDataset.ts b/packages/ldo/src/LdoTransactionDataset.ts index 75bb43c..dbd21ac 100644 --- a/packages/ldo/src/LdoTransactionDataset.ts +++ b/packages/ldo/src/LdoTransactionDataset.ts @@ -2,7 +2,7 @@ import { TransactionDataset } from "@ldo/subscribable-dataset"; import type { Quad } from "@rdfjs/types"; import type { ILdoDataset } from "./types.js"; import { LdoBuilder } from "./LdoBuilder.js"; -import type { ShapeType } from "./ShapeType.js"; +import type { ShapeType, AnyShapeType } from "./ShapeType.js"; import type { LdoBase } from "./util.js"; import jsonldDatasetProxy from "@ldo/jsonld-dataset-proxy"; @@ -11,9 +11,10 @@ export class LdoTransactionDataset implements ILdoDataset { usingType( - shapeType: ShapeType, + shapeType: AnyShapeType, ): LdoBuilder { - const proxyBuilder = jsonldDatasetProxy(this, shapeType.context); + const context = (shapeType as ShapeType).context || {}; + const proxyBuilder = jsonldDatasetProxy(this, context); return new LdoBuilder(proxyBuilder, shapeType); } } diff --git a/packages/ldo/src/ShapeType.ts b/packages/ldo/src/ShapeType.ts index 8ce0234..9c455fa 100644 --- a/packages/ldo/src/ShapeType.ts +++ b/packages/ldo/src/ShapeType.ts @@ -54,3 +54,17 @@ export type ShapeType = { */ exampleData?: Type; }; + +/** + * CompactShapeType is a reduced variant without a JSON-LD context, used for the + * compact typings format. + */ +export type CompactShapeType = { + schema: Schema; + shape: string; + exampleData?: Type; +}; + +export type AnyShapeType = + | ShapeType + | CompactShapeType; diff --git a/packages/schema-converter-shex/src/context/JsonLdContextBuilder.ts b/packages/schema-converter-shex/src/context/JsonLdContextBuilder.ts index bd99c0d..24785dd 100644 --- a/packages/schema-converter-shex/src/context/JsonLdContextBuilder.ts +++ b/packages/schema-converter-shex/src/context/JsonLdContextBuilder.ts @@ -9,14 +9,14 @@ import { hashValueSetValue } from "./util/hashValueSetValue.js"; export function iriToName(iri: string): string { try { const url = new URL(iri); - let name: string + let name: string; if (url.hash) { name = url.hash.slice(1); } else { const splitPathname = url.pathname.split("/"); name = splitPathname[splitPathname.length - 1]; } - return name.replace(/(?(); + +export interface CompactTransformerContext { + getNameFromIri: (iri: string, rdfType?: string) => string; +} + +function commentFromAnnotations( + annotations?: Annotation[], +): string | undefined { + const commentAnnotationObject = annotations?.find( + (annotation) => + annotation.predicate === "http://www.w3.org/2000/01/rdf-schema#comment", + )?.object; + if (typeof commentAnnotationObject === "string") + return commentAnnotationObject; + return commentAnnotationObject?.value; +} + +// Helper: classify a dom.Type into categories we care about. +function isObjectLike(t: dom.Type): boolean { + return ( + (t as dom.ObjectType).kind === "object" || + (t as dom.InterfaceDeclaration).kind === "interface" + ); +} + +function isPrimitiveLike(t: dom.Type): boolean { + const kind = (t as any)?.kind; + if (kind === "name") return true; // named references and intrinsic tokens + if (kind === "union") { + return (t as dom.UnionType).members.every(isPrimitiveLike); + } + if (kind === "type-parameter") return true; + // Fallback: treat scalar intrinsic tokens as primitive + const intrinsicKinds = new Set(["string", "number", "boolean", "undefined"]); + return intrinsicKinds.has(kind || ""); +} + +// Property name collision resolution using predicate IRI mapping +const predicateIriByProp = new WeakMap(); +/** + * resolveCollisions + * ----------------- + * Purpose: ensure that properties derived from different predicate IRIs but + * sharing the same local ending (final segment after '/', '#' or ':') become + * uniquely named TypeScript properties. + * + * Strategy (simplified): + * 1. Group properties by their current (local) name. + * 2. For any group with more than one property, rename each to + * `${secondLast}_${local}` where `secondLast` is the segment immediately + * before the local segment in the predicate IRI. + * 3. If collisions still remain (i.e. two IRIs share both secondLast and local + * segments), fall back to using a sanitized form of the full IRI (without the + * protocol) as the property name. + */ +function resolveCollisions(props: dom.PropertyDeclaration[]): void { + const groups = new Map(); + props.forEach((p) => { + const base = p.name.replace(/\d+$/, ""); + if (!groups.has(base)) groups.set(base, []); + groups.get(base)!.push(p); + }); + groups.forEach((list) => { + if (list.length < 2) return; + // First pass rename using second last segment + list.forEach((prop) => { + const iri = predicateIriByProp.get(prop) || prop.name; + const segs = iri.split(/[#:\/]/).filter(Boolean); + if (!segs.length) return; + const local = segs.at(-1)!; + const secondLast = segs.length > 1 ? segs.at(-2)! : undefined; + if (secondLast) { + prop.name = `${secondLast}_${local}`; + } + }); + // Detect any remaining duplicates after first pass + const nameCounts = new Map(); + list.forEach((p) => + nameCounts.set(p.name, (nameCounts.get(p.name) || 0) + 1), + ); + list.forEach((p) => { + if (nameCounts.get(p.name)! > 1) { + p.name = predicateIriByProp.get(p) || p.name; + } + }); + }); +} + +// Merge duplicate properties without introducing LdSet. If a property appears multiple +// times (e.g., via EXTENDS or grouped expressions) we: +// - union the types (flattening existing unions) +// - if one side is Set and the other is plain U, produce Set +// - if both are Set, Set -> Set +// - preserve optional flag if any occurrence optional +function dedupeCompactProperties( + props: dom.PropertyDeclaration[], +): dom.PropertyDeclaration[] { + const byName: Record = {}; + const isSetRef = (t: dom.Type): t is dom.NamedTypeReference => + (t as any).kind === "name" && (t as any).name === "Set"; + const getSetInner = (t: dom.Type): dom.Type => + isSetRef(t) ? (t as any).typeArguments[0] : t; + const toSet = (inner: dom.Type): dom.Type => + ({ kind: "name", name: "Set", typeArguments: [inner] }) as any; + const makeUnion = (a: dom.Type, b: dom.Type): dom.Type => { + const collect = (t: dom.Type, acc: dom.Type[]) => { + if ((t as any).kind === "union") { + (t as any).members.forEach((m: dom.Type) => collect(m, acc)); + } else acc.push(t); + }; + const members: dom.Type[] = []; + collect(a, members); + collect(b, members); + // de-dup via string emission heuristic + const seen = new Set(); + const filtered: dom.Type[] = []; + members.forEach((m) => { + const key = + (m as any).name || + (m as any).value || + (m as any).kind + JSON.stringify(m); + if (!seen.has(key)) { + seen.add(key); + filtered.push(m); + } + }); + if (filtered.length === 1) return filtered[0]; + return dom.create.union(filtered); + }; + props.forEach((p) => { + const existing = byName[p.name]; + if (!existing) { + byName[p.name] = p; + return; + } + // If predicates differ, keep both (assign numeric suffix to new one) + const predExisting = predicateIriByProp.get(existing); + const predNew = predicateIriByProp.get(p); + if (predExisting && predNew && predExisting !== predNew) { + const base = p.name; + let counter = 2; + while (byName[`${base}${counter}`]) counter++; + const newName = `${base}${counter}`; + const clone = dom.create.property(newName, p.type, p.flags); + clone.jsDocComment = p.jsDocComment; + if (predNew) predicateIriByProp.set(clone, predNew); + byName[newName] = clone; + return; + } + const existingSet = isSetRef(existing.type); + const newSet = isSetRef(p.type); + let mergedType: dom.Type; + if (existingSet && newSet) { + mergedType = toSet( + makeUnion(getSetInner(existing.type), getSetInner(p.type)), + ); + } else if (existingSet && !newSet) { + mergedType = toSet(makeUnion(getSetInner(existing.type), p.type)); + } else if (!existingSet && newSet) { + mergedType = toSet(makeUnion(existing.type, getSetInner(p.type))); + } else { + mergedType = makeUnion(existing.type, p.type); + } + const optional = + existing.flags === dom.DeclarationFlags.Optional || + p.flags === dom.DeclarationFlags.Optional + ? dom.DeclarationFlags.Optional + : dom.DeclarationFlags.None; + const merged = dom.create.property(p.name, mergedType, optional); + merged.jsDocComment = + existing.jsDocComment && p.jsDocComment + ? `${existing.jsDocComment} | ${p.jsDocComment}` + : existing.jsDocComment || p.jsDocComment; + // Preserve predicate mapping + const pred = predicateIriByProp.get(existing) || predicateIriByProp.get(p); + if (pred) predicateIriByProp.set(merged, pred); + byName[p.name] = merged; + }); + return Object.values(byName); +} + +export const ShexJTypingTransformerCompact = ShexJTraverser.createTransformer< + { + Schema: { return: dom.TopLevelDeclaration[] }; + ShapeDecl: { return: dom.InterfaceDeclaration }; + Shape: { return: dom.InterfaceDeclaration }; + EachOf: { return: dom.ObjectType | dom.InterfaceDeclaration }; + TripleConstraint: { return: dom.PropertyDeclaration }; + NodeConstraint: { return: dom.Type }; + ShapeOr: { return: dom.UnionType }; + ShapeAnd: { return: dom.IntersectionType }; + ShapeNot: { return: never }; + ShapeExternal: { return: never }; + }, + CompactTransformerContext +>({ + // Transformer from Schema to interfaces + Schema: { + transformer: async (_schema, getTransformedChildren) => { + const transformedChildren = await getTransformedChildren(); + const interfaces: dom.TopLevelDeclaration[] = []; + transformedChildren.shapes?.forEach((shape) => { + if ( + typeof shape !== "string" && + (shape as dom.InterfaceDeclaration).kind === "interface" + ) { + interfaces.push(shape as dom.InterfaceDeclaration); + } + }); + return interfaces; + }, + }, + + // Transformer from ShapeDecl to interface + ShapeDecl: { + transformer: async (shapeDecl, getTransformedChildren) => { + const shapeName = nameFromObject(shapeDecl) || "Shape"; + const { shapeExpr } = await getTransformedChildren(); + if ((shapeExpr as dom.InterfaceDeclaration).kind === "interface") { + const shapeInterface = shapeExpr as ShapeInterfaceDeclaration; + shapeInterface.name = shapeName; + // Preserve shape id for downstream shapeTypes generation + // (mirrors standard transformer behavior) + shapeInterface.shapeId = shapeDecl.id; + if ( + !shapeInterface.members.find( + (m) => m.kind === "property" && m.name === "id", + ) + ) { + shapeInterface.members.unshift( + dom.create.property( + "id", + dom.create.namedTypeReference("IRI"), + dom.DeclarationFlags.Optional, + ), + ); + } + return shapeInterface; + } + throw new Error( + "Unsupported direct shape expression on ShapeDecl for compact format.", + ); + }, + }, + + // Transformer from Shape to interface + Shape: { + transformer: async (_shape, getTransformedChildren, setReturnPointer) => { + const newInterface: ShapeInterfaceDeclaration = dom.create.interface(""); + setReturnPointer(newInterface); + const transformedChildren = await getTransformedChildren(); + if ( + typeof transformedChildren.expression !== "string" && + transformedChildren.expression && + ((transformedChildren.expression as dom.ObjectType).kind === "object" || + (transformedChildren.expression as dom.InterfaceDeclaration).kind === + "interface") + ) { + newInterface.members.push( + ...(transformedChildren.expression as dom.ObjectType).members, + ); + } else if ( + (transformedChildren.expression as dom.PropertyDeclaration)?.kind === + "property" + ) { + newInterface.members.push( + transformedChildren.expression as dom.PropertyDeclaration, + ); + } + if (transformedChildren.extends) { + transformedChildren.extends.forEach((ext) => { + const extInt = ext as dom.InterfaceDeclaration; + if (extInt.kind === "interface") { + const merged = [ + ...extInt.members.filter( + (m) => !(m.kind === "property" && m.name === "id"), + ), + ...newInterface.members, + ].filter( + (m): m is dom.PropertyDeclaration => m.kind === "property", + ); + newInterface.members = dedupeCompactProperties(merged); + } + }); + } + // Final pass: ensure only a single id property + const idSeen = new Set(); + newInterface.members = newInterface.members.filter((m, idx) => { + if (m.kind !== "property" || m.name !== "id") return true; + if (idSeen.size === 0) { + idSeen.add(idx); + // normalize id type to IRI + m.type = dom.create.namedTypeReference("IRI"); + m.flags = dom.DeclarationFlags.Optional; + return true; + } + return false; + }); + return newInterface; + }, + }, + + // Transformer from EachOf to object type. EachOf contains the `expressions` array of properties (TripleConstraint) + EachOf: { + transformer: async (eachOf, getTransformedChildren, setReturnPointer) => { + const transformedChildren = await getTransformedChildren(); + const name = nameFromObject(eachOf); + + const objectType = name + ? dom.create.interface(name) + : dom.create.objectType([]); + setReturnPointer(objectType); + const inputProps: dom.PropertyDeclaration[] = []; + transformedChildren.expressions.forEach((expr) => { + if (!expr || typeof expr === "string") return; + const kind = (expr as any).kind; + if (kind === "property") { + inputProps.push(expr as dom.PropertyDeclaration); + } else if (kind === "object" || kind === "interface") { + (expr as dom.ObjectType | dom.InterfaceDeclaration).members.forEach( + (m) => { + if ((m as any).kind === "property") { + inputProps.push(m as dom.PropertyDeclaration); + } + }, + ); + } + }); + const deduped = dedupeCompactProperties(inputProps); + resolveCollisions(deduped); + objectType.members.push(...deduped); + return objectType; + }, + }, + + // Transformer from triple constraints to type properties. + TripleConstraint: { + transformer: async ( + tripleConstraint, + getTransformedChildren, + _setReturnPointer, + node, + context, + ) => { + const transformedChildren = await getTransformedChildren(); + const rdfTypes = getRdfTypesForTripleConstraint(node); + const baseName = context.getNameFromIri( + tripleConstraint.predicate, + rdfTypes[0], + ); + const max = tripleConstraint.max; + const isPlural = max === -1 || (max !== undefined && max !== 1); + const isOptional = tripleConstraint.min === 0; + + let valueType: dom.Type = dom.type.any; + if (transformedChildren.valueExpr) + valueType = transformedChildren.valueExpr as dom.Type; + + // Normalize NodeConstraint returned object forms for IRIs into IRI + // Heuristic: existing transformer (compact) returns string/number/boolean OR object/interface. + // We treat any simple string/number/boolean/name as primitive. + + // Determine category + const objLike = isObjectLike(valueType); + const isUnion = + (valueType as unknown as { kind?: string })?.kind === "union"; + const unionMembers: dom.Type[] = isUnion + ? (valueType as dom.UnionType).members + : []; + const unionAllObjLike = + isUnion && unionMembers.length > 0 && unionMembers.every(isObjectLike); + const primLike = isPrimitiveLike(valueType); + if ( + !primLike && + !objLike && + (valueType as dom.UnionType).kind === "union" + ) { + const u = valueType as dom.UnionType; + const hasObj = u.members.some(isObjectLike); + const hasPrim = u.members.some(isPrimitiveLike); + if (isPlural && hasObj && hasPrim) { + throw new Error( + `Mixed plural union (object + primitive) not supported for predicate ${tripleConstraint.predicate}`, + ); + } + } + + let finalType: dom.Type; + if (isPlural) { + if (objLike || unionAllObjLike) { + if ( + (valueType as dom.InterfaceDeclaration).kind === "interface" && + (valueType as dom.InterfaceDeclaration).name + ) { + const ifaceName = (valueType as dom.InterfaceDeclaration).name; + // Dictionary of full object instances keyed by IRI + finalType = { + kind: "name", + name: "Record", + typeArguments: [ + dom.create.namedTypeReference("IRI"), + dom.create.namedTypeReference(ifaceName), + ], + } as dom.Type; + } else { + // Anonymous object or union of anonymous/interface objects + let valueForRecord: dom.Type = valueType; + if (unionAllObjLike) { + // Ensure each union member has id?: IRI if anonymous object + (valueType as dom.UnionType).members = ( + valueType as dom.UnionType + ).members.map((m) => { + if ((m as dom.InterfaceDeclaration).kind === "interface") + return m; + if ((m as dom.ObjectType).kind === "object") { + const anonMembers = ( + m as unknown as { members?: dom.PropertyDeclaration[] } + ).members; + const hasId = (anonMembers || []).some( + (mm) => mm.name === "id", + ); + if (!hasId && anonMembers) { + anonMembers.unshift( + dom.create.property( + "id", + dom.create.namedTypeReference("IRI"), + dom.DeclarationFlags.Optional, + ), + ); + } + } + return m; + }); + valueForRecord = valueType; // union retained + } else { + const anon = valueType as dom.ObjectType; + const anonMembers = ( + anon as unknown as { members?: dom.PropertyDeclaration[] } + ).members; + const hasId = (anonMembers || []).some((m) => m.name === "id"); + if (!hasId && anonMembers) { + anonMembers.unshift( + dom.create.property( + "id", + dom.create.namedTypeReference("IRI"), + dom.DeclarationFlags.Optional, + ), + ); + } + valueForRecord = anon as dom.Type; + } + finalType = { + kind: "name", + name: "Record", + typeArguments: [ + dom.create.namedTypeReference("IRI"), + valueForRecord, + ], + } as dom.Type; + } + } else { + finalType = { + kind: "name", + name: "Set", + typeArguments: [valueType], + } as dom.Type; + } + } else { + // Singular: always the interface/object type itself (never Id union) + if ( + (valueType as dom.InterfaceDeclaration).kind === "interface" && + (valueType as dom.InterfaceDeclaration).name + ) { + finalType = dom.create.namedTypeReference( + (valueType as dom.InterfaceDeclaration).name, + ); + } else { + finalType = valueType; + } + } + + const prop = dom.create.property( + baseName, + finalType, + isOptional ? dom.DeclarationFlags.Optional : dom.DeclarationFlags.None, + ); + predicateIriByProp.set(prop, tripleConstraint.predicate); + prop.jsDocComment = + commentFromAnnotations(tripleConstraint.annotations) || ""; + // Always append original predicate IRI reference (compact format only) + // If an existing comment is present, add a blank line before the Original IRI line. + if (prop.jsDocComment) { + prop.jsDocComment = `${prop.jsDocComment}\n\nOriginal IRI: ${tripleConstraint.predicate}`; + } else { + prop.jsDocComment = `Original IRI: ${tripleConstraint.predicate}`; + } + return prop; + }, + }, + + // Transformer from node constraint to type + NodeConstraint: { + transformer: async (nodeConstraint) => { + if (nodeConstraint.datatype) { + switch (nodeConstraint.datatype) { + case "http://www.w3.org/2001/XMLSchema#boolean": + return dom.type.boolean; + case "http://www.w3.org/2001/XMLSchema#byte": + case "http://www.w3.org/2001/XMLSchema#decimal": + case "http://www.w3.org/2001/XMLSchema#double": + case "http://www.w3.org/2001/XMLSchema#float": + case "http://www.w3.org/2001/XMLSchema#int": + case "http://www.w3.org/2001/XMLSchema#integer": + case "http://www.w3.org/2001/XMLSchema#long": + case "http://www.w3.org/2001/XMLSchema#negativeInteger": + case "http://www.w3.org/2001/XMLSchema#nonNegativeInteger": + case "http://www.w3.org/2001/XMLSchema#nonPositiveInteger": + case "http://www.w3.org/2001/XMLSchema#positiveInteger": + case "http://www.w3.org/2001/XMLSchema#short": + case "http://www.w3.org/2001/XMLSchema#unsignedLong": + case "http://www.w3.org/2001/XMLSchema#unsignedInt": + case "http://www.w3.org/2001/XMLSchema#unsignedShort": + case "http://www.w3.org/2001/XMLSchema#unsignedByte": + return dom.type.number; + default: + return dom.type.string; // treat most as string + } + } + if (nodeConstraint.nodeKind) { + switch (nodeConstraint.nodeKind) { + case "iri": + return dom.create.namedTypeReference("IRI"); + case "bnode": + return dom.type.string; // opaque id as string + case "nonliteral": + return dom.create.namedTypeReference("IRI"); + case "literal": + default: + return dom.type.string; + } + } + if (nodeConstraint.values) { + const u = dom.create.union([]); + nodeConstraint.values.forEach((v) => { + if (typeof v === "string") u.members.push(dom.type.stringLiteral(v)); + }); + if (!u.members.length) return dom.type.string; + if (u.members.length === 1) return u.members[0]; + return u; + } + return dom.type.any; + }, + }, + + // Transformer from ShapeOr to union type + ShapeOr: { + transformer: async (_shapeOr, getTransformedChildren) => { + const tc = await getTransformedChildren(); + const valid: dom.Type[] = []; + tc.shapeExprs.forEach((t) => { + if (typeof t === "object") valid.push(t); + }); + return dom.create.union(valid); + }, + }, + + // Transformer from ShapeAnd to intersection type + ShapeAnd: { + transformer: async (_shapeAnd, getTransformedChildren) => { + const tc = await getTransformedChildren(); + const valid: dom.Type[] = []; + tc.shapeExprs.forEach((t) => { + if (typeof t === "object") valid.push(t); + }); + return dom.create.intersection(valid); + }, + }, + + // Transformer from ShapeNot to type - not supported. + ShapeNot: { + transformer: async () => { + throw new Error("ShapeNot not supported (compact)"); + }, + }, + + // Transformer from ShapeExternal to type - not supported. + ShapeExternal: { + transformer: async () => { + throw new Error("ShapeExternal not supported (compact)"); + }, + }, +}); diff --git a/packages/schema-converter-shex/src/typing/shexjToTyping.ts b/packages/schema-converter-shex/src/typing/shexjToTyping.ts index 94c8031..4e300d5 100644 --- a/packages/schema-converter-shex/src/typing/shexjToTyping.ts +++ b/packages/schema-converter-shex/src/typing/shexjToTyping.ts @@ -1,59 +1,18 @@ import type { ContextDefinition } from "jsonld"; import type { Schema } from "shexj"; -import { JsonLdContextBuilder } from "../context/JsonLdContextBuilder.js"; -import { ShexJNameVisitor } from "../context/ShexJContextVisitor.js"; -import { jsonld2graphobject } from "jsonld2graphobject"; -import { ShexJTypingTransformer } from "./ShexJTypingTransformer.js"; -import * as dom from "dts-dom"; +import type { TypeingReturn } from "./shexjToTypingLdo.js"; +import { shexjToTypingLdo } from "./shexjToTypingLdo.js"; +import { shexjToTypingCompact } from "./shexjToTypingCompact.js"; -export interface TypeingReturn { - typingsString: string; - typings: { - typingString: string; - dts: dom.TopLevelDeclaration; - }[]; +export interface TypingsOptions { + format?: "ldo" | "compact"; } export async function shexjToTyping( shexj: Schema, -): Promise<[TypeingReturn, ContextDefinition]> { - const processedShexj: Schema = (await jsonld2graphobject( - { - ...shexj, - "@id": "SCHEMA", - "@context": "http://www.w3.org/ns/shex.jsonld", - }, - "SCHEMA", - )) as unknown as Schema; - const jsonLdContextBuilder = new JsonLdContextBuilder(); - await ShexJNameVisitor.visit(processedShexj, "Schema", jsonLdContextBuilder); - - const declarations = await ShexJTypingTransformer.transform( - processedShexj, - "Schema", - { - getNameFromIri: - jsonLdContextBuilder.getNameFromIri.bind(jsonLdContextBuilder), - }, - ); - const typings = declarations.map((declaration) => { - return { - typingString: dom - .emit(declaration, { - rootFlags: dom.ContextFlags.InAmbientNamespace, - }) - .replace(/\r\n/g, "\n"), - dts: declaration, - }; - }); - const typingsString = - `import { LdSet, LdoJsonldContext } from "@ldo/ldo"\n\n` + - typings.map((typing) => `export ${typing.typingString}`).join(""); - - const typeingReturn: TypeingReturn = { - typingsString, - typings, - }; - - return [typeingReturn, jsonLdContextBuilder.generateJsonldContext()]; + options: TypingsOptions = {}, +): Promise<[TypeingReturn, ContextDefinition | undefined]> { + const format = options.format || "ldo"; + if (format === "compact") return shexjToTypingCompact(shexj); + return shexjToTypingLdo(shexj); } diff --git a/packages/schema-converter-shex/src/typing/shexjToTypingCompact.ts b/packages/schema-converter-shex/src/typing/shexjToTypingCompact.ts new file mode 100644 index 0000000..bd0d39b --- /dev/null +++ b/packages/schema-converter-shex/src/typing/shexjToTypingCompact.ts @@ -0,0 +1,52 @@ +import type { Schema } from "shexj"; +import { jsonld2graphobject } from "jsonld2graphobject"; +import { ShexJNameVisitor } from "../context/ShexJContextVisitor.js"; +import { JsonLdContextBuilder } from "../context/JsonLdContextBuilder.js"; +import { + ShexJTypingTransformerCompact, + additionalCompactEnumAliases, +} from "./ShexJTypingTransformerCompact.js"; +import * as dom from "dts-dom"; +import type { TypeingReturn } from "./shexjToTypingLdo.js"; + +export async function shexjToTypingCompact( + shexj: Schema, +): Promise<[TypeingReturn, undefined]> { + // 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; + const nameBuilder = new JsonLdContextBuilder(); + await ShexJNameVisitor.visit(processedShexj, "Schema", nameBuilder); + + additionalCompactEnumAliases.clear(); + const declarations = await ShexJTypingTransformerCompact.transform( + processedShexj, + "Schema", + { + getNameFromIri: nameBuilder.getNameFromIri.bind(nameBuilder), + }, + ); + + // Append only enum aliases (no interface Id aliases in compact format now) + additionalCompactEnumAliases.forEach((alias) => { + const exists = declarations.some((d) => (d as any).name === alias); + if (!exists) declarations.push(dom.create.alias(alias, dom.type.string)); + }); + + 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 }, undefined]; +} diff --git a/packages/schema-converter-shex/src/typing/shexjToTypingLdo.ts b/packages/schema-converter-shex/src/typing/shexjToTypingLdo.ts new file mode 100644 index 0000000..0a6824d --- /dev/null +++ b/packages/schema-converter-shex/src/typing/shexjToTypingLdo.ts @@ -0,0 +1,58 @@ +import type { ContextDefinition } from "jsonld"; +import type { Schema } from "shexj"; +import { JsonLdContextBuilder } from "../context/JsonLdContextBuilder.js"; +import { ShexJNameVisitor } from "../context/ShexJContextVisitor.js"; +import { jsonld2graphobject } from "jsonld2graphobject"; +import { ShexJTypingTransformer } from "./ShexJTypingTransformer.js"; +import * as dom from "dts-dom"; + +export interface TypeingReturn { + typingsString: string; + typings: { + typingString: string; + dts: dom.TopLevelDeclaration; + }[]; +} + +export async function shexjToTypingLdo( + shexj: Schema, +): Promise<[TypeingReturn, ContextDefinition]> { + const processedShexj: Schema = (await jsonld2graphobject( + { + ...shexj, + "@id": "SCHEMA", + "@context": "http://www.w3.org/ns/shex.jsonld", + }, + "SCHEMA", + )) as unknown as Schema; + const jsonLdContextBuilder = new JsonLdContextBuilder(); + await ShexJNameVisitor.visit(processedShexj, "Schema", jsonLdContextBuilder); + + const declarations = await ShexJTypingTransformer.transform( + processedShexj, + "Schema", + { + getNameFromIri: + jsonLdContextBuilder.getNameFromIri.bind(jsonLdContextBuilder), + }, + ); + + const typings = declarations.map((declaration) => { + return { + typingString: dom + .emit(declaration, { + rootFlags: dom.ContextFlags.InAmbientNamespace, + }) + .replace(/\r\n/g, "\n"), + dts: declaration, + }; + }); + + const header = `import { LdSet, LdoJsonldContext } from "@ldo/ldo"\n\n`; + const typingsString = + header + typings.map((t) => `export ${t.typingString}`).join(""); + + const typeingReturn: TypeingReturn = { typingsString, typings }; + + return [typeingReturn, jsonLdContextBuilder.generateJsonldContext()]; +} diff --git a/packages/schema-converter-shex/test/context.test.ts b/packages/schema-converter-shex/test/context.test.ts index a9f0db4..aa59230 100644 --- a/packages/schema-converter-shex/test/context.test.ts +++ b/packages/schema-converter-shex/test/context.test.ts @@ -7,13 +7,13 @@ console.warn = () => {}; describe("context", () => { testData.forEach(({ name, shexc, successfulContext }) => { + // Skip entries with empty context placeholder (compact-only tests) + if (!successfulContext || !Object.keys(successfulContext).length) return; it(`Creates a context for ${name}`, async () => { const schema: Schema = parser .construct("https://ldo.js.org/") .parse(shexc); - // console.log("SCHEMA:", JSON.stringify(schema, null, 2)); const context = await shexjToContext(schema); - // console.log("CONTEXT:", JSON.stringify(context, null, 2)); expect(context).toEqual(successfulContext); }); }); diff --git a/packages/schema-converter-shex/test/testData/circular.ts b/packages/schema-converter-shex/test/testData/circular.ts index 886bea5..52359ce 100644 --- a/packages/schema-converter-shex/test/testData/circular.ts +++ b/packages/schema-converter-shex/test/testData/circular.ts @@ -72,4 +72,5 @@ export const circular: TestData = { }, successfulTypings: 'import { LdSet, LdoJsonldContext } from "@ldo/ldo"\n\nexport interface Parent {\n "@id"?: string;\n "@context"?: LdoJsonldContext;\n type?: LdSet<{\n "@id": "Parent";\n }>;\n hasChild: Child;\n}\n\nexport interface Child {\n "@id"?: string;\n "@context"?: LdoJsonldContext;\n type?: LdSet<{\n "@id": "Child";\n }>;\n hasParent: Parent;\n}\n\n', + successfulCompactTypings: `export type IRI = string;\n\nexport interface Parent {\n id?: IRI;\n /**\n * Original IRI: http://www.w3.org/1999/02/22-rdf-syntax-ns#type\n */\n type?: "http://example.com/Parent";\n /**\n * Original IRI: http://example.com/hasChild\n */\n hasChild: Child;\n}\n\nexport interface Child {\n id?: IRI;\n /**\n * Original IRI: http://www.w3.org/1999/02/22-rdf-syntax-ns#type\n */\n type?: "http://example.com/Child";\n /**\n * Original IRI: http://example.com/hasParent\n */\n hasParent: Parent;\n}\n\n`, }; diff --git a/packages/schema-converter-shex/test/testData/extendsSimple.ts b/packages/schema-converter-shex/test/testData/extendsSimple.ts index b391d00..eedbceb 100644 --- a/packages/schema-converter-shex/test/testData/extendsSimple.ts +++ b/packages/schema-converter-shex/test/testData/extendsSimple.ts @@ -80,4 +80,5 @@ export const extendsSimple: TestData = { }, successfulTypings: 'import { LdSet, LdoJsonldContext } from "@ldo/ldo"\n\nexport interface Entity {\n "@id"?: string;\n "@context"?: LdoJsonldContext;\n type: LdSet<{\n "@id": "Entity";\n }>;\n entityId: any;\n}\n\nexport interface Person {\n "@id"?: LdSet;\n "@context"?: LdSet;\n type: LdSet<{\n "@id": "Entity";\n } | {\n "@id": "Person";\n }>;\n entityId: any;\n name: any;\n}\n\nexport interface Employee {\n "@id"?: LdSet;\n "@context"?: LdSet;\n type: LdSet<{\n "@id": "Entity";\n } | {\n "@id": "Person";\n } | {\n "@id": "Employee";\n }>;\n entityId: any;\n name: any;\n employeeNumber: any;\n}\n\n', + successfulCompactTypings: `export type IRI = string;\n\nexport interface Entity {\n id?: IRI;\n /**\n * Original IRI: http://www.w3.org/1999/02/22-rdf-syntax-ns#type\n */\n type: "https://example.com/Entity";\n /**\n * Original IRI: https://example.com/entityId\n */\n entityId: any;\n}\n\nexport interface Person {\n /**\n * Original IRI: http://www.w3.org/1999/02/22-rdf-syntax-ns#type | Original IRI: http://www.w3.org/1999/02/22-rdf-syntax-ns#type\n */\n type: "https://example.com/Entity" | "https://example.com/Person";\n /**\n * Original IRI: https://example.com/entityId\n */\n entityId: any;\n id?: IRI;\n /**\n * Original IRI: http://xmlns.com/foaf/0.1/name\n */\n name: any;\n}\n\nexport interface Employee {\n /**\n * Original IRI: http://www.w3.org/1999/02/22-rdf-syntax-ns#type | Original IRI: http://www.w3.org/1999/02/22-rdf-syntax-ns#type | Original IRI: http://www.w3.org/1999/02/22-rdf-syntax-ns#type\n */\n type: "https://example.com/Entity" | "https://example.com/Person" | "https://example.com/Employee";\n /**\n * Original IRI: https://example.com/entityId\n */\n entityId: any;\n /**\n * Original IRI: http://xmlns.com/foaf/0.1/name\n */\n name: any;\n id?: IRI;\n /**\n * Original IRI: https://example.com/employeeNumber\n */\n employeeNumber: any;\n}\n\n`, }; diff --git a/packages/schema-converter-shex/test/testData/mixedPluralUnionError.ts b/packages/schema-converter-shex/test/testData/mixedPluralUnionError.ts new file mode 100644 index 0000000..8d476e1 --- /dev/null +++ b/packages/schema-converter-shex/test/testData/mixedPluralUnionError.ts @@ -0,0 +1,17 @@ +import type { TestData } from "./testData.js"; + +export const mixedPluralUnionError: TestData = { + name: "mixed plural union error", + shexc: ` + PREFIX ex: + PREFIX xsd: + ex:FooShape { ex:mixed ( @ex:BarShape OR @ex:BazShape )* } + ex:BarShape { ex:label . } + ex:BazShape { ex:other . } + `, + sampleTurtle: ``, + baseNode: "http://ex/foo2", + successfulContext: {}, + successfulTypings: "", + successfulCompactTypings: `export type IRI = string;\n\nexport interface Foo {\n id?: IRI;\n /**\n * Original IRI: http://ex/mixed\n */\n mixed?: Record;\n}\n\nexport interface Bar {\n id?: IRI;\n /**\n * Original IRI: http://ex/label\n */\n label: any;\n}\n\nexport interface Baz {\n id?: IRI;\n /**\n * Original IRI: http://ex/other\n */\n other: any;\n}\n\n`, +}; diff --git a/packages/schema-converter-shex/test/testData/pluralAnonymous.ts b/packages/schema-converter-shex/test/testData/pluralAnonymous.ts new file mode 100644 index 0000000..83947f6 --- /dev/null +++ b/packages/schema-converter-shex/test/testData/pluralAnonymous.ts @@ -0,0 +1,15 @@ +import type { TestData } from "./testData.js"; + +export const pluralAnonymous: TestData = { + name: "plural anonymous", + shexc: ` + PREFIX ex: + ex:ConfigHolderShape { ex:configs @ex:ConfigShape* } + ex:ConfigShape { ex:key . ; ex:val . } + `, + sampleTurtle: ``, + baseNode: "http://ex/cfg1", + successfulContext: {}, + successfulTypings: "", + successfulCompactTypings: `export type IRI = string;\n\nexport interface ConfigHolder {\n id?: IRI;\n /**\n * Original IRI: http://ex/configs\n */\n configs?: Record;\n}\n\nexport interface Config {\n id?: IRI;\n /**\n * Original IRI: http://ex/key\n */\n key: any;\n /**\n * Original IRI: http://ex/val\n */\n val: any;\n}\n\n`, +}; diff --git a/packages/schema-converter-shex/test/testData/pluralObjects.ts b/packages/schema-converter-shex/test/testData/pluralObjects.ts new file mode 100644 index 0000000..72de707 --- /dev/null +++ b/packages/schema-converter-shex/test/testData/pluralObjects.ts @@ -0,0 +1,15 @@ +import type { TestData } from "./testData.js"; + +export const pluralObjects: TestData = { + name: "plural objects", + shexc: ` + PREFIX ex: + ex:FooShape { ex:bars @ex:BarShape* } + ex:BarShape { ex:name . } + `, + sampleTurtle: ``, + baseNode: "http://ex/foo1", + successfulContext: {}, + successfulTypings: "", // not used in this test context + successfulCompactTypings: `export type IRI = string;\n\nexport interface Foo {\n id?: IRI;\n /**\n * Original IRI: http://ex/bars\n */\n bars?: Record;\n}\n\nexport interface Bar {\n id?: IRI;\n /**\n * Original IRI: http://ex/name\n */\n name: any;\n}\n\n`, +}; diff --git a/packages/schema-converter-shex/test/testData/pluralUnionObjects.ts b/packages/schema-converter-shex/test/testData/pluralUnionObjects.ts new file mode 100644 index 0000000..ef27bce --- /dev/null +++ b/packages/schema-converter-shex/test/testData/pluralUnionObjects.ts @@ -0,0 +1,16 @@ +import type { TestData } from "./testData.js"; + +export const pluralUnionObjects: TestData = { + name: "plural union objects", + shexc: ` + PREFIX ex: + ex:A { ex:items ( @ex:Foo OR @ex:Bar )* } + ex:Foo { ex:f . } + ex:Bar { ex:b . } + `, + sampleTurtle: ``, + baseNode: "http://ex/a1", + successfulContext: {} as any, + successfulTypings: "", + successfulCompactTypings: `export type IRI = string;\n\nexport interface A {\n id?: IRI;\n /**\n * Original IRI: http://ex/items\n */\n items?: Record;\n}\n\nexport interface Foo {\n id?: IRI;\n /**\n * Original IRI: http://ex/f\n */\n f: any;\n}\n\nexport interface Bar {\n id?: IRI;\n /**\n * Original IRI: http://ex/b\n */\n b: any;\n}\n\n`, +}; diff --git a/packages/schema-converter-shex/test/testData/propertyCollision.ts b/packages/schema-converter-shex/test/testData/propertyCollision.ts new file mode 100644 index 0000000..6a9ef4f --- /dev/null +++ b/packages/schema-converter-shex/test/testData/propertyCollision.ts @@ -0,0 +1,18 @@ +import type { TestData } from "./testData.js"; + +export const propertyCollision: TestData = { + name: "property collision", + shexc: ` + PREFIX ex: + PREFIX ex2: + PREFIX foaf: + PREFIX v1: + PREFIX ver: + ex:C { ex:label . ; ex2:label . ; foaf:label . ; v1:label . ; ver:label . } + `, + sampleTurtle: ``, + baseNode: "http://ex/c1", + successfulContext: {} as any, + successfulTypings: "", + successfulCompactTypings: `export type IRI = string;\n\nexport interface C {\n id?: IRI;\n /**\n * Original IRI: http://ex/label\n */\n ex_label: any;\n /**\n * Original IRI: http://ex2/label\n */\n ex2_label: any;\n /**\n * Original IRI: http://xmlns.com/foaf/0.1/label\n */\n "0.1_label": any;\n /**\n * Original IRI: http://example.com/v1#label\n */\n v1_label: any;\n /**\n * Original IRI: http://api.example.com/v2.1:label\n */\n "v2.1:label": any;\n}\n\n`, +}; diff --git a/packages/schema-converter-shex/test/testData/reusedPredicates.ts b/packages/schema-converter-shex/test/testData/reusedPredicates.ts index 5d35efc..194fa08 100644 --- a/packages/schema-converter-shex/test/testData/reusedPredicates.ts +++ b/packages/schema-converter-shex/test/testData/reusedPredicates.ts @@ -114,4 +114,5 @@ export const reusedPredicates: TestData = { }, successfulTypings: 'import { LdSet, LdoJsonldContext } from "@ldo/ldo"\n\nexport interface Document {\n "@id"?: string;\n "@context"?: LdoJsonldContext;\n type: LdSet<{\n "@id": "Document";\n }>;\n vocabulary?: LdSet;\n law: Law;\n}\n\nexport interface Law {\n "@id"?: string;\n "@context"?: LdoJsonldContext;\n type: LdSet<{\n "@id": "Law";\n }>;\n name?: LdSet;\n path: {\n "@id": string;\n };\n}\n\nexport interface Vocabulary {\n "@id"?: string;\n "@context"?: LdoJsonldContext;\n type: LdSet<{\n "@id": "Vocabulary";\n }>;\n name: string;\n path?: LdSet<{\n "@id": string;\n }>;\n}\n\n', + successfulCompactTypings: `export type IRI = string;\n\nexport interface Document {\n id?: IRI;\n /**\n * Original IRI: http://www.w3.org/1999/02/22-rdf-syntax-ns#type\n */\n type: "https://www.forsakringskassan.se/vocabs/fk-sem-poc.ttl#Document";\n /**\n * Original IRI: https://www.forsakringskassan.se/vocabs/fk-sem-poc.ttl#vocabulary\n */\n vocabulary?: Record;\n /**\n * Original IRI: https://www.forsakringskassan.se/vocabs/fk-sem-poc.ttl#law\n */\n law: Law;\n}\n\nexport interface Law {\n id?: IRI;\n /**\n * Original IRI: http://www.w3.org/1999/02/22-rdf-syntax-ns#type\n */\n type: "https://www.forsakringskassan.se/vocabs/fk-sem-poc.ttl#Law";\n /**\n * Original IRI: https://www.forsakringskassan.se/vocabs/fk-sem-poc.ttl#name\n */\n name?: Set;\n /**\n * Original IRI: https://www.forsakringskassan.se/vocabs/fk-sem-poc.ttl#path\n */\n path: IRI;\n}\n\nexport interface Vocabulary {\n id?: IRI;\n /**\n * Original IRI: http://www.w3.org/1999/02/22-rdf-syntax-ns#type\n */\n type: "https://www.forsakringskassan.se/vocabs/fk-sem-poc.ttl#Vocabulary";\n /**\n * Original IRI: https://www.forsakringskassan.se/vocabs/fk-sem-poc.ttl#name\n */\n name: string;\n /**\n * Original IRI: https://www.forsakringskassan.se/vocabs/fk-sem-poc.ttl#path\n */\n path?: Set;\n}\n\n`, }; diff --git a/packages/schema-converter-shex/test/testData/simple.ts b/packages/schema-converter-shex/test/testData/simple.ts index 9841dfc..f71bd0b 100644 --- a/packages/schema-converter-shex/test/testData/simple.ts +++ b/packages/schema-converter-shex/test/testData/simple.ts @@ -57,4 +57,5 @@ export const simple: TestData = { }, successfulTypings: 'import { LdSet, LdoJsonldContext } from "@ldo/ldo"\n\nexport interface Employee {\n "@id"?: string;\n "@context"?: LdoJsonldContext;\n givenName: LdSet;\n familyName: string;\n phone?: LdSet<{\n "@id": string;\n }>;\n mbox: {\n "@id": string;\n };\n someDouble: number;\n}\n\n', + successfulCompactTypings: `export type IRI = string;\n\nexport interface Employee {\n id?: IRI;\n /**\n * Original IRI: http://xmlns.com/foaf/0.1/givenName\n */\n givenName: Set;\n /**\n * Original IRI: http://xmlns.com/foaf/0.1/familyName\n */\n familyName: string;\n /**\n * Original IRI: http://xmlns.com/foaf/0.1/phone\n */\n phone?: Set;\n /**\n * Original IRI: http://xmlns.com/foaf/0.1/mbox\n */\n mbox: IRI;\n /**\n * Original IRI: https://ns.example/someDouble\n */\n someDouble: number;\n}\n\n`, }; diff --git a/packages/schema-converter-shex/test/testData/testData.ts b/packages/schema-converter-shex/test/testData/testData.ts index cf253f7..195e05c 100644 --- a/packages/schema-converter-shex/test/testData/testData.ts +++ b/packages/schema-converter-shex/test/testData/testData.ts @@ -11,6 +11,11 @@ import { orSimple } from "./orSimple.js"; import { andSimple } from "./andSimple.js"; import { eachOfAndSimple } from "./eachOfAndSimple.js"; import { multipleSharedPredicates } from "./multipleSharedPredicates.js"; +import { pluralObjects } from "./pluralObjects.js"; +import { pluralAnonymous } from "./pluralAnonymous.js"; +import { mixedPluralUnionError } from "./mixedPluralUnionError.js"; +import { pluralUnionObjects } from "./pluralUnionObjects.js"; +import { propertyCollision } from "./propertyCollision.js"; export interface TestData { name: string; @@ -19,6 +24,7 @@ export interface TestData { baseNode: string; successfulContext: LdoJsonldContext; successfulTypings: string; + successfulCompactTypings?: string; } export const testData: TestData[] = [ @@ -34,4 +40,9 @@ export const testData: TestData[] = [ andSimple, eachOfAndSimple, multipleSharedPredicates, + pluralObjects, + pluralAnonymous, + mixedPluralUnionError, + pluralUnionObjects, + propertyCollision, ]; diff --git a/packages/schema-converter-shex/test/typing.compact.test.ts b/packages/schema-converter-shex/test/typing.compact.test.ts new file mode 100644 index 0000000..2ef7a42 --- /dev/null +++ b/packages/schema-converter-shex/test/typing.compact.test.ts @@ -0,0 +1,24 @@ +import parser from "@shexjs/parser"; +import { testData } from "./testData/testData.js"; +import { shexjToTyping } from "../src/typing/shexjToTyping.js"; +import type { Schema } from "shexj"; + +console.warn = () => {}; + +describe("typing-compact", () => { + testData.forEach((td) => { + const { name, shexc, successfulCompactTypings } = td; + if (!successfulCompactTypings) return; // skip if neither + it(`Creates compact typings for ${name}`, async () => { + const schema: Schema = parser + .construct("https://ldo.js.org/") + .parse(shexc); + const [compact] = await shexjToTyping(schema, { format: "compact" }); + const normalize = (s: string) => + s.replace(/\r\n/g, "\n").replace(/\n+$/s, "\n"); + expect(normalize(compact.typingsString)).toBe( + normalize(successfulCompactTypings) + ); + }); + }); +}); diff --git a/packages/schema-converter-shex/test/typing.test.ts b/packages/schema-converter-shex/test/typing.test.ts index c806d50..5726abe 100644 --- a/packages/schema-converter-shex/test/typing.test.ts +++ b/packages/schema-converter-shex/test/typing.test.ts @@ -6,16 +6,16 @@ import type { Schema } from "shexj"; console.warn = () => {}; describe("typing", () => { - testData.forEach(({ name, shexc, successfulTypings }) => { - it(`Creates a typings for ${name}`, async () => { + testData.forEach((td) => { + const { name, shexc, successfulTypings } = td; + if (!successfulTypings) return; // skip entries only meant for compact tests + it(`Creates typings for ${name}`, async () => { const schema: Schema = parser .construct("https://ldo.js.org/") .parse(shexc); - // console.log("SCHEMA:", JSON.stringify(schema, null, 2)); const [typings] = await shexjToTyping(schema); - // console.log(typings.typingsString); - // console.log(JSON.stringify(typings.typingsString)); expect(typings.typingsString).toBe(successfulTypings); + // Compact format tested in typing.compact.test.ts }); }); }); diff --git a/packages/subscribable-dataset/src/TransactionDatasetFactory.ts b/packages/subscribable-dataset/src/TransactionDatasetFactory.ts index e4f033e..f3fb626 100644 --- a/packages/subscribable-dataset/src/TransactionDatasetFactory.ts +++ b/packages/subscribable-dataset/src/TransactionDatasetFactory.ts @@ -1,5 +1,8 @@ import type { BaseQuad, DatasetFactory } from "@rdfjs/types"; -import type { ISubscribableDataset, ITransactionDatasetFactory } from "./types.js"; +import type { + ISubscribableDataset, + ITransactionDatasetFactory, +} from "./types.js"; import { TransactionDataset } from "./TransactionDataset.js"; export class TransactionDatasetFactory