vite-plugin-mock-dev-server 1.9.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,36 +1,34 @@
1
- import { isArray, isBoolean, promiseParallel, toArray, uniq } from "./dist-CAA1v47s.js";
2
- import { createDefineMock, createSSEStream, defineMock, defineMockData } from "./helper-DhHU-YoO.js";
3
- import { baseMiddleware, createLogger, debug, doesProxyContextMatchUrl, ensureProxies, logLevels, lookupFile, mockWebSocket, normalizePath, recoverRequest, sortByValidator, transformMockData, transformRawData, urlParse } from "./server-C-u7jwot.js";
4
- import pc from "picocolors";
1
+ import { createLogger, createMatcher, createMockMiddleware, debug, doesProxyContextMatchUrl, isPathMatch, logLevels, mockWebSocket, normalizePath, processMockData, processRawData, recoverRequest, sortByValidator, urlParse } from "./server-D-sv_WW9.js";
2
+ import { createDefineMock, createSSEStream, defineMock, defineMockData } from "./helper-BbR8Si2U.js";
3
+ import { isArray, isBoolean, promiseParallel, toArray, uniq } from "@pengzhanbo/utils";
5
4
  import fs, { promises } from "node:fs";
6
5
  import fsp from "node:fs/promises";
7
6
  import path from "node:path";
8
7
  import process from "node:process";
9
- import { createFilter } from "@rollup/pluginutils";
10
- import fg from "fast-glob";
11
- import isCore from "is-core-module";
8
+ import ansis from "ansis";
9
+ import { getPackageInfoSync, isPackageExists, loadPackageJSON, loadPackageJSONSync } from "local-pkg";
12
10
  import { pathToFileURL } from "node:url";
13
- import { build } from "esbuild";
14
11
  import JSON5 from "json5";
15
- import { pathToRegexp } from "path-to-regexp";
16
- import cors from "cors";
17
12
  import EventEmitter from "node:events";
18
- import chokidar from "chokidar";
13
+ import { watch } from "chokidar";
14
+ import { glob } from "tinyglobby";
15
+ import isCore from "is-core-module";
16
+ import cors from "cors";
19
17
 
