@netrojs/fnetro 0.2.21 → 0.3.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/README.md +185 -878
- package/client.ts +213 -242
- package/core.ts +74 -175
- package/dist/client.d.ts +69 -60
- package/dist/client.js +170 -177
- package/dist/core.d.ts +57 -40
- package/dist/core.js +50 -28
- package/dist/server.d.ts +69 -66
- package/dist/server.js +178 -199
- package/dist/types.d.ts +99 -0
- package/package.json +21 -20
- package/server.ts +263 -350
- package/types.ts +125 -0
package/dist/core.js
CHANGED
|
@@ -1,16 +1,55 @@
|
|
|
1
|
+
// types.ts
|
|
2
|
+
var SPA_HEADER = "x-fnetro-spa";
|
|
3
|
+
var STATE_KEY = "__FNETRO_STATE__";
|
|
4
|
+
var PARAMS_KEY = "__FNETRO_PARAMS__";
|
|
5
|
+
var SEO_KEY = "__FNETRO_SEO__";
|
|
6
|
+
var DATA_KEY = /* @__PURE__ */ Symbol.for("fnetro:data");
|
|
7
|
+
|
|
1
8
|
// core.ts
|
|
9
|
+
var VUE_BRANDS = ["__name", "__file", "__vccOpts", "setup", "render", "data", "components"];
|
|
10
|
+
function isAsyncLoader(c) {
|
|
11
|
+
if (typeof c !== "function") return false;
|
|
12
|
+
const f = c;
|
|
13
|
+
for (const brand of VUE_BRANDS) {
|
|
14
|
+
if (brand in f) return false;
|
|
15
|
+
}
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
2
18
|
function definePage(def) {
|
|
3
19
|
return { __type: "page", ...def };
|
|
4
20
|
}
|
|
5
21
|
function defineGroup(def) {
|
|
6
22
|
return { __type: "group", ...def };
|
|
7
23
|
}
|
|
8
|
-
function defineLayout(
|
|
9
|
-
return { __type: "layout",
|
|
24
|
+
function defineLayout(component) {
|
|
25
|
+
return { __type: "layout", component };
|
|
10
26
|
}
|
|
11
27
|
function defineApiRoute(path, register) {
|
|
12
28
|
return { __type: "api", path, register };
|
|
13
29
|
}
|
|
30
|
+
function compilePath(path) {
|
|
31
|
+
const keys = [];
|
|
32
|
+
const src = path.replace(/\[\.\.\.([^\]]+)\]/g, (_, k) => {
|
|
33
|
+
keys.push(k);
|
|
34
|
+
return "(.*)";
|
|
35
|
+
}).replace(/\[([^\]]+)\]/g, (_, k) => {
|
|
36
|
+
keys.push(k);
|
|
37
|
+
return "([^/]+)";
|
|
38
|
+
}).replace(/\*/g, "(.*)");
|
|
39
|
+
return { re: new RegExp(`^${src}$`), keys };
|
|
40
|
+
}
|
|
41
|
+
function matchPath(cp, pathname) {
|
|
42
|
+
const m = pathname.match(cp.re);
|
|
43
|
+
if (!m) return null;
|
|
44
|
+
const params = {};
|
|
45
|
+
cp.keys.forEach((k, i) => {
|
|
46
|
+
params[k] = decodeURIComponent(m[i + 1] ?? "");
|
|
47
|
+
});
|
|
48
|
+
return params;
|
|
49
|
+
}
|
|
50
|
+
function toVueRouterPath(fnetroPath) {
|
|
51
|
+
return fnetroPath.replace(/\[\.\.\.([^\]]+)\]/g, ":$1(.*)*").replace(/\[([^\]]+)\]/g, ":$1");
|
|
52
|
+
}
|
|
14
53
|
function resolveRoutes(routes, options = {}) {
|
|
15
54
|
const pages = [];
|
|
16
55
|
const apis = [];
|
|
@@ -21,7 +60,11 @@ function resolveRoutes(routes, options = {}) {
|
|
|
21
60
|
const prefix = (options.prefix ?? "") + route.prefix;
|
|
22
61
|
const mw = [...options.middleware ?? [], ...route.middleware ?? []];
|
|
23
62
|
const layout = route.layout !== void 0 ? route.layout : options.layout;
|
|
24
|
-
const sub = resolveRoutes(route.routes, {
|
|
63
|
+
const sub = resolveRoutes(route.routes, {
|
|
64
|
+
prefix,
|
|
65
|
+
middleware: mw,
|
|
66
|
+
...layout !== void 0 && { layout }
|
|
67
|
+
});
|
|
25
68
|
pages.push(...sub.pages);
|
|
26
69
|
apis.push(...sub.apis);
|
|
27
70
|
} else {
|
|
@@ -35,31 +78,8 @@ function resolveRoutes(routes, options = {}) {
|
|
|
35
78
|
}
|
|
36
79
|
return { pages, apis };
|
|
37
80
|
}
|
|
38
|
-
function compilePath(path) {
|
|
39
|
-
const keys = [];
|
|
40
|
-
const src = path.replace(/\[\.\.\.([^\]]+)\]/g, (_, k) => {
|
|
41
|
-
keys.push(k);
|
|
42
|
-
return "(.*)";
|
|
43
|
-
}).replace(/\[([^\]]+)\]/g, (_, k) => {
|
|
44
|
-
keys.push(k);
|
|
45
|
-
return "([^/]+)";
|
|
46
|
-
}).replace(/\*/g, "(.*)");
|
|
47
|
-
return { re: new RegExp(`^${src}$`), keys };
|
|
48
|
-
}
|
|
49
|
-
function matchPath(compiled, pathname) {
|
|
50
|
-
const m = pathname.match(compiled.re);
|
|
51
|
-
if (!m) return null;
|
|
52
|
-
const params = {};
|
|
53
|
-
compiled.keys.forEach((k, i) => {
|
|
54
|
-
params[k] = decodeURIComponent(m[i + 1] ?? "");
|
|
55
|
-
});
|
|
56
|
-
return params;
|
|
57
|
-
}
|
|
58
|
-
var SPA_HEADER = "x-fnetro-spa";
|
|
59
|
-
var STATE_KEY = "__FNETRO_STATE__";
|
|
60
|
-
var PARAMS_KEY = "__FNETRO_PARAMS__";
|
|
61
|
-
var SEO_KEY = "__FNETRO_SEO__";
|
|
62
81
|
export {
|
|
82
|
+
DATA_KEY,
|
|
63
83
|
PARAMS_KEY,
|
|
64
84
|
SEO_KEY,
|
|
65
85
|
SPA_HEADER,
|
|
@@ -69,6 +89,8 @@ export {
|
|
|
69
89
|
defineGroup,
|
|
70
90
|
defineLayout,
|
|
71
91
|
definePage,
|
|
92
|
+
isAsyncLoader,
|
|
72
93
|
matchPath,
|
|
73
|
-
resolveRoutes
|
|
94
|
+
resolveRoutes,
|
|
95
|
+
toVueRouterPath
|
|
74
96
|
};
|
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Hono, MiddlewareHandler, Context } from 'hono';
|
|
2
|
-
import { Component
|
|
2
|
+
import { Component } from 'vue';
|
|
3
3
|
import { Plugin } from 'vite';
|
|
4
4
|
|
|
5
5
|
type HonoMiddleware = MiddlewareHandler;
|
|
@@ -16,44 +16,33 @@ interface SEOMeta {
|
|
|
16
16
|
ogDescription?: string;
|
|
17
17
|
ogImage?: string;
|
|
18
18
|
ogImageAlt?: string;
|
|
19
|
-
ogImageWidth?: string;
|
|
20
|
-
ogImageHeight?: string;
|
|
21
19
|
ogUrl?: string;
|
|
22
20
|
ogType?: string;
|
|
23
21
|
ogSiteName?: string;
|
|
24
|
-
ogLocale?: string;
|
|
25
22
|
twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player';
|
|
26
23
|
twitterSite?: string;
|
|
27
|
-
twitterCreator?: string;
|
|
28
24
|
twitterTitle?: string;
|
|
29
25
|
twitterDescription?: string;
|
|
30
26
|
twitterImage?: string;
|
|
31
|
-
|
|
27
|
+
/** Structured data injected as <script type="application/ld+json">. */
|
|
32
28
|
jsonLd?: Record<string, unknown> | Record<string, unknown>[];
|
|
33
|
-
extra?: Array<{
|
|
34
|
-
name?: string;
|
|
35
|
-
property?: string;
|
|
36
|
-
httpEquiv?: string;
|
|
37
|
-
content: string;
|
|
38
|
-
}>;
|
|
39
29
|
}
|
|
40
|
-
type
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
interface LayoutProps {
|
|
45
|
-
children: JSX.Element;
|
|
46
|
-
url: string;
|
|
47
|
-
params: Record<string, string>;
|
|
48
|
-
}
|
|
49
|
-
interface PageDef<TData extends object = {}> {
|
|
30
|
+
type AsyncLoader = () => Promise<{
|
|
31
|
+
default: Component;
|
|
32
|
+
} | Component>;
|
|
33
|
+
interface PageDef<TData extends object = Record<string, never>> {
|
|
50
34
|
readonly __type: 'page';
|
|
51
35
|
path: string;
|
|
52
36
|
middleware?: HonoMiddleware[];
|
|
53
37
|
loader?: (c: LoaderCtx) => TData | Promise<TData>;
|
|
54
38
|
seo?: SEOMeta | ((data: TData, params: Record<string, string>) => SEOMeta);
|
|
39
|
+
/** Override or disable the app-level layout for this route. */
|
|
55
40
|
layout?: LayoutDef | false;
|
|
56
|
-
|
|
41
|
+
/**
|
|
42
|
+
* The Vue component to render for this route.
|
|
43
|
+
* Use () => import('./Page.vue') for automatic code splitting.
|
|
44
|
+
*/
|
|
45
|
+
component: Component | AsyncLoader;
|
|
57
46
|
}
|
|
58
47
|
interface GroupDef {
|
|
59
48
|
readonly __type: 'group';
|
|
@@ -64,7 +53,8 @@ interface GroupDef {
|
|
|
64
53
|
}
|
|
65
54
|
interface LayoutDef {
|
|
66
55
|
readonly __type: 'layout';
|
|
67
|
-
|
|
56
|
+
/** Vue layout component — must contain <slot /> for page content. */
|
|
57
|
+
component: Component;
|
|
68
58
|
}
|
|
69
59
|
interface ApiRouteDef {
|
|
70
60
|
readonly __type: 'api';
|
|
@@ -79,66 +69,79 @@ interface AppConfig {
|
|
|
79
69
|
routes: Route[];
|
|
80
70
|
notFound?: Component;
|
|
81
71
|
htmlAttrs?: Record<string, string>;
|
|
72
|
+
/** Extra HTML injected into <head> (e.g. font preloads). */
|
|
82
73
|
head?: string;
|
|
83
74
|
}
|
|
84
|
-
type ClientMiddleware = (url: string, next: () => Promise<void>) => Promise<void>;
|
|
85
|
-
declare function definePage<TData extends object = {}>(def: Omit<PageDef<TData>, '__type'>): PageDef<TData>;
|
|
86
|
-
declare function defineGroup(def: Omit<GroupDef, '__type'>): GroupDef;
|
|
87
|
-
declare function defineLayout(Component: Component<LayoutProps>): LayoutDef;
|
|
88
|
-
declare function defineApiRoute(path: string, register: ApiRouteDef['register']): ApiRouteDef;
|
|
89
75
|
interface ResolvedRoute {
|
|
90
76
|
fullPath: string;
|
|
91
77
|
page: PageDef<any>;
|
|
92
78
|
layout: LayoutDef | false | undefined;
|
|
93
79
|
middleware: HonoMiddleware[];
|
|
94
80
|
}
|
|
95
|
-
declare function resolveRoutes(routes: Route[], options?: {
|
|
96
|
-
prefix?: string;
|
|
97
|
-
middleware?: HonoMiddleware[];
|
|
98
|
-
layout?: LayoutDef | false;
|
|
99
|
-
}): {
|
|
100
|
-
pages: ResolvedRoute[];
|
|
101
|
-
apis: ApiRouteDef[];
|
|
102
|
-
};
|
|
103
81
|
interface CompiledPath {
|
|
104
82
|
re: RegExp;
|
|
105
83
|
keys: string[];
|
|
106
84
|
}
|
|
107
|
-
|
|
108
|
-
|
|
85
|
+
type ClientMiddleware = (url: string, next: () => Promise<void>) => Promise<void>;
|
|
86
|
+
/** Custom request header that identifies an SPA navigation (JSON payload). */
|
|
109
87
|
declare const SPA_HEADER = "x-fnetro-spa";
|
|
88
|
+
/** window key for SSR-injected per-page loader data. */
|
|
110
89
|
declare const STATE_KEY = "__FNETRO_STATE__";
|
|
90
|
+
/** window key for SSR-injected URL params. */
|
|
111
91
|
declare const PARAMS_KEY = "__FNETRO_PARAMS__";
|
|
92
|
+
/** window key for SSR-injected SEO meta. */
|
|
112
93
|
declare const SEO_KEY = "__FNETRO_SEO__";
|
|
94
|
+
/**
|
|
95
|
+
* Vue provide/inject key for the reactive page-data object.
|
|
96
|
+
* Symbol.for() ensures the same reference across module instances (SSR safe).
|
|
97
|
+
*/
|
|
98
|
+
declare const DATA_KEY: unique symbol;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Returns true when `c` is an async factory function (i.e. `() => import(...)`)
|
|
102
|
+
* rather than a resolved Vue component object.
|
|
103
|
+
*
|
|
104
|
+
* Used by both server.ts (to resolve the import before SSR) and client.ts
|
|
105
|
+
* (to wrap with defineAsyncComponent for lazy hydration).
|
|
106
|
+
*/
|
|
107
|
+
declare function isAsyncLoader(c: unknown): c is AsyncLoader;
|
|
108
|
+
declare function definePage<TData extends object = Record<string, never>>(def: Omit<PageDef<TData>, '__type'>): PageDef<TData>;
|
|
109
|
+
declare function defineGroup(def: Omit<GroupDef, '__type'>): GroupDef;
|
|
110
|
+
/** Wrap a Vue layout component (must render <slot />) as a FNetro layout. */
|
|
111
|
+
declare function defineLayout(component: Component): LayoutDef;
|
|
112
|
+
declare function defineApiRoute(path: string, register: ApiRouteDef['register']): ApiRouteDef;
|
|
113
|
+
declare function compilePath(path: string): CompiledPath;
|
|
114
|
+
declare function matchPath(cp: CompiledPath, pathname: string): Record<string, string> | null;
|
|
115
|
+
/**
|
|
116
|
+
* Convert FNetro `[param]` syntax to Vue Router `:param` syntax.
|
|
117
|
+
*
|
|
118
|
+
* `/posts/[slug]` → `/posts/:slug`
|
|
119
|
+
* `/files/[...path]` → `/files/:path(.*)*`
|
|
120
|
+
*/
|
|
121
|
+
declare function toVueRouterPath(fnetroPath: string): string;
|
|
122
|
+
declare function resolveRoutes(routes: Route[], options?: {
|
|
123
|
+
prefix?: string;
|
|
124
|
+
middleware?: HonoMiddleware[];
|
|
125
|
+
layout?: LayoutDef | false;
|
|
126
|
+
}): {
|
|
127
|
+
pages: ResolvedRoute[];
|
|
128
|
+
apis: ApiRouteDef[];
|
|
129
|
+
};
|
|
113
130
|
|
|
114
131
|
interface AssetConfig {
|
|
115
|
-
/** Explicit script URLs injected into every HTML page. */
|
|
116
132
|
scripts?: string[];
|
|
117
|
-
/** Explicit stylesheet URLs injected into every HTML page. */
|
|
118
133
|
styles?: string[];
|
|
119
|
-
/**
|
|
120
|
-
* Directory that contains the Vite-generated `manifest.json`.
|
|
121
|
-
* When provided, asset URLs are resolved from the manifest so hashed
|
|
122
|
-
* filenames work correctly. Typically equals `clientOutDir`.
|
|
123
|
-
*/
|
|
134
|
+
/** Directory containing the Vite-built assets and .vite/manifest.json. */
|
|
124
135
|
manifestDir?: string;
|
|
125
|
-
/**
|
|
126
|
-
* Key in the manifest corresponding to the client entry file.
|
|
127
|
-
* @default `'client.ts'`
|
|
128
|
-
*/
|
|
129
136
|
manifestEntry?: string;
|
|
130
137
|
}
|
|
131
138
|
interface FNetroOptions extends AppConfig {
|
|
132
|
-
/**
|
|
133
|
-
* Production asset configuration.
|
|
134
|
-
* In dev mode `@hono/vite-dev-server` injects assets automatically — ignored.
|
|
135
|
-
*/
|
|
136
139
|
assets?: AssetConfig;
|
|
137
140
|
}
|
|
138
141
|
interface FNetroApp {
|
|
139
|
-
/** The
|
|
142
|
+
/** The Hono instance — attach extra routes, error handlers, middleware. */
|
|
140
143
|
app: Hono;
|
|
141
|
-
/**
|
|
144
|
+
/** WinterCG-compatible fetch handler for edge runtimes. */
|
|
142
145
|
handler: typeof Hono.prototype.fetch;
|
|
143
146
|
}
|
|
144
147
|
declare function createFNetro(config: FNetroOptions): FNetroApp;
|
|
@@ -149,24 +152,24 @@ interface ServeOptions {
|
|
|
149
152
|
port?: number;
|
|
150
153
|
hostname?: string;
|
|
151
154
|
runtime?: Runtime;
|
|
152
|
-
/** Root directory
|
|
155
|
+
/** Root directory that contains the built assets and public files. */
|
|
153
156
|
staticDir?: string;
|
|
154
157
|
}
|
|
155
158
|
declare function serve(opts: ServeOptions): Promise<void>;
|
|
156
159
|
interface FNetroPluginOptions {
|
|
157
|
-
/** Server entry file.
|
|
160
|
+
/** Server entry file. @default 'server.ts' */
|
|
158
161
|
serverEntry?: string;
|
|
159
|
-
/** Client entry file.
|
|
162
|
+
/** Client entry file. @default 'client.ts' */
|
|
160
163
|
clientEntry?: string;
|
|
161
|
-
/** Server bundle output
|
|
164
|
+
/** Server bundle output dir. @default 'dist/server' */
|
|
162
165
|
serverOutDir?: string;
|
|
163
|
-
/** Client assets output
|
|
166
|
+
/** Client assets output dir. @default 'dist/assets' */
|
|
164
167
|
clientOutDir?: string;
|
|
165
|
-
/** Extra packages
|
|
168
|
+
/** Extra packages external to the server bundle. */
|
|
166
169
|
serverExternal?: string[];
|
|
167
|
-
/**
|
|
168
|
-
|
|
170
|
+
/** Options forwarded to @vitejs/plugin-vue in the client build. */
|
|
171
|
+
vueOptions?: Record<string, unknown>;
|
|
169
172
|
}
|
|
170
|
-
declare function fnetroVitePlugin(opts?: FNetroPluginOptions): Plugin
|
|
173
|
+
declare function fnetroVitePlugin(opts?: FNetroPluginOptions): Plugin;
|
|
171
174
|
|
|
172
|
-
export { type ApiRouteDef, type AppConfig, type AssetConfig, type ClientMiddleware, type CompiledPath, type FNetroApp, type FNetroOptions, type FNetroPluginOptions, type GroupDef, type HonoMiddleware, type LayoutDef, type
|
|
175
|
+
export { type ApiRouteDef, type AppConfig, type AssetConfig, type AsyncLoader, type ClientMiddleware, type CompiledPath, DATA_KEY, type FNetroApp, type FNetroOptions, type FNetroPluginOptions, type GroupDef, type HonoMiddleware, type LayoutDef, type LoaderCtx, PARAMS_KEY, type PageDef, type ResolvedRoute, type Route, type Runtime, type SEOMeta, SEO_KEY, SPA_HEADER, STATE_KEY, type ServeOptions, compilePath, createFNetro, defineApiRoute, defineGroup, defineLayout, definePage, detectRuntime, fnetroVitePlugin, isAsyncLoader, matchPath, resolveRoutes, serve, toVueRouterPath };
|