diff --git a/.gitignore b/.gitignore index ea4e1c8d..56888464 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ districts/ flogo.log nidus-sync nidus-sync.log +node_modules/ result stadia/cmd/bulk-geocode/bulk-geocode stadia/cmd/reverse-geocode/reverse-geocode diff --git a/README.md b/README.md index 694cfd7c..35847479 100644 --- a/README.md +++ b/README.md @@ -86,10 +86,18 @@ This uses [goose](https://github.com/pressly/goose). You can use the goose comma ### typescript -You can generate the TypeScript with: +In order to work on the TypeScript code you'll need to install the dependencies locally in your dev environment: ``` -esbuild ts/main.ts --bundle --outfile=html/static/bundle.js --format=iife --minify +nix develop +pnpm install +``` + +You can then generate the TypeScript with: + +``` +pnpm watch + ``` The only page that works right now is `https://sync.nidus.cloud/template-test` diff --git a/default.nix b/default.nix index 386c5ec7..6a5e0947 100644 --- a/default.nix +++ b/default.nix @@ -1,4 +1,14 @@ { pkgs ? import { }, proj ? pkgs.proj }: + +let + # Fetch pnpm dependencies + pnpmDeps = pkgs.pnpm.fetchDeps { + pname = "nidus-sync-frontend"; + version = "0.0.11"; + src ./.; + hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; # nix will tell you the correct hash + }; +in pkgs.buildGoModule rec { meta = { description = "Nidus Sync"; @@ -16,9 +26,17 @@ pkgs.buildGoModule rec { pkgs.pkg-config pkgs.dart-sass pkgs.esbuild + pkgs.pnpm.configHook + pkgs.nodejs ]; + pnpmDeps = pnpmDeps; + preBuild = '' +# Setup node_modules from pnpm dependencies +export HOME=$(mktemp -d) +pnpm config set store-dir $HOME/.pnpm-store + # Compile SCSS SASS_SRC_DIR="./scss" CSS_OUTPUT_DIR="./static/gen/css" @@ -37,8 +55,16 @@ echo "Generated CSS style with hash: $STYLE_HASH" JS_OUTPUT_DIR="./static/gen/js" mkdir -p "$JS_OUTPUT_DIR" -echo "Bundling TypeScript..." -esbuild ts/main.ts --bundle --minify --outfile="$JS_OUTPUT_DIR/bundle.js" +echo "Bundling TypeScript with Vue..." +esbuild ts/main.ts \ + --bundle \ + --minify \ + --format=esm \ + --define:__VUE_OPTIONS_API__=true \ + --define:__VUE_PROD_DEVTOOLS__=false \ + --define:__VUE_PROD_HYDRATION_MISMATCH_DETAILS__=false \ + --alias:vue=vue/dist/vue.esm-bundler.js \ + --outfile="$JS_OUTPUT_DIR/bundle.js" # Generate hash and rename bundle BUNDLE_HASH=$(sha256sum "$JS_OUTPUT_DIR/bundle.js" | cut -c1-12) diff --git a/flake.nix b/flake.nix index 623d1199..d2fb0166 100644 --- a/flake.nix +++ b/flake.nix @@ -36,7 +36,9 @@ pkgs.goose pkgs.gotools pkgs.lefthook + pkgs.nodejs pkgs.pkg-config + pkgs.pnpm pkgs.prettier pkgs.prettier-plugin-go-template proj.packages.${system}.default diff --git a/html/template/sync/template-test.html b/html/template/sync/template-test.html index c1a01d5e..40eec6f4 100644 --- a/html/template/sync/template-test.html +++ b/html/template/sync/template-test.html @@ -3,16 +3,11 @@ - Alpine + TypeScript Demo + VueJS + TypeScript Demo -
-

- -

Hello, !

- -
+
- + diff --git a/package.json b/package.json new file mode 100644 index 00000000..3b4aacbb --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "nidus-sync-frontend", + "version": "0.0.11", + "private": true, + "type": "module", + "dependencies": { + "vue": "^3.5.30" + }, + "devDependencies": { + "typescript": "^5.3.0" + }, + "scripts": { + "build": "esbuild ts/main.ts --bundle --format=esm --outfile=static/gen/js/bundle.js --define:__VUE_OPTIONS_API__=true --define:__VUE_PROD_DEVTOOLS__=false --define:__VUE_PROD_HYDRATION_MISMATCH_DETAILS__=false --alias:vue=vue/dist/vue.esm-bundler.js", + "build:prod": "esbuild ts/main.ts --bundle --minify --format=esm --outfile=static/gen/js/bundle.js --define:__VUE_OPTIONS_API__=true --define:__VUE_PROD_DEVTOOLS__=false --define:__VUE_PROD_HYDRATION_MISMATCH_DETAILS__=false --alias:vue=vue/dist/vue.esm-bundler.js", + "watch": "esbuild ts/main.ts --bundle --format=esm --outfile=static/gen/js/bundle.js --define:__VUE_OPTIONS_API__=true --define:__VUE_PROD_DEVTOOLS__=false --define:__VUE_PROD_HYDRATION_MISMATCH_DETAILS__=false --alias:vue=vue/dist/vue.esm-bundler.js --watch" + } +} + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..f5b87d97 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,215 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + vue: + specifier: ^3.5.30 + version: 3.5.30(typescript@5.9.3) + devDependencies: + typescript: + specifier: ^5.3.0 + version: 5.9.3 + +packages: + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@vue/compiler-core@3.5.30': + resolution: {integrity: sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==} + + '@vue/compiler-dom@3.5.30': + resolution: {integrity: sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==} + + '@vue/compiler-sfc@3.5.30': + resolution: {integrity: sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==} + + '@vue/compiler-ssr@3.5.30': + resolution: {integrity: sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==} + + '@vue/reactivity@3.5.30': + resolution: {integrity: sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q==} + + '@vue/runtime-core@3.5.30': + resolution: {integrity: sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg==} + + '@vue/runtime-dom@3.5.30': + resolution: {integrity: sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw==} + + '@vue/server-renderer@3.5.30': + resolution: {integrity: sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ==} + peerDependencies: + vue: 3.5.30 + + '@vue/shared@3.5.30': + resolution: {integrity: sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + postcss@8.5.8: + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + engines: {node: ^10 || ^12 || >=14} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + vue@3.5.30: + resolution: {integrity: sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + +snapshots: + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/parser@7.29.2': + dependencies: + '@babel/types': 7.29.0 + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@vue/compiler-core@3.5.30': + dependencies: + '@babel/parser': 7.29.2 + '@vue/shared': 3.5.30 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.30': + dependencies: + '@vue/compiler-core': 3.5.30 + '@vue/shared': 3.5.30 + + '@vue/compiler-sfc@3.5.30': + dependencies: + '@babel/parser': 7.29.2 + '@vue/compiler-core': 3.5.30 + '@vue/compiler-dom': 3.5.30 + '@vue/compiler-ssr': 3.5.30 + '@vue/shared': 3.5.30 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.8 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.30': + dependencies: + '@vue/compiler-dom': 3.5.30 + '@vue/shared': 3.5.30 + + '@vue/reactivity@3.5.30': + dependencies: + '@vue/shared': 3.5.30 + + '@vue/runtime-core@3.5.30': + dependencies: + '@vue/reactivity': 3.5.30 + '@vue/shared': 3.5.30 + + '@vue/runtime-dom@3.5.30': + dependencies: + '@vue/reactivity': 3.5.30 + '@vue/runtime-core': 3.5.30 + '@vue/shared': 3.5.30 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.30(vue@3.5.30(typescript@5.9.3))': + dependencies: + '@vue/compiler-ssr': 3.5.30 + '@vue/shared': 3.5.30 + vue: 3.5.30(typescript@5.9.3) + + '@vue/shared@3.5.30': {} + + csstype@3.2.3: {} + + entities@7.0.1: {} + + estree-walker@2.0.2: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + nanoid@3.3.11: {} + + picocolors@1.1.1: {} + + postcss@8.5.8: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + source-map-js@1.2.1: {} + + typescript@5.9.3: {} + + vue@3.5.30(typescript@5.9.3): + dependencies: + '@vue/compiler-dom': 3.5.30 + '@vue/compiler-sfc': 3.5.30 + '@vue/runtime-dom': 3.5.30 + '@vue/server-renderer': 3.5.30(vue@3.5.30(typescript@5.9.3)) + '@vue/shared': 3.5.30 + optionalDependencies: + typescript: 5.9.3 diff --git a/ts/main.ts b/ts/main.ts index 37ea0b23..f422d08c 100644 --- a/ts/main.ts +++ b/ts/main.ts @@ -1,32 +1,19 @@ -import Alpine from './vendor/alpinejs-3.15.8.js'; -import bootstrap from '../static/vendor/bootstrap-5.3.8/bootstrap.bundle.min.js'; -import { SSEManager } from './sse-manager'; +import { createApp } from 'vue'; -// Make Alpine available on window for inline Alpine -window.Alpine = Alpine; - -// Make bootstrap available on window for various scripts -window.bootstrap = bootstrap; - -// Make SSEManager available to all the JavaScript -window.SSEManager = SSEManager; - -// Wait for DOM to be ready, then initialize Alpine -document.addEventListener("DOMContentLoaded", () => { - Alpine.start(); - SSEManager.connect("/api/events"); -}); -interface GreetingComponent { - message: string; - name: string; - updateMessage(): void; -} - -Alpine.data('greeting', (): GreetingComponent => ({ - message: 'Welcome to Alpine + TypeScript!', - name: 'World', - - updateMessage() { - this.message = 'Message updated at ' + new Date().toLocaleTimeString(); +// Simple example without SFCs +const App = { + data() { + return { + count: 0, + message: 'Hello from Vue 3!' } -})); + }, + template: ` +
+

{{ message }}

+ +
+ ` +}; + +createApp(App).mount('#app');