20
- //#region src/core/compiler.ts
18
+ //#region src/compiler/esbuild.ts
21
19
  const externalizeDeps = {
22
20
  name: "externalize-deps",
23
- setup(build$1) {
24
- build$1.onResolve({ filter: /.*/ }, ({ path: id }) => {
21
+ setup(build) {
22
+ build.onResolve({ filter: /.*/ }, ({ path: id }) => {
25
23
  if (id[0] !== "." && !path.isAbsolute(id)) return { external: true };
26
24
  });
27
25
  }
28
26
  };
29
27
  const json5Loader = {
30
28
  name: "json5-loader",
31
- setup(build$1) {
32
- build$1.onLoad({ filter: /\.json5$/ }, async ({ path: path$1 }) => {
33
- const content = await promises.readFile(path$1, "utf-8");
29
+ setup(build) {
30
+ build.onLoad({ filter: /\.json5$/ }, async ({ path: path$1 }) => {
31
+ const content = await fsp.readFile(path$1, "utf-8");
34
32
  return {
35
33
  contents: `export default ${JSON.stringify(JSON5.parse(content))}`,
36
34
  loader: "js"
@@ -40,9 +38,9 @@ const json5Loader = {
40
38
  };
41
39
  const jsonLoader = {
42
40
  name: "json-loader",
43
- setup(build$1) {
44
- build$1.onLoad({ filter: /\.json$/ }, async ({ path: path$1 }) => {
45
- const content = await promises.readFile(path$1, "utf-8");
41
+ setup(build) {
42
+ build.onLoad({ filter: /\.json$/ }, async ({ path: path$1 }) => {
43
+ const content = await fsp.readFile(path$1, "utf-8");
46
44
  return {
47
45
  contents: `export default ${content}`,
48
46
  loader: "js"
@@ -50,10 +48,10 @@ const jsonLoader = {
50
48
  });
51
49
  }
52
50
  };
53
- const renamePlugin = {
51
+ const renamePlugin$1 = {
54
52
  name: "rename-plugin",
55
- setup(build$1) {
56
- build$1.onResolve({ filter: /.*/ }, ({ path: id }) => {
53
+ setup(build) {
54
+ build.onResolve({ filter: /.*/ }, ({ path: id }) => {
57
55
  if (id === "vite-plugin-mock-dev-server") return {
58
56
  path: "vite-plugin-mock-dev-server/helper",
59
57
  external: true
@@ -65,12 +63,12 @@ const renamePlugin = {
65
63
  function aliasPlugin(alias) {
66
64
  return {
67
65
  name: "alias-plugin",
68
- setup(build$1) {
69
- build$1.onResolve({ filter: /.*/ }, async ({ path: id }) => {
66
+ setup(build) {
67
+ build.onResolve({ filter: /.*/ }, async ({ path: id }) => {
70
68
  const matchedEntry = alias.find(({ find: find$1 }) => aliasMatches(find$1, id));
71
69
  if (!matchedEntry) return null;
72
70
  const { find, replacement } = matchedEntry;
73
- const result = await build$1.resolve(id.replace(find, replacement), {
71
+ const result = await build.resolve(id.replace(find, replacement), {
74
72
  kind: "import-statement",
75
73
  resolveDir: replacement,
76
74
  namespace: "file"
@@ -89,12 +87,17 @@ function aliasMatches(pattern, importee) {
89
87
  if (importee === pattern) return true;
90
88
  return importee.startsWith(`${pattern}/`);
91
89
  }
92
- async function transformWithEsbuild(entryPoint, options) {
93
- const { isESM = true, define, alias, cwd = process.cwd() } = options;
90
+ let _build = null;
91
+ async function esbuild() {
92
+ _build ||= (await import("esbuild")).build;
93
+ return _build;
94
+ }
95
+ async function transformWithEsbuild(entryPoint, { isESM = true, define, alias, cwd = process.cwd() }) {
94
96
  const filepath = path.resolve(cwd, entryPoint);
95
97
  const filename = path.basename(entryPoint);
96
98
  const dirname = path.dirname(filepath);
97
99
  try {
100
+ const build = await esbuild();
98
101
  const result = await build({
99
102
  entryPoints: [entryPoint],
100
103
  outfile: "out.js",
@@ -112,25 +115,31 @@ async function transformWithEsbuild(entryPoint, options) {
112
115
  },
113
116
  plugins: [
114
117
  aliasPlugin(alias),
115
- renamePlugin,
118
+ renamePlugin$1,
116
119
  externalizeDeps,
117
120
  jsonLoader,
118
121
  json5Loader
119
122
  ],
120
123
  absWorkingDir: cwd
121
124
  });
125
+ const deps = /* @__PURE__ */ new Set();
126
+ const inputs = result.metafile?.inputs || {};
127
+ Object.keys(inputs).forEach((key) => inputs[key].imports.forEach((dep) => deps.add(dep.path)));
122
128
  return {
123
129
  code: result.outputFiles[0].text,
124
- deps: result.metafile?.inputs || {}
130
+ deps: Array.from(deps)
125
131
  };
126
132
  } catch (e) {
127
133
  console.error(e);
128
134
  }
129
135
  return {
130
136
  code: "",
131
- deps: {}
137
+ deps: []
132
138
  };
133
139
  }
140
+
141
+ //#endregion
142
+ //#region src/compiler/loadFromCode.ts
134
143
  async function loadFromCode({ filepath, code, isESM, cwd }) {
135
144
  filepath = path.resolve(cwd, filepath);
136
145
  const ext = isESM ? ".mjs" : ".cjs";
@@ -148,222 +157,203 @@ async function loadFromCode({ filepath, code, isESM, cwd }) {
148
157
  }
149
158
 
150
159
  //#endregion
151
- //#region src/core/build.ts
152
- async function generateMockServer(ctx, options) {
153
- const include = toArray(options.include);
154
- const exclude = toArray(options.exclude);
155
- const cwd = options.cwd || process.cwd();
156
- let pkg = {};
157
- try {
158
- const pkgStr = lookupFile(options.context, ["package.json"]);
159
- if (pkgStr) pkg = JSON.parse(pkgStr);
160
- } catch {}
161
- const outputDir = options.build.dist;
162
- const content = await generateMockEntryCode(cwd, include, exclude);
163
- const mockEntry = path.join(cwd, `mock-data-${Date.now()}.js`);
164
- await fsp.writeFile(mockEntry, content, "utf-8");
165
- const { code, deps } = await transformWithEsbuild(mockEntry, options);
166
- const mockDeps = getMockDependencies(deps, options.alias);
167
- await fsp.unlink(mockEntry);
168
- const outputList = [
169
- {
170
- filename: path.join(outputDir, "mock-data.js"),
171
- source: code
172
- },
173
- {
174
- filename: path.join(outputDir, "index.js"),
175
- source: generatorServerEntryCode(options)
176
- },
177
- {
178
- filename: path.join(outputDir, "package.json"),
179
- source: generatePackageJson(pkg, mockDeps)
160
+ //#region src/compiler/rolldown.ts
161
+ const renamePlugin = {
162
+ name: "vite-mock:rename-plugin",
163
+ resolveId(id) {
164
+ if (id === "vite-plugin-mock-dev-server") return {
165
+ id: "vite-plugin-mock-dev-server/helper",
166
+ external: true
167
+ };
168
+ }
169
+ };
170
+ const json5Plugin = {
171
+ name: "vite-mock:json5-plugin",
172
+ transform: {
173
+ filter: { id: /\.json5$/ },
174
+ handler: (code) => {
175
+ return { code: `export default ${JSON5.stringify(JSON5.parse(code))}` };
180
176
  }
181
- ];
177
+ }
178
+ };
179
+ let _rolldown = null;
180
+ async function rolldown() {
181
+ _rolldown ||= {
182
+ build: (await import("rolldown")).build,
183
+ aliasPlugin: (await import("rolldown/experimental")).aliasPlugin
184
+ };
185
+ return _rolldown;
186
+ }
187
+ async function transformWithRolldown(entryPoint, { isESM = true, define, alias, cwd = process.cwd() }) {
188
+ const filepath = path.resolve(cwd, entryPoint);
189
+ const filename = path.basename(entryPoint);
190
+ const dirname = path.dirname(filepath);
191
+ const isAlias = (p) => !!alias.find(({ find }) => aliasMatches(find, p));
182
192
  try {
183
- if (path.isAbsolute(outputDir)) {
184
- for (const { filename } of outputList) if (fs.existsSync(filename)) await fsp.rm(filename);
185
- options.logger.info(`${pc.green("✓")} generate mock server in ${pc.cyan(outputDir)}`);
186
- for (const { filename, source } of outputList) {
187
- fs.mkdirSync(path.dirname(filename), { recursive: true });
188
- await fsp.writeFile(filename, source, "utf-8");
189
- const sourceSize = (source.length / 1024).toFixed(2);
190
- const name = path.relative(outputDir, filename);
191
- const space = name.length < 30 ? " ".repeat(30 - name.length) : "";
192
- options.logger.info(` ${pc.green(name)}${space}${pc.bold(pc.dim(`${sourceSize} kB`))}`);
193
- }
194
- } else for (const { filename, source } of outputList) ctx.emitFile({
195
- type: "asset",
196
- fileName: filename,
197
- source
193
+ const { build, aliasPlugin: aliasPlugin$1 } = await rolldown();
194
+ const result = await build({
195
+ input: entryPoint,
196
+ write: false,
197
+ cwd,
198
+ output: {
199
+ format: isESM ? "esm" : "cjs",
200
+ sourcemap: false,
201
+ file: "out.js"
202
+ },
203
+ platform: "node",
204
+ define: {
205
+ ...define,
206
+ __dirname: JSON.stringify(dirname),
207
+ __filename: JSON.stringify(filename),
208
+ ...isESM ? {} : { "import.meta.url": JSON.stringify(pathToFileURL(filepath)) }
209
+ },
210
+ external(id) {
211
+ if (isAlias(id)) return false;
212
+ if (id[0] !== "." && !path.isAbsolute(id) && id !== "vite-plugin-mock-dev-server") return true;
213
+ },
214
+ plugins: [
215
+ aliasPlugin$1({ entries: alias }),
216
+ renamePlugin,
217
+ json5Plugin
218
+ ]
198
219
  });
220
+ return {
221
+ code: result.output[0].code,
222
+ deps: result.output[0].imports
223
+ };
199
224
  } catch (e) {
200
225
  console.error(e);
201
226
  }
202
- }
203
- function getMockDependencies(deps, alias) {
204
- const list = /* @__PURE__ */ new Set();
205
- const excludeDeps = [
206
- "vite-plugin-mock-dev-server",
207
- "connect",
208
- "cors"
209
- ];
210
- const isAlias = (p) => alias.find(({ find }) => aliasMatches(find, p));
211
- Object.keys(deps).forEach((mPath) => {
212
- const imports = deps[mPath].imports.filter((_) => _.external && !_.path.startsWith("<define:") && !isAlias(_.path)).map((_) => _.path);
213
- imports.forEach((dep) => {
214
- const name = normalizePackageName(dep);
215
- if (!excludeDeps.includes(name) && !isCore(name)) list.add(name);
216
- });
217
- });
218
- return Array.from(list);
219
- }
220
- function normalizePackageName(dep) {
221
- const [scope, name] = dep.split("/");
222
- if (scope[0] === "@") return `${scope}/${name}`;
223
- return scope;
224
- }
225
- function generatePackageJson(pkg, mockDeps) {
226
- const { dependencies = {}, devDependencies = {} } = pkg;
227
- const dependents = {
228
- ...dependencies,
229
- ...devDependencies
230
- };
231
- const mockPkg = {
232
- name: "mock-server",
233
- type: "module",
234
- scripts: { start: "node index.js" },
235
- dependencies: {
236
- connect: "^3.7.0",
237
- ["vite-plugin-mock-dev-server"]: `^1.9.1`,
238
- cors: "^2.8.5"
239
- },
240
- pnpm: { peerDependencyRules: { ignoreMissing: ["vite"] } }
227
+ return {
228
+ code: "",
229
+ deps: []
241
230
  };
242
- mockDeps.forEach((dep) => {
243
- mockPkg.dependencies[dep] = dependents[dep] || "latest";
244
- });
245
- return JSON.stringify(mockPkg, null, 2);
246
231
  }
247
- function generatorServerEntryCode({ proxies, wsProxies, cookiesOptions, bodyParserOptions, priority, build: build$1 }) {
248
- const { serverPort, log } = build$1;
249
- return `import { createServer } from 'node:http';
250
- import connect from 'connect';
251
- import corsMiddleware from 'cors';
252
- import { baseMiddleware, createLogger, mockWebSocket } from 'vite-plugin-mock-dev-server/server';
253
- import mockData from './mock-data.js';
254
232
 
255
- const app = connect();
256
- const server = createServer(app);
257
- const logger = createLogger('mock-server', '${log}');
258
- const proxies = ${JSON.stringify(proxies)};
259
- const wsProxies = ${JSON.stringify(wsProxies)};
260
- const cookiesOptions = ${JSON.stringify(cookiesOptions)};
261
- const bodyParserOptions = ${JSON.stringify(bodyParserOptions)};
262
- const priority = ${JSON.stringify(priority)};
263
- const compiler = { mockData }
264
-
265
- mockWebSocket(compiler, server, { wsProxies, cookiesOptions, logger });
266
-
267
- app.use(corsMiddleware());
268
- app.use(baseMiddleware(compiler, {
269
- formidableOptions: { multiples: true },
270
- proxies,
271
- priority,
272
- cookiesOptions,
273
- bodyParserOptions,
274
- logger,
275
- }));
276
-
277
- server.listen(${serverPort});
278
-
279
- console.log('listen: http://localhost:${serverPort}');
280
- `;
233
+ //#endregion
234
+ //#region src/compiler/compile.ts
235
+ const hasRolldown = isPackageExists("rolldown");
236
+ const hasEsbuild = isPackageExists("esbuild");
237
+ async function transform(entryPoint, options) {
238
+ if (hasRolldown) return transformWithRolldown(entryPoint, options);
239
+ if (hasEsbuild) return transformWithEsbuild(entryPoint, options);
240
+ throw new Error("rolldown or esbuild not found");
281
241
  }
282
- async function generateMockEntryCode(cwd, include, exclude) {
283
- const includePaths = await fg(include, { cwd });
284
- const includeFilter = createFilter(include, exclude, { resolve: false });
285
- const mockFiles = includePaths.filter(includeFilter);
286
- let importers = "";
287
- const exporters = [];
288
- mockFiles.forEach((filepath, index) => {
289
- const file = normalizePath(path.join(cwd, filepath));
290
- importers += `import * as m${index} from '${file}';\n`;
291
- exporters.push(`[m${index}, '${filepath}']`);
242
+ async function compile(filepath, options) {
243
+ let isESM = false;
244
+ if (/\.m[jt]s$/.test(filepath)) isESM = true;
245
+ else if (/\.c[jt]s$/.test(filepath)) isESM = false;
246
+ else isESM = options.isESM || false;
247
+ const { code, deps } = await transform(filepath, {
248
+ ...options,
249
+ isESM
292
250
  });
293
- return `import { transformMockData, transformRawData } from 'vite-plugin-mock-dev-server/server';
294
- ${importers}
295
- const exporters = [\n ${exporters.join(",\n ")}\n];
296
- const mockList = exporters.map(([mod, filepath]) => {
297
- const raw = mod.default || mod;
298
- return transformRawData(raw, filepath);
299
- });
300
- export default transformMockData(mockList);`;
251
+ const data = await loadFromCode({
252
+ filepath,
253
+ code,
254
+ isESM,
255
+ cwd: options.cwd || process.cwd()
256
+ }) || {};
257
+ return {
258
+ data,
259
+ deps
260
+ };
301
261
  }
302
262
 
303
263
  //#endregion
304
- //#region src/core/mockCompiler.ts
305
- function createMockCompiler(options) {
306
- return new MockCompiler(options);
307
- }
264
+ //#region src/compiler/compiler.ts
308
265
  /**
309
- * mock配置加载器
266
+ * Mock 文件加载编译,并转换为 Mock 数据
310
267
  */
311
- var MockCompiler = class extends EventEmitter {
268
+ var Compiler = class extends EventEmitter {
312
269
  moduleCache = /* @__PURE__ */ new Map();
313
270
  moduleDeps = /* @__PURE__ */ new Map();
314
271
  cwd;
315
272
  mockWatcher;
316
273
  depsWatcher;
317
- moduleType = "cjs";
274
+ isESM = false;
318
275
  _mockData = {};
319
276
  constructor(options) {
320
277
  super();
321
278
  this.options = options;
322
279
  this.cwd = options.cwd || process.cwd();
323
280
  try {
324
- const pkg = lookupFile(this.cwd, ["package.json"]);
325
- this.moduleType = !!pkg && JSON.parse(pkg).type === "module" ? "esm" : "cjs";
281
+ const pkg = loadPackageJSONSync(this.cwd);
282
+ this.isESM = pkg?.type === "module";
326
283
  } catch {}
327
284
  }
328
285
  get mockData() {
329
286
  return this._mockData;
330
287
  }
331
- run() {
288
+ run(watch$1) {
332
289
  const { include, exclude } = this.options;
333
- /**
334
- * 使用 rollup 提供的 include/exclude 规则,
335
- * 过滤包含文件
336
- */
337
- const includeFilter = createFilter(include, exclude, { resolve: false });
338
- fg(include, { cwd: this.cwd }).then((files) => files.filter(includeFilter).map((file) => () => this.loadMock(file))).then((loadList) => promiseParallel(loadList, 10)).then(() => this.updateMockList());
339
- this.watchMockEntry();
290
+ const { pattern, ignore, isMatch } = createMatcher(include, exclude);
291
+ glob(pattern, {
292
+ ignore,
293
+ cwd: path.join(this.cwd, this.options.dir)
294
+ }).then((files) => files.map((file) => () => this.load(path.join(this.options.dir, file)))).then((loaders) => promiseParallel(loaders, 64)).then(() => this.updateMockData());
295
+ if (!watch$1) return;
296
+ this.watchMockEntry(isMatch);
340
297
  this.watchDeps();
341
298
  let timer = null;
342
299
  this.on("mock:update", async (filepath) => {
343
- if (!includeFilter(filepath)) return;
344
- await this.loadMock(filepath);
300
+ if (!isMatch(filepath)) return;
301
+ await this.load(filepath);
345
302
  if (timer) clearImmediate(timer);
346
303
  timer = setImmediate(() => {
347
- this.updateMockList();
348
- this.emit("mock:update-end", filepath);
304
+ this.updateMockData();
305
+ this.emit("mock:update-end", normalizePath(filepath));
349
306
  timer = null;
350
307
  });
351
308
  });
352
309
  this.on("mock:unlink", async (filepath) => {
353
- if (!includeFilter(filepath)) return;
310
+ if (!isMatch(filepath)) return;
311
+ filepath = normalizePath(path.join(this.options.dir, filepath));
354
312
  this.moduleCache.delete(filepath);
355
- this.updateMockList();
313
+ this.updateMockData();
356
314
  this.emit("mock:update-end", filepath);
357
315
  });
358
316
  }
359
- watchMockEntry() {
360
- const { include } = this.options;
361
- const [firstGlob, ...otherGlob] = toArray(include);
362
- const watcher = this.mockWatcher = chokidar.watch(firstGlob, {
317
+ close() {
318
+ this.mockWatcher?.close();
319
+ this.depsWatcher?.close();
320
+ }
321
+ async load(filepath) {
322
+ if (!filepath) return;
323
+ try {
324
+ const { define, alias } = this.options;
325
+ const { data, deps } = await compile(filepath, {
326
+ cwd: this.cwd,
327
+ isESM: this.isESM,
328
+ define,
329
+ alias
330
+ });
331
+ this.moduleCache.set(filepath, processRawData(data, filepath));
332
+ this.updateModuleDeps(filepath, deps);
333
+ } catch (e) {
334
+ console.error(e);
335
+ }
336
+ }
337
+ updateMockData() {
338
+ this._mockData = processMockData(this.moduleCache);
339
+ }
340
+ updateModuleDeps(filepath, deps) {
341
+ for (const dep of deps) {
342
+ if (!this.moduleDeps.has(dep)) this.moduleDeps.set(dep, /* @__PURE__ */ new Set());
343
+ const cur = this.moduleDeps.get(dep);
344
+ cur.add(filepath);
345
+ }
346
+ this.emit("update:deps");
347
+ }
348
+ watchMockEntry(isMatch) {
349
+ const watcher = this.mockWatcher = watch(this.options.dir, {
363
350
  ignoreInitial: true,
364
- cwd: this.cwd
351
+ cwd: this.cwd,
352
+ ignored: (filepath, stats) => {
353
+ if (filepath.includes("node_modules")) return true;
354
+ return !!stats?.isFile() && !isMatch(filepath);
355
+ }
365
356
  });
366
- if (otherGlob.length > 0) otherGlob.forEach((glob) => watcher.add(glob));
367
357
  watcher.on("add", async (filepath) => {
368
358
  filepath = normalizePath(filepath);
369
359
  this.emit("mock:update", filepath);
@@ -380,131 +370,212 @@ var MockCompiler = class extends EventEmitter {
380
370
  debug("watcher:unlink", filepath);
381
371
  });
382
372
  }
383
- /**
384
- * 监听 mock文件依赖的本地文件变动,
385
- * mock依赖文件更新,mock文件也一并更新
386
- */
387
373
  watchDeps() {
388
- const oldDeps = [];
389
- this.depsWatcher = chokidar.watch([], {
374
+ let oldDeps = [...this.moduleDeps.keys()];
375
+ const watcher = this.depsWatcher = watch([...oldDeps], {
390
376
  ignoreInitial: true,
391
377
  cwd: this.cwd
392
378
  });
393
- this.depsWatcher.on("change", (filepath) => {
379
+ watcher.on("change", (filepath) => {
394
380
  filepath = normalizePath(filepath);
395
381
  const mockFiles = this.moduleDeps.get(filepath);
396
- mockFiles?.forEach((file) => {
397
- this.emit("mock:update", file);
398
- });
382
+ mockFiles?.forEach((file) => this.emit("mock:update", file));
399
383
  });
400
- this.depsWatcher.on("unlink", (filepath) => {
384
+ watcher.on("unlink", (filepath) => {
401
385
  filepath = normalizePath(filepath);
402
386
  this.moduleDeps.delete(filepath);
403
387
  });
404
388
  this.on("update:deps", () => {
405
- const deps = [];
406
- for (const [dep] of this.moduleDeps.entries()) deps.push(dep);
389
+ const deps = [...this.moduleDeps.keys()];
407
390
  const exactDeps = deps.filter((dep) => !oldDeps.includes(dep));
408
- if (exactDeps.length > 0) this.depsWatcher.add(exactDeps);
391
+ oldDeps = deps;
392
+ if (exactDeps.length > 0) watcher.add(exactDeps);
409
393
  });
410
394
  }
411
- close() {
412
- this.mockWatcher?.close();
413
- this.depsWatcher?.close();
414
- }
415
- updateMockList() {
416
- this._mockData = transformMockData(this.moduleCache);
417
- }
418
- updateModuleDeps(filepath, deps) {
419
- Object.keys(deps).forEach((mPath) => {
420
- const imports = deps[mPath].imports.map((_) => _.path);
421
- imports.forEach((dep) => {
422
- if (!this.moduleDeps.has(dep)) this.moduleDeps.set(dep, /* @__PURE__ */ new Set());
423
- const cur = this.moduleDeps.get(dep);
424
- cur.add(filepath);
425
- });
426
- });
427
- this.emit("update:deps");
428
- }
429
- async loadMock(filepath) {
430
- if (!filepath) return;
431
- let isESM = false;
432
- if (/\.m[jt]s$/.test(filepath)) isESM = true;
433
- else if (/\.c[jt]s$/.test(filepath)) isESM = false;
434
- else isESM = this.moduleType === "esm";
435
- const { define, alias } = this.options;
436
- const { code, deps } = await transformWithEsbuild(filepath, {
437
- isESM,
438
- define,
439
- alias,
440
- cwd: this.cwd
441
- });
442
- try {
443
- const raw = await loadFromCode({
444
- filepath,
445
- code,
446
- isESM,
447
- cwd: this.cwd
448
- }) || {};
449
- this.moduleCache.set(filepath, transformRawData(raw, filepath));
450
- this.updateModuleDeps(filepath, deps);
451
- } catch (e) {
452
- console.error(e);
453
- }
454
- }
455
395
  };
456
396
 
457
397
  //#endregion
458
- //#region src/core/mockMiddleware.ts
459
- function mockServerMiddleware(options, server, ws) {
460
- /**
461
- * 加载 mock 文件, 包括监听 mock 文件的依赖文件变化,
462
- * 并注入 vite `define` / `alias`
463
- */
464
- const compiler = createMockCompiler(options);
465
- compiler.run();
466
- /**
467
- * 监听 mock 文件是否发生变更,如何配置了 reload 为 true,
468
- * 当发生变更时,通知当前页面进行重新加载
469
- */
470
- compiler.on("mock:update-end", () => {
471
- if (options.reload) ws?.send({ type: "full-reload" });
398
+ //#region src/build/mockEntryCode.ts
399
+ async function generateMockEntryCode(cwd, dir, include, exclude) {
400
+ const { pattern, ignore } = createMatcher(include, exclude);
401
+ const mockFiles = await glob(pattern, {
402
+ ignore,
403
+ cwd: path.join(cwd, dir)
472
404
  });
473
- server?.on("close", () => compiler.close());
474
- /**
475
- * 虽然 config.server.proxy 中有关于 ws 的代理配置,
476
- * 但是由于 vite 内部在启动时,直接对 ws相关的请求,通过 upgrade 事件,发送给 http-proxy
477
- * ws 代理方法。如果插件直接使用 config.server.proxy 中的 ws 配置,
478
- * 就会导致两次 upgrade 事件 对 wss 实例的冲突。
479
- * 由于 vite 内部并没有提供其他的方式跳过 内部 upgrade 的方式,(个人认为也没有必要提供此类方式)
480
- * 所以插件选择了通过插件的配置项 `wsPrefix` 来做 判断的首要条件。
481
- * 当前插件默认会将已配置在 wsPrefix 的值,从 config.server.proxy 的删除,避免发生冲突问题。
482
- */
483
- mockWebSocket(compiler, server, options);
484
- const middlewares = [];
485
- middlewares.push(
486
- /**
487
- * 在 vite 的开发服务中,由于插件 的 enforce 为 `pre`,
488
- * mock 中间件的执行顺序 早于 vite 内部的 cors 中间件执行,
489
- * 这导致了 vite 默认开启的 cors 对 mock 请求不生效。
490
- * 在一些比如 微前端项目、或者联合项目中,会由于端口不一致而导致跨域问题。
491
- * 所以在这里,使用 cors 中间件 来解决这个问题。
492
- *
493
- * 同时为了使 插件内的 cors 和 vite 的 cors 不产生冲突,并拥有一致的默认行为,
494
- * 也会使用 viteConfig.server.cors 配置,并支持 用户可以对 mock 中的 cors 中间件进行配置。
495
- * 而用户的配置也仅对 mock 的接口生效。
496
- */
497
- corsMiddleware(compiler, options),
498
- baseMiddleware(compiler, options)
499
- );
500
- return middlewares.filter(Boolean);
405
+ let importers = "";
406
+ const exporters = [];
407
+ mockFiles.forEach((filepath, index) => {
408
+ const file = normalizePath(path.join(cwd, dir, filepath));
409
+ importers += `import * as m${index} from '${file}';\n`;
410
+ exporters.push(`[m${index}, '${normalizePath(path.join(dir, filepath))}']`);
411
+ });
412
+ return `import { processMockData, processRawData } from 'vite-plugin-mock-dev-server/server';
413
+ ${importers}
414
+ const exporters = [\n ${exporters.join(",\n ")}\n];
415
+ const mockList = exporters.map(([mod, filepath]) => processRawData(mod.default || mod, filepath));
416
+ export default processMockData(mockList);`;
417
+ }
418
+
419
+ //#endregion
420
+ //#region package.json
421
+ var name = "vite-plugin-mock-dev-server";
422
+ var version = "1.9.3";
423
+
424
+ //#endregion
425
+ //#region src/build/packageJson.ts
426
+ /**
427
+ * mock 文件的 importers 中获取依赖
428
+ */
429
+ function getMockDependencies(deps, alias) {
430
+ const list = /* @__PURE__ */ new Set();
431
+ const excludeDeps = [
432
+ name,
433
+ "connect",
434
+ "cors"
435
+ ];
436
+ const isAlias = (p) => alias.find(({ find }) => aliasMatches(find, p));
437
+ deps.forEach((dep) => {
438
+ const name$1 = normalizePackageName(dep);
439
+ if (name$1.startsWith("<define:") || isAlias(name$1) || isCore(name$1)) return;
440
+ if (name$1[0] === "/" || name$1.startsWith("./") || name$1.startsWith("../")) return;
441
+ if (!excludeDeps.includes(name$1)) list.add(name$1);
442
+ });
443
+ return Array.from(list);
444
+ }
445
+ function normalizePackageName(dep) {
446
+ const [scope, name$1] = dep.split("/");
447
+ if (scope[0] === "@") return `${scope}/${name$1}`;
448
+ return scope;
449
+ }
450
+ function generatePackageJson(pkg, mockDeps) {
451
+ const { dependencies = {}, devDependencies = {} } = pkg;
452
+ const dependents = {
453
+ ...dependencies,
454
+ ...devDependencies
455
+ };
456
+ const mockPkg = {
457
+ name: "mock-server",
458
+ type: "module",
459
+ scripts: { start: "node index.js" },
460
+ dependencies: {
461
+ connect: "^3.7.0",
462
+ [name]: `^${version}`,
463
+ cors: "^2.8.5"
464
+ },
465
+ pnpm: { peerDependencyRules: { ignoreMissing: ["vite"] } }
466
+ };
467
+ const ignores = [
468
+ "catalog:",
469
+ "file:",
470
+ "workspace:"
471
+ ];
472
+ for (const dep of mockDeps) {
473
+ const version$1 = dependents[dep];
474
+ if (!version$1 || ignores.some((ignore) => version$1.startsWith(ignore))) {
475
+ const info = getPackageInfoSync(dep);
476
+ mockPkg.dependencies[dep] = info?.version ? `^${info.version}` : "latest";
477
+ } else mockPkg.dependencies[dep] = "latest";
478
+ }
479
+ return JSON.stringify(mockPkg, null, 2);
480
+ }
481
+
482
+ //#endregion
483
+ //#region src/build/serverEntryCode.ts
484
+ function generatorServerEntryCode({ proxies, wsProxies, cookiesOptions, bodyParserOptions, priority, build }) {
485
+ const { serverPort, log } = build;
486
+ return `import { createServer } from 'node:http';
487
+ import connect from 'connect';
488
+ import corsMiddleware from 'cors';
489
+ import { createMockMiddleware, createLogger, mockWebSocket } from 'vite-plugin-mock-dev-server/server';
490
+ import mockData from './mock-data.js';
491
+
492
+ const app = connect();
493
+ const server = createServer(app);
494
+ const logger = createLogger('mock-server', '${log}');
495
+ const proxies = ${JSON.stringify(proxies)};
496
+ const wsProxies = ${JSON.stringify(wsProxies)};
497
+ const cookiesOptions = ${JSON.stringify(cookiesOptions)};
498
+ const bodyParserOptions = ${JSON.stringify(bodyParserOptions)};
499
+ const priority = ${JSON.stringify(priority)};
500
+ const compiler = { mockData }
501
+
502
+ mockWebSocket(compiler, server, { wsProxies, cookiesOptions, logger });
503
+
504
+ app.use(corsMiddleware());
505
+ app.use(createMockMiddleware(compiler, {
506
+ formidableOptions: { multiples: true },
507
+ proxies,
508
+ priority,
509
+ cookiesOptions,
510
+ bodyParserOptions,
511
+ logger,
512
+ }));
513
+
514
+ server.listen(${serverPort});
515
+
516
+ console.log('listen: http://localhost:${serverPort}');
517
+ `;
501
518
  }
502
- function corsMiddleware(compiler, { proxies, cors: corsOptions }) {
519
+
520
+ //#endregion
521
+ //#region src/build/generate.ts
522
+ async function generateMockServer(ctx, options) {
523
+ const include = toArray(options.include);
524
+ const exclude = toArray(options.exclude);
525
+ const cwd = options.cwd || process.cwd();
526
+ const dir = options.dir;
527
+ const pkg = await loadPackageJSON(options.context) || {};
528
+ const outputDir = options.build.dist;
529
+ const content = await generateMockEntryCode(cwd, dir, include, exclude);
530
+ const mockEntry = path.join(cwd, `mock-data-${Date.now()}.js`);
531
+ await fsp.writeFile(mockEntry, content, "utf-8");
532
+ const { code, deps } = await transform(mockEntry, options);
533
+ const mockDeps = getMockDependencies(deps, options.alias);
534
+ await fsp.unlink(mockEntry);
535
+ const outputList = [
536
+ {
537
+ filename: path.join(outputDir, "mock-data.js"),
538
+ source: code
539
+ },
540
+ {
541
+ filename: path.join(outputDir, "index.js"),
542
+ source: generatorServerEntryCode(options)
543
+ },
544
+ {
545
+ filename: path.join(outputDir, "package.json"),
546
+ source: generatePackageJson(pkg, mockDeps)
547
+ }
548
+ ];
549
+ try {
550
+ if (path.isAbsolute(outputDir)) {
551
+ for (const { filename } of outputList) if (fs.existsSync(filename)) await fsp.rm(filename);
552
+ options.logger.info(`${ansis.green("✓")} generate mock server in ${ansis.cyan(outputDir)}`);
553
+ for (const { filename, source } of outputList) {
554
+ fs.mkdirSync(path.dirname(filename), { recursive: true });
555
+ await fsp.writeFile(filename, source, "utf-8");
556
+ const sourceSize = (source.length / 1024).toFixed(2);
557
+ const name$1 = path.relative(outputDir, filename);
558
+ const space = name$1.length < 30 ? " ".repeat(30 - name$1.length) : "";
559
+ options.logger.info(` ${ansis.green(name$1)}${space}${ansis.bold.dim(`${sourceSize} kB`)}`);
560
+ }
561
+ } else for (const { filename, source } of outputList) ctx.emitFile({
562
+ type: "asset",
563
+ fileName: filename,
564
+ source
565
+ });
566
+ } catch (e) {
567
+ console.error(e);
568
+ }
569
+ }
570
+
571
+ //#endregion
572
+ //#region src/core/corsMiddleware.ts
573
+ function createCorsMiddleware(compiler, { proxies, cors: corsOptions }) {
503
574
  return !corsOptions ? void 0 : function(req, res, next) {
504
575
  const { pathname } = urlParse(req.url);
505
576
  if (!pathname || proxies.length === 0 || !proxies.some((context) => doesProxyContextMatchUrl(context, req.url))) return next();
506
577
  const mockData = compiler.mockData;
507
- const mockUrl = Object.keys(mockData).find((key) => pathToRegexp(key).test(pathname));
578
+ const mockUrl = Object.keys(mockData).find((pattern) => isPathMatch(pattern, pathname));
508
579
  if (!mockUrl) return next();
509
580
  cors(corsOptions)(req, res, next);
510
581
  };
@@ -588,17 +659,59 @@ function canJsonParse(value) {
588
659
  }
589
660
 
590
661
  //#endregion
591
- //#region src/core/resolvePluginOptions.ts
592
- function resolvePluginOptions({ prefix = [], wsPrefix = [], cwd, include = ["mock/**/*.mock.{js,ts,cjs,mjs,json,json5}"], exclude = [
593
- "**/node_modules/**",
594
- "**/.vscode/**",
595
- "**/.git/**"
596
- ], reload = false, log = "info", cors: cors$1 = true, formidableOptions = {}, build: build$1 = false, cookiesOptions = {}, bodyParserOptions = {}, priority = {} }, config) {
662
+ //#region src/core/init.ts
663
+ function initMockMiddlewares(options, server, ws) {
664
+ /**
665
+ * 加载 mock 文件, 包括监听 mock 文件的依赖文件变化,
666
+ * 并注入 vite `define` / `alias`
667
+ */
668
+ const compiler = new Compiler(options);
669
+ compiler.run(!!server);
670
+ /**
671
+ * 监听 mock 文件是否发生变更,如何配置了 reload 为 true,
672
+ * 当发生变更时,通知当前页面进行重新加载
673
+ */
674
+ compiler.on("mock:update-end", () => {
675
+ if (options.reload) ws?.send({ type: "full-reload" });
676
+ });
677
+ server?.on("close", () => compiler.close());
678
+ /**
679
+ * 虽然 config.server.proxy 中有关于 ws 的代理配置,
680
+ * 但是由于 vite 内部在启动时,直接对 ws相关的请求,通过 upgrade 事件,发送给 http-proxy
681
+ * 的 ws 代理方法。如果插件直接使用 config.server.proxy 中的 ws 配置,
682
+ * 就会导致两次 upgrade 事件 对 wss 实例的冲突。
683
+ * 由于 vite 内部并没有提供其他的方式跳过 内部 upgrade 的方式,(个人认为也没有必要提供此类方式)
684
+ * 所以插件选择了通过插件的配置项 `wsPrefix` 来做 判断的首要条件。
685
+ * 当前插件默认会将已配置在 wsPrefix 的值,从 config.server.proxy 的删除,避免发生冲突问题。
686
+ */
687
+ mockWebSocket(compiler, server, options);
688
+ const middlewares = [];
689
+ middlewares.push(
690
+ /**
691
+ * 在 vite 的开发服务中,由于插件 的 enforce 为 `pre`,
692
+ * mock 中间件的执行顺序 早于 vite 内部的 cors 中间件执行,
693
+ * 这导致了 vite 默认开启的 cors 对 mock 请求不生效。
694
+ * 在一些比如 微前端项目、或者联合项目中,会由于端口不一致而导致跨域问题。
695
+ * 所以在这里,使用 cors 中间件 来解决这个问题。
696
+ *
697
+ * 同时为了使 插件内的 cors 和 vite 的 cors 不产生冲突,并拥有一致的默认行为,
698
+ * 也会使用 viteConfig.server.cors 配置,并支持 用户可以对 mock 中的 cors 中间件进行配置。
699
+ * 而用户的配置也仅对 mock 的接口生效。
700
+ */
701
+ createCorsMiddleware(compiler, options),
702
+ createMockMiddleware(compiler, options)
703
+ );
704
+ return middlewares.filter(Boolean);
705
+ }
706
+
707
+ //#endregion
708
+ //#region src/options.ts
709
+ function resolvePluginOptions({ prefix = [], wsPrefix = [], cwd, dir = "mock", include = ["**/*.mock.{js,ts,cjs,mjs,json,json5}"], exclude = [], reload = false, log = "info", cors: cors$1 = true, formidableOptions = {}, build = false, cookiesOptions = {}, bodyParserOptions = {}, priority = {} }, config) {
597
710
  const logger = createLogger("vite:mock", isBoolean(log) ? log ? "info" : "error" : log);
598
711
  const { httpProxies } = ensureProxies(config.server.proxy || {});
599
712
  const proxies = uniq([...toArray(prefix), ...httpProxies]);
600
713
  const wsProxies = toArray(wsPrefix);
601
- if (!proxies.length && !wsProxies.length) logger.warn(`No proxy was configured, mock server will not work. See ${pc.cyan("https://vite-plugin-mock-dev-server.netlify.app/guide/usage")}`);
714
+ if (!proxies.length && !wsProxies.length) logger.warn(`No proxy was configured, mock server will not work. See ${ansis.cyan("https://vite-plugin-mock-dev-server.netlify.app/guide/usage")}`);
602
715
  const enabled = cors$1 === false ? false : config.server.cors !== false;
603
716
  let corsOptions = {};
604
717
  if (enabled && config.server.cors !== false) corsOptions = {
@@ -620,6 +733,7 @@ function resolvePluginOptions({ prefix = [], wsPrefix = [], cwd, include = ["moc
620
733
  });
621
734
  return {
622
735
  cwd: cwd || process.cwd(),
736
+ dir,
623
737
  include,
624
738
  exclude,
625
739
  context: config.root,
@@ -633,11 +747,12 @@ function resolvePluginOptions({ prefix = [], wsPrefix = [], cwd, include = ["moc
633
747
  },
634
748
  bodyParserOptions,
635
749
  priority,
636
- build: build$1 ? Object.assign({
750
+ build: build ? {
637
751
  serverPort: 8080,
638
752
  dist: "mockServer",
639
- log: "error"
640
- }, typeof build$1 === "object" ? build$1 : {}) : false,
753
+ log: "error",
754
+ ...typeof build === "object" ? build : {}
755
+ } : false,
641
756
  proxies,
642
757
  wsProxies,
643
758
  logger,
@@ -645,6 +760,19 @@ function resolvePluginOptions({ prefix = [], wsPrefix = [], cwd, include = ["moc
645
760
  define: viteDefine(config)
646
761
  };
647
762
  }
763
+ function ensureProxies(serverProxy = {}) {
764
+ const httpProxies = [];
765
+ const wsProxies = [];
766
+ Object.keys(serverProxy).forEach((key) => {
767
+ const value = serverProxy[key];
768
+ if (typeof value === "string" || !value.ws && !value.target?.toString().startsWith("ws:") && !value.target?.toString().startsWith("wss:")) httpProxies.push(key);
769
+ else wsProxies.push(key);
770
+ });
771
+ return {
772
+ httpProxies,
773
+ wsProxies
774
+ };
775
+ }
648
776
 
649
777
  //#endregion
650
778
  //#region src/plugin.ts
@@ -693,25 +821,15 @@ function serverPlugin(options) {
693
821
  config.logger.warn("");
694
822
  },
695
823
  configureServer({ middlewares, httpServer, ws }) {
696
- const middlewareList = mockServerMiddleware(resolvedOptions, httpServer, ws);
824
+ const middlewareList = initMockMiddlewares(resolvedOptions, httpServer, ws);
697
825
  middlewareList.forEach((middleware) => middlewares.use(middleware));
698
826
  },
699
827
  configurePreviewServer({ middlewares, httpServer }) {
700
- const middlewareList = mockServerMiddleware(resolvedOptions, httpServer);
828
+ const middlewareList = initMockMiddlewares(resolvedOptions, httpServer);
701
829
  middlewareList.forEach((middleware) => middlewares.use(middleware));
702
830
  }
703
831
  };
704
832
  }
705
833
 
706
834
  //#endregion
707
- //#region src/index.ts
708
- /**
709
- * @deprecated use named export instead
710
- */
711
- function mockDevServerPluginWithDefaultExportWasDeprecated(options = {}) {
712
- console.warn(`${pc.yellow("[vite-plugin-mock-dev-server]")} ${pc.yellow(pc.bold("WARNING:"))} The plugin default export is ${pc.bold("deprecated")}, it will be removed in next major version, use ${pc.bold("named export")} instead:\n\n ${pc.green("import { mockDevServerPlugin } from \"vite-plugin-mock-dev-server\"")}\n`);
713
- return mockDevServerPlugin(options);
714
- }
715
-
716
- //#endregion
717
- export { baseMiddleware, createDefineMock, createLogger, createSSEStream, mockDevServerPluginWithDefaultExportWasDeprecated as default, defineMock, defineMockData, logLevels, mockDevServerPlugin, mockWebSocket, sortByValidator, transformMockData, transformRawData };
835
+ export { createDefineMock, createLogger, createMockMiddleware, createSSEStream, defineMock, defineMockData, logLevels, mockDevServerPlugin, mockWebSocket, processMockData, processRawData, sortByValidator };