vite-plugin-automock 1.0.0 → 1.0.2

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.
@@ -330,9 +330,10 @@ var writeMockFile = async (filePath, mockInfo) => {
330
330
 
331
331
  export {
332
332
  DEFAULT_CONFIG,
333
+ toPosixPath,
333
334
  saveMockData,
334
335
  buildMockIndex,
335
336
  parseMockModule,
336
337
  writeMockFile
337
338
  };
338
- //# sourceMappingURL=chunk-KZB7ARYV.mjs.map
339
+ //# sourceMappingURL=chunk-PS6HLNJZ.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mockFileUtils.ts"],"sourcesContent":["import path from \"path\";\nimport fs from \"fs-extra\";\nimport prettier from \"prettier\";\n\nexport interface MockFileConfig {\n enable: boolean;\n data: unknown;\n delay: number;\n status: number;\n [key: string]: unknown;\n}\n\nexport interface MockFileInfo {\n config: MockFileConfig;\n serializable: boolean;\n hasDynamicData: boolean;\n headerComment?: string;\n description?: string;\n dataText: string;\n isBinary?: boolean;\n}\n\nexport const DEFAULT_CONFIG: MockFileConfig = {\n enable: true,\n data: null,\n delay: 0,\n status: 200,\n};\n\n// 简单实现 cross-platform 路径分隔符转换\nfunction toPosixPath(p: string): string {\n return p.replace(/\\\\/g, \"/\");\n}\n\n// 检测是否为二进制文件\nconst isBinaryResponse = (\n contentType: string | undefined,\n data: Buffer,\n): boolean => {\n if (!contentType) return false;\n\n const binaryTypes = [\n \"application/octet-stream\",\n \"application/pdf\",\n \"application/zip\",\n \"application/x-zip-compressed\",\n \"application/vnd.ms-excel\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \"application/msword\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.ms-powerpoint\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \"image/\",\n \"video/\",\n \"audio/\",\n ];\n\n return (\n binaryTypes.some((type) => contentType.toLowerCase().includes(type)) ||\n !isBufferTextLike(data)\n );\n};\n\n// 检测Buffer是否像文本数据\nconst isBufferTextLike = (buffer: Buffer): boolean => {\n try {\n // 检查前100字节是否包含非文本字符\n const sample = buffer.slice(0, 100);\n const text = sample.toString(\"utf8\");\n\n // 如果包含null字节或过多的控制字符,可能是二进制\n const nullBytes = [...sample].filter((b) => b === 0).length;\n const controlChars = [...sample].filter(\n (b) => b < 32 && b !== 9 && b !== 10 && b !== 13,\n ).length;\n\n return nullBytes === 0 && controlChars < 5;\n } catch {\n return false;\n }\n};\n\n// 从Content-Type获取文件扩展名\nconst getFileExtension = (\n contentType: string | undefined,\n url: string,\n): string => {\n // 从Content-Type映射\n const mimeMap: Record<string, string> = {\n \"application/json\": \"json\",\n \"application/pdf\": \"pdf\",\n \"application/zip\": \"zip\",\n \"application/vnd.ms-excel\": \"xls\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\": \"xlsx\",\n \"application/msword\": \"doc\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\":\n \"docx\",\n \"application/vnd.ms-powerpoint\": \"ppt\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\":\n \"pptx\",\n \"image/jpeg\": \"jpg\",\n \"image/png\": \"png\",\n \"image/gif\": \"gif\",\n \"text/plain\": \"txt\",\n \"text/html\": \"html\",\n \"text/css\": \"css\",\n \"application/javascript\": \"js\",\n \"text/xml\": \"xml\",\n };\n\n if (contentType && mimeMap[contentType.toLowerCase()]) {\n return mimeMap[contentType.toLowerCase()];\n }\n\n // 尝试从URL的查询参数中解析文件名\n try {\n const urlObj = new URL(url, \"http://localhost\");\n const fileName = urlObj.searchParams.get(\"file_name\");\n if (fileName) {\n const extensionMatch = fileName.match(/\\.([a-zA-Z0-9]+)$/);\n if (extensionMatch) {\n return extensionMatch[1];\n }\n }\n } catch (error) {\n // 忽略URL解析错误\n }\n\n return \"bin\";\n};\n\nconst saveMockData = async (\n url: string,\n method: string,\n data: Buffer | string,\n rootDir: string,\n statusCode?: number,\n contentType?: string,\n): Promise<string | null> => {\n try {\n const absoluteRootDir = path.isAbsolute(rootDir)\n ? rootDir\n : path.resolve(process.cwd(), rootDir);\n\n // 正确解析URL,处理查询参数和路径\n let pathname: string;\n let search: string;\n\n try {\n // 如果url包含协议信息,直接解析\n if (url.startsWith(\"http\")) {\n const urlObj = new URL(url);\n pathname = urlObj.pathname;\n search = urlObj.search;\n } else {\n // 如果是相对路径,先补全协议\n const urlObj = new URL(url, \"http://localhost\");\n pathname = urlObj.pathname;\n search = urlObj.search;\n }\n } catch (error) {\n // 如果URL解析失败,尝试手动处理\n const [pathPart, ...searchPart] = url.split(\"?\");\n pathname = pathPart;\n search = searchPart.length > 0 ? \"?\" + searchPart.join(\"?\") : \"\";\n }\n\n const filePath = path.join(\n absoluteRootDir,\n pathname.replace(/^\\//, \"\"),\n method.toLowerCase() + \".js\",\n );\n\n const dir = path.dirname(filePath);\n fs.ensureDirSync(dir);\n\n // 检测是否为二进制文件\n const isBuffer = Buffer.isBuffer(data);\n const binaryData = isBuffer ? data : Buffer.from(data || \"\");\n const isBinary = isBinaryResponse(contentType, binaryData);\n\n if (isBinary) {\n // 二进制文件处理\n const extension = getFileExtension(contentType, url);\n const binaryFilePath = filePath.replace(/\\.js$/, \".\" + extension);\n\n // 检查文件是否已存在,如果存在则不覆盖\n if (fs.existsSync(binaryFilePath)) {\n return null;\n }\n\n // 保存二进制文件\n fs.writeFileSync(binaryFilePath, binaryData);\n\n // 创建对应的JS配置文件\n const configContent = `/**\n * Mock data for ${pathname} (${method.toUpperCase()})${search || \"\"}\n * @description ${pathname}${search || \"\"} - Binary file (${extension})\n * Generated at ${new Date().toISOString()}\n */\nexport default {\n enable: false,\n data: {\n __binaryFile: '${extension}',\n __originalPath: '${pathname}',\n __originalQuery: '${search}',\n __originalUrl: '${pathname}${search || \"\"}',\n __contentType: '${contentType}',\n __fileSize: ${binaryData.length}\n },\n delay: 0,\n status: ${statusCode || 200}\n}`;\n\n try {\n const formattedCode = await prettier.format(configContent, {\n parser: \"babel\",\n });\n fs.writeFileSync(filePath, formattedCode, \"utf-8\");\n } catch (error) {\n fs.writeFileSync(filePath, configContent, \"utf-8\");\n }\n\n return filePath;\n } else {\n // JSON文件处理\n // 检查文件是否已存在,如果存在则不覆盖\n if (fs.existsSync(filePath)) {\n return null;\n }\n\n const dataStr = isBuffer ? data.toString(\"utf8\") : data || \"\";\n let jsonData;\n // 如果响应为空或只包含空白字符\n if (!dataStr || dataStr.trim() === \"\") {\n jsonData = {\n error: true,\n message: `Empty response (${statusCode || \"unknown status\"})`,\n status: statusCode || 404,\n data: null,\n };\n } else {\n try {\n jsonData = JSON.parse(dataStr);\n // 如果解析成功且是错误状态码,添加错误标记\n if (statusCode && statusCode >= 400) {\n if (typeof jsonData === \"object\" && jsonData !== null) {\n jsonData = {\n ...jsonData,\n __mockStatusCode: statusCode,\n __isErrorResponse: true,\n };\n } else {\n jsonData = {\n originalData: jsonData,\n __mockStatusCode: statusCode,\n __isErrorResponse: true,\n };\n }\n }\n } catch {\n // 如果不是JSON格式,可能是HTML错误页面或其他格式\n jsonData = {\n error: true,\n message: `Non-JSON response (${statusCode || \"unknown status\"})`,\n status: statusCode || 404,\n data: dataStr,\n __mockStatusCode: statusCode,\n __isErrorResponse: true,\n };\n }\n }\n\n const content = `/**\n * Mock data for ${pathname} (${method.toUpperCase()})${search || \"\"}\n * @description ${pathname}${search || \"\"}\n * Generated at ${new Date().toISOString()}\n */\nexport default {\n enable: false,\n data: ${JSON.stringify(jsonData)},\n delay: 0,\n status: ${statusCode || 200}\n}`;\n\n try {\n const formattedCode = await prettier.format(content, {\n parser: \"babel\",\n });\n fs.writeFileSync(filePath, formattedCode, \"utf-8\");\n return filePath;\n } catch (error) {\n fs.writeFileSync(filePath, content, \"utf-8\");\n return filePath;\n }\n }\n } catch (error) {\n console.error(`Failed to save mock data for ${url}:`, error);\n console.error(\n `URL details: url=${url}, method=${method}, statusCode=${statusCode}, contentType=${contentType}`,\n );\n throw error;\n }\n};\n\nconst recursiveReadAllFiles = (dir: string): string[] => {\n if (!fs.existsSync(dir)) return [];\n\n const files: string[] = [];\n try {\n const list = fs.readdirSync(dir);\n list.forEach((file) => {\n const filePath = path.join(dir, file);\n const stat = fs.statSync(filePath);\n if (stat.isDirectory()) {\n files.push(...recursiveReadAllFiles(filePath));\n } else {\n files.push(filePath);\n }\n });\n } catch (error) {\n console.error(`Error reading directory ${dir}:`, error);\n }\n return files;\n};\n\nconst buildMockIndex = (mockDir: string): Map<string, string> => {\n const mockFileMap = new Map<string, string>();\n\n if (!fs.existsSync(mockDir)) {\n fs.ensureDirSync(mockDir);\n return mockFileMap;\n }\n\n const files = recursiveReadAllFiles(mockDir);\n\n files.forEach((filePath) => {\n if (!filePath.endsWith(\".js\")) {\n return;\n }\n\n try {\n const relativePath = path.relative(mockDir, filePath);\n const method = path.basename(filePath, \".js\");\n const dirPath = path.dirname(relativePath);\n const urlPath = \"/\" + toPosixPath(dirPath);\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(process.cwd(), filePath);\n const key = `${urlPath}/${method}.js`.toLowerCase();\n\n mockFileMap.set(key, absolutePath);\n } catch (error) {\n console.error(`❌ [automock] 处理文件失败 ${filePath}:`, error);\n }\n });\n\n return mockFileMap;\n};\n\nconst parseMockModule = async (filePath: string): Promise<MockFileInfo> => {\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(process.cwd(), filePath);\n\n if (!fs.existsSync(absolutePath)) {\n throw new Error(`Mock file does not exist: ${absolutePath}`);\n }\n\n const content = fs.readFileSync(absolutePath, \"utf-8\");\n const headerCommentMatch = content.match(/^(\\/\\*\\*[\\s\\S]*?\\*\\/)/);\n const headerComment = headerCommentMatch ? headerCommentMatch[1] : undefined;\n\n // 提取 @description\n let description: string | undefined;\n if (headerComment) {\n const descMatch = headerComment.match(/@description\\s+(.+?)(?:\\n|\\*\\/)/s);\n if (descMatch) {\n description = descMatch[1].trim();\n }\n }\n\n const { default: mockModule } = await import(\n `${absolutePath}?t=${Date.now()}`\n );\n\n const exportedConfig =\n typeof mockModule === \"function\" ? mockModule() : mockModule;\n const hasDynamicData = typeof exportedConfig?.data === \"function\";\n const config: MockFileConfig = {\n ...DEFAULT_CONFIG,\n ...(exportedConfig ?? {}),\n };\n\n const serializable = !hasDynamicData;\n const dataObj =\n typeof config.data === \"object\" && config.data !== null\n ? (config.data as Record<string, unknown>)\n : null;\n const isBinary = dataObj !== null && \"__binaryFile\" in dataObj;\n\n return {\n config,\n serializable,\n hasDynamicData,\n headerComment,\n description,\n dataText: isBinary\n ? `/* Binary file: ${dataObj?.__binaryFile} */`\n : serializable\n ? JSON.stringify(config.data ?? null, null, 2)\n : \"/* data is generated dynamically and cannot be edited here */\",\n isBinary,\n };\n};\n\nconst writeMockFile = async (\n filePath: string,\n mockInfo: Pick<MockFileInfo, \"config\" | \"headerComment\" | \"description\">,\n): Promise<void> => {\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(process.cwd(), filePath);\n\n let header = mockInfo.headerComment || \"\";\n\n // 如果提供了 description,更新或添加到注释中\n if (mockInfo.description !== undefined) {\n if (header) {\n // 替换现有的 @description\n if (/@description/.test(header)) {\n header = header.replace(\n /@description\\s+.+?(?=\\n|\\*\\/)/s,\n `@description ${mockInfo.description}`,\n );\n } else {\n // 在最后一行之前添加 @description\n header = header.replace(\n /\\*\\//,\n ` * @description ${mockInfo.description}\\n */`,\n );\n }\n }\n }\n\n const content = header ? `${header}\\n` : \"\";\n const finalContent = `${content}export default ${JSON.stringify(mockInfo.config, null, 2)}\\n`;\n\n try {\n const formattedCode = await prettier.format(finalContent, {\n parser: \"babel\",\n });\n fs.writeFileSync(absolutePath, formattedCode, \"utf-8\");\n } catch (error) {\n console.error(\"Error formatting code with prettier:\", error);\n fs.writeFileSync(absolutePath, finalContent, \"utf-8\");\n }\n};\n\nexport { saveMockData, buildMockIndex, parseMockModule, writeMockFile };\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/mockFileUtils.ts"],"sourcesContent":["import path from \"path\";\nimport fs from \"fs-extra\";\nimport prettier from \"prettier\";\n\nexport interface MockFileConfig {\n enable: boolean;\n data: unknown;\n delay: number;\n status: number;\n [key: string]: unknown;\n}\n\nexport interface MockFileInfo {\n config: MockFileConfig;\n serializable: boolean;\n hasDynamicData: boolean;\n headerComment?: string;\n description?: string;\n dataText: string;\n isBinary?: boolean;\n}\n\nexport const DEFAULT_CONFIG: MockFileConfig = {\n enable: true,\n data: null,\n delay: 0,\n status: 200,\n};\n\n// 简单实现 cross-platform 路径分隔符转换\nfunction toPosixPath(p: string): string {\n return p.replace(/\\\\/g, \"/\");\n}\n\n// 检测是否为二进制文件\nconst isBinaryResponse = (\n contentType: string | undefined,\n data: Buffer,\n): boolean => {\n if (!contentType) return false;\n\n const binaryTypes = [\n \"application/octet-stream\",\n \"application/pdf\",\n \"application/zip\",\n \"application/x-zip-compressed\",\n \"application/vnd.ms-excel\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \"application/msword\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.ms-powerpoint\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \"image/\",\n \"video/\",\n \"audio/\",\n ];\n\n return (\n binaryTypes.some((type) => contentType.toLowerCase().includes(type)) ||\n !isBufferTextLike(data)\n );\n};\n\n// 检测Buffer是否像文本数据\nconst isBufferTextLike = (buffer: Buffer): boolean => {\n try {\n // 检查前100字节是否包含非文本字符\n const sample = buffer.slice(0, 100);\n const text = sample.toString(\"utf8\");\n\n // 如果包含null字节或过多的控制字符,可能是二进制\n const nullBytes = [...sample].filter((b) => b === 0).length;\n const controlChars = [...sample].filter(\n (b) => b < 32 && b !== 9 && b !== 10 && b !== 13,\n ).length;\n\n return nullBytes === 0 && controlChars < 5;\n } catch {\n return false;\n }\n};\n\n// 从Content-Type获取文件扩展名\nconst getFileExtension = (\n contentType: string | undefined,\n url: string,\n): string => {\n // 从Content-Type映射\n const mimeMap: Record<string, string> = {\n \"application/json\": \"json\",\n \"application/pdf\": \"pdf\",\n \"application/zip\": \"zip\",\n \"application/vnd.ms-excel\": \"xls\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\": \"xlsx\",\n \"application/msword\": \"doc\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\":\n \"docx\",\n \"application/vnd.ms-powerpoint\": \"ppt\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\":\n \"pptx\",\n \"image/jpeg\": \"jpg\",\n \"image/png\": \"png\",\n \"image/gif\": \"gif\",\n \"text/plain\": \"txt\",\n \"text/html\": \"html\",\n \"text/css\": \"css\",\n \"application/javascript\": \"js\",\n \"text/xml\": \"xml\",\n };\n\n if (contentType && mimeMap[contentType.toLowerCase()]) {\n return mimeMap[contentType.toLowerCase()];\n }\n\n // 尝试从URL的查询参数中解析文件名\n try {\n const urlObj = new URL(url, \"http://localhost\");\n const fileName = urlObj.searchParams.get(\"file_name\");\n if (fileName) {\n const extensionMatch = fileName.match(/\\.([a-zA-Z0-9]+)$/);\n if (extensionMatch) {\n return extensionMatch[1];\n }\n }\n } catch (error) {\n // 忽略URL解析错误\n }\n\n return \"bin\";\n};\n\nconst saveMockData = async (\n url: string,\n method: string,\n data: Buffer | string,\n rootDir: string,\n statusCode?: number,\n contentType?: string,\n): Promise<string | null> => {\n try {\n const absoluteRootDir = path.isAbsolute(rootDir)\n ? rootDir\n : path.resolve(process.cwd(), rootDir);\n\n // 正确解析URL,处理查询参数和路径\n let pathname: string;\n let search: string;\n\n try {\n // 如果url包含协议信息,直接解析\n if (url.startsWith(\"http\")) {\n const urlObj = new URL(url);\n pathname = urlObj.pathname;\n search = urlObj.search;\n } else {\n // 如果是相对路径,先补全协议\n const urlObj = new URL(url, \"http://localhost\");\n pathname = urlObj.pathname;\n search = urlObj.search;\n }\n } catch (error) {\n // 如果URL解析失败,尝试手动处理\n const [pathPart, ...searchPart] = url.split(\"?\");\n pathname = pathPart;\n search = searchPart.length > 0 ? \"?\" + searchPart.join(\"?\") : \"\";\n }\n\n const filePath = path.join(\n absoluteRootDir,\n pathname.replace(/^\\//, \"\"),\n method.toLowerCase() + \".js\",\n );\n\n const dir = path.dirname(filePath);\n fs.ensureDirSync(dir);\n\n // 检测是否为二进制文件\n const isBuffer = Buffer.isBuffer(data);\n const binaryData = isBuffer ? data : Buffer.from(data || \"\");\n const isBinary = isBinaryResponse(contentType, binaryData);\n\n if (isBinary) {\n // 二进制文件处理\n const extension = getFileExtension(contentType, url);\n const binaryFilePath = filePath.replace(/\\.js$/, \".\" + extension);\n\n // 检查文件是否已存在,如果存在则不覆盖\n if (fs.existsSync(binaryFilePath)) {\n return null;\n }\n\n // 保存二进制文件\n fs.writeFileSync(binaryFilePath, binaryData);\n\n // 创建对应的JS配置文件\n const configContent = `/**\n * Mock data for ${pathname} (${method.toUpperCase()})${search || \"\"}\n * @description ${pathname}${search || \"\"} - Binary file (${extension})\n * Generated at ${new Date().toISOString()}\n */\nexport default {\n enable: false,\n data: {\n __binaryFile: '${extension}',\n __originalPath: '${pathname}',\n __originalQuery: '${search}',\n __originalUrl: '${pathname}${search || \"\"}',\n __contentType: '${contentType}',\n __fileSize: ${binaryData.length}\n },\n delay: 0,\n status: ${statusCode || 200}\n}`;\n\n try {\n const formattedCode = await prettier.format(configContent, {\n parser: \"babel\",\n });\n fs.writeFileSync(filePath, formattedCode, \"utf-8\");\n } catch (error) {\n fs.writeFileSync(filePath, configContent, \"utf-8\");\n }\n\n return filePath;\n } else {\n // JSON文件处理\n // 检查文件是否已存在,如果存在则不覆盖\n if (fs.existsSync(filePath)) {\n return null;\n }\n\n const dataStr = isBuffer ? data.toString(\"utf8\") : data || \"\";\n let jsonData;\n // 如果响应为空或只包含空白字符\n if (!dataStr || dataStr.trim() === \"\") {\n jsonData = {\n error: true,\n message: `Empty response (${statusCode || \"unknown status\"})`,\n status: statusCode || 404,\n data: null,\n };\n } else {\n try {\n jsonData = JSON.parse(dataStr);\n // 如果解析成功且是错误状态码,添加错误标记\n if (statusCode && statusCode >= 400) {\n if (typeof jsonData === \"object\" && jsonData !== null) {\n jsonData = {\n ...jsonData,\n __mockStatusCode: statusCode,\n __isErrorResponse: true,\n };\n } else {\n jsonData = {\n originalData: jsonData,\n __mockStatusCode: statusCode,\n __isErrorResponse: true,\n };\n }\n }\n } catch {\n // 如果不是JSON格式,可能是HTML错误页面或其他格式\n jsonData = {\n error: true,\n message: `Non-JSON response (${statusCode || \"unknown status\"})`,\n status: statusCode || 404,\n data: dataStr,\n __mockStatusCode: statusCode,\n __isErrorResponse: true,\n };\n }\n }\n\n const content = `/**\n * Mock data for ${pathname} (${method.toUpperCase()})${search || \"\"}\n * @description ${pathname}${search || \"\"}\n * Generated at ${new Date().toISOString()}\n */\nexport default {\n enable: false,\n data: ${JSON.stringify(jsonData)},\n delay: 0,\n status: ${statusCode || 200}\n}`;\n\n try {\n const formattedCode = await prettier.format(content, {\n parser: \"babel\",\n });\n fs.writeFileSync(filePath, formattedCode, \"utf-8\");\n return filePath;\n } catch (error) {\n fs.writeFileSync(filePath, content, \"utf-8\");\n return filePath;\n }\n }\n } catch (error) {\n console.error(`Failed to save mock data for ${url}:`, error);\n console.error(\n `URL details: url=${url}, method=${method}, statusCode=${statusCode}, contentType=${contentType}`,\n );\n throw error;\n }\n};\n\nconst recursiveReadAllFiles = (dir: string): string[] => {\n if (!fs.existsSync(dir)) return [];\n\n const files: string[] = [];\n try {\n const list = fs.readdirSync(dir);\n list.forEach((file) => {\n const filePath = path.join(dir, file);\n const stat = fs.statSync(filePath);\n if (stat.isDirectory()) {\n files.push(...recursiveReadAllFiles(filePath));\n } else {\n files.push(filePath);\n }\n });\n } catch (error) {\n console.error(`Error reading directory ${dir}:`, error);\n }\n return files;\n};\n\nconst buildMockIndex = (mockDir: string): Map<string, string> => {\n const mockFileMap = new Map<string, string>();\n\n if (!fs.existsSync(mockDir)) {\n fs.ensureDirSync(mockDir);\n return mockFileMap;\n }\n\n const files = recursiveReadAllFiles(mockDir);\n\n files.forEach((filePath) => {\n if (!filePath.endsWith(\".js\")) {\n return;\n }\n\n try {\n const relativePath = path.relative(mockDir, filePath);\n const method = path.basename(filePath, \".js\");\n const dirPath = path.dirname(relativePath);\n const urlPath = \"/\" + toPosixPath(dirPath);\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(process.cwd(), filePath);\n const key = `${urlPath}/${method}.js`.toLowerCase();\n\n mockFileMap.set(key, absolutePath);\n } catch (error) {\n console.error(`❌ [automock] 处理文件失败 ${filePath}:`, error);\n }\n });\n\n return mockFileMap;\n};\n\nconst parseMockModule = async (filePath: string): Promise<MockFileInfo> => {\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(process.cwd(), filePath);\n\n if (!fs.existsSync(absolutePath)) {\n throw new Error(`Mock file does not exist: ${absolutePath}`);\n }\n\n const content = fs.readFileSync(absolutePath, \"utf-8\");\n const headerCommentMatch = content.match(/^(\\/\\*\\*[\\s\\S]*?\\*\\/)/);\n const headerComment = headerCommentMatch ? headerCommentMatch[1] : undefined;\n\n // 提取 @description\n let description: string | undefined;\n if (headerComment) {\n const descMatch = headerComment.match(/@description\\s+(.+?)(?:\\n|\\*\\/)/s);\n if (descMatch) {\n description = descMatch[1].trim();\n }\n }\n\n const { default: mockModule } = await import(\n `${absolutePath}?t=${Date.now()}`\n );\n\n const exportedConfig =\n typeof mockModule === \"function\" ? mockModule() : mockModule;\n const hasDynamicData = typeof exportedConfig?.data === \"function\";\n const config: MockFileConfig = {\n ...DEFAULT_CONFIG,\n ...(exportedConfig ?? {}),\n };\n\n const serializable = !hasDynamicData;\n const dataObj =\n typeof config.data === \"object\" && config.data !== null\n ? (config.data as Record<string, unknown>)\n : null;\n const isBinary = dataObj !== null && \"__binaryFile\" in dataObj;\n\n return {\n config,\n serializable,\n hasDynamicData,\n headerComment,\n description,\n dataText: isBinary\n ? `/* Binary file: ${dataObj?.__binaryFile} */`\n : serializable\n ? JSON.stringify(config.data ?? null, null, 2)\n : \"/* data is generated dynamically and cannot be edited here */\",\n isBinary,\n };\n};\n\nconst writeMockFile = async (\n filePath: string,\n mockInfo: Pick<MockFileInfo, \"config\" | \"headerComment\" | \"description\">,\n): Promise<void> => {\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(process.cwd(), filePath);\n\n let header = mockInfo.headerComment || \"\";\n\n // 如果提供了 description,更新或添加到注释中\n if (mockInfo.description !== undefined) {\n if (header) {\n // 替换现有的 @description\n if (/@description/.test(header)) {\n header = header.replace(\n /@description\\s+.+?(?=\\n|\\*\\/)/s,\n `@description ${mockInfo.description}`,\n );\n } else {\n // 在最后一行之前添加 @description\n header = header.replace(\n /\\*\\//,\n ` * @description ${mockInfo.description}\\n */`,\n );\n }\n }\n }\n\n const content = header ? `${header}\\n` : \"\";\n const finalContent = `${content}export default ${JSON.stringify(mockInfo.config, null, 2)}\\n`;\n\n try {\n const formattedCode = await prettier.format(finalContent, {\n parser: \"babel\",\n });\n fs.writeFileSync(absolutePath, formattedCode, \"utf-8\");\n } catch (error) {\n console.error(\"Error formatting code with prettier:\", error);\n fs.writeFileSync(absolutePath, finalContent, \"utf-8\");\n }\n};\n\nexport { saveMockData, buildMockIndex, parseMockModule, writeMockFile, toPosixPath };\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -37,6 +37,7 @@ __export(mockFileUtils_exports, {
37
37
  buildMockIndex: () => buildMockIndex,
38
38
  parseMockModule: () => parseMockModule,
39
39
  saveMockData: () => saveMockData,
40
+ toPosixPath: () => toPosixPath,
40
41
  writeMockFile: () => writeMockFile
41
42
  });
