rspack-plugin-mock
Version:
inject api mock server to development server
558 lines (510 loc) • 18 kB
JavaScript
;Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;
var _chunkHTVJXQRMcjs = require('./chunk-HTVJXQRM.cjs');
// src/core/build.ts
var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs);
var _promises = require('fs/promises'); var _promises2 = _interopRequireDefault(_promises);
var _path = require('path'); var _path2 = _interopRequireDefault(_path);
var _process = require('process'); var _process2 = _interopRequireDefault(_process);
var _utils = require('@pengzhanbo/utils');
var _pluginutils = require('@rollup/pluginutils');
var _fastglob = require('fast-glob'); var _fastglob2 = _interopRequireDefault(_fastglob);
var _picocolors = require('picocolors'); var _picocolors2 = _interopRequireDefault(_picocolors);
// src/core/createRspackCompiler.ts
var _module = require('module');
var _core = require('@rspack/core'); var rspackCore = _interopRequireWildcard(_core);
var _iscoremodule = require('is-core-module'); var _iscoremodule2 = _interopRequireDefault(_iscoremodule);
var require2 = _module.createRequire.call(void 0, import.meta.url);
function createCompiler(options, callback) {
const rspackOptions = resolveRspackOptions(options);
const isWatch = rspackOptions.watch === true;
async function handler(err, stats) {
const name = "[rspack:mock]";
const logError = (...args) => {
if (stats) {
stats.compilation.getLogger(name).error(...args);
} else {
console.error(_picocolors2.default.red(name), ...args);
}
};
if (err) {
logError(err.stack || err);
if ("details" in err) {
logError(err.details);
}
return;
}
if (_optionalChain([stats, 'optionalAccess', _ => _.hasErrors, 'call', _2 => _2()])) {
const info = stats.toJson();
logError(info.errors);
}
const code = _chunkHTVJXQRMcjs.vfs.readFileSync("/output.js", "utf-8");
const externals = [];
if (!isWatch) {
const modules = _optionalChain([stats, 'optionalAccess', _3 => _3.toJson, 'call', _4 => _4(), 'access', _5 => _5.modules]) || [];
const aliasList = Object.keys(options.alias || {}).map((key) => key.replace(/\$$/g, ""));
for (const { name: name2 } of modules) {
if (_optionalChain([name2, 'optionalAccess', _6 => _6.startsWith, 'call', _7 => _7("external")])) {
const packageName = normalizePackageName(name2);
if (!_iscoremodule2.default.call(void 0, packageName) && !aliasList.includes(packageName))
externals.push(normalizePackageName(name2));
}
}
}
await callback({ code, externals });
}
const compiler = rspackCore.rspack(rspackOptions, isWatch ? handler : void 0);
if (compiler)
compiler.outputFileSystem = _chunkHTVJXQRMcjs.vfs;
if (!isWatch) {
_optionalChain([compiler, 'optionalAccess', _8 => _8.run, 'call', _9 => _9(async (...args) => {
await handler(...args);
compiler.close(() => {
});
})]);
}
return compiler;
}
function transformWithRspack(options) {
return new Promise((resolve) => {
createCompiler({ ...options, watch: false }, (result) => {
resolve(result);
});
});
}
function normalizePackageName(name) {
const filepath = name.replace("external ", "").slice(1, -1);
const [scope, packageName] = filepath.split("/");
if (filepath[0] === "@") {
return `${scope}/${packageName}`;
}
return scope;
}
function resolveRspackOptions({
cwd,
isEsm = true,
entryFile,
plugins,
alias,
watch = false
}) {
const targets = ["node >= 18.0.0"];
if (alias && "@swc/helpers" in alias) {
delete alias["@swc/helpers"];
}
return {
mode: "production",
context: cwd,
entry: entryFile,
watch,
target: "node18.0",
externalsType: isEsm ? "module" : "commonjs2",
resolve: {
alias,
extensions: [".js", ".ts", ".cjs", ".mjs", ".json5", ".json"]
},
plugins,
output: {
library: { type: !isEsm ? "commonjs2" : "module" },
filename: "output.js",
path: "/"
},
experiments: { outputModule: isEsm },
optimization: { minimize: !watch },
module: {
rules: [
{
test: /\.json5?$/,
loader: require2.resolve("#json5-loader"),
type: "javascript/auto"
},
{
test: /\.[cm]?js$/,
use: [
{
loader: "builtin:swc-loader",
options: {
jsc: { parser: { syntax: "ecmascript" } },
env: { targets }
}
}
]
},
{
test: /\.[cm]?ts$/,
use: [
{
loader: "builtin:swc-loader",
options: {
jsc: { parser: { syntax: "typescript" } },
env: { targets }
}
}
]
}
]
}
};
}
// src/core/build.ts
async function buildMockServer(options, outputDir) {
const entryFile = _path2.default.resolve(_process2.default.cwd(), "node_modules/.cache/mock-server/mock-server.ts");
const mockFileList = await getMockFileList(options);
await writeMockEntryFile(entryFile, mockFileList, options.cwd);
const { code, externals } = await transformWithRspack({
entryFile,
cwd: options.cwd,
plugins: options.plugins,
alias: options.alias
});
await _promises2.default.unlink(entryFile);
const outputList = [
{ filename: "mock-data.js", source: code },
{ filename: "index.js", source: generatorServerEntryCode(options) },
{ filename: "package.json", source: generatePackageJson(options, externals) }
];
const dist = _path2.default.resolve(outputDir, options.build.dist);
options.logger.info(
`${_picocolors2.default.green("\u2713")} generate mock server in ${_picocolors2.default.cyan(_path2.default.relative(_process2.default.cwd(), dist))}`
);
if (!_fs2.default.existsSync(dist)) {
await _promises2.default.mkdir(dist, { recursive: true });
}
for (const { filename, source } of outputList) {
await _promises2.default.writeFile(_path2.default.join(dist, filename), source, "utf8");
const sourceSize = (source.length / 1024).toFixed(2);
const space = filename.length < 24 ? " ".repeat(24 - filename.length) : "";
options.logger.info(` ${_picocolors2.default.green(filename)}${space}${_picocolors2.default.bold(_picocolors2.default.dim(`${sourceSize} kB`))}`);
}
}
function generatePackageJson(options, externals) {
const deps = getHostDependencies(options.cwd);
const { name, version } = getPluginPackageInfo();
const exclude = [name, "connect", "cors"];
const mockPkg = {
name: "mock-server",
type: "module",
scripts: {
start: "node index.js"
},
dependencies: {
connect: "^3.7.0",
[name]: `^${version}`,
cors: "^2.8.5"
}
};
externals.filter((dep) => !exclude.includes(dep)).forEach((dep) => {
mockPkg.dependencies[dep] = deps[dep] || "latest";
});
return JSON.stringify(mockPkg, null, 2);
}
function generatorServerEntryCode({
proxies,
wsPrefix,
cookiesOptions,
bodyParserOptions,
priority,
build
}) {
const { serverPort, log } = build;
return `import { createServer } from 'node:http';
import connect from 'connect';
import corsMiddleware from 'cors';
import {
baseMiddleware,
createLogger,
mockWebSocket,
transformMockData,
transformRawData
} from 'rspack-plugin-mock/server';
import rawData from './mock-data.js';
const app = connect();
const server = createServer(app);
const logger = createLogger('mock-server', '${log}');
const proxies = ${JSON.stringify(proxies)};
const wsProxies = ${JSON.stringify(_utils.toArray.call(void 0, wsPrefix))};
const cookiesOptions = ${JSON.stringify(cookiesOptions)};
const bodyParserOptions = ${JSON.stringify(bodyParserOptions)};
const priority = ${JSON.stringify(priority)};
const mockConfig = {
mockData: transformMockData(transformRawData(rawData)),
on: () => {},
};
mockWebSocket(mockConfig, server, { wsProxies, cookiesOptions, logger });
app.use(corsMiddleware());
app.use(baseMiddleware(mockConfig, {
formidableOptions: { multiples: true },
proxies,
priority,
cookiesOptions,
bodyParserOptions,
logger,
}));
server.listen(${serverPort});
console.log('listen: http://localhost:${serverPort}');
`;
}
async function getMockFileList({ cwd, include, exclude }) {
const filter = _pluginutils.createFilter.call(void 0, include, exclude, { resolve: false });
return await _fastglob2.default.call(void 0, include, { cwd }).then((files) => files.filter(filter));
}
async function writeMockEntryFile(entryFile, files, cwd) {
const importers = [];
const exporters = [];
for (const [index, filepath] of files.entries()) {
const file = _chunkHTVJXQRMcjs.normalizePath.call(void 0, _path2.default.join(cwd, filepath));
importers.push(`import * as m${index} from '${file}'`);
exporters.push(`[m${index}, '${filepath}']`);
}
const code = `${importers.join("\n")}
export default [
${exporters.join(",\n ")}
]`;
const dirname = _path2.default.dirname(entryFile);
if (!_fs2.default.existsSync(dirname)) {
await _promises2.default.mkdir(dirname, { recursive: true });
}
await _promises2.default.writeFile(entryFile, code, "utf8");
}
function getPluginPackageInfo() {
let pkg = {};
try {
const filepath = _path2.default.join(_chunkHTVJXQRMcjs.packageDir, "../package.json");
if (_fs2.default.existsSync(filepath)) {
pkg = JSON.parse(_fs2.default.readFileSync(filepath, "utf8"));
}
} catch (e2) {
}
return {
name: pkg.name || "rspack-plugin-mock",
version: pkg.version || "latest"
};
}
function getHostDependencies(context) {
let pkg = {};
try {
const content = _chunkHTVJXQRMcjs.lookupFile.call(void 0, context, ["package.json"]);
if (content)
pkg = JSON.parse(content);
} catch (e3) {
}
return { ...pkg.dependencies, ...pkg.devDependencies };
}
// src/core/mockCompiler.ts
var _events = require('events'); var _events2 = _interopRequireDefault(_events);
var _chokidar = require('chokidar'); var _chokidar2 = _interopRequireDefault(_chokidar);
// src/core/loadFromCode.ts
var _url = require('url');
async function loadFromCode({
filepath,
code,
isESM,
cwd
}) {
filepath = _path2.default.resolve(cwd, filepath);
const ext = isESM ? ".mjs" : ".cjs";
const filepathTmp = `${filepath}.timestamp-${Date.now()}${ext}`;
const file = _url.pathToFileURL.call(void 0, filepathTmp).toString();
await _fs.promises.writeFile(filepathTmp, code, "utf8");
try {
const mod = await Promise.resolve().then(() => _interopRequireWildcard(require(file)));
return mod.default || mod;
} finally {
try {
_fs2.default.unlinkSync(filepathTmp);
} catch (e4) {
}
}
}
// src/core/mockCompiler.ts
function createMockCompiler(options) {
return new MockCompiler(options);
}
var MockCompiler = (_class = class extends _events2.default {
constructor(options) {
super();_class.prototype.__init.call(this);_class.prototype.__init2.call(this);;
this.options = options;
this.cwd = options.cwd || _process2.default.cwd();
const { include, exclude } = this.options;
this.fileFilter = _pluginutils.createFilter.call(void 0, include, exclude, { resolve: false });
try {
const pkg = _chunkHTVJXQRMcjs.lookupFile.call(void 0, this.cwd, ["package.json"]);
this.moduleType = !!pkg && JSON.parse(pkg).type === "module" ? "esm" : "cjs";
} catch (e5) {
}
this.entryFile = _path2.default.resolve(_process2.default.cwd(), "node_modules/.cache/mock-server/mock-server.ts");
}
__init() {this.moduleType = "cjs"}
__init2() {this._mockData = {}}
get mockData() {
return this._mockData;
}
async run() {
await this.updateMockEntry();
this.watchMockFiles();
const { plugins, alias } = this.options;
const options = {
isEsm: this.moduleType === "esm",
cwd: this.cwd,
plugins,
entryFile: this.entryFile,
alias,
watch: true
};
this.compiler = createCompiler(options, async ({ code }) => {
try {
const result = await loadFromCode({
filepath: "mock.bundle.js",
code,
isESM: this.moduleType === "esm",
cwd: this.cwd
});
this._mockData = _chunkHTVJXQRMcjs.transformMockData.call(void 0, _chunkHTVJXQRMcjs.transformRawData.call(void 0, result));
this.emit("update", this.watchInfo || {});
} catch (e) {
this.options.logger.error(e.stack || e.message);
}
});
}
close() {
this.mockWatcher.close();
_optionalChain([this, 'access', _10 => _10.compiler, 'optionalAccess', _11 => _11.close, 'call', _12 => _12(() => {
})]);
this.emit("close");
}
updateAlias(alias) {
this.options.alias = {
...this.options.alias,
...alias
};
}
async updateMockEntry() {
const files = await this.getMockFiles();
await writeMockEntryFile(this.entryFile, files, this.cwd);
}
async getMockFiles() {
const { include } = this.options;
const files = await _fastglob2.default.call(void 0, include, { cwd: this.cwd });
return files.filter(this.fileFilter);
}
watchMockFiles() {
const { include } = this.options;
const [firstGlob, ...otherGlob] = _utils.toArray.call(void 0, include);
const watcher = this.mockWatcher = _chokidar2.default.watch(firstGlob, {
ignoreInitial: true,
cwd: this.cwd
});
if (otherGlob.length > 0)
otherGlob.forEach((glob) => watcher.add(glob));
watcher.on("add", (filepath) => {
if (this.fileFilter(filepath)) {
this.watchInfo = { filepath, type: "add" };
this.updateMockEntry();
}
});
watcher.on("change", (filepath) => {
if (this.fileFilter(filepath)) {
this.watchInfo = { filepath, type: "change" };
}
});
watcher.on("unlink", async (filepath) => {
this.watchInfo = { filepath, type: "unlink" };
this.updateMockEntry();
});
}
}, _class);
// src/core/mockMiddleware.ts
var _cors = require('cors'); var _cors2 = _interopRequireDefault(_cors);
var _pathtoregexp = require('path-to-regexp');
function createMockMiddleware(compiler, options) {
function mockMiddleware(middlewares, reload) {
middlewares.unshift(_chunkHTVJXQRMcjs.baseMiddleware.call(void 0, compiler, options));
const corsMiddleware = createCorsMiddleware(compiler, options);
if (corsMiddleware) {
middlewares.unshift(corsMiddleware);
}
if (options.reload) {
compiler.on("update", () => _optionalChain([reload, 'optionalCall', _13 => _13()]));
}
return middlewares;
}
return mockMiddleware;
}
function createCorsMiddleware(compiler, options) {
let corsOptions = {};
const enabled = options.cors !== false;
if (enabled) {
corsOptions = {
...corsOptions,
...typeof options.cors === "boolean" ? {} : options.cors
};
}
const proxies = options.proxies;
return !enabled ? void 0 : function(req, res, next) {
const { pathname } = _chunkHTVJXQRMcjs.urlParse.call(void 0, req.url);
if (!pathname || proxies.length === 0 || !proxies.some(
(context) => _chunkHTVJXQRMcjs.doesProxyContextMatchUrl.call(void 0, context, req.url, req)
)) {
return next();
}
const mockData = compiler.mockData;
const mockUrl = Object.keys(mockData).find(
(key) => _pathtoregexp.pathToRegexp.call(void 0, key).test(pathname)
);
if (!mockUrl)
return next();
_cors2.default.call(void 0, corsOptions)(req, res, next);
};
}
// src/core/resolvePluginOptions.ts
function resolvePluginOptions({
prefix = [],
wsPrefix = [],
cwd,
include = ["mock/**/*.mock.{js,ts,cjs,mjs,json,json5}"],
exclude = ["**/node_modules/**", "**/.vscode/**", "**/.git/**"],
reload = false,
log = "info",
cors: cors2 = true,
formidableOptions = {},
build = false,
cookiesOptions = {},
bodyParserOptions = {},
priority = {}
} = {}, { alias, context, plugins, proxies }) {
const logger = _chunkHTVJXQRMcjs.createLogger.call(void 0,
"rspack:mock",
_utils.isBoolean.call(void 0, log) ? log ? "info" : "error" : log
);
prefix = _utils.toArray.call(void 0, prefix);
return {
prefix,
wsPrefix,
cwd: cwd || context || _process2.default.cwd(),
include,
exclude,
reload,
cors: cors2,
cookiesOptions,
log,
formidableOptions: {
multiples: true,
...formidableOptions
},
bodyParserOptions,
priority,
build: build ? Object.assign(
{
serverPort: 8080,
dist: "mockServer",
log: "error"
},
typeof build === "object" ? build : {}
) : false,
alias,
plugins,
proxies: [...proxies, ...prefix],
wsProxies: _utils.toArray.call(void 0, wsPrefix),
logger
};
}
exports.buildMockServer = buildMockServer; exports.createMockCompiler = createMockCompiler; exports.MockCompiler = MockCompiler; exports.createMockMiddleware = createMockMiddleware; exports.resolvePluginOptions = resolvePluginOptions;