@esmx/core
Version:
A high-performance microfrontend framework supporting Vue, React, Preact, Solid, and Svelte with SSR and Module Federation capabilities.
700 lines (699 loc) • 25.2 kB
TypeScript
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';
/**
* Core configuration options interface for the Esmx framework
*/
export interface EsmxOptions {
/**
* Project root directory path
* - Can be absolute or relative path
* - Defaults to current working directory (process.cwd())
*/
root?: string;
/**
* Whether it is production environment
* - true: Production environment
* - false: Development environment
* - Defaults to process.env.NODE_ENV === 'production'
*/
isProd?: boolean;
/**
* Base path placeholder configuration
* - string: Custom placeholder
* - false: Disable placeholder
* - Default value is '[[[___ESMX_DYNAMIC_BASE___]]]'
* - Used for dynamically replacing the base path of assets at runtime
*/
basePathPlaceholder?: string | false;
/**
* Module configuration options
* - Used to configure module resolution rules for the project
* - Includes module aliases, external dependencies, etc.
*/
modules?: ModuleConfig;
/**
* Package configuration options
* - Used to package build artifacts into standard npm .tgz format packages
* - Includes output path, package.json handling, packaging hooks, etc.
*/
packs?: PackConfig;
/**
* Development environment application creation function
* - Only used in development environment
* - Used to create application instance for development server
* @param esmx Esmx instance
*/
devApp?: (esmx: Esmx) => Promise<App>;
/**
* Server startup configuration function
* - Used to configure and start HTTP server
* - Can be used in both development and production environments
* @param esmx Esmx instance
*/
server?: (esmx: Esmx) => Promise<void>;
/**
* Post-build processing function
* - Executed after project build is completed
* - Can be used to perform additional resource processing, deployment, etc.
* @param esmx Esmx instance
*/
postBuild?: (esmx: Esmx) => Promise<void>;
}
/**
* Application build target types.
* - client: Client build target, used to generate code that runs in the browser
* - server: Server build target, used to generate code that runs in Node.js environment
*/
export type BuildEnvironment = 'client' | 'server';
/**
* Command enumeration for the Esmx framework.
* Used to control the runtime mode and lifecycle of the framework.
*/
export declare enum COMMAND {
/**
* Development mode
* Starts development server with hot reload support
*/
dev = "dev",
/**
* Build mode
* Generates production build artifacts
*/
build = "build",
/**
* Preview mode
* Preview build artifacts
*/
preview = "preview",
/**
* Start mode
* Starts production environment server
*/
start = "start"
}
export type { ImportMap, SpecifierMap, ScopesMap };
export declare class Esmx {
private readonly _options;
private _readied;
private _importmapHash;
private get readied();
/**
* Get module name
* @returns {string} The name of the current module, sourced from module configuration
* @throws {NotReadyError} Throws error when framework instance is not initialized
*/
get name(): string;
/**
* Get module variable name
* @returns {string} A valid JavaScript variable name generated based on the module name
* @throws {NotReadyError} Throws error when framework instance is not initialized
*/
get varName(): string;
/**
* Get the absolute path of the project root directory
* @returns {string} The absolute path of the project root directory
* If the configured root is a relative path, it is resolved to an absolute path based on the current working directory
*/
get root(): string;
/**
* Determine if currently in production environment
* @returns {boolean} Environment flag
* Prioritizes the isProd in configuration, if not configured, judges based on process.env.NODE_ENV
*/
get isProd(): boolean;
/**
* Get the base path of the module
* @returns {string} The base path of the module starting and ending with a slash
* Used to construct the access path for module assets
*/
get basePath(): string;
/**
* Get the base path placeholder
* @returns {string} Base path placeholder or empty string
* Used for dynamically replacing the base path of the module at runtime, can be disabled through configuration
*/
get basePathPlaceholder(): string;
/**
* Get the currently executing command
* @returns {COMMAND} The command enumeration value currently being executed
* @throws {NotReadyError} Throws error when calling this method if the framework instance is not initialized
*/
get command(): COMMAND;
/**
* Get the command enumeration type
* @returns {typeof COMMAND} Command enumeration type definition
*/
get COMMAND(): typeof COMMAND;
/**
* Get module configuration information
* @returns {ParsedModuleConfig} Complete configuration information of the current module
*/
get moduleConfig(): ParsedModuleConfig;
/**
* Get package configuration information
* @returns {ParsedPackConfig} Package-related configuration of the current module
*/
get packConfig(): ParsedPackConfig;
/**
* Get the static asset processing middleware for the application.
*
* This middleware is responsible for handling static asset requests for the application,
* providing different implementations based on the runtime environment:
* - Development environment: Supports real-time compilation and hot reloading of source code, uses no-cache strategy
* - Production environment: Handles built static assets, supports long-term caching for immutable files
*
* @returns {Middleware} Returns the static asset processing middleware function
* @throws {NotReadyError} Throws error when calling this method if the framework instance is not initialized
*
* @example
* ```ts
* const server = http.createServer((req, res) => {
* // Use middleware to handle static asset requests
* esmx.middleware(req, res, async () => {
* const rc = await esmx.render({ url: req.url });
* res.end(rc.html);
* });
* });
* ```
*/
get middleware(): Middleware;
/**
* Get the server-side rendering function for the application.
*
* This function is responsible for executing server-side rendering,
* providing different implementations based on the runtime environment:
* - Development environment: Loads server entry file from source code, supports hot reloading and real-time preview
* - Production environment: Loads built server entry file, provides optimized rendering performance
*
* @returns {(options?: RenderContextOptions) => Promise<RenderContext>} Returns the server-side rendering function
* @throws {NotReadyError} Throws error when calling this method if the framework instance is not initialized
*
* @example
* ```ts
* // Basic usage
* const rc = await esmx.render({
* params: { url: req.url }
* });
* res.end(rc.html);
*
* // Advanced configuration
* const rc = await esmx.render({
* base: '', // Set base path
* importmapMode: 'inline', // Set import map mode
* entryName: 'default', // Specify render entry
* params: {
* url: req.url,
* state: { user: 'admin' }
* }
* });
* ```
*/
get render(): (options?: RenderContextOptions) => Promise<RenderContext>;
constructor(options?: EsmxOptions);
/**
* Initialize the Esmx framework instance.
*
* This method executes the following core initialization process:
* 1. Parse project configuration (package.json, module configuration, package configuration, etc.)
* 2. Create application instance (development or production environment)
* 3. Execute corresponding lifecycle methods based on the command
*
* @param command - Framework running command
* - dev: Start development server with hot reload support
* - build: Build production artifacts
* - preview: Preview build artifacts
* - start: Start production environment server
*
* @returns Returns true for successful initialization
* @throws {Error} Throws error when initializing repeatedly
*
* @example
* ```ts
* // entry.node.ts
* import type { EsmxOptions } from '@esmx/core';
*
* export default {
* // Development environment configuration
* async devApp(esmx) {
* return import('@esmx/rspack').then((m) =>
* m.createRspackHtmlApp(esmx, {
* config(context) {
* // Custom Rspack configuration
* }
* })
* );
* },
*
* // HTTP server configuration
* async server(esmx) {
* const server = http.createServer((req, res) => {
* // Static file handling
* esmx.middleware(req, res, async () => {
* // Pass rendering parameters
* const render = await esmx.render({
* params: { url: req.url }
* });
* // Respond with HTML content
* res.end(render.html);
* });
* });
*
* // Listen to port
* server.listen(3000, () => {
* console.log('http://localhost:3000');
* });
* }
* } satisfies EsmxOptions;
* ```
*/
init(command: COMMAND): Promise<boolean>;
/**
* Destroy the Esmx framework instance, performing resource cleanup and connection closing operations.
*
* This method is mainly used for resource cleanup in development environment, including:
* - Closing development servers (such as Rspack Dev Server)
* - Cleaning up temporary files and cache
* - Releasing system resources
*
* Note: In general, the framework automatically handles resource release, users do not need to manually call this method.
* Only use it when custom resource cleanup logic is needed.
*
* @returns Returns a Promise that resolves to a boolean value
* - true: Cleanup successful or no cleanup needed
* - false: Cleanup failed
*
* @example
* ```ts
* // Use when custom cleanup logic is needed
* process.once('SIGTERM', async () => {
* await esmx.destroy(); // Clean up resources
* process.exit(0);
* });
* ```
*/
destroy(): Promise<boolean>;
/**
* Execute the application's build process.
*
* This method is responsible for executing the entire application build process, including:
* - Compiling source code
* - Generating production build artifacts
* - Optimizing and compressing code
* - Generating asset manifests
*
* The build process prints start and end times, as well as total duration and other information.
*
* @returns Returns a Promise that resolves to a boolean value
* - true: Build successful or build method not implemented
* - false: Build failed
*
* @throws {NotReadyError} Throws error when calling this method if the framework instance is not initialized
*
* @example
* ```ts
* // entry.node.ts
* import type { EsmxOptions } from '@esmx/core';
*
* export default {
* // Development environment configuration
* async devApp(esmx) {
* return import('@esmx/rspack').then((m) =>
* m.createRspackHtmlApp(esmx, {
* config(context) {
* // Custom Rspack configuration
* }
* })
* );
* },
*
* // Post-build processing
* async postBuild(esmx) {
* // Generate static HTML after build completion
* const render = await esmx.render({
* params: { url: '/' }
* });
* esmx.writeSync(
* esmx.resolvePath('dist/client', 'index.html'),
* render.html
* );
* }
* } satisfies EsmxOptions;
* ```
*/
build(): Promise<boolean>;
/**
* Start HTTP server and configure server instance.
*
* This method is called in the following lifecycle of the framework:
* - Development environment (dev): Start development server, providing features like hot reload
* - Production environment (start): Start production server, providing production-grade performance
*
* The specific implementation of the server is provided by the user through the server configuration function in EsmxOptions.
* This function is responsible for:
* - Creating HTTP server instance
* - Configuring middleware and routes
* - Handling requests and responses
* - Starting server listening
*
* @returns Returns a Promise that resolves when the server startup is complete
* @throws {NotReadyError} Throws error when calling this method if the framework instance is not initialized
*
* @example
* ```ts
* // entry.node.ts
* import http from 'node:http';
* import type { EsmxOptions } from '@esmx/core';
*
* export default {
* // Server configuration
* async server(esmx) {
* const server = http.createServer((req, res) => {
* // Handle static assets
* esmx.middleware(req, res, async () => {
* // Server-side rendering
* const render = await esmx.render({
* params: { url: req.url }
* });
* res.end(render.html);
* });
* });
*
* // Start server
* server.listen(3000, () => {
* console.log('Server running at http://localhost:3000');
* });
* }
* } satisfies EsmxOptions;
* ```
*/
server(): Promise<void>;
/**
* Execute post-build processing logic.
*
* This method is called after the application build is completed, used to perform additional resource processing, such as:
* - Generating static HTML files
* - Processing build artifacts
* - Executing deployment tasks
* - Sending build notifications
*
* The method automatically captures and handles exceptions during execution, ensuring it does not affect the main build process.
*
* @returns Returns a Promise that resolves to a boolean value
* - true: Post-processing successful or no processing needed
* - false: Post-processing failed
*
* @example
* ```ts
* // entry.node.ts
* import type { EsmxOptions } from '@esmx/core';
*
* export default {
* // Post-build processing
* async postBuild(esmx) {
* // Generate static HTML for multiple pages
* const pages = ['/', '/about', '/404'];
*
* for (const url of pages) {
* const render = await esmx.render({
* params: { url }
* });
*
* // Write static HTML file
* esmx.writeSync(
* esmx.resolvePath('dist/client', url.substring(1), 'index.html'),
* render.html
* );
* }
* }
* } satisfies EsmxOptions;
* ```
*/
postBuild(): Promise<boolean>;
/**
* Resolve project relative path to absolute path
*
* @param projectPath - Project path type, such as 'dist/client', 'dist/server', etc.
* @param args - Path segments to be concatenated
* @returns Resolved absolute path
*
* @example
* ```ts
* // Used in entry.node.ts
* async postBuild(esmx) {
* const outputPath = esmx.resolvePath('dist/client', 'index.html');
* // Output: /project/root/dist/client/index.html
* }
* ```
*/
resolvePath(projectPath: ProjectPath, ...args: string[]): string;
/**
* Write file content synchronously
*
* @param filepath - Absolute path of the file
* @param data - Data to be written, can be string, Buffer or object
* @returns Whether the write was successful
*
* @example
* ```ts
* // Used in 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;
/**
* Write file content asynchronously
*
* @param filepath - Absolute path of the file
* @param data - Data to be written, can be string, Buffer or object
* @returns Promise<boolean> Whether the write was successful
*
* @example
* ```ts
* // Used in 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>;
/**
* Read and parse JSON file synchronously
*
* @template T - Expected JSON object type to return
* @param filename - Absolute path of the JSON file
* @returns {T} Parsed JSON object
* @throws Throws exception when file does not exist or JSON format is incorrect
*
* @example
* ```ts
* // Used in entry.node.ts
* async server(esmx) {
* const manifest = esmx.readJsonSync<Manifest>(esmx.resolvePath('dist/client', 'manifest.json'));
* // Use manifest object
* }
* ```
*/
readJsonSync<T = any>(filename: string): T;
/**
* Read and parse JSON file asynchronously
*
* @template T - Expected JSON object type to return
* @param filename - Absolute path of the JSON file
* @returns {Promise<T>} Parsed JSON object
* @throws Throws exception when file does not exist or JSON format is incorrect
*
* @example
* ```ts
* // Used in entry.node.ts
* async server(esmx) {
* const manifest = await esmx.readJson<Manifest>(esmx.resolvePath('dist/client', 'manifest.json'));
* // Use manifest object
* }
* ```
*/
readJson<T = any>(filename: string): Promise<T>;
/**
* Get build manifest list
*
* @description
* This method is used to get the build manifest list for the specified target environment, including the following features:
* 1. **Cache Management**
* - Uses internal caching mechanism to avoid repeated loading
* - Returns immutable manifest list
*
* 2. **Environment Adaptation**
* - Supports both client and server environments
* - Returns corresponding manifest information based on the target environment
*
* 3. **Module Mapping**
* - Contains module export information
* - Records resource dependency relationships
*
* @param env - Target environment type
* - 'client': Client environment
* - 'server': Server environment
* @returns Returns read-only build manifest list
* @throws {NotReadyError} Throws error when calling this method if the framework instance is not initialized
*
* @example
* ```ts
* // Used in entry.node.ts
* async server(esmx) {
* // Get client build manifest
* const manifests = await esmx.getManifestList('client');
*
* // Find build information for a specific module
* const appModule = manifests.find(m => m.name === 'my-app');
* if (appModule) {
* console.log('App exports:', appModule.exports);
* console.log('App chunks:', appModule.chunks);
* }
* }
* ```
*/
getManifestList(env: BuildEnvironment): Promise<readonly ManifestJson[]>;
/**
* Get import map object
*
* @description
* This method is used to generate ES module import maps with the following features:
* 1. **Module Resolution**
* - Generate module mappings based on build manifests
* - Support both client and server environments
* - Automatically handle module path resolution
*
* 2. **Cache Optimization**
* - Use internal caching mechanism
* - Return immutable mapping objects
*
* 3. **Path Handling**
* - Automatically handle module paths
* - Support dynamic base paths
*
* @param env - Target environment type
* - 'client': Generate import map for browser environment
* - 'server': Generate import map for server environment
* @returns Returns read-only import map object
* @throws {NotReadyError} Throws error when calling this method if the framework instance is not initialized
*
* @example
* ```ts
* // Used in entry.node.ts
* async server(esmx) {
* // Get client import map
* const importmap = await esmx.getImportMap('client');
*
* // Custom HTML template
* const html = `
* <!DOCTYPE html>
* <html>
* <head>
* <script type="importmap">
* ${JSON.stringify(importmap)}
* </script>
* </head>
* <body>
* <!-- Page content -->
* </body>
* </html>
* `;
* }
* ```
*/
getImportMap(env: BuildEnvironment): Promise<Readonly<ImportMap>>;
/**
* Get client import map information
*
* @description
* This method is used to generate import map code for client environment, supporting two modes:
* 1. **Inline Mode (inline)**
* - Inline import map directly into HTML
* - Reduce additional network requests
* - Suitable for scenarios with smaller import maps
*
* 2. **JS File Mode (js)**
* - Generate standalone JS file
* - Support browser caching
* - Suitable for scenarios with larger import maps
*
* Core Features:
* - Automatically handle dynamic base paths
* - Support module path runtime replacement
* - Optimize caching strategy
* - Ensure module loading order
*
* @param mode - Import map mode
* - 'inline': Inline mode, returns HTML script tag
* - 'js': JS file mode, returns information with file path
* @returns Returns import map related information
* - src: URL of the JS file (only in js mode)
* - filepath: Local path of the JS file (only in js mode)
* - code: HTML script tag content
* @throws {NotReadyError} Throws error when calling this method if the framework instance is not initialized
*
* @example
* ```ts
* // Used in entry.node.ts
* async server(esmx) {
* const server = express();
* server.use(esmx.middleware);
*
* server.get('*', async (req, res) => {
* // Use JS file mode
* const result = await esmx.render({
* importmapMode: 'js',
* params: { url: req.url }
* });
* res.send(result.html);
* });
*
* // Or use inline mode
* 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;
}>;
/**
* Get the list of static import paths for a module.
*
* @param env - Build target ('client' | 'server')
* @param specifier - Module specifier
* @returns Returns the list of static import paths, returns null if not found
* @throws {NotReadyError} Throws error when calling this method if the framework instance is not initialized
*
* @example
* ```ts
* // Get static import paths for client entry module
* const paths = await esmx.getStaticImportPaths(
* 'client',
* `your-app-name/src/entry.client`
* );
* ```
*/
getStaticImportPaths(env: BuildEnvironment, specifier: string): Promise<readonly string[] | null>;
}