dinou 6.1.1 ā 7.0.0
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/CHANGELOG.md +94 -0
- package/README.md +9461 -1080
- package/cli.js +392 -169
- package/dinou/adapters/bun.js +62 -0
- package/dinou/adapters/cloudflare.js +65 -0
- package/dinou/adapters/deno.js +48 -0
- package/dinou/adapters/netlify.js +85 -0
- package/dinou/adapters/node.mjs +31 -0
- package/dinou/bun/build.mjs +1077 -0
- package/dinou/cloudflare/build.mjs +1121 -0
- package/dinou/cloudflare/shims/child_process.js +14 -0
- package/dinou/cloudflare/shims/empty.js +2 -0
- package/dinou/cloudflare/shims/fs.js +99 -0
- package/dinou/cloudflare/shims/os.js +29 -0
- package/dinou/config.js +1 -0
- package/dinou/config.mjs +2 -0
- package/dinou/constants.js +29 -4
- package/dinou/core/babel-esm-loader.js +66 -37
- package/dinou/core/bailout-proxy.js +61 -0
- package/dinou/core/build-static-pages.js +309 -157
- package/dinou/core/cache-revalidate.js +471 -144
- package/dinou/core/cache-slot.js +387 -0
- package/dinou/core/client-redirect.jsx +12 -7
- package/dinou/core/client-webpack.jsx +457 -82
- package/dinou/core/client.jsx +452 -82
- package/dinou/core/config.js +11 -0
- package/dinou/core/dinou-paths.js +52 -0
- package/dinou/core/edge-ssr.js +122 -0
- package/dinou/core/export-static.mjs +64 -0
- package/dinou/core/generate-route-types.js +252 -0
- package/dinou/core/generate-static-page.js +55 -94
- package/dinou/core/generate-static-rsc.js +47 -15
- package/dinou/core/generate-static.js +212 -10
- package/dinou/core/generating-isg.js +5 -0
- package/dinou/core/get-asset-from-manifest.js +9 -3
- package/dinou/core/get-error-jsx.js +221 -41
- package/dinou/core/get-file-path-and-dynamic-params.js +9 -13
- package/dinou/core/get-jsx.js +172 -95
- package/dinou/core/handler.js +2296 -0
- package/dinou/core/http-adapter.js +115 -0
- package/dinou/core/import-module.js +83 -53
- package/dinou/core/layout-functions.js +181 -0
- package/dinou/core/link.jsx +18 -6
- package/dinou/core/load-dinou-config.js +60 -0
- package/dinou/core/manifest-provider.js +261 -0
- package/dinou/core/navigation-utils.js +4 -0
- package/dinou/core/navigation.js +50 -39
- package/dinou/core/parse-exports.js +207 -2
- package/dinou/core/ppr-context.js +105 -0
- package/dinou/core/ppr-runtime.js +263 -0
- package/dinou/core/redirect.jsx +20 -19
- package/dinou/core/render-jsx-to-client-jsx.js +133 -15
- package/dinou/core/request-context.js +47 -13
- package/dinou/core/resolve-module-ppr.js +32 -0
- package/dinou/core/revalidating.js +28 -9
- package/dinou/core/route-generator.js +107 -0
- package/dinou/core/rsc-renderer.js +90 -0
- package/dinou/core/run-ssg.js +19 -0
- package/dinou/core/scan-dependency-components.js +218 -0
- package/dinou/core/server-function-proxy-webpack.js +3 -0
- package/dinou/core/server-function-proxy.js +3 -0
- package/dinou/core/slot.js +302 -0
- package/dinou/core/status-manifest.js +41 -1
- package/dinou/core/storage-adapter.js +522 -0
- package/dinou/core/url-resolver.js +16 -16
- package/dinou/core/vfs.js +139 -68
- package/dinou/deno/build.mjs +1112 -0
- package/dinou/deno/seed-kv.js +115 -0
- package/dinou/esbuild/build.mjs +51 -1
- package/dinou/esbuild/dev.mjs +290 -224
- package/dinou/esbuild/helpers-esbuild/get-config-esbuild-prod.mjs +3 -2
- package/dinou/esbuild/helpers-esbuild/get-config-esbuild.mjs +104 -16
- package/dinou/esbuild/helpers-esbuild/get-esbuild-entries.mjs +31 -19
- package/dinou/esbuild/helpers-esbuild/strip-namespace.mjs +9 -0
- package/dinou/esbuild/helpers-esbuild/swc-disk-cache.mjs +225 -0
- package/dinou/esbuild/helpers-esbuild/update-manifest-for-module.mjs +7 -0
- package/dinou/esbuild/helpers-esbuild/write.mjs +111 -2
- package/dinou/esbuild/plugins-esbuild/assets-plugin.mjs +236 -161
- package/dinou/esbuild/plugins-esbuild/css-processor-plugin.mjs +101 -5
- package/dinou/esbuild/plugins-esbuild/react-client-manifest-plugin.mjs +63 -24
- package/dinou/esbuild/plugins-esbuild/server-functions-plugin.mjs +177 -56
- package/dinou/esbuild/plugins-esbuild/skip-missing-entry-points-plugin.mjs +3 -0
- package/dinou/esbuild/plugins-esbuild/stable-chunk-names-and-maps-plugin.mjs +182 -137
- package/dinou/esbuild/plugins-postcss/postcss-extract-plugin.js +38 -11
- package/dinou/esbuild/react-refresh/esm-hmr/client.mjs +55 -8
- package/dinou/esbuild/react-refresh/esm-hmr/server.js +1 -1
- package/dinou/esbuild/react-refresh/esm-hmr-plugin.mjs +238 -101
- package/dinou/esbuild/react-refresh/is-react-refresh-boundary.mjs +18 -4
- package/dinou/index.d.ts +315 -11
- package/dinou/index.js +12 -6
- package/dinou/index.mjs +9 -0
- package/dinou/node/build.mjs +195 -0
- package/dinou/node/bundle-dual-engine.mjs +1012 -0
- package/dinou/node/dev.mjs +2589 -0
- package/dinou/node/port-selector.mjs +164 -0
- package/dinou/node/terminal-status.mjs +345 -0
- package/dinou/package.json +16 -14
- package/dinou/rollup/react-refresh/esm-hmr/client.js +66 -10
- package/dinou/rollup/react-refresh/esm-hmr/server.js +1 -1
- package/dinou/rollup/react-refresh/is-react-refresh-boundary.js +24 -5
- package/dinou/rollup/react-refresh/react-refresh-runtime.js +13 -6
- package/dinou/rollup/react-refresh/react-refresh-wrap-modules.js +56 -11
- package/dinou/rollup/react-refresh/rollup-plugin-esm-hmr.js +176 -48
- package/dinou/rollup/rollup-plugins/dinou-asset-plugin.js +9 -5
- package/dinou/rollup/rollup-plugins/rollup-plugin-memory.js +66 -0
- package/dinou/rollup/rollup-plugins/rollup-plugin-react-client-manifest.js +468 -123
- package/dinou/rollup/rollup-plugins/rollup-plugin-server-functions.js +44 -12
- package/dinou/rollup/rollup-plugins/rollup-plugin-swc.js +100 -0
- package/dinou/rollup/rollup.config.js +155 -43
- package/dinou/server.d.ts +114 -12
- package/dinou/server.js +35 -10
- package/dinou/server.mjs +30 -9
- package/dinou/webpack/helpers/get-webpack-entries.js +14 -19
- package/dinou/webpack/loaders/server-functions-loader.js +22 -1
- package/dinou/webpack/loaders/swc-loader.js +76 -0
- package/dinou/webpack/plugins/manifest-generator-plugin.js +1 -0
- package/dinou/webpack/plugins/server-functions-plugin.js +75 -19
- package/dinou/webpack/plugins/webpack-memory-plugin.js +66 -0
- package/dinou/webpack/webpack.config.js +227 -29
- package/eject.js +173 -94
- package/package.json +110 -108
- package/dinou/core/asset-require-hook.js +0 -58
- package/dinou/core/client-error-webpack.jsx +0 -432
- package/dinou/core/client-error.jsx +0 -432
- package/dinou/core/concurrency-manager.js +0 -48
- package/dinou/core/context-proxy.js +0 -68
- package/dinou/core/generate-static-pages.js +0 -130
- package/dinou/core/generate-static-rscs.js +0 -151
- package/dinou/core/get-ssg-metadata.js +0 -31
- package/dinou/core/register-loader.mjs +0 -11
- package/dinou/core/register-paths.js +0 -27
- package/dinou/core/render-app-to-html.js +0 -430
- package/dinou/core/render-html.js +0 -407
- package/dinou/core/server.js +0 -1542
- package/dinou/esbuild/plugins-esbuild/write-metafile-plugin.mjs +0 -16
package/dinou/index.d.ts
CHANGED
|
@@ -89,6 +89,9 @@ export interface RequestContextStore {
|
|
|
89
89
|
headers: Headers;
|
|
90
90
|
path: string;
|
|
91
91
|
method: "GET" | "POST" | string;
|
|
92
|
+
env?: Record<string, any>;
|
|
93
|
+
ctx?: any;
|
|
94
|
+
[key: string]: any;
|
|
92
95
|
};
|
|
93
96
|
|
|
94
97
|
/**
|
|
@@ -96,8 +99,180 @@ export interface RequestContextStore {
|
|
|
96
99
|
* response functions in the main process.
|
|
97
100
|
*/
|
|
98
101
|
res: ResponseProxy;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Platform host environment (Cloudflare env, D1, KV, R2, etc.).
|
|
105
|
+
*/
|
|
106
|
+
env?: Record<string, any>;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Platform execution context (Cloudflare ctx, waitUntil, etc.).
|
|
110
|
+
*/
|
|
111
|
+
ctx?: any;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Any custom properties attached to context by plugins (e.g., context.user, context.auth).
|
|
115
|
+
*/
|
|
116
|
+
[key: string]: any;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ====================================================================
|
|
120
|
+
// DINOU CONFIGURATION & PLUGIN INTERFACES
|
|
121
|
+
// ====================================================================
|
|
122
|
+
|
|
123
|
+
export interface DinouPlugin {
|
|
124
|
+
name?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Universal Web Standards middleware and webhook handler.
|
|
127
|
+
* Runs on every incoming HTTP request before React rendering.
|
|
128
|
+
* If a Response is returned, Dinou sends it immediately and terminates the request.
|
|
129
|
+
* If nothing is returned, execution continues and any mutations to `context`
|
|
130
|
+
* are preserved and accessible in Server Components and Server Functions via getContext().
|
|
131
|
+
*/
|
|
132
|
+
onRequest?(
|
|
133
|
+
request: Request,
|
|
134
|
+
context: RequestContextStore
|
|
135
|
+
): Promise<Response | void> | Response | void;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Legacy context hook for backwards compatibility.
|
|
139
|
+
*/
|
|
140
|
+
onRequestContext?(simReq: any, resBridge: any, context: any): void;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface DinouConfig {
|
|
144
|
+
/**
|
|
145
|
+
* Enable or disable React StrictMode in development mode.
|
|
146
|
+
* When enabled, React double-invokes render and effect functions in development to catch side-effects.
|
|
147
|
+
* @default true
|
|
148
|
+
*/
|
|
149
|
+
reactStrictMode?: boolean;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Global Partial Prerendering (PPR) configuration.
|
|
153
|
+
* When enabled globally, all static-eligible routes generate static shells with Suspense streaming holes.
|
|
154
|
+
* Can also be enabled or disabled per route or layout in page_functions.ts or layout_functions.ts.
|
|
155
|
+
* @default false
|
|
156
|
+
*/
|
|
157
|
+
ppr?: boolean;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Custom storage adapter for ISR / ISG page caching.
|
|
161
|
+
*/
|
|
162
|
+
storage?: any;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Array of Dinou plugins.
|
|
166
|
+
*/
|
|
167
|
+
plugins?: DinouPlugin[];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Route Segment Configuration for Partial Prerendering (PPR).
|
|
172
|
+
* In Dinou, export `const ppr = true;` or `export function ppr(): boolean`
|
|
173
|
+
* in your page_functions.ts or layout_functions.ts to enable instant static shell delivery with dynamic Suspense streaming holes.
|
|
174
|
+
*/
|
|
175
|
+
export type PPRConfig = boolean | (() => boolean | Promise<boolean>);
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* A single static path item returned or defined by `getStaticPaths`.
|
|
179
|
+
* Can be:
|
|
180
|
+
* - A string: e.g. `"alpha"` for dynamic segment `[slug]`
|
|
181
|
+
* - A number: e.g. `1` for dynamic segment `[id]`
|
|
182
|
+
* - A string array: e.g. `["docs", "getting-started"]` for catch-all segment `[...slug]`
|
|
183
|
+
* - A parameter object: e.g. `{ slug: "alpha", id: "1" }`
|
|
184
|
+
*/
|
|
185
|
+
export type StaticPathItem<TParams = Record<string, string | number | string[] | undefined>> =
|
|
186
|
+
| string
|
|
187
|
+
| number
|
|
188
|
+
| string[]
|
|
189
|
+
| TParams;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Result array or promise of array for `getStaticPaths`.
|
|
193
|
+
*/
|
|
194
|
+
export type StaticPathsResult<TParams = Record<string, string | number | string[] | undefined>> =
|
|
195
|
+
| StaticPathItem<TParams>[]
|
|
196
|
+
| Promise<StaticPathItem<TParams>[]>;
|
|
197
|
+
|
|
198
|
+
export interface RouteSegmentConfig<TParams = any, TProps = any> {
|
|
199
|
+
/**
|
|
200
|
+
* Partial Prerendering (PPR) flag.
|
|
201
|
+
* Can be declared as a constant or a function (sync or async) in page_functions or layout_functions.
|
|
202
|
+
* @default false
|
|
203
|
+
*/
|
|
204
|
+
ppr?: PPRConfig;
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Route caching and revalidation strategy in seconds (s). Single source of truth for page rendering mode.
|
|
208
|
+
* - `0` (or `< 1`): Dynamically rendered on every request (dynamic SSR, bypassing static cache).
|
|
209
|
+
* - `>= 1`: Pre-rendered/cached with Incremental Static Regeneration (ISR) expiring after the specified seconds.
|
|
210
|
+
* - `false` (or omitted): Statically pre-rendered and cached forever until on-demand revalidation via `revalidatePath` or `revalidateTag`.
|
|
211
|
+
*
|
|
212
|
+
* Can be declared as a number, false, or as a sync/async function in `page_functions.ts` or `layout_functions.ts`.
|
|
213
|
+
*/
|
|
214
|
+
revalidate?: number | false | (() => number | false | Promise<number | false>);
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Cache tags associated with this route segment for on-demand invalidation via `revalidateTag(tag)`.
|
|
218
|
+
* Can be declared as a sync/async function `getCacheTags(params)` or as a constant array `getCacheTags = [...]`.
|
|
219
|
+
*/
|
|
220
|
+
getCacheTags?: string[] | ((params?: TParams) => string[] | Promise<string[]>);
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Generates static path parameter objects or values for dynamic route segments during build time (SSG).
|
|
224
|
+
* Supports returning:
|
|
225
|
+
* - Array of strings (e.g. `["alpha", "beta"]`)
|
|
226
|
+
* - Array of numbers (e.g. `[1, 2, 3]`)
|
|
227
|
+
* - Array of string arrays for catch-all routes (e.g. `[["docs", "intro"], ["docs", "setup"]]`)
|
|
228
|
+
* - Array of parameter objects (e.g. `[{ slug: "alpha" }, { slug: "beta" }]`)
|
|
229
|
+
*
|
|
230
|
+
* Can be declared as a sync or async function, or as a constant array in `page_functions.ts` or `layout_functions.ts`.
|
|
231
|
+
*/
|
|
232
|
+
getStaticPaths?:
|
|
233
|
+
| StaticPathItem<TParams>[]
|
|
234
|
+
| (() => StaticPathsResult<TParams>);
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Server-side loader that fetches or calculates props passed directly to the Page or Layout component.
|
|
238
|
+
*/
|
|
239
|
+
getProps?: (params?: TParams) => TProps | Promise<TProps>;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Parameter validator hook. If it returns false, Dinou halts rendering and returns 404 Not Found.
|
|
243
|
+
*/
|
|
244
|
+
validateParams?: (params?: TParams) => boolean | Promise<boolean>;
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Controls whether on-demand Incremental Static Generation (ISG) is allowed for ungenerated paths.
|
|
248
|
+
* @default true
|
|
249
|
+
*/
|
|
250
|
+
allowISG?: boolean | (() => boolean | Promise<boolean>);
|
|
99
251
|
}
|
|
100
252
|
|
|
253
|
+
/**
|
|
254
|
+
* Type-safe interface representing the available exports in `page_functions.ts`.
|
|
255
|
+
* Accepts either a route pattern string (e.g. `"/products/[id]"`) or a direct parameters dictionary.
|
|
256
|
+
*/
|
|
257
|
+
export type PageFunctions<TRouteOrParams = any, TProps = any> = RouteSegmentConfig<
|
|
258
|
+
[TRouteOrParams] extends [string] ? RouteParams<TRouteOrParams> : TRouteOrParams,
|
|
259
|
+
TProps
|
|
260
|
+
>;
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Type-safe interface representing the available exports in `layout_functions.ts`.
|
|
264
|
+
* Accepts either a route pattern string (e.g. `"/[organization]"`) or a direct parameters dictionary.
|
|
265
|
+
*/
|
|
266
|
+
export type LayoutFunctions<TRouteOrParams = any, TProps = any> = RouteSegmentConfig<
|
|
267
|
+
[TRouteOrParams] extends [string] ? RouteParams<TRouteOrParams> : TRouteOrParams,
|
|
268
|
+
TProps
|
|
269
|
+
>;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Type-safe configuration helper for dinou.config.js / dinou.config.mjs.
|
|
273
|
+
*/
|
|
274
|
+
export declare function defineConfig(config: DinouConfig): DinouConfig;
|
|
275
|
+
|
|
101
276
|
// ====================================================================
|
|
102
277
|
// MAIN EXPORTED FUNCTIONS (SERVER SIDE)
|
|
103
278
|
// ====================================================================
|
|
@@ -153,8 +328,8 @@ export declare function ClientRedirect(props: ClientRedirectProps): ReactNode;
|
|
|
153
328
|
export declare function redirect(destination: string): ReactNode;
|
|
154
329
|
|
|
155
330
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
331
|
+
* Universal hook (works in both Server Components and Client Components) that lets you read the current URL's pathname.
|
|
332
|
+
* On the client, this hook triggers a re-render when the route changes.
|
|
158
333
|
*
|
|
159
334
|
* @returns {string} The current pathname (e.g., "/dashboard") without search parameters.
|
|
160
335
|
* @example
|
|
@@ -164,8 +339,8 @@ export declare function redirect(destination: string): ReactNode;
|
|
|
164
339
|
export declare function usePathname(): string;
|
|
165
340
|
|
|
166
341
|
/**
|
|
167
|
-
*
|
|
168
|
-
*
|
|
342
|
+
* Universal hook (works in both Server Components and Client Components) that lets you read the current URL's search parameters.
|
|
343
|
+
* On the client, this hook triggers a re-render when the route changes.
|
|
169
344
|
*
|
|
170
345
|
* @returns {URLSearchParams} A read-only version of the standard URLSearchParams interface.
|
|
171
346
|
* @example
|
|
@@ -211,20 +386,124 @@ export interface NavigationOptions {
|
|
|
211
386
|
* // Soft refresh (re-fetch data without full reload)
|
|
212
387
|
* router.refresh();
|
|
213
388
|
*/
|
|
389
|
+
// ====================================================================
|
|
390
|
+
// TYPE-SAFE ROUTING (Level 1 & Level 2)
|
|
391
|
+
// ====================================================================
|
|
392
|
+
|
|
393
|
+
export namespace DinouRouter {
|
|
394
|
+
export interface Register {
|
|
395
|
+
// When .dinou/types/routes.d.ts is generated, it augments this interface.
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Union of all valid routes in the application.
|
|
401
|
+
* When typed routes are generated, autocompletes known routes.
|
|
402
|
+
* Falls back to string if not yet generated or in loose mode.
|
|
403
|
+
*/
|
|
404
|
+
export type DinouRoute = DinouRouter.Register extends { route: infer R extends string }
|
|
405
|
+
? R
|
|
406
|
+
: string;
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Utility types to parse dynamic route parameters from a route string pattern.
|
|
410
|
+
* e.g. ExtractRouteParams<"/blog/[id]"> => { id: string }
|
|
411
|
+
* e.g. ExtractRouteParams<"/docs/[...slug]"> => { slug: string[] }
|
|
412
|
+
* e.g. ExtractRouteParams<"/shop/[[...slug]]"> => { slug?: string[] }
|
|
413
|
+
* e.g. ExtractRouteParams<"/item/[[code]]"> => { code?: string }
|
|
414
|
+
*/
|
|
415
|
+
export type ExtractSegmentParam<Segment extends string> =
|
|
416
|
+
Segment extends `[[...${infer Param}]]`
|
|
417
|
+
? { [K in Param]?: string[] }
|
|
418
|
+
: Segment extends `[[${infer Param}]]`
|
|
419
|
+
? { [K in Param]?: string }
|
|
420
|
+
: Segment extends `[...${infer Param}]`
|
|
421
|
+
? { [K in Param]: string[] }
|
|
422
|
+
: Segment extends `[${infer Param}]`
|
|
423
|
+
? { [K in Param]: string }
|
|
424
|
+
: {};
|
|
425
|
+
|
|
426
|
+
export type ExtractRouteParams<Path extends string> =
|
|
427
|
+
Path extends `/${infer Rest}`
|
|
428
|
+
? ExtractRouteParams<Rest>
|
|
429
|
+
: Path extends `${infer Start}/${infer Rest}`
|
|
430
|
+
? ExtractSegmentParam<Start> & ExtractRouteParams<Rest>
|
|
431
|
+
: ExtractSegmentParam<Path>;
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* All known route patterns in the application (e.g. "/blog/[slug]", "/t-params/[[slug]]").
|
|
435
|
+
* Autocompletes all project routes when typing PageProps<"..." or LayoutProps<"...".
|
|
436
|
+
*/
|
|
437
|
+
export type DinouRoutePattern = DinouRouter.Register extends { params: infer P }
|
|
438
|
+
? (keyof P extends string ? keyof P | (keyof P extends `/${infer Rest}` ? Rest : never) : string)
|
|
439
|
+
: string;
|
|
440
|
+
|
|
441
|
+
export type CleanRoutePath<T extends string> = T extends `/${string}` ? T : `/${T}`;
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Resolved route parameter types for a route.
|
|
445
|
+
* Prioritizes the generated route parameters map from .dinou/types/routes.d.ts,
|
|
446
|
+
* and falls back to template literal extraction.
|
|
447
|
+
*/
|
|
448
|
+
export type RouteParams<T extends string = string> =
|
|
449
|
+
DinouRouter.Register extends { params: infer P }
|
|
450
|
+
? (CleanRoutePath<T> extends keyof P
|
|
451
|
+
? P[CleanRoutePath<T>]
|
|
452
|
+
: (T extends keyof P ? P[T] : ExtractRouteParams<T>))
|
|
453
|
+
: ExtractRouteParams<T>;
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Props for Dinou Page components.
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* export default function Page({ params }: PageProps<"/blog/[id]">) {
|
|
460
|
+
* return <h1>Post {params.id}</h1>;
|
|
461
|
+
* }
|
|
462
|
+
*/
|
|
463
|
+
export interface PageProps<T extends DinouRoutePattern = DinouRoutePattern> {
|
|
464
|
+
params: RouteParams<T>;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Props for Dinou Layout components.
|
|
469
|
+
*
|
|
470
|
+
* @example
|
|
471
|
+
* export default function Layout({ children, params }: LayoutProps<"/blog/[id]">) {
|
|
472
|
+
* return <div>{children}</div>;
|
|
473
|
+
* }
|
|
474
|
+
*/
|
|
475
|
+
export interface LayoutProps<T extends DinouRoutePattern = DinouRoutePattern> {
|
|
476
|
+
children: React.ReactNode;
|
|
477
|
+
params: RouteParams<T>;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Props for Dinou Error Page components (error.tsx).
|
|
482
|
+
*
|
|
483
|
+
* @example
|
|
484
|
+
* export default function ErrorPage({ error, params }: ErrorProps<"/blog/[id]">) {
|
|
485
|
+
* return <h1>Error loading {params.id}: {error.message}</h1>;
|
|
486
|
+
* }
|
|
487
|
+
*/
|
|
488
|
+
export interface ErrorProps<T extends DinouRoutePattern = DinouRoutePattern> {
|
|
489
|
+
error: Error;
|
|
490
|
+
params: RouteParams<T>;
|
|
491
|
+
}
|
|
492
|
+
|
|
214
493
|
export declare function useRouter(): {
|
|
215
494
|
/**
|
|
216
495
|
* Navigate to the provided href. Pushes a new entry into the history stack.
|
|
217
496
|
* @param href - The URL to navigate to (e.g., "/about").
|
|
218
497
|
* @param options - Optional configuration for the navigation (e.g., force fresh data).
|
|
219
498
|
*/
|
|
220
|
-
push: (href:
|
|
499
|
+
push: (href: DinouRoute, options?: NavigationOptions) => void;
|
|
221
500
|
|
|
222
501
|
/**
|
|
223
502
|
* Navigate to the provided href. Replaces the current entry in the history stack.
|
|
224
503
|
* @param href - The URL to navigate to.
|
|
225
504
|
* @param options - Optional configuration for the navigation.
|
|
226
505
|
*/
|
|
227
|
-
replace: (href:
|
|
506
|
+
replace: (href: DinouRoute, options?: NavigationOptions) => void;
|
|
228
507
|
|
|
229
508
|
/**
|
|
230
509
|
* Navigate back in the browser's history.
|
|
@@ -242,15 +521,33 @@ export declare function useRouter(): {
|
|
|
242
521
|
* Refresh the current route.
|
|
243
522
|
*
|
|
244
523
|
* This triggers a "Soft Reload":
|
|
245
|
-
* 1. Clears the client-side cache for the current
|
|
524
|
+
* 1. Clears the client-side cache for the current page (`pageCache`).
|
|
246
525
|
* 2. Re-fetches fresh RSC payload from the server.
|
|
247
|
-
* 3. Re-renders the page components without a full browser refresh.
|
|
526
|
+
* 3. Re-renders the page components without a full browser refresh, preserving client state.
|
|
527
|
+
*
|
|
528
|
+
* By default, it refreshes only the page. Pass `{ layout: true }` to also invalidate and re-fetch the layout.
|
|
248
529
|
*
|
|
249
530
|
* Useful for updating the UI after a mutation (e.g., form submission) or to poll for new data.
|
|
531
|
+
*
|
|
532
|
+
* @param options - Optional configuration (`{ layout?: boolean }`).
|
|
533
|
+
*/
|
|
534
|
+
refresh: (options?: { layout?: boolean }) => void;
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Refreshes a specific DinouCacheSlot without re-rendering the rest of the page.
|
|
538
|
+
* @param slotId - The slot identifier or tag.
|
|
539
|
+
* @param options - Optional configuration ({ fresh?: boolean }).
|
|
250
540
|
*/
|
|
251
|
-
|
|
541
|
+
refreshSlot: (slotId: string, options?: { fresh?: boolean }) => void;
|
|
252
542
|
};
|
|
253
543
|
|
|
544
|
+
/**
|
|
545
|
+
* Programmatically refreshes a specific DinouCacheSlot in the browser without reloading the page.
|
|
546
|
+
* @param slotId - The slot identifier or tag to refresh.
|
|
547
|
+
* @param options - Optional configuration ({ fresh?: boolean }).
|
|
548
|
+
*/
|
|
549
|
+
export declare function refreshSlot(slotId: string, options?: { fresh?: boolean }): void;
|
|
550
|
+
|
|
254
551
|
/**
|
|
255
552
|
* A Client Component hook that returns true if a navigation (SPA transition) is currently in progress.
|
|
256
553
|
* Useful for showing progress bars or loading spinners globally.
|
|
@@ -269,13 +566,19 @@ import type { AnchorHTMLAttributes } from "react";
|
|
|
269
566
|
* Props for the Dinou Link component.
|
|
270
567
|
* It extends standard HTML <a> attributes, allowing className, style, etc.
|
|
271
568
|
*/
|
|
272
|
-
export interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
569
|
+
export interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
|
|
273
570
|
/**
|
|
274
571
|
* The destination URL or path.
|
|
572
|
+
* * Autocompletes all known routes discovered by Dinou.
|
|
275
573
|
* * Supports absolute paths (e.g., `/dashboard`).
|
|
276
574
|
* * Supports relative paths (e.g., `../settings` or `details`).
|
|
277
575
|
*/
|
|
278
|
-
href
|
|
576
|
+
href?: DinouRoute;
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Alias for `href` (React Router / Remix compatibility).
|
|
580
|
+
*/
|
|
581
|
+
to?: DinouRoute;
|
|
279
582
|
|
|
280
583
|
/**
|
|
281
584
|
* Whether to prefetch the RSC payload when the mouse enters the link area (hover).
|
|
@@ -314,3 +617,4 @@ export interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
|
314
617
|
* </Link>
|
|
315
618
|
*/
|
|
316
619
|
export declare function Link(props: LinkProps): ReactNode;
|
|
620
|
+
|
package/dinou/index.js
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
const contextModule = require("./core/request-context.js");
|
|
2
2
|
const navigationModule = require("./core/navigation.js");
|
|
3
|
-
const
|
|
4
|
-
const redirectModule = require("./core/redirect.jsx");
|
|
5
|
-
const linkModule = require("./core/link.jsx");
|
|
3
|
+
const { defineConfig } = require("./core/config.js");
|
|
6
4
|
|
|
7
5
|
module.exports = {
|
|
6
|
+
defineConfig,
|
|
8
7
|
getContext: contextModule.getContext,
|
|
9
8
|
usePathname: navigationModule.usePathname,
|
|
10
9
|
useSearchParams: navigationModule.useSearchParams,
|
|
11
10
|
useRouter: navigationModule.useRouter,
|
|
11
|
+
refreshSlot: navigationModule.refreshSlot,
|
|
12
12
|
useNavigationLoading: navigationModule.useNavigationLoading,
|
|
13
|
-
redirect
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
get redirect() {
|
|
14
|
+
return require("./core/redirect.jsx").redirect;
|
|
15
|
+
},
|
|
16
|
+
get ClientRedirect() {
|
|
17
|
+
return require("./core/client-redirect.jsx").ClientRedirect;
|
|
18
|
+
},
|
|
19
|
+
get Link() {
|
|
20
|
+
return require("./core/link.jsx").Link;
|
|
21
|
+
},
|
|
16
22
|
};
|
package/dinou/index.mjs
CHANGED
|
@@ -3,17 +3,24 @@ import {
|
|
|
3
3
|
usePathname,
|
|
4
4
|
useSearchParams,
|
|
5
5
|
useRouter,
|
|
6
|
+
refreshSlot,
|
|
6
7
|
useNavigationLoading,
|
|
7
8
|
} from "./core/navigation.js";
|
|
8
9
|
import { redirect } from "./core/redirect.jsx";
|
|
9
10
|
import { ClientRedirect } from "./core/client-redirect.jsx";
|
|
10
11
|
import { Link } from "./core/link.jsx";
|
|
11
12
|
|
|
13
|
+
function defineConfig(config) {
|
|
14
|
+
return config;
|
|
15
|
+
}
|
|
16
|
+
|
|
12
17
|
const dinou = {
|
|
18
|
+
defineConfig,
|
|
13
19
|
getContext,
|
|
14
20
|
usePathname,
|
|
15
21
|
useSearchParams,
|
|
16
22
|
useRouter,
|
|
23
|
+
refreshSlot,
|
|
17
24
|
useNavigationLoading,
|
|
18
25
|
redirect,
|
|
19
26
|
ClientRedirect,
|
|
@@ -21,10 +28,12 @@ const dinou = {
|
|
|
21
28
|
};
|
|
22
29
|
|
|
23
30
|
export {
|
|
31
|
+
defineConfig,
|
|
24
32
|
getContext,
|
|
25
33
|
usePathname,
|
|
26
34
|
useSearchParams,
|
|
27
35
|
useRouter,
|
|
36
|
+
refreshSlot,
|
|
28
37
|
useNavigationLoading,
|
|
29
38
|
redirect,
|
|
30
39
|
ClientRedirect,
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// dinou/node/build.mjs
|
|
2
|
+
// Dual-Bundle Build script for packaging Dinou v7 for Node.js (Pre-bundled AOT Standalone).
|
|
3
|
+
// Pass A & Pass B: Handled via bundleDualEngine
|
|
4
|
+
// Pass C: Main Node Orchestrator (.dinou/node/server.js)
|
|
5
|
+
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import esbuild from "esbuild";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { bundleDualEngine } from "./bundle-dual-engine.mjs";
|
|
11
|
+
|
|
12
|
+
const projectRoot = process.cwd();
|
|
13
|
+
const nodeDir = path.resolve(projectRoot, ".dinou/node");
|
|
14
|
+
fs.mkdirSync(nodeDir, { recursive: true });
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const bundlerName = process.env.DINOU_BUILD_TOOL || "esbuild";
|
|
18
|
+
console.log(`\nš¦ Dinou v7 (Node.js ⢠${bundlerName})`);
|
|
19
|
+
console.log(` ā” Compiling Pass A (RSC) and Pass B (SSR) via Dual-Engine...`);
|
|
20
|
+
const engineResult = await bundleDualEngine({
|
|
21
|
+
isDev: false,
|
|
22
|
+
projectRoot,
|
|
23
|
+
outDir: ".dinou/node",
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const { dinouDirSlash, externalList, banner, commonAlias, commonLoader, isWebpackBuild } = engineResult;
|
|
27
|
+
|
|
28
|
+
// Pass C: Node Orchestrator Entry
|
|
29
|
+
const nodeEntryContent = `// Auto-generated main entry for Node.js (Dual-Bundle)
|
|
30
|
+
import http from "node:http";
|
|
31
|
+
import path from "node:path";
|
|
32
|
+
import fs from "node:fs";
|
|
33
|
+
import { fileURLToPath } from "node:url";
|
|
34
|
+
import { Readable } from "node:stream";
|
|
35
|
+
import { handleRequest } from "./rsc-engine.mjs";
|
|
36
|
+
import { renderHtml } from "./ssr-engine.mjs";
|
|
37
|
+
import {
|
|
38
|
+
setStorageAdapter,
|
|
39
|
+
FileSystemStorage,
|
|
40
|
+
MemoryStorage,
|
|
41
|
+
} from "${dinouDirSlash}/core/storage-adapter.js";
|
|
42
|
+
import {
|
|
43
|
+
nodeToWebRequest,
|
|
44
|
+
sendWebResponseToNode,
|
|
45
|
+
} from "${dinouDirSlash}/core/http-adapter.js";
|
|
46
|
+
|
|
47
|
+
if (typeof globalThis !== "undefined") {
|
|
48
|
+
globalThis.__DINOU_RUNTIME__ = "node-bundle";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let storageInitialized = false;
|
|
52
|
+
|
|
53
|
+
function initStorage() {
|
|
54
|
+
if (!storageInitialized) {
|
|
55
|
+
try {
|
|
56
|
+
const cwd = typeof process !== "undefined" && typeof process.cwd === "function" ? process.cwd() : ".";
|
|
57
|
+
const dist2Dir = path.resolve(cwd, ".dinou/dist2");
|
|
58
|
+
setStorageAdapter(new FileSystemStorage(dist2Dir));
|
|
59
|
+
} catch (e) {
|
|
60
|
+
setStorageAdapter(new MemoryStorage());
|
|
61
|
+
}
|
|
62
|
+
storageInitialized = true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const MIME_TYPES = {
|
|
67
|
+
".js": "application/javascript; charset=utf-8",
|
|
68
|
+
".mjs": "application/javascript; charset=utf-8",
|
|
69
|
+
".css": "text/css; charset=utf-8",
|
|
70
|
+
".json": "application/json",
|
|
71
|
+
".svg": "image/svg+xml",
|
|
72
|
+
".png": "image/png",
|
|
73
|
+
".jpg": "image/jpeg",
|
|
74
|
+
".jpeg": "image/jpeg",
|
|
75
|
+
".webp": "image/webp",
|
|
76
|
+
".ico": "image/x-icon",
|
|
77
|
+
".txt": "text/plain; charset=utf-8",
|
|
78
|
+
".woff": "font/woff",
|
|
79
|
+
".woff2": "font/woff2",
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const cwd = typeof process !== "undefined" && typeof process.cwd === "function" ? process.cwd() : ".";
|
|
83
|
+
const dist3Dir = path.resolve(cwd, ".dinou/dist3");
|
|
84
|
+
|
|
85
|
+
export async function fetch(req) {
|
|
86
|
+
initStorage();
|
|
87
|
+
|
|
88
|
+
const url = new URL(req.url);
|
|
89
|
+
const pathname = url.pathname;
|
|
90
|
+
|
|
91
|
+
// Static assets delivery (Zero-copy with Node streams)
|
|
92
|
+
if (pathname !== "/") {
|
|
93
|
+
const filePath = path.join(dist3Dir, pathname);
|
|
94
|
+
if (fs.existsSync(filePath)) {
|
|
95
|
+
try {
|
|
96
|
+
const stat = fs.statSync(filePath);
|
|
97
|
+
if (stat.isFile()) {
|
|
98
|
+
const nodeStream = fs.createReadStream(filePath);
|
|
99
|
+
const webStream = Readable.toWeb(nodeStream);
|
|
100
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
101
|
+
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
102
|
+
return new Response(webStream, {
|
|
103
|
+
headers: {
|
|
104
|
+
"content-type": contentType,
|
|
105
|
+
"content-length": String(stat.size),
|
|
106
|
+
"cache-control": "public, max-age=31536000, immutable",
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
} catch (e) {}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return handleRequest(req, {
|
|
115
|
+
runtime: "node-bundle",
|
|
116
|
+
renderHtmlStream: renderHtml,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const PORT = Number(process.env.PORT || 3000);
|
|
121
|
+
|
|
122
|
+
export function startServer(port = PORT, host = "0.0.0.0") {
|
|
123
|
+
const server = http.createServer(async (req, res) => {
|
|
124
|
+
try {
|
|
125
|
+
const webReq = nodeToWebRequest(req);
|
|
126
|
+
const webRes = await fetch(webReq);
|
|
127
|
+
await sendWebResponseToNode(webRes, res);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
console.error("[Dinou Node Server] Request error:", err);
|
|
130
|
+
if (!res.headersSent) {
|
|
131
|
+
res.statusCode = 500;
|
|
132
|
+
res.setHeader("content-type", "text/plain; charset=utf-8");
|
|
133
|
+
res.end("Internal Server Error");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return new Promise((resolve, reject) => {
|
|
139
|
+
server.on("error", reject);
|
|
140
|
+
server.listen(port, () => {
|
|
141
|
+
console.log(\`š Dinou (Node.js AOT Dual-Bundle) server running on http://localhost:\${port}\`);
|
|
142
|
+
resolve(server);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export default {
|
|
148
|
+
port: PORT,
|
|
149
|
+
fetch,
|
|
150
|
+
startServer,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const isMain = typeof process !== "undefined" &&
|
|
154
|
+
process.argv[1] &&
|
|
155
|
+
(path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url)));
|
|
156
|
+
|
|
157
|
+
if (isMain) {
|
|
158
|
+
startServer(PORT);
|
|
159
|
+
}
|
|
160
|
+
`;
|
|
161
|
+
const nodeEntryPath = path.join(nodeDir, "node-entry.mjs");
|
|
162
|
+
fs.writeFileSync(nodeEntryPath, nodeEntryContent, "utf8");
|
|
163
|
+
|
|
164
|
+
// Pass C: Final Node Orchestrator
|
|
165
|
+
const finalOutfile = path.join(nodeDir, "server.mjs");
|
|
166
|
+
console.log(" š Bundling Pass C: Node Orchestrator (.dinou/node/server.mjs)...");
|
|
167
|
+
await esbuild.build({
|
|
168
|
+
entryPoints: [nodeEntryPath],
|
|
169
|
+
outfile: finalOutfile,
|
|
170
|
+
bundle: true,
|
|
171
|
+
format: "esm",
|
|
172
|
+
target: "node20",
|
|
173
|
+
platform: "node",
|
|
174
|
+
mainFields: ["module", "main"],
|
|
175
|
+
conditions: ["node", "worker", "browser"],
|
|
176
|
+
external: [...externalList, "./rsc-engine.mjs", "./ssr-engine.mjs"],
|
|
177
|
+
banner,
|
|
178
|
+
alias: commonAlias,
|
|
179
|
+
loader: commonLoader,
|
|
180
|
+
define: {
|
|
181
|
+
"process.env.NODE_ENV": '"production"',
|
|
182
|
+
"process.env.DINOU_RUNTIME": '"node-bundle"',
|
|
183
|
+
"process.env.DINOU_BUILD_TOOL": JSON.stringify(isWebpackBuild ? "webpack" : (process.env.DINOU_BUILD_TOOL || "esbuild")),
|
|
184
|
+
},
|
|
185
|
+
logLevel: "warning",
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const relOutfile = path.relative(projectRoot, finalOutfile).replace(/\\/g, "/");
|
|
189
|
+
console.log(`\nā Node.js production build complete!`);
|
|
190
|
+
console.log(` Output: ${relOutfile}`);
|
|
191
|
+
console.log(` š Run with: npm start (or node ${relOutfile})\n`);
|
|
192
|
+
} catch (err) {
|
|
193
|
+
console.error("ā [Dinou Node] Build failed:", err);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|