@pyreon/zero 0.1.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/LICENSE +21 -0
- package/README.md +53 -0
- package/lib/cache.js +80 -0
- package/lib/cache.js.map +1 -0
- package/lib/client.js +58 -0
- package/lib/client.js.map +1 -0
- package/lib/config.js +35 -0
- package/lib/config.js.map +1 -0
- package/lib/font.js +251 -0
- package/lib/font.js.map +1 -0
- package/lib/fs-router-BkbIWqek.js +30 -0
- package/lib/fs-router-BkbIWqek.js.map +1 -0
- package/lib/fs-router-jfd1QGLB.js +261 -0
- package/lib/fs-router-jfd1QGLB.js.map +1 -0
- package/lib/image-plugin.js +289 -0
- package/lib/image-plugin.js.map +1 -0
- package/lib/image.js +113 -0
- package/lib/image.js.map +1 -0
- package/lib/index.js +1665 -0
- package/lib/index.js.map +1 -0
- package/lib/link.js +186 -0
- package/lib/link.js.map +1 -0
- package/lib/script.js +102 -0
- package/lib/script.js.map +1 -0
- package/lib/seo.js +136 -0
- package/lib/seo.js.map +1 -0
- package/lib/theme.js +165 -0
- package/lib/theme.js.map +1 -0
- package/lib/types/adapters/bun.d.ts +6 -0
- package/lib/types/adapters/bun.d.ts.map +1 -0
- package/lib/types/adapters/index.d.ts +10 -0
- package/lib/types/adapters/index.d.ts.map +1 -0
- package/lib/types/adapters/node.d.ts +6 -0
- package/lib/types/adapters/node.d.ts.map +1 -0
- package/lib/types/adapters/static.d.ts +7 -0
- package/lib/types/adapters/static.d.ts.map +1 -0
- package/lib/types/app.d.ts +24 -0
- package/lib/types/app.d.ts.map +1 -0
- package/lib/types/cache.d.ts +54 -0
- package/lib/types/cache.d.ts.map +1 -0
- package/lib/types/client.d.ts +19 -0
- package/lib/types/client.d.ts.map +1 -0
- package/lib/types/config.d.ts +18 -0
- package/lib/types/config.d.ts.map +1 -0
- package/lib/types/entry-server.d.ts +26 -0
- package/lib/types/entry-server.d.ts.map +1 -0
- package/lib/types/font.d.ts +119 -0
- package/lib/types/font.d.ts.map +1 -0
- package/lib/types/fs-router.d.ts +33 -0
- package/lib/types/fs-router.d.ts.map +1 -0
- package/lib/types/image-plugin.d.ts +79 -0
- package/lib/types/image-plugin.d.ts.map +1 -0
- package/lib/types/image.d.ts +50 -0
- package/lib/types/image.d.ts.map +1 -0
- package/lib/types/index.d.ts +27 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/isr.d.ts +9 -0
- package/lib/types/isr.d.ts.map +1 -0
- package/lib/types/link.d.ts +116 -0
- package/lib/types/link.d.ts.map +1 -0
- package/lib/types/script.d.ts +34 -0
- package/lib/types/script.d.ts.map +1 -0
- package/lib/types/seo.d.ts +88 -0
- package/lib/types/seo.d.ts.map +1 -0
- package/lib/types/theme.d.ts +38 -0
- package/lib/types/theme.d.ts.map +1 -0
- package/lib/types/types.d.ts +104 -0
- package/lib/types/types.d.ts.map +1 -0
- package/lib/types/utils/use-intersection-observer.d.ts +10 -0
- package/lib/types/utils/use-intersection-observer.d.ts.map +1 -0
- package/lib/types/utils/with-headers.d.ts +6 -0
- package/lib/types/utils/with-headers.d.ts.map +1 -0
- package/lib/types/vite-plugin.d.ts +17 -0
- package/lib/types/vite-plugin.d.ts.map +1 -0
- package/package.json +100 -0
- package/src/adapters/bun.ts +65 -0
- package/src/adapters/index.ts +29 -0
- package/src/adapters/node.ts +113 -0
- package/src/adapters/static.ts +17 -0
- package/src/app.ts +62 -0
- package/src/cache.ts +149 -0
- package/src/client.ts +43 -0
- package/src/config.ts +36 -0
- package/src/entry-server.ts +51 -0
- package/src/font.ts +461 -0
- package/src/fs-router.ts +380 -0
- package/src/image-plugin.ts +452 -0
- package/src/image.tsx +167 -0
- package/src/index.ts +119 -0
- package/src/isr.ts +95 -0
- package/src/link.tsx +266 -0
- package/src/script.tsx +133 -0
- package/src/seo.ts +281 -0
- package/src/sharp.d.ts +20 -0
- package/src/theme.tsx +162 -0
- package/src/types.ts +130 -0
- package/src/utils/use-intersection-observer.ts +36 -0
- package/src/utils/with-headers.ts +16 -0
- package/src/vite-plugin.ts +92 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { ComponentFn } from '@pyreon/core';
|
|
2
|
+
import type { NavigationGuard } from '@pyreon/router';
|
|
3
|
+
import type { Middleware } from '@pyreon/server';
|
|
4
|
+
/** What a route file (e.g. `src/routes/index.tsx`) can export. */
|
|
5
|
+
export interface RouteModule {
|
|
6
|
+
/** Default export is the page component. */
|
|
7
|
+
default?: ComponentFn;
|
|
8
|
+
/** Layout wrapper — wraps this route and all children. */
|
|
9
|
+
layout?: ComponentFn;
|
|
10
|
+
/** Loading component shown while lazy-loading or during Suspense. */
|
|
11
|
+
loading?: ComponentFn;
|
|
12
|
+
/** Error component shown when the route errors. */
|
|
13
|
+
error?: ComponentFn;
|
|
14
|
+
/** Server-side data loader. */
|
|
15
|
+
loader?: (ctx: LoaderContext) => Promise<unknown>;
|
|
16
|
+
/** Per-route middleware. */
|
|
17
|
+
middleware?: Middleware | Middleware[];
|
|
18
|
+
/** Navigation guard — can redirect or block navigation. */
|
|
19
|
+
guard?: NavigationGuard;
|
|
20
|
+
/** Route metadata. */
|
|
21
|
+
meta?: RouteMeta;
|
|
22
|
+
/** Rendering mode override for this route. */
|
|
23
|
+
renderMode?: RenderMode;
|
|
24
|
+
}
|
|
25
|
+
/** Context passed to route loaders. */
|
|
26
|
+
export interface LoaderContext {
|
|
27
|
+
params: Record<string, string>;
|
|
28
|
+
query: Record<string, string>;
|
|
29
|
+
signal: AbortSignal;
|
|
30
|
+
request: Request;
|
|
31
|
+
}
|
|
32
|
+
/** Per-route metadata. */
|
|
33
|
+
export interface RouteMeta {
|
|
34
|
+
title?: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
[key: string]: unknown;
|
|
37
|
+
}
|
|
38
|
+
export type RenderMode = 'ssr' | 'ssg' | 'spa' | 'isr';
|
|
39
|
+
export interface ISRConfig {
|
|
40
|
+
/** Revalidation interval in seconds. */
|
|
41
|
+
revalidate: number;
|
|
42
|
+
}
|
|
43
|
+
export interface ZeroConfig {
|
|
44
|
+
/** Default rendering mode. Default: "ssr" */
|
|
45
|
+
mode?: RenderMode;
|
|
46
|
+
/** Vite config overrides. */
|
|
47
|
+
vite?: Record<string, unknown>;
|
|
48
|
+
/** SSR options. */
|
|
49
|
+
ssr?: {
|
|
50
|
+
/** Streaming mode. Default: "string" */
|
|
51
|
+
mode?: 'string' | 'stream';
|
|
52
|
+
};
|
|
53
|
+
/** SSG options — only used when mode is "ssg". */
|
|
54
|
+
ssg?: {
|
|
55
|
+
/** Paths to prerender (or function returning paths). */
|
|
56
|
+
paths?: string[] | (() => string[] | Promise<string[]>);
|
|
57
|
+
};
|
|
58
|
+
/** ISR config — only used when mode is "isr". */
|
|
59
|
+
isr?: ISRConfig;
|
|
60
|
+
/** Deploy adapter. Default: "node" */
|
|
61
|
+
adapter?: 'node' | 'bun' | 'static';
|
|
62
|
+
/** Base URL path. Default: "/" */
|
|
63
|
+
base?: string;
|
|
64
|
+
/** App-level middleware applied to all routes. */
|
|
65
|
+
middleware?: Middleware[];
|
|
66
|
+
/** Server port for dev/preview. Default: 3000 */
|
|
67
|
+
port?: number;
|
|
68
|
+
}
|
|
69
|
+
/** Internal representation of a file-system route before conversion to RouteRecord. */
|
|
70
|
+
export interface FileRoute {
|
|
71
|
+
/** File path relative to routes dir (e.g. "users/[id].tsx") */
|
|
72
|
+
filePath: string;
|
|
73
|
+
/** Parsed URL path pattern (e.g. "/users/:id") */
|
|
74
|
+
urlPath: string;
|
|
75
|
+
/** Directory path for grouping (e.g. "users" or "" for root) */
|
|
76
|
+
dirPath: string;
|
|
77
|
+
/** Route segment depth for nesting. */
|
|
78
|
+
depth: number;
|
|
79
|
+
/** Whether this is a layout file. */
|
|
80
|
+
isLayout: boolean;
|
|
81
|
+
/** Whether this is an error boundary file. */
|
|
82
|
+
isError: boolean;
|
|
83
|
+
/** Whether this is a loading fallback file. */
|
|
84
|
+
isLoading: boolean;
|
|
85
|
+
/** Whether this is a catch-all route. */
|
|
86
|
+
isCatchAll: boolean;
|
|
87
|
+
/** Resolved rendering mode. */
|
|
88
|
+
renderMode: RenderMode;
|
|
89
|
+
}
|
|
90
|
+
export interface Adapter {
|
|
91
|
+
name: string;
|
|
92
|
+
/** Build the production server/output for this adapter. */
|
|
93
|
+
build(options: AdapterBuildOptions): Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
export interface AdapterBuildOptions {
|
|
96
|
+
/** Path to the built server entry. */
|
|
97
|
+
serverEntry: string;
|
|
98
|
+
/** Path to the client build output. */
|
|
99
|
+
clientOutDir: string;
|
|
100
|
+
/** Final output directory. */
|
|
101
|
+
outDir: string;
|
|
102
|
+
config: ZeroConfig;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observes an element and calls `onIntersect` once it enters the viewport.
|
|
3
|
+
* Automatically disconnects after the first intersection.
|
|
4
|
+
*
|
|
5
|
+
* @param getElement - Getter for the target element (may be undefined before mount).
|
|
6
|
+
* @param onIntersect - Callback fired when the element becomes visible.
|
|
7
|
+
* @param rootMargin - IntersectionObserver rootMargin. Default: "200px".
|
|
8
|
+
*/
|
|
9
|
+
export declare function useIntersectionObserver(getElement: () => HTMLElement | undefined, onIntersect: () => void, rootMargin?: string): void;
|
|
10
|
+
//# sourceMappingURL=use-intersection-observer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-intersection-observer.d.ts","sourceRoot":"","sources":["../../../src/utils/use-intersection-observer.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,WAAW,GAAG,SAAS,EACzC,WAAW,EAAE,MAAM,IAAI,EACvB,UAAU,SAAU,QAsBrB"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clone a Response with modified headers.
|
|
3
|
+
* Avoids repeating the `new Response(body, { status, statusText, headers })` pattern.
|
|
4
|
+
*/
|
|
5
|
+
export declare function withHeaders(response: Response, modify: (headers: Headers) => void): Response;
|
|
6
|
+
//# sourceMappingURL=with-headers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-headers.d.ts","sourceRoot":"","sources":["../../../src/utils/with-headers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GACjC,QAAQ,CAQV"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
import type { ZeroConfig } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Zero Vite plugin — adds file-based routing and zero-config conventions
|
|
5
|
+
* on top of @pyreon/vite-plugin.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // vite.config.ts
|
|
9
|
+
* import pyreon from "@pyreon/vite-plugin"
|
|
10
|
+
* import zero from "@pyreon/zero"
|
|
11
|
+
*
|
|
12
|
+
* export default {
|
|
13
|
+
* plugins: [pyreon(), zero()],
|
|
14
|
+
* }
|
|
15
|
+
*/
|
|
16
|
+
export declare function zeroPlugin(userConfig?: ZeroConfig): Plugin;
|
|
17
|
+
//# sourceMappingURL=vite-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAGlC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAKzC;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,UAAU,GAAE,UAAe,GAAG,MAAM,CAsE9D"}
|
package/package.json
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pyreon/zero",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pyreon Zero — zero-config full-stack framework powered by Pyreon and Vite",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Vit Bokisch",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/pyreon/zero",
|
|
10
|
+
"directory": "packages/zero"
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"files": [
|
|
14
|
+
"lib",
|
|
15
|
+
"!lib/analysis",
|
|
16
|
+
"src",
|
|
17
|
+
"!src/tests",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"main": "./lib/index.js",
|
|
22
|
+
"module": "./lib/index.js",
|
|
23
|
+
"types": "./lib/types/index.d.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"bun": "./src/index.ts",
|
|
27
|
+
"import": "./lib/index.js",
|
|
28
|
+
"types": "./lib/types/index.d.ts"
|
|
29
|
+
},
|
|
30
|
+
"./client": {
|
|
31
|
+
"bun": "./src/client.ts",
|
|
32
|
+
"import": "./lib/client.js",
|
|
33
|
+
"types": "./lib/types/client.d.ts"
|
|
34
|
+
},
|
|
35
|
+
"./config": {
|
|
36
|
+
"bun": "./src/config.ts",
|
|
37
|
+
"import": "./lib/config.js",
|
|
38
|
+
"types": "./lib/types/config.d.ts"
|
|
39
|
+
},
|
|
40
|
+
"./image": {
|
|
41
|
+
"bun": "./src/image.tsx",
|
|
42
|
+
"import": "./lib/image.js",
|
|
43
|
+
"types": "./lib/types/image.d.ts"
|
|
44
|
+
},
|
|
45
|
+
"./link": {
|
|
46
|
+
"bun": "./src/link.tsx",
|
|
47
|
+
"import": "./lib/link.js",
|
|
48
|
+
"types": "./lib/types/link.d.ts"
|
|
49
|
+
},
|
|
50
|
+
"./script": {
|
|
51
|
+
"bun": "./src/script.tsx",
|
|
52
|
+
"import": "./lib/script.js",
|
|
53
|
+
"types": "./lib/types/script.d.ts"
|
|
54
|
+
},
|
|
55
|
+
"./font": {
|
|
56
|
+
"bun": "./src/font.ts",
|
|
57
|
+
"import": "./lib/font.js",
|
|
58
|
+
"types": "./lib/types/font.d.ts"
|
|
59
|
+
},
|
|
60
|
+
"./cache": {
|
|
61
|
+
"bun": "./src/cache.ts",
|
|
62
|
+
"import": "./lib/cache.js",
|
|
63
|
+
"types": "./lib/types/cache.d.ts"
|
|
64
|
+
},
|
|
65
|
+
"./seo": {
|
|
66
|
+
"bun": "./src/seo.ts",
|
|
67
|
+
"import": "./lib/seo.js",
|
|
68
|
+
"types": "./lib/types/seo.d.ts"
|
|
69
|
+
},
|
|
70
|
+
"./theme": {
|
|
71
|
+
"bun": "./src/theme.tsx",
|
|
72
|
+
"import": "./lib/theme.js",
|
|
73
|
+
"types": "./lib/types/theme.d.ts"
|
|
74
|
+
},
|
|
75
|
+
"./image-plugin": {
|
|
76
|
+
"bun": "./src/image-plugin.ts",
|
|
77
|
+
"import": "./lib/image-plugin.js",
|
|
78
|
+
"types": "./lib/types/image-plugin.d.ts"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"scripts": {
|
|
82
|
+
"build": "vl_rolldown_build && tsc",
|
|
83
|
+
"dev": "vl_rolldown_build-watch",
|
|
84
|
+
"test": "vitest run",
|
|
85
|
+
"typecheck": "tsc --noEmit"
|
|
86
|
+
},
|
|
87
|
+
"dependencies": {
|
|
88
|
+
"@pyreon/core": "^0.2.1",
|
|
89
|
+
"@pyreon/head": "^0.2.1",
|
|
90
|
+
"@pyreon/router": "^0.2.1",
|
|
91
|
+
"@pyreon/runtime-dom": "^0.2.1",
|
|
92
|
+
"@pyreon/runtime-server": "^0.2.1",
|
|
93
|
+
"@pyreon/server": "^0.2.1",
|
|
94
|
+
"@pyreon/vite-plugin": "^0.2.1",
|
|
95
|
+
"vite": "^8.0.0"
|
|
96
|
+
},
|
|
97
|
+
"peerDependencies": {
|
|
98
|
+
"@pyreon/reactivity": "^0.2.1"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Adapter, AdapterBuildOptions } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bun adapter — generates a standalone Bun.serve() entry.
|
|
5
|
+
*/
|
|
6
|
+
export function bunAdapter(): Adapter {
|
|
7
|
+
return {
|
|
8
|
+
name: 'bun',
|
|
9
|
+
async build(options: AdapterBuildOptions) {
|
|
10
|
+
const { writeFile, cp, mkdir } = await import('node:fs/promises')
|
|
11
|
+
const { join } = await import('node:path')
|
|
12
|
+
|
|
13
|
+
const outDir = options.outDir
|
|
14
|
+
await mkdir(outDir, { recursive: true })
|
|
15
|
+
|
|
16
|
+
// Copy server and client builds
|
|
17
|
+
await cp(options.clientOutDir, join(outDir, 'client'), {
|
|
18
|
+
recursive: true,
|
|
19
|
+
})
|
|
20
|
+
await cp(join(options.serverEntry, '..'), join(outDir, 'server'), {
|
|
21
|
+
recursive: true,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const port = options.config.port ?? 3000
|
|
25
|
+
const serverEntry = `
|
|
26
|
+
const handler = (await import("./server/entry-server.js")).default
|
|
27
|
+
const clientDir = new URL("./client/", import.meta.url).pathname
|
|
28
|
+
|
|
29
|
+
Bun.serve({
|
|
30
|
+
port: ${port},
|
|
31
|
+
async fetch(req) {
|
|
32
|
+
const url = new URL(req.url)
|
|
33
|
+
|
|
34
|
+
// Try static files first
|
|
35
|
+
if (req.method === "GET") {
|
|
36
|
+
const filePath = clientDir + (url.pathname === "/" ? "index.html" : url.pathname)
|
|
37
|
+
// Prevent path traversal — ensure resolved path stays within clientDir
|
|
38
|
+
const resolved = Bun.resolveSync(filePath, ".")
|
|
39
|
+
if (!resolved.startsWith(Bun.resolveSync(clientDir, "."))) {
|
|
40
|
+
return new Response("Forbidden", { status: 403 })
|
|
41
|
+
}
|
|
42
|
+
const file = Bun.file(filePath)
|
|
43
|
+
if (await file.exists()) {
|
|
44
|
+
return new Response(file, {
|
|
45
|
+
headers: {
|
|
46
|
+
"cache-control": filePath.endsWith(".js") || filePath.endsWith(".css")
|
|
47
|
+
? "public, max-age=31536000, immutable"
|
|
48
|
+
: "public, max-age=3600",
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Fall through to SSR handler
|
|
55
|
+
return handler(req)
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
console.log("\\n ⚡ Zero production server running on http://localhost:${port}\\n")
|
|
60
|
+
`.trimStart()
|
|
61
|
+
|
|
62
|
+
await writeFile(join(outDir, 'index.ts'), serverEntry)
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export { bunAdapter } from './bun'
|
|
2
|
+
export { nodeAdapter } from './node'
|
|
3
|
+
export { staticAdapter } from './static'
|
|
4
|
+
|
|
5
|
+
import type { Adapter, ZeroConfig } from '../types'
|
|
6
|
+
import { bunAdapter } from './bun'
|
|
7
|
+
import { nodeAdapter } from './node'
|
|
8
|
+
import { staticAdapter } from './static'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the adapter from config.
|
|
12
|
+
* Returns a built-in adapter or throws if unknown.
|
|
13
|
+
*/
|
|
14
|
+
export function resolveAdapter(config: ZeroConfig): Adapter {
|
|
15
|
+
const name = config.adapter ?? 'node'
|
|
16
|
+
|
|
17
|
+
switch (name) {
|
|
18
|
+
case 'node':
|
|
19
|
+
return nodeAdapter()
|
|
20
|
+
case 'bun':
|
|
21
|
+
return bunAdapter()
|
|
22
|
+
case 'static':
|
|
23
|
+
return staticAdapter()
|
|
24
|
+
default:
|
|
25
|
+
throw new Error(
|
|
26
|
+
`[zero] Unknown adapter: "${name}". Use "node", "bun", or "static".`,
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { Adapter, AdapterBuildOptions } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Node.js adapter — generates a standalone server entry using node:http.
|
|
5
|
+
*/
|
|
6
|
+
export function nodeAdapter(): Adapter {
|
|
7
|
+
return {
|
|
8
|
+
name: 'node',
|
|
9
|
+
async build(options: AdapterBuildOptions) {
|
|
10
|
+
const { writeFile, cp, mkdir } = await import('node:fs/promises')
|
|
11
|
+
const { join } = await import('node:path')
|
|
12
|
+
|
|
13
|
+
const outDir = options.outDir
|
|
14
|
+
await mkdir(outDir, { recursive: true })
|
|
15
|
+
|
|
16
|
+
// Copy server and client builds
|
|
17
|
+
await cp(options.clientOutDir, join(outDir, 'client'), {
|
|
18
|
+
recursive: true,
|
|
19
|
+
})
|
|
20
|
+
await cp(join(options.serverEntry, '..'), join(outDir, 'server'), {
|
|
21
|
+
recursive: true,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
// Generate standalone server entry
|
|
25
|
+
const port = options.config.port ?? 3000
|
|
26
|
+
const serverEntry = `
|
|
27
|
+
import { createServer } from "node:http"
|
|
28
|
+
import { readFile } from "node:fs/promises"
|
|
29
|
+
import { join, extname } from "node:path"
|
|
30
|
+
import { fileURLToPath } from "node:url"
|
|
31
|
+
|
|
32
|
+
const __dirname = fileURLToPath(new URL(".", import.meta.url))
|
|
33
|
+
const handler = (await import("./server/entry-server.js")).default
|
|
34
|
+
const clientDir = join(__dirname, "client")
|
|
35
|
+
|
|
36
|
+
const MIME_TYPES = {
|
|
37
|
+
".html": "text/html",
|
|
38
|
+
".js": "application/javascript",
|
|
39
|
+
".css": "text/css",
|
|
40
|
+
".json": "application/json",
|
|
41
|
+
".png": "image/png",
|
|
42
|
+
".jpg": "image/jpeg",
|
|
43
|
+
".svg": "image/svg+xml",
|
|
44
|
+
".woff2": "font/woff2",
|
|
45
|
+
".woff": "font/woff",
|
|
46
|
+
".ico": "image/x-icon",
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const server = createServer(async (req, res) => {
|
|
50
|
+
const url = new URL(req.url ?? "/", "http://localhost")
|
|
51
|
+
|
|
52
|
+
// Try to serve static files first
|
|
53
|
+
if (req.method === "GET") {
|
|
54
|
+
try {
|
|
55
|
+
const filePath = join(clientDir, url.pathname === "/" ? "index.html" : url.pathname)
|
|
56
|
+
// Prevent path traversal — ensure resolved path stays within clientDir
|
|
57
|
+
const { resolve } = await import("node:path")
|
|
58
|
+
const resolved = resolve(filePath)
|
|
59
|
+
if (!resolved.startsWith(resolve(clientDir))) {
|
|
60
|
+
res.writeHead(403)
|
|
61
|
+
res.end("Forbidden")
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
const ext = extname(filePath)
|
|
65
|
+
if (ext && ext !== ".html") {
|
|
66
|
+
const data = await readFile(filePath)
|
|
67
|
+
const mime = MIME_TYPES[ext] || "application/octet-stream"
|
|
68
|
+
res.writeHead(200, {
|
|
69
|
+
"content-type": mime,
|
|
70
|
+
"cache-control": ext === ".js" || ext === ".css"
|
|
71
|
+
? "public, max-age=31536000, immutable"
|
|
72
|
+
: "public, max-age=3600",
|
|
73
|
+
})
|
|
74
|
+
res.end(data)
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
} catch {}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Fall through to SSR handler
|
|
81
|
+
const headers = {}
|
|
82
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
83
|
+
if (value) headers[key] = Array.isArray(value) ? value.join(", ") : value
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const request = new Request(url.href, {
|
|
87
|
+
method: req.method,
|
|
88
|
+
headers,
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
const response = await handler(request)
|
|
92
|
+
const body = await response.text()
|
|
93
|
+
|
|
94
|
+
const responseHeaders = {}
|
|
95
|
+
response.headers.forEach((v, k) => { responseHeaders[k] = v })
|
|
96
|
+
|
|
97
|
+
res.writeHead(response.status, responseHeaders)
|
|
98
|
+
res.end(body)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
server.listen(${port}, () => {
|
|
102
|
+
console.log("\\n ⚡ Zero production server running on http://localhost:${port}\\n")
|
|
103
|
+
})
|
|
104
|
+
`.trimStart()
|
|
105
|
+
|
|
106
|
+
await writeFile(join(outDir, 'index.js'), serverEntry)
|
|
107
|
+
await writeFile(
|
|
108
|
+
join(outDir, 'package.json'),
|
|
109
|
+
JSON.stringify({ type: 'module' }, null, 2),
|
|
110
|
+
)
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Adapter, AdapterBuildOptions } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Static adapter — just copies the client build output.
|
|
5
|
+
* Used with SSG mode where all pages are pre-rendered at build time.
|
|
6
|
+
*/
|
|
7
|
+
export function staticAdapter(): Adapter {
|
|
8
|
+
return {
|
|
9
|
+
name: 'static',
|
|
10
|
+
async build(options: AdapterBuildOptions) {
|
|
11
|
+
const { cp, mkdir } = await import('node:fs/promises')
|
|
12
|
+
|
|
13
|
+
await mkdir(options.outDir, { recursive: true })
|
|
14
|
+
await cp(options.clientOutDir, options.outDir, { recursive: true })
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
}
|
package/src/app.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ComponentFn, Props } from '@pyreon/core'
|
|
2
|
+
import { Fragment, h } from '@pyreon/core'
|
|
3
|
+
import { HeadProvider } from '@pyreon/head'
|
|
4
|
+
import type { RouteRecord } from '@pyreon/router'
|
|
5
|
+
import { createRouter, RouterProvider, RouterView } from '@pyreon/router'
|
|
6
|
+
|
|
7
|
+
// ─── App assembly ────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
export interface CreateAppOptions {
|
|
10
|
+
/** Route definitions (from file-based routing or manual). */
|
|
11
|
+
routes: RouteRecord[]
|
|
12
|
+
|
|
13
|
+
/** Router mode. Default: "history" for SSR, "hash" for SPA. */
|
|
14
|
+
routerMode?: 'hash' | 'history'
|
|
15
|
+
|
|
16
|
+
/** Initial URL for SSR. */
|
|
17
|
+
url?: string
|
|
18
|
+
|
|
19
|
+
/** Root layout component wrapping all routes. */
|
|
20
|
+
layout?: ComponentFn
|
|
21
|
+
|
|
22
|
+
/** Global error component. */
|
|
23
|
+
errorComponent?: ComponentFn
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Create a full Zero app — assembles router, head provider, and root layout.
|
|
28
|
+
*
|
|
29
|
+
* Used internally by entry-server and entry-client.
|
|
30
|
+
*/
|
|
31
|
+
export function createApp(options: CreateAppOptions) {
|
|
32
|
+
const router = createRouter({
|
|
33
|
+
routes: options.routes,
|
|
34
|
+
mode: options.routerMode ?? 'history',
|
|
35
|
+
url: options.url,
|
|
36
|
+
scrollBehavior: 'top',
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const Layout = options.layout ?? DefaultLayout
|
|
40
|
+
|
|
41
|
+
function App() {
|
|
42
|
+
return h(
|
|
43
|
+
HeadProvider,
|
|
44
|
+
null,
|
|
45
|
+
h(
|
|
46
|
+
RouterProvider as ComponentFn<Props>,
|
|
47
|
+
{ router },
|
|
48
|
+
h(Layout, null, h(RouterView as ComponentFn<Props>, null)),
|
|
49
|
+
),
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { App, router }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function DefaultLayout(props: Props) {
|
|
57
|
+
return h(
|
|
58
|
+
Fragment,
|
|
59
|
+
null,
|
|
60
|
+
...(Array.isArray(props.children) ? props.children : [props.children]),
|
|
61
|
+
)
|
|
62
|
+
}
|