From 644e0ceae96fb4233f3f2128a93c0ce5d7ed93fc Mon Sep 17 00:00:00 2001 From: Felix Kaspar Date: Fri, 23 Feb 2024 23:48:03 +0100 Subject: [PATCH] Dynamic access to Operators in both front and backend --- client-tauri/package.json | 1 + .../src/components/fields/BuildFields.tsx | 15 + .../src/components/fields/StringField.tsx | 12 + client-tauri/src/pages/Dynamic.tsx | 26 +- client-tauri/vite.config.ts | 3 + package-lock.json | 456 +++++++++++++++++- server-node/package.json | 3 + server-node/rollup.config.js | 9 +- server-node/src/index.ts | 3 + .../api/dynamic-operations-controller.ts | 11 +- server-node/tsconfig.json | 2 +- .../declarations/ImportMeta.d.ts | 3 + .../src/functions/arrangePages.ts | 9 +- .../src/workflow/getOperatorByName.ts | 34 -- .../src/workflow/listOperatorsInDir.ts | 38 ++ .../src/workflow/operatorAccessor.ts | 24 + .../src/workflow/traverseOperations.ts | 6 +- .../src/workflow/validateOperations.ts | 12 +- 18 files changed, 593 insertions(+), 74 deletions(-) create mode 100644 client-tauri/src/components/fields/BuildFields.tsx create mode 100644 client-tauri/src/components/fields/StringField.tsx create mode 100644 shared-operations/declarations/ImportMeta.d.ts delete mode 100644 shared-operations/src/workflow/getOperatorByName.ts create mode 100644 shared-operations/src/workflow/listOperatorsInDir.ts create mode 100644 shared-operations/src/workflow/operatorAccessor.ts diff --git a/client-tauri/package.json b/client-tauri/package.json index 52322bec2..869ab4021 100644 --- a/client-tauri/package.json +++ b/client-tauri/package.json @@ -36,6 +36,7 @@ "@vitejs/plugin-react": "^4.0.3", "typescript": "^5.0.2", "vite": "^4.4.4", + "vite-plugin-compile-time": "^0.2.1", "vite-plugin-dynamic-import": "^1.5.0" } } diff --git a/client-tauri/src/components/fields/BuildFields.tsx b/client-tauri/src/components/fields/BuildFields.tsx new file mode 100644 index 000000000..6e259dc8c --- /dev/null +++ b/client-tauri/src/components/fields/BuildFields.tsx @@ -0,0 +1,15 @@ +import Joi from "@stirling-tools/joi"; + + +interface BuildFieldsProps { + /** The text to display inside the button */ + schemaDescription: Joi.Description | undefined; +} + + +export function BuildFields({ schemaDescription }: BuildFieldsProps) { + console.log("Render Build Fields", schemaDescription); + return ( +
Description: {(schemaDescription?.flags as any)?.description}
+ ); +} \ No newline at end of file diff --git a/client-tauri/src/components/fields/StringField.tsx b/client-tauri/src/components/fields/StringField.tsx new file mode 100644 index 000000000..532092dbc --- /dev/null +++ b/client-tauri/src/components/fields/StringField.tsx @@ -0,0 +1,12 @@ +interface StringFieldProps { + /** The text to display inside the button */ + validValues: string[]; + exampleValues: string; +} + + +export function StringField({ validValues, exampleValues }: StringFieldProps) { + return ( + + ); +} \ No newline at end of file diff --git a/client-tauri/src/pages/Dynamic.tsx b/client-tauri/src/pages/Dynamic.tsx index cff9f9678..bca30de1c 100644 --- a/client-tauri/src/pages/Dynamic.tsx +++ b/client-tauri/src/pages/Dynamic.tsx @@ -1,15 +1,23 @@ import { Link } from "react-router-dom"; -import { BaseSyntheticEvent } from "react"; +import { BaseSyntheticEvent, createContext, useState } from "react"; import { Operator } from "@stirling-pdf/shared-operations/src/functions"; import i18next from "i18next"; +import Joi from "@stirling-tools/joi"; +import { BuildFields } from "../components/fields/BuildFields"; function Dynamic() { + const [schemaDescription, setSchemaDescription] = useState(); + + const operators = ["impose"]; // TODO: Make this dynamic function selectionChanged(s: BaseSyntheticEvent) { const selectedValue = s.target.value; - if(selectedValue == "none") return; + if(selectedValue == "none") { + setSchemaDescription(undefined); + return; + } i18next.loadNamespaces("impose", (err, t) => { if (err) throw err; @@ -19,8 +27,8 @@ function Dynamic() { const Operator = Module[capitalizeFirstLetter(selectedValue)]; const description = Operator.schema.describe(); + setSchemaDescription(description); // This will update children console.log(description); - // TODO: use description to generate fields }); }); } @@ -35,21 +43,19 @@ function Dynamic() {
- -
- -
- -
+ +
+ +

- +

Go back home... diff --git a/client-tauri/vite.config.ts b/client-tauri/vite.config.ts index 8b7accce2..596922d5c 100644 --- a/client-tauri/vite.config.ts +++ b/client-tauri/vite.config.ts @@ -2,6 +2,8 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import topLevelAwait from "vite-plugin-top-level-await"; import dynamicImport from 'vite-plugin-dynamic-import' +import compileTime from "vite-plugin-compile-time" + // https://vitejs.dev/config/ export default defineConfig(async () => ({ @@ -13,6 +15,7 @@ export default defineConfig(async () => ({ // The function to generate import names of top-level await promise in each chunk module promiseImportName: i => `__tla_${i}` }), + compileTime(), dynamicImport(), ], diff --git a/package-lock.json b/package-lock.json index d9a76f888..65aab4b7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,6 +50,7 @@ "@vitejs/plugin-react": "^4.0.3", "typescript": "^5.0.2", "vite": "^4.4.4", + "vite-plugin-compile-time": "^0.2.1", "vite-plugin-dynamic-import": "^1.5.0" } }, @@ -474,6 +475,21 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/android-arm": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", @@ -790,9 +806,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz", - "integrity": "sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", "cpu": [ "x64" ], @@ -1550,6 +1566,30 @@ } } }, + "node_modules/@rollup/plugin-dynamic-import-vars": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-dynamic-import-vars/-/plugin-dynamic-import-vars-2.1.2.tgz", + "integrity": "sha512-4lr2oXxs9hcxtGGaK8s0i9evfjzDrAs7ngw28TqruWKTEm0+U4Eljb+F6HXGYdFv8xRojQlrQwV7M/yxeh3yzQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "astring": "^1.8.5", + "estree-walker": "^2.0.2", + "fast-glob": "^3.2.12", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-inject": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz", @@ -2910,7 +2950,6 @@ "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -3118,6 +3157,15 @@ "util": "^0.12.5" } }, + "node_modules/astring": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz", + "integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==", + "dev": true, + "bin": { + "astring": "bin/astring" + } + }, "node_modules/async": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", @@ -3438,6 +3486,20 @@ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==" }, + "node_modules/bundle-require": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-4.0.2.tgz", + "integrity": "sha512-jwzPOChofl67PSTW2SGubV9HBQAhhR2i6nskiOThauo9dzwDUgOWQScFVaJkjEfYX+UXiD+LEx8EblQMc2wIag==", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.17" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -4233,6 +4295,11 @@ "node": ">=8" } }, + "node_modules/devalue": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.2.tgz", + "integrity": "sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==" + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -4370,8 +4437,7 @@ "node_modules/es-module-lexer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", - "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", - "dev": true + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==" }, "node_modules/esbuild": { "version": "0.18.20", @@ -6098,6 +6164,14 @@ "node": ">= 0.8.0" } }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -8970,11 +9044,376 @@ } } }, + "node_modules/vite-plugin-compile-time": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vite-plugin-compile-time/-/vite-plugin-compile-time-0.2.1.tgz", + "integrity": "sha512-lRuoSO2wg2r0rWPLo9aeOH3s70FcuQZIhvcR7yGubbezFVsebljWf9Vtk/TlvuvBLLahfnEUyeV2FQM2sj2EYQ==", + "dependencies": { + "bundle-require": "^4.0.1", + "devalue": "^4.3.2", + "esbuild": "^0.19.1", + "magic-string": "^0.30.2" + }, + "peerDependencies": { + "vite": ">=2" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-plugin-compile-time/node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, "node_modules/vite-plugin-dynamic-import": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/vite-plugin-dynamic-import/-/vite-plugin-dynamic-import-1.5.0.tgz", "integrity": "sha512-Qp85c+AVJmLa8MLni74U4BDiWpUeFNx7NJqbGZyR2XJOU7mgW0cb7nwlAMucFyM4arEd92Nfxp4j44xPi6Fu7g==", - "dev": true, "dependencies": { "acorn": "^8.8.2", "es-module-lexer": "^1.2.1", @@ -9318,10 +9757,13 @@ "pdf-lib": "^1.17.1", "rollup-plugin-copy": "^3.5.0", "tsconfig-paths": "^4.2.0", + "vite-plugin-compile-time": "^0.2.1", + "vite-plugin-dynamic-import": "^1.5.0", "vite-plugin-node-polyfills": "^0.19.0", "vite-plugin-top-level-await": "^1.4.1" }, "devDependencies": { + "@rollup/plugin-dynamic-import-vars": "^2.1.2", "@rollup/plugin-run": "^3.0.2", "@rollup/plugin-typescript": "^11.1.6", "copyfiles": "^2.4.1", diff --git a/server-node/package.json b/server-node/package.json index 554d002a2..42733ddf0 100644 --- a/server-node/package.json +++ b/server-node/package.json @@ -36,10 +36,13 @@ "pdf-lib": "^1.17.1", "rollup-plugin-copy": "^3.5.0", "tsconfig-paths": "^4.2.0", + "vite-plugin-compile-time": "^0.2.1", + "vite-plugin-dynamic-import": "^1.5.0", "vite-plugin-node-polyfills": "^0.19.0", "vite-plugin-top-level-await": "^1.4.1" }, "devDependencies": { + "@rollup/plugin-dynamic-import-vars": "^2.1.2", "@rollup/plugin-run": "^3.0.2", "@rollup/plugin-typescript": "^11.1.6", "copyfiles": "^2.4.1", diff --git a/server-node/rollup.config.js b/server-node/rollup.config.js index 615b9a5d6..1ae3f5144 100644 --- a/server-node/rollup.config.js +++ b/server-node/rollup.config.js @@ -1,15 +1,16 @@ import run from "@rollup/plugin-run"; import typescript from '@rollup/plugin-typescript'; import json from '@rollup/plugin-json'; -import copy from 'rollup-plugin-copy' - +import copy from 'rollup-plugin-copy'; +import compileTime from "vite-plugin-compile-time"; +import dynamicImportVars from '@rollup/plugin-dynamic-import-vars'; const isDev = process.env.NODE_ENV !== "production"; export default { input: "src/index.ts", output: { - file: "dist/bundle.js", + dir: "dist/", format: "es", }, watch: { @@ -18,6 +19,8 @@ export default { plugins: [ json(), typescript(), + dynamicImportVars(), + compileTime(), copy({ targets: [ { src: '../shared-operations/public', dest: 'dist' }, diff --git a/server-node/src/index.ts b/server-node/src/index.ts index 0e1a85c85..6fc4b44dc 100644 --- a/server-node/src/index.ts +++ b/server-node/src/index.ts @@ -6,6 +6,7 @@ const PORT = 8000; // server-node: backend api import api from "./routes/api/api-controller"; +import { listOperatorNames } from "@stirling-pdf/shared-operations/src/workflow/operatorAccessor"; app.use("/api", api); // serve @@ -16,3 +17,5 @@ app.listen(PORT, () => { process.on('unhandledRejection', (reason, promise) => { console.error('Unhandled Rejection at:', promise, 'reason:', reason); }); + +console.log("Available Modules: ", listOperatorNames()) \ No newline at end of file diff --git a/server-node/src/routes/api/dynamic-operations-controller.ts b/server-node/src/routes/api/dynamic-operations-controller.ts index 48a5708f7..b38d031d4 100644 --- a/server-node/src/routes/api/dynamic-operations-controller.ts +++ b/server-node/src/routes/api/dynamic-operations-controller.ts @@ -2,7 +2,7 @@ import express, { Request, Response } from "express"; const router = express.Router(); import multer from "multer"; const upload = multer(); -import { getOperatorByName } from "@stirling-pdf/shared-operations/src/workflow/getOperatorByName"; +import { getOperatorByName } from "@stirling-pdf/shared-operations/src/workflow/operatorAccessor"; import { Operator } from "@stirling-pdf/shared-operations/src/functions"; import { PdfFile } from "@stirling-pdf/shared-operations/src/wrappers/PdfFile"; @@ -11,14 +11,14 @@ import { Action } from "@stirling-pdf/shared-operations/declarations/Action"; import { JoiPDFFileSchema } from "@stirling-pdf/shared-operations/src/wrappers/PdfFileJoi"; router.post("/:func", upload.array("file"), async function(req: Request, res: Response) { - handleEndpoint(req, res); + await handleEndpoint(req, res); }); router.post("/:dir/:func", upload.array("file"), async function(req: Request, res: Response) { - handleEndpoint(req, res); + await handleEndpoint(req, res); }); -function handleEndpoint(req: Request, res: Response) { +async function handleEndpoint(req: Request, res: Response) { if(!req.files || req.files.length == 0) { res.status(400).json({error: "no input file(s) were provided"}); return; @@ -31,7 +31,8 @@ function handleEndpoint(req: Request, res: Response) { } const pdfFiles: PdfFile[] = validationResults.value; - const operator = getOperatorByName(req.params.func); + const operator = await getOperatorByName(req.params.func); + if(operator) { const action: Action = {type: req.params.func, values: req.body}; diff --git a/server-node/tsconfig.json b/server-node/tsconfig.json index c32ef5428..86d3a8845 100644 --- a/server-node/tsconfig.json +++ b/server-node/tsconfig.json @@ -30,7 +30,7 @@ "moduleResolution": "Node", /* Specify how TypeScript looks up a file from a given module specifier. */ "baseUrl": "./src", /* Specify the base directory to resolve non-relative module names. */ "paths": { - "#pdfcpu": ["@stirling-pdf/shared-operations/src/wasm/pdfcpu/pdfcpu-wrapper.server"], + "#pdfcpu": ["../shared-operations/src/wasm/pdfcpu/pdfcpu-wrapper.server.js"], "@stirling-pdf/*": [ "../../*" ] }, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ diff --git a/shared-operations/declarations/ImportMeta.d.ts b/shared-operations/declarations/ImportMeta.d.ts new file mode 100644 index 000000000..9d1c2b246 --- /dev/null +++ b/shared-operations/declarations/ImportMeta.d.ts @@ -0,0 +1,3 @@ +declare interface ImportMeta { + compileTime: (file: string) => T +} \ No newline at end of file diff --git a/shared-operations/src/functions/arrangePages.ts b/shared-operations/src/functions/arrangePages.ts index 9ad75b3c8..2c7f28c68 100644 --- a/shared-operations/src/functions/arrangePages.ts +++ b/shared-operations/src/functions/arrangePages.ts @@ -1,8 +1,7 @@ - -import { PdfFile } from "../wrappers/PdfFile.js"; -import { Sorts } from "./common/pageIndexesSorting.js"; -import { getPages } from "./common/getPagesByIndex.js"; -import { parsePageIndexSpecification } from "./common/pageIndexesUtils.js"; +import { PdfFile } from "../wrappers/PdfFile"; +import { Sorts } from "./common/pageIndexesSorting"; +import { getPages } from "./common/getPagesByIndex"; +import { parsePageIndexSpecification } from "./common/pageIndexesUtils"; export interface ArrangePagesParamsType { file: PdfFile; diff --git a/shared-operations/src/workflow/getOperatorByName.ts b/shared-operations/src/workflow/getOperatorByName.ts deleted file mode 100644 index 99bd9e9cd..000000000 --- a/shared-operations/src/workflow/getOperatorByName.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Operator } from "../functions"; -import i18next from "i18next"; - -// TODO: Import other Operators (should make this dynamic imports) -i18next.loadNamespaces("impose", (err, t) => { if (err) throw err; }); -import { Impose } from "../functions/impose"; - -export const Operators = { - Impose: Impose -}; - -// TODO: Convert this to a map or similar -export function getOperatorByName(name: string): typeof Operator | undefined { - let foundClass: typeof Operator | undefined = undefined; - - // Loop over each default export - Object.entries(Operators).some(([className, exportedClass]) => { - // Check if the exported item is a class - if (typeof exportedClass === "function" && exportedClass.prototype) { - if (exportedClass.type === name) { - foundClass = exportedClass; - return true; // Stop the iteration - } - } - return false; - }); - - return foundClass; -} - -export function listOperatorNames(): string[] { - // TODO: Implement this - return ["impose"]; -} diff --git a/shared-operations/src/workflow/listOperatorsInDir.ts b/shared-operations/src/workflow/listOperatorsInDir.ts new file mode 100644 index 000000000..a0c28585b --- /dev/null +++ b/shared-operations/src/workflow/listOperatorsInDir.ts @@ -0,0 +1,38 @@ +import fs from "fs"; +import path from "path"; +import { + CompileTimeFunctionArgs, + CompileTimeFunctionResult, + } from "vite-plugin-compile-time" + +function getAllJsFiles(directory) { + const jsFiles = []; + + // Synchronously read the contents of the directory + const files = fs.readdirSync(directory); + + // Iterate through the files and filter out the JavaScript files + files.forEach((file) => { + const filePath = path.join(directory, file) + const isJsFile = fs.statSync(filePath).isFile() && path.extname(filePath) === '.ts'; + + if (isJsFile) { + const baseName = path.basename(filePath, '.ts'); + if(baseName != "index") { + jsFiles.push(baseName); + } + } + }); + + return jsFiles; +} +export default async ( + args: CompileTimeFunctionArgs, + ): Promise => { + const jsFiles = getAllJsFiles(__dirname + "/../functions/"); + return { + data: jsFiles, + // Trigger rebuild when watched files change + watchFiles: [__filename], + } + } \ No newline at end of file diff --git a/shared-operations/src/workflow/operatorAccessor.ts b/shared-operations/src/workflow/operatorAccessor.ts new file mode 100644 index 000000000..faf5295ad --- /dev/null +++ b/shared-operations/src/workflow/operatorAccessor.ts @@ -0,0 +1,24 @@ +import { Operator } from "../functions"; +import i18next from "i18next"; + +function getCompileTimeOperatorList(): string[] { + return import.meta.compileTime("./listOperatorsInDir.ts"); +} + +export async function getOperatorByName(name: string): Promise { + // Check if exists + if(!getCompileTimeOperatorList().includes(name)) return; + + i18next.loadNamespaces(name, (err, t) => { if (err) throw err; }); + return (await import("../functions/" + name + ".ts"))[capitalizeFirstLetter(name)]; +} + +export function listOperatorNames(): string[] { + const availableOperators = getCompileTimeOperatorList(); + // TODO: Implement this + return availableOperators; +} + +function capitalizeFirstLetter(string: String) { + return string.charAt(0).toUpperCase() + string.slice(1); +} \ No newline at end of file diff --git a/shared-operations/src/workflow/traverseOperations.ts b/shared-operations/src/workflow/traverseOperations.ts index 4dd684aca..d8e7ab7bd 100644 --- a/shared-operations/src/workflow/traverseOperations.ts +++ b/shared-operations/src/workflow/traverseOperations.ts @@ -3,10 +3,10 @@ import { Action, WaitAction } from "../../declarations/Action"; import { PdfFile } from "../wrappers/PdfFile"; import { Progress } from "../functions"; import { validateOperations } from "./validateOperations"; -import { getOperatorByName } from "./getOperatorByName"; +import { getOperatorByName } from "./operatorAccessor"; export async function traverseOperations(operations: Action[], input: PdfFile[], progressCallback: (state: Progress) => void): Promise { - const validationResult = validateOperations(operations); + const validationResult = await validateOperations(operations); if(!validationResult.valid) { return Promise.reject({validationError: validationResult.reason}); } @@ -47,7 +47,7 @@ export async function traverseOperations(operations: Action[], input: PdfFile[], } break; default: - const operator = getOperatorByName(action.type); + const operator = await getOperatorByName(action.type); if(operator) { const operation = new operator(action); input = await operation.run(input, progressCallback); diff --git a/shared-operations/src/workflow/validateOperations.ts b/shared-operations/src/workflow/validateOperations.ts index 66de33812..f3d89bbbd 100644 --- a/shared-operations/src/workflow/validateOperations.ts +++ b/shared-operations/src/workflow/validateOperations.ts @@ -1,9 +1,9 @@ import { Operator } from "../functions"; import { Action } from "../../declarations/Action"; -import { getOperatorByName } from "./getOperatorByName"; +import { getOperatorByName } from "./operatorAccessor"; /** This function validates the "workflow-json" from the API */ -export function validateOperations(actions: Action[]): { valid: boolean, reason?: string} { +export async function validateOperations(actions: Action[]): Promise<{ valid: boolean, reason?: string}> { const done: Action[] = []; for (const action of actions) { @@ -15,7 +15,7 @@ export function validateOperations(actions: Action[]): { valid: boolean, reason? continue; } - const operator = getOperatorByName(action.type); + const operator = await getOperatorByName(action.type); if(!operator) { return { valid: false, reason: `action.type ${action.type} does not exist` }; } @@ -35,7 +35,7 @@ export function validateOperations(actions: Action[]): { valid: boolean, reason? } for (const afterDoneChild of done[childAction.values.id]?.actions || []) { - const receivingOperator = getOperatorByName(afterDoneChild.type); + const receivingOperator = await getOperatorByName(afterDoneChild.type); if (receivingOperator === undefined) { return { valid: false, reason: `action.type ${afterDoneChild.type} does not exist.` }; } else if (!ioCompatible(operator, receivingOperator)) { @@ -47,7 +47,7 @@ export function validateOperations(actions: Action[]): { valid: boolean, reason? return { valid: false, reason: "There shouldn't be a done action here." }; } else { - const receivingOperator = getOperatorByName(childAction.type); + const receivingOperator = await getOperatorByName(childAction.type); if (receivingOperator === undefined) { return { valid: false, reason: `action.type ${childAction.type} does not exist.` }; } else if (!ioCompatible(operator, receivingOperator)) { @@ -56,7 +56,7 @@ export function validateOperations(actions: Action[]): { valid: boolean, reason? } } - const validationResult = validateOperations(action.actions); + const validationResult = await validateOperations(action.actions); if(!validationResult.valid) { return validationResult;