@ubean/build 0.6.0-beta.1 → 0.6.0-beta.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/dist/codegen/index.d.ts +3 -0
- package/dist/codegen/index.js +1 -1
- package/dist/{codegen-B3oGeRtK.js → codegen-Up8-XhRo.js} +134 -12
- package/dist/production.js +1 -1
- package/dist/{vite-DvHEv7Op.js → vite-VaaeRB17.js} +154 -15
- package/dist/vite.d.ts +11 -1
- package/dist/vite.js +3 -3
- package/dist/{vue-BrbbaXTC.js → vue-CKAJjBBR.js} +217 -198
- package/dist/vue.js +1 -1
- package/package.json +21 -21
package/dist/codegen/index.d.ts
CHANGED
|
@@ -99,6 +99,8 @@ interface AutoImportResult {
|
|
|
99
99
|
components: ComponentInfo[];
|
|
100
100
|
autoImportsDtsPath: string;
|
|
101
101
|
componentsDtsPath: string;
|
|
102
|
+
/** 虚拟组件(配对/单边基名)的 ambient 声明文件。没有虚拟组件时内容为空声明。 */
|
|
103
|
+
virtualComponentsDtsPath: string;
|
|
102
104
|
}
|
|
103
105
|
export declare function generateAutoImports(_scanResult: ScanResult, options: GenerateAutoImportsOptions): Promise<AutoImportResult>;
|
|
104
106
|
export declare function getBuiltinComposables(): Import[];
|
|
@@ -228,6 +230,7 @@ export interface CodegenResult {
|
|
|
228
230
|
i18nTypesPath?: string | null;
|
|
229
231
|
autoImportsDtsPath?: string;
|
|
230
232
|
componentsDtsPath?: string;
|
|
233
|
+
virtualComponentsDtsPath?: string;
|
|
231
234
|
generated: string[];
|
|
232
235
|
}
|
|
233
236
|
export declare function generateTypes(result: ScanResult, options: CodegenOptions): Promise<CodegenResult>;
|
package/dist/codegen/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { C as getUbeanAutoImportConfig, D as toArray, E as resolveComponentsConfig, S as getBuiltinComposables, T as resolveAutoImportsConfig, _ as VUE_PRESET, a as generateOpenApiTypesFromServer, b as generateImportsTransform, c as generateI18nTypes, f as HONO_OPENAPI_PRESET, g as VUE_MACROS_PRESET, h as VUE_I18N_PRESET, i as generateOpenApiTypes, l as messagesToTsType, m as UBEAN_SERVER_PRESET, n as CODEGEN_FILES, o as generatePageTypes, p as UBEAN_CLIENT_PRESET, r as generateTypes, s as generateRouteTypes, t as CODEGEN_CONTRACT_VERSION, u as BUILTIN_PRESETS, v as VUE_ROUTER_PRESET, w as getUbeanComponentsConfig, x as getAutoImportPresets, y as generateAutoImports } from "../codegen-Up8-XhRo.js";
|
|
2
2
|
export { BUILTIN_PRESETS, CODEGEN_CONTRACT_VERSION, CODEGEN_FILES, HONO_OPENAPI_PRESET, UBEAN_CLIENT_PRESET, UBEAN_SERVER_PRESET, VUE_I18N_PRESET, VUE_MACROS_PRESET, VUE_PRESET, VUE_ROUTER_PRESET, generateAutoImports, generateI18nTypes, generateImportsTransform, generateOpenApiTypes, generateOpenApiTypesFromServer, generatePageTypes, generateRouteTypes, generateTypes, getAutoImportPresets, getBuiltinComposables, getUbeanAutoImportConfig, getUbeanComponentsConfig, messagesToTsType, resolveAutoImportsConfig, resolveComponentsConfig, toArray };
|
|
@@ -260,23 +260,55 @@ function transformImportPath(filePath, srcDir) {
|
|
|
260
260
|
function isPackageSource(from) {
|
|
261
261
|
return !from.startsWith(".") && !from.startsWith("/") && !from.startsWith("~");
|
|
262
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* `.server.vue` / `.client.vue` 不是独立组件,**不能**进自动导入表。
|
|
265
|
+
*
|
|
266
|
+
* 它们是同一个组件的两个变体:`.client.vue` 客户端独有、`.server.vue` 服务端独有,
|
|
267
|
+
* 成对出现时(`Foo.server.vue` + `Foo.client.vue`)由 `@ubean/islands` 的
|
|
268
|
+
* `resolveId` 按基名 `Foo.vue` 生成配对包装。用户侧的用法是**显式 import**
|
|
269
|
+
* (`import Foo from './Foo.vue'` 或直接 import 某一半),不存在 `<Foo.server />` 这种写法。
|
|
270
|
+
*
|
|
271
|
+
* 而按文件名派生名字会把它们扫成 `Foo.server` / `Foo.client` —— 这些名字进了
|
|
272
|
+
* `components.d.ts` 就是**非法 TypeScript**:ubean 自己的写入器会产出未加引号的
|
|
273
|
+
* 带点键(接口成员不允许限定名,`@ts-nocheck` 也压不住语法错误),
|
|
274
|
+
* unplugin-vue-components 重写时会产出 `const 'Foo.server': …`(`declare global` 块里
|
|
275
|
+
* 非法)—— 结果示例项目的 `pnpm type-check` 直接红。
|
|
276
|
+
*
|
|
277
|
+
* codegen 侧在 `scanComponentsDir` 里按这个形态分区(半成品不进组件表,但会被收成虚拟组件);
|
|
278
|
+
* unplugin 没有分区逻辑,所以这份 glob 直接喂给它的 `globsExclude`(`./vue-plugin.ts`),
|
|
279
|
+
* 保证两个写入器都不产出这些条目。
|
|
280
|
+
*/
|
|
281
|
+
const COMPONENT_HALF_GLOBS = ["**/*.server.vue", "**/*.client.vue"];
|
|
263
282
|
async function scanComponentsDir(dir, srcDir, directoryAsNamespace, ignore = [
|
|
264
283
|
"**/*.test.*",
|
|
265
284
|
"**/*.spec.*",
|
|
266
285
|
"**/_*"
|
|
267
286
|
]) {
|
|
268
287
|
const components = [];
|
|
288
|
+
const virtualComponents = [];
|
|
269
289
|
const files = await glob("**/*.vue", {
|
|
270
290
|
cwd: dir,
|
|
271
291
|
dot: true,
|
|
272
292
|
ignore,
|
|
273
293
|
absolute: true
|
|
274
294
|
}).catch(() => []);
|
|
295
|
+
/** 基名 → 半成品真实路径(`/foo/Base.server.vue` 等)。 */
|
|
296
|
+
const halves = /* @__PURE__ */ new Map();
|
|
297
|
+
/** 同目录下真实存在的 `.vue` 基名 —— 它们由 TS 自行解析,不需要 ambient 声明。 */
|
|
298
|
+
const realBases = /* @__PURE__ */ new Set();
|
|
275
299
|
for (const fullPath of files.sort()) {
|
|
276
300
|
const relativeToSrc = toPosixPath(relative(srcDir, fullPath));
|
|
277
301
|
const relativeToDir = toPosixPath(relative(dir, fullPath));
|
|
278
302
|
const base = fileBasename(fullPath);
|
|
279
303
|
if (base.startsWith("_")) continue;
|
|
304
|
+
const half = /^(.*)\.(server|client)$/.exec(base);
|
|
305
|
+
if (half) {
|
|
306
|
+
const entry = halves.get(half[1]) ?? {};
|
|
307
|
+
entry[half[2]] = fullPath;
|
|
308
|
+
halves.set(half[1], entry);
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
realBases.add(base);
|
|
280
312
|
let name;
|
|
281
313
|
if (directoryAsNamespace) {
|
|
282
314
|
const dirPart = fileDirname(relativeToDir) === "." ? "" : fileDirname(relativeToDir);
|
|
@@ -291,7 +323,18 @@ async function scanComponentsDir(dir, srcDir, directoryAsNamespace, ignore = [
|
|
|
291
323
|
pascalName: name
|
|
292
324
|
});
|
|
293
325
|
}
|
|
294
|
-
|
|
326
|
+
for (const [baseName, pair] of halves) {
|
|
327
|
+
if (realBases.has(baseName)) continue;
|
|
328
|
+
virtualComponents.push({
|
|
329
|
+
baseName,
|
|
330
|
+
name: toPascalCase(baseName),
|
|
331
|
+
typeSourcePath: pair.server ?? pair.client
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
components,
|
|
336
|
+
virtualComponents
|
|
337
|
+
};
|
|
295
338
|
}
|
|
296
339
|
async function generateAutoImports(_scanResult, options) {
|
|
297
340
|
const { cwd, srcDir, buildDir, dirs = {}, autoImports: autoImportsInput, components: componentsInput } = options;
|
|
@@ -304,6 +347,7 @@ async function generateAutoImports(_scanResult, options) {
|
|
|
304
347
|
const componentsDir = dirs.components || "components";
|
|
305
348
|
let composablesImports = [];
|
|
306
349
|
let components = [];
|
|
350
|
+
const virtualComponents = [];
|
|
307
351
|
const autoImportsDtsPath = join(outDir, "auto-imports.d.ts");
|
|
308
352
|
const componentsDtsPath = join(outDir, "components.d.ts");
|
|
309
353
|
if (resolvedAutoImports.enabled) {
|
|
@@ -330,29 +374,86 @@ async function generateAutoImports(_scanResult, options) {
|
|
|
330
374
|
from: transformImportPath(imp.from, srcDir)
|
|
331
375
|
};
|
|
332
376
|
});
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
} });
|
|
337
|
-
await writeFile(autoImportsDtsPath, dtsContent, "utf-8");
|
|
377
|
+
const resolveFrom = (from) => isPackageSource(from) ? from : transformImportPath(from, srcDir);
|
|
378
|
+
const dtsContent = toTypeDeclarationFile(composablesImports, { resolvePath: (imp) => resolveFrom(imp.from) });
|
|
379
|
+
await writeFile(autoImportsDtsPath, `${dtsContent}\n\n${buildTemplateAutoImportBlock(composablesImports, resolveFrom)}`, "utf-8");
|
|
338
380
|
} else await writeFile(autoImportsDtsPath, "// Auto-generated by ubean - auto-imports disabled\n/* eslint-disable */\n// @ts-nocheck\nexport {}\n", "utf-8");
|
|
339
381
|
if (resolvedComponents.enabled) {
|
|
340
382
|
const allComponentsDirs = [join(srcDir, componentsDir), ...resolvedComponents.options.dirs ?? []];
|
|
341
383
|
for (const dir of allComponentsDirs) {
|
|
342
384
|
const scanned = await scanComponentsDir(dir, srcDir, directoryAsNamespace);
|
|
343
|
-
components.push(...scanned);
|
|
385
|
+
components.push(...scanned.components);
|
|
386
|
+
virtualComponents.push(...scanned.virtualComponents);
|
|
344
387
|
}
|
|
345
388
|
}
|
|
346
389
|
const componentsDts = generateComponentsDts(components, componentsDtsPath);
|
|
347
390
|
await writeFile(componentsDtsPath, componentsDts, "utf-8");
|
|
391
|
+
const virtualComponentsDtsPath = join(outDir, "virtual-components.d.ts");
|
|
392
|
+
await writeFile(virtualComponentsDtsPath, generateVirtualComponentsDts(virtualComponents, virtualComponentsDtsPath), "utf-8");
|
|
348
393
|
return {
|
|
349
394
|
composablesImports,
|
|
350
395
|
components,
|
|
351
396
|
autoImportsDtsPath,
|
|
352
|
-
componentsDtsPath
|
|
397
|
+
componentsDtsPath,
|
|
398
|
+
virtualComponentsDtsPath
|
|
353
399
|
};
|
|
354
400
|
}
|
|
355
401
|
/**
|
|
402
|
+
* 生成「模板里的自动导入」声明段(对齐 `AutoImport({ vueTemplate: true })` 的产物)。
|
|
403
|
+
*
|
|
404
|
+
* **为什么必须自己产出这段**:模板表达式里的标识符由 vue 的**组件实例类型**解析
|
|
405
|
+
* (vue-tsc 把模板编译成 `__VLS_ctx.x`,即 `ComponentCustomProperties`),而不是走模块作用域的
|
|
406
|
+
* 全局声明。只有 `declare global { const … }` 时,`<script setup>` 里的自动导入照常工作,而
|
|
407
|
+
* **模板里**用同一个 helper 会报 `Property 'isPageCached' does not exist on type '{ $: … }'`。
|
|
408
|
+
*
|
|
409
|
+
* ubean 的 codegen 与 unplugin-auto-import 写的是同一份文件(谁后写谁赢),所以两边都必须产出
|
|
410
|
+
* 这段:codegen 只在 `prepare` / CLI dev / build 跑,unplugin 在每次 Vite dev/build 重写 ——
|
|
411
|
+
* 少一段就会出现「先跑 CLI 时红、跑过一次 dev 后变绿」这种漂移。
|
|
412
|
+
*/
|
|
413
|
+
function buildTemplateAutoImportBlock(imports, resolveFrom) {
|
|
414
|
+
return [
|
|
415
|
+
"// for vue template auto import",
|
|
416
|
+
"import { UnwrapRef } from 'vue'",
|
|
417
|
+
"declare module 'vue' {",
|
|
418
|
+
" interface GlobalComponents {}",
|
|
419
|
+
" interface ComponentCustomProperties {",
|
|
420
|
+
...imports.filter((imp) => imp.name && imp.from && imp.type !== true).map((imp) => ` readonly ${toDtsKey(imp.name)}: UnwrapRef<typeof import('${resolveFrom(imp.from)}')['${imp.name}']>`).sort(),
|
|
421
|
+
" }",
|
|
422
|
+
"}"
|
|
423
|
+
].join("\n");
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* 生成 `.ubean/virtual-components.d.ts` —— 虚拟组件(配对/单边基名)的 ambient 声明。
|
|
427
|
+
*
|
|
428
|
+
* 为什么必须单独一份文件:这份内容会被 `unplugin-vue-components` 的重写丢掉(它只捞
|
|
429
|
+
* `GlobalComponents` 接口里的条目,周围的 `declare module` 语句一律不保留),而
|
|
430
|
+
* `components.d.ts` 正是它拥有的文件。
|
|
431
|
+
*
|
|
432
|
+
* 用**通配**说明符(`*foo.vue`)而不是相对路径:`declare module` 里的相对路径只在「说明符
|
|
433
|
+
* 字符串完全相同」时匹配,而同一个虚拟组件可以从任意目录、任意相对路径导入。通配声明只在
|
|
434
|
+
* TS 无法解析到真实文件时才生效,因此不会遮蔽真实 `.vue` 文件。
|
|
435
|
+
*
|
|
436
|
+
* 类型取服务端变体(配对与 server-only 都有),单边 `.client.vue` 则取客户端变体 —— 与
|
|
437
|
+
* 运行时首屏渲染的那一侧一致。
|
|
438
|
+
*/
|
|
439
|
+
function generateVirtualComponentsDts(virtual, dtsPath) {
|
|
440
|
+
const dtsDir = fileDirname(toPosixPath(normalize(dtsPath)));
|
|
441
|
+
const lines = [
|
|
442
|
+
"// Auto-generated by ubean - do not edit manually",
|
|
443
|
+
"/* eslint-disable */",
|
|
444
|
+
"// @ts-nocheck",
|
|
445
|
+
"",
|
|
446
|
+
"// 本文件刻意保持 script(非模块)语义,让下面的声明成为全局 ambient 声明。",
|
|
447
|
+
""
|
|
448
|
+
];
|
|
449
|
+
const sorted = [...virtual].sort((a, b) => a.baseName.localeCompare(b.baseName));
|
|
450
|
+
for (const comp of sorted) {
|
|
451
|
+
const rel = toPosixPath(relative(dtsDir, toPosixPath(normalize(comp.typeSourcePath))));
|
|
452
|
+
lines.push(`// ${comp.name}:磁盘上没有 ${comp.baseName}.vue,由 @ubean/islands 按同名 .server.vue / .client.vue 合成`, `declare module '*${comp.baseName}.vue' {`, ` const component: typeof import('./${rel}')['default'];`, " export default component;", "}", "");
|
|
453
|
+
}
|
|
454
|
+
return `${lines.join("\n")}\n`;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
356
457
|
* Generate the `.ubean/components.d.ts` in a format compatible with
|
|
357
458
|
* unplugin-vue-components (both writers own the same file).
|
|
358
459
|
*
|
|
@@ -390,12 +491,26 @@ function generateComponentsDts(components, dtsPath) {
|
|
|
390
491
|
" export interface GlobalComponents {"
|
|
391
492
|
];
|
|
392
493
|
const sorted = [...entries.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
393
|
-
lines.push(...sorted.map(([name, declaration]) => ` ${name}: ${declaration}`));
|
|
494
|
+
lines.push(...sorted.map(([name, declaration]) => ` ${toDtsKey(name)}: ${declaration}`));
|
|
394
495
|
lines.push(" }");
|
|
395
496
|
lines.push("}");
|
|
396
497
|
lines.push("");
|
|
397
498
|
return `${lines.join("\n")}\n`;
|
|
398
499
|
}
|
|
500
|
+
/**
|
|
501
|
+
* 接口成员名 → 合法的 TS 键。
|
|
502
|
+
*
|
|
503
|
+
* 名字里含 `.` 时(如 `My.Comp.vue` 这种文件名)直接写就是**限定名**,在接口里是语法
|
|
504
|
+
* 错误 —— 而语法错误不受本文件顶部 `@ts-nocheck` 影响,整个 d.ts 会连累项目 type-check。
|
|
505
|
+
* 加引号后是合法的字符串字面量键,且 unplugin-vue-components 重写时也用同样形式
|
|
506
|
+
* (实测它把非标识符名写成 `'My.Comp': …`),两边不会互相反复改写。
|
|
507
|
+
*
|
|
508
|
+
* 注意这里只改**写法**不改名字:名字必须与 unplugin 的派生结果一致,否则两个写入器
|
|
509
|
+
* 会各自重写同一份文件。
|
|
510
|
+
*/
|
|
511
|
+
function toDtsKey(name) {
|
|
512
|
+
return /^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name);
|
|
513
|
+
}
|
|
399
514
|
function getBuiltinComposables() {
|
|
400
515
|
const imports = [];
|
|
401
516
|
for (const preset of BUILTIN_PRESETS) for (const name of preset.imports) if (typeof name === "string") imports.push({
|
|
@@ -725,6 +840,12 @@ const CODEGEN_FILES = [
|
|
|
725
840
|
module: null,
|
|
726
841
|
required: true,
|
|
727
842
|
types: []
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
name: "virtual-components.d.ts",
|
|
846
|
+
module: null,
|
|
847
|
+
required: false,
|
|
848
|
+
types: []
|
|
728
849
|
}
|
|
729
850
|
];
|
|
730
851
|
async function generateTypes(result, options) {
|
|
@@ -741,13 +862,13 @@ async function generateTypes(result, options) {
|
|
|
741
862
|
generated.push(pageTypesPath);
|
|
742
863
|
const i18nTypesPath = await generateI18nTypes(result, { outDir });
|
|
743
864
|
if (i18nTypesPath) generated.push(i18nTypesPath);
|
|
744
|
-
const { autoImportsDtsPath, componentsDtsPath } = await generateAutoImports(result, {
|
|
865
|
+
const { autoImportsDtsPath, componentsDtsPath, virtualComponentsDtsPath } = await generateAutoImports(result, {
|
|
745
866
|
cwd,
|
|
746
867
|
srcDir,
|
|
747
868
|
buildDir,
|
|
748
869
|
...autoImportOptions
|
|
749
870
|
});
|
|
750
|
-
generated.push(autoImportsDtsPath, componentsDtsPath);
|
|
871
|
+
generated.push(autoImportsDtsPath, componentsDtsPath, virtualComponentsDtsPath);
|
|
751
872
|
const manifest = {
|
|
752
873
|
contractVersion: 1,
|
|
753
874
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -765,8 +886,9 @@ async function generateTypes(result, options) {
|
|
|
765
886
|
i18nTypesPath,
|
|
766
887
|
autoImportsDtsPath,
|
|
767
888
|
componentsDtsPath,
|
|
889
|
+
virtualComponentsDtsPath,
|
|
768
890
|
generated
|
|
769
891
|
};
|
|
770
892
|
}
|
|
771
893
|
//#endregion
|
|
772
|
-
export {
|
|
894
|
+
export { getUbeanAutoImportConfig as C, toArray as D, resolveComponentsConfig as E, getBuiltinComposables as S, resolveAutoImportsConfig as T, VUE_PRESET as _, generateOpenApiTypesFromServer as a, generateImportsTransform as b, generateI18nTypes as c, COMPONENT_HALF_GLOBS as d, HONO_OPENAPI_PRESET as f, VUE_MACROS_PRESET as g, VUE_I18N_PRESET as h, generateOpenApiTypes as i, messagesToTsType as l, UBEAN_SERVER_PRESET as m, CODEGEN_FILES as n, generatePageTypes as o, UBEAN_CLIENT_PRESET as p, generateTypes as r, generateRouteTypes as s, CODEGEN_CONTRACT_VERSION as t, BUILTIN_PRESETS as u, VUE_ROUTER_PRESET as v, getUbeanComponentsConfig as w, getAutoImportPresets as x, generateAutoImports as y };
|
package/dist/production.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as getPresetBuildConfig, S as generateVirtualModulesToDisk, T as buildWithEnvironments, b as generateNodeServerEntry, v as createIslandsSsrStubPlugin, w as serializePagesForEntry, x as generateStandardHandlerEntry, y as generateCloudflareWorkerEntry } from "./vite-
|
|
1
|
+
import { C as getPresetBuildConfig, S as generateVirtualModulesToDisk, T as buildWithEnvironments, b as generateNodeServerEntry, v as createIslandsSsrStubPlugin, w as serializePagesForEntry, x as generateStandardHandlerEntry, y as generateCloudflareWorkerEntry } from "./vite-VaaeRB17.js";
|
|
2
2
|
export { buildWithEnvironments, createIslandsSsrStubPlugin, generateCloudflareWorkerEntry, generateNodeServerEntry, generateStandardHandlerEntry, generateVirtualModulesToDisk, getPresetBuildConfig, serializePagesForEntry };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { a as ssrSingletonProdOptimizeExclude, m as createVirtualRegistry, o as ssrSingletonProdSsr, y as __exportAll } from "./ssr-singleton-DPTawJSx.js";
|
|
2
2
|
import { a as createRoutingVirtualModule, i as createPagesVirtualModule, n as createLocalesVirtualModule, r as createMetaVirtualModule, s as transformMacros, t as createAppVirtualModule } from "./virtual-modules-CvGWlsXj.js";
|
|
3
|
-
import {
|
|
3
|
+
import { i as generateOpenApiTypes, r as generateTypes } from "./codegen-Up8-XhRo.js";
|
|
4
|
+
import { _ as toWebRequest, a as createVueAppEntryVirtualModule, b as getDevScanCoordinator, g as sendWebResponse, i as createServerEntryVirtualModule, n as ubeanVite, o as createVuePagesVirtualModule, r as createClientEntryVirtualModule, u as ensureDevApp } from "./vue-CKAJjBBR.js";
|
|
4
5
|
import { createStaticSsgRenderer } from "./static-render.js";
|
|
5
6
|
import { prerender, resolvePrerenderStaticDir } from "./prerender.js";
|
|
6
7
|
import { join, relative, resolve } from "pathe";
|
|
@@ -11,9 +12,9 @@ import { isEphemeralCachePreset, registerBuiltinPresets, resolvePresetByName, re
|
|
|
11
12
|
import { createServerRouter } from "@ubean/routes";
|
|
12
13
|
import { scanProject } from "@ubean/scan";
|
|
13
14
|
import { getLogger } from "@ubean/shared/logger";
|
|
14
|
-
import { getVueLocaleParam } from "@ubean/i18n";
|
|
15
15
|
import * as nodeFsPromises from "node:fs/promises";
|
|
16
16
|
import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
17
|
+
import { getVueLocaleParam } from "@ubean/i18n";
|
|
17
18
|
import { getColorModeScript, resolveColorModeConfig } from "@ubean/client";
|
|
18
19
|
import { DevEnvironment, createBuilder, normalizePath, perEnvironmentPlugin } from "vite";
|
|
19
20
|
import { ubeanIslandsPlugin } from "@ubean/islands/vite";
|
|
@@ -348,7 +349,7 @@ export async function renderStaticPage(input) {
|
|
|
348
349
|
* 核心插件是唯一提供者,取值顺序为「调用方传入的内存 manifest(RM-V18 的传递)→ 服务端
|
|
349
350
|
* outDir 旁的磁盘 manifest(`<outputDir>/public/.vite/manifest.json`)→ 空 + 构建期告警」。
|
|
350
351
|
*/
|
|
351
|
-
const logger$
|
|
352
|
+
const logger$6 = getLogger("build");
|
|
352
353
|
/** 本插件提供的虚拟模块 id。 */
|
|
353
354
|
const ASSET_MANIFEST_VIRTUAL_ID = "virtual:ubean-asset-manifest";
|
|
354
355
|
const RESOLVED_ID = `\0${ASSET_MANIFEST_VIRTUAL_ID}`;
|
|
@@ -434,7 +435,7 @@ function ubeanAssetManifestPlugin(getManifest, getServerOutDir) {
|
|
|
434
435
|
load(id, options) {
|
|
435
436
|
if (id !== RESOLVED_ID) return void 0;
|
|
436
437
|
const { tags, source } = resolveInjectedAssetTags(getManifest(), getServerOutDir?.());
|
|
437
|
-
if (source === "none") logger$
|
|
438
|
+
if (source === "none") logger$6.warn(`${ASSET_MANIFEST_VIRTUAL_ID} 注入时没有可用的 client manifest(环境:${options?.ssr ? "server" : "client"})—— 产出的 HTML 将不含 asset tags。`);
|
|
438
439
|
return `export const assetTags = ${JSON.stringify({
|
|
439
440
|
...tags,
|
|
440
441
|
favicon: null
|
|
@@ -667,7 +668,7 @@ function isDirectory(path) {
|
|
|
667
668
|
*
|
|
668
669
|
* 这里只搬移、不改语义:所有行为与旧编排原先逐段一致。
|
|
669
670
|
*/
|
|
670
|
-
const logger$
|
|
671
|
+
const logger$5 = getLogger("build");
|
|
671
672
|
/** 产物目录布局(`dist/` 根、`dist/public`、`dist/server`,虚拟模块落 `.ubean/virtual`)。 */
|
|
672
673
|
function getBuildOutDirs(cwd, outputDir) {
|
|
673
674
|
const outputRoot = resolve(cwd, outputDir);
|
|
@@ -682,7 +683,7 @@ function getBuildOutDirs(cwd, outputDir) {
|
|
|
682
683
|
/** 清空并重建产物目录(页面/服务端目录按 mode 决定是否创建)。 */
|
|
683
684
|
async function cleanBuildOutput(options) {
|
|
684
685
|
const { outDirs, hasPages, hasServer, mode, ssrEnabled } = options;
|
|
685
|
-
logger$
|
|
686
|
+
logger$5.info(`Cleaning output directory... (mode=${mode}${mode === "fullstack" ? `, ssr=${ssrEnabled}` : ""})`);
|
|
686
687
|
if (existsSync(outDirs.root)) await rm(outDirs.root, {
|
|
687
688
|
recursive: true,
|
|
688
689
|
force: true
|
|
@@ -751,7 +752,7 @@ ${tags.css ? ` ${tags.css.split("\n").join("\n ")}\n` : ""}</head>
|
|
|
751
752
|
async function writePresetWrapper(options) {
|
|
752
753
|
const { mode, presetBuildConfig, outDirs, entries } = options;
|
|
753
754
|
if (mode === "ssg") {
|
|
754
|
-
logger$
|
|
755
|
+
logger$5.info("SSG mode: skipping preset server wrapper (static output only)");
|
|
755
756
|
return "entry.mjs";
|
|
756
757
|
}
|
|
757
758
|
if (presetBuildConfig.entryType === "node") {
|
|
@@ -775,7 +776,7 @@ assets = { directory = "./public" }
|
|
|
775
776
|
`.trim();
|
|
776
777
|
await writeFile(join(outDirs.root, "wrangler.toml"), wranglerToml, "utf-8");
|
|
777
778
|
const unsupported = findUnsupportedNodeImports(outDirs.server);
|
|
778
|
-
if (unsupported.length > 0) logger$
|
|
779
|
+
if (unsupported.length > 0) logger$5.warn(`worker 产物包含 workerd 不支持的 Node 内建导入(${unsupported.join("、")})—— Worker 运行时会在启动时直接失败。来源通常是仅 Node 可用的静态服务/文件系统路径。`);
|
|
779
780
|
return "worker.mjs";
|
|
780
781
|
}
|
|
781
782
|
await writeFile(join(outDirs.server, "handler.mjs"), entries.standard(), "utf-8");
|
|
@@ -799,11 +800,11 @@ async function writeBuildManifest(options) {
|
|
|
799
800
|
preset: preset.name || "standard"
|
|
800
801
|
};
|
|
801
802
|
await writeFile(join(outDirs.root, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
802
|
-
logger$
|
|
803
|
-
if (hasPages) logger$
|
|
803
|
+
logger$5.info(`Build complete! Output: ${outDirs.root}`);
|
|
804
|
+
if (hasPages) logger$5.info(` Client assets: ${outDirs.public}`);
|
|
804
805
|
if (hasServer) {
|
|
805
|
-
logger$
|
|
806
|
-
logger$
|
|
806
|
+
logger$5.info(` Server bundle: ${outDirs.server}`);
|
|
807
|
+
logger$5.info(` Entry: ${serverEntry}`);
|
|
807
808
|
}
|
|
808
809
|
return manifest;
|
|
809
810
|
}
|
|
@@ -929,6 +930,79 @@ function createBuildEnvironments(input) {
|
|
|
929
930
|
};
|
|
930
931
|
}
|
|
931
932
|
//#endregion
|
|
933
|
+
//#region src/vite/openapi-step.ts
|
|
934
|
+
/**
|
|
935
|
+
* 构建期生成 `.ubean/openapi.d.ts`(OpenAPI → TypeScript 类型)。
|
|
936
|
+
*
|
|
937
|
+
* **为什么需要这一步**:`/_openapi.json` 是 `generateSpecs(app, …)`(hono-openapi)从 Hono app
|
|
938
|
+
* 现算的,需要 routes 已经注册并加载完毕。`ubean dev` 靠「起 server 再 HTTP 拉一次」拿到它,
|
|
939
|
+
* 于是类型声明**只在跑过一次 dev 之后才存在** —— 干净检出下 `pnpm type-check` 会报一片
|
|
940
|
+
* `Cannot find module '../../.ubean/openapi'` 以及连带的 `pathParams`/`query` 类型不匹配
|
|
941
|
+
* (实测:示例项目因此在 CI 里根本没法把 type-check 变成门禁)。
|
|
942
|
+
*
|
|
943
|
+
* 构建产物里的 `entry.mjs` 恰好就是那份 app,所以这里发一个**进程内请求**取 schema:不发网络、
|
|
944
|
+
* 不依赖 dev server,与 `createSsrFetcher` 同源(同一条「import entry → createFetchHandler →
|
|
945
|
+
* 合成 `http://localhost<path>` 请求」的路子)。
|
|
946
|
+
*
|
|
947
|
+
* 放在 `runEnvBuilds` 里而不是 CLI:`ubean build` 与 `vite build` 两条路径都要产出它
|
|
948
|
+
* (ADR-0012 的产物一致性)。
|
|
949
|
+
*/
|
|
950
|
+
const logger$4 = getLogger("build");
|
|
951
|
+
/**
|
|
952
|
+
* 从一个 app 的 `/_openapi.json` 生成类型声明(**进程内**请求,不走 HTTP)。
|
|
953
|
+
*
|
|
954
|
+
* 构建路径(`runOpenApiTypesStep`)与 dev 路径(核心插件的 `listening` 钩子,见
|
|
955
|
+
* `ensureDevApp`)共用这段:两者唯一的差别是 app 从哪来。
|
|
956
|
+
*/
|
|
957
|
+
async function generateOpenApiTypesFromApp(app, options) {
|
|
958
|
+
const response = await app.fetch(new Request("http://localhost/_openapi.json"));
|
|
959
|
+
if (!response.ok) return { skipped: `/_openapi.json responded ${response.status}` };
|
|
960
|
+
if (!(response.headers.get("content-type") || "").includes("json")) return { skipped: "no JSON response" };
|
|
961
|
+
const schema = await response.json();
|
|
962
|
+
return { filePath: await generateOpenApiTypes(schema, { outDir: join(options.cwd, options.buildDir) }) };
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* 从构建好的 SSR entry 取 `/_openapi.json` 并生成类型声明。
|
|
966
|
+
*
|
|
967
|
+
* 任何一步不成立都**不抛错**:类型声明是 DX 产物,不该让构建失败(与预渲染的容错策略一致)。
|
|
968
|
+
* 无后端模式(ssg / spa)本就没有服务端产物或该路由返回 HTML,直接跳过。
|
|
969
|
+
*/
|
|
970
|
+
async function runOpenApiTypesStep(options) {
|
|
971
|
+
const { cwd, manifest, buildDir } = options;
|
|
972
|
+
const entryPath = resolve(cwd, manifest.serverDir, "entry.mjs");
|
|
973
|
+
if (!existsSync(entryPath)) return { skipped: "no server entry" };
|
|
974
|
+
let mod;
|
|
975
|
+
try {
|
|
976
|
+
mod = await import(
|
|
977
|
+
/* @vite-ignore */
|
|
978
|
+
pathToFileURL(entryPath).href
|
|
979
|
+
);
|
|
980
|
+
} catch (err) {
|
|
981
|
+
logger$4.warn(`Failed to load SSR entry for OpenAPI types: ${err instanceof Error ? err.message : String(err)}`);
|
|
982
|
+
return { skipped: "entry import failed" };
|
|
983
|
+
}
|
|
984
|
+
try {
|
|
985
|
+
const createApp = mod.createApp;
|
|
986
|
+
if (typeof createApp !== "function") return { skipped: "no createApp export" };
|
|
987
|
+
const app = await createApp({ openAPI: {
|
|
988
|
+
scalarPath: "/_scalar",
|
|
989
|
+
openAPIPath: "/_openapi.json"
|
|
990
|
+
} });
|
|
991
|
+
if (typeof app?.fetch !== "function") return { skipped: "app.fetch not available" };
|
|
992
|
+
return await generateOpenApiTypesFromApp(app, {
|
|
993
|
+
cwd,
|
|
994
|
+
buildDir
|
|
995
|
+
});
|
|
996
|
+
} catch (err) {
|
|
997
|
+
logger$4.warn(`Failed to generate OpenAPI types: ${err instanceof Error ? err.message : String(err)}`);
|
|
998
|
+
return { skipped: "generation failed" };
|
|
999
|
+
} finally {
|
|
1000
|
+
try {
|
|
1001
|
+
await mod.close?.();
|
|
1002
|
+
} catch {}
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
//#endregion
|
|
932
1006
|
//#region src/vite/prerender-step.ts
|
|
933
1007
|
/**
|
|
934
1008
|
* 预渲染步骤(RM-V21 的最后一环)。
|
|
@@ -1254,6 +1328,15 @@ async function runEnvBuilds(builder, prepared) {
|
|
|
1254
1328
|
manifest: builtManifest,
|
|
1255
1329
|
contentSnapshot: prepared.contentSnapshot
|
|
1256
1330
|
});
|
|
1331
|
+
if (hasServer) {
|
|
1332
|
+
const openApiResult = await runOpenApiTypesStep({
|
|
1333
|
+
cwd,
|
|
1334
|
+
manifest: builtManifest,
|
|
1335
|
+
buildDir: ".ubean"
|
|
1336
|
+
});
|
|
1337
|
+
if (openApiResult.filePath) logger$2.info(`OpenAPI types generated: ${openApiResult.filePath}`);
|
|
1338
|
+
else if (openApiResult.skipped) logger$2.info(`OpenAPI types skipped (${openApiResult.skipped}).`);
|
|
1339
|
+
}
|
|
1257
1340
|
return builtManifest;
|
|
1258
1341
|
}
|
|
1259
1342
|
/**
|
|
@@ -2376,6 +2459,8 @@ async function buildEnvironmentsForConfig(config) {
|
|
|
2376
2459
|
function ubeanPlugin(options) {
|
|
2377
2460
|
const virtualRegistry = options?.registry ?? createVirtualRegistry();
|
|
2378
2461
|
let ubeanConfig = options?.config ?? tryGetConfig() ?? void 0;
|
|
2462
|
+
/** 已跑过 codegen 的扫描结果(`buildStart` 会在两个环境各跑一次)。 */
|
|
2463
|
+
let lastCodegenScan = null;
|
|
2379
2464
|
/** client manifest 的内存载体(RM-V18):`buildApp` 填、本插件的 load 读。 */
|
|
2380
2465
|
const assetManifestRef = { current: null };
|
|
2381
2466
|
let srcDirAbs = "";
|
|
@@ -2425,8 +2510,9 @@ function ubeanPlugin(options) {
|
|
|
2425
2510
|
* `dist/{public,server,manifest.json}`。builder 与 environments 由 Vite 按本钩子创建,
|
|
2426
2511
|
* 插件只跑拆分好的阶段(`prepareBuild` → `runEnvBuilds`),**不**自建 builder(会递归)。
|
|
2427
2512
|
*
|
|
2428
|
-
*
|
|
2429
|
-
*
|
|
2513
|
+
* 预渲染也在本路径内(`runEnvBuilds` → `runPrerenderStep`,同一份实现 CLI 路径也在用)。
|
|
2514
|
+
* 实测(2026-09,examples/ubean-test,`vp build` vs `ubean build`):产物树逐项一致,
|
|
2515
|
+
* 189 个文件全部对应,仅资源哈希不同 —— 别再照抄「`vite build` 缺静态 HTML」的旧结论。
|
|
2430
2516
|
*/
|
|
2431
2517
|
builder: { async buildApp(builder) {
|
|
2432
2518
|
const [{ createBuildContext, prepareBuild, runEnvBuilds }, { resolvePresetByName, registerBuiltinPresets }, { loadContentForBuild }] = await Promise.all([
|
|
@@ -2524,6 +2610,7 @@ function ubeanPlugin(options) {
|
|
|
2524
2610
|
reload: () => server.ws.send({ type: "full-reload" })
|
|
2525
2611
|
})).subscribe(async (result, changed) => {
|
|
2526
2612
|
await applyScan(result);
|
|
2613
|
+
await runProjectCodegen(result);
|
|
2527
2614
|
for (const mod of VIRTUAL_MODULES) {
|
|
2528
2615
|
const module = server.moduleGraph.getModuleById(VIRTUAL_PREFIX + mod);
|
|
2529
2616
|
if (module) server.moduleGraph.invalidateModule(module);
|
|
@@ -2534,6 +2621,24 @@ function ubeanPlugin(options) {
|
|
|
2534
2621
|
data: { file }
|
|
2535
2622
|
});
|
|
2536
2623
|
});
|
|
2624
|
+
if (process.env.UBEAN_CODEGEN_BY_CLI !== "1") {
|
|
2625
|
+
const runOpenApiTypes = async () => {
|
|
2626
|
+
if (!ubeanConfig) return;
|
|
2627
|
+
try {
|
|
2628
|
+
const app = await ensureDevApp(server);
|
|
2629
|
+
if (!app) return;
|
|
2630
|
+
const result = await generateOpenApiTypesFromApp(app, {
|
|
2631
|
+
cwd: ubeanConfig.rootDir,
|
|
2632
|
+
buildDir: ".ubean"
|
|
2633
|
+
});
|
|
2634
|
+
if (result.filePath) logger.info(`OpenAPI types generated: ${result.filePath}`);
|
|
2635
|
+
} catch (err) {
|
|
2636
|
+
logger.warn(`Failed to generate OpenAPI types: ${err instanceof Error ? err.message : String(err)}`);
|
|
2637
|
+
}
|
|
2638
|
+
};
|
|
2639
|
+
if (server.httpServer) server.httpServer.once("listening", () => void runOpenApiTypes());
|
|
2640
|
+
else runOpenApiTypes();
|
|
2641
|
+
}
|
|
2537
2642
|
},
|
|
2538
2643
|
/**
|
|
2539
2644
|
* RM-V24:`vite preview` 接管。
|
|
@@ -2557,7 +2662,41 @@ function ubeanPlugin(options) {
|
|
|
2557
2662
|
}
|
|
2558
2663
|
async function scanAndRegister() {
|
|
2559
2664
|
if (!ubeanConfig) return;
|
|
2560
|
-
|
|
2665
|
+
const result = await scanProjectOnce();
|
|
2666
|
+
await applyScan(result);
|
|
2667
|
+
await runProjectCodegen(result);
|
|
2668
|
+
}
|
|
2669
|
+
/**
|
|
2670
|
+
* 生成项目类型声明(`.ubean/routes.d.ts`、`pages.d.ts`、`i18n.d.ts`、`auto-imports.d.ts`、
|
|
2671
|
+
* `components.d.ts`、`virtual-components.d.ts` …)。
|
|
2672
|
+
*
|
|
2673
|
+
* **为什么由插件负责**:此前只有 CLI(`ubean dev` / `build` / `prepare`)会跑这套 codegen,
|
|
2674
|
+
* 于是裸 Vite 路径(`vite dev` / `vite build`)下这些文件根本不生成 —— 用户的 type-check
|
|
2675
|
+
* 与编辑器补全直接不成立。实测:`vp build` 之后示例的 `pnpm type-check` 报
|
|
2676
|
+
* `Cannot find module '../components/sc/ThemeBadge.vue'`(虚拟组件声明缺失),以及
|
|
2677
|
+
* `routes.d.ts`/`pages.d.ts` 缺失导致的一连串类型错误。ADR-0012 说两条路径等价,
|
|
2678
|
+
* 那这一步也得在两条路径上都成立。
|
|
2679
|
+
*
|
|
2680
|
+
* 与 CLI 的分工:CLI 已经生成过时插件让位(`UBEAN_CODEGEN_BY_CLI` / 构建期的
|
|
2681
|
+
* `UBEAN_BUILD_DRIVEN_BY_CLI`),避免同一份产物被两个执行者反复重写。
|
|
2682
|
+
*/
|
|
2683
|
+
async function runProjectCodegen(result) {
|
|
2684
|
+
if (!ubeanConfig) return;
|
|
2685
|
+
if (process.env.UBEAN_CODEGEN_BY_CLI === "1" || process.env.UBEAN_BUILD_DRIVEN_BY_CLI === "1") return;
|
|
2686
|
+
if (lastCodegenScan === result) return;
|
|
2687
|
+
lastCodegenScan = result;
|
|
2688
|
+
try {
|
|
2689
|
+
await generateTypes(result, {
|
|
2690
|
+
cwd: ubeanConfig.rootDir,
|
|
2691
|
+
srcDir: ubeanConfig.srcDir,
|
|
2692
|
+
buildDir: ".ubean",
|
|
2693
|
+
dirs: ubeanConfig.dir,
|
|
2694
|
+
autoImports: ubeanConfig.autoImports,
|
|
2695
|
+
components: ubeanConfig.components
|
|
2696
|
+
});
|
|
2697
|
+
} catch (err) {
|
|
2698
|
+
logger.warn(`Failed to generate type definitions: ${err instanceof Error ? err.message : String(err)}`);
|
|
2699
|
+
}
|
|
2561
2700
|
}
|
|
2562
2701
|
/**
|
|
2563
2702
|
* 依据扫描结果重建路由/中间件注册与虚拟模块。与扫描分离(RM-V13):dev 下由
|
package/dist/vite.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { i as VirtualModuleResolver, o as createVirtualRegistry, r as VirtualModuleRegistry } from "./virtual-registry-CaU8k-YP.js";
|
|
2
2
|
import { ResolvedConfig } from "@ubean/config";
|
|
3
3
|
import { ScanResult, ScannedLayout, ScannedPageRoute } from "@ubean/scan";
|
|
4
|
+
import { UbeanApp } from "@ubean/app";
|
|
4
5
|
import { Connect, DevEnvironment, DevEnvironmentContext, HotChannel, Logger, Plugin, ResolvedConfig as ResolvedConfig$1, ViteDevServer } from "vite";
|
|
5
6
|
import { PageRenderer, PageRenderer as PageRenderer$1 } from "@ubean/pages";
|
|
6
|
-
import { UbeanApp } from "@ubean/app";
|
|
7
7
|
import { IncomingMessage, ServerResponse } from "node:http";
|
|
8
8
|
//#region src/dev/dev-worker.d.ts
|
|
9
9
|
/** 生成到 `.ubean/` 下的文件名。 */
|
|
@@ -199,6 +199,16 @@ interface DevBootstrapOptions {
|
|
|
199
199
|
export declare function getDevApp(server: ViteDevServer): {
|
|
200
200
|
fetch: (request: Request) => Response | Promise<Response>;
|
|
201
201
|
} | undefined;
|
|
202
|
+
/**
|
|
203
|
+
* 按需触发自举(而不是等第一个请求),返回当前 app。
|
|
204
|
+
*
|
|
205
|
+
* 用途:裸 `vite dev` 下没有 CLI 去生成 OpenAPI 类型,而该类型来自 app 的 `/_openapi.json` ——
|
|
206
|
+
* 消费方(`@ubean/build` 的 dev openapi 步骤)需要能主动把 app 立起来,不能等页面请求。
|
|
207
|
+
* 与 `getDevApp` 一样挂在 server 对象上(插件与 CLI 可能持有不同的模块实例)。
|
|
208
|
+
*/
|
|
209
|
+
export declare function ensureDevApp(server: ViteDevServer): Promise<{
|
|
210
|
+
fetch: (request: Request) => Response | Promise<Response>;
|
|
211
|
+
}> | undefined;
|
|
202
212
|
interface DevRequestHandlers {
|
|
203
213
|
/** `configureServer` 钩子体内使用:排在 Vite transform/静态中间件之前。 */
|
|
204
214
|
pre: Connect.NextHandleFunction;
|
package/dist/vite.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { m as createVirtualRegistry } from "./ssr-singleton-DPTawJSx.js";
|
|
2
|
-
import { C as
|
|
3
|
-
import { D as createCloudflarePreviewRunner, E as MINIFLARE_INSTALL_HINT, O as defaultLoadMiniflare, _ as ubeanPreviewPlugin, a as createEnvRunnerTransport, c as getDevWorkerEntrySource, d as attachPreviewMiddleware, f as createPreviewMiddleware, g as sendPreviewStatus, h as sendPreviewFile, i as bridgeEnvRunnerInvokes, k as readCompatibilityDate, l as invalidateDevWorkerModules, m as resolvePreviewFile, n as UBEAN_DEV_ENV_NAME, o as createUbeanDevEnvironmentFactory, p as previewMimeType, r as UbeanDevEnvironment, s as DEV_WORKER_FILE, t as ubeanPlugin, u as writeDevWorkerEntry } from "./vite-
|
|
4
|
-
export { DEVTOOLS_PASS_THROUGH_PREFIXES, DEV_SCAN_DIRS, DEV_WORKER_FILE, MINIFLARE_INSTALL_HINT, UBEAN_DEV_ENV_NAME, UbeanDevEnvironment, attachPreviewMiddleware, bootstrapDevApp, bridgeEnvRunnerInvokes, buildDevSsrRoutes, collectDevCssLinks, createCloudflarePreviewRunner, createDevApp, createDevAppReady, createDevScanCoordinator, createEnvRunnerTransport, createPreviewMiddleware, createUbeanDevEnvironmentFactory, createUbeanRequestHandlers, createVirtualRegistry, defaultLoadMiniflare, enhanceDevApp, getDevApp, getDevScanCoordinator, getDevWorkerEntrySource, injectStylesheetLinks, invalidateDevWorkerModules, isFrameworkHtmlPage, isViteResourceRequest, onDevScan, peekDevScanCoordinator, previewMimeType, readCompatibilityDate, resolveDevSecurityHeaders, resolveLocaleVueParam, resolvePreviewFile, sendPreviewFile, sendPreviewStatus, sendWebResponse, toVueRouterPagePath, toWebRequest, ubeanDevRequestPlugin, ubeanPlugin, ubeanPreviewPlugin, writeDevWorkerEntry };
|
|
2
|
+
import { A as toVueRouterPagePath, C as bootstrapDevApp, D as resolveLocaleVueParam, E as resolveDevSecurityHeaders, O as enhanceDevApp, S as peekDevScanCoordinator, T as createDevAppReady, _ as toWebRequest, b as getDevScanCoordinator, c as collectDevCssLinks, d as getDevApp, f as injectStylesheetLinks, g as sendWebResponse, h as ubeanDevRequestPlugin, k as buildDevSsrRoutes, l as createUbeanRequestHandlers, m as isViteResourceRequest, p as isFrameworkHtmlPage, s as DEVTOOLS_PASS_THROUGH_PREFIXES, u as ensureDevApp, v as DEV_SCAN_DIRS, w as createDevApp, x as onDevScan, y as createDevScanCoordinator } from "./vue-CKAJjBBR.js";
|
|
3
|
+
import { D as createCloudflarePreviewRunner, E as MINIFLARE_INSTALL_HINT, O as defaultLoadMiniflare, _ as ubeanPreviewPlugin, a as createEnvRunnerTransport, c as getDevWorkerEntrySource, d as attachPreviewMiddleware, f as createPreviewMiddleware, g as sendPreviewStatus, h as sendPreviewFile, i as bridgeEnvRunnerInvokes, k as readCompatibilityDate, l as invalidateDevWorkerModules, m as resolvePreviewFile, n as UBEAN_DEV_ENV_NAME, o as createUbeanDevEnvironmentFactory, p as previewMimeType, r as UbeanDevEnvironment, s as DEV_WORKER_FILE, t as ubeanPlugin, u as writeDevWorkerEntry } from "./vite-VaaeRB17.js";
|
|
4
|
+
export { DEVTOOLS_PASS_THROUGH_PREFIXES, DEV_SCAN_DIRS, DEV_WORKER_FILE, MINIFLARE_INSTALL_HINT, UBEAN_DEV_ENV_NAME, UbeanDevEnvironment, attachPreviewMiddleware, bootstrapDevApp, bridgeEnvRunnerInvokes, buildDevSsrRoutes, collectDevCssLinks, createCloudflarePreviewRunner, createDevApp, createDevAppReady, createDevScanCoordinator, createEnvRunnerTransport, createPreviewMiddleware, createUbeanDevEnvironmentFactory, createUbeanRequestHandlers, createVirtualRegistry, defaultLoadMiniflare, enhanceDevApp, ensureDevApp, getDevApp, getDevScanCoordinator, getDevWorkerEntrySource, injectStylesheetLinks, invalidateDevWorkerModules, isFrameworkHtmlPage, isViteResourceRequest, onDevScan, peekDevScanCoordinator, previewMimeType, readCompatibilityDate, resolveDevSecurityHeaders, resolveLocaleVueParam, resolvePreviewFile, sendPreviewFile, sendPreviewStatus, sendWebResponse, toVueRouterPagePath, toWebRequest, ubeanDevRequestPlugin, ubeanPlugin, ubeanPreviewPlugin, writeDevWorkerEntry };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { c as getComponentResolvers, h as defineVirtualModule, i as ssrSingletonDevPolicy, l as getCssImports, m as createVirtualRegistry } from "./ssr-singleton-DPTawJSx.js";
|
|
2
|
-
import {
|
|
2
|
+
import { D as toArray, E as resolveComponentsConfig, T as resolveAutoImportsConfig, d as COMPONENT_HALF_GLOBS, x as getAutoImportPresets } from "./codegen-Up8-XhRo.js";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { join, resolve } from "pathe";
|
|
5
5
|
import { loadUbeanConfig } from "@ubean/config";
|
|
6
6
|
import { scanProject } from "@ubean/scan";
|
|
7
|
+
import { applyServerConfig, createUbeanApp } from "@ubean/app";
|
|
7
8
|
import { getVueLocaleParam, toVueRouterLocalePath } from "@ubean/i18n";
|
|
8
9
|
import { getColorModeScript, getPartyTownScript, resolveColorModeConfig, resolvePartyTownConfig } from "@ubean/client";
|
|
9
10
|
import { transformWithOxc } from "vite";
|
|
@@ -14,201 +15,7 @@ import AutoImport from "unplugin-auto-import/vite";
|
|
|
14
15
|
import { ubeanMdxPlugin } from "@ubean/markdown";
|
|
15
16
|
import { renderFaviconLink } from "@ubean/pages";
|
|
16
17
|
import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite";
|
|
17
|
-
import { applyServerConfig, createUbeanApp } from "@ubean/app";
|
|
18
18
|
import { generatePagesModuleSource } from "@ubean/vue/vite";
|
|
19
|
-
//#region src/dev/dev-scan.ts
|
|
20
|
-
/** 触发扫描的目录名(相对 `srcDir`)。并集来自 core 与 vue 两个插件此前的清单。 */
|
|
21
|
-
const DEV_SCAN_DIRS = [
|
|
22
|
-
"routes",
|
|
23
|
-
"middleware",
|
|
24
|
-
"pages",
|
|
25
|
-
"layouts",
|
|
26
|
-
"plugins",
|
|
27
|
-
"app",
|
|
28
|
-
"api",
|
|
29
|
-
"locales"
|
|
30
|
-
];
|
|
31
|
-
const SCAN_FILE_EXTENSIONS = /\.(ts|js|mjs|cjs|mts|cts|vue|json|md|mdx)$/;
|
|
32
|
-
/**
|
|
33
|
-
* `srcDir` **根部**的入口文件名(`app.ts` / `app.vue` / `App.vue` / `server.ts` /
|
|
34
|
-
* `server.dev.ts` …)。它们不在任何被监听的目录里,却同样决定服务端行为
|
|
35
|
-
* (appRoot、defineApp、defineServer),因此必须纳入判据。
|
|
36
|
-
*
|
|
37
|
-
* 用静态规则而不是「等第一次扫描拿到 appEntry 再登记」:后者在入口文件本身是第一个改动对象时
|
|
38
|
-
* 会漏掉 —— 首次扫描永远等不到,入口文件也就永远进不了监听集合(这正是此前「改 app.ts
|
|
39
|
-
* 完全不触发重扫」的缺陷)。
|
|
40
|
-
*/
|
|
41
|
-
const ENTRY_FILE_PATTERN = /^(app|App)(\.(server|client))?\.(ts|js|mjs|mts|vue)$|^server(\.(dev|prod))?\.(ts|js|mjs|mts)$/;
|
|
42
|
-
function createDevScanCoordinator(options) {
|
|
43
|
-
const { rootDir, source, debounceMs = 150 } = options;
|
|
44
|
-
const srcDir = options.srcDir.startsWith("/") ? options.srcDir : `${rootDir}/${options.srcDir}`;
|
|
45
|
-
const srcDirNormalized = srcDir.replace(/\/+$/, "");
|
|
46
|
-
const subscribers = /* @__PURE__ */ new Set();
|
|
47
|
-
const pending = /* @__PURE__ */ new Set();
|
|
48
|
-
let debounceTimer = null;
|
|
49
|
-
let running = null;
|
|
50
|
-
let rerunRequested = false;
|
|
51
|
-
let started = false;
|
|
52
|
-
const warn = options.onError ?? ((error) => console.error("[ubean] scan failed:", error));
|
|
53
|
-
/** 扫描目录(绝对)与「额外监听的文件」(入口文件,由扫描结果给出)。 */
|
|
54
|
-
const scanDirPaths = DEV_SCAN_DIRS.map((dir) => `${srcDirNormalized}/${dir}`);
|
|
55
|
-
const extraWatchFiles = /* @__PURE__ */ new Set();
|
|
56
|
-
function isIgnored(file) {
|
|
57
|
-
if (file.startsWith(".ubean/") || file.includes("/.ubean/")) return true;
|
|
58
|
-
if (file.includes("/node_modules/") || file.includes("/.git/")) return true;
|
|
59
|
-
if (file.endsWith(".bak")) return true;
|
|
60
|
-
return (options.ignore ?? []).some((pattern) => file.includes(pattern.replace(/\*/g, "")));
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* 事件是否与扫描相关。
|
|
64
|
-
*
|
|
65
|
-
* 判据是**绝对路径**(Vite 的 watcher 给的就是绝对路径):早先 CLI 那套监听自己把
|
|
66
|
-
* `srcDir` 与事件名拼成路径,`path.join` 遇到绝对路径不重置导致拼出 `<cwd><cwd>/src/...`,
|
|
67
|
-
* 所有监听目标 ENOENT 且异常被吞 —— 表现为「服务端改动完全不生效」。这里不做任何路径拼接。
|
|
68
|
-
*/
|
|
69
|
-
function isScanRelevant(file) {
|
|
70
|
-
if (!file.endsWith(".vue") && !SCAN_FILE_EXTENSIONS.test(file)) return false;
|
|
71
|
-
if (extraWatchFiles.has(file)) return true;
|
|
72
|
-
if (!file.startsWith(`${srcDirNormalized}/`)) return false;
|
|
73
|
-
const rest = file.slice(srcDirNormalized.length + 1);
|
|
74
|
-
if (DEV_SCAN_DIRS.includes(rest.split("/")[0])) return true;
|
|
75
|
-
return !rest.includes("/") && ENTRY_FILE_PATTERN.test(rest);
|
|
76
|
-
}
|
|
77
|
-
function registerExtraFiles(result) {
|
|
78
|
-
const entries = [
|
|
79
|
-
result.appEntry?.shared,
|
|
80
|
-
result.appEntry?.server,
|
|
81
|
-
result.appEntry?.client,
|
|
82
|
-
result.appEntry?.root,
|
|
83
|
-
result.serverEntry?.shared,
|
|
84
|
-
result.serverEntry?.dev,
|
|
85
|
-
result.serverEntry?.prod
|
|
86
|
-
];
|
|
87
|
-
for (const entry of entries) if (entry?.exists && entry.fullPath && !extraWatchFiles.has(entry.fullPath)) {
|
|
88
|
-
extraWatchFiles.add(entry.fullPath);
|
|
89
|
-
source.add(entry.fullPath);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
async function runScan(changed) {
|
|
93
|
-
const result = await (options.scan ?? (() => scanProject({
|
|
94
|
-
cwd: rootDir,
|
|
95
|
-
srcDir,
|
|
96
|
-
dirs: options.dirs,
|
|
97
|
-
ignore: options.ignore
|
|
98
|
-
})))();
|
|
99
|
-
registerExtraFiles(result);
|
|
100
|
-
for (const subscriber of subscribers) await subscriber(result, changed);
|
|
101
|
-
options.reload?.();
|
|
102
|
-
return result;
|
|
103
|
-
}
|
|
104
|
-
async function rescan(changed = []) {
|
|
105
|
-
if (running) {
|
|
106
|
-
rerunRequested = true;
|
|
107
|
-
return running;
|
|
108
|
-
}
|
|
109
|
-
running = runScan(changed).catch((error) => {
|
|
110
|
-
warn(error);
|
|
111
|
-
throw error;
|
|
112
|
-
});
|
|
113
|
-
try {
|
|
114
|
-
return await running;
|
|
115
|
-
} finally {
|
|
116
|
-
running = null;
|
|
117
|
-
if (rerunRequested) {
|
|
118
|
-
rerunRequested = false;
|
|
119
|
-
rescan([...pending]).catch(() => {});
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
function queue(file) {
|
|
124
|
-
pending.add(file);
|
|
125
|
-
if (debounceTimer) clearTimeout(debounceTimer);
|
|
126
|
-
debounceTimer = setTimeout(() => {
|
|
127
|
-
debounceTimer = null;
|
|
128
|
-
const changed = [...pending];
|
|
129
|
-
pending.clear();
|
|
130
|
-
rescan(changed).catch(() => {});
|
|
131
|
-
}, debounceMs);
|
|
132
|
-
}
|
|
133
|
-
const onFileEvent = (file) => {
|
|
134
|
-
if (isIgnored(file) || !isScanRelevant(file)) return;
|
|
135
|
-
queue(file);
|
|
136
|
-
};
|
|
137
|
-
return {
|
|
138
|
-
subscribe(fn) {
|
|
139
|
-
subscribers.add(fn);
|
|
140
|
-
return () => subscribers.delete(fn);
|
|
141
|
-
},
|
|
142
|
-
start() {
|
|
143
|
-
if (started) return;
|
|
144
|
-
started = true;
|
|
145
|
-
for (const dir of scanDirPaths) source.add(dir);
|
|
146
|
-
source.on("add", onFileEvent);
|
|
147
|
-
source.on("unlink", onFileEvent);
|
|
148
|
-
source.on("change", onFileEvent);
|
|
149
|
-
},
|
|
150
|
-
rescan,
|
|
151
|
-
subscriberCount: () => subscribers.size,
|
|
152
|
-
stop() {
|
|
153
|
-
if (!started) return;
|
|
154
|
-
started = false;
|
|
155
|
-
source.off?.("add", onFileEvent);
|
|
156
|
-
source.off?.("unlink", onFileEvent);
|
|
157
|
-
source.off?.("change", onFileEvent);
|
|
158
|
-
if (debounceTimer) {
|
|
159
|
-
clearTimeout(debounceTimer);
|
|
160
|
-
debounceTimer = null;
|
|
161
|
-
}
|
|
162
|
-
pending.clear();
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
const SCAN_STATE_KEY = "__ubeanScanState";
|
|
167
|
-
function scanStateOf(server) {
|
|
168
|
-
const holder = server;
|
|
169
|
-
holder[SCAN_STATE_KEY] ??= {};
|
|
170
|
-
return holder[SCAN_STATE_KEY];
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* 取(或建立)该 dev server 的协调器。
|
|
174
|
-
*
|
|
175
|
-
* core 与 vue 插件都调用它,因此**先到的那个**负责建实例;后到的拿同一个。
|
|
176
|
-
* `init` 只在首次调用时执行。
|
|
177
|
-
*/
|
|
178
|
-
function getDevScanCoordinator(server, init) {
|
|
179
|
-
const state = scanStateOf(server);
|
|
180
|
-
if (state.coordinator) return state.coordinator;
|
|
181
|
-
const coordinator = createDevScanCoordinator(init(server.watcher));
|
|
182
|
-
state.coordinator = coordinator;
|
|
183
|
-
if (state.pending) {
|
|
184
|
-
for (const fn of state.pending) coordinator.subscribe(fn);
|
|
185
|
-
state.pending = void 0;
|
|
186
|
-
}
|
|
187
|
-
coordinator.start();
|
|
188
|
-
return coordinator;
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* 取该 dev server **已存在**的协调器(不存在返回 undefined)。
|
|
192
|
-
*
|
|
193
|
-
* 给「只想触发一次扫描」的调用方用(CLI 的 `rescan()`):`getDevScanCoordinator` 需要 init
|
|
194
|
-
* 参数、会在缺失时建实例,不适合这里。
|
|
195
|
-
*/
|
|
196
|
-
function peekDevScanCoordinator(server) {
|
|
197
|
-
return scanStateOf(server).coordinator;
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* 订阅 dev 扫描(CLI 用:每次扫描后重建 Hono app 并让浏览器重载)。
|
|
201
|
-
*
|
|
202
|
-
* 允许在协调器建立之前订阅:`getDevScanCoordinator` 会把已登记的订阅转交给新实例。
|
|
203
|
-
*/
|
|
204
|
-
function onDevScan(server, handler) {
|
|
205
|
-
const state = scanStateOf(server);
|
|
206
|
-
if (state.coordinator) return state.coordinator.subscribe(handler);
|
|
207
|
-
state.pending ??= /* @__PURE__ */ new Set();
|
|
208
|
-
state.pending.add(handler);
|
|
209
|
-
return () => state.pending?.delete(handler);
|
|
210
|
-
}
|
|
211
|
-
//#endregion
|
|
212
19
|
//#region src/dev/dev-ssr-routes.ts
|
|
213
20
|
/**
|
|
214
21
|
* dev/SSR 路由表(RM-V11 的组成部件,从 CLI dev server 抽出)。
|
|
@@ -587,6 +394,199 @@ function resolveLocaleVueParam(config) {
|
|
|
587
394
|
});
|
|
588
395
|
}
|
|
589
396
|
//#endregion
|
|
397
|
+
//#region src/dev/dev-scan.ts
|
|
398
|
+
/** 触发扫描的目录名(相对 `srcDir`)。并集来自 core 与 vue 两个插件此前的清单。 */
|
|
399
|
+
const DEV_SCAN_DIRS = [
|
|
400
|
+
"routes",
|
|
401
|
+
"middleware",
|
|
402
|
+
"pages",
|
|
403
|
+
"layouts",
|
|
404
|
+
"plugins",
|
|
405
|
+
"app",
|
|
406
|
+
"api",
|
|
407
|
+
"locales"
|
|
408
|
+
];
|
|
409
|
+
const SCAN_FILE_EXTENSIONS = /\.(ts|js|mjs|cjs|mts|cts|vue|json|md|mdx)$/;
|
|
410
|
+
/**
|
|
411
|
+
* `srcDir` **根部**的入口文件名(`app.ts` / `app.vue` / `App.vue` / `server.ts` /
|
|
412
|
+
* `server.dev.ts` …)。它们不在任何被监听的目录里,却同样决定服务端行为
|
|
413
|
+
* (appRoot、defineApp、defineServer),因此必须纳入判据。
|
|
414
|
+
*
|
|
415
|
+
* 用静态规则而不是「等第一次扫描拿到 appEntry 再登记」:后者在入口文件本身是第一个改动对象时
|
|
416
|
+
* 会漏掉 —— 首次扫描永远等不到,入口文件也就永远进不了监听集合(这正是此前「改 app.ts
|
|
417
|
+
* 完全不触发重扫」的缺陷)。
|
|
418
|
+
*/
|
|
419
|
+
const ENTRY_FILE_PATTERN = /^(app|App)(\.(server|client))?\.(ts|js|mjs|mts|vue)$|^server(\.(dev|prod))?\.(ts|js|mjs|mts)$/;
|
|
420
|
+
function createDevScanCoordinator(options) {
|
|
421
|
+
const { rootDir, source, debounceMs = 150 } = options;
|
|
422
|
+
const srcDir = options.srcDir.startsWith("/") ? options.srcDir : `${rootDir}/${options.srcDir}`;
|
|
423
|
+
const srcDirNormalized = srcDir.replace(/\/+$/, "");
|
|
424
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
425
|
+
const pending = /* @__PURE__ */ new Set();
|
|
426
|
+
let debounceTimer = null;
|
|
427
|
+
let running = null;
|
|
428
|
+
let rerunRequested = false;
|
|
429
|
+
let started = false;
|
|
430
|
+
const warn = options.onError ?? ((error) => console.error("[ubean] scan failed:", error));
|
|
431
|
+
/** 扫描目录(绝对)与「额外监听的文件」(入口文件,由扫描结果给出)。 */
|
|
432
|
+
const scanDirPaths = DEV_SCAN_DIRS.map((dir) => `${srcDirNormalized}/${dir}`);
|
|
433
|
+
const extraWatchFiles = /* @__PURE__ */ new Set();
|
|
434
|
+
function isIgnored(file) {
|
|
435
|
+
if (file.startsWith(".ubean/") || file.includes("/.ubean/")) return true;
|
|
436
|
+
if (file.includes("/node_modules/") || file.includes("/.git/")) return true;
|
|
437
|
+
if (file.endsWith(".bak")) return true;
|
|
438
|
+
return (options.ignore ?? []).some((pattern) => file.includes(pattern.replace(/\*/g, "")));
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* 事件是否与扫描相关。
|
|
442
|
+
*
|
|
443
|
+
* 判据是**绝对路径**(Vite 的 watcher 给的就是绝对路径):早先 CLI 那套监听自己把
|
|
444
|
+
* `srcDir` 与事件名拼成路径,`path.join` 遇到绝对路径不重置导致拼出 `<cwd><cwd>/src/...`,
|
|
445
|
+
* 所有监听目标 ENOENT 且异常被吞 —— 表现为「服务端改动完全不生效」。这里不做任何路径拼接。
|
|
446
|
+
*/
|
|
447
|
+
function isScanRelevant(file) {
|
|
448
|
+
if (!file.endsWith(".vue") && !SCAN_FILE_EXTENSIONS.test(file)) return false;
|
|
449
|
+
if (extraWatchFiles.has(file)) return true;
|
|
450
|
+
if (!file.startsWith(`${srcDirNormalized}/`)) return false;
|
|
451
|
+
const rest = file.slice(srcDirNormalized.length + 1);
|
|
452
|
+
if (DEV_SCAN_DIRS.includes(rest.split("/")[0])) return true;
|
|
453
|
+
return !rest.includes("/") && ENTRY_FILE_PATTERN.test(rest);
|
|
454
|
+
}
|
|
455
|
+
function registerExtraFiles(result) {
|
|
456
|
+
const entries = [
|
|
457
|
+
result.appEntry?.shared,
|
|
458
|
+
result.appEntry?.server,
|
|
459
|
+
result.appEntry?.client,
|
|
460
|
+
result.appEntry?.root,
|
|
461
|
+
result.serverEntry?.shared,
|
|
462
|
+
result.serverEntry?.dev,
|
|
463
|
+
result.serverEntry?.prod
|
|
464
|
+
];
|
|
465
|
+
for (const entry of entries) if (entry?.exists && entry.fullPath && !extraWatchFiles.has(entry.fullPath)) {
|
|
466
|
+
extraWatchFiles.add(entry.fullPath);
|
|
467
|
+
source.add(entry.fullPath);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
async function runScan(changed) {
|
|
471
|
+
const result = await (options.scan ?? (() => scanProject({
|
|
472
|
+
cwd: rootDir,
|
|
473
|
+
srcDir,
|
|
474
|
+
dirs: options.dirs,
|
|
475
|
+
ignore: options.ignore
|
|
476
|
+
})))();
|
|
477
|
+
registerExtraFiles(result);
|
|
478
|
+
for (const subscriber of subscribers) await subscriber(result, changed);
|
|
479
|
+
options.reload?.();
|
|
480
|
+
return result;
|
|
481
|
+
}
|
|
482
|
+
async function rescan(changed = []) {
|
|
483
|
+
if (running) {
|
|
484
|
+
rerunRequested = true;
|
|
485
|
+
return running;
|
|
486
|
+
}
|
|
487
|
+
running = runScan(changed).catch((error) => {
|
|
488
|
+
warn(error);
|
|
489
|
+
throw error;
|
|
490
|
+
});
|
|
491
|
+
try {
|
|
492
|
+
return await running;
|
|
493
|
+
} finally {
|
|
494
|
+
running = null;
|
|
495
|
+
if (rerunRequested) {
|
|
496
|
+
rerunRequested = false;
|
|
497
|
+
rescan([...pending]).catch(() => {});
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
function queue(file) {
|
|
502
|
+
pending.add(file);
|
|
503
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
504
|
+
debounceTimer = setTimeout(() => {
|
|
505
|
+
debounceTimer = null;
|
|
506
|
+
const changed = [...pending];
|
|
507
|
+
pending.clear();
|
|
508
|
+
rescan(changed).catch(() => {});
|
|
509
|
+
}, debounceMs);
|
|
510
|
+
}
|
|
511
|
+
const onFileEvent = (file) => {
|
|
512
|
+
if (isIgnored(file) || !isScanRelevant(file)) return;
|
|
513
|
+
queue(file);
|
|
514
|
+
};
|
|
515
|
+
return {
|
|
516
|
+
subscribe(fn) {
|
|
517
|
+
subscribers.add(fn);
|
|
518
|
+
return () => subscribers.delete(fn);
|
|
519
|
+
},
|
|
520
|
+
start() {
|
|
521
|
+
if (started) return;
|
|
522
|
+
started = true;
|
|
523
|
+
for (const dir of scanDirPaths) source.add(dir);
|
|
524
|
+
source.on("add", onFileEvent);
|
|
525
|
+
source.on("unlink", onFileEvent);
|
|
526
|
+
source.on("change", onFileEvent);
|
|
527
|
+
},
|
|
528
|
+
rescan,
|
|
529
|
+
subscriberCount: () => subscribers.size,
|
|
530
|
+
stop() {
|
|
531
|
+
if (!started) return;
|
|
532
|
+
started = false;
|
|
533
|
+
source.off?.("add", onFileEvent);
|
|
534
|
+
source.off?.("unlink", onFileEvent);
|
|
535
|
+
source.off?.("change", onFileEvent);
|
|
536
|
+
if (debounceTimer) {
|
|
537
|
+
clearTimeout(debounceTimer);
|
|
538
|
+
debounceTimer = null;
|
|
539
|
+
}
|
|
540
|
+
pending.clear();
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
const SCAN_STATE_KEY = "__ubeanScanState";
|
|
545
|
+
function scanStateOf(server) {
|
|
546
|
+
const holder = server;
|
|
547
|
+
holder[SCAN_STATE_KEY] ??= {};
|
|
548
|
+
return holder[SCAN_STATE_KEY];
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* 取(或建立)该 dev server 的协调器。
|
|
552
|
+
*
|
|
553
|
+
* core 与 vue 插件都调用它,因此**先到的那个**负责建实例;后到的拿同一个。
|
|
554
|
+
* `init` 只在首次调用时执行。
|
|
555
|
+
*/
|
|
556
|
+
function getDevScanCoordinator(server, init) {
|
|
557
|
+
const state = scanStateOf(server);
|
|
558
|
+
if (state.coordinator) return state.coordinator;
|
|
559
|
+
const coordinator = createDevScanCoordinator(init(server.watcher));
|
|
560
|
+
state.coordinator = coordinator;
|
|
561
|
+
if (state.pending) {
|
|
562
|
+
for (const fn of state.pending) coordinator.subscribe(fn);
|
|
563
|
+
state.pending = void 0;
|
|
564
|
+
}
|
|
565
|
+
coordinator.start();
|
|
566
|
+
return coordinator;
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* 取该 dev server **已存在**的协调器(不存在返回 undefined)。
|
|
570
|
+
*
|
|
571
|
+
* 给「只想触发一次扫描」的调用方用(CLI 的 `rescan()`):`getDevScanCoordinator` 需要 init
|
|
572
|
+
* 参数、会在缺失时建实例,不适合这里。
|
|
573
|
+
*/
|
|
574
|
+
function peekDevScanCoordinator(server) {
|
|
575
|
+
return scanStateOf(server).coordinator;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* 订阅 dev 扫描(CLI 用:每次扫描后重建 Hono app 并让浏览器重载)。
|
|
579
|
+
*
|
|
580
|
+
* 允许在协调器建立之前订阅:`getDevScanCoordinator` 会把已登记的订阅转交给新实例。
|
|
581
|
+
*/
|
|
582
|
+
function onDevScan(server, handler) {
|
|
583
|
+
const state = scanStateOf(server);
|
|
584
|
+
if (state.coordinator) return state.coordinator.subscribe(handler);
|
|
585
|
+
state.pending ??= /* @__PURE__ */ new Set();
|
|
586
|
+
state.pending.add(handler);
|
|
587
|
+
return () => state.pending?.delete(handler);
|
|
588
|
+
}
|
|
589
|
+
//#endregion
|
|
590
590
|
//#region src/dev/node-web.ts
|
|
591
591
|
/**
|
|
592
592
|
* `node:http` 请求 → Web `Request`。
|
|
@@ -862,6 +862,19 @@ const DEV_APP_KEY = "__ubeanDevApp";
|
|
|
862
862
|
function getDevApp(server) {
|
|
863
863
|
return server[DEV_APP_KEY];
|
|
864
864
|
}
|
|
865
|
+
/**
|
|
866
|
+
* 按需触发自举(而不是等第一个请求),返回当前 app。
|
|
867
|
+
*
|
|
868
|
+
* 用途:裸 `vite dev` 下没有 CLI 去生成 OpenAPI 类型,而该类型来自 app 的 `/_openapi.json` ——
|
|
869
|
+
* 消费方(`@ubean/build` 的 dev openapi 步骤)需要能主动把 app 立起来,不能等页面请求。
|
|
870
|
+
* 与 `getDevApp` 一样挂在 server 对象上(插件与 CLI 可能持有不同的模块实例)。
|
|
871
|
+
*/
|
|
872
|
+
function ensureDevApp(server) {
|
|
873
|
+
const ensure = server[DEV_ENSURE_KEY];
|
|
874
|
+
return ensure?.();
|
|
875
|
+
}
|
|
876
|
+
/** `ensureDevApp` 在 server 对象上的键(由 `createLazyBootstrapHandler` 登记)。 */
|
|
877
|
+
const DEV_ENSURE_KEY = "__ubeanEnsureDevApp";
|
|
865
878
|
function createLazyBootstrapHandler(server, options = {}) {
|
|
866
879
|
let bootstrapPromise = null;
|
|
867
880
|
const start = async () => {
|
|
@@ -878,7 +891,7 @@ function createLazyBootstrapHandler(server, options = {}) {
|
|
|
878
891
|
onDevScan(server, async (result) => {
|
|
879
892
|
await (await bootstrapPromise?.catch(() => null))?.rebuild(result);
|
|
880
893
|
});
|
|
881
|
-
|
|
894
|
+
const ensureBootstrap = async () => {
|
|
882
895
|
bootstrapPromise ??= start().then((bootstrap) => {
|
|
883
896
|
server[DEV_APP_KEY] = bootstrap.app;
|
|
884
897
|
return bootstrap;
|
|
@@ -886,7 +899,11 @@ function createLazyBootstrapHandler(server, options = {}) {
|
|
|
886
899
|
const bootstrap = await bootstrapPromise;
|
|
887
900
|
await bootstrap.ready();
|
|
888
901
|
server[DEV_APP_KEY] = bootstrap.app;
|
|
889
|
-
return bootstrap
|
|
902
|
+
return bootstrap;
|
|
903
|
+
};
|
|
904
|
+
server[DEV_ENSURE_KEY] = async () => (await ensureBootstrap()).app;
|
|
905
|
+
return async (request) => {
|
|
906
|
+
return (await ensureBootstrap()).app.fetch(request);
|
|
890
907
|
};
|
|
891
908
|
}
|
|
892
909
|
/** 500 错误页。dev 才看得到,直接给可读的原文与栈。 */
|
|
@@ -1666,11 +1683,13 @@ function ubeanVite(options) {
|
|
|
1666
1683
|
dirs: componentsDirs,
|
|
1667
1684
|
extensions,
|
|
1668
1685
|
include: includePatterns,
|
|
1686
|
+
globsExclude: [...toArray(userComponents.globsExclude), ...COMPONENT_HALF_GLOBS],
|
|
1669
1687
|
dts: userComponents.dts === void 0 ? join(dtsDir, "components.d.ts") : userComponents.dts,
|
|
1670
1688
|
resolvers: [...dynamicResolvers, ...toArray(userComponents.resolvers)]
|
|
1671
1689
|
}));
|
|
1672
1690
|
} else plugins.push(Components({
|
|
1673
1691
|
dts: true,
|
|
1692
|
+
globsExclude: COMPONENT_HALF_GLOBS,
|
|
1674
1693
|
resolvers: dynamicResolvers
|
|
1675
1694
|
}));
|
|
1676
1695
|
const searchConfig = ubeanConfig.search;
|
|
@@ -1726,4 +1745,4 @@ async function runPagefindIndexing(ubeanConfig, searchConfig) {
|
|
|
1726
1745
|
});
|
|
1727
1746
|
}
|
|
1728
1747
|
//#endregion
|
|
1729
|
-
export {
|
|
1748
|
+
export { toVueRouterPagePath as A, bootstrapDevApp as C, resolveLocaleVueParam as D, resolveDevSecurityHeaders as E, enhanceDevApp as O, peekDevScanCoordinator as S, createDevAppReady as T, toWebRequest as _, createVueAppEntryVirtualModule as a, getDevScanCoordinator as b, collectDevCssLinks as c, getDevApp as d, injectStylesheetLinks as f, sendWebResponse as g, ubeanDevRequestPlugin as h, createServerEntryVirtualModule as i, buildDevSsrRoutes as k, createUbeanRequestHandlers as l, isViteResourceRequest as m, ubeanVite as n, createVuePagesVirtualModule as o, isFrameworkHtmlPage as p, createClientEntryVirtualModule as r, DEVTOOLS_PASS_THROUGH_PREFIXES as s, VUE_PLUGIN_INCLUDE as t, ensureDevApp as u, DEV_SCAN_DIRS as v, createDevApp as w, onDevScan as x, createDevScanCoordinator as y };
|
package/dist/vue.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as createVueAppEntryVirtualModule, i as createServerEntryVirtualModule, n as ubeanVite, o as createVuePagesVirtualModule, r as createClientEntryVirtualModule, t as VUE_PLUGIN_INCLUDE } from "./vue-
|
|
1
|
+
import { a as createVueAppEntryVirtualModule, i as createServerEntryVirtualModule, n as ubeanVite, o as createVuePagesVirtualModule, r as createClientEntryVirtualModule, t as VUE_PLUGIN_INCLUDE } from "./vue-CKAJjBBR.js";
|
|
2
2
|
export { VUE_PLUGIN_INCLUDE, createClientEntryVirtualModule, createServerEntryVirtualModule, createVueAppEntryVirtualModule, createVuePagesVirtualModule, ubeanVite as default, ubeanVite };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ubean/build",
|
|
3
|
-
"version": "0.6.0-beta.
|
|
3
|
+
"version": "0.6.0-beta.2",
|
|
4
4
|
"description": "Vite plugins, production build, SSG prerender, type codegen, and Server Actions plugin for ubean",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -45,20 +45,20 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@intlify/unplugin-vue-i18n": "11.2.5",
|
|
48
|
-
"@ubean/app": "0.6.0-beta.
|
|
49
|
-
"@ubean/client": "0.6.0-beta.
|
|
50
|
-
"@ubean/config": "0.6.0-beta.
|
|
51
|
-
"@ubean/i18n": "0.6.0-beta.
|
|
52
|
-
"@ubean/islands": "0.6.0-beta.
|
|
53
|
-
"@ubean/markdown": "0.6.0-beta.
|
|
54
|
-
"@ubean/pages": "0.6.0-beta.
|
|
55
|
-
"@ubean/preset": "0.6.0-beta.
|
|
56
|
-
"@ubean/routes": "0.6.0-beta.
|
|
57
|
-
"@ubean/scan": "0.6.0-beta.
|
|
58
|
-
"@ubean/server": "0.6.0-beta.
|
|
59
|
-
"@ubean/shared": "0.6.0-beta.
|
|
60
|
-
"@ubean/vue": "0.6.0-beta.
|
|
61
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
48
|
+
"@ubean/app": "0.6.0-beta.2",
|
|
49
|
+
"@ubean/client": "0.6.0-beta.2",
|
|
50
|
+
"@ubean/config": "0.6.0-beta.2",
|
|
51
|
+
"@ubean/i18n": "0.6.0-beta.2",
|
|
52
|
+
"@ubean/islands": "0.6.0-beta.2",
|
|
53
|
+
"@ubean/markdown": "0.6.0-beta.2",
|
|
54
|
+
"@ubean/pages": "0.6.0-beta.2",
|
|
55
|
+
"@ubean/preset": "0.6.0-beta.2",
|
|
56
|
+
"@ubean/routes": "0.6.0-beta.2",
|
|
57
|
+
"@ubean/scan": "0.6.0-beta.2",
|
|
58
|
+
"@ubean/server": "0.6.0-beta.2",
|
|
59
|
+
"@ubean/shared": "0.6.0-beta.2",
|
|
60
|
+
"@ubean/vue": "0.6.0-beta.2",
|
|
61
|
+
"@vitejs/plugin-vue": "^6.0.9",
|
|
62
62
|
"openapi-typescript": "^7.13.0",
|
|
63
63
|
"pathe": "^2.0.3",
|
|
64
64
|
"tinyglobby": "^0.2.17",
|
|
@@ -66,17 +66,17 @@
|
|
|
66
66
|
"unplugin-auto-import": "21.1.0",
|
|
67
67
|
"unplugin-vue-components": "^32.1.0",
|
|
68
68
|
"unplugin-vue-markdown": "^32.1.1",
|
|
69
|
-
"vue-i18n": "11.4.
|
|
69
|
+
"vue-i18n": "11.4.12"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
|
-
"@types/node": "^26.
|
|
73
|
-
"hono": "4.13.
|
|
72
|
+
"@types/node": "^26.6.1",
|
|
73
|
+
"hono": "4.13.8",
|
|
74
74
|
"typescript": "npm:typescript-native-bridge@latest",
|
|
75
|
-
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.
|
|
76
|
-
"vite-plus": "0.3.
|
|
75
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.3",
|
|
76
|
+
"vite-plus": "0.3.3"
|
|
77
77
|
},
|
|
78
78
|
"peerDependencies": {
|
|
79
|
-
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.
|
|
79
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.3"
|
|
80
80
|
},
|
|
81
81
|
"peerDependenciesMeta": {
|
|
82
82
|
"vite": {
|