官方模拟版
2023年06月11日
一、认识
备注: 在完成了依赖预构建的功能之后,我们开始搭建 Vite
的插件机制,实现插件容器和插件上下文对象。
1.1 增加类型
首先,你可以新建 src/node/pluginContainer.ts
文件,增加如下的类型定义:
import type {
LoadResult,
PartialResolvedId,
SourceDescription,
PluginContext as RollupPluginContext,
ResolvedId,
} from "rollup";
export interface PluginContainer {
resolveId(id: string, importer?: string): Promise<PartialResolvedId | null>;
load(id: string): Promise<LoadResult | null>;
transform(code: string, id: string): Promise<SourceDescription | null>;
}
另外,由于插件容器需要接收 Vite
插件作为初始化参数,因此我们需要提前声明插件的类型,你可以继续新建src/node/plugin.ts
来声明如下的插件类型:
import { LoadResult, PartialResolvedId, SourceDescription } from "rollup";
import { ServerContext } from "./server";
export type ServerHook = (
server: ServerContext
) => (() => void) | void | Promise<(() => void) | void>;
// 只实现以下这几个钩子
export interface Plugin {
name: string;
configureServer?: ServerHook;
resolveId?: (
id: string,
importer?: string
) => Promise<PartialResolvedId | null> | PartialResolvedId | null;
load?: (id: string) => Promise<LoadResult | null> | LoadResult | null;
transform?: (
code: string,
id: string
) => Promise<SourceDescription | null> | SourceDescription | null;
transformIndexHtml?: (raw: string) => Promise<string> | string;
}
对于其中的 ServerContext
,你暂时不用过于关心,只需要在server/index.ts
中简单声明一下类型即可:
// src/node/server/index.ts
// 增加如下类型声明
export interface ServerContext {}
1.2 实现 createPluginContainer
// src/node/pluginContainer.ts
export const createPluginContainer = (plugins: Plugin[]): PluginContainer => {
class Context implements RollupPluginContext {
async resolve(id: string, importer?: string) {
let out = await pluginContainer.resolveId(id, importer);
if (typeof out === 'string') out = { id: out };
return out as ResolvedId | null;
}
}
const pluginContainer: PluginContainer = {
async resolveId(id: string, importer?: string) {
const ctx = new Context() as any;
for (const plugin of plugins) {
if (plugin.resolveId) {
const newId = await plugin.resolveId.call(ctx as any, id, importer);
if (newId) {
id = typeof newId === 'string' ? newId : newId.id;
return { id };
}
}
}
return null;
},
async load(id) {
const ctx = new Context() as any;
for (const plugin of plugins) {
if (plugin.load) {
const result = await plugin.load.call(ctx, id);
if (result) {
return result;
}
}
}
return null;
},
async transform(code, id) {
const ctx = new Context() as any;
for (const plugin of plugins) {
if (plugin.transform) {
const result = await plugin.transform.call(ctx, code, id);
if (!result) continue;
if (typeof result === 'string') {
code = result;
} else if (result.code) {
code = result.code;
}
}
}
return { code };
}
};
return pluginContainer;
};
接着,我们来完善一下之前的服务器逻辑:
// src/node/server/index.ts
import connect from 'connect';
import { Plugin } from "../plugin";
import { blue, green } from 'picocolors';
import { resolvePlugins } from '../plugins';
import { optimize } from '../optimizer/index';
import { createPluginContainer, PluginContainer } from '../pluginContainer';
export interface ServerContext {
root: string;
pluginContainer: PluginContainer;
app: connect.Server;
plugins: Plugin[];
}
export async function startDevServer() {
const app = connect();
const root = process.cwd();
const startTime = Date.now();
const plugins = resolvePlugins();
const pluginContainer = createPluginContainer(plugins);
const serverContext: ServerContext = {
root: process.cwd(),
app,
pluginContainer,
plugins
};
for (const plugin of plugins) {
if (plugin.configureServer) {
await plugin.configureServer(serverContext);
}
}
app.listen(3000, async () => {
await optimize(root);
console.log(
green('🚀 No-Bundle 服务已经成功启动!'),
`耗时: ${Date.now() - startTime}ms`
);
console.log(`> 本地访问路径: ${blue('http://localhost:3000')}`);
});
}
1.3 增加 resolvePlugins 入口
其中 resolvePlugins
方法我们还未定义,你可以新建 src/node/plugins/index.ts
文件,内容如下:
import { Plugin } from "../plugin";
export function resolvePlugins(): Plugin[] {
// 下一部分会逐个补充插件逻辑
return [];
}
二、测试
备注: 在完成了依赖预构建的功能之后,我们开始搭建 Vite
的插件机制,实现插件容器和插件上下文对象。