@chuckcchen/vite-plugin 0.0.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.
@@ -0,0 +1,66 @@
1
+ /**
2
+ * EdgeOne Vite Plugin Adapter - 服务端打包模块
3
+ *
4
+ * 本模块负责使用 esbuild 打包服务端代码,主要功能包括:
5
+ * - 将框架的服务端构建产物打包成单个文件
6
+ * - 生成 HTTP 请求处理的包装器代码
7
+ * - 处理 Node.js 请求到 Web API Request 的转换
8
+ */
9
+ import * as esbuild from "esbuild";
10
+ import type { BuildContext, ServerBundleConfig, ServerWrapperConfig } from "./types.js";
11
+ /**
12
+ * 使用 esbuild 打包服务端代码
13
+ *
14
+ * 将服务端入口文件及其所有依赖打包成单个 JavaScript 文件,
15
+ * 以便在 EdgeOne 边缘函数环境中运行
16
+ *
17
+ * @param context - 构建上下文,包含项目根目录和日志工具
18
+ * @param config - 打包配置,包含入口点、输出路径等
19
+ * @returns esbuild 的构建结果,包含元数据信息
20
+ *
21
+ * @example
22
+ * await bundleServerCode(context, {
23
+ * entryPoints: ['server-wrapper.temp.js'],
24
+ * outfile: '.edgeone/server-handler/handler.js'
25
+ * });
26
+ */
27
+ export declare function bundleServerCode(context: BuildContext, config: ServerBundleConfig): Promise<esbuild.BuildResult>;
28
+ /**
29
+ * 创建服务端包装器文件
30
+ *
31
+ * 将框架的服务端构建产物包装成一个统一的 HTTP 请求处理器,
32
+ * 该包装器负责:
33
+ * 1. 将 Node.js 的 IncomingMessage 转换为 Web API 的 Request 对象
34
+ * 2. 调用框架的请求处理函数
35
+ * 3. 导出统一的请求处理接口
36
+ *
37
+ * @param context - 构建上下文
38
+ * @param config - 包装器配置
39
+ * @returns 生成的临时包装器文件路径
40
+ *
41
+ * @example
42
+ * const wrapperPath = await createServerWrapper(context, {
43
+ * serverEntryPath: 'build/server/index.js'
44
+ * });
45
+ */
46
+ export declare function createServerWrapper(context: BuildContext, config: ServerWrapperConfig): Promise<string>;
47
+ /**
48
+ * 清理临时包装器文件
49
+ * 在打包完成后删除临时生成的包装器文件
50
+ *
51
+ * @param wrapperPath - 包装器文件路径
52
+ */
53
+ export declare function cleanupWrapper(wrapperPath: string): Promise<void>;
54
+ /**
55
+ * 获取打包结果的文件大小
56
+ *
57
+ * @param result - esbuild 的构建结果
58
+ * @returns 打包后的文件大小(字节),如果无法获取则返回 0
59
+ *
60
+ * @example
61
+ * const result = await bundleServerCode(context, config);
62
+ * const size = getBundleSize(result);
63
+ * console.log(`打包大小: ${formatSize(size)}`);
64
+ */
65
+ export declare function getBundleSize(result: esbuild.BuildResult): number;
66
+ //# sourceMappingURL=bundler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../src/bundler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAaxF;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CA+B9B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,MAAM,CAAC,CA6BjB;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMvE;AAkGD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,GAAG,MAAM,CAWjE"}
@@ -0,0 +1,229 @@
1
+ /**
2
+ * EdgeOne Vite Plugin Adapter - 服务端打包模块
3
+ *
4
+ * 本模块负责使用 esbuild 打包服务端代码,主要功能包括:
5
+ * - 将框架的服务端构建产物打包成单个文件
6
+ * - 生成 HTTP 请求处理的包装器代码
7
+ * - 处理 Node.js 请求到 Web API Request 的转换
8
+ */
9
+ import * as esbuild from "esbuild";
10
+ import path from "path";
11
+ import { readFile, writeFile, deleteFile, ensureDirectory } from "./utils.js";
12
+ /**
13
+ * 默认的 ESM 兼容性代码头
14
+ * 用于在 ESM 模块中提供 CommonJS 的 require、__filename、__dirname 支持
15
+ * 这是因为某些依赖可能仍然使用 CommonJS 语法
16
+ */
17
+ const DEFAULT_BANNER = `import { createRequire } from 'node:module';
18
+ const require = createRequire(import.meta.url);
19
+ const __filename = new URL('', import.meta.url).pathname;
20
+ const __dirname = new URL('.', import.meta.url).pathname;`;
21
+ /**
22
+ * 使用 esbuild 打包服务端代码
23
+ *
24
+ * 将服务端入口文件及其所有依赖打包成单个 JavaScript 文件,
25
+ * 以便在 EdgeOne 边缘函数环境中运行
26
+ *
27
+ * @param context - 构建上下文,包含项目根目录和日志工具
28
+ * @param config - 打包配置,包含入口点、输出路径等
29
+ * @returns esbuild 的构建结果,包含元数据信息
30
+ *
31
+ * @example
32
+ * await bundleServerCode(context, {
33
+ * entryPoints: ['server-wrapper.temp.js'],
34
+ * outfile: '.edgeone/server-handler/handler.js'
35
+ * });
36
+ */
37
+ export async function bundleServerCode(context, config) {
38
+ const { projectRoot, logger } = context;
39
+ logger.verbose("正在打包服务端代码...");
40
+ // 确保输出目录存在
41
+ await ensureDirectory(path.dirname(config.outfile));
42
+ // 使用 esbuild 进行打包
43
+ const result = await esbuild.build({
44
+ entryPoints: config.entryPoints, // 入口文件列表
45
+ bundle: true, // 启用打包模式,将所有依赖打包到一起
46
+ platform: "node", // 目标平台为 Node.js
47
+ target: "node18", // 目标 Node.js 版本
48
+ format: "esm", // 输出格式为 ESM
49
+ outfile: config.outfile, // 输出文件路径
50
+ minify: false, // 不压缩代码,便于调试
51
+ treeShaking: true, // 启用 Tree Shaking 移除未使用的代码
52
+ external: ["node:*", ...(config.external || [])], // 排除 Node.js 内置模块
53
+ metafile: true, // 生成元数据文件,用于分析打包结果
54
+ logLevel: "warning", // 日志级别
55
+ absWorkingDir: projectRoot, // 工作目录
56
+ banner: {
57
+ js: DEFAULT_BANNER, // 添加 ESM 兼容性代码头
58
+ },
59
+ ...config.esbuildOptions, // 合并额外的 esbuild 配置
60
+ });
61
+ logger.verbose(`服务端代码已打包到: ${config.outfile}`);
62
+ return result;
63
+ }
64
+ /**
65
+ * 创建服务端包装器文件
66
+ *
67
+ * 将框架的服务端构建产物包装成一个统一的 HTTP 请求处理器,
68
+ * 该包装器负责:
69
+ * 1. 将 Node.js 的 IncomingMessage 转换为 Web API 的 Request 对象
70
+ * 2. 调用框架的请求处理函数
71
+ * 3. 导出统一的请求处理接口
72
+ *
73
+ * @param context - 构建上下文
74
+ * @param config - 包装器配置
75
+ * @returns 生成的临时包装器文件路径
76
+ *
77
+ * @example
78
+ * const wrapperPath = await createServerWrapper(context, {
79
+ * serverEntryPath: 'build/server/index.js'
80
+ * });
81
+ */
82
+ export async function createServerWrapper(context, config) {
83
+ const { projectRoot, logger } = context;
84
+ logger.verbose("正在创建服务端包装器...");
85
+ // 读取原始服务端构建内容
86
+ const serverBuildContent = await readFile(config.serverEntryPath);
87
+ // 生成包装器内容
88
+ let wrapperContent;
89
+ if (config.wrapperTemplate) {
90
+ // 使用自定义模板,替换占位符
91
+ wrapperContent = config.wrapperTemplate.replace("{{SERVER_BUILD_CONTENT}}", serverBuildContent);
92
+ }
93
+ else {
94
+ // 使用默认包装器模板
95
+ wrapperContent = generateDefaultWrapper(serverBuildContent, config);
96
+ }
97
+ // 写入临时文件
98
+ const tempPath = path.join(projectRoot, "server-wrapper.temp.js");
99
+ await writeFile(tempPath, wrapperContent);
100
+ logger.verbose("服务端包装器创建完成");
101
+ return tempPath;
102
+ }
103
+ /**
104
+ * 清理临时包装器文件
105
+ * 在打包完成后删除临时生成的包装器文件
106
+ *
107
+ * @param wrapperPath - 包装器文件路径
108
+ */
109
+ export async function cleanupWrapper(wrapperPath) {
110
+ try {
111
+ await deleteFile(wrapperPath);
112
+ }
113
+ catch {
114
+ // 忽略删除错误(文件可能不存在)
115
+ }
116
+ }
117
+ /**
118
+ * 生成默认的服务端包装器代码
119
+ *
120
+ * 包装器代码包含:
121
+ * 1. 原始服务端构建内容
122
+ * 2. Node.js 请求到 Web Request 的转换函数
123
+ * 3. 统一的请求处理器导出
124
+ *
125
+ * @param serverBuildContent - 服务端构建的原始代码内容
126
+ * @param config - 包装器配置
127
+ * @returns 生成的包装器代码字符串
128
+ */
129
+ function generateDefaultWrapper(serverBuildContent, config) {
130
+ const banner = config.banner || "";
131
+ const exports = config.exports || [];
132
+ // 构建导出语句
133
+ const exportsStr = exports.length > 0
134
+ ? exports.join(", ")
135
+ : "default as handler";
136
+ return `// ========== 服务端构建内容 ==========
137
+ ${serverBuildContent}
138
+
139
+ // ========== HTTP 服务端包装器 ==========
140
+ ${banner}
141
+
142
+ /**
143
+ * 将 Node.js IncomingMessage 转换为 Web API Request
144
+ *
145
+ * EdgeOne 边缘函数使用 Web API 标准的 Request/Response,
146
+ * 但某些框架的服务端构建可能期望 Node.js 风格的请求对象,
147
+ * 此函数负责进行转换
148
+ *
149
+ * @param nodeReq - Node.js 的 IncomingMessage 对象
150
+ * @returns Web API 的 Request 对象
151
+ */
152
+ function nodeRequestToWebRequest(nodeReq) {
153
+ // 构建完整的 URL
154
+ // 根据连接是否加密判断协议
155
+ const protocol = nodeReq.connection?.encrypted ? 'https' : 'http';
156
+ const host = nodeReq.headers.host || 'localhost';
157
+ const url = \`\${protocol}://\${host}\${nodeReq.url}\`;
158
+
159
+ // 转换请求头
160
+ // Node.js 的 headers 是普通对象,需要转换为 Headers 实例
161
+ const headers = new Headers();
162
+ for (const [key, value] of Object.entries(nodeReq.headers)) {
163
+ if (value) {
164
+ if (Array.isArray(value)) {
165
+ // 多值头部(如 Set-Cookie)需要多次 append
166
+ value.forEach(v => headers.append(key, v));
167
+ } else {
168
+ headers.set(key, value);
169
+ }
170
+ }
171
+ }
172
+
173
+ // 构建 Request 初始化选项
174
+ const init = {
175
+ method: nodeReq.method,
176
+ headers: headers,
177
+ };
178
+
179
+ // 对于非 GET/HEAD 请求,添加请求体
180
+ // GET 和 HEAD 请求不应该有请求体
181
+ if (nodeReq.method !== 'GET' && nodeReq.method !== 'HEAD') {
182
+ init.body = nodeReq;
183
+ }
184
+
185
+ return new Request(url, init);
186
+ }
187
+
188
+ /**
189
+ * Node.js 请求处理器包装函数
190
+ *
191
+ * 这是导出给 EdgeOne 边缘函数调用的主入口
192
+ * 负责将 Node.js 请求转换后传递给框架的处理函数
193
+ *
194
+ * @param nodeReq - Node.js 的请求对象
195
+ * @param args - 其他参数(如环境变量、上下文等)
196
+ * @returns 框架处理函数的返回值(通常是 Response 对象)
197
+ */
198
+ async function nodeRequestHandler(nodeReq, ...args) {
199
+ const webRequest = nodeRequestToWebRequest(nodeReq);
200
+ return handler(webRequest, ...args);
201
+ }
202
+
203
+ export default nodeRequestHandler;
204
+ export { ${exportsStr} };
205
+ `;
206
+ }
207
+ /**
208
+ * 获取打包结果的文件大小
209
+ *
210
+ * @param result - esbuild 的构建结果
211
+ * @returns 打包后的文件大小(字节),如果无法获取则返回 0
212
+ *
213
+ * @example
214
+ * const result = await bundleServerCode(context, config);
215
+ * const size = getBundleSize(result);
216
+ * console.log(`打包大小: ${formatSize(size)}`);
217
+ */
218
+ export function getBundleSize(result) {
219
+ // 需要 metafile 才能获取大小信息
220
+ if (!result.metafile)
221
+ return 0;
222
+ const outputs = result.metafile.outputs;
223
+ const outputKeys = Object.keys(outputs);
224
+ if (outputKeys.length === 0)
225
+ return 0;
226
+ // 返回第一个输出文件的大小
227
+ return outputs[outputKeys[0]].bytes;
228
+ }
229
+ //# sourceMappingURL=bundler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundler.js","sourceRoot":"","sources":["../src/bundler.ts"],"names":[],"mappings":"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"}
package/dist/core.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * EdgeOne Vite Plugin Adapter - 核心插件模块
3
+ *
4
+ * 本模块是整个适配器系统的核心,负责:
5
+ * - 构建产物检测:自动识别客户端和服务端的构建输出
6
+ * - 静态资源复制:将客户端构建产物复制到部署目录
7
+ * - 服务端代码打包:将服务端代码打包成单文件
8
+ * - 路由配置生成:根据框架特性生成路由信息
9
+ * - Meta.json 生成:生成 EdgeOne 部署所需的配置文件
10
+ */
11
+ import type { Plugin } from "vite";
12
+ import type { CoreAdapterOptions } from "./types.js";
13
+ /**
14
+ * 创建 EdgeOne 核心适配器插件
15
+ *
16
+ * 这是创建适配器的主要入口函数,返回一个 Vite 插件实例。
17
+ * 该插件会在 Vite 构建完成后执行,处理构建产物并生成部署配置。
18
+ *
19
+ * @param options - 适配器配置选项
20
+ * @returns Vite 插件实例
21
+ *
22
+ * @example
23
+ * // 基本用法
24
+ * createCoreAdapter({ verbose: true })
25
+ *
26
+ * @example
27
+ * // 使用框架适配器
28
+ * createCoreAdapter({
29
+ * adapter: myFrameworkAdapter,
30
+ * outputDir: '.edgeone'
31
+ * })
32
+ */
33
+ export declare function createCoreAdapter(options?: CoreAdapterOptions): Plugin;
34
+ export default createCoreAdapter;
35
+ //# sourceMappingURL=core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAkB,MAAM,MAAM,CAAC;AAEnD,OAAO,KAAK,EACV,kBAAkB,EAOnB,MAAM,YAAY,CAAC;AAepB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB,GAAG,MAAM,CAmI1E;AAyRD,eAAe,iBAAiB,CAAC"}