@fluixi/start 0.1.0-alpha.73 → 0.1.0-alpha.74
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/dist/adapter.cjs +748 -0
- package/dist/adapter.mjs +717 -0
- package/dist/adapters/entry.cjs +122 -0
- package/dist/adapters/entry.mjs +95 -0
- package/dist/adapters/platforms.cjs +311 -0
- package/dist/adapters/platforms.mjs +284 -0
- package/dist/api-RH6E5UTH.mjs +114 -0
- package/dist/api.cjs +145 -0
- package/dist/api.mjs +116 -0
- package/dist/commands/api-23MJFSYL.mjs +17 -0
- package/dist/commands/api-4IYNQRKG.mjs +16 -0
- package/dist/commands/api-RH6E5UTH.mjs +114 -0
- package/dist/commands/build.cjs +870 -0
- package/dist/commands/build.mjs +710 -0
- package/dist/commands/chunk-NS5GR2GX.mjs +115 -0
- package/dist/commands/chunk-OCPCK4ZJ.mjs +117 -0
- package/dist/commands/dev.cjs +663 -0
- package/dist/commands/dev.mjs +505 -0
- package/dist/commands/index.cjs +1138 -0
- package/dist/commands/index.mjs +975 -0
- package/dist/commands/prerender.cjs +402 -0
- package/dist/commands/prerender.mjs +375 -0
- package/dist/commands/start.cjs +461 -0
- package/dist/commands/start.mjs +426 -0
- package/dist/config.cjs +144 -0
- package/dist/config.mjs +108 -0
- package/dist/di.cjs +200 -0
- package/dist/di.mjs +169 -0
- package/dist/document.cjs +115 -0
- package/dist/document.mjs +86 -0
- package/dist/generated-api.cjs +43 -0
- package/dist/generated-api.mjs +18 -0
- package/dist/generated-server-fns.cjs +43 -0
- package/dist/generated-server-fns.mjs +18 -0
- package/dist/handler-core.cjs +211 -0
- package/dist/handler-core.mjs +185 -0
- package/dist/handler.cjs +181 -0
- package/dist/handler.mjs +150 -0
- package/dist/head.cjs +25 -0
- package/dist/head.mjs +4 -0
- package/dist/image.cjs +146 -0
- package/dist/image.mjs +120 -0
- package/dist/index.cjs +1036 -0
- package/dist/index.mjs +968 -0
- package/dist/interceptors.cjs +71 -0
- package/dist/interceptors.mjs +42 -0
- package/dist/internal.cjs +388 -0
- package/dist/internal.mjs +218 -0
- package/dist/middleware.cjs +58 -0
- package/dist/middleware.mjs +31 -0
- package/dist/preload.cjs +42 -0
- package/dist/preload.mjs +18 -0
- package/dist/router.cjs +25 -0
- package/dist/router.mjs +4 -0
- package/dist/server-fn-setup.cjs +46 -0
- package/dist/server-fn-setup.mjs +22 -0
- package/dist/server-fn.cjs +109 -0
- package/dist/server-fn.mjs +79 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/ui.cjs +123 -0
- package/dist/ui.mjs +93 -0
- package/package.json +45 -25
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
import {
|
|
2
|
+
API_VIRTUAL_MODULE,
|
|
3
|
+
isApiPath
|
|
4
|
+
} from "./chunk-NS5GR2GX.mjs";
|
|
5
|
+
|
|
6
|
+
// src/commands/dev.ts
|
|
7
|
+
import { createServer as createHttp } from "node:http";
|
|
8
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
|
|
9
|
+
import { resolve as resolve2 } from "node:path";
|
|
10
|
+
|
|
11
|
+
// src/config.ts
|
|
12
|
+
import { resolve, isAbsolute } from "node:path";
|
|
13
|
+
function resolveConfig(config = {}) {
|
|
14
|
+
const root = resolve(config.root ?? process.cwd());
|
|
15
|
+
return {
|
|
16
|
+
root,
|
|
17
|
+
ssr: config.ssr ?? true,
|
|
18
|
+
port: config.port ?? 3e3,
|
|
19
|
+
serverEntry: config.serverEntry ?? "src/entry-server.ts",
|
|
20
|
+
clientEntry: config.clientEntry ?? "src/entry-client.ts",
|
|
21
|
+
template: config.template ?? "index.html",
|
|
22
|
+
mountId: config.mountId ?? "root",
|
|
23
|
+
routesDir: config.routesDir ?? "src/routes",
|
|
24
|
+
apiDir: config.apiDir ?? "src/api",
|
|
25
|
+
middleware: config.middleware ?? "src/middleware.ts",
|
|
26
|
+
ssrNoExternal: config.ssrNoExternal ?? [],
|
|
27
|
+
resolve: config.resolve ?? [],
|
|
28
|
+
adapter: config.adapter,
|
|
29
|
+
i18n: resolveI18n(config.i18n),
|
|
30
|
+
prerender: resolvePrerender(config.prerender)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function resolvePrerender(prerender) {
|
|
34
|
+
if (!prerender) return false;
|
|
35
|
+
const opts = typeof prerender === "object" ? prerender : {};
|
|
36
|
+
return {
|
|
37
|
+
routes: opts.routes?.length ? opts.routes : ["/"],
|
|
38
|
+
crawl: opts.crawl ?? true
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function resolveI18n(i18n) {
|
|
42
|
+
if (i18n === false) return false;
|
|
43
|
+
const opts = typeof i18n === "object" ? i18n : {};
|
|
44
|
+
return {
|
|
45
|
+
dir: opts.dir ?? "src/i18n",
|
|
46
|
+
defaultLocale: opts.defaultLocale,
|
|
47
|
+
locales: opts.locales
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/internal.ts
|
|
52
|
+
import { readdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
53
|
+
import { resolve as resolvePath, relative as relativePath } from "node:path";
|
|
54
|
+
|
|
55
|
+
// src/document.ts
|
|
56
|
+
import { extractHeadMarker } from "@fluixi/head";
|
|
57
|
+
function injectApp(template, appHtml, mountId = "root") {
|
|
58
|
+
const mount = new RegExp(`<div id=["']${mountId}["']\\s*>\\s*</div>`);
|
|
59
|
+
if (mount.test(template)) {
|
|
60
|
+
return template.replace(mount, `<div id="${mountId}">${appHtml}</div>`);
|
|
61
|
+
}
|
|
62
|
+
if (template.includes("<!--ssr-outlet-->")) {
|
|
63
|
+
return template.replace("<!--ssr-outlet-->", appHtml);
|
|
64
|
+
}
|
|
65
|
+
return template.replace("</body>", `<div id="${mountId}">${appHtml}</div></body>`);
|
|
66
|
+
}
|
|
67
|
+
function splitTemplate(template, mountId = "root") {
|
|
68
|
+
const mount = new RegExp(`<div id=["']${mountId}["']\\s*>\\s*</div>`);
|
|
69
|
+
const m = template.match(mount);
|
|
70
|
+
if (m && m.index !== void 0) {
|
|
71
|
+
return {
|
|
72
|
+
head: template.slice(0, m.index) + `<div id="${mountId}">`,
|
|
73
|
+
tail: `</div>` + template.slice(m.index + m[0].length)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const outlet = template.indexOf("<!--ssr-outlet-->");
|
|
77
|
+
if (outlet !== -1) {
|
|
78
|
+
return {
|
|
79
|
+
head: template.slice(0, outlet),
|
|
80
|
+
tail: template.slice(outlet + "<!--ssr-outlet-->".length)
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const body = template.indexOf("</body>");
|
|
84
|
+
if (body !== -1) {
|
|
85
|
+
return {
|
|
86
|
+
head: template.slice(0, body) + `<div id="${mountId}">`,
|
|
87
|
+
tail: `</div>` + template.slice(body)
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return { head: template, tail: "" };
|
|
91
|
+
}
|
|
92
|
+
function setHtmlAttr(html, name, value) {
|
|
93
|
+
const existing = new RegExp(`(<html\\b[^>]*?)\\s${name}="[^"]*"`, "i");
|
|
94
|
+
if (existing.test(html)) return html.replace(existing, `$1 ${name}="${value}"`);
|
|
95
|
+
return html.replace(/<html\b/i, `<html ${name}="${value}"`);
|
|
96
|
+
}
|
|
97
|
+
function injectAppAndHead(template, rendered, mountId = "root") {
|
|
98
|
+
const { head, body } = extractHeadMarker(rendered);
|
|
99
|
+
let html = injectApp(template, body, mountId);
|
|
100
|
+
if (head) {
|
|
101
|
+
if (head.headHtml) {
|
|
102
|
+
if (/<title[\s>]/i.test(head.headHtml)) html = html.replace(/<title>[\s\S]*?<\/title>/i, "");
|
|
103
|
+
html = html.replace("</head>", `${head.headHtml}</head>`);
|
|
104
|
+
}
|
|
105
|
+
for (const [k, v] of Object.entries(head.htmlAttrs)) html = setHtmlAttr(html, k, v);
|
|
106
|
+
}
|
|
107
|
+
return html;
|
|
108
|
+
}
|
|
109
|
+
function guardHandler(handler, onError) {
|
|
110
|
+
return async (request) => {
|
|
111
|
+
try {
|
|
112
|
+
return await handler(request);
|
|
113
|
+
} catch (e) {
|
|
114
|
+
onError?.(e);
|
|
115
|
+
return new Response(String(e?.stack || e), {
|
|
116
|
+
status: 500,
|
|
117
|
+
headers: { "content-type": "text/plain; charset=utf-8" }
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/internal.ts
|
|
124
|
+
var SERVER_FNS_VMOD = "virtual:fluixi-server-fns";
|
|
125
|
+
var I18N_VMOD = "virtual:fluixi-i18n";
|
|
126
|
+
function i18nVitePlugin(opts) {
|
|
127
|
+
let root = process.cwd();
|
|
128
|
+
const id = "\0" + I18N_VMOD;
|
|
129
|
+
return {
|
|
130
|
+
name: "fluixi-i18n",
|
|
131
|
+
configResolved(config) {
|
|
132
|
+
root = config.root || root;
|
|
133
|
+
},
|
|
134
|
+
resolveId(source) {
|
|
135
|
+
return source === I18N_VMOD ? id : null;
|
|
136
|
+
},
|
|
137
|
+
load(source) {
|
|
138
|
+
if (source !== id) return null;
|
|
139
|
+
const dir = resolvePath(root, opts.dir);
|
|
140
|
+
let files = [];
|
|
141
|
+
try {
|
|
142
|
+
files = readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
143
|
+
} catch {
|
|
144
|
+
}
|
|
145
|
+
const discovered = files.map((f) => f.replace(/\.json$/, "")).sort();
|
|
146
|
+
const locales = opts.locales?.length ? opts.locales : discovered;
|
|
147
|
+
const defaultLocale = opts.defaultLocale ?? locales[0] ?? "en";
|
|
148
|
+
const imports = locales.map((l, i) => `import _${i} from ${JSON.stringify(resolvePath(dir, l + ".json"))};`).join("\n");
|
|
149
|
+
const messages = `export const messages = {${locales.map((l, i) => `${JSON.stringify(l)}:_${i}`).join(",")}};`;
|
|
150
|
+
return `${imports}
|
|
151
|
+
export const locales = ${JSON.stringify(locales)};
|
|
152
|
+
export const defaultLocale = ${JSON.stringify(defaultLocale)};
|
|
153
|
+
${messages}
|
|
154
|
+
`;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
async function loadServerEnv(root, mode) {
|
|
159
|
+
const { loadEnv } = await import("vite");
|
|
160
|
+
const env = loadEnv(mode, root, "");
|
|
161
|
+
for (const [k, v] of Object.entries(env)) {
|
|
162
|
+
if (process.env[k] === void 0) process.env[k] = v;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function i18nTypeBlock(root, i18n) {
|
|
166
|
+
const dir = resolvePath(root, i18n.dir);
|
|
167
|
+
let discovered = [];
|
|
168
|
+
try {
|
|
169
|
+
discovered = readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, "")).sort();
|
|
170
|
+
} catch {
|
|
171
|
+
}
|
|
172
|
+
const locales = i18n.locales?.length ? i18n.locales : discovered;
|
|
173
|
+
const defaultLocale = i18n.defaultLocale ?? locales[0] ?? "en";
|
|
174
|
+
const typed = locales.length > 0 && locales.every((l) => discovered.includes(l));
|
|
175
|
+
if (!typed) {
|
|
176
|
+
return [
|
|
177
|
+
`declare module 'virtual:fluixi-i18n' {`,
|
|
178
|
+
` export const messages: Record<string, Record<string, unknown>>;`,
|
|
179
|
+
` export const locales: string[];`,
|
|
180
|
+
` export const defaultLocale: string;`,
|
|
181
|
+
`}`
|
|
182
|
+
];
|
|
183
|
+
}
|
|
184
|
+
const srcDir = resolvePath(root, "src");
|
|
185
|
+
const toSpec = (l) => {
|
|
186
|
+
const rel = relativePath(srcDir, resolvePath(dir, l + ".json")).split(/[\\/]/).join("/");
|
|
187
|
+
return rel.startsWith(".") ? rel : "./" + rel;
|
|
188
|
+
};
|
|
189
|
+
const entries = locales.map((l) => ` ${JSON.stringify(l)}: (typeof import(${JSON.stringify(toSpec(l))}))['default'];`);
|
|
190
|
+
const tuple = locales.map((l) => JSON.stringify(l)).join(", ");
|
|
191
|
+
return [
|
|
192
|
+
`declare module 'virtual:fluixi-i18n' {`,
|
|
193
|
+
` export const messages: {`,
|
|
194
|
+
...entries,
|
|
195
|
+
` };`,
|
|
196
|
+
` export const locales: readonly [${tuple}];`,
|
|
197
|
+
` export const defaultLocale: ${JSON.stringify(defaultLocale)};`,
|
|
198
|
+
`}`
|
|
199
|
+
];
|
|
200
|
+
}
|
|
201
|
+
function startTypesPlugin(cfg) {
|
|
202
|
+
let root = process.cwd();
|
|
203
|
+
return {
|
|
204
|
+
name: "fluixi-start-types",
|
|
205
|
+
configResolved(config) {
|
|
206
|
+
root = config.root || root;
|
|
207
|
+
},
|
|
208
|
+
buildStart() {
|
|
209
|
+
const blocks = [];
|
|
210
|
+
if (cfg.i18n) blocks.push(...i18nTypeBlock(root, cfg.i18n));
|
|
211
|
+
if (blocks.length === 0) return;
|
|
212
|
+
const content = `// Auto-generated by @fluixi/start — do not edit.
|
|
213
|
+
${blocks.join("\n")}
|
|
214
|
+
`;
|
|
215
|
+
const file = resolvePath(root, "src/fluixi-start-env.d.ts");
|
|
216
|
+
try {
|
|
217
|
+
if (!existsSync(file) || readFileSync(file, "utf8") !== content) writeFileSync(file, content);
|
|
218
|
+
} catch {
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
async function fluixiPlugins(cfg) {
|
|
224
|
+
const { fluixi, fluixiRoutesPlugin } = await import("@fluixi/core/plugins");
|
|
225
|
+
const { serverFunctionsVitePlugin } = await import("@fluixi/compiler/integrations");
|
|
226
|
+
const plugins = [
|
|
227
|
+
serverFunctionsVitePlugin(),
|
|
228
|
+
fluixiRoutesPlugin({ routesDir: cfg.routesDir, polyfills: false }),
|
|
229
|
+
// The app's component-resolution rules reach the compiler here; without this an SSR
|
|
230
|
+
// app has no way to say where an unimported tag comes from.
|
|
231
|
+
//
|
|
232
|
+
// `fluixi()` and not `createVitePlugin()`: the latter is the bare compiler integration,
|
|
233
|
+
// and the reactive graph — agent, source marks, /__fluixi/graph — is composed on top of
|
|
234
|
+
// it by the former. A start app got no graph at all until this changed.
|
|
235
|
+
fluixi({ resolve: cfg.resolve })
|
|
236
|
+
];
|
|
237
|
+
if (cfg.i18n) plugins.push(i18nVitePlugin(cfg.i18n));
|
|
238
|
+
const { apiRoutesVitePlugin } = await import("./api-4IYNQRKG.mjs");
|
|
239
|
+
plugins.push(apiRoutesVitePlugin({ dir: cfg.apiDir }));
|
|
240
|
+
plugins.push(startTypesPlugin(cfg));
|
|
241
|
+
return plugins;
|
|
242
|
+
}
|
|
243
|
+
var _guardsInstalled = false;
|
|
244
|
+
function installCrashGuards() {
|
|
245
|
+
if (_guardsInstalled) return;
|
|
246
|
+
_guardsInstalled = true;
|
|
247
|
+
process.on(
|
|
248
|
+
"unhandledRejection",
|
|
249
|
+
(reason) => console.warn("[fluixi] unhandled rejection (backend likely down) —", reason?.message || reason)
|
|
250
|
+
);
|
|
251
|
+
process.on(
|
|
252
|
+
"uncaughtException",
|
|
253
|
+
(err) => console.warn("[fluixi] uncaught exception —", err?.message || err)
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// src/handler.ts
|
|
258
|
+
function streamDocument(head, body, tail) {
|
|
259
|
+
const enc = new TextEncoder();
|
|
260
|
+
const stream = new ReadableStream({
|
|
261
|
+
async start(controller) {
|
|
262
|
+
try {
|
|
263
|
+
controller.enqueue(enc.encode(head));
|
|
264
|
+
const reader = body.getReader();
|
|
265
|
+
for (; ; ) {
|
|
266
|
+
const { done, value } = await reader.read();
|
|
267
|
+
if (done) break;
|
|
268
|
+
controller.enqueue(value);
|
|
269
|
+
}
|
|
270
|
+
controller.enqueue(enc.encode(tail));
|
|
271
|
+
controller.close();
|
|
272
|
+
} catch (err) {
|
|
273
|
+
controller.error(err);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
return new Response(stream, {
|
|
278
|
+
status: 200,
|
|
279
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
function nodeToRequest(req) {
|
|
283
|
+
const host = req.headers.host ?? "localhost";
|
|
284
|
+
const url = `http://${host}${req.url ?? "/"}`;
|
|
285
|
+
const headers = new Headers();
|
|
286
|
+
for (const [k, v] of Object.entries(req.headers)) {
|
|
287
|
+
if (Array.isArray(v)) v.forEach((x) => headers.append(k, x));
|
|
288
|
+
else if (v != null) headers.set(k, v);
|
|
289
|
+
}
|
|
290
|
+
const method = req.method ?? "GET";
|
|
291
|
+
const init = { method, headers };
|
|
292
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
293
|
+
init.body = req;
|
|
294
|
+
init.duplex = "half";
|
|
295
|
+
}
|
|
296
|
+
return new Request(url, init);
|
|
297
|
+
}
|
|
298
|
+
async function sendResponse(res, response) {
|
|
299
|
+
res.statusCode = response.status;
|
|
300
|
+
response.headers.forEach((value, key) => res.setHeader(key, value));
|
|
301
|
+
const body = response.body;
|
|
302
|
+
if (!body) {
|
|
303
|
+
res.end(await response.text().catch(() => ""));
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const reader = body.getReader();
|
|
307
|
+
try {
|
|
308
|
+
for (; ; ) {
|
|
309
|
+
const { done, value } = await reader.read();
|
|
310
|
+
if (done) break;
|
|
311
|
+
res.write(value);
|
|
312
|
+
}
|
|
313
|
+
} finally {
|
|
314
|
+
res.end();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function toNodeHandler(handler) {
|
|
318
|
+
return async (req, res) => {
|
|
319
|
+
const response = await handler(nodeToRequest(req));
|
|
320
|
+
await sendResponse(res, response);
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// src/middleware.ts
|
|
325
|
+
function normalizeMiddleware(mod) {
|
|
326
|
+
const def = mod?.default ?? mod;
|
|
327
|
+
if (def == null) return [];
|
|
328
|
+
if (Array.isArray(def)) return def.filter((m) => typeof m === "function");
|
|
329
|
+
return typeof def === "function" ? [def] : [];
|
|
330
|
+
}
|
|
331
|
+
function composeMiddleware(middlewares, core) {
|
|
332
|
+
if (middlewares.length === 0) return core;
|
|
333
|
+
return (request) => {
|
|
334
|
+
let lastCalled = -1;
|
|
335
|
+
const dispatch = (i) => {
|
|
336
|
+
if (i <= lastCalled) {
|
|
337
|
+
return Promise.reject(new Error("middleware called next() more than once"));
|
|
338
|
+
}
|
|
339
|
+
lastCalled = i;
|
|
340
|
+
const mw = middlewares[i];
|
|
341
|
+
if (!mw) return Promise.resolve(core(request));
|
|
342
|
+
return Promise.resolve(mw(request, () => dispatch(i + 1)));
|
|
343
|
+
};
|
|
344
|
+
return dispatch(0);
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// src/ui.ts
|
|
349
|
+
import { networkInterfaces } from "node:os";
|
|
350
|
+
var COLOR = process.env.NO_COLOR || process.env.FORCE_COLOR === "0" ? false : (!!process.stdout.isTTY || !!process.env.FORCE_COLOR) && process.env.TERM !== "dumb";
|
|
351
|
+
var sgr = (open, close) => (s) => COLOR ? `\x1B[${open}m${s}\x1B[${close}m` : `${s}`;
|
|
352
|
+
var c = {
|
|
353
|
+
bold: sgr(1, 22),
|
|
354
|
+
dim: sgr(2, 22),
|
|
355
|
+
italic: sgr(3, 23),
|
|
356
|
+
underline: sgr(4, 24),
|
|
357
|
+
red: sgr(31, 39),
|
|
358
|
+
green: sgr(32, 39),
|
|
359
|
+
yellow: sgr(33, 39),
|
|
360
|
+
blue: sgr(34, 39),
|
|
361
|
+
magenta: sgr(35, 39),
|
|
362
|
+
cyan: sgr(36, 39),
|
|
363
|
+
gray: sgr(90, 39)
|
|
364
|
+
};
|
|
365
|
+
var rgb = (r, g, b) => (s) => COLOR ? `\x1B[38;2;${r};${g};${b}m${s}\x1B[39m` : s;
|
|
366
|
+
var BRAND = [
|
|
367
|
+
[34, 211, 238],
|
|
368
|
+
// cyan-400
|
|
369
|
+
[56, 189, 248],
|
|
370
|
+
[129, 140, 248],
|
|
371
|
+
[167, 139, 250],
|
|
372
|
+
[192, 132, 252],
|
|
373
|
+
// violet-400
|
|
374
|
+
[217, 130, 245]
|
|
375
|
+
];
|
|
376
|
+
function gradient(text) {
|
|
377
|
+
if (!COLOR) return text;
|
|
378
|
+
const chars = [...text];
|
|
379
|
+
return chars.map((ch, i) => {
|
|
380
|
+
const [r, g, b] = BRAND[Math.min(BRAND.length - 1, Math.floor(i / Math.max(1, chars.length - 1) * (BRAND.length - 1)))];
|
|
381
|
+
return rgb(r, g, b)(ch);
|
|
382
|
+
}).join("");
|
|
383
|
+
}
|
|
384
|
+
function wordmark(version) {
|
|
385
|
+
const mark = `${gradient("◆")} ${c.bold(gradient("fluixi"))}`;
|
|
386
|
+
return version ? `${mark} ${c.dim(`v${version}`)}` : mark;
|
|
387
|
+
}
|
|
388
|
+
var SYM = {
|
|
389
|
+
arrow: c.gray("➜"),
|
|
390
|
+
ok: c.green("✓"),
|
|
391
|
+
err: c.red("✗")
|
|
392
|
+
};
|
|
393
|
+
function networkAddress() {
|
|
394
|
+
for (const list of Object.values(networkInterfaces())) {
|
|
395
|
+
for (const net of list ?? []) {
|
|
396
|
+
const family = typeof net.family === "string" ? net.family : `IPv${net.family}`;
|
|
397
|
+
if (family === "IPv4" && !net.internal) return net.address;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
function devReady(opts) {
|
|
403
|
+
const ms = Date.now() - opts.startedAt;
|
|
404
|
+
const local = `http://localhost:${opts.port}/`;
|
|
405
|
+
const net = networkAddress();
|
|
406
|
+
const link = (u) => c.cyan(c.underline(u));
|
|
407
|
+
const label = (s) => c.gray(s.padEnd(8));
|
|
408
|
+
const lines = [
|
|
409
|
+
"",
|
|
410
|
+
` ${wordmark(opts.version)} ${c.gray(`dev ready in ${c.bold(String(ms))} ms`)}`,
|
|
411
|
+
"",
|
|
412
|
+
` ${SYM.arrow} ${label("Local")} ${link(local)}`,
|
|
413
|
+
net ? ` ${SYM.arrow} ${label("Network")} ${link(`http://${net}:${opts.port}/`)}` : ` ${SYM.arrow} ${label("Network")} ${c.dim("not exposed")}`,
|
|
414
|
+
""
|
|
415
|
+
];
|
|
416
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// src/commands/dev.ts
|
|
420
|
+
var STYLE_FILE = /\.(css|scss|sass|less|styl|stylus|pcss|postcss)(\?|$)/;
|
|
421
|
+
async function renderedStyles(vite, entry) {
|
|
422
|
+
const seen = /* @__PURE__ */ new Set();
|
|
423
|
+
const found = [];
|
|
424
|
+
const visit = (mod) => {
|
|
425
|
+
if (!mod || seen.has(mod.url)) return;
|
|
426
|
+
seen.add(mod.url);
|
|
427
|
+
if (STYLE_FILE.test(mod.url)) found.push(mod.url);
|
|
428
|
+
for (const dep of mod.importedModules ?? []) visit(dep);
|
|
429
|
+
};
|
|
430
|
+
try {
|
|
431
|
+
visit(await vite.moduleGraph.getModuleByUrl("/" + entry, true));
|
|
432
|
+
} catch {
|
|
433
|
+
return [];
|
|
434
|
+
}
|
|
435
|
+
return found;
|
|
436
|
+
}
|
|
437
|
+
function injectStyles(html, urls) {
|
|
438
|
+
if (urls.length === 0) return html;
|
|
439
|
+
const links = urls.map((url) => `<link rel="stylesheet" href="${url}${url.includes("?") ? "&" : "?"}direct">`).join("\n ");
|
|
440
|
+
return html.includes("</head>") ? html.replace("</head>", ` ${links}
|
|
441
|
+
</head>`) : html;
|
|
442
|
+
}
|
|
443
|
+
async function dev(config = {}) {
|
|
444
|
+
const startedAt = Date.now();
|
|
445
|
+
const cfg = resolveConfig(config);
|
|
446
|
+
installCrashGuards();
|
|
447
|
+
await loadServerEnv(cfg.root, "development");
|
|
448
|
+
const { createServer } = await import("vite");
|
|
449
|
+
const template = readFileSync2(resolve2(cfg.root, cfg.template), "utf-8");
|
|
450
|
+
let vite;
|
|
451
|
+
const renderHandler = guardHandler(async (request) => {
|
|
452
|
+
const url = new URL(request.url);
|
|
453
|
+
const path = url.pathname + url.search;
|
|
454
|
+
if (url.pathname === "/_server") {
|
|
455
|
+
const sf = await vite.ssrLoadModule(SERVER_FNS_VMOD);
|
|
456
|
+
if (sf.isServerFnRequest(request)) return sf.handleServerFn(request);
|
|
457
|
+
}
|
|
458
|
+
if (isApiPath(url.pathname)) {
|
|
459
|
+
const api = await vite.ssrLoadModule(API_VIRTUAL_MODULE);
|
|
460
|
+
if (api.isApiRequest(request)) return api.handleApiRequest(request);
|
|
461
|
+
}
|
|
462
|
+
if (!cfg.ssr) {
|
|
463
|
+
const html2 = await vite.transformIndexHtml(path, template);
|
|
464
|
+
return new Response(html2, { status: 200, headers: { "content-type": "text/html; charset=utf-8" } });
|
|
465
|
+
}
|
|
466
|
+
const mod = await vite.ssrLoadModule("/" + cfg.serverEntry);
|
|
467
|
+
if (typeof mod.renderStream === "function") {
|
|
468
|
+
const shell = await vite.transformIndexHtml(
|
|
469
|
+
path,
|
|
470
|
+
injectStyles(template, await renderedStyles(vite, cfg.serverEntry))
|
|
471
|
+
);
|
|
472
|
+
const { head, tail } = splitTemplate(shell, cfg.mountId);
|
|
473
|
+
return streamDocument(head, await mod.renderStream(path, request), tail);
|
|
474
|
+
}
|
|
475
|
+
const render = mod.render ?? mod.default;
|
|
476
|
+
const appHtml = await render(path, request);
|
|
477
|
+
const styled = injectStyles(
|
|
478
|
+
injectAppAndHead(template, appHtml, cfg.mountId),
|
|
479
|
+
await renderedStyles(vite, cfg.serverEntry)
|
|
480
|
+
);
|
|
481
|
+
const html = await vite.transformIndexHtml(path, styled);
|
|
482
|
+
return new Response(html, { status: 200, headers: { "content-type": "text/html; charset=utf-8" } });
|
|
483
|
+
}, (e) => vite.ssrFixStacktrace?.(e));
|
|
484
|
+
const hasMiddleware = existsSync2(resolve2(cfg.root, cfg.middleware));
|
|
485
|
+
const handler = hasMiddleware ? guardHandler(async (request) => {
|
|
486
|
+
const mwMod = await vite.ssrLoadModule("/" + cfg.middleware);
|
|
487
|
+
return composeMiddleware(normalizeMiddleware(mwMod), renderHandler)(request);
|
|
488
|
+
}, (e) => vite.ssrFixStacktrace?.(e)) : renderHandler;
|
|
489
|
+
const nodeHandler = toNodeHandler(handler);
|
|
490
|
+
const http = createHttp((req, res) => {
|
|
491
|
+
vite.middlewares(req, res, () => nodeHandler(req, res));
|
|
492
|
+
});
|
|
493
|
+
vite = await createServer({
|
|
494
|
+
root: cfg.root,
|
|
495
|
+
plugins: await fluixiPlugins(cfg),
|
|
496
|
+
// file routing + JSX/SSR transform (fluixi)
|
|
497
|
+
server: { middlewareMode: true, hmr: { server: http } },
|
|
498
|
+
appType: "custom",
|
|
499
|
+
ssr: cfg.ssrNoExternal.length ? { noExternal: cfg.ssrNoExternal } : void 0
|
|
500
|
+
});
|
|
501
|
+
http.listen(cfg.port, () => devReady({ port: cfg.port, startedAt }));
|
|
502
|
+
}
|
|
503
|
+
export {
|
|
504
|
+
dev
|
|
505
|
+
};
|