frontend-hamroun 1.2.74 → 1.2.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Counter.d.ts +0 -0
- package/dist/batch/package.json +16 -0
- package/dist/client-router/package.json +16 -0
- package/dist/component/package.json +16 -0
- package/dist/context/package.json +16 -0
- package/dist/event-bus/package.json +16 -0
- package/dist/forms/package.json +16 -0
- package/dist/hooks/package.json +16 -0
- package/dist/hooks-0728361a.cjs +1 -0
- package/dist/hooks-b58f947c.js +133 -0
- package/dist/hooks.js +1 -0
- package/dist/hooks.mjs +13 -0
- package/dist/index.js +1 -384
- package/dist/index.mjs +130 -374
- package/dist/jsx-runtime/package.json +16 -0
- package/dist/jsx-runtime.js +1 -0
- package/dist/jsx-runtime.mjs +64 -0
- package/dist/lifecycle-events/package.json +16 -0
- package/dist/package.json +71 -0
- package/dist/render-component/package.json +16 -0
- package/dist/renderer/package.json +16 -0
- package/dist/renderer.js +1 -0
- package/dist/renderer.mjs +27 -0
- package/dist/router/package.json +16 -0
- package/dist/server/package.json +17 -0
- package/dist/server/src/batch.d.ts +3 -0
- package/dist/server/src/batch.js +23 -0
- package/dist/server/src/batch.js.map +1 -0
- package/dist/server/src/client-router.d.ts +60 -0
- package/dist/server/src/client-router.js +210 -0
- package/dist/server/src/client-router.js.map +1 -0
- package/dist/server/src/component.d.ts +14 -0
- package/dist/server/src/component.js +106 -0
- package/dist/server/src/component.js.map +1 -0
- package/dist/server/src/context.d.ts +13 -0
- package/dist/server/src/context.js +21 -0
- package/dist/server/src/context.js.map +1 -0
- package/dist/server/src/event-bus.d.ts +23 -0
- package/dist/server/src/event-bus.js +75 -0
- package/dist/server/src/event-bus.js.map +1 -0
- package/dist/server/src/forms.d.ts +40 -0
- package/dist/server/src/forms.js +148 -0
- package/dist/server/src/forms.js.map +1 -0
- package/dist/server/src/hooks.d.ts +12 -0
- package/dist/server/src/hooks.js +170 -0
- package/dist/server/src/hooks.js.map +1 -0
- package/dist/server/src/index.client.d.ts +12 -0
- package/dist/server/src/index.client.js +14 -0
- package/dist/server/src/index.client.js.map +1 -0
- package/dist/server/src/index.d.ts +88 -0
- package/dist/server/src/index.js +78 -0
- package/dist/server/src/index.js.map +1 -0
- package/dist/server/src/jsx-runtime/jsx-dev-runtime.d.ts +1 -0
- package/dist/server/src/jsx-runtime/jsx-dev-runtime.js +2 -0
- package/dist/server/src/jsx-runtime/jsx-dev-runtime.js.map +1 -0
- package/dist/server/src/jsx-runtime/jsx-runtime.d.ts +4 -0
- package/dist/server/src/jsx-runtime/jsx-runtime.js +41 -0
- package/dist/server/src/jsx-runtime/jsx-runtime.js.map +1 -0
- package/dist/server/src/jsx-runtime.d.ts +20 -0
- package/dist/server/src/jsx-runtime.js +105 -0
- package/dist/server/src/jsx-runtime.js.map +1 -0
- package/dist/server/src/lifecycle-events.d.ts +108 -0
- package/dist/server/src/lifecycle-events.js +177 -0
- package/dist/server/src/lifecycle-events.js.map +1 -0
- package/dist/server/src/renderComponent.d.ts +14 -0
- package/dist/server/src/renderComponent.js +25 -0
- package/dist/server/src/renderComponent.js.map +1 -0
- package/dist/server/src/renderer.d.ts +2 -0
- package/dist/server/src/renderer.js +31 -0
- package/dist/server/src/renderer.js.map +1 -0
- package/dist/server/src/router.d.ts +55 -0
- package/dist/server/src/router.js +166 -0
- package/dist/server/src/router.js.map +1 -0
- package/dist/server/src/server/api-router.d.ts +15 -0
- package/dist/server/src/server/api-router.js +111 -0
- package/dist/server/src/server/api-router.js.map +1 -0
- package/dist/server/src/server/auth.d.ts +32 -0
- package/dist/server/src/server/auth.js +80 -0
- package/dist/server/src/server/auth.js.map +1 -0
- package/dist/server/src/server/database.d.ts +24 -0
- package/dist/server/src/server/database.js +135 -0
- package/dist/server/src/server/database.js.map +1 -0
- package/dist/server/src/server/index.d.ts +127 -0
- package/dist/server/src/server/index.js +388 -0
- package/dist/server/src/server/index.js.map +1 -0
- package/dist/server/src/server/middleware.d.ts +11 -0
- package/dist/server/src/server/middleware.js +46 -0
- package/dist/server/src/server/middleware.js.map +1 -0
- package/dist/server/src/server/server.d.ts +9 -0
- package/dist/server/src/server/server.js +87 -0
- package/dist/server/src/server/server.js.map +1 -0
- package/dist/server/src/server/templates.d.ts +28 -0
- package/dist/server/src/server/templates.js +204 -0
- package/dist/server/src/server/templates.js.map +1 -0
- package/dist/server/src/server/types.d.ts +38 -0
- package/dist/server/src/server/types.js +4 -0
- package/dist/server/src/server/types.js.map +1 -0
- package/dist/server/src/server/utils.d.ts +70 -0
- package/dist/server/src/server/utils.js +156 -0
- package/dist/server/src/server/utils.js.map +1 -0
- package/dist/server/src/server/wasm.d.ts +9 -0
- package/dist/server/src/server/wasm.js +117 -0
- package/dist/server/src/server/wasm.js.map +1 -0
- package/dist/server/src/server-renderer.d.ts +5 -0
- package/dist/server/src/server-renderer.js +106 -0
- package/dist/server/src/server-renderer.js.map +1 -0
- package/dist/server/src/server-types.d.ts +42 -0
- package/dist/server/src/server-types.js +6 -0
- package/dist/server/src/server-types.js.map +1 -0
- package/dist/server/src/store.d.ts +41 -0
- package/dist/server/src/store.js +99 -0
- package/dist/server/src/store.js.map +1 -0
- package/dist/server/src/types.d.ts +19 -0
- package/dist/server/src/types.js +2 -0
- package/dist/server/src/types.js.map +1 -0
- package/dist/server/src/utils.d.ts +46 -0
- package/dist/server/src/utils.js +144 -0
- package/dist/server/src/utils.js.map +1 -0
- package/dist/server/src/vdom.d.ts +8 -0
- package/dist/server/src/vdom.js +22 -0
- package/dist/server/src/vdom.js.map +1 -0
- package/dist/server/src/wasm.d.ts +36 -0
- package/dist/server/src/wasm.js +159 -0
- package/dist/server/src/wasm.js.map +1 -0
- package/dist/server/tsconfig.server.tsbuildinfo +1 -0
- package/dist/server-renderer/package.json +16 -0
- package/dist/server-renderer.js +1 -0
- package/dist/server-renderer.mjs +64 -0
- package/dist/store/package.json +16 -0
- package/dist/types/package.json +16 -0
- package/dist/utils/package.json +16 -0
- package/dist/vdom/package.json +16 -0
- package/dist/wasm/package.json +16 -0
- package/dist/wasm.js +1 -0
- package/dist/wasm.mjs +103 -0
- package/package.json +14 -13
- package/templates/complete-app/build.js +284 -0
- package/templates/complete-app/package.json +40 -0
- package/templates/complete-app/public/styles.css +345 -0
- package/templates/complete-app/src/api/index.js +31 -0
- package/templates/complete-app/src/client.js +93 -0
- package/templates/complete-app/src/components/App.js +66 -0
- package/templates/complete-app/src/components/Footer.js +19 -0
- package/templates/complete-app/src/components/Header.js +38 -0
- package/templates/complete-app/src/pages/About.js +59 -0
- package/templates/complete-app/src/pages/Home.js +54 -0
- package/templates/complete-app/src/pages/WasmDemo.js +136 -0
- package/templates/complete-app/src/server.js +186 -0
- package/templates/complete-app/src/wasm/build.bat +16 -0
- package/templates/complete-app/src/wasm/build.sh +16 -0
- package/templates/complete-app/src/wasm/example.go +101 -0
- package/templates/fullstack-app/build/main.css +225 -15
- package/templates/fullstack-app/build/main.css.map +2 -2
- package/templates/fullstack-app/build/main.js +657 -372
- package/templates/fullstack-app/build/main.js.map +4 -4
- package/templates/fullstack-app/build.ts +3 -4
- package/templates/fullstack-app/public/styles.css +222 -15
- package/templates/fullstack-app/server.ts +46 -12
- package/templates/fullstack-app/src/components/ClientHome.tsx +0 -0
- package/templates/fullstack-app/src/components/ErrorBoundary.tsx +36 -0
- package/templates/fullstack-app/src/components/Layout.tsx +23 -26
- package/templates/fullstack-app/src/components/StateDemo.tsx +207 -0
- package/templates/fullstack-app/src/components/UserList.tsx +30 -13
- package/templates/fullstack-app/src/data/api.ts +173 -38
- package/templates/fullstack-app/src/main.tsx +88 -154
- package/templates/fullstack-app/src/middleware.ts +28 -0
- package/templates/fullstack-app/src/pages/404.tsx +28 -0
- package/templates/fullstack-app/src/pages/[id].tsx +0 -0
- package/templates/fullstack-app/src/pages/_app.tsx +11 -0
- package/templates/fullstack-app/src/pages/_document.tsx +25 -0
- package/templates/fullstack-app/src/pages/_error.tsx +45 -0
- package/templates/fullstack-app/src/pages/about.tsx +71 -0
- package/templates/fullstack-app/src/pages/api/users/[id].ts +73 -0
- package/templates/fullstack-app/src/pages/api/users/index.ts +43 -0
- package/templates/fullstack-app/src/pages/index.tsx +97 -20
- package/templates/fullstack-app/src/pages/users/[id].tsx +153 -0
- package/templates/fullstack-app/src/pages/wasm-demo.tsx +1 -0
- package/templates/go/build.sh +43 -43
- package/templates/go/example.go +99 -86
- package/templates/go-wasm-app/babel.config.js +8 -2
- package/templates/go-wasm-app/build-wasm.js +84 -84
- package/templates/go-wasm-app/build.config.js +62 -0
- package/templates/go-wasm-app/build.js +218 -0
- package/templates/go-wasm-app/package.json +21 -11
- package/templates/go-wasm-app/public/index.html +49 -53
- package/templates/go-wasm-app/server.js +56 -695
- package/templates/go-wasm-app/src/app.js +173 -0
- package/templates/go-wasm-app/vite.config.js +16 -5
- package/templates/ssr-template/client.js +54 -26
- package/templates/ssr-template/server.js +5 -28
- package/templates/ssr-template/vite.config.js +21 -5
- package/dist/index.d.ts +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
@@ -1,709 +1,70 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
import
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
// ESM
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
const WASM_PATH = path.join(__dirname, isProduction ? 'dist' : 'public', 'wasm', 'example.wasm');
|
18
|
-
const WASM_EXEC_NODE_PATH = path.join(__dirname, isProduction ? 'dist' : 'public', 'wasm', 'wasm_exec_node.js');
|
19
|
-
|
20
|
-
// Express app setup
|
21
|
-
const app = express();
|
22
|
-
|
23
|
-
// IMPORTANT: Configure routes in correct order for Express
|
24
|
-
|
25
|
-
// 1. Special route for WASM files to set correct MIME type
|
26
|
-
app.get('*.wasm', (req, res, next) => {
|
27
|
-
res.set('Content-Type', 'application/wasm');
|
28
|
-
next();
|
29
|
-
});
|
30
|
-
|
31
|
-
// 2. Serve static files EXCEPT index.html (this prevents the static index.html from being served)
|
32
|
-
app.use(express.static(path.join(__dirname, 'public'), {
|
33
|
-
index: false // Don't serve index.html automatically
|
34
|
-
}));
|
35
|
-
|
36
|
-
// Setup WASM support on the server - this needs to be done before defining routes
|
37
|
-
async function setupWasmSupport() {
|
38
|
-
try {
|
39
|
-
// Add WebAssembly support for Node.js
|
40
|
-
global.WebAssembly = WebAssembly;
|
41
|
-
|
42
|
-
// Check if WASM file exists
|
43
|
-
if (!fs.existsSync(WASM_PATH)) {
|
44
|
-
console.warn(`⚠️ WASM file not found at: ${WASM_PATH}`);
|
45
|
-
}
|
46
|
-
|
47
|
-
// Since we're having issues with direct WASM loading in Node.js,
|
48
|
-
// use the mock implementation for template development
|
49
|
-
console.log('Using mock WASM implementation for template development');
|
50
|
-
return {
|
51
|
-
functions: {
|
52
|
-
goAdd: (a, b) => Number(a) + Number(b),
|
53
|
-
goProcessData: (data) => JSON.stringify({
|
54
|
-
...data,
|
55
|
-
processed: true,
|
56
|
-
processor: "Mock Go WASM (Server)",
|
57
|
-
timestamp: new Date().toISOString(),
|
58
|
-
sum: Array.isArray(data.values) ?
|
59
|
-
data.values.reduce((sum, val) => sum + Number(val), 0) : 0
|
60
|
-
})
|
61
|
-
}
|
62
|
-
};
|
63
|
-
} catch (error) {
|
64
|
-
console.error('Setup WASM error:', error);
|
65
|
-
// Return mock implementation as fallback
|
66
|
-
return {
|
67
|
-
functions: {
|
68
|
-
goAdd: (a, b) => Number(a) + Number(b),
|
69
|
-
goProcessData: (data) => JSON.stringify({
|
70
|
-
...data,
|
71
|
-
processed: true,
|
72
|
-
processor: "Mock Go WASM (Server)",
|
73
|
-
error: error.message
|
74
|
-
})
|
75
|
-
}
|
76
|
-
};
|
77
|
-
}
|
1
|
+
// Server script with dual module support (ESM/CommonJS)
|
2
|
+
|
3
|
+
// Dynamically import dependencies based on module system
|
4
|
+
const isESM = typeof require === 'undefined';
|
5
|
+
let express, path, fs;
|
6
|
+
|
7
|
+
if (isESM) {
|
8
|
+
// ESM imports
|
9
|
+
import('express').then(module => express = module.default);
|
10
|
+
import('path').then(module => path = module);
|
11
|
+
import('fs').then(module => fs = module);
|
12
|
+
} else {
|
13
|
+
// CommonJS requires
|
14
|
+
express = require('express');
|
15
|
+
path = require('path');
|
16
|
+
fs = require('fs');
|
78
17
|
}
|
79
18
|
|
80
|
-
//
|
81
|
-
function renderToString(component) {
|
82
|
-
// Simple JSX-like rendering
|
83
|
-
const renderElement = (el) => {
|
84
|
-
if (typeof el === 'string' || typeof el === 'number' || el == null) {
|
85
|
-
return String(el);
|
86
|
-
}
|
87
|
-
|
88
|
-
if (Array.isArray(el)) {
|
89
|
-
return el.map(renderElement).join('');
|
90
|
-
}
|
91
|
-
|
92
|
-
if (!el.type) return '';
|
93
|
-
|
94
|
-
if (typeof el.type === 'function') {
|
95
|
-
return renderElement(el.type(el.props || {}));
|
96
|
-
}
|
97
|
-
|
98
|
-
const { children, ...props } = el.props || {};
|
99
|
-
const attrs = Object.entries(props || {})
|
100
|
-
.filter(([_, v]) => v !== undefined)
|
101
|
-
.map(([k, v]) => {
|
102
|
-
if (k === 'className') k = 'class';
|
103
|
-
if (k === 'style' && typeof v === 'object') {
|
104
|
-
const styleStr = Object.entries(v)
|
105
|
-
.map(([sk, sv]) => `${sk.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`)}:${sv}`)
|
106
|
-
.join(';');
|
107
|
-
return `${k}="${styleStr}"`;
|
108
|
-
}
|
109
|
-
return `${k}="${v}"`;
|
110
|
-
})
|
111
|
-
.join(' ');
|
112
|
-
|
113
|
-
const childrenContent = children
|
114
|
-
? (Array.isArray(children) ? children : [children]).map(renderElement).join('')
|
115
|
-
: '';
|
116
|
-
|
117
|
-
return `<${el.type}${attrs ? ' ' + attrs : ''}>${childrenContent}</${el.type}>`;
|
118
|
-
};
|
119
|
-
|
120
|
-
try {
|
121
|
-
return renderElement(component);
|
122
|
-
} catch (error) {
|
123
|
-
console.error('Render error:', error);
|
124
|
-
return `<div>Render error: ${error.message}</div>`;
|
125
|
-
}
|
126
|
-
}
|
127
|
-
|
128
|
-
// Global WASM instance for server-side processing
|
129
|
-
let serverWasmInstance = null;
|
130
|
-
|
131
|
-
// Initialize server and WASM
|
19
|
+
// Wait for all imports to resolve
|
132
20
|
async function startServer() {
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
renderPage(req, res);
|
140
|
-
});
|
141
|
-
|
142
|
-
// 4. Handle all other routes with server-side rendering
|
143
|
-
app.get('*', (req, res) => {
|
144
|
-
renderPage(req, res);
|
145
|
-
});
|
146
|
-
|
147
|
-
// Start the server
|
148
|
-
const server = app.listen(PORT, () => {
|
149
|
-
console.log(`\n🚀 Server running at http://localhost:${PORT}\n`);
|
150
|
-
});
|
151
|
-
|
152
|
-
return server;
|
153
|
-
} catch (error) {
|
154
|
-
console.error('Failed to start server:', error);
|
155
|
-
throw error;
|
156
|
-
}
|
157
|
-
}
|
158
|
-
|
159
|
-
// Function to render the page with server-side rendering
|
160
|
-
function renderPage(req, res) {
|
161
|
-
try {
|
162
|
-
// Initial state
|
163
|
-
const initialState = {
|
164
|
-
path: req.path,
|
165
|
-
wasmAvailable: serverWasmInstance !== null && Object.keys(serverWasmInstance.functions || {}).length > 0,
|
166
|
-
ssrRendered: true,
|
167
|
-
timestamp: new Date().toISOString()
|
168
|
-
};
|
169
|
-
|
170
|
-
// Process data with WASM if available
|
171
|
-
if (serverWasmInstance && serverWasmInstance.functions.goProcessData) {
|
172
|
-
try {
|
173
|
-
const processDataFn = serverWasmInstance.functions.goProcessData;
|
174
|
-
const processedData = processDataFn({
|
175
|
-
source: 'server',
|
176
|
-
timestamp: new Date().toISOString(),
|
177
|
-
values: [10, 20, 30, 40, 50]
|
178
|
-
});
|
179
|
-
|
180
|
-
initialState.processedData = typeof processedData === 'string'
|
181
|
-
? JSON.parse(processedData)
|
182
|
-
: processedData;
|
183
|
-
} catch (error) {
|
184
|
-
console.error('Error using WASM on server:', error);
|
185
|
-
initialState.wasmError = error.message;
|
186
|
-
}
|
187
|
-
}
|
188
|
-
|
189
|
-
// Component for SSR
|
190
|
-
const App = ({ initialState }) => ({
|
191
|
-
type: 'div',
|
192
|
-
props: {
|
193
|
-
className: 'app',
|
194
|
-
children: [
|
195
|
-
{
|
196
|
-
type: 'header',
|
197
|
-
props: {
|
198
|
-
children: [
|
199
|
-
{ type: 'h1', props: { children: 'Frontend Hamroun + Go WebAssembly' } },
|
200
|
-
{ type: 'p', props: { children: 'Server-side rendered template' } },
|
201
|
-
{
|
202
|
-
type: 'div',
|
203
|
-
props: {
|
204
|
-
className: 'rendering-info',
|
205
|
-
children: [
|
206
|
-
{
|
207
|
-
type: 'span',
|
208
|
-
props: {
|
209
|
-
className: 'badge active',
|
210
|
-
children: 'Server Rendered'
|
211
|
-
}
|
212
|
-
},
|
213
|
-
{
|
214
|
-
type: 'span',
|
215
|
-
props: {
|
216
|
-
className: 'badge',
|
217
|
-
children: 'Hydrated'
|
218
|
-
}
|
219
|
-
}
|
220
|
-
]
|
221
|
-
}
|
222
|
-
}
|
223
|
-
]
|
224
|
-
}
|
225
|
-
},
|
226
|
-
{
|
227
|
-
type: 'main',
|
228
|
-
props: {
|
229
|
-
children: [
|
230
|
-
{
|
231
|
-
type: 'div',
|
232
|
-
props: {
|
233
|
-
className: 'card',
|
234
|
-
children: [
|
235
|
-
{ type: 'h2', props: { children: 'WebAssembly Status' } },
|
236
|
-
{ type: 'p', props: {
|
237
|
-
children: initialState.wasmAvailable
|
238
|
-
? '✅ WebAssembly module loaded and ready'
|
239
|
-
: '⚠️ WebAssembly module not available'
|
240
|
-
} },
|
241
|
-
initialState.wasmAvailable && {
|
242
|
-
type: 'p',
|
243
|
-
props: {
|
244
|
-
children: [
|
245
|
-
'Available functions: ',
|
246
|
-
Object.keys(serverWasmInstance?.functions || {}).join(', ')
|
247
|
-
]
|
248
|
-
}
|
249
|
-
},
|
250
|
-
initialState.wasmError && {
|
251
|
-
type: 'div',
|
252
|
-
props: {
|
253
|
-
className: 'error',
|
254
|
-
children: initialState.wasmError
|
255
|
-
}
|
256
|
-
}
|
257
|
-
].filter(Boolean)
|
258
|
-
}
|
259
|
-
},
|
260
|
-
initialState.processedData && {
|
261
|
-
type: 'div',
|
262
|
-
props: {
|
263
|
-
className: 'card',
|
264
|
-
children: [
|
265
|
-
{ type: 'h2', props: { children: 'WASM-Processed Data' } },
|
266
|
-
{ type: 'p', props: { children: 'This data was processed by Go WASM on the server:' } },
|
267
|
-
{
|
268
|
-
type: 'pre',
|
269
|
-
props: {
|
270
|
-
className: 'result',
|
271
|
-
children: JSON.stringify(initialState.processedData, null, 2)
|
272
|
-
}
|
273
|
-
}
|
274
|
-
]
|
275
|
-
}
|
276
|
-
}
|
277
|
-
].filter(Boolean)
|
278
|
-
}
|
279
|
-
},
|
280
|
-
{
|
281
|
-
type: 'footer',
|
282
|
-
props: {
|
283
|
-
children: [
|
284
|
-
{
|
285
|
-
type: 'p',
|
286
|
-
props: {
|
287
|
-
children: [
|
288
|
-
'Built with ',
|
289
|
-
{
|
290
|
-
type: 'a',
|
291
|
-
props: {
|
292
|
-
href: 'https://github.com/hamroun/frontend-hamroun',
|
293
|
-
target: '_blank',
|
294
|
-
rel: 'noopener noreferrer',
|
295
|
-
children: 'Frontend Hamroun'
|
296
|
-
}
|
297
|
-
},
|
298
|
-
' and Go WebAssembly'
|
299
|
-
]
|
300
|
-
}
|
301
|
-
}
|
302
|
-
]
|
303
|
-
}
|
304
|
-
}
|
305
|
-
]
|
306
|
-
}
|
307
|
-
});
|
308
|
-
|
309
|
-
// Render the app
|
310
|
-
const content = renderToString(App({ initialState }));
|
311
|
-
|
312
|
-
// Send HTML with server-side rendered content
|
313
|
-
res.send(`<!DOCTYPE html>
|
314
|
-
<html lang="en">
|
315
|
-
<head>
|
316
|
-
<meta charset="UTF-8">
|
317
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
318
|
-
<title>Frontend Hamroun + Go WebAssembly</title>
|
319
|
-
<style>
|
320
|
-
:root {
|
321
|
-
--primary-color: #0070f3;
|
322
|
-
--secondary-color: #0051cc;
|
323
|
-
--background: #f9f9f9;
|
324
|
-
--text-color: #333;
|
325
|
-
--card-background: #fff;
|
326
|
-
--border-color: #eaeaea;
|
327
|
-
--error-color: #f44336;
|
328
|
-
--success-color: #4caf50;
|
329
|
-
}
|
330
|
-
|
331
|
-
* {
|
332
|
-
box-sizing: border-box;
|
333
|
-
}
|
334
|
-
|
335
|
-
body {
|
336
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
337
|
-
background-color: var(--background);
|
338
|
-
color: var(--text-color);
|
339
|
-
margin: 0;
|
340
|
-
padding: 0;
|
341
|
-
line-height: 1.6;
|
342
|
-
}
|
343
|
-
|
344
|
-
.app {
|
345
|
-
max-width: 900px;
|
346
|
-
margin: 0 auto;
|
347
|
-
padding: 20px;
|
348
|
-
}
|
349
|
-
|
350
|
-
header {
|
351
|
-
text-align: center;
|
352
|
-
margin-bottom: 2rem;
|
21
|
+
// Ensure all modules are loaded
|
22
|
+
if (!express || !path || !fs) {
|
23
|
+
if (isESM) {
|
24
|
+
express = (await import('express')).default;
|
25
|
+
path = await import('path');
|
26
|
+
fs = await import('fs');
|
353
27
|
}
|
354
|
-
|
355
|
-
header h1 {
|
356
|
-
margin-bottom: 0.5rem;
|
357
|
-
color: var(--primary-color);
|
358
|
-
}
|
359
|
-
|
360
|
-
.rendering-info {
|
361
|
-
display: flex;
|
362
|
-
gap: 10px;
|
363
|
-
justify-content: center;
|
364
|
-
margin-top: 10px;
|
365
|
-
}
|
366
|
-
|
367
|
-
.badge {
|
368
|
-
display: inline-block;
|
369
|
-
padding: 4px 8px;
|
370
|
-
border-radius: 4px;
|
371
|
-
background-color: #eaeaea;
|
372
|
-
color: #666;
|
373
|
-
font-size: 0.8rem;
|
374
|
-
}
|
375
|
-
|
376
|
-
.badge.active {
|
377
|
-
background-color: var(--success-color);
|
378
|
-
color: white;
|
379
|
-
}
|
380
|
-
|
381
|
-
.card {
|
382
|
-
background-color: var(--card-background);
|
383
|
-
border: 1px solid var(--border-color);
|
384
|
-
border-radius: 8px;
|
385
|
-
padding: 1.5rem;
|
386
|
-
margin-bottom: 2rem;
|
387
|
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
388
|
-
}
|
389
|
-
|
390
|
-
.result {
|
391
|
-
background-color: #f0f7ff;
|
392
|
-
padding: 1rem;
|
393
|
-
border-radius: 4px;
|
394
|
-
margin-top: 1rem;
|
395
|
-
white-space: pre-wrap;
|
396
|
-
overflow-x: auto;
|
397
|
-
font-family: monospace;
|
398
|
-
font-size: 0.9rem;
|
399
|
-
}
|
400
|
-
|
401
|
-
.error {
|
402
|
-
background-color: #ffebee;
|
403
|
-
color: #c62828;
|
404
|
-
padding: 0.75rem;
|
405
|
-
border-radius: 4px;
|
406
|
-
margin-top: 0.5rem;
|
407
|
-
}
|
408
|
-
|
409
|
-
footer {
|
410
|
-
margin-top: 3rem;
|
411
|
-
text-align: center;
|
412
|
-
color: #666;
|
413
|
-
padding: 1rem 0;
|
414
|
-
border-top: 1px solid var(--border-color);
|
415
|
-
}
|
416
|
-
|
417
|
-
footer a {
|
418
|
-
color: var(--primary-color);
|
419
|
-
text-decoration: none;
|
420
|
-
}
|
421
|
-
</style>
|
422
|
-
</head>
|
423
|
-
<body>
|
424
|
-
<div id="root">${content}</div>
|
425
|
-
<script>
|
426
|
-
// Store initial state from server
|
427
|
-
window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};
|
428
|
-
|
429
|
-
// Client-side hydration script
|
430
|
-
document.addEventListener('DOMContentLoaded', function() {
|
431
|
-
// Mark the app as hydrated
|
432
|
-
const badges = document.querySelectorAll('.badge');
|
433
|
-
if (badges.length >= 2) {
|
434
|
-
badges[0].classList.remove('active');
|
435
|
-
badges[1].classList.add('active');
|
436
|
-
}
|
437
|
-
|
438
|
-
// Load WebAssembly if available
|
439
|
-
if (typeof WebAssembly !== 'undefined') {
|
440
|
-
const wasmExecScript = document.createElement('script');
|
441
|
-
wasmExecScript.src = '/wasm/wasm_exec.js';
|
442
|
-
wasmExecScript.onload = loadWasmModule;
|
443
|
-
document.head.appendChild(wasmExecScript);
|
444
|
-
}
|
445
|
-
|
446
|
-
function loadWasmModule() {
|
447
|
-
if (typeof Go !== 'function') return;
|
448
|
-
|
449
|
-
const go = new Go();
|
450
|
-
fetch('/wasm/example.wasm')
|
451
|
-
.then(response => response.arrayBuffer())
|
452
|
-
.then(bytes => WebAssembly.instantiate(bytes, go.importObject))
|
453
|
-
.then(result => {
|
454
|
-
// Run the Go instance
|
455
|
-
go.run(result.instance);
|
456
|
-
|
457
|
-
// Add demo component when WebAssembly is loaded
|
458
|
-
addWasmDemo();
|
459
|
-
})
|
460
|
-
.catch(err => {
|
461
|
-
console.error('Error loading WebAssembly:', err);
|
462
|
-
});
|
463
|
-
}
|
464
|
-
|
465
|
-
function addWasmDemo() {
|
466
|
-
if (typeof window.goAdd !== 'function') return;
|
467
|
-
|
468
|
-
const mainElement = document.querySelector('main');
|
469
|
-
const demoCard = document.createElement('div');
|
470
|
-
demoCard.className = 'card';
|
471
|
-
demoCard.innerHTML = \`
|
472
|
-
<h2>Client-Side WebAssembly Demo</h2>
|
473
|
-
<div>
|
474
|
-
<p>Try using the WebAssembly module in the browser:</p>
|
475
|
-
<div style="display:flex;gap:8px;align-items:center;margin-bottom:16px;">
|
476
|
-
<input type="number" id="num1" value="10" style="padding:8px;width:80px;">
|
477
|
-
<span>+</span>
|
478
|
-
<input type="number" id="num2" value="5" style="padding:8px;width:80px;">
|
479
|
-
<button onclick="calculateSum()" style="background-color:var(--primary-color);color:white;border:none;padding:8px 16px;border-radius:4px;cursor:pointer;">Calculate</button>
|
480
|
-
</div>
|
481
|
-
<div id="result"></div>
|
482
|
-
</div>
|
483
|
-
\`;
|
484
|
-
|
485
|
-
mainElement.appendChild(demoCard);
|
486
|
-
|
487
|
-
// Add calculation function
|
488
|
-
window.calculateSum = function() {
|
489
|
-
const num1 = parseInt(document.getElementById('num1').value) || 0;
|
490
|
-
const num2 = parseInt(document.getElementById('num2').value) || 0;
|
491
|
-
|
492
|
-
try {
|
493
|
-
// Call Go function
|
494
|
-
const sum = window.goAdd(num1, num2);
|
495
|
-
document.getElementById('result').innerHTML =
|
496
|
-
\`<p>Result: <strong>\${sum}</strong> (calculated by Go WebAssembly)</p>\`;
|
497
|
-
} catch (error) {
|
498
|
-
document.getElementById('result').innerHTML =
|
499
|
-
\`<p class="error">Error: \${error.message}</p>\`;
|
500
|
-
}
|
501
|
-
};
|
502
|
-
}
|
503
|
-
});
|
504
|
-
</script>
|
505
|
-
</body>
|
506
|
-
</html>`);
|
507
|
-
} catch (error) {
|
508
|
-
console.error('Server rendering error:', error);
|
509
|
-
res.status(500).send(`
|
510
|
-
<h1>Server Error</h1>
|
511
|
-
<p>${error.message}</p>
|
512
|
-
<pre>${error.stack}</pre>
|
513
|
-
`);
|
514
|
-
}
|
515
|
-
}
|
516
|
-
|
517
|
-
// Configure Express file serving
|
518
|
-
app.get('/wasm/wasm_exec.js', (req, res) => {
|
519
|
-
res.sendFile(WASM_EXEC_NODE_PATH);
|
520
|
-
});
|
521
|
-
|
522
|
-
app.get('/wasm/example.wasm', (req, res) => {
|
523
|
-
res.sendFile(WASM_PATH);
|
524
|
-
});
|
525
|
-
|
526
|
-
// Dynamic Go code generation endpoint
|
527
|
-
app.post('/api/generate-wasm', async (req, res) => {
|
528
|
-
try {
|
529
|
-
const { functionName, functionBody, inputTypes, returnType } = req.body;
|
530
|
-
|
531
|
-
if (!functionName || !functionBody) {
|
532
|
-
return res.status(400).json({ error: 'Missing required fields: functionName and functionBody' });
|
533
|
-
}
|
534
|
-
|
535
|
-
// Create Go source directory if it doesn't exist
|
536
|
-
const goSourceDir = path.join(__dirname, 'src', 'wasm');
|
537
|
-
if (!fs.existsSync(goSourceDir)) {
|
538
|
-
fs.mkdirSync(goSourceDir, { recursive: true });
|
539
|
-
}
|
540
|
-
|
541
|
-
// Create the Go file with the user-provided function
|
542
|
-
const goFilePath = path.join(goSourceDir, `${functionName}.go`);
|
543
|
-
|
544
|
-
// Generate Go code with the user's function
|
545
|
-
const goCode = generateGoCode(functionName, functionBody, inputTypes, returnType);
|
546
|
-
|
547
|
-
// Write Go file
|
548
|
-
fs.writeFileSync(goFilePath, goCode);
|
549
|
-
console.log(`Created Go file: ${goFilePath}`);
|
550
|
-
|
551
|
-
// Build the WASM module
|
552
|
-
const wasmOutputDir = path.join(__dirname, 'public', 'wasm');
|
553
|
-
if (!fs.existsSync(wasmOutputDir)) {
|
554
|
-
fs.mkdirSync(wasmOutputDir, { recursive: true });
|
555
|
-
}
|
556
|
-
|
557
|
-
const wasmFilePath = path.join(wasmOutputDir, `${functionName}.wasm`);
|
558
|
-
|
559
|
-
// Import the build function from build-wasm.js
|
560
|
-
const { buildGoFile } = await import('./build-wasm.js');
|
561
|
-
|
562
|
-
// Build the Go file to WASM
|
563
|
-
const result = await buildGoFile(goFilePath, wasmFilePath);
|
564
|
-
|
565
|
-
if (result.success) {
|
566
|
-
res.json({
|
567
|
-
success: true,
|
568
|
-
message: 'WebAssembly module generated successfully',
|
569
|
-
function: functionName,
|
570
|
-
wasmPath: `/wasm/${functionName}.wasm`,
|
571
|
-
jsUsage: `
|
572
|
-
// Load and use the WASM module:
|
573
|
-
const wasm = await loadGoWasm('/wasm/${functionName}.wasm');
|
574
|
-
const result = wasm.functions.${functionName}(...args);
|
575
|
-
`
|
576
|
-
});
|
577
|
-
} else {
|
578
|
-
res.status(500).json({
|
579
|
-
success: false,
|
580
|
-
error: result.error,
|
581
|
-
message: 'Failed to build WebAssembly module'
|
582
|
-
});
|
583
|
-
}
|
584
|
-
} catch (error) {
|
585
|
-
console.error('Error generating WASM:', error);
|
586
|
-
res.status(500).json({ error: error.message });
|
587
28
|
}
|
588
|
-
});
|
589
29
|
|
590
|
-
|
591
|
-
|
592
|
-
// Default input types to interface{} (any) if not provided
|
593
|
-
const goInputTypes = inputTypes.length > 0
|
594
|
-
? inputTypes.map(t => mapJsTypeToGo(t)).join(', ')
|
595
|
-
: 'interface{}, interface{}';
|
30
|
+
const app = express();
|
31
|
+
const PORT = process.env.PORT || 3000;
|
596
32
|
|
597
|
-
//
|
598
|
-
|
33
|
+
// Serve static files from public directory
|
34
|
+
app.use(express.static(path.join(__dirname, 'public')));
|
35
|
+
|
36
|
+
// Special handling for wasm files to ensure correct MIME type
|
37
|
+
app.get('*.wasm', (req, res, next) => {
|
38
|
+
res.set('Content-Type', 'application/wasm');
|
39
|
+
next();
|
40
|
+
});
|
599
41
|
|
600
|
-
return
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
// Implementation
|
619
|
-
${functionBody}
|
620
|
-
}
|
621
|
-
|
622
|
-
// Example Go function to be called from JavaScript
|
623
|
-
func goAdd(this js.Value, args []js.Value) interface{} {
|
624
|
-
if len(args) != 2 {
|
625
|
-
return js.ValueOf("Error: Expected two arguments")
|
626
|
-
}
|
627
|
-
|
628
|
-
a := args[0].Int()
|
629
|
-
b := args[1].Int()
|
630
|
-
return js.ValueOf(a + b)
|
42
|
+
// Always return index.html for client-side routing
|
43
|
+
app.get('*', (req, res) => {
|
44
|
+
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
45
|
+
});
|
46
|
+
|
47
|
+
// Start the server
|
48
|
+
app.listen(PORT, () => {
|
49
|
+
console.log(`
|
50
|
+
┌────────────────────────────────────────────────────┐
|
51
|
+
│ │
|
52
|
+
│ Go WASM Demo Server running on port ${PORT} │
|
53
|
+
│ │
|
54
|
+
│ Local: http://localhost:${PORT} │
|
55
|
+
│ │
|
56
|
+
└────────────────────────────────────────────────────┘
|
57
|
+
`);
|
58
|
+
});
|
631
59
|
}
|
632
60
|
|
633
|
-
|
634
|
-
func goProcessData(this js.Value, args []js.Value) interface{} {
|
635
|
-
if len(args) == 0 {
|
636
|
-
return js.ValueOf("Error: Expected at least one argument")
|
637
|
-
}
|
638
|
-
|
639
|
-
// Get input data
|
640
|
-
data := args[0]
|
641
|
-
if data.Type() != js.TypeObject {
|
642
|
-
return js.ValueOf("Error: Expected JSON object")
|
643
|
-
}
|
644
|
-
|
645
|
-
// Convert JS object to Go map
|
646
|
-
jsonStr := js.Global().Get("JSON").Call("stringify", data).String()
|
647
|
-
var inputMap map[string]interface{}
|
648
|
-
if err := json.Unmarshal([]byte(jsonStr), &inputMap); err != nil {
|
649
|
-
return js.ValueOf(fmt.Sprintf("Error parsing JSON: %s", err.Error()))
|
650
|
-
}
|
61
|
+
startServer();
|
651
62
|
|
652
|
-
|
653
|
-
|
654
|
-
inputMap["processor"] = "Go WASM"
|
63
|
+
// Module export for both ESM and CommonJS
|
64
|
+
const server = { startServer };
|
655
65
|
|
656
|
-
|
657
|
-
|
658
|
-
sum := 0.0
|
659
|
-
for _, v := range values {
|
660
|
-
if num, ok := v.(float64); ok {
|
661
|
-
sum += num
|
662
|
-
}
|
663
|
-
}
|
664
|
-
inputMap["sum"] = sum
|
665
|
-
}
|
666
|
-
|
667
|
-
// Convert back to JS
|
668
|
-
resultJSON, err := json.Marshal(inputMap)
|
669
|
-
if err != nil {
|
670
|
-
return js.ValueOf(fmt.Sprintf("Error generating JSON: %s", err.Error()))
|
671
|
-
}
|
672
|
-
|
673
|
-
return js.ValueOf(string(resultJSON))
|
674
|
-
}
|
675
|
-
|
676
|
-
func main() {
|
677
|
-
fmt.Println("Go WASM Module initialized")
|
678
|
-
|
679
|
-
// Register functions to be callable from JavaScript
|
680
|
-
js.Global().Set("goAdd", js.FuncOf(goAdd))
|
681
|
-
js.Global().Set("goProcessData", js.FuncOf(goProcessData))
|
682
|
-
js.Global().Set("${functionName}", js.FuncOf(${functionName}))
|
683
|
-
|
684
|
-
// Keep the program running
|
685
|
-
<-make(chan bool)
|
686
|
-
}
|
687
|
-
`;
|
688
|
-
}
|
689
|
-
|
690
|
-
// Map JavaScript types to Go types
|
691
|
-
function mapJsTypeToGo(jsType) {
|
692
|
-
const typeMap = {
|
693
|
-
'string': 'string',
|
694
|
-
'number': 'float64',
|
695
|
-
'integer': 'int',
|
696
|
-
'boolean': 'bool',
|
697
|
-
'object': 'map[string]interface{}',
|
698
|
-
'array': '[]interface{}',
|
699
|
-
'any': 'interface{}'
|
700
|
-
};
|
701
|
-
|
702
|
-
return typeMap[jsType] || 'interface{}';
|
66
|
+
if (typeof module !== 'undefined' && module.exports) {
|
67
|
+
module.exports = server;
|
703
68
|
}
|
704
69
|
|
705
|
-
|
706
|
-
startServer().catch(err => {
|
707
|
-
console.error('Failed to start server:', err);
|
708
|
-
process.exit(1);
|
709
|
-
});
|
70
|
+
export default server;
|