rspack-plugin-mock
Version:
inject api mock server to development server
203 lines (201 loc) • 6.65 kB
JavaScript
import {
buildMockServer,
createMockCompiler,
createMockMiddleware,
resolvePluginOptions
} from "./chunk-HV5L72CY.js";
import {
mockWebSocket,
rewriteRequest
} from "./chunk-OGWV5ZGG.js";
// src/rsbuild.ts
import { createServer } from "node:http";
import path from "node:path";
import process from "node:process";
import { isArray, toArray } from "@pengzhanbo/utils";
import rspack from "@rspack/core";
import color from "picocolors";
import { getPortPromise } from "portfinder";
function pluginMockServer(options = {}) {
return {
name: "plugin-mock-server",
setup(api) {
const rsbuildConfig = api.getRsbuildConfig();
const resolvedOptions = resolvePluginOptions(options, {
proxies: resolveConfigProxies(rsbuildConfig),
alias: {},
context: api.context.rootPath,
plugins: [new rspack.DefinePlugin(rsbuildConfig.source?.define || {})]
});
if (process.env.NODE_ENV === "production") {
if (resolvedOptions.build) {
api.onAfterBuild(async () => {
const config = api.getNormalizedConfig();
await buildMockServer(
resolvedOptions,
path.resolve(process.cwd(), config.output.distPath.root || "dist")
);
});
}
return;
}
const mockCompiler = createMockCompiler(resolvedOptions);
api.modifyRsbuildConfig((config) => {
updateServerProxyConfigByHttpMock(config);
const mockMiddleware = createMockMiddleware(mockCompiler, resolvedOptions);
config.dev ??= {};
config.dev.setupMiddlewares ??= [];
config.dev.setupMiddlewares.push((middlewares, server2) => {
mockMiddleware(middlewares, () => server2.sockWrite("static-changed"));
});
});
let port = 3079;
const shouldMockWs = toArray(resolvedOptions.wsPrefix).length > 0;
if (shouldMockWs) {
api.modifyRsbuildConfig(async (config) => {
const defaultPort = (config.server?.port || port) + 1;
port = await getPortPromise({ port: defaultPort });
updateServerProxyConfigByWSMock(config, options.wsPrefix || [], port);
});
}
let server;
function startMockServer() {
mockCompiler.run();
if (shouldMockWs) {
server = createServer();
mockWebSocket(mockCompiler, server, resolvedOptions);
server.listen(port);
}
}
function close() {
mockCompiler.close();
server?.close();
}
api.onAfterCreateCompiler(({ compiler }) => {
if ("compilers" in compiler) {
compiler.compilers.forEach((compiler2) => {
mockCompiler.updateAlias(compiler2.options.resolve?.alias || {});
});
} else {
mockCompiler.updateAlias(compiler.options.resolve?.alias || {});
}
});
api.onAfterStartDevServer(startMockServer);
api.onAfterStartProdServer(startMockServer);
api.onExit(close);
}
};
}
function onProxyError(err, _req, res) {
console.error(color.red(err?.stack || err.message));
res.statusCode = 500;
res.end();
}
function updateServerProxyConfigByHttpMock(config) {
if (!config.server?.proxy)
return;
if (isArray(config.server.proxy)) {
config.server.proxy = config.server.proxy.map((item) => {
if (typeof item !== "function" && !item.ws) {
const onProxyReq = item.onProxyReq;
const onError = item.onError;
return {
...item,
onError: onError || onProxyError,
onProxyReq: (proxyReq, req, ...args) => {
onProxyReq?.(proxyReq, req, ...args);
rewriteRequest(proxyReq, req);
}
};
}
return item;
});
} else if ("target" in config.server.proxy) {
const onProxyReq = config.server.proxy.onProxyReq;
config.server.proxy.onProxyReq = (proxyReq, req, ...args) => {
onProxyReq?.(proxyReq, req, ...args);
rewriteRequest(proxyReq, req);
};
config.server.proxy.onError ??= onProxyError;
} else if (config.server.proxy) {
const proxy = config.server.proxy;
Object.keys(proxy).forEach((key) => {
const target = proxy[key];
const options = typeof target === "string" ? { target } : target;
if (options.ws)
return;
const { onProxyReq, onError, ...rest } = options;
proxy[key] = {
...rest,
onProxyReq: (proxyReq, req, ...args) => {
onProxyReq?.(proxyReq, req, ...args);
rewriteRequest(proxyReq, req);
},
onError: onError || onProxyError
};
});
}
}
function updateServerProxyConfigByWSMock(config, wsPrefix, port) {
config.server ??= {};
const proxy = config.server.proxy ??= {};
const wsTarget = `ws://localhost:${port}`;
const prefix = toArray(wsPrefix);
const has = (context) => typeof context === "string" && prefix.includes(context);
const used = /* @__PURE__ */ new Set();
function updateProxy(item) {
if (isArray(item.context)) {
item.context = item.context.filter(has);
} else if (has(item.context)) {
used.add(item.context);
item.target = wsTarget;
}
}
if (isArray(proxy)) {
for (const item of proxy) {
if (typeof item !== "function" && item.context && item.ws) {
updateProxy(item);
}
}
prefix.filter((context) => !used.has(context)).forEach((context) => proxy.push({ context, target: wsTarget }));
} else if ("target" in proxy) {
if (proxy.ws) {
updateProxy(proxy);
const list = config.server.proxy = [proxy];
prefix.filter((context) => !used.has(context)).forEach((context) => list.push({ context, target: wsTarget }));
}
} else {
Object.entries(proxy).forEach(([, opt]) => {
if (typeof opt !== "string" && opt.ws) {
updateProxy(opt);
}
});
prefix.filter((context) => !used.has(context)).forEach((context) => {
proxy[context] = { target: wsTarget, ws: true };
});
}
}
function resolveConfigProxies(config) {
config.server ??= {};
const proxy = config.server.proxy ??= {};
const proxies = [];
if (isArray(proxy)) {
for (const item of proxy) {
if (typeof item !== "function" && item.context && !item.ws) {
proxies.push(...toArray(item.context));
}
}
} else if ("target" in proxy) {
if (!proxy.ws)
proxies.push(...toArray(proxy.context));
} else {
Object.entries(proxy).forEach(([context, opt]) => {
if (typeof opt === "string" || !opt.ws)
proxies.push(context);
});
}
return proxies;
}
export {
pluginMockServer
};