@ubean/build 0.6.3 → 0.6.5
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 +21 -1
- package/dist/codegen/index.js +1 -1
- package/dist/{codegen-DNUCCW3C.js → codegen-BL0s5Pyq.js} +101 -2
- package/dist/env/plugin.d.ts +89 -0
- package/dist/env/plugin.js +80 -0
- package/dist/prerender.js +1 -1
- package/dist/production.d.ts +5 -0
- package/dist/production.js +1 -1
- package/dist/types-JZjwXWJd.js +160 -0
- package/dist/{vite-D9LgxIQD.js → vite-CmtXmVIm.js} +22 -8
- package/dist/vite.js +2 -2
- package/dist/{vue-CAQVpHaN.js → vue-15_4jzH7.js} +39 -2
- package/dist/vue.js +1 -1
- package/package.json +19 -15
package/dist/codegen/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AutoImportOptions, ComponentsOptions } from "@ubean/config";
|
|
1
|
+
import { AutoImportOptions, ComponentsOptions, ResolvedEnvConfig } from "@ubean/config";
|
|
2
2
|
import { ScanResult } from "@ubean/scan";
|
|
3
3
|
import { Import, InlinePreset } from "unimport";
|
|
4
4
|
import { Options } from "unplugin-vue-components/types";
|
|
@@ -208,6 +208,14 @@ export interface CodegenFileContract {
|
|
|
208
208
|
types: string[];
|
|
209
209
|
}
|
|
210
210
|
export declare const CODEGEN_FILES: readonly CodegenFileContract[];
|
|
211
|
+
/**
|
|
212
|
+
* `generateTypes()` 会写、但**不属于**外部冻结契约的产物。
|
|
213
|
+
*
|
|
214
|
+
* 存在的意义只为一个可执行断言服务:`test/codegen-doc-contract.test.ts` 断言「文档里列出的
|
|
215
|
+
* `*.d.ts` 必须在代码里真的产出」,而 `env.d.ts` 故意不进 `CODEGEN_FILES`(理由见那里),
|
|
216
|
+
* 于是它有产物、没契约条目。把名字登记在这里,比在测试里硬编码字符串更难漂移。
|
|
217
|
+
*/
|
|
218
|
+
export declare const CODEGEN_NON_CONTRACT_FILES: readonly string[];
|
|
211
219
|
export interface CodegenManifest {
|
|
212
220
|
contractVersion: typeof CODEGEN_CONTRACT_VERSION;
|
|
213
221
|
generatedAt: string;
|
|
@@ -223,6 +231,10 @@ export interface CodegenOptions extends Omit<GenerateAutoImportsOptions, 'cwd' |
|
|
|
223
231
|
cwd: string;
|
|
224
232
|
srcDir: string;
|
|
225
233
|
buildDir: string;
|
|
234
|
+
/** `env.schema` 的归一化配置(`ResolvedConfig.env`)。 */
|
|
235
|
+
env?: ResolvedEnvConfig;
|
|
236
|
+
/** Vite 解析后的 `import.meta.env` 快照,用于生成 `ImportMetaEnv`。 */
|
|
237
|
+
importMetaEnv?: Record<string, unknown>;
|
|
226
238
|
}
|
|
227
239
|
export interface CodegenResult {
|
|
228
240
|
routeTypesPath: string;
|
|
@@ -231,6 +243,14 @@ export interface CodegenResult {
|
|
|
231
243
|
autoImportsDtsPath?: string;
|
|
232
244
|
componentsDtsPath?: string;
|
|
233
245
|
virtualComponentsDtsPath?: string;
|
|
246
|
+
/**
|
|
247
|
+
* `.ubean/env.d.ts`(`ubean:env/*` 虚拟模块类型 + 生成的 `ImportMetaEnv`)。
|
|
248
|
+
*
|
|
249
|
+
* **刻意不进 `CODEGEN_FILES`**:那份清单是 studio / IDE 插件按名字读产物的**冻结契约**,
|
|
250
|
+
* 加一个文件就要把 `contractVersion` 推到 2 并同步 `docs/contracts/codegen-v1.md`,
|
|
251
|
+
* 而这份产物只给 TypeScript 编译器读,不在外部工具消费面上。
|
|
252
|
+
*/
|
|
253
|
+
envTypesPath: string;
|
|
234
254
|
generated: string[];
|
|
235
255
|
}
|
|
236
256
|
export declare function generateTypes(result: ScanResult, options: CodegenOptions): Promise<CodegenResult>;
|
package/dist/codegen/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { d as BUILTIN_PRESETS, t as CODEGEN_CONTRACT_VERSION, n as CODEGEN_FILES, r as CODEGEN_NON_CONTRACT_FILES, p as HONO_OPENAPI_PRESET, m as UBEAN_CLIENT_PRESET, h as UBEAN_SERVER_PRESET, g as VUE_I18N_PRESET, _ as VUE_MACROS_PRESET, v as VUE_PRESET, y as VUE_ROUTER_PRESET, b as generateAutoImports, l as generateI18nTypes, x as generateImportsTransform, a as generateOpenApiTypes, o as generateOpenApiTypesFromServer, s as generatePageTypes, c as generateRouteTypes, i as generateTypes, S as getAutoImportPresets, C as getBuiltinComposables, w as getUbeanAutoImportConfig, T as getUbeanComponentsConfig, u as messagesToTsType, E as resolveAutoImportsConfig, D as resolveComponentsConfig, O as toArray } from "../codegen-BL0s5Pyq.js";
|
|
@@ -1,8 +1,89 @@
|
|
|
1
|
+
import { r as resolveEnvModules } from "./types-JZjwXWJd.js";
|
|
1
2
|
import { isAbsolute, join, normalize, relative } from "pathe";
|
|
2
3
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
4
|
import { glob } from "tinyglobby";
|
|
4
5
|
import { createUnimport, toTypeDeclarationFile } from "unimport";
|
|
5
6
|
import openapiTS, { astToString } from "openapi-typescript";
|
|
7
|
+
//#region src/env/codegen.ts
|
|
8
|
+
/**
|
|
9
|
+
* `.ubean/env.d.ts` 生成器。
|
|
10
|
+
*
|
|
11
|
+
* 两个职责:
|
|
12
|
+
* 1. 给 `ubean:env/client` / `ubean:env/server` 两个虚拟模块补类型(否则用户 import 时 TS2307)
|
|
13
|
+
* 2. 从 Vite 解析好的 `import.meta.env` 自动生成 `ImportMetaEnv`,替掉用户手写的那份
|
|
14
|
+
*
|
|
15
|
+
* 第 2 点顺手修掉一类漂移:手写 `ImportMetaEnv` 会漏 key。本仓库的实例是
|
|
16
|
+
* `src/env.ts` 读了 `import.meta.env.VITE_API_PROXY_KEYS`,而 `src/typings/env.d.ts`
|
|
17
|
+
* 里没有对应声明 —— 手写清单没有东西能提醒它过期。
|
|
18
|
+
*/
|
|
19
|
+
const VITE_BUILTIN_ENV_KEYS = /* @__PURE__ */ new Set([
|
|
20
|
+
"MODE",
|
|
21
|
+
"DEV",
|
|
22
|
+
"PROD",
|
|
23
|
+
"BASE_URL",
|
|
24
|
+
"SSR"
|
|
25
|
+
]);
|
|
26
|
+
/**
|
|
27
|
+
* 产物文件名(单一来源:`generateEnvTypes()` 的默认值与 `CODEGEN_NON_CONTRACT_FILES`
|
|
28
|
+
* 都用它,免得改了一处漏另一处)。
|
|
29
|
+
*/
|
|
30
|
+
const ENV_TYPES_FILE_NAME = "env.d.ts";
|
|
31
|
+
function tsTypeOfLiteral(value) {
|
|
32
|
+
if (typeof value === "number") return "number";
|
|
33
|
+
if (typeof value === "boolean") return "boolean";
|
|
34
|
+
return "string";
|
|
35
|
+
}
|
|
36
|
+
function renderModuleTypes(moduleId, fields) {
|
|
37
|
+
if (fields.length === 0) return `declare module '${moduleId}' {\n // env.schema is empty\n}\n`;
|
|
38
|
+
return `declare module '${moduleId}' {\n${fields.map((f) => {
|
|
39
|
+
return ` export const ${f.key}: ${f.type}${f.optional ? " | undefined" : ""};`;
|
|
40
|
+
}).join("\n")}\n}\n`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 从 `import.meta.env` 快照生成 `ImportMetaEnv`。
|
|
44
|
+
*
|
|
45
|
+
* 值同时用于**类型**:字符串字面量能收窄成具体字符串 —— 但只在值是字面量时。这里选择
|
|
46
|
+
* 宽化到 `string` / `number` / `boolean`,因为 `.env` 里的值一律是字符串,写成裸字面量
|
|
47
|
+
* 会让 `VITE_API_ENV` 被推成 `"local"` 这种过窄类型,赋值时报错。
|
|
48
|
+
*/
|
|
49
|
+
function renderImportMetaEnv(importMetaEnv) {
|
|
50
|
+
const keys = Object.keys(importMetaEnv).filter((key) => key.startsWith("VITE_") || VITE_BUILTIN_ENV_KEYS.has(key)).sort();
|
|
51
|
+
if (keys.length === 0) return `interface ImportMetaEnv {\n readonly [key: string]: string | boolean | undefined;\n}\n`;
|
|
52
|
+
return `interface ImportMetaEnv {\n${keys.map((key) => {
|
|
53
|
+
const value = importMetaEnv[key];
|
|
54
|
+
if (VITE_BUILTIN_ENV_KEYS.has(key)) {
|
|
55
|
+
if (key === "MODE" || key === "BASE_URL") return ` readonly ${key}: string;`;
|
|
56
|
+
return ` readonly ${key}: boolean;`;
|
|
57
|
+
}
|
|
58
|
+
return ` readonly ${key}: ${tsTypeOfLiteral(value)};`;
|
|
59
|
+
}).join("\n")}\n}\n`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 生成 `.ubean/env.d.ts`。
|
|
63
|
+
*
|
|
64
|
+
* 顶层**不能**出现 `export {}`:`ImportMetaEnv` 的声明必须留在全局作用域,一旦文件变成
|
|
65
|
+
* 模块,TypeScript 的类型增强就失效(Vite 官方文档专门警告过这一点)。
|
|
66
|
+
* `declare module '...'` 块本身不需要文件是模块。
|
|
67
|
+
*/
|
|
68
|
+
async function generateEnvTypes(options) {
|
|
69
|
+
const { outDir, fileName = ENV_TYPES_FILE_NAME, modules, importMetaEnv = {} } = options;
|
|
70
|
+
const contents = `// Auto-generated by ubean from \`env.schema\` in ubean.config.ts — do not edit manually.
|
|
71
|
+
// Provides typed access to \`ubean:env/client\` / \`ubean:env/server\` and declares ImportMetaEnv.
|
|
72
|
+
/* eslint-disable */
|
|
73
|
+
/// <reference types="vite/client" />
|
|
74
|
+
|
|
75
|
+
${renderImportMetaEnv(importMetaEnv)}
|
|
76
|
+
interface ImportMeta {
|
|
77
|
+
readonly env: ImportMetaEnv;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
${renderModuleTypes("ubean:env/client", modules.client)}${renderModuleTypes("ubean:env/server", modules.server)}`;
|
|
81
|
+
await mkdir(outDir, { recursive: true });
|
|
82
|
+
const outPath = join(outDir, fileName);
|
|
83
|
+
await writeFile(outPath, contents, "utf8");
|
|
84
|
+
return outPath;
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
6
87
|
//#region src/codegen/auto-imports.ts
|
|
7
88
|
/** 将 `boolean | AutoImportOptions` 归一化为分库开关 + 透传选项。 */
|
|
8
89
|
function resolveAutoImportsConfig(input) {
|
|
@@ -851,8 +932,16 @@ const CODEGEN_FILES = [
|
|
|
851
932
|
types: []
|
|
852
933
|
}
|
|
853
934
|
];
|
|
935
|
+
/**
|
|
936
|
+
* `generateTypes()` 会写、但**不属于**外部冻结契约的产物。
|
|
937
|
+
*
|
|
938
|
+
* 存在的意义只为一个可执行断言服务:`test/codegen-doc-contract.test.ts` 断言「文档里列出的
|
|
939
|
+
* `*.d.ts` 必须在代码里真的产出」,而 `env.d.ts` 故意不进 `CODEGEN_FILES`(理由见那里),
|
|
940
|
+
* 于是它有产物、没契约条目。把名字登记在这里,比在测试里硬编码字符串更难漂移。
|
|
941
|
+
*/
|
|
942
|
+
const CODEGEN_NON_CONTRACT_FILES = [ENV_TYPES_FILE_NAME];
|
|
854
943
|
async function generateTypes(result, options) {
|
|
855
|
-
const { cwd, srcDir, buildDir, ...autoImportOptions } = options;
|
|
944
|
+
const { cwd, srcDir, buildDir, env, importMetaEnv, ...autoImportOptions } = options;
|
|
856
945
|
const outDir = join(cwd, buildDir);
|
|
857
946
|
await mkdir(outDir, { recursive: true });
|
|
858
947
|
const generated = [];
|
|
@@ -865,6 +954,15 @@ async function generateTypes(result, options) {
|
|
|
865
954
|
generated.push(pageTypesPath);
|
|
866
955
|
const i18nTypesPath = await generateI18nTypes(result, { outDir });
|
|
867
956
|
if (i18nTypesPath) generated.push(i18nTypesPath);
|
|
957
|
+
const envTypesPath = await generateEnvTypes({
|
|
958
|
+
outDir,
|
|
959
|
+
modules: resolveEnvModules({
|
|
960
|
+
schema: env?.schema ?? {},
|
|
961
|
+
importMetaEnv
|
|
962
|
+
}),
|
|
963
|
+
importMetaEnv
|
|
964
|
+
});
|
|
965
|
+
generated.push(envTypesPath);
|
|
868
966
|
const { autoImportsDtsPath, componentsDtsPath, virtualComponentsDtsPath } = await generateAutoImports(result, {
|
|
869
967
|
cwd,
|
|
870
968
|
srcDir,
|
|
@@ -890,8 +988,9 @@ async function generateTypes(result, options) {
|
|
|
890
988
|
autoImportsDtsPath,
|
|
891
989
|
componentsDtsPath,
|
|
892
990
|
virtualComponentsDtsPath,
|
|
991
|
+
envTypesPath,
|
|
893
992
|
generated
|
|
894
993
|
};
|
|
895
994
|
}
|
|
896
995
|
//#endregion
|
|
897
|
-
export {
|
|
996
|
+
export { getBuiltinComposables as C, resolveComponentsConfig as D, resolveAutoImportsConfig as E, toArray as O, getAutoImportPresets as S, getUbeanComponentsConfig as T, VUE_MACROS_PRESET as _, generateOpenApiTypes as a, generateAutoImports as b, generateRouteTypes as c, BUILTIN_PRESETS as d, COMPONENT_HALF_GLOBS as f, VUE_I18N_PRESET as g, UBEAN_SERVER_PRESET as h, generateTypes as i, generateI18nTypes as l, UBEAN_CLIENT_PRESET as m, CODEGEN_FILES as n, generateOpenApiTypesFromServer as o, HONO_OPENAPI_PRESET as p, CODEGEN_NON_CONTRACT_FILES as r, generatePageTypes as s, CODEGEN_CONTRACT_VERSION as t, messagesToTsType as u, VUE_PRESET as v, getUbeanAutoImportConfig as w, generateImportsTransform as x, VUE_ROUTER_PRESET as y };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { ResolvedConfig } from "@ubean/config";
|
|
2
|
+
import { Plugin } from "vite";
|
|
3
|
+
//#region src/env/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* `env.schema` → 两个虚拟模块(`ubean:env/client` / `ubean:env/server`)的规范化与取值。
|
|
6
|
+
*
|
|
7
|
+
* 这一层刻意与 Vite 无耦合(纯函数 + 显式入参),因为同一个产物要服务两个环境:
|
|
8
|
+
* - **client 模块**:字段被内联成字面量,取值来自 Vite 已解析好的 `import.meta.env`(构建期)
|
|
9
|
+
* - **server 模块**:字段从 `process.env` 读(运行期),并在 `mode: 'throw'` 时于 import 期抛错
|
|
10
|
+
*
|
|
11
|
+
* 维度规则照抄 Astro `astro:env`:`client+public` / `server+public` / `server+secret` 三种合法,
|
|
12
|
+
* `client+secret` 在**规范化阶段**就抛错(配置期失败,比构建期失败早)。
|
|
13
|
+
*/
|
|
14
|
+
type EnvContext = 'client' | 'server';
|
|
15
|
+
type EnvAccess = 'public' | 'secret';
|
|
16
|
+
interface EnvFieldSpec {
|
|
17
|
+
key: string;
|
|
18
|
+
context: EnvContext;
|
|
19
|
+
access: EnvAccess;
|
|
20
|
+
/** TypeScript 字面量类型,直接写进 `.ubean/env.d.ts`。 */
|
|
21
|
+
type: string;
|
|
22
|
+
optional: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* 已解析的值:
|
|
25
|
+
* - `context: 'client'` → 构建期内联用的字面量(来自 `import.meta.env` / `.env`,已含 default 回退)
|
|
26
|
+
* - `context: 'server'` → **`default` 的回退值**(`process.env` 里没值时用)。
|
|
27
|
+
* 服务端的真实取值发生在运行期,这里只把回退值送到生成代码里。
|
|
28
|
+
* 字段没写 `default` 时保持 `undefined`,生成代码便能区分「有回退」与「无回退」。
|
|
29
|
+
*/
|
|
30
|
+
value?: unknown;
|
|
31
|
+
/** 生成代码里表达「值来自 process.env 的哪个键」——恒等于 `key`(保留字段便于日后加 alias)。 */
|
|
32
|
+
sourceKey: string;
|
|
33
|
+
}
|
|
34
|
+
interface ResolveEnvModulesInput {
|
|
35
|
+
/** 归一化 schema(`ResolvedConfig.env.schema`)。 */
|
|
36
|
+
schema: Record<string, unknown>;
|
|
37
|
+
/** Vite 解析后的 `import.meta.env`(`ResolvedConfig.env`),用于 client 字段取值。 */
|
|
38
|
+
importMetaEnv?: Record<string, unknown>;
|
|
39
|
+
/**
|
|
40
|
+
* `.env` 文件的原始内容(`loadEnv(mode, envDir, '')`)。
|
|
41
|
+
*
|
|
42
|
+
* 与 `importMetaEnv` 的分工:`import.meta.env` 只含 Vite 注入的 key(`VITE_*` + 内建),
|
|
43
|
+
* 而 `.env` 里有全量。优先 `importMetaEnv`,缺了才回落这里 —— 这样 `MODE` 这类构建期
|
|
44
|
+
* 派生值不会因为 `.env` 里也写了同名 key 被盖掉。
|
|
45
|
+
*/
|
|
46
|
+
dotEnv?: Record<string, string>;
|
|
47
|
+
/** 服务端侧来源,默认 `process.env`。测试可注入。 */
|
|
48
|
+
serverEnv?: Record<string, string | undefined>;
|
|
49
|
+
}
|
|
50
|
+
interface EnvModules {
|
|
51
|
+
client: EnvFieldSpec[];
|
|
52
|
+
server: EnvFieldSpec[];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 规范化 schema → 两个模块各自的字段清单。
|
|
56
|
+
*
|
|
57
|
+
* 值解析失败(缺必填、类型不符、枚举越界)**不在这里报错** —— 那是运行时校验(`resolveEnvSchema()`
|
|
58
|
+
* 与生成的 server 模块)的职责。这里只负责把维度与类型摊平,缺值时留 `undefined` 让下游决定。
|
|
59
|
+
*/
|
|
60
|
+
export declare function resolveEnvModules(input: ResolveEnvModulesInput): EnvModules;
|
|
61
|
+
/** 生成客户端虚拟模块源码:每个字段一个具名导出的字面量。 */
|
|
62
|
+
export declare function renderEnvClientModule(fields: EnvFieldSpec[]): string;
|
|
63
|
+
/**
|
|
64
|
+
* 生成服务端虚拟模块源码。
|
|
65
|
+
*
|
|
66
|
+
* 语义是 **static**(借 SvelteKit 的术语):模块求值时读一次 `process.env`,之后固定。
|
|
67
|
+
* 与 Astro `astro:env/server` 一致 —— 需要 per-request 动态读取时用 `getSecret()`(见下)。
|
|
68
|
+
*
|
|
69
|
+
* 校验失败时:`mode: 'throw'` 直接抛,`'warn'`(默认)打 warn 并把缺值放行。
|
|
70
|
+
* 选择在 import 期抛而不是返回 `undefined`:缺一个必填 secret 却静默跑下去,
|
|
71
|
+
* 会把「部署忘了配环境变量」拖到第一次请求才炸。
|
|
72
|
+
*/
|
|
73
|
+
export declare function renderEnvServerModule(fields: EnvFieldSpec[], mode?: 'warn' | 'throw'): string;
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/env/plugin.d.ts
|
|
76
|
+
export declare const ENV_CLIENT_ID = "ubean:env/client";
|
|
77
|
+
export declare const ENV_SERVER_ID = "ubean:env/server";
|
|
78
|
+
export interface UbeanEnvPluginOptions {
|
|
79
|
+
/** 已解析配置(`ensureUbeanConfig()` 的产物)。 */
|
|
80
|
+
config: ResolvedConfig;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 生成模块所需的信息在 `configResolved` 才能全部拿到:
|
|
84
|
+
* - `config.env`(Vite 解析后的 `import.meta.env` 快照)由 Vite 在钩子入参里给
|
|
85
|
+
* - 同一个钩子还会给出 `mode`,用于判定生产/开发(决定 warn 的措辞)
|
|
86
|
+
*/
|
|
87
|
+
export declare function ubeanEnvPlugin(options: UbeanEnvPluginOptions): Plugin;
|
|
88
|
+
//#endregion
|
|
89
|
+
export type { EnvFieldSpec, EnvModules, ResolveEnvModulesInput };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { n as renderEnvServerModule, r as resolveEnvModules, t as renderEnvClientModule } from "../types-JZjwXWJd.js";
|
|
2
|
+
import { loadEnv } from "vite";
|
|
3
|
+
//#region src/env/plugin.ts
|
|
4
|
+
/**
|
|
5
|
+
* `ubean:env/*` 虚拟模块插件(Astro `astro:env` 的 Vite 版)。
|
|
6
|
+
*
|
|
7
|
+
* 提供两个模块:
|
|
8
|
+
* - `ubean:env/client` — 客户端可见字段,构建期内联成字面量。任何环境都能 import。
|
|
9
|
+
* - `ubean:env/server` — 服务端字段,运行期读 `process.env`。**在客户端环境 import 直接让构建失败**
|
|
10
|
+
* (这是抄 SvelteKit `$env/static/private` 的护栏;比 Astro 的「类型上没有」更强一层)。
|
|
11
|
+
*
|
|
12
|
+
* 与 `ubean:core` 的关系:本插件**不**进 `VIRTUAL_MODULES` 数组(那是 core 的私有清单),
|
|
13
|
+
* 自己处理 `resolveId`/`load`。好处是两条路径(CLI 自建 builder / 裸 `vite build`)都只需
|
|
14
|
+
* 在 `ubeanPlugin()` 里加一行,core 的 resolveId 顺序不受影响 —— 只要本插件排在 core **之后**
|
|
15
|
+
* 即可(core 只会拦它自己那份清单,不会碰 `ubean:env/*`)。
|
|
16
|
+
*/
|
|
17
|
+
const ENV_CLIENT_ID = "ubean:env/client";
|
|
18
|
+
const ENV_SERVER_ID = "ubean:env/server";
|
|
19
|
+
const RESOLVED_CLIENT = "\0ubean:env/client";
|
|
20
|
+
const RESOLVED_SERVER = "\0ubean:env/server";
|
|
21
|
+
/** 客户端环境名(`createBuildEnvironments()` 里注册的那个)。 */
|
|
22
|
+
const CLIENT_ENVIRONMENT = "client";
|
|
23
|
+
/**
|
|
24
|
+
* 生成模块所需的信息在 `configResolved` 才能全部拿到:
|
|
25
|
+
* - `config.env`(Vite 解析后的 `import.meta.env` 快照)由 Vite 在钩子入参里给
|
|
26
|
+
* - 同一个钩子还会给出 `mode`,用于判定生产/开发(决定 warn 的措辞)
|
|
27
|
+
*/
|
|
28
|
+
function ubeanEnvPlugin(options) {
|
|
29
|
+
const { config } = options;
|
|
30
|
+
let importMetaEnv = {};
|
|
31
|
+
/**
|
|
32
|
+
* `.env` 文件的原始内容(`loadEnv(mode, envDir, '')`,**不过 prefix 过滤**)。
|
|
33
|
+
*
|
|
34
|
+
* 服务端字段的解析来源:`configResolved` 里把它合进 `process.env`,生成的
|
|
35
|
+
* `ubean:env/server` 模块才能读到 `.env`。不合并的话,`process.env` 里只有真实导出的
|
|
36
|
+
* 环境变量,`.env` 里写的 `SERVER_PORT` / `JWT_SECRET` 全是 undefined。
|
|
37
|
+
*/
|
|
38
|
+
let fileEnv = {};
|
|
39
|
+
let modules = null;
|
|
40
|
+
/**
|
|
41
|
+
* 幂等构建(dev 下 client / ubean 两个环境各跑一次钩子,但结果一致 —— 缓存即可)。
|
|
42
|
+
*
|
|
43
|
+
* **不缓存**会被服务端环境污染:`resolveEnvModules()` 的 server 分支会读 `process.env`
|
|
44
|
+
* 来决定 optional,而两次调用之间 `.env` 不会变,所以缓存是安全的。
|
|
45
|
+
*/
|
|
46
|
+
function ensureModules() {
|
|
47
|
+
if (modules) return modules;
|
|
48
|
+
modules = resolveEnvModules({
|
|
49
|
+
schema: config.env?.schema ?? {},
|
|
50
|
+
dotEnv: fileEnv,
|
|
51
|
+
importMetaEnv
|
|
52
|
+
});
|
|
53
|
+
return modules;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
name: "ubean:env",
|
|
57
|
+
enforce: "post",
|
|
58
|
+
configResolved(resolved) {
|
|
59
|
+
importMetaEnv = resolved.env;
|
|
60
|
+
fileEnv = loadEnv(resolved.mode, resolved.envDir ?? resolved.root, "");
|
|
61
|
+
for (const [key, value] of Object.entries(fileEnv)) if (process.env[key] === void 0) process.env[key] = value;
|
|
62
|
+
modules = null;
|
|
63
|
+
},
|
|
64
|
+
resolveId(id) {
|
|
65
|
+
if (id === "ubean:env/client") return RESOLVED_CLIENT;
|
|
66
|
+
if (id === "ubean:env/server") return RESOLVED_SERVER;
|
|
67
|
+
},
|
|
68
|
+
load(id) {
|
|
69
|
+
if (id === RESOLVED_CLIENT) return renderEnvClientModule(ensureModules().client);
|
|
70
|
+
if (id === RESOLVED_SERVER) {
|
|
71
|
+
if (this.environment?.name === CLIENT_ENVIRONMENT) throw new Error(`[ubean:env] \`${ENV_SERVER_ID}\` cannot be imported from client code.\nServer env (including \`access: 'secret'\` fields) is never sent to the browser.
|
|
72
|
+
Move the read into server-only code (\`src/routes/**\`, \`src/services/**\`, \`src/db/**\`),
|
|
73
|
+
or declare the field with \`context: 'client', access: 'public'\` if it is safe to inline.`);
|
|
74
|
+
return renderEnvServerModule(ensureModules().server, config.env?.mode ?? "warn");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
export { ENV_CLIENT_ID, ENV_SERVER_ID, renderEnvClientModule, renderEnvServerModule, resolveEnvModules, ubeanEnvPlugin };
|
package/dist/prerender.js
CHANGED
|
@@ -2,8 +2,8 @@ import { STATIC_NOT_FOUND_ROUTE } from "./static-render.js";
|
|
|
2
2
|
import { dirname, isAbsolute, join } from "pathe";
|
|
3
3
|
import { resolvePrerenderConfig } from "@ubean/config";
|
|
4
4
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
5
|
-
import { DATA_PAYLOAD_ID } from "@ubean/pages";
|
|
6
5
|
import { matchGlob } from "@ubean/shared";
|
|
6
|
+
import { DATA_PAYLOAD_ID } from "@ubean/pages";
|
|
7
7
|
//#region src/prerender.ts
|
|
8
8
|
const LINK_REGEX = /<a[^>]+href=["']([^"']+)["'][^>]*>/gi;
|
|
9
9
|
/**
|
package/dist/production.d.ts
CHANGED
|
@@ -21,7 +21,12 @@ export interface BuildOptions {
|
|
|
21
21
|
config: ResolvedConfig;
|
|
22
22
|
preset: Preset;
|
|
23
23
|
scanResult: ScanResult;
|
|
24
|
+
/**
|
|
25
|
+
* 缺省时回落到 `config.build.minify`,再回落到 `true`。
|
|
26
|
+
* CLI `--minify` 优先级最高,由 `packages/cli/src/build.ts` 传入。
|
|
27
|
+
*/
|
|
24
28
|
minify?: boolean;
|
|
29
|
+
/** 缺省时回落到 `config.build.sourcemap`,再回落到 `false`。 */
|
|
25
30
|
sourcemap?: boolean;
|
|
26
31
|
/** Inlined content collections for production SSR (`queryCollection`). */
|
|
27
32
|
contentSnapshot?: Record<string, unknown[]>;
|
package/dist/production.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { T as buildWithEnvironments, v as createIslandsSsrStubPlugin, y as generateCloudflareWorkerEntry, b as generateNodeServerEntry, x as generateStandardHandlerEntry, S as generateVirtualModulesToDisk, C as getPresetBuildConfig, w as serializePagesForEntry } from "./vite-
|
|
1
|
+
export { T as buildWithEnvironments, v as createIslandsSsrStubPlugin, y as generateCloudflareWorkerEntry, b as generateNodeServerEntry, x as generateStandardHandlerEntry, S as generateVirtualModulesToDisk, C as getPresetBuildConfig, w as serializePagesForEntry } from "./vite-CmtXmVIm.js";
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { isEnvFieldMeta } from "@ubean/shared";
|
|
2
|
+
//#region src/env/types.ts
|
|
3
|
+
/**
|
|
4
|
+
* `env.schema` → 两个虚拟模块(`ubean:env/client` / `ubean:env/server`)的规范化与取值。
|
|
5
|
+
*
|
|
6
|
+
* 这一层刻意与 Vite 无耦合(纯函数 + 显式入参),因为同一个产物要服务两个环境:
|
|
7
|
+
* - **client 模块**:字段被内联成字面量,取值来自 Vite 已解析好的 `import.meta.env`(构建期)
|
|
8
|
+
* - **server 模块**:字段从 `process.env` 读(运行期),并在 `mode: 'throw'` 时于 import 期抛错
|
|
9
|
+
*
|
|
10
|
+
* 维度规则照抄 Astro `astro:env`:`client+public` / `server+public` / `server+secret` 三种合法,
|
|
11
|
+
* `client+secret` 在**规范化阶段**就抛错(配置期失败,比构建期失败早)。
|
|
12
|
+
*/
|
|
13
|
+
function typeOfField(def) {
|
|
14
|
+
if (isEnvFieldMeta(def)) {
|
|
15
|
+
if (def.type === "enum") return (def.values ?? []).map((v) => JSON.stringify(v)).join(" | ") || "string";
|
|
16
|
+
if (def.type === "number") return "number";
|
|
17
|
+
if (def.type === "boolean") return "boolean";
|
|
18
|
+
return "string";
|
|
19
|
+
}
|
|
20
|
+
const legacy = def;
|
|
21
|
+
if (legacy.type === Number) return "number";
|
|
22
|
+
if (legacy.type === Boolean) return "boolean";
|
|
23
|
+
if (legacy.type === String) return "string";
|
|
24
|
+
return "unknown";
|
|
25
|
+
}
|
|
26
|
+
function dimensionsOf(def) {
|
|
27
|
+
if (isEnvFieldMeta(def)) return {
|
|
28
|
+
context: def.context,
|
|
29
|
+
access: def.access
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
context: "server",
|
|
33
|
+
access: "secret"
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 规范化 schema → 两个模块各自的字段清单。
|
|
38
|
+
*
|
|
39
|
+
* 值解析失败(缺必填、类型不符、枚举越界)**不在这里报错** —— 那是运行时校验(`resolveEnvSchema()`
|
|
40
|
+
* 与生成的 server 模块)的职责。这里只负责把维度与类型摊平,缺值时留 `undefined` 让下游决定。
|
|
41
|
+
*/
|
|
42
|
+
function resolveEnvModules(input) {
|
|
43
|
+
const { schema, importMetaEnv = {}, dotEnv = {}, serverEnv = process.env } = input;
|
|
44
|
+
const client = [];
|
|
45
|
+
const server = [];
|
|
46
|
+
for (const [key, def] of Object.entries(schema)) {
|
|
47
|
+
const { context, access } = dimensionsOf(def);
|
|
48
|
+
if (context === "client" && access === "secret") throw new Error(`Invalid env field "${key}": context 'client' cannot be combined with access 'secret'. Secrets never reach the client — use \`{ context: 'server', access: 'secret' }\`.`);
|
|
49
|
+
const meta = isEnvFieldMeta(def) ? def : null;
|
|
50
|
+
const optional = meta ? meta.optional === true : false;
|
|
51
|
+
const spec = {
|
|
52
|
+
key,
|
|
53
|
+
context,
|
|
54
|
+
access,
|
|
55
|
+
type: typeOfField(def),
|
|
56
|
+
optional,
|
|
57
|
+
sourceKey: key
|
|
58
|
+
};
|
|
59
|
+
if (context === "client") {
|
|
60
|
+
const raw = importMetaEnv[key] ?? dotEnv[key];
|
|
61
|
+
const resolved = raw === void 0 || raw === "" ? meta?.default : raw;
|
|
62
|
+
if (resolved !== void 0) spec.value = resolved;
|
|
63
|
+
client.push(spec);
|
|
64
|
+
} else {
|
|
65
|
+
const hasDefault = meta ? meta.default !== void 0 : def.default !== void 0;
|
|
66
|
+
const rawServer = serverEnv[key];
|
|
67
|
+
spec.optional = optional || !hasDefault && (rawServer === void 0 || rawServer === "");
|
|
68
|
+
if (hasDefault) spec.value = meta ? meta.default : def.default;
|
|
69
|
+
server.push(spec);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
client,
|
|
74
|
+
server
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** 生成客户端虚拟模块源码:每个字段一个具名导出的字面量。 */
|
|
78
|
+
function renderEnvClientModule(fields) {
|
|
79
|
+
const lines = ["// Generated by ubean from `env.schema` in ubean.config.ts — do not edit.", "// Values are inlined at build time from Vite-resolved `import.meta.env`."];
|
|
80
|
+
for (const field of fields) {
|
|
81
|
+
if (field.value === void 0) {
|
|
82
|
+
lines.push(`// ${field.key}: no value resolved at build time${field.optional ? " (optional)" : ""}`);
|
|
83
|
+
lines.push(`export const ${field.key} = undefined;`);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
lines.push(`export const ${field.key} = ${JSON.stringify(field.value)};`);
|
|
87
|
+
}
|
|
88
|
+
return `${lines.join("\n")}\n`;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 生成服务端虚拟模块源码。
|
|
92
|
+
*
|
|
93
|
+
* 语义是 **static**(借 SvelteKit 的术语):模块求值时读一次 `process.env`,之后固定。
|
|
94
|
+
* 与 Astro `astro:env/server` 一致 —— 需要 per-request 动态读取时用 `getSecret()`(见下)。
|
|
95
|
+
*
|
|
96
|
+
* 校验失败时:`mode: 'throw'` 直接抛,`'warn'`(默认)打 warn 并把缺值放行。
|
|
97
|
+
* 选择在 import 期抛而不是返回 `undefined`:缺一个必填 secret 却静默跑下去,
|
|
98
|
+
* 会把「部署忘了配环境变量」拖到第一次请求才炸。
|
|
99
|
+
*/
|
|
100
|
+
function renderEnvServerModule(fields, mode = "warn") {
|
|
101
|
+
const specs = fields.map((f) => ({
|
|
102
|
+
key: f.key,
|
|
103
|
+
type: f.type === "number" ? "number" : f.type === "boolean" ? "boolean" : "string",
|
|
104
|
+
optional: f.optional,
|
|
105
|
+
default: f.value
|
|
106
|
+
}));
|
|
107
|
+
return `// Generated by ubean from \`env.schema\` in ubean.config.ts — do not edit.
|
|
108
|
+
// Reads \`process.env\` once, at module evaluation time (static semantics).
|
|
109
|
+
|
|
110
|
+
const __specs = ${JSON.stringify(specs)};
|
|
111
|
+
const __source = typeof process !== 'undefined' && process.env ? process.env : {};
|
|
112
|
+
const __errors = [];
|
|
113
|
+
const __values = {};
|
|
114
|
+
|
|
115
|
+
for (const spec of __specs) {
|
|
116
|
+
const raw = __source[spec.key];
|
|
117
|
+
if (raw === undefined || raw === '') {
|
|
118
|
+
if (spec.default !== undefined) {
|
|
119
|
+
__values[spec.key] = spec.default;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (!spec.optional) __errors.push(\` - \${spec.key}: Missing required env\`);
|
|
123
|
+
__values[spec.key] = undefined;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (spec.type === 'number') {
|
|
127
|
+
const n = Number(raw);
|
|
128
|
+
if (Number.isNaN(n)) {
|
|
129
|
+
__errors.push(\` - \${spec.key}: must be a number, got \${raw}\`);
|
|
130
|
+
__values[spec.key] = undefined;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
__values[spec.key] = n;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (spec.type === 'boolean') {
|
|
137
|
+
__values[spec.key] = raw === 'true' || raw === '1';
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
__values[spec.key] = raw;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (__errors.length > 0) {
|
|
144
|
+
const message = 'Environment validation failed:\\n' + __errors.join('\\n');
|
|
145
|
+
if (${JSON.stringify(mode)} === 'throw') throw new Error(message);
|
|
146
|
+
// eslint-disable-next-line no-console
|
|
147
|
+
console.warn('[ubean:env/server] ' + message);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const __env = __values;
|
|
151
|
+
${fields.map((f) => `export const ${f.key} = __values[${JSON.stringify(f.key)}];`).join("\n")}
|
|
152
|
+
|
|
153
|
+
/** 读取未在 \`env.schema\` 里声明的变量(动态 / per-request 场景)。 */
|
|
154
|
+
export function getSecret(key) {
|
|
155
|
+
return __source[key];
|
|
156
|
+
}
|
|
157
|
+
`;
|
|
158
|
+
}
|
|
159
|
+
//#endregion
|
|
160
|
+
export { renderEnvServerModule as n, resolveEnvModules as r, renderEnvClientModule as t };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { a as ssrSingletonProdOptimizeExclude, m as createVirtualRegistry, o as ssrSingletonProdSsr, y as __exportAll } from "./ssr-singleton-Bbds29jS.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-BK0L1Op-.js";
|
|
3
|
-
import {
|
|
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-
|
|
3
|
+
import { a as generateOpenApiTypes, i as generateTypes } from "./codegen-BL0s5Pyq.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-15_4jzH7.js";
|
|
5
5
|
import { createStaticSsgRenderer } from "./static-render.js";
|
|
6
6
|
import { prerender, resolvePrerenderStaticDir } from "./prerender.js";
|
|
7
|
-
import { dirname, join, relative, resolve } from "pathe";
|
|
7
|
+
import { dirname, isAbsolute, join, relative, resolve } from "pathe";
|
|
8
8
|
import * as nodeFs from "node:fs";
|
|
9
9
|
import { existsSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
10
10
|
import { checkProjectDependenciesOnce, loadUbeanConfig, resolveModules, resolvePrerenderConfig, tryGetConfig } from "@ubean/config";
|
|
@@ -1218,7 +1218,7 @@ function readClientManifest(publicDir) {
|
|
|
1218
1218
|
}
|
|
1219
1219
|
/** 由 options 派生上下文(不做任何 IO)。 */
|
|
1220
1220
|
function createBuildContext(options) {
|
|
1221
|
-
const { cwd, config, preset, scanResult, minify =
|
|
1221
|
+
const { cwd, config, preset, scanResult, minify = config.build.minify, sourcemap = config.build.sourcemap, contentSnapshot } = options;
|
|
1222
1222
|
const mode = config.mode;
|
|
1223
1223
|
return {
|
|
1224
1224
|
cwd,
|
|
@@ -1301,7 +1301,7 @@ async function runEnvBuilds(builder, prepared) {
|
|
|
1301
1301
|
await builder.build(builder.environments.ubean);
|
|
1302
1302
|
}
|
|
1303
1303
|
if (hasPages) {
|
|
1304
|
-
const publicDir = join(cwd,
|
|
1304
|
+
const publicDir = isAbsolute(prepared.config.dir.public) ? prepared.config.dir.public : join(cwd, prepared.config.dir.public);
|
|
1305
1305
|
if (existsSync(publicDir)) {
|
|
1306
1306
|
logger$2.info("Copying public assets...");
|
|
1307
1307
|
await cp(publicDir, outDirs.public, { recursive: true });
|
|
@@ -2490,8 +2490,8 @@ async function buildEnvironmentsForConfig(config) {
|
|
|
2490
2490
|
return createBuildEnvironments({
|
|
2491
2491
|
outDirs,
|
|
2492
2492
|
clientInput: existsSync(clientEntryPath) ? clientEntryPath : join(outDirs.virtual, "client-entry.mjs"),
|
|
2493
|
-
minify:
|
|
2494
|
-
sourcemap:
|
|
2493
|
+
minify: config.build.minify,
|
|
2494
|
+
sourcemap: config.build.sourcemap,
|
|
2495
2495
|
presetBuildConfig: getPresetBuildConfig(resolvePresetByName(config.build.preset)),
|
|
2496
2496
|
ssrNoExternal: ssrSingletonProdSsr().noExternal
|
|
2497
2497
|
});
|
|
@@ -2502,6 +2502,14 @@ function ubeanPlugin(options) {
|
|
|
2502
2502
|
/** 已跑过 codegen 的扫描结果(`buildStart` 会在两个环境各跑一次)。 */
|
|
2503
2503
|
let lastCodegenScan = null;
|
|
2504
2504
|
/**
|
|
2505
|
+
* 本插件实例拿到的 Vite 解析后环境变量快照(`configResolved` 钩子填充)。
|
|
2506
|
+
*
|
|
2507
|
+
* 为什么不在 `runProjectCodegen` 里自己 `loadEnv()`:Vite 已经在 `configResolved` 把
|
|
2508
|
+
* **合并完 `.env` / `.env.local` / `.env.[mode]` / 进程环境**的结果给了插件,再自己读一遍
|
|
2509
|
+
* 就多一份可能漂移的实现(优先级规则容易抄错),而且与 `import.meta.env` 的实际内容未必一致。
|
|
2510
|
+
*/
|
|
2511
|
+
let resolvedImportMetaEnv = {};
|
|
2512
|
+
/**
|
|
2505
2513
|
* dev 扫描协调器(`configureServer` 建立、`buildStart` 用)。
|
|
2506
2514
|
*
|
|
2507
2515
|
* Vite 的顺序是 `configureServer` → `buildStart`,且 dev 下每个环境各跑一次 `buildStart`
|
|
@@ -2596,6 +2604,9 @@ function ubeanPlugin(options) {
|
|
|
2596
2604
|
environments: await buildEnvironmentsForConfig(ubeanConfig)
|
|
2597
2605
|
};
|
|
2598
2606
|
},
|
|
2607
|
+
configResolved(resolved) {
|
|
2608
|
+
resolvedImportMetaEnv = resolved.env;
|
|
2609
|
+
},
|
|
2599
2610
|
async buildStart() {
|
|
2600
2611
|
if (!ubeanConfig) {
|
|
2601
2612
|
ubeanConfig = await loadUbeanConfig();
|
|
@@ -2689,6 +2700,7 @@ function ubeanPlugin(options) {
|
|
|
2689
2700
|
});
|
|
2690
2701
|
const announceMissingOpenApiTypes = async () => {
|
|
2691
2702
|
if (!ubeanConfig) return;
|
|
2703
|
+
if (ubeanConfig.mode === "ssg" || ubeanConfig.mode === "spa") return;
|
|
2692
2704
|
const filePath = join(ubeanConfig.rootDir, ".ubean", "openapi.d.ts");
|
|
2693
2705
|
try {
|
|
2694
2706
|
const app = await ensureDevApp(server);
|
|
@@ -2778,7 +2790,9 @@ function ubeanPlugin(options) {
|
|
|
2778
2790
|
buildDir: ".ubean",
|
|
2779
2791
|
dirs: ubeanConfig.dir,
|
|
2780
2792
|
autoImports: ubeanConfig.autoImports,
|
|
2781
|
-
components: ubeanConfig.components
|
|
2793
|
+
components: ubeanConfig.components,
|
|
2794
|
+
env: ubeanConfig.env,
|
|
2795
|
+
importMetaEnv: resolvedImportMetaEnv
|
|
2782
2796
|
});
|
|
2783
2797
|
} catch (err) {
|
|
2784
2798
|
logger.warn(`Failed to generate type definitions: ${err instanceof Error ? err.message : String(err)}`);
|
package/dist/vite.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { m as createVirtualRegistry } from "./ssr-singleton-Bbds29jS.js";
|
|
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-
|
|
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-
|
|
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-15_4jzH7.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-CmtXmVIm.js";
|
|
4
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,5 +1,5 @@
|
|
|
1
1
|
import { c as getComponentResolvers, h as defineVirtualModule, i as ssrSingletonDevPolicy, l as getCssImports, m as createVirtualRegistry } from "./ssr-singleton-Bbds29jS.js";
|
|
2
|
-
import { D as
|
|
2
|
+
import { D as resolveComponentsConfig, E as resolveAutoImportsConfig, O as toArray, S as getAutoImportPresets, f as COMPONENT_HALF_GLOBS } from "./codegen-BL0s5Pyq.js";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { isAbsolute, join, normalize, relative, resolve } from "pathe";
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
@@ -1009,6 +1009,22 @@ function renderErrorPage(error) {
|
|
|
1009
1009
|
function createUbeanRequestHandlers(server, options) {
|
|
1010
1010
|
const { skipHtmlTransform, onError } = options;
|
|
1011
1011
|
const handler = options.handler ?? createLazyBootstrapHandler(server, options.bootstrap);
|
|
1012
|
+
/**
|
|
1013
|
+
* `vite.config.ts` 的 `server.headers` 合并到 ubean 自己渲染的响应上。
|
|
1014
|
+
*
|
|
1015
|
+
* Vite 只把它应用到自己的中间件(`servePublicMiddleware` / `transformMiddleware`),而 ubean 的
|
|
1016
|
+
* 页面走 `respond()` 直接输出 —— 不合并的话「给所有响应加 header」在应用页面上静默失效。
|
|
1017
|
+
* 通过 `res.setHeader` 写的值优先(框架/Hono 显式设置的不该被配置文件盖掉)。
|
|
1018
|
+
*/
|
|
1019
|
+
function applyConfiguredHeaders(res) {
|
|
1020
|
+
const headers = server.config.server.headers;
|
|
1021
|
+
if (!headers) return;
|
|
1022
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
1023
|
+
if (value === void 0) continue;
|
|
1024
|
+
if (res.hasHeader(key)) continue;
|
|
1025
|
+
res.setHeader(key, value);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1012
1028
|
async function respond(req, res) {
|
|
1013
1029
|
const url = req.url || "/";
|
|
1014
1030
|
const webReq = await toWebRequest(req, "localhost", req.socket?.encrypted ? "https" : "http");
|
|
@@ -1025,9 +1041,11 @@ function createUbeanRequestHandlers(server, options) {
|
|
|
1025
1041
|
webRes.headers.forEach((value, key) => {
|
|
1026
1042
|
res.setHeader(key, value);
|
|
1027
1043
|
});
|
|
1044
|
+
applyConfiguredHeaders(res);
|
|
1028
1045
|
res.end(transformed);
|
|
1029
1046
|
return;
|
|
1030
1047
|
}
|
|
1048
|
+
applyConfiguredHeaders(res);
|
|
1031
1049
|
await sendWebResponse(res, webRes);
|
|
1032
1050
|
}
|
|
1033
1051
|
/** 统一错误处理:SSR 栈映射回源码,响应头已发出时只能断开。 */
|
|
@@ -1050,8 +1068,27 @@ function createUbeanRequestHandlers(server, options) {
|
|
|
1050
1068
|
const pathname = url.split("?")[0].split("#")[0];
|
|
1051
1069
|
return passThrough.some((prefix) => pathname.startsWith(prefix));
|
|
1052
1070
|
};
|
|
1071
|
+
/**
|
|
1072
|
+
* 命中 `vite.config.ts` 的 `server.proxy` 键?命中则让给 Vite。
|
|
1073
|
+
*
|
|
1074
|
+
* `proxyMiddleware` 在上游是 **`configureServer` 钩子之后**才 `use()` 的(本插件的 `pre` 走
|
|
1075
|
+
* 钩子体内),因此无扩展名的代理路径会被 `pre` 先吃掉 —— `isViteResourceRequest` 不认识
|
|
1076
|
+
* proxy 键,`/probe-proxy` 这种路径最终返回 ubean 的 404 而不是被代理。`/api/**` 这类路径
|
|
1077
|
+
* 明明写了 `server.proxy` 也一样,因为保留命名空间判据先命中。
|
|
1078
|
+
*
|
|
1079
|
+
* 前置让给 Vite 与 `routeRules.proxy` 不冲突:Vite 只代理配置里显式写了的键,没写就照旧归 ubean。
|
|
1080
|
+
*/
|
|
1081
|
+
const proxyKeys = Object.keys(server.config.server.proxy ?? {});
|
|
1082
|
+
const isProxyPath = (url) => {
|
|
1083
|
+
if (proxyKeys.length === 0) return false;
|
|
1084
|
+
const pathname = url.split("?")[0].split("#")[0];
|
|
1085
|
+
return proxyKeys.some((key) => {
|
|
1086
|
+
const pattern = key.startsWith("^") ? new RegExp(key) : null;
|
|
1087
|
+
return pattern ? pattern.test(pathname) : pathname.startsWith(key);
|
|
1088
|
+
});
|
|
1089
|
+
};
|
|
1053
1090
|
const pre = (req, res, next) => {
|
|
1054
|
-
if (isPassThrough(req.url || "/") || isViteResourceRequest(req.url || "/", req.headers)) {
|
|
1091
|
+
if (isPassThrough(req.url || "/") || isProxyPath(req.url || "/") || isViteResourceRequest(req.url || "/", req.headers)) {
|
|
1055
1092
|
next();
|
|
1056
1093
|
return;
|
|
1057
1094
|
}
|
package/dist/vue.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as VUE_PLUGIN_INCLUDE, r as createClientEntryVirtualModule, i as createServerEntryVirtualModule, a as createVueAppEntryVirtualModule, o as createVuePagesVirtualModule, n as default, n as ubeanVite } from "./vue-
|
|
1
|
+
export { t as VUE_PLUGIN_INCLUDE, r as createClientEntryVirtualModule, i as createServerEntryVirtualModule, a as createVueAppEntryVirtualModule, o as createVuePagesVirtualModule, n as default, n as ubeanVite } from "./vue-15_4jzH7.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ubean/build",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"description": "Vite plugins, production build, SSG prerender, type codegen, and Server Actions plugin for ubean",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -41,23 +41,27 @@
|
|
|
41
41
|
"./actions": {
|
|
42
42
|
"types": "./dist/actions-plugin.d.ts",
|
|
43
43
|
"import": "./dist/actions-plugin.js"
|
|
44
|
+
},
|
|
45
|
+
"./env": {
|
|
46
|
+
"types": "./dist/env/plugin.d.ts",
|
|
47
|
+
"import": "./dist/env/plugin.js"
|
|
44
48
|
}
|
|
45
49
|
},
|
|
46
50
|
"dependencies": {
|
|
47
51
|
"@intlify/unplugin-vue-i18n": "11.2.6",
|
|
48
|
-
"@ubean/app": "0.6.
|
|
49
|
-
"@ubean/client": "0.6.
|
|
50
|
-
"@ubean/config": "0.6.
|
|
51
|
-
"@ubean/i18n": "0.6.
|
|
52
|
-
"@ubean/islands": "0.6.
|
|
53
|
-
"@ubean/markdown": "0.6.
|
|
54
|
-
"@ubean/pages": "0.6.
|
|
55
|
-
"@ubean/preset": "0.6.
|
|
56
|
-
"@ubean/routes": "0.6.
|
|
57
|
-
"@ubean/scan": "0.6.
|
|
58
|
-
"@ubean/server": "0.6.
|
|
59
|
-
"@ubean/shared": "0.6.
|
|
60
|
-
"@ubean/vue": "0.6.
|
|
52
|
+
"@ubean/app": "0.6.5",
|
|
53
|
+
"@ubean/client": "0.6.5",
|
|
54
|
+
"@ubean/config": "0.6.5",
|
|
55
|
+
"@ubean/i18n": "0.6.5",
|
|
56
|
+
"@ubean/islands": "0.6.5",
|
|
57
|
+
"@ubean/markdown": "0.6.5",
|
|
58
|
+
"@ubean/pages": "0.6.5",
|
|
59
|
+
"@ubean/preset": "0.6.5",
|
|
60
|
+
"@ubean/routes": "0.6.5",
|
|
61
|
+
"@ubean/scan": "0.6.5",
|
|
62
|
+
"@ubean/server": "0.6.5",
|
|
63
|
+
"@ubean/shared": "0.6.5",
|
|
64
|
+
"@ubean/vue": "0.6.5",
|
|
61
65
|
"@vitejs/plugin-vue": "^6.0.10",
|
|
62
66
|
"openapi-typescript": "^7.13.0",
|
|
63
67
|
"pathe": "^2.0.3",
|
|
@@ -66,7 +70,7 @@
|
|
|
66
70
|
"unplugin-auto-import": "21.1.0",
|
|
67
71
|
"unplugin-vue-components": "^32.1.0",
|
|
68
72
|
"unplugin-vue-markdown": "^32.1.1",
|
|
69
|
-
"vue-i18n": "11.4.
|
|
73
|
+
"vue-i18n": "11.4.15"
|
|
70
74
|
},
|
|
71
75
|
"devDependencies": {
|
|
72
76
|
"@types/node": "^26.6.5",
|