UNPKG

rspack-plugin-mock

Version:
558 lines (510 loc) 18 kB
"use strict";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;