42
43
  function toPosixPath(p) {
@@ -2572,7 +2573,8 @@ async function handleInspectorApi({
2572
2573
  for (const [key2, value] of newMockFileMap.entries()) {
2573
2574
  mockFileMap.set(key2, value);
2574
2575
  }
2575
- const key = apiPath + "/" + method.toLowerCase() + ".js";
2576
+ const normalizedPath = toPosixPath(apiPath);
2577
+ const key = (normalizedPath.startsWith("/") ? "" : "/") + normalizedPath.toLowerCase() + "/" + method.toLowerCase() + ".js";
2576
2578
  const filePath = mockFileMap.get(key);
2577
2579
  if (!filePath) {
2578
2580
  sendJson(
@@ -2702,6 +2704,20 @@ function automock(options) {
2702
2704
  mockDir,
2703
2705
  getMockFileMap: () => mockFileMap
2704
2706
  });
2707
+ server.httpServer?.once("listening", () => {
2708
+ setTimeout(() => {
2709
+ const address = server.httpServer?.address();
2710
+ if (address && typeof address === "object") {
2711
+ const protocol = server.config.server.https ? "https" : "http";
2712
+ const host = address.address === "::" || address.address === "0.0.0.0" ? "localhost" : address.address;
2713
+ const port = address.port;
2714
+ const mockApiUrl = `${protocol}://${host}:${port}${apiPrefix}`;
2715
+ console.log(
2716
+ ` \u279C \x1B[32mMock API\x1B[0m: \x1B[1m${mockApiUrl}\x1B[0m`
2717
+ );
2718
+ }
2719
+ }, 100);
2720
+ });
2705
2721
  if (inspector) {
2706
2722
  const inspectorConfig = normalizeInspectorConfig(inspector);
2707
2723
  server.httpServer?.once("listening", () => {
@@ -2713,10 +2729,10 @@ function automock(options) {
2713
2729
  const port = address.port;
2714
2730
  const inspectorUrl = `${protocol}://${host}:${port}${inspectorConfig.route}`;
2715
2731
  console.log(
2716
- ` \u279C \x1B[36mMock Inspector\x1B[0m: \x1B[1m${inspectorUrl}\x1B[0m`
2732
+ ` \u279C \x1B[95mMock Inspector\x1B[0m: \x1B[1m\x1B[96m${inspectorUrl}\x1B[0m`
2717
2733
  );
2718
2734
  }
2719
- }, 100);
2735
+ }, 150);
2720
2736
  });
2721
2737
  }
2722
2738
  server.middlewares.use(
@@ -2912,7 +2928,9 @@ function automock(options) {
2912
2928
  sendProxyRequest2(bodyStr);
2913
2929
  });
2914
2930
  } else {
2915
- sendProxyRequest2();
2931
+ req.on("end", () => {
2932
+ sendProxyRequest2();
2933
+ });
2916
2934
  }
2917
2935
  } catch (error) {
2918
2936
  console.error("\u274C [automock] \u4EE3\u7406\u8BF7\u6C42\u5F02\u5E38:", error);