weifuwu 0.33.0 → 0.33.2
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 +303 -145
- package/dist/client/app.d.ts +21 -0
- package/dist/client/index.d.ts +21 -0
- package/dist/client/index.js +320 -0
- package/dist/client/jsx-runtime.d.ts +60 -0
- package/dist/client/jsx-runtime.js +320 -0
- package/dist/client/router.d.ts +51 -0
- package/dist/client/signal.d.ts +19 -0
- package/dist/client/types.d.ts +45 -0
- package/dist/index.d.ts +0 -7
- package/dist/index.js +13 -925
- package/dist/types.d.ts +0 -1
- package/package.json +9 -42
- package/dist/middleware/esbuild-dev.d.ts +0 -69
- package/dist/middleware/esbuild-dev.js +0 -335
- package/dist/middleware/tailwind-dev.d.ts +0 -43
- package/dist/middleware/tailwind-dev.js +0 -199
- package/dist/react/client.d.ts +0 -76
- package/dist/react/client.js +0 -182
- package/dist/react/compile.d.ts +0 -17
- package/dist/react/context.d.ts +0 -2
- package/dist/react/error-boundary.d.ts +0 -30
- package/dist/react/hooks.d.ts +0 -6
- package/dist/react/index.d.ts +0 -92
- package/dist/react/index.js +0 -910
- package/dist/react/types.d.ts +0 -146
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
// src/middleware/tailwind-dev.ts
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
|
-
import { stat, readFile, glob } from "node:fs/promises";
|
|
4
|
-
function escapeHtml(s) {
|
|
5
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
6
|
-
}
|
|
7
|
-
function defaultErrorTemplate(errors) {
|
|
8
|
-
return `<!DOCTYPE html>
|
|
9
|
-
<html>
|
|
10
|
-
<head>
|
|
11
|
-
<meta charset="utf-8">
|
|
12
|
-
<title>Tailwind Build Error</title>
|
|
13
|
-
<style>
|
|
14
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
15
|
-
body { font-family: system-ui; background: #1a1a2e; color: #e0e0e0; padding: 2rem; }
|
|
16
|
-
.overlay { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; }
|
|
17
|
-
.card { background: #16213e; border: 1px solid #06b6d4; border-radius: 12px; padding: 2rem; max-width: 800px; width: 100%; box-shadow: 0 8px 32px rgba(0,0,0,0.4); }
|
|
18
|
-
h1 { color: #06b6d4; font-size: 1.5rem; margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem; }
|
|
19
|
-
pre { background: #0f0f23; padding: 1.5rem; border-radius: 8px; overflow: auto; font-size: 0.85rem; line-height: 1.6; white-space: pre-wrap; word-break: break-word; }
|
|
20
|
-
</style>
|
|
21
|
-
</head>
|
|
22
|
-
<body>
|
|
23
|
-
<div class="overlay">
|
|
24
|
-
<div class="card">
|
|
25
|
-
<h1>\u{1F3A8} Tailwind CSS Build Error</h1>
|
|
26
|
-
<pre>${escapeHtml(errors)}</pre>
|
|
27
|
-
</div>
|
|
28
|
-
</div>
|
|
29
|
-
</body>
|
|
30
|
-
</html>`;
|
|
31
|
-
}
|
|
32
|
-
var TW_CANDIDATE_RE = /[a-z][\w-]*(?::[a-z][\w-]*)*(?:-\[[^\]]*\])*(?:\/[0-9]+)?/gi;
|
|
33
|
-
var CLASS_CONTEXT_RE = /(?:class(?:Name)?\s*[=:]\s*["'`]([^"'`]+)["'`]|["'`]([^"'`\n]{2,})["'`])/g;
|
|
34
|
-
async function extractCandidates(patterns, root) {
|
|
35
|
-
const seen = /* @__PURE__ */ new Set();
|
|
36
|
-
for (const pattern of patterns) {
|
|
37
|
-
const resolved = resolve(root, pattern);
|
|
38
|
-
try {
|
|
39
|
-
for await (const file of glob(resolved)) {
|
|
40
|
-
try {
|
|
41
|
-
const content = await readFile(file, "utf-8");
|
|
42
|
-
for (const m of content.matchAll(CLASS_CONTEXT_RE)) {
|
|
43
|
-
const str = m[1] ?? m[2];
|
|
44
|
-
if (!str) continue;
|
|
45
|
-
for (const c of str.matchAll(TW_CANDIDATE_RE)) {
|
|
46
|
-
seen.add(c[0]);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
} catch {
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
} catch {
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return [...seen];
|
|
56
|
-
}
|
|
57
|
-
async function collectFileMtimes(files) {
|
|
58
|
-
const map = /* @__PURE__ */ new Map();
|
|
59
|
-
await Promise.all(
|
|
60
|
-
files.map(async (f) => {
|
|
61
|
-
try {
|
|
62
|
-
const s = await stat(f);
|
|
63
|
-
map.set(f, s.mtimeMs);
|
|
64
|
-
} catch {
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
);
|
|
68
|
-
return map;
|
|
69
|
-
}
|
|
70
|
-
function isCacheValid(cached) {
|
|
71
|
-
return Promise.all(
|
|
72
|
-
[...cached.files].map(async ([file, mtime]) => {
|
|
73
|
-
try {
|
|
74
|
-
const s = await stat(file);
|
|
75
|
-
return s.mtimeMs === mtime;
|
|
76
|
-
} catch {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
})
|
|
80
|
-
).then((results) => results.every(Boolean));
|
|
81
|
-
}
|
|
82
|
-
function tailwindDev(opts) {
|
|
83
|
-
const cacheMode = opts.cache ?? "memory";
|
|
84
|
-
const errorTemplate = opts.errorTemplate ?? defaultErrorTemplate;
|
|
85
|
-
const entries = Object.entries(opts.entries).map(([p, e]) => {
|
|
86
|
-
const path = p.startsWith("/") ? p : "/" + p;
|
|
87
|
-
const config = typeof e === "string" ? { entry: e } : e;
|
|
88
|
-
return { path, config };
|
|
89
|
-
});
|
|
90
|
-
const cacheStore = /* @__PURE__ */ new Map();
|
|
91
|
-
let compileFn = null;
|
|
92
|
-
let loadError = null;
|
|
93
|
-
async function getCompile() {
|
|
94
|
-
if (compileFn) return compileFn;
|
|
95
|
-
try {
|
|
96
|
-
const mod = await import("@tailwindcss/node");
|
|
97
|
-
compileFn = mod.compile;
|
|
98
|
-
return compileFn;
|
|
99
|
-
} catch (err) {
|
|
100
|
-
loadError = `@tailwindcss/node is not installed. Run: npm install -D tailwindcss @tailwindcss/node
|
|
101
|
-
|
|
102
|
-
${String(err)}`;
|
|
103
|
-
throw new Error(loadError, { cause: err });
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
async function compileCss(entry) {
|
|
107
|
-
const compile = await getCompile();
|
|
108
|
-
const entryAbs = resolve(entry.entry);
|
|
109
|
-
const cssSource = await readFile(entryAbs, "utf-8");
|
|
110
|
-
const baseDir = resolve(entry.entry, "..");
|
|
111
|
-
const deps = [entryAbs];
|
|
112
|
-
const result = await compile(cssSource, {
|
|
113
|
-
base: baseDir,
|
|
114
|
-
onDependency: (p) => {
|
|
115
|
-
if (!deps.includes(p)) deps.push(p);
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
const contentPatterns = entry.content ?? [];
|
|
119
|
-
for (const src of result.sources) {
|
|
120
|
-
if (!src.negated) {
|
|
121
|
-
contentPatterns.push(resolve(src.base, src.pattern));
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
let candidates = [];
|
|
125
|
-
if (contentPatterns.length > 0) {
|
|
126
|
-
candidates = await extractCandidates(contentPatterns, baseDir);
|
|
127
|
-
}
|
|
128
|
-
const css = result.build(candidates);
|
|
129
|
-
for (const pattern of contentPatterns) {
|
|
130
|
-
try {
|
|
131
|
-
for await (const file of glob(resolve(baseDir, pattern))) {
|
|
132
|
-
if (!deps.includes(file)) deps.push(file);
|
|
133
|
-
}
|
|
134
|
-
} catch {
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
const etag = `"tw-${hashCode(css)}"`;
|
|
138
|
-
return { css, etag, files: deps };
|
|
139
|
-
}
|
|
140
|
-
return async (req, ctx, next) => {
|
|
141
|
-
const url = new URL(req.url);
|
|
142
|
-
const pathname = url.pathname;
|
|
143
|
-
const matched = entries.find((e) => e.path === pathname);
|
|
144
|
-
if (!matched) return next(req, ctx);
|
|
145
|
-
const { config } = matched;
|
|
146
|
-
try {
|
|
147
|
-
if (cacheMode === "memory") {
|
|
148
|
-
const cached = cacheStore.get(pathname);
|
|
149
|
-
if (cached) {
|
|
150
|
-
const valid = await isCacheValid(cached);
|
|
151
|
-
if (valid) {
|
|
152
|
-
if (req.headers.get("if-none-match") === cached.etag) {
|
|
153
|
-
return new Response(null, { status: 304 });
|
|
154
|
-
}
|
|
155
|
-
return new Response(cached.css, {
|
|
156
|
-
headers: {
|
|
157
|
-
"Content-Type": "text/css; charset=utf-8",
|
|
158
|
-
ETag: cached.etag,
|
|
159
|
-
"Cache-Control": "no-cache"
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
cacheStore.delete(pathname);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
const { css, etag, files } = await compileCss(config);
|
|
167
|
-
if (cacheMode === "memory") {
|
|
168
|
-
const mtimes = await collectFileMtimes(files);
|
|
169
|
-
cacheStore.set(pathname, { css, etag, files: mtimes });
|
|
170
|
-
}
|
|
171
|
-
return new Response(css, {
|
|
172
|
-
headers: {
|
|
173
|
-
"Content-Type": "text/css; charset=utf-8",
|
|
174
|
-
ETag: etag,
|
|
175
|
-
"Cache-Control": "no-cache"
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
} catch (err) {
|
|
179
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
180
|
-
const html = errorTemplate(message);
|
|
181
|
-
return new Response(html, {
|
|
182
|
-
status: 500,
|
|
183
|
-
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
function hashCode(s) {
|
|
189
|
-
let hash = 0;
|
|
190
|
-
for (let i = 0; i < s.length; i++) {
|
|
191
|
-
const ch = s.charCodeAt(i);
|
|
192
|
-
hash = (hash << 5) - hash + ch;
|
|
193
|
-
hash |= 0;
|
|
194
|
-
}
|
|
195
|
-
return Math.abs(hash).toString(36);
|
|
196
|
-
}
|
|
197
|
-
export {
|
|
198
|
-
tailwindDev
|
|
199
|
-
};
|
package/dist/react/client.d.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client-side entry for React hydration and SPA routing.
|
|
3
|
-
*
|
|
4
|
-
* Hydration:
|
|
5
|
-
* ```ts
|
|
6
|
-
* import { hydrate } from 'weifuwu/react/client'
|
|
7
|
-
* hydrate(HomePage, { layout: PageShell })
|
|
8
|
-
* ```
|
|
9
|
-
*
|
|
10
|
-
* Client-side router:
|
|
11
|
-
* ```ts
|
|
12
|
-
* import { createBrowserRouter } from 'weifuwu/react/client'
|
|
13
|
-
* createBrowserRouter({
|
|
14
|
-
* layout: PageShell,
|
|
15
|
-
* routes: {
|
|
16
|
-
* '/': () => import('./pages/HomePage.tsx'),
|
|
17
|
-
* '/users/:id': () => import('./pages/UserPage.tsx'),
|
|
18
|
-
* },
|
|
19
|
-
* })
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
import { type ComponentType, type ReactNode } from 'react';
|
|
23
|
-
export interface HydrateOptions {
|
|
24
|
-
/** Hydration target element. Default: document.getElementById('root'). */
|
|
25
|
-
container?: HTMLElement;
|
|
26
|
-
/**
|
|
27
|
-
* Layout component matching the server-side `react({ layout })` config.
|
|
28
|
-
* Must accept `{ children: ReactNode }`.
|
|
29
|
-
* When provided, wraps the page component before hydrating —
|
|
30
|
-
* producing the same tree the server rendered.
|
|
31
|
-
*/
|
|
32
|
-
layout?: ComponentType<{
|
|
33
|
-
children: ReactNode;
|
|
34
|
-
}>;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Hydrate a page component. When `layout` is provided, the hydration tree
|
|
38
|
-
* matches the server-rendered structure: `<div id="root"><Layout><App /></Layout></div>`.
|
|
39
|
-
*/
|
|
40
|
-
export declare function hydrate(App: ComponentType, opts?: HydrateOptions): void;
|
|
41
|
-
export interface ClientRouterOptions {
|
|
42
|
-
/** Layout component shared across all routes. */
|
|
43
|
-
layout?: ComponentType<{
|
|
44
|
-
children: ReactNode;
|
|
45
|
-
}>;
|
|
46
|
-
/**
|
|
47
|
-
* Route map: URL pattern → lazy component loader.
|
|
48
|
-
* Patterns support `:param` segments (e.g. `/users/:id`).
|
|
49
|
-
*/
|
|
50
|
-
routes: Record<string, () => Promise<Record<string, unknown>>>;
|
|
51
|
-
/**
|
|
52
|
-
* Fallback component for unmatched routes.
|
|
53
|
-
* When omitted, unmatched routes do a full page navigation.
|
|
54
|
-
*/
|
|
55
|
-
fallback?: () => Promise<Record<string, unknown>>;
|
|
56
|
-
/** Target element. Default: document.getElementById('root'). */
|
|
57
|
-
container?: HTMLElement;
|
|
58
|
-
}
|
|
59
|
-
interface RouterHandle {
|
|
60
|
-
navigate(url: string): Promise<void>;
|
|
61
|
-
renderPath(pathname: string, isInitial: boolean, data?: Record<string, unknown>): Promise<void>;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Initialise the client-side router.
|
|
65
|
-
*
|
|
66
|
-
* On the initial page load, it hydrates the matching route from the
|
|
67
|
-
* server-rendered HTML. Subsequent in-app navigations fetch fresh
|
|
68
|
-
* server data and render the new page client-side.
|
|
69
|
-
*/
|
|
70
|
-
export declare function createBrowserRouter(opts: ClientRouterOptions): RouterHandle;
|
|
71
|
-
/**
|
|
72
|
-
* Programmatic navigation (call from anywhere).
|
|
73
|
-
* Falls back to `window.location.href = url` if the router hasn't been initialised.
|
|
74
|
-
*/
|
|
75
|
-
export declare function navigate(url: string): void;
|
|
76
|
-
export { useServerData } from './hooks.ts';
|
package/dist/react/client.js
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
// src/react/client.ts
|
|
2
|
-
import { createElement } from "react";
|
|
3
|
-
import { hydrateRoot, createRoot } from "react-dom/client";
|
|
4
|
-
|
|
5
|
-
// src/react/context.ts
|
|
6
|
-
import { createContext } from "react";
|
|
7
|
-
var CTX_KEY = /* @__PURE__ */ Symbol.for("weifuwu.react.ServerDataContext");
|
|
8
|
-
function getServerDataContext() {
|
|
9
|
-
const globalStore = globalThis;
|
|
10
|
-
if (globalStore[CTX_KEY]) return globalStore[CTX_KEY];
|
|
11
|
-
const ctx = createContext({});
|
|
12
|
-
ctx.displayName = "ServerData";
|
|
13
|
-
globalStore[CTX_KEY] = ctx;
|
|
14
|
-
return ctx;
|
|
15
|
-
}
|
|
16
|
-
var ServerDataContext = getServerDataContext();
|
|
17
|
-
|
|
18
|
-
// src/react/hooks.ts
|
|
19
|
-
import { useContext } from "react";
|
|
20
|
-
function useServerData() {
|
|
21
|
-
return useContext(ServerDataContext);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// src/react/client.ts
|
|
25
|
-
function readInitialData() {
|
|
26
|
-
if (typeof document === "undefined") return {};
|
|
27
|
-
const el = document.getElementById("__WEIFUWU_DATA__");
|
|
28
|
-
if (el?.textContent) {
|
|
29
|
-
try {
|
|
30
|
-
return JSON.parse(el.textContent);
|
|
31
|
-
} catch {
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return {};
|
|
35
|
-
}
|
|
36
|
-
async function fetchPageData(url) {
|
|
37
|
-
const res = await fetch(url);
|
|
38
|
-
const html = await res.text();
|
|
39
|
-
const match = html.match(/<script[^>]*id="__WEIFUWU_DATA__"[^>]*>([\s\S]*?)<\/script>/);
|
|
40
|
-
if (match) {
|
|
41
|
-
try {
|
|
42
|
-
return JSON.parse(match[1]);
|
|
43
|
-
} catch {
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return {};
|
|
47
|
-
}
|
|
48
|
-
function compilePattern(pattern) {
|
|
49
|
-
const paramNames = [];
|
|
50
|
-
const regexStr = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/:([^/]+)/g, (_, name) => {
|
|
51
|
-
paramNames.push(name);
|
|
52
|
-
return "([^/]+)";
|
|
53
|
-
});
|
|
54
|
-
return { regex: new RegExp(`^${regexStr}$`), paramNames };
|
|
55
|
-
}
|
|
56
|
-
function matchRoute(pathname, routes) {
|
|
57
|
-
for (const route of routes) {
|
|
58
|
-
const m = pathname.match(route.regex);
|
|
59
|
-
if (m) {
|
|
60
|
-
const params = {};
|
|
61
|
-
route.paramNames.forEach((name, i) => {
|
|
62
|
-
params[name] = m[i + 1];
|
|
63
|
-
});
|
|
64
|
-
return { route, params };
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
function extractComponent(mod) {
|
|
70
|
-
if (mod.default && typeof mod.default === "function") return mod.default;
|
|
71
|
-
for (const val of Object.values(mod)) {
|
|
72
|
-
if (typeof val === "function") return val;
|
|
73
|
-
}
|
|
74
|
-
throw new Error("No component export found in module");
|
|
75
|
-
}
|
|
76
|
-
function hydrate(App, opts) {
|
|
77
|
-
const container = opts?.container ?? document.getElementById("root");
|
|
78
|
-
const data = readInitialData();
|
|
79
|
-
let element = createElement(App);
|
|
80
|
-
if (opts?.layout) {
|
|
81
|
-
element = createElement(opts.layout, { children: element });
|
|
82
|
-
}
|
|
83
|
-
hydrateRoot(
|
|
84
|
-
container,
|
|
85
|
-
createElement(ServerDataContext.Provider, { value: data }, element)
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
var _root = null;
|
|
89
|
-
function renderPage(container, layout, Component, data, isInitial) {
|
|
90
|
-
let element = createElement(Component);
|
|
91
|
-
if (layout) {
|
|
92
|
-
element = createElement(layout, { children: element });
|
|
93
|
-
}
|
|
94
|
-
const wrapped = createElement(ServerDataContext.Provider, { value: data }, element);
|
|
95
|
-
if (isInitial) {
|
|
96
|
-
_root = hydrateRoot(container, wrapped);
|
|
97
|
-
} else {
|
|
98
|
-
if (!_root) _root = createRoot(container);
|
|
99
|
-
_root.render(wrapped);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
var _router = null;
|
|
103
|
-
function createBrowserRouter(opts) {
|
|
104
|
-
if (_router) return _router;
|
|
105
|
-
const container = opts.container ?? document.getElementById("root");
|
|
106
|
-
const layout = opts.layout;
|
|
107
|
-
const routes = Object.entries(opts.routes).map(([pattern, loader]) => {
|
|
108
|
-
const { regex, paramNames } = compilePattern(pattern);
|
|
109
|
-
return { pattern, regex, paramNames, loader };
|
|
110
|
-
});
|
|
111
|
-
let fallback = null;
|
|
112
|
-
if (opts.fallback) {
|
|
113
|
-
const { regex, paramNames } = compilePattern("*");
|
|
114
|
-
fallback = { pattern: "*", regex, paramNames, loader: opts.fallback };
|
|
115
|
-
}
|
|
116
|
-
async function renderPath(pathname, isInitial, data) {
|
|
117
|
-
const matched = matchRoute(pathname, routes);
|
|
118
|
-
const route = matched?.route ?? fallback;
|
|
119
|
-
if (!route) {
|
|
120
|
-
window.location.href = pathname;
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
try {
|
|
124
|
-
const mod = await route.loader();
|
|
125
|
-
const Component = extractComponent(mod);
|
|
126
|
-
const pageData = data ?? (isInitial ? readInitialData() : await fetchPageData(pathname));
|
|
127
|
-
renderPage(container, layout, Component, pageData, isInitial);
|
|
128
|
-
} catch (err) {
|
|
129
|
-
console.error("[weifuwu] router error:", err);
|
|
130
|
-
if (fallback && route !== fallback) {
|
|
131
|
-
try {
|
|
132
|
-
const fallbackMod = await fallback.loader();
|
|
133
|
-
const FallbackComponent = extractComponent(fallbackMod);
|
|
134
|
-
renderPage(container, layout, FallbackComponent, { error: String(err) }, false);
|
|
135
|
-
} catch {
|
|
136
|
-
container.innerHTML = "<h1>Something went wrong</h1>";
|
|
137
|
-
}
|
|
138
|
-
} else {
|
|
139
|
-
container.innerHTML = "<h1>Something went wrong</h1>";
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
async function navigate2(url) {
|
|
144
|
-
const u = new URL(url, window.location.origin);
|
|
145
|
-
if (u.origin !== window.location.origin || u.pathname === window.location.pathname) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
history.pushState({}, "", url);
|
|
149
|
-
scrollTo(0, 0);
|
|
150
|
-
await renderPath(u.pathname, false);
|
|
151
|
-
}
|
|
152
|
-
window.addEventListener("popstate", () => {
|
|
153
|
-
renderPath(window.location.pathname, false);
|
|
154
|
-
});
|
|
155
|
-
document.addEventListener("click", (e) => {
|
|
156
|
-
const target = e.target.closest("a");
|
|
157
|
-
if (!target) return;
|
|
158
|
-
const href = target.getAttribute("href");
|
|
159
|
-
if (!href) return;
|
|
160
|
-
const u = new URL(href, window.location.origin);
|
|
161
|
-
if (u.origin !== window.location.origin || target.hasAttribute("download") || target.target === "_blank" || target.getAttribute("rel") === "external") return;
|
|
162
|
-
e.preventDefault();
|
|
163
|
-
navigate2(href);
|
|
164
|
-
});
|
|
165
|
-
const initialData = readInitialData();
|
|
166
|
-
renderPath(window.location.pathname, true, initialData);
|
|
167
|
-
_router = { navigate: navigate2, renderPath };
|
|
168
|
-
return _router;
|
|
169
|
-
}
|
|
170
|
-
function navigate(url) {
|
|
171
|
-
if (_router) {
|
|
172
|
-
_router.navigate(url);
|
|
173
|
-
} else {
|
|
174
|
-
window.location.href = url;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
export {
|
|
178
|
-
createBrowserRouter,
|
|
179
|
-
hydrate,
|
|
180
|
-
navigate,
|
|
181
|
-
useServerData
|
|
182
|
-
};
|
package/dist/react/compile.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* On-the-fly TSX compilation for ctx.render().
|
|
3
|
-
*
|
|
4
|
-
* Compiles .tsx files with esbuild, caches by source-content hash.
|
|
5
|
-
* Externalizes react/react-dom — the framework's own instance is used.
|
|
6
|
-
* Persisted to disk for fast restarts.
|
|
7
|
-
*/
|
|
8
|
-
import type { ComponentType } from 'react';
|
|
9
|
-
export declare function setReactCacheDir(dir: string): void;
|
|
10
|
-
/**
|
|
11
|
-
* Load a .tsx/.ts module, compiling on-the-fly with esbuild.
|
|
12
|
-
* Results are:
|
|
13
|
-
* 1. Cached in memory by source-content hash
|
|
14
|
-
* 2. Persisted to disk ({cacheDir}/{sourceHash}.mjs) for instant restarts
|
|
15
|
-
*/
|
|
16
|
-
export declare function loadTsxModule(entryPath: string): Promise<Record<string, unknown>>;
|
|
17
|
-
export declare function loadTsxComponent(entryPath: string): Promise<ComponentType>;
|
package/dist/react/context.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { Component, type ReactNode } from 'react';
|
|
2
|
-
interface ErrorBoundaryProps {
|
|
3
|
-
children: ReactNode;
|
|
4
|
-
/** Fallback UI rendered when a child component throws. */
|
|
5
|
-
fallback?: ReactNode;
|
|
6
|
-
/** Called with the error (client-side only). */
|
|
7
|
-
onError?: (error: Error) => void;
|
|
8
|
-
}
|
|
9
|
-
interface ErrorBoundaryState {
|
|
10
|
-
hasError: boolean;
|
|
11
|
-
error: Error | null;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Catch render errors in child components.
|
|
15
|
-
* Works during SSR (renders fallback) and client-side hydration.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```tsx
|
|
19
|
-
* <ErrorBoundary fallback={<p>Something went wrong</p>}>
|
|
20
|
-
* <RiskyComponent />
|
|
21
|
-
* </ErrorBoundary>
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
export declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
25
|
-
state: ErrorBoundaryState;
|
|
26
|
-
static getDerivedStateFromError(error: Error): ErrorBoundaryState;
|
|
27
|
-
componentDidCatch(error: Error): void;
|
|
28
|
-
render(): ReactNode;
|
|
29
|
-
}
|
|
30
|
-
export {};
|
package/dist/react/hooks.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Access server-loaded data in any component.
|
|
3
|
-
* On the server, reads from the request-scoped ServerDataContext.
|
|
4
|
-
* On the client (after hydration), reads from the same context populated by hydrate().
|
|
5
|
-
*/
|
|
6
|
-
export declare function useServerData<T extends Record<string, unknown> = Record<string, unknown>>(): T;
|
package/dist/react/index.d.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { type ReactElement, type ComponentType } from 'react';
|
|
2
|
-
import { type Middleware } from '../types.ts';
|
|
3
|
-
import type { Router } from '../core/router.ts';
|
|
4
|
-
import type { ReactOptions, ReactRouterOptions, ReactAppOptions } from './types.ts';
|
|
5
|
-
/**
|
|
6
|
-
* React SSR middleware — injects `ctx.render(path, opts?)`.
|
|
7
|
-
*
|
|
8
|
-
* Options:
|
|
9
|
-
* - `layout`: Wrap every page in a shared layout component (nav, footer, etc.)
|
|
10
|
-
*
|
|
11
|
-
* For Tailwind CSS: use `tailwindDev` middleware.
|
|
12
|
-
* For client bundles: use `esbuildDev` middleware.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```ts
|
|
16
|
-
* app.use(tailwindDev({ '/assets/tailwind.css': { entry: './styles/input.css' } }))
|
|
17
|
-
* app.use(esbuildDev({ '/assets/client.js': { entry: './client.ts', ... } }))
|
|
18
|
-
* app.use(react({ layout: './components/PageShell.tsx' }))
|
|
19
|
-
* app.get('/', (_req, ctx) => ctx.render('./pages/HomePage.tsx', {
|
|
20
|
-
* stylesheets: ['/assets/tailwind.css'],
|
|
21
|
-
* bootstrapModules: ['/assets/client.js'],
|
|
22
|
-
* }))
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
/**
|
|
26
|
-
* React SSR middleware — injects `ctx.render(path, opts?)`.
|
|
27
|
-
*
|
|
28
|
-
* **Lightweight mode** (no `pages`): returns middleware for `app.use()`.
|
|
29
|
-
* Use with manual `app.get()` + `ctx.render()`.
|
|
30
|
-
*
|
|
31
|
-
* **Full mode** (has `pages`): returns a plugin for `app.plugin()`.
|
|
32
|
-
* Handles routing, data loading, Tailwind, client bundle, and error pages.
|
|
33
|
-
*/
|
|
34
|
-
export declare function react(opts?: ReactOptions): Middleware;
|
|
35
|
-
export declare function react(opts: ReactAppOptions): (app: Router) => void;
|
|
36
|
-
/**
|
|
37
|
-
* Auto-register routes from a shared route config.
|
|
38
|
-
*
|
|
39
|
-
* Use a single routes file shared between server and client to eliminate duplication.
|
|
40
|
-
* Routes with data dependencies use the `loaders` option — request ctx is passed
|
|
41
|
-
* to the loader, which can throw `HttpError` for non-200 status codes.
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* ```ts
|
|
45
|
-
* // routes.ts — shared by server and client
|
|
46
|
-
* export const routes = {
|
|
47
|
-
* '/': () => import('./pages/Home.tsx'),
|
|
48
|
-
* '/users': () => import('./pages/Users.tsx'),
|
|
49
|
-
* '/users/:id': () => import('./pages/UserDetail.tsx'),
|
|
50
|
-
* }
|
|
51
|
-
*
|
|
52
|
-
* // server.ts
|
|
53
|
-
* import { reactRouter } from 'weifuwu/react'
|
|
54
|
-
* import { routes } from './routes.ts'
|
|
55
|
-
*
|
|
56
|
-
* reactRouter(app, routes, {
|
|
57
|
-
* layout: './layouts/Root.tsx',
|
|
58
|
-
* stylesheets: ['/assets/tailwind.css'],
|
|
59
|
-
* bootstrapModules: ['/assets/client.js'],
|
|
60
|
-
* loaders: {
|
|
61
|
-
* '/users': async (ctx) => ({ users: await db.listUsers() }),
|
|
62
|
-
* '/users/:id': async (ctx) => {
|
|
63
|
-
* const user = await db.findUser(ctx.params.id)
|
|
64
|
-
* if (!user) throw new HttpError('Not found', 404)
|
|
65
|
-
* return { user }
|
|
66
|
-
* },
|
|
67
|
-
* },
|
|
68
|
-
* })
|
|
69
|
-
*
|
|
70
|
-
* // client.ts — same routes config, no loaders
|
|
71
|
-
* import { createBrowserRouter } from 'weifuwu/react/client'
|
|
72
|
-
* import { routes } from './routes.ts'
|
|
73
|
-
*
|
|
74
|
-
* createBrowserRouter({ layout: Root, routes })
|
|
75
|
-
* ```
|
|
76
|
-
*/
|
|
77
|
-
export declare function reactRouter(app: Router, routes: Record<string, () => Promise<{
|
|
78
|
-
default: ComponentType;
|
|
79
|
-
}>>, opts?: ReactRouterOptions): void;
|
|
80
|
-
/**
|
|
81
|
-
* Client-side navigation link. Renders as `<a>` on the server.
|
|
82
|
-
* On the client, intercepted by `createBrowserRouter` for SPA navigation.
|
|
83
|
-
*/
|
|
84
|
-
export declare function Link({ href, children, ...props }: {
|
|
85
|
-
href: string;
|
|
86
|
-
children: React.ReactNode;
|
|
87
|
-
[key: string]: unknown;
|
|
88
|
-
}): ReactElement;
|
|
89
|
-
export { ErrorBoundary } from './error-boundary.ts';
|
|
90
|
-
export { useServerData } from './hooks.ts';
|
|
91
|
-
export { ServerDataContext } from './context.ts';
|
|
92
|
-
export type { ReactOptions, RenderOptions, ReactRouterOptions, ReactAppOptions } from './types.ts';
|