index.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var __defProp = Object.defineProperty;
  2. var __hasOwnProp = Object.prototype.hasOwnProperty;
  3. var __getOwnPropSymbols = Object.getOwnPropertySymbols;
  4. var __propIsEnum = Object.prototype.propertyIsEnumerable;
  5. var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {enumerable: true, configurable: true, writable: true, value}) : obj[key] = value;
  6. var __assign = (a, b) => {
  7. for (var prop in b || (b = {}))
  8. if (__hasOwnProp.call(b, prop))
  9. __defNormalProp(a, prop, b[prop]);
  10. if (__getOwnPropSymbols)
  11. for (var prop of __getOwnPropSymbols(b)) {
  12. if (__propIsEnum.call(b, prop))
  13. __defNormalProp(a, prop, b[prop]);
  14. }
  15. return a;
  16. };
  17. // src/index.ts
  18. var _path = require('path'); var _path2 = _interopRequireDefault(_path);
  19. // src/utils.ts
  20. var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs);
  21. var toString = Object.prototype.toString;
  22. function is(val, type) {
  23. return toString.call(val) === `[object ${type}]`;
  24. }
  25. function isFunction(val) {
  26. return is(val, "Function") || is(val, "AsyncFunction");
  27. }
  28. function isArray(val) {
  29. return val && Array.isArray(val);
  30. }
  31. function isRegExp(val) {
  32. return is(val, "RegExp");
  33. }
  34. function sleep(time) {
  35. return new Promise((resolve) => {
  36. setTimeout(() => {
  37. resolve("");
  38. }, time);
  39. });
  40. }
  41. function fileExists(f) {
  42. try {
  43. _fs2.default.accessSync(f, _fs2.default.constants.W_OK);
  44. return true;
  45. } catch (error) {
  46. return false;
  47. }
  48. }
  49. // src/index.ts
  50. var _vite = require('vite');
  51. // src/createMockServer.ts
  52. var _chokidar = require('chokidar'); var _chokidar2 = _interopRequireDefault(_chokidar);
  53. var _chalk = require('chalk'); var _chalk2 = _interopRequireDefault(_chalk);
  54. var _url = require('url'); var _url2 = _interopRequireDefault(_url);
  55. var _fastglob = require('fast-glob'); var _fastglob2 = _interopRequireDefault(_fastglob);
  56. var _mockjs = require('mockjs'); var _mockjs2 = _interopRequireDefault(_mockjs);
  57. var _esbuild = require('esbuild');
  58. var _pathtoregexp = require('path-to-regexp');
  59. var _module = require('module'); var _module2 = _interopRequireDefault(_module);
  60. var mockData = [];
  61. async function createMockServer(opt = {mockPath: "mock", configPath: "vite.mock.config"}) {
  62. opt = __assign({
  63. mockPath: "mock",
  64. watchFiles: true,
  65. supportTs: true,
  66. configPath: "vite.mock.config.ts",
  67. logger: true
  68. }, opt);
  69. if (mockData.length > 0)
  70. return;
  71. mockData = await getMockConfig(opt);
  72. await createWatch(opt);
  73. }
  74. async function requestMiddleware(opt) {
  75. const {logger = true} = opt;
  76. const middleware = async (req, res, next) => {
  77. let queryParams = {};
  78. if (req.url) {
  79. queryParams = _url2.default.parse(req.url, true);
  80. }
  81. const reqUrl = queryParams.pathname;
  82. const matchRequest = mockData.find((item) => {
  83. if (!reqUrl || !item || !item.url) {
  84. return false;
  85. }
  86. if (item.method && item.method.toUpperCase() !== req.method) {
  87. return false;
  88. }
  89. return _pathtoregexp.pathToRegexp.call(void 0, item.url).test(reqUrl);
  90. });
  91. if (matchRequest) {
  92. const isGet = req.method && req.method.toUpperCase() === "GET";
  93. const {response, rawResponse, timeout, statusCode, url: url2} = matchRequest;
  94. if (timeout) {
  95. await sleep(timeout);
  96. }
  97. const urlMatch = _pathtoregexp.match.call(void 0, url2, {decode: decodeURIComponent});
  98. let query = queryParams.query;
  99. if (reqUrl) {
  100. if (isGet && JSON.stringify(query) === "{}" || !isGet) {
  101. const params = urlMatch(reqUrl).params;
  102. if (JSON.stringify(params) !== "{}") {
  103. query = urlMatch(reqUrl).params || {};
  104. } else {
  105. query = queryParams.query || {};
  106. }
  107. }
  108. }
  109. if (isFunction(rawResponse)) {
  110. await rawResponse(req, res);
  111. } else {
  112. const body = await parseJson(req);
  113. const mockResponse = isFunction(response) ? response({url: req.url, body, query, headers: req.headers}) : response;
  114. res.setHeader("Content-Type", "application/json");
  115. res.statusCode = statusCode || 200;
  116. res.end(JSON.stringify(_mockjs2.default.mock(mockResponse)));
  117. }
  118. logger && loggerOutput("request invoke", req.url);
  119. return;
  120. }
  121. next();
  122. };
  123. return middleware;
  124. }
  125. function createWatch(opt) {
  126. const {configPath, logger, watchFiles} = opt;
  127. if (!watchFiles) {
  128. return;
  129. }
  130. const {absConfigPath, absMockPath} = getPath(opt);
  131. if (process.env.VITE_DISABLED_WATCH_MOCK === "true") {
  132. return;
  133. }
  134. const watchDir = [];
  135. const exitsConfigPath = _fs2.default.existsSync(absConfigPath);
  136. exitsConfigPath && configPath ? watchDir.push(absConfigPath) : watchDir.push(absMockPath);
  137. const watcher = _chokidar2.default.watch(watchDir, {
  138. ignoreInitial: true
  139. });
  140. watcher.on("all", async (event, file) => {
  141. logger && loggerOutput(`mock file ${event}`, file);
  142. mockData = await getMockConfig(opt);
  143. });
  144. }
  145. function cleanRequireCache(opt) {
  146. if (!require.cache) {
  147. return;
  148. }
  149. const {absConfigPath, absMockPath} = getPath(opt);
  150. Object.keys(require.cache).forEach((file) => {
  151. if (file === absConfigPath || file.indexOf(absMockPath) > -1) {
  152. delete require.cache[file];
  153. }
  154. });
  155. }
  156. function parseJson(req) {
  157. return new Promise((resolve) => {
  158. let body = "";
  159. let jsonStr = "";
  160. req.on("data", function(chunk) {
  161. body += chunk;
  162. });
  163. req.on("end", function() {
  164. try {
  165. jsonStr = JSON.parse(body);
  166. } catch (err) {
  167. jsonStr = "";
  168. }
  169. resolve(jsonStr);
  170. return;
  171. });
  172. });
  173. }
  174. async function getMockConfig(opt) {
  175. cleanRequireCache(opt);
  176. const {absConfigPath, absMockPath} = getPath(opt);
  177. const {ignore, configPath, logger} = opt;
  178. let ret = [];
  179. if (configPath && _fs2.default.existsSync(absConfigPath)) {
  180. logger && loggerOutput(`load mock data from`, absConfigPath);
  181. ret = await resolveModule(absConfigPath);
  182. return ret;
  183. }
  184. const mockFiles = _fastglob2.default.sync(`**/*.{ts,js}`, {
  185. cwd: absMockPath
  186. }).filter((item) => {
  187. if (!ignore) {
  188. return true;
  189. }
  190. if (isFunction(ignore)) {
  191. return ignore(item);
  192. }
  193. if (isRegExp(ignore)) {
  194. return !ignore.test(_path2.default.basename(item));
  195. }
  196. return true;
  197. });
  198. try {
  199. ret = [];
  200. const resolveModulePromiseList = [];
  201. for (let index = 0; index < mockFiles.length; index++) {
  202. const mockFile = mockFiles[index];
  203. resolveModulePromiseList.push(resolveModule(_path2.default.join(absMockPath, mockFile)));
  204. }
  205. const loadAllResult = await Promise.all(resolveModulePromiseList);
  206. for (const resultModule of loadAllResult) {
  207. let mod = resultModule;
  208. if (!isArray(mod)) {
  209. mod = [mod];
  210. }
  211. ret = [...ret, ...mod];
  212. }
  213. } catch (error) {
  214. loggerOutput(`mock reload error`, error);
  215. ret = [];
  216. }
  217. return ret;
  218. }
  219. async function resolveModule(p) {
  220. const result = await _esbuild.build.call(void 0, {
  221. entryPoints: [p],
  222. outfile: "out.js",
  223. write: false,
  224. platform: "node",
  225. bundle: true,
  226. format: "cjs",
  227. metafile: true,
  228. target: "es2015"
  229. });
  230. const {text} = result.outputFiles[0];
  231. return await loadConfigFromBundledFile(p, text);
  232. }
  233. function getPath(opt) {
  234. const {mockPath, configPath} = opt;
  235. const cwd = process.cwd();
  236. const absMockPath = _path2.default.join(cwd, mockPath || "");
  237. const absConfigPath = _path2.default.join(cwd, configPath || "");
  238. return {
  239. absMockPath,
  240. absConfigPath
  241. };
  242. }
  243. function loggerOutput(title, msg, type = "info") {
  244. const tag = type === "info" ? _chalk2.default.cyan.bold(`[vite:mock]`) : _chalk2.default.red.bold(`[vite:mock-server]`);
  245. return console.log(`${_chalk2.default.dim(new Date().toLocaleTimeString())} ${tag} ${_chalk2.default.green(title)} ${_chalk2.default.dim(msg)}`);
  246. }
  247. async function loadConfigFromBundledFile(fileName, bundledCode) {
  248. const extension = _path2.default.extname(fileName);
  249. const extensions = _module2.default.Module._extensions;
  250. let defaultLoader;
  251. const isJs = extension === ".js";
  252. if (isJs) {
  253. defaultLoader = extensions[extension];
  254. }
  255. extensions[extension] = (module2, filename) => {
  256. if (filename === fileName) {
  257. module2._compile(bundledCode, filename);
  258. } else {
  259. if (!isJs) {
  260. extensions[extension](module2, filename);
  261. } else {
  262. defaultLoader(module2, filename);
  263. }
  264. }
  265. };
  266. let config;
  267. try {
  268. if (isJs && require && require.cache) {
  269. delete require.cache[fileName];
  270. }
  271. const raw = require(fileName);
  272. config = raw.__esModule ? raw.default : raw;
  273. if (defaultLoader && isJs) {
  274. extensions[extension] = defaultLoader;
  275. }
  276. } catch (error) {
  277. console.error(error);
  278. }
  279. return config;
  280. }
  281. // src/index.ts
  282. (async () => {
  283. try {
  284. await Promise.resolve().then(() => require("mockjs"));
  285. } catch (e) {
  286. throw new Error("vite-plugin-vue-mock requires mockjs to be present in the dependency tree.");
  287. }
  288. })();
  289. function getDefaultPath(supportTs = true) {
  290. return _path2.default.resolve(process.cwd(), `src/main.${supportTs ? "ts" : "js"}`);
  291. }
  292. function viteMockServe(opt) {
  293. let defaultPath = getDefaultPath();
  294. if (!fileExists(defaultPath)) {
  295. defaultPath = getDefaultPath(false);
  296. if (!fileExists(defaultPath)) {
  297. defaultPath = "";
  298. }
  299. }
  300. const defaultEnter = _vite.normalizePath.call(void 0, defaultPath);
  301. const {injectFile = defaultEnter} = opt;
  302. let isDev = false;
  303. let config;
  304. let needSourcemap = false;
  305. return {
  306. name: "vite:mock",
  307. enforce: "pre",
  308. configResolved(resolvedConfig) {
  309. config = resolvedConfig;
  310. isDev = config.command === "serve";
  311. needSourcemap = !!resolvedConfig.build.sourcemap;
  312. isDev && createMockServer(opt);
  313. },
  314. configureServer: async ({middlewares}) => {
  315. const {localEnabled = isDev} = opt;
  316. if (!localEnabled) {
  317. return;
  318. }
  319. const middleware = await requestMiddleware(opt);
  320. middlewares.use(middleware);
  321. },
  322. async transform(code, id) {
  323. if (isDev || !injectFile || !id.endsWith(injectFile)) {
  324. return null;
  325. }
  326. const {prodEnabled = true, injectCode = ""} = opt;
  327. if (!prodEnabled) {
  328. return null;
  329. }
  330. return {
  331. map: needSourcemap ? this.getCombinedSourcemap() : null,
  332. code: `${code}
  333. ${injectCode}`
  334. };
  335. }
  336. };
  337. }
  338. exports.viteMockServe = viteMockServe;