UNPKG

@esmx/core

Version:

A high-performance microfrontend framework supporting Vue, React, Preact, Solid, and Svelte with SSR and Module Federation capabilities.

698 lines (697 loc) 23.1 kB
import type { ImportMap, ScopesMap, SpecifierMap } from '@esmx/import'; import { type App } from './app'; import { type ManifestJson } from './manifest-json'; import { type ModuleConfig, type ParsedModuleConfig } from './module-config'; import { type PackConfig, type ParsedPackConfig } from './pack-config'; import type { ImportmapMode } from './render-context'; import type { RenderContext, RenderContextOptions } from './render-context'; import type { Middleware } from './utils/middleware'; import { type ProjectPath } from './utils/resolve-path'; /** * Esmx 框架的核心配置选项接口 */ export interface EsmxOptions { /** * 项目根目录路径 * - 可以是绝对路径或相对路径 * - 默认为当前工作目录 (process.cwd()) */ root?: string; /** * 是否为生产环境 * - true: 生产环境 * - false: 开发环境 * - 默认根据 process.env.NODE_ENV === 'production' 判断 */ isProd?: boolean; /** * 基础路径占位符配置 * - string: 自定义占位符 * - false: 禁用占位符 * - 默认值为 '[[[___GEZ_DYNAMIC_BASE___]]]' * - 用于运行时动态替换资源的基础路径 */ basePathPlaceholder?: string | false; /** * 模块配置选项 * - 用于配置项目的模块解析规则 * - 包括模块别名、外部依赖等配置 */ modules?: ModuleConfig; /** * 打包配置选项 * - 用于将构建产物打包成标准的 npm .tgz 格式软件包 * - 包括输出路径、package.json 处理、打包钩子等配置 */ packs?: PackConfig; /** * 开发环境应用创建函数 * - 仅在开发环境中使用 * - 用于创建开发服务器的应用实例 * @param esmx Esmx实例 */ devApp?: (esmx: Esmx) => Promise<App>; /** * 服务器启动配置函数 * - 用于配置和启动 HTTP 服务器 * - 在开发环境和生产环境中都可使用 * @param esmx Esmx实例 */ server?: (esmx: Esmx) => Promise<void>; /** * 构建后置处理函数 * - 在项目构建完成后执行 * - 可用于执行额外的资源处理、部署等操作 * @param esmx Esmx实例 */ postBuild?: (esmx: Esmx) => Promise<void>; } /** * 应用程序构建目标类型。 * - client: 客户端构建目标,用于生成浏览器端运行的代码 * - server: 服务端构建目标,用于生成 Node.js 环境运行的代码 */ export type BuildSsrTarget = 'client' | 'server'; /** * Esmx 框架的命令枚举。 * 用于控制框架的运行模式和生命周期。 */ export declare enum COMMAND { /** * 开发模式 * 启动开发服务器并支持热更新 */ dev = "dev", /** * 构建模式 * 生成生产环境构建产物 */ build = "build", /** * 预览模式 * 预览构建产物 */ preview = "preview", /** * 启动模式 * 启动生产环境服务器 */ start = "start" } export type { ImportMap, SpecifierMap, ScopesMap }; export declare class Esmx { private readonly _options; private _readied; private _importmapHash; private get readied(); /** * 获取模块名称 * @returns {string} 当前模块的名称,来源于模块配置 * @throws {NotReadyError} 在框架实例未初始化时抛出错误 */ get name(): string; /** * 获取模块变量名 * @returns {string} 基于模块名称生成的合法 JavaScript 变量名 * @throws {NotReadyError} 在框架实例未初始化时抛出错误 */ get varName(): string; /** * 获取项目根目录的绝对路径 * @returns {string} 项目根目录的绝对路径 * 如果配置的 root 为相对路径,则基于当前工作目录解析为绝对路径 */ get root(): string; /** * 判断当前是否为生产环境 * @returns {boolean} 环境标识 * 优先使用配置项中的 isProd,若未配置则根据 process.env.NODE_ENV 判断 */ get isProd(): boolean; /** * 获取模块的基础路径 * @returns {string} 以斜杠开头和结尾的模块基础路径 * 用于构建模块资源的访问路径 */ get basePath(): string; /** * 获取基础路径占位符 * @returns {string} 基础路径占位符或空字符串 * 用于运行时动态替换模块的基础路径,可通过配置禁用 */ get basePathPlaceholder(): string; /** * 获取当前执行的命令 * @returns {COMMAND} 当前正在执行的命令枚举值 * @throws {NotReadyError} 在框架实例未初始化时调用此方法会抛出错误 */ get command(): COMMAND; /** * 获取命令枚举类型 * @returns {typeof COMMAND} 命令枚举类型定义 */ get COMMAND(): typeof COMMAND; /** * 获取模块配置信息 * @returns {ParsedModuleConfig} 当前模块的完整配置信息 */ get moduleConfig(): ParsedModuleConfig; /** * 获取打包配置信息 * @returns {ParsedPackConfig} 当前模块的打包相关配置 */ get packConfig(): ParsedPackConfig; /** * 获取应用程序的静态资源处理中间件。 * * 该中间件负责处理应用程序的静态资源请求,根据运行环境提供不同的实现: * - 开发环境:支持源码的实时编译、热更新,使用 no-cache 缓存策略 * - 生产环境:处理构建后的静态资源,支持不可变文件的长期缓存 * * @returns {Middleware} 返回静态资源处理中间件函数 * @throws {NotReadyError} 在框架实例未初始化时调用此方法会抛出错误 * * @example * ```ts * const server = http.createServer((req, res) => { * // 使用中间件处理静态资源请求 * esmx.middleware(req, res, async () => { * const rc = await esmx.render({ url: req.url }); * res.end(rc.html); * }); * }); * ``` */ get middleware(): Middleware; /** * 获取应用程序的服务端渲染函数。 * * 该函数负责执行服务端渲染,根据运行环境提供不同的实现: * - 开发环境:加载源码中的服务端入口文件,支持热更新和实时预览 * - 生产环境:加载构建后的服务端入口文件,提供优化的渲染性能 * * @returns {(options?: RenderContextOptions) => Promise<RenderContext>} 返回服务端渲染函数 * @throws {NotReadyError} 在框架实例未初始化时调用此方法会抛出错误 * * @example * ```ts * // 基本用法 * const rc = await esmx.render({ * params: { url: req.url } * }); * res.end(rc.html); * * // 高级配置 * const rc = await esmx.render({ * base: '', // 设置基础路径 * importmapMode: 'inline', // 设置导入映射模式 * entryName: 'default', // 指定渲染入口 * params: { * url: req.url, * state: { user: 'admin' } * } * }); * ``` */ get render(): (options?: RenderContextOptions) => Promise<RenderContext>; constructor(options?: EsmxOptions); /** * 初始化 Esmx 框架实例。 * * 该方法执行以下核心初始化流程: * 1. 解析项目配置(package.json、模块配置、打包配置等) * 2. 创建应用实例(开发环境或生产环境) * 3. 根据命令执行相应的生命周期方法 * * @param command - 框架运行命令 * - dev: 启动开发服务器,支持热更新 * - build: 构建生产环境产物 * - preview: 预览构建产物 * - start: 启动生产环境服务器 * * @returns 初始化成功返回 true * @throws {Error} 重复初始化时抛出错误 * * @example * ```ts * // entry.node.ts * import type { EsmxOptions } from '@esmx/core'; * * export default { * // 开发环境配置 * async devApp(esmx) { * return import('@esmx/rspack').then((m) => * m.createRspackHtmlApp(esmx, { * config(context) { * // 自定义 Rspack 配置 * } * }) * ); * }, * * // HTTP 服务器配置 * async server(esmx) { * const server = http.createServer((req, res) => { * // 静态文件处理 * esmx.middleware(req, res, async () => { * // 传入渲染的参数 * const render = await esmx.render({ * params: { url: req.url } * }); * // 响应 HTML 内容 * res.end(render.html); * }); * }); * * // 监听端口 * server.listen(3000, () => { * console.log('http://localhost:3000'); * }); * } * } satisfies EsmxOptions; * ``` */ init(command: COMMAND): Promise<boolean>; /** * 销毁 Esmx 框架实例,执行资源清理和连接关闭等操作。 * * 该方法主要用于开发环境下的资源清理,包括: * - 关闭开发服务器(如 Rspack Dev Server) * - 清理临时文件和缓存 * - 释放系统资源 * * 注意:一般情况下,框架会自动处理资源的释放,用户无需手动调用此方法。 * 仅在需要自定义资源清理逻辑时才需要使用。 * * @returns 返回一个 Promise,resolve 为 boolean 值 * - true: 清理成功或无需清理 * - false: 清理失败 * * @example * ```ts * // 在需要自定义清理逻辑时使用 * process.once('SIGTERM', async () => { * await esmx.destroy(); // 清理资源 * process.exit(0); * }); * ``` */ destroy(): Promise<boolean>; /** * 执行应用程序的构建流程。 * * 该方法负责执行整个应用的构建过程,包括: * - 编译源代码 * - 生成生产环境的构建产物 * - 优化和压缩代码 * - 生成资源清单 * * 构建过程会打印开始和结束时间,以及总耗时等信息。 * * @returns 返回一个 Promise,resolve 为 boolean 值 * - true: 构建成功或构建方法未实现 * - false: 构建失败 * * @throws {NotReadyError} 在框架实例未初始化时调用此方法会抛出错误 * * @example * ```ts * // entry.node.ts * import type { EsmxOptions } from '@esmx/core'; * * export default { * // 开发环境配置 * async devApp(esmx) { * return import('@esmx/rspack').then((m) => * m.createRspackHtmlApp(esmx, { * config(context) { * // 自定义 Rspack 配置 * } * }) * ); * }, * * // 构建后处理 * async postBuild(esmx) { * // 构建完成后生成静态 HTML * const render = await esmx.render({ * params: { url: '/' } * }); * esmx.writeSync( * esmx.resolvePath('dist/client', 'index.html'), * render.html * ); * } * } satisfies EsmxOptions; * ``` */ build(): Promise<boolean>; /** * 启动 HTTP 服务器并配置服务器实例。 * * 该方法在框架的以下生命周期中被调用: * - 开发环境(dev):启动开发服务器,提供热更新等功能 * - 生产环境(start):启动生产服务器,提供生产级性能 * * 服务器的具体实现由用户通过 EsmxOptions 的 server 配置函数提供。 * 该函数负责: * - 创建 HTTP 服务器实例 * - 配置中间件和路由 * - 处理请求和响应 * - 启动服务器监听 * * @returns 返回一个 Promise,在服务器启动完成后 resolve * @throws {NotReadyError} 在框架实例未初始化时调用此方法会抛出错误 * * @example * ```ts * // entry.node.ts * import http from 'node:http'; * import type { EsmxOptions } from '@esmx/core'; * * export default { * // 服务器配置 * async server(esmx) { * const server = http.createServer((req, res) => { * // 处理静态资源 * esmx.middleware(req, res, async () => { * // 服务端渲染 * const render = await esmx.render({ * params: { url: req.url } * }); * res.end(render.html); * }); * }); * * // 启动服务器 * server.listen(3000, () => { * console.log('Server running at http://localhost:3000'); * }); * } * } satisfies EsmxOptions; * ``` */ server(): Promise<void>; /** * 执行构建后的处理逻辑。 * * 该方法在应用构建完成后被调用,用于执行额外的资源处理,如: * - 生成静态 HTML 文件 * - 处理构建产物 * - 执行部署任务 * - 发送构建通知 * * 方法会自动捕获并处理执行过程中的异常,确保不会影响主构建流程。 * * @returns 返回一个 Promise,resolve 为 boolean 值 * - true: 后处理成功或无需处理 * - false: 后处理失败 * * @example * ```ts * // entry.node.ts * import type { EsmxOptions } from '@esmx/core'; * * export default { * // 构建后处理 * async postBuild(esmx) { * // 生成多个页面的静态 HTML * const pages = ['/', '/about', '/404']; * * for (const url of pages) { * const render = await esmx.render({ * params: { url } * }); * * // 写入静态 HTML 文件 * esmx.writeSync( * esmx.resolvePath('dist/client', url.substring(1), 'index.html'), * render.html * ); * } * } * } satisfies EsmxOptions; * ``` */ postBuild(): Promise<boolean>; /** * 解析项目相对路径为绝对路径 * * @param projectPath - 项目路径类型,如 'dist/client'、'dist/server' 等 * @param args - 需要拼接的路径片段 * @returns 解析后的绝对路径 * * @example * ```ts * // 在 entry.node.ts 中使用 * async postBuild(esmx) { * const outputPath = esmx.resolvePath('dist/client', 'index.html'); * // 输出: /project/root/dist/client/index.html * } * ``` */ resolvePath(projectPath: ProjectPath, ...args: string[]): string; /** * 同步写入文件内容 * * @param filepath - 文件的绝对路径 * @param data - 要写入的数据,可以是字符串、Buffer 或对象 * @returns 写入是否成功 * * @example * ```ts * // 在 entry.node.ts 中使用 * async postBuild(esmx) { * const htmlPath = esmx.resolvePath('dist/client', 'index.html'); * const success = esmx.writeSync(htmlPath, '<html>...</html>'); * } * ``` */ writeSync(filepath: string, data: any): boolean; /** * 异步写入文件内容 * * @param filepath - 文件的绝对路径 * @param data - 要写入的数据,可以是字符串、Buffer 或对象 * @returns Promise<boolean> 写入是否成功 * * @example * ```ts * // 在 entry.node.ts 中使用 * async postBuild(esmx) { * const htmlPath = esmx.resolvePath('dist/client', 'index.html'); * const success = await esmx.write(htmlPath, '<html>...</html>'); * } * ``` */ write(filepath: string, data: any): Promise<boolean>; /** * 同步读取并解析 JSON 文件 * * @template T - 期望返回的JSON对象类型 * @param filename - JSON 文件的绝对路径 * @returns {T} 解析后的 JSON 对象 * @throws 当文件不存在或 JSON 格式错误时抛出异常 * * @example * ```ts * // 在 entry.node.ts 中使用 * async server(esmx) { * const manifest = esmx.readJsonSync<Manifest>(esmx.resolvePath('dist/client', 'manifest.json')); * // 使用 manifest 对象 * } * ``` */ readJsonSync<T = any>(filename: string): T; /** * 异步读取并解析 JSON 文件 * * @template T - 期望返回的JSON对象类型 * @param filename - JSON 文件的绝对路径 * @returns {Promise<T>} 解析后的 JSON 对象 * @throws 当文件不存在或 JSON 格式错误时抛出异常 * * @example * ```ts * // 在 entry.node.ts 中使用 * async server(esmx) { * const manifest = await esmx.readJson<Manifest>(esmx.resolvePath('dist/client', 'manifest.json')); * // 使用 manifest 对象 * } * ``` */ readJson<T = any>(filename: string): Promise<T>; /** * 获取构建清单列表 * * @description * 该方法用于获取指定目标环境的构建清单列表,包含以下功能: * 1. **缓存管理** * - 使用内部缓存机制避免重复加载 * - 返回不可变的清单列表 * * 2. **环境适配** * - 支持客户端和服务端两种环境 * - 根据目标环境返回对应的清单信息 * * 3. **模块映射** * - 包含模块导出信息 * - 记录资源依赖关系 * * @param target - 目标环境类型 * - 'client': 客户端环境 * - 'server': 服务端环境 * @returns 返回只读的构建清单列表 * @throws {NotReadyError} 在框架实例未初始化时调用此方法会抛出错误 * * @example * ```ts * // 在 entry.node.ts 中使用 * async server(esmx) { * // 获取客户端构建清单 * const manifests = await esmx.getManifestList('client'); * * // 查找特定模块的构建信息 * const appModule = manifests.find(m => m.name === 'my-app'); * if (appModule) { * console.log('App exports:', appModule.exports); * console.log('App chunks:', appModule.chunks); * } * } * ``` */ getManifestList(target: BuildSsrTarget): Promise<readonly ManifestJson[]>; /** * 获取导入映射对象 * * @description * 该方法用于生成 ES 模块导入映射(Import Map),具有以下特点: * 1. **模块解析** * - 基于构建清单生成模块映射 * - 支持客户端和服务端两种环境 * - 自动处理模块路径解析 * * 2. **缓存优化** * - 使用内部缓存机制 * - 返回不可变的映射对象 * * 3. **路径处理** * - 自动处理模块路径 * - 支持动态基础路径 * * @param target - 目标环境类型 * - 'client': 生成浏览器环境的导入映射 * - 'server': 生成服务端环境的导入映射 * @returns 返回只读的导入映射对象 * @throws {NotReadyError} 在框架实例未初始化时调用此方法会抛出错误 * * @example * ```ts * // 在 entry.node.ts 中使用 * async server(esmx) { * // 获取客户端导入映射 * const importmap = await esmx.getImportMap('client'); * * // 自定义 HTML 模板 * const html = ` * <!DOCTYPE html> * <html> * <head> * <script type="importmap"> * ${JSON.stringify(importmap)} * </script> * </head> * <body> * <!-- 页面内容 --> * </body> * </html> * `; * } * ``` */ getImportMap(target: BuildSsrTarget): Promise<Readonly<ImportMap>>; /** * 获取客户端导入映射信息 * * @description * 该方法用于生成客户端环境的导入映射代码,支持两种模式: * 1. **内联模式 (inline)** * - 将导入映射直接内联到 HTML 中 * - 减少额外的网络请求 * - 适合导入映射较小的场景 * * 2. **JS 文件模式 (js)** * - 生成独立的 JS 文件 * - 支持浏览器缓存 * - 适合导入映射较大的场景 * * 核心功能: * - 自动处理动态基础路径 * - 支持模块路径运行时替换 * - 优化缓存策略 * - 确保模块加载顺序 * * @param mode - 导入映射模式 * - 'inline': 内联模式,返回 HTML script 标签 * - 'js': JS 文件模式,返回带有文件路径的信息 * @returns 返回导入映射的相关信息 * - src: JS 文件的 URL(仅在 js 模式下) * - filepath: JS 文件的本地路径(仅在 js 模式下) * - code: HTML script 标签内容 * @throws {NotReadyError} 在框架实例未初始化时调用此方法会抛出错误 * * @example * ```ts * // 在 entry.node.ts 中使用 * async server(esmx) { * const server = express(); * server.use(esmx.middleware); * * server.get('*', async (req, res) => { * // 使用 JS 文件模式 * const result = await esmx.render({ * importmapMode: 'js', * params: { url: req.url } * }); * res.send(result.html); * }); * * // 或者使用内联模式 * server.get('/inline', async (req, res) => { * const result = await esmx.render({ * importmapMode: 'inline', * params: { url: req.url } * }); * res.send(result.html); * }); * } * ``` */ getImportMapClientInfo<T extends ImportmapMode>(mode: T): Promise<T extends 'js' ? { src: string; filepath: string; code: string; } : { src: null; filepath: null; code: string; }>; /** * 获取模块的静态导入路径列表。 * * @param target - 构建目标('client' | 'server') * @param specifier - 模块标识符 * @returns 返回静态导入路径列表,如果未找到则返回 null * @throws {NotReadyError} 在框架实例未初始化时调用此方法会抛出错误 * * @example * ```ts * // 获取客户端入口模块的静态导入路径 * const paths = await esmx.getStaticImportPaths( * 'client', * `your-app-name/src/entry.client` * ); * ``` */ getStaticImportPaths(target: BuildSsrTarget, specifier: string): Promise<readonly string[] | null>; }