Builder Instance
本章节描述了 Builder 实例对象上所有的属性和方法。
builder.context
builder.context
是一个只读对象,提供一些上下文信息。
builder.context.entry
构建入口对象,对应调用 createBuilder
时传入的 entry
选项。
type BuilderEntry = Record<string, string | string[]>;
builder.context.target
构建产物类型,对应调用 createBuilder
时传入的 target
选项。
type BuilderTarget = 'web' | 'node' | 'modern-web' | 'web-worker';
type Context = {
target: BuilderTarget | BuilderTarget[];
};
builder.context.rootPath
当前执行构建的根路径,对应调用 createBuilder
时传入的 cwd
选项。
builder.context.srcPath
src 目录的绝对路径。
builder.context.distPath
构建产物输出目录的绝对路径,对应 BuilderConfig
中的 output.distPath.root
配置项。
builder.context.cachePath
构建过程中生成的缓存文件所在的绝对路径。
builder.context.configPath
框架配置文件的绝对路径,对应调用 createBuilder
时传入的 configPath
选项。
type ConfigPath = string | undefined;
builder.context.tsconfigPath
tsconfig.json 文件的绝对路径,若项目中不存在 tsconfig.json 文件,则为 undefined
。
type TsconfigPath = string | undefined;
builder.context.framework
框架的英文名称,唯一标识符,默认值为 'modern.js'
。
type Framework = string | undefined;
builder.context.devServer
Dev Server 相关信息,包含了当前 Dev Server 的 hostname 和端口号。
type DevServer = {
hostname: string;
port: number;
};
builder.context.bundlerType
当前执行构建的构建工具类型。
type bundlerType = 'webpack' | 'rspack';
builder.build
调用 build 方法时,会执行一次生产环境构建。
type BuildOptions = {
mode?: 'development' | 'production';
watch?: boolean;
// 自定义 Compiler 对象
compiler?: Compiler | MultiCompiler;
};
function Build(options?: BuildOptions): Promise<void>;
开发环境构建
如果需要执行一次开发环境构建,可以将 mode
参数设置为 'development'
。
await builder.build({
mode: 'development',
});
监听文件变化
如果需要自动监听文件变化并重新执行构建,可以将 watch
参数设置为 true
。
await builder.build({
watch: true,
});
自定义 Compiler
个别情况下,你可能希望使用自定义的 compiler:
const compiler = webpack({
// ...
});
await builder.build({
compiler,
});
builder.startDevServer
启动本地 Dev Server,基于 Modern.js Dev Server 实现。
type StartDevServerOptions = {
// 是否输出 URL 信息,默认为 true
printURLs?: boolean | Function;
// 是否在端口被占用时抛出异常,默认为 false
strictPort?: boolean;
// 自定义 Compiler 对象
compiler?: Compiler | MultiCompiler;
// 透传与构建无关的 dev server 配置
serverOptions?: Partial<ModernDevServerOptions>;
// 是否在启动时静默获取端口号,默认为 false
getPortSliently?: boolean;
// 自定义日志输出对象
logger?: Logger;
};
type StartServerResult = {
urls: string[];
port: number;
server: Server;
};
function StartDevServer(
options?: StartDevServerOptions,
): Promise<StartServerResult>;
启动 Dev Server:
await builder.startDevServer();
成功启动 Dev Server 后,可以看到以下日志信息:
info Starting dev server...
> Local: http://localhost:8080
> Network: http://192.168.0.1:8080
startDevServer
会返回以下参数:
urls
:访问 Dev Server 的 URLs
port
实际监听的端口号
server
:Server 实例对象
const { urls, port, server } = await builder.startDevServer();
console.log(urls); // ['http://localhost:8080', 'http://192.168.0.1:8080']
console.log(port); // 8080
// 关闭 Dev Server
await server.close();
自定义 URL 输出
将 printURLs
设置为 false
可以禁用默认的 URL 输出,此时你可以输出自定义的日志内容。
await builder.startDevServer({
printURLs: false,
});
你也可以直接将 printURLs
配置为一个函数来修改 URL,比如给每个 URL 增加一个子路径:
await builder.startDevServer({
printURLs: urls => {
return urls.map(({ label, url }) => ({
label,
url: `${url}/path`,
}));
},
});
严格限制端口
当端口被占用时,Builder 会自动递增端口号,直至找到一个可用端口。
如果你希望在端口被占用时抛出异常,可以将 strictPort
设置为 true
。
await builder.startDevServer({
strictPort: true,
});
自定义 Compiler
个别情况下,你可能希望使用自定义的 compiler:
const compiler = webpack({
// ...
});
await builder.startDevServer({
compiler,
});
静默获取端口号
某些情况下,默认启动的端口号已经被占用,此时 Builder 会自动递增端口号,直至找到一个可用端口。这个过程会输出提示日志,如果你不希望这段日志,可以将 getPortSliently
设置为 true
。
await builder.startDevServer({
getPortSliently: true,
});
自定义日志输出对象
默认情况下,Builder 会使用 @modern-js/utils/logger
来输出日志,你可以通过 logger
参数来自定义日志输出对象。
const customLogger = {
// 你需要定义以下的方法
info(msg: string) {
console.log(msg);
},
error(msg: string) {
console.error(msg);
},
warn(msg: string) {
console.warn(msg);
},
success(msg: string) {
console.log(`✅ msg`);
},
debug(msg: string) {
if (process.env.DEBUG) {
console.log(msg);
}
},
log(msg: string) {
console.log(msg);
};
}
await builder.startDevServer({
logger: customLogger,
});
这样,Builder 会使用你自定义的日志输出对象来输出日志。
builder.serve
在本地启动 Server 来预览生产环境构建的产物,需要在 builder.build
方法之后执行。
type StartServerResult = {
urls: string[];
port: number;
server: Server;
};
function server(): Promise<StartServerResult>;
启动 Server:
serve
会返回以下参数:
urls
:访问 Server 的 URLs
port
实际监听的端口号
server
:Server 实例对象
const { urls, port, server } = await builder.serve();
console.log(urls); // ['http://localhost:8080', 'http://192.168.0.1:8080']
console.log(port); // 8080
// 关闭 Server
await server.close();
builder.createCompiler
创建一个 compiler 对象。
当 createBuilder
的 target
选项包含一个值时,返回值为 Compiler
;当 target
包含多个值时,返回值为 MultiCompiler
。
function CreateCompiler(): Promise<Compiler | MultiCompiler>;
const compiler = await builder.createCompiler();
大部分场景下,不需要使用该 API,除非需要进行自定义 Dev Server 等高级操作。
builder.addPlugins
注册一个或多个 builder 插件,可以被多次调用。
该方法需要在开始编译前调用,如果在开始编译之后调用,则不会影响编译结果。
type AddPluginsOptions = { before?: string } | { after?: string };
function AddPlugins(
plugins: BuilderPlugins[],
options?: AddPluginsOptions,
): Promise<void>;
builder.addPlugins([builderPluginFoo(), builderPluginBar()]);
// 在 bar 插件之前插入
builder.addPlugins([builderPluginFoo()], { before: 'bar' });
// 在 bar 插件之后插入
builder.addPlugins([builderPluginFoo()], { after: 'bar' });
builder.removePlugins
移除一个或多个 builder 插件,可以被多次调用。
该方法需要在开始编译前调用,如果在开始编译之后调用,则不会影响编译结果。
function RemovePlugins(pluginNames: string[]): void;
// 添加插件
const pluginFoo = builderPluginFoo();
builder.addPlugins(pluginFoo);
// 移除插件
builder.removePlugins([pluginFoo.name]);
builder.isPluginExists
判断某个插件是否已经被注册。
function IsPluginExists(pluginName: string): boolean;
builder.addPlugins([builderPluginFoo()]);
builder.isPluginExists(builderPluginFoo().name); // true
builder.inspectConfig
查看 Builder 内部最终生成的 builder 配置和 bundler 配置。
TIP
inspectConfig
方法不支持与 startDevServer
/ build
方法同时使用。
当你需要在构建过程中查看完整的 builder 和 bundler 配置时,可以使用 调试模式,也可以通过 onBeforeBuild
、onBeforeCreateCompile
等钩子函数来获取。
type InspectConfigOptions = {
// 查看指定环境下的配置,默认为 "development",可以设置为 "production"
env?: BuilderMode;
// 是否开启冗余模式,展示配置中函数的完整内容,默认为 `false`
verbose?: boolean;
// 指定输出路径,默认为 `output.distPath.root` 配置的值
outputPath?: string;
// 是否将结果写入到磁盘中,默认为 `false`
writeToDisk?: boolean;
};
async function InspectConfig(options?: InspectConfigOptions): Promise<{
builderConfig: string;
bundlerConfigs: string[];
origin: {
builderConfig: BuilderConfig;
bundlerConfigs: BundlerConfigs[];
};
}>;
拿到字符串格式的 Config 内容:
const { builderConfig, bundlerConfigs } = await builder.inspectConfig();
console.log(builderConfig, bundlerConfigs);
直接将配置内容写入到磁盘上:
await builder.inspectConfig({
writeToDisk: true,
});
builder.onBeforeCreateCompiler
onBeforeCreateCompiler
是在创建底层 Compiler 实例前触发的回调函数,当你执行 builder.startDevServer
、builder.build
或 builder.createCompiler
时,都会调用此钩子。
你可以通过 bundlerConfigs
参数获取到底层打包工具的最终配置数组:
- 如果当前打包工具为 webpack,则获取到的是 webpack 配置数组。
- 如果当前打包工具为 Rspack,则获取到的是 Rspack 配置数组。
- 配置数组中可能包含一份或多份配置,这取决于你是否开启了 SSR 等功能。
你可以通过 bundlerConfigs
参数获取到底层打包工具的最终配置对象。
function OnBeforeCreateCompiler(
callback: (params: {
bundlerConfigs: WebpackConfig[] | RspackConfig[];
}) => Promise<void> | void,
): void;
builder.onBeforeCreateCompiler(({ bundlerConfigs }) => {
console.log('the bundler config is ', bundlerConfigs);
});
builder.onAfterCreateCompiler
onAfterCreateCompiler
是在创建 Compiler 实例后、执行构建前触发的回调函数,当你执行 builder.startDevServer
、builder.build
或 builder.createCompiler
时,都会调用此钩子。
你可以通过 compiler
参数获取到 Compiler 实例对象:
function OnAfterCreateCompiler(callback: (params: {
compiler: Compiler | MultiCompiler;
}) => Promise<void> | void;): void;
builder.onAfterCreateCompiler(({ compiler }) => {
console.log('the compiler is ', compiler);
});
builder.onBeforeBuild
onBeforeBuild
是在执行生产环境构建前触发的回调函数,你可以通过 bundlerConfigs
参数获取到底层打包工具的最终配置数组:
-
如果当前打包工具为 webpack,则获取到的是 webpack 配置数组。
-
如果当前打包工具为 Rspack,则获取到的是 Rspack 配置数组。
-
配置数组中可能包含一份或多份配置,这取决于 Builder 当前 target 配置的值。
-
类型
function OnBeforeBuild(
callback: (params: {
bundlerConfigs?: WebpackConfig[] | RspackConfig[];
}) => Promise<void> | void,
): void;
builder.onBeforeBuild(({ bundlerConfigs }) => {
console.log('the bundler config is ', bundlerConfigs);
});
builder.onAfterBuild
onAfterBuild
是在执行生产环境构建后触发的回调函数,你可以通过 stats
参数获取到构建结果信息:
function OnAfterBuild(
callback: (params: { stats?: Stats | MultiStats }) => Promise<void> | void,
): void;
builder.onAfterBuild(({ stats }) => {
console.log(stats?.toJson());
});
builder.onBeforeStartDevServer
在启动开发服务器前调用。
function OnBeforeStartDevServer(callback: () => Promise<void> | void): void;
builder.onBeforeStartDevServer(() => {
console.log('before start!');
});
builder.onAfterStartDevServer
在启动开发服务器后调用,你可以通过 port
参数获得开发服务器监听的端口号。
function OnAfterStartDevServer(
callback: (params: { port: number }) => Promise<void> | void,
): void;
builder.onAfterStartDevServer(({ port }) => {
console.log('this port is: ', port);
});
builder.onDevCompileDone
在每次开发环境构建结束后调用,你可以通过 isFirstCompile
来判断是否为首次构建。
function OnDevCompileDone(
callback: (params: { isFirstCompile: boolean }) => Promise<void> | void,
): void;
builder.onDevCompileDone(({ isFirstCompile }) => {
if (isFirstCompile) {
console.log('first compile!');
} else {
console.log('re-compile!');
}
});
builder.onExit
在进程即将退出时调用,这个钩子只能执行同步代码。
function OnExit(callback: () => void): void;
builder.onExit(() => {
console.log('exit!');
});
builder.getBuilderConfig
获取 Builder 配置,该方法必须在 modifyBuilderConfig
钩子执行完成后才能被调用。
function GetBuilderConfig(): Readonly<BuilderConfig>;
builder.onBeforeBuild(() => {
const config = api.getBuilderConfig();
console.log(config.html?.title);
});
builder.getNormalizedConfig
获取归一化后的 Builder 配置,该方法必须在 modifyBuilderConfig
钩子执行完成后才能被调用。
相较于 getBuilderConfig
方法,该方法返回的配置经过了归一化处理,配置的类型定义会得到收敛,比如 config.html
的 undefined
类型将被移除。
推荐优先使用该方法获取配置。
function GetNormalizedConfig(): Readonly<NormalizedConfig>;
builder.onBeforeBuild(() => {
const config = api.getNormalizedConfig();
console.log(config.html.title);
});
builder.getHTMLPaths
获取所有 HTML 产物的路径信息。
该方法会返回一个对象,对象的 key 为 entry 名称,value 为 HTML 文件在产物目录下的相对路径。
function GetHTMLPaths(): Record<string, string>;
builder.onBeforeBuild(() => {
const htmlPaths = api.getHTMLPaths();
console.log(htmlPaths); // { main: 'html/main/index.html' };
});