Get VueJS working in a sample project

This commit is contained in:
Eli Ribble 2026-03-21 17:44:14 +00:00
parent 228f4a6db9
commit ccdb391ccc
No known key found for this signature in database
8 changed files with 294 additions and 42 deletions

1
.gitignore vendored
View file

@ -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

View file

@ -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`

View file

@ -1,4 +1,14 @@
{ pkgs ? import <nixpkgs> { }, 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)

View file

@ -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

View file

@ -3,16 +3,11 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Alpine + TypeScript Demo</title>
<title>VueJS + TypeScript Demo</title>
</head>
<body>
<div x-data="greeting">
<h1 x-text="message"></h1>
<input type="text" x-model="name" placeholder="Enter your name" />
<p>Hello, <span x-text="name"></span>!</p>
<button @click="updateMessage">Change Message</button>
</div>
<div id="app"></div>
<script src="/static/bundle.js"></script>
<script src="/static/gen/js/bundle.js" type="module"></script>
</body>
</html>

18
package.json Normal file
View file

@ -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"
}
}

215
pnpm-lock.yaml generated Normal file
View file

@ -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

View file

@ -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: `
<div>
<p>{{ message }}</p>
<button @click="count++">Count: {{ count }}</button>
</div>
`
};
createApp(App).mount('#app');