hadars 0.1.35 → 0.1.36
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/README.md +77 -0
- package/cli-lib.ts +82 -5
- package/dist/chunk-OID7K4D3.js +1060 -0
- package/dist/{chunk-OS3V4CPN.js → chunk-OZUZS2PD.js} +4 -4
- package/dist/chunk-UNQSQIOO.js +60 -0
- package/dist/cli.js +255 -258
- package/dist/hadars-ScRgKezP.d.cts +176 -0
- package/dist/hadars-ScRgKezP.d.ts +176 -0
- package/dist/index.cjs +11 -19
- package/dist/index.d.cts +105 -0
- package/dist/index.d.ts +4 -175
- package/dist/index.js +14 -42
- package/dist/{jsx-runtime-97ca74a5.d.ts → jsx-runtime-BOYrELJb.d.cts} +1 -1
- package/dist/jsx-runtime-BOYrELJb.d.ts +18 -0
- package/dist/lambda.cjs +1405 -0
- package/dist/lambda.d.cts +93 -0
- package/dist/lambda.d.ts +93 -0
- package/dist/lambda.js +499 -0
- package/dist/loader.cjs +22 -44
- package/dist/slim-react/index.cjs +39 -68
- package/dist/slim-react/index.d.cts +190 -0
- package/dist/slim-react/index.d.ts +3 -3
- package/dist/slim-react/index.js +34 -1043
- package/dist/slim-react/jsx-runtime.cjs +2 -4
- package/dist/slim-react/jsx-runtime.d.cts +1 -0
- package/dist/slim-react/jsx-runtime.d.ts +1 -1
- package/dist/slim-react/jsx-runtime.js +1 -1
- package/dist/ssr-render-worker.js +44 -78
- package/dist/ssr-watch.js +3 -6
- package/dist/utils/Head.tsx +2 -2
- package/package.json +11 -6
- package/src/build.ts +5 -166
- package/src/lambda.ts +268 -0
- package/src/utils/Head.tsx +2 -2
- package/src/utils/ssrHandler.ts +164 -0
package/README.md
CHANGED
|
@@ -88,6 +88,9 @@ hadars build
|
|
|
88
88
|
|
|
89
89
|
# Serve the production build
|
|
90
90
|
hadars run
|
|
91
|
+
|
|
92
|
+
# Bundle the app into a single self-contained Lambda .mjs file
|
|
93
|
+
hadars export lambda [output.mjs]
|
|
91
94
|
```
|
|
92
95
|
|
|
93
96
|
## Features
|
|
@@ -142,6 +145,8 @@ const UserCard = ({ userId }: { userId: string }) => {
|
|
|
142
145
|
| `proxyCORS` | `boolean` | - | Inject CORS headers on proxied responses |
|
|
143
146
|
| `define` | `Record` | - | Compile-time constants for rspack's DefinePlugin |
|
|
144
147
|
| `swcPlugins` | `array` | - | Extra SWC plugins (e.g. Relay compiler) |
|
|
148
|
+
|
|
149
|
+
> **Environment variables in client bundles:** `process.env.*` references in client-side code are replaced at build time by rspack's DefinePlugin. They are **not** read at runtime in the browser. Use the `define` option to expose specific values, or keep env var access inside `getInitProps` / `fetch` (server-only code) so they are resolved at request time.
|
|
145
150
|
| `moduleRules` | `array` | - | Extra rspack module rules appended to the built-in set (client + SSR) |
|
|
146
151
|
| `fetch` | `function` | - | Custom fetch handler; return a `Response` to short-circuit SSR |
|
|
147
152
|
| `websocket` | `object` | - | WebSocket handler (Bun only) |
|
|
@@ -184,6 +189,78 @@ const config: HadarsOptions = {
|
|
|
184
189
|
export default config;
|
|
185
190
|
```
|
|
186
191
|
|
|
192
|
+
## AWS Lambda
|
|
193
|
+
|
|
194
|
+
hadars apps can be deployed to AWS Lambda backed by API Gateway (HTTP API v2 or REST API v1).
|
|
195
|
+
|
|
196
|
+
### File-based deployment
|
|
197
|
+
|
|
198
|
+
Run `hadars build`, then create a Lambda entry file that imports `createLambdaHandler` from `hadars/lambda`:
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
// lambda-entry.ts
|
|
202
|
+
import { createLambdaHandler } from 'hadars/lambda';
|
|
203
|
+
import config from './hadars.config';
|
|
204
|
+
|
|
205
|
+
export const handler = createLambdaHandler(config);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Deploy the entire project directory (including the `.hadars/` output folder) as your Lambda package. Static assets (JS, CSS, fonts) under `.hadars/static/` are served directly by the Lambda handler — for production, front the function with CloudFront and route static paths to an S3 origin instead.
|
|
209
|
+
|
|
210
|
+
### Single-file bundle
|
|
211
|
+
|
|
212
|
+
`hadars export lambda` produces a completely self-contained `.mjs` file that requires no `.hadars/` directory on disk. The SSR module and HTML template are inlined at build time. Static assets must be served separately (S3 + CloudFront).
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
# Outputs lambda.mjs in the current directory
|
|
216
|
+
hadars export lambda
|
|
217
|
+
|
|
218
|
+
# Custom output path
|
|
219
|
+
hadars export lambda dist/lambda.mjs
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The command:
|
|
223
|
+
1. Runs `hadars build`
|
|
224
|
+
2. Generates a temporary entry shim with static imports of the SSR module and `out.html`
|
|
225
|
+
3. Bundles everything into a single ESM `.mjs` with esbuild (`.html` files loaded as text, Node built-ins kept external)
|
|
226
|
+
4. Prints deploy instructions
|
|
227
|
+
|
|
228
|
+
**Deploy steps:**
|
|
229
|
+
1. Upload the output `.mjs` as your Lambda function code
|
|
230
|
+
2. Set the handler to `index.handler`
|
|
231
|
+
3. Upload `.hadars/static/` assets to S3 and serve via CloudFront
|
|
232
|
+
|
|
233
|
+
### `createLambdaHandler` API
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
import { createLambdaHandler, type LambdaBundled } from 'hadars/lambda';
|
|
237
|
+
|
|
238
|
+
// File-based (reads .hadars/ at runtime)
|
|
239
|
+
export const handler = createLambdaHandler(config);
|
|
240
|
+
|
|
241
|
+
// Bundled (zero I/O — for use with `hadars export lambda` output)
|
|
242
|
+
import * as ssrModule from './.hadars/index.ssr.js';
|
|
243
|
+
import outHtml from './.hadars/static/out.html';
|
|
244
|
+
export const handler = createLambdaHandler(config, { ssrModule, outHtml });
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
| Parameter | Type | Description |
|
|
248
|
+
|---|---|---|
|
|
249
|
+
| `options` | `HadarsOptions` | Same config object used for `dev`/`run` |
|
|
250
|
+
| `bundled` | `LambdaBundled` *(optional)* | Pre-loaded SSR module + HTML; eliminates all runtime file I/O |
|
|
251
|
+
|
|
252
|
+
The handler accepts both **API Gateway HTTP API (v2)** and **REST API (v1)** event formats. Binary responses (images, fonts, pre-compressed assets) are automatically base64-encoded.
|
|
253
|
+
|
|
254
|
+
### Environment variables on Lambda
|
|
255
|
+
|
|
256
|
+
`process.env` is available in all server-side code (`getInitProps`, `fetch`, `cache`, etc.) and is resolved at runtime per invocation — Lambda injects env vars into the process before your handler runs.
|
|
257
|
+
|
|
258
|
+
Client-side code (anything that runs in the browser) is an exception: `process.env.*` references are substituted **at build time** by rspack. They will not reflect Lambda env vars set after the build. Expose runtime values to the client by returning them from `getInitProps` instead, or use the `define` option for values that are known at build time.
|
|
259
|
+
|
|
260
|
+
### `useServerData` on Lambda
|
|
261
|
+
|
|
262
|
+
Client-side navigation sends a `GET <url>` request with `Accept: application/json` to refetch server data. The Lambda handler returns a JSON `{ serverData }` map for these requests — the same as the regular server does — so `useServerData` works identically in both deployment modes.
|
|
263
|
+
|
|
187
264
|
## slim-react
|
|
188
265
|
|
|
189
266
|
hadars ships its own lightweight React-compatible SSR renderer called **slim-react** (`src/slim-react/`). It replaces `react-dom/server` on the server side entirely.
|
package/cli-lib.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
|
-
import { mkdir, writeFile } from 'node:fs/promises'
|
|
3
|
-
import { resolve, join } from 'node:path'
|
|
2
|
+
import { mkdir, writeFile, unlink } from 'node:fs/promises'
|
|
3
|
+
import { resolve, join, dirname } from 'node:path'
|
|
4
|
+
import { tmpdir } from 'node:os'
|
|
4
5
|
import * as Hadars from './src/build'
|
|
5
6
|
import type { HadarsOptions } from './src/types/hadars'
|
|
6
7
|
|
|
@@ -42,7 +43,73 @@ async function loadConfig(configPath: string): Promise<HadarsOptions> {
|
|
|
42
43
|
return (mod && (mod.default ?? mod)) as HadarsOptions
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
// ── hadars
|
|
46
|
+
// ── hadars export lambda ────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
async function bundleLambda(
|
|
49
|
+
config: HadarsOptions,
|
|
50
|
+
configPath: string,
|
|
51
|
+
outputFile: string,
|
|
52
|
+
cwd: string,
|
|
53
|
+
): Promise<void> {
|
|
54
|
+
// 1. Ensure the hadars production build is up to date.
|
|
55
|
+
console.log('Building hadars project...')
|
|
56
|
+
await Hadars.build({ ...config, mode: 'production' })
|
|
57
|
+
|
|
58
|
+
// 2. Resolve paths.
|
|
59
|
+
const ssrBundle = resolve(cwd, '.hadars', 'index.ssr.js')
|
|
60
|
+
const outHtml = resolve(cwd, '.hadars', 'static', 'out.html')
|
|
61
|
+
|
|
62
|
+
if (!existsSync(ssrBundle)) {
|
|
63
|
+
console.error(`SSR bundle not found: ${ssrBundle}`)
|
|
64
|
+
process.exit(1)
|
|
65
|
+
}
|
|
66
|
+
if (!existsSync(outHtml)) {
|
|
67
|
+
console.error(`HTML template not found: ${outHtml}`)
|
|
68
|
+
process.exit(1)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 3. Write a temporary entry shim that statically imports the SSR module
|
|
72
|
+
// and the HTML template so esbuild can inline both.
|
|
73
|
+
const shimPath = join(tmpdir(), `hadars-lambda-shim-${Date.now()}.ts`)
|
|
74
|
+
const shim = [
|
|
75
|
+
`import * as ssrModule from ${JSON.stringify(ssrBundle)};`,
|
|
76
|
+
`import outHtml from ${JSON.stringify(outHtml)};`,
|
|
77
|
+
`import { createLambdaHandler } from 'hadars/lambda';`,
|
|
78
|
+
`import config from ${JSON.stringify(configPath)};`,
|
|
79
|
+
`export const handler = createLambdaHandler(config as any, { ssrModule: ssrModule as any, outHtml });`,
|
|
80
|
+
].join('\n') + '\n'
|
|
81
|
+
await writeFile(shimPath, shim, 'utf-8')
|
|
82
|
+
|
|
83
|
+
// 4. Bundle with esbuild.
|
|
84
|
+
try {
|
|
85
|
+
const { build: esbuild } = await import('esbuild')
|
|
86
|
+
console.log(`Bundling Lambda handler → ${outputFile}`)
|
|
87
|
+
await esbuild({
|
|
88
|
+
entryPoints: [shimPath],
|
|
89
|
+
bundle: true,
|
|
90
|
+
platform: 'node',
|
|
91
|
+
format: 'esm',
|
|
92
|
+
target: ['node20'],
|
|
93
|
+
outfile: outputFile,
|
|
94
|
+
sourcemap: false,
|
|
95
|
+
loader: { '.html': 'text', '.tsx': 'tsx', '.ts': 'ts' },
|
|
96
|
+
// Node built-ins are available in Lambda — mark them all external.
|
|
97
|
+
packages: 'external',
|
|
98
|
+
// Keep React external so there's only one instance across the bundle.
|
|
99
|
+
external: ['react', 'react-dom', '@rspack/*'],
|
|
100
|
+
})
|
|
101
|
+
console.log(`Lambda bundle written to ${outputFile}`)
|
|
102
|
+
console.log(`\nDeploy instructions:`)
|
|
103
|
+
console.log(` 1. Upload ${outputFile} as your Lambda function code`)
|
|
104
|
+
console.log(` 2. Set handler to: index.handler`)
|
|
105
|
+
console.log(` 3. Upload .hadars/static/ assets to S3 and serve them via CloudFront`)
|
|
106
|
+
console.log(` (the Lambda handler does not serve static JS/CSS — route those to S3)`)
|
|
107
|
+
} finally {
|
|
108
|
+
await unlink(shimPath).catch(() => {})
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
46
113
|
|
|
47
114
|
const TEMPLATES: Record<string, (name: string) => string> = {
|
|
48
115
|
'package.json': (name: string) => JSON.stringify({
|
|
@@ -313,7 +380,7 @@ Done! Next steps:
|
|
|
313
380
|
// ── CLI entry ─────────────────────────────────────────────────────────────────
|
|
314
381
|
|
|
315
382
|
function usage(): void {
|
|
316
|
-
console.log('Usage: hadars <new <name> | dev | build | run>')
|
|
383
|
+
console.log('Usage: hadars <new <name> | dev | build | run | export lambda [output.mjs]>')
|
|
317
384
|
}
|
|
318
385
|
|
|
319
386
|
export async function runCli(argv: string[], cwd = process.cwd()): Promise<void> {
|
|
@@ -334,7 +401,7 @@ export async function runCli(argv: string[], cwd = process.cwd()): Promise<void>
|
|
|
334
401
|
return
|
|
335
402
|
}
|
|
336
403
|
|
|
337
|
-
if (!cmd || !['dev', 'build', 'run'].includes(cmd)) {
|
|
404
|
+
if (!cmd || !['dev', 'build', 'run', 'export'].includes(cmd)) {
|
|
338
405
|
usage()
|
|
339
406
|
process.exit(1)
|
|
340
407
|
}
|
|
@@ -359,6 +426,16 @@ export async function runCli(argv: string[], cwd = process.cwd()): Promise<void>
|
|
|
359
426
|
await build(cfg);
|
|
360
427
|
console.log('Build complete')
|
|
361
428
|
process.exit(0)
|
|
429
|
+
case 'export': {
|
|
430
|
+
const subCmd = argv[3]
|
|
431
|
+
if (subCmd !== 'lambda') {
|
|
432
|
+
console.error(`Unknown export target: ${subCmd ?? '(none)'}. Did you mean: hadars export lambda`)
|
|
433
|
+
process.exit(1)
|
|
434
|
+
}
|
|
435
|
+
const outputFile = resolve(cwd, argv[4] ?? 'lambda.mjs')
|
|
436
|
+
await bundleLambda(cfg, configPath, outputFile, cwd)
|
|
437
|
+
process.exit(0)
|
|
438
|
+
}
|
|
362
439
|
case 'run':
|
|
363
440
|
console.log('Running project...')
|
|
364
441
|
await run(cfg);
|