跳到主要内容

官方模拟版

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 的插件机制,实现插件容器和插件上下文对象。