baraqex 2.0.7 → 2.0.8
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/package.json +1 -1
- package/templates/fullstack-app/build-wasm.js +2 -2
- package/templates/fullstack-app/build.mjs +3 -2
- package/templates/fullstack-app/package.json +1 -1
- package/templates/fullstack-app/server.ts +43 -20
- package/templates/fullstack-app/src/App.tsx +1 -1
- package/templates/fullstack-app/src/main.tsx +0 -1
- package/templates/fullstack-app/src/pages/WasmDemo.tsx +23 -3
package/package.json
CHANGED
|
@@ -43,9 +43,9 @@ function buildWasm() {
|
|
|
43
43
|
execSync('go mod init wasm-example', { cwd: goDir, stdio: 'inherit' });
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
// Compile with the js/wasm target
|
|
46
|
+
// Compile with the js/wasm target; -s -w strips debug info to shrink the module
|
|
47
47
|
execSync(
|
|
48
|
-
|
|
48
|
+
'go build -ldflags="-s -w" -o "' + wasmFile + '" main.go',
|
|
49
49
|
{
|
|
50
50
|
cwd: goDir,
|
|
51
51
|
stdio: 'inherit',
|
|
@@ -104,7 +104,7 @@ async function buildServer() {
|
|
|
104
104
|
target: ['node18'],
|
|
105
105
|
format: 'esm',
|
|
106
106
|
outfile: path.join(outdir, 'server.js'),
|
|
107
|
-
external: ['express', 'cors', 'compression', 'socket.io', 'dotenv'],
|
|
107
|
+
external: ['express', 'cors', 'compression', 'socket.io', 'dotenv', 'postcss', 'tailwindcss', 'autoprefixer', 'chokidar'],
|
|
108
108
|
plugins: [
|
|
109
109
|
{
|
|
110
110
|
name: 'external-all',
|
|
@@ -114,7 +114,8 @@ async function buildServer() {
|
|
|
114
114
|
}
|
|
115
115
|
],
|
|
116
116
|
define: { 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development') },
|
|
117
|
-
sourcemap: false
|
|
117
|
+
sourcemap: false,
|
|
118
|
+
minify: process.env.NODE_ENV === 'production'
|
|
118
119
|
});
|
|
119
120
|
console.log('✅ Server build complete');
|
|
120
121
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"start": "node dist/server.js",
|
|
9
9
|
"prod": "npm run build && npm run start",
|
|
10
10
|
"dev": "tsx server.ts",
|
|
11
|
-
"build": "node build.mjs",
|
|
11
|
+
"build": "cross-env NODE_ENV=production node build.mjs",
|
|
12
12
|
"build:wasm": "node build-wasm.js",
|
|
13
13
|
"build:all": "node build-wasm.js && node build.mjs"
|
|
14
14
|
},
|
|
@@ -28,7 +28,17 @@ const io = new SocketServer(httpServer, {
|
|
|
28
28
|
|
|
29
29
|
app.use(express.json());
|
|
30
30
|
app.use(express.urlencoded({ extended: true }));
|
|
31
|
-
app.use(compression(
|
|
31
|
+
app.use(compression({
|
|
32
|
+
filter: (req, res) => {
|
|
33
|
+
const type = res.getHeader('Content-Type');
|
|
34
|
+
if (type === undefined) return true;
|
|
35
|
+
// Compress text-like responses plus binary assets that gzip well (wasm, svg, fonts)
|
|
36
|
+
if (/^(text\/|application\/(json|javascript|xml|wasm)|image\/svg|font\/)/i.test(String(type))) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
return compression.filter(req, res);
|
|
40
|
+
}
|
|
41
|
+
}));
|
|
32
42
|
app.use(cors());
|
|
33
43
|
|
|
34
44
|
// ---------------------------------------------------------------------------
|
|
@@ -78,9 +88,15 @@ app.get('/api/posts', (req: Request, res: Response) => {
|
|
|
78
88
|
// ---------------------------------------------------------------------------
|
|
79
89
|
// Static assets: built client bundle + public (incl. WASM output)
|
|
80
90
|
// ---------------------------------------------------------------------------
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
// Dev must never cache rebuilt bundles; prod can cache them briefly. WASM files
|
|
92
|
+
// are rebuilt via `npm run build:wasm` and are safe to cache for a long time.
|
|
93
|
+
const buildCache = isDev
|
|
94
|
+
? { setHeaders: (res: Response) => res.setHeader('Cache-Control', 'no-store') }
|
|
95
|
+
: { maxAge: '1h' };
|
|
96
|
+
const wasmCache = { maxAge: '1y', immutable: true };
|
|
97
|
+
app.use('/build', express.static(path.join(__dirname, 'build'), buildCache));
|
|
98
|
+
app.use('/wasm', express.static(path.join(__dirname, 'public', 'wasm'), wasmCache));
|
|
99
|
+
app.use(express.static(path.join(__dirname, 'public'), buildCache));
|
|
84
100
|
|
|
85
101
|
// ---------------------------------------------------------------------------
|
|
86
102
|
// Dev mode: esbuild context (bundles src + rebuilds on change)
|
|
@@ -135,23 +151,18 @@ async function buildStyles() {
|
|
|
135
151
|
console.log('✅ styles.css built');
|
|
136
152
|
}
|
|
137
153
|
|
|
138
|
-
// Build the Tailwind stylesheet
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
154
|
+
// Build the Tailwind stylesheet and (in dev) rebuild it on change. In prod the
|
|
155
|
+
// file is already written by `npm run build`, so only (re)build when missing.
|
|
156
|
+
await Promise.all([
|
|
157
|
+
isDev || !existsSync(path.join(__dirname, 'build', 'styles.css')) ? buildStyles() : Promise.resolve(),
|
|
158
|
+
isDev ? setupEsbuild() : Promise.resolve()
|
|
159
|
+
]);
|
|
145
160
|
|
|
146
|
-
// Only
|
|
147
|
-
//
|
|
161
|
+
// Only used when the Go WASM module has been built (`npm run build:wasm`,
|
|
162
|
+
// requires Go). Off by default so dev works without Go.
|
|
148
163
|
const hasWasm = existsSync(path.join(__dirname, 'public', 'wasm', 'example.wasm'))
|
|
149
164
|
&& existsSync(path.join(__dirname, 'public', 'wasm', 'wasm_exec.js'));
|
|
150
165
|
|
|
151
|
-
const wasmScripts = hasWasm
|
|
152
|
-
? '<script src="/wasm/wasm_exec.js"></script>'
|
|
153
|
-
: '<script>console.warn("Go WASM not built — run `npm run build:wasm` (requires Go)");</script>';
|
|
154
|
-
|
|
155
166
|
// ---------------------------------------------------------------------------
|
|
156
167
|
// SSR: render the App on the server, then hydrate on the client
|
|
157
168
|
// ---------------------------------------------------------------------------
|
|
@@ -166,11 +177,20 @@ app.get('*', async (req: Request, res: Response, next: NextFunction) => {
|
|
|
166
177
|
route,
|
|
167
178
|
serverTime: new Date().toISOString(),
|
|
168
179
|
serverRendered: true,
|
|
169
|
-
users: store.users
|
|
180
|
+
users: store.users,
|
|
181
|
+
hasWasm
|
|
170
182
|
};
|
|
171
183
|
|
|
172
184
|
const content = await renderToString(jsx(App, { route, initialState }));
|
|
173
185
|
|
|
186
|
+
// Load the Go runtime only on the /wasm page — Home/About never pay for it.
|
|
187
|
+
// Direct visits get it server-side; SPA navigation loads it dynamically.
|
|
188
|
+
const wasmRuntimeScript = req.path.startsWith('/wasm')
|
|
189
|
+
? (hasWasm
|
|
190
|
+
? '<script src="/wasm/wasm_exec.js" defer></script>'
|
|
191
|
+
: '<script>console.warn("Go WASM not built — run `npm run build:wasm` (requires Go)");</script>')
|
|
192
|
+
: '';
|
|
193
|
+
|
|
174
194
|
const html = `<!DOCTYPE html>
|
|
175
195
|
<html lang="en">
|
|
176
196
|
<head>
|
|
@@ -183,8 +203,8 @@ app.get('*', async (req: Request, res: Response, next: NextFunction) => {
|
|
|
183
203
|
</head>
|
|
184
204
|
<body>
|
|
185
205
|
<div id="root">${content}</div>
|
|
186
|
-
${
|
|
187
|
-
${isDev ? '<script src="/socket.io/socket.io.js"></script>' : ''}
|
|
206
|
+
${wasmRuntimeScript}
|
|
207
|
+
${isDev ? '<script src="/socket.io/socket.io.js" defer></script>' : ''}
|
|
188
208
|
<script src="/build/main.js" type="module"></script>
|
|
189
209
|
</body>
|
|
190
210
|
</html>`;
|
|
@@ -215,6 +235,9 @@ if (isDev) {
|
|
|
215
235
|
watcher.on('change', async (changedPath) => {
|
|
216
236
|
console.log(`[Watcher] Changed: ${changedPath}`);
|
|
217
237
|
try {
|
|
238
|
+
if (changedPath.endsWith('styles.css')) {
|
|
239
|
+
await buildStyles();
|
|
240
|
+
}
|
|
218
241
|
await esbuildContext?.rebuild();
|
|
219
242
|
io.emit('reload');
|
|
220
243
|
console.log('[Watcher] Rebuild + reload sent');
|
|
@@ -67,7 +67,7 @@ export function App({ route, initialState }: AppProps) {
|
|
|
67
67
|
<main className="flex-grow max-w-5xl w-full mx-auto px-4 py-8">
|
|
68
68
|
{current === 'home' && <HomePage initialState={initialState} />}
|
|
69
69
|
{current === 'about' && <AboutPage />}
|
|
70
|
-
{current === 'wasm' && <WasmDemo />}
|
|
70
|
+
{current === 'wasm' && <WasmDemo hasWasm={!!initialState?.hasWasm} />}
|
|
71
71
|
</main>
|
|
72
72
|
|
|
73
73
|
<footer className="bg-gray-800 text-white">
|
|
@@ -1,19 +1,39 @@
|
|
|
1
1
|
import { jsx, useState, useEffect, batchUpdates } from 'baraqex';
|
|
2
2
|
import { loadGoWasm } from 'baraqex';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Ensure the Go runtime (wasm_exec.js) is available before loading the module.
|
|
6
|
+
* The SSR page preloads it on direct visits; SPA navigation loads it on demand.
|
|
7
|
+
*/
|
|
8
|
+
function ensureWasmRuntime(): Promise<void> {
|
|
9
|
+
const w = window as any;
|
|
10
|
+
if (w.Go) return Promise.resolve();
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const script = document.createElement('script');
|
|
13
|
+
script.src = '/wasm/wasm_exec.js';
|
|
14
|
+
script.async = true;
|
|
15
|
+
script.onload = () => resolve();
|
|
16
|
+
script.onerror = () => reject(new Error('/wasm/wasm_exec.js could not be loaded'));
|
|
17
|
+
document.head.appendChild(script);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function WasmDemo({ hasWasm = false }: { hasWasm?: boolean }) {
|
|
5
22
|
const [ready, setReady] = useState(false);
|
|
6
|
-
const [status, setStatus] = useState('Preparing Go WASM…');
|
|
7
|
-
const [output, setOutput] = useState('');
|
|
23
|
+
const [status, setStatus] = useState(hasWasm ? 'Preparing Go WASM…' : 'Go WASM not built');
|
|
24
|
+
const [output, setOutput] = useState(hasWasm ? '' : 'Run `npm run build:wasm` (requires Go) to enable this page.');
|
|
8
25
|
const [a, setA] = useState('5');
|
|
9
26
|
const [b, setB] = useState('7');
|
|
10
27
|
const [n, setN] = useState('10');
|
|
11
28
|
|
|
12
29
|
useEffect(() => {
|
|
30
|
+
if (!hasWasm) return;
|
|
13
31
|
let cancelled = false;
|
|
14
32
|
(async () => {
|
|
15
33
|
try {
|
|
16
34
|
setStatus('Loading Go WASM module…');
|
|
35
|
+
await ensureWasmRuntime();
|
|
36
|
+
if (cancelled) return;
|
|
17
37
|
await loadGoWasm('/wasm/example.wasm');
|
|
18
38
|
if (cancelled) return;
|
|
19
39
|
batchUpdates(() => {
|