anl 26.929.0 → 26.1008.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/README.md +33 -0
- package/config.d.ts +20 -0
- package/lib/index.cjs +1 -1
- package/lib/mock-generator/index.cjs +1 -0
- package/lib/mock-generator/reader.cjs +4 -0
- package/lib/mock-generator/template.cjs +1 -0
- package/lib/mock-generator/writer.cjs +2 -0
- package/lib/package.cjs +1 -1
- package/lib/swagger-codegen/components/enum-parser.cjs +5 -5
- package/lib/swagger-codegen/components/schema-resolver.cjs +5 -5
- package/lib/swagger-codegen/components/writer.cjs +1 -1
- package/lib/swagger-codegen/index.cjs +6 -6
- package/lib/swagger-codegen/path/index.cjs +4 -4
- package/lib/swagger-codegen/path/schema-resolver.cjs +2 -2
- package/lib/swagger-codegen/path/writer.cjs +3 -3
- package/lib/swagger-codegen/shared/prettier.cjs +1 -1
- package/lib/swagger-codegen/shared/schema-utils.cjs +1 -1
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[English](https://bianliuzhu.github.io/an-cli/#/en/) | [简体中文](https://bianliuzhu.github.io/an-cli/#/zh-cn/) | [日本語](https://bianliuzhu.github.io/an-cli/#/jp/) | [Español](https://bianliuzhu.github.io/an-cli/#/es/) | [Français](https://bianliuzhu.github.io/an-cli/#/fr/) | [Русский](https://bianliuzhu.github.io/an-cli/#/ru/) | [العربية](https://bianliuzhu.github.io/an-cli/#/ar/)
|
|
4
4
|
|
|
5
|
+
> [!NOTE]
|
|
6
|
+
> Starting from version **26.201.0**, new features are documented only in **English** and **简体中文**. Other language versions are no longer updated.
|
|
7
|
+
>
|
|
8
|
+
> 从 **26.201.0** 版本开始,新增功能仅更新 **英文** 和 **简体中文** 文档,其他语言文档不再更新。
|
|
9
|
+
|
|
5
10
|
---
|
|
6
11
|
|
|
7
12
|
# Overview
|
|
@@ -9,8 +14,10 @@
|
|
|
9
14
|
> an-cli is a frontend command-line tool that includes the following commands:
|
|
10
15
|
>
|
|
11
16
|
> - `anl type` command: A command-line tool that automatically generates TypeScript type definitions and API request functions based on Swagger JSON.
|
|
17
|
+
> - `anl mock` command: Generates Mock.js JSON files from local service response types for mock-service-plugin. [Documentation](https://bianliuzhu.github.io/an-cli/#/en/anl-mock).
|
|
12
18
|
> - `anl lint` command: Generates eslint, stylelint, prettier, commitLint, and VSCode related configurations for React or Vue projects
|
|
13
19
|
> - `anl git` command: Generates git local configuration with optional features: gitflow standard branch creation, git commit messages subject, and git custom command configuration
|
|
20
|
+
> - `anl skill` command: Initializes Agent Skill files for AI-assisted development, supporting api-report and api-mock skills.
|
|
14
21
|
|
|
15
22
|
## Features
|
|
16
23
|
|
|
@@ -26,6 +33,13 @@
|
|
|
26
33
|
- 🌐 Supports multiple Swagger server configurations
|
|
27
34
|
- 🔧 Supports HTTP methods like OPTIONS, HEAD, SEARCH
|
|
28
35
|
|
|
36
|
+
- `anl mock`
|
|
37
|
+
- Select one, multiple, or all generated services
|
|
38
|
+
- Recursively generate Mock.js templates with inferred response envelopes
|
|
39
|
+
- Write service-scoped JSON files with `@url` and `@method` headers
|
|
40
|
+
- Preserve existing mocks unless `--overwrite` is specified
|
|
41
|
+
- [Usage and plugin installation](https://bianliuzhu.github.io/an-cli/#/en/anl-mock)
|
|
42
|
+
|
|
29
43
|
- `anl lint`
|
|
30
44
|
- 🔍 One-click configuration for various lint tools
|
|
31
45
|
- 🎨 Automated ESLint configuration
|
|
@@ -40,6 +54,11 @@
|
|
|
40
54
|
- 🔄 Provides git custom command configuration and entry points
|
|
41
55
|
- 📦 Automated generation with zero configuration
|
|
42
56
|
|
|
57
|
+
- `anl skill`
|
|
58
|
+
- Initialize Agent Skill files for AI-assisted development
|
|
59
|
+
- Support api-report for API change reports and api-mock for AI-assisted mock generation
|
|
60
|
+
- [Usage](https://bianliuzhu.github.io/an-cli/#/en/anl-skill)
|
|
61
|
+
|
|
43
62
|
---
|
|
44
63
|
|
|
45
64
|
# 功能概述
|
|
@@ -47,8 +66,10 @@
|
|
|
47
66
|
> an-cli 是前端命令行工具,包含以下命令:
|
|
48
67
|
>
|
|
49
68
|
> - `anl type` 命令:基于 Swagger JSON 自动生成 TypeScript 类型定义和 API 请求函数的命令行工具。
|
|
69
|
+
> - `anl mock` 命令:从本地服务响应类型生成供 mock-service-plugin 使用的 Mock.js JSON 文件。[使用文档](https://bianliuzhu.github.io/an-cli/#/zh-cn/anl-mock)。
|
|
50
70
|
> - `anl lint` 命令: 生成 react 或 vue 项目 eslint、stylelint、prettier、commitLint、VSCode相关配置
|
|
51
71
|
> - `anl git` 命令: 生成 git 本地配置,并设有可选功能: gitflow 标准分支创建、git commit messages 主题、git 自定义命令配置
|
|
72
|
+
> - `anl skill` 命令:初始化 AI 辅助开发的 Agent Skill 文件,支持 api-report 和 api-mock。
|
|
52
73
|
|
|
53
74
|
## 功能特点
|
|
54
75
|
|
|
@@ -64,6 +85,13 @@
|
|
|
64
85
|
- 🌐 支持多 Swagger 服务器配置
|
|
65
86
|
- 🔧 支持 OPTIONS、HEAD、SEARCH 等 HTTP 方法
|
|
66
87
|
|
|
88
|
+
- `anl mock`
|
|
89
|
+
- 选择单个、多个或全部已生成的服务
|
|
90
|
+
- 推导响应外壳并递归生成 Mock.js 模板
|
|
91
|
+
- 按服务目录输出带有 `@url`、`@method` 注释的 JSON 文件
|
|
92
|
+
- 默认保留已有 mock,仅在指定 `--overwrite` 时覆盖
|
|
93
|
+
- [使用说明与插件安装](https://bianliuzhu.github.io/an-cli/#/zh-cn/anl-mock)
|
|
94
|
+
|
|
67
95
|
- `anl lint`
|
|
68
96
|
- 🔍 一键配置各种 lint 工具
|
|
69
97
|
- 🎨 ESLint 配置自动化
|
|
@@ -78,6 +106,11 @@
|
|
|
78
106
|
- 🔄 提供 git 自定义命令配置以及入口
|
|
79
107
|
- 📦 自动化生成 0 配置
|
|
80
108
|
|
|
109
|
+
- `anl skill`
|
|
110
|
+
- 初始化 AI 辅助开发的 Agent Skill 文件
|
|
111
|
+
- 支持 api-report 接口变更报告与 api-mock AI 辅助 mock 生成
|
|
112
|
+
- [使用说明](https://bianliuzhu.github.io/an-cli/#/zh-cn/anl-skill)
|
|
113
|
+
|
|
81
114
|
---
|
|
82
115
|
|
|
83
116
|
# نظرة عامة على الوظائف
|
package/config.d.ts
CHANGED
|
@@ -26,6 +26,22 @@ export interface IIncludeInterface {
|
|
|
26
26
|
dataLevel?: TDatalevel;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
export type MockValue = string | number | boolean | null | MockValue[] | { [key: string]: MockValue };
|
|
30
|
+
|
|
31
|
+
export interface MockGenerationOptions {
|
|
32
|
+
/** 数组默认元素数量,默认 1,取值 1-100 */
|
|
33
|
+
arrayLength?: number;
|
|
34
|
+
/** 类型递归最大深度,默认 8,取值 1-30 */
|
|
35
|
+
maxDepth?: number;
|
|
36
|
+
/** 覆盖响应根节点已有字段的模板值,例如 { code: 200, msg: 'OK' };不会添加未声明字段 */
|
|
37
|
+
responseDefaults?: Record<string, MockValue>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface MockConfig extends MockGenerationOptions {
|
|
41
|
+
/** mock-service-plugin 扫描的根目录,相对于项目根目录,默认 mocks */
|
|
42
|
+
mockDir?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
29
45
|
export interface IConfigSwaggerServer {
|
|
30
46
|
/** 服务名称,作为 `anl type --service <name>` 的匹配标识,建议在多服务场景下显式指定。
|
|
31
47
|
* 未指定时,会回退到 apiListFileName 去扩展名后的值作为标识。 */
|
|
@@ -54,6 +70,8 @@ export interface IConfigSwaggerServer {
|
|
|
54
70
|
modulePrefix?: string;
|
|
55
71
|
/** 响应模型转换配置 */
|
|
56
72
|
responseModelTransform?: IResponseModelTransform;
|
|
73
|
+
/** 当前服务的 mock 生成选项,覆盖全局 mock 配置 */
|
|
74
|
+
mock?: MockGenerationOptions;
|
|
57
75
|
/** 请求超时时间(毫秒),默认 60000 */
|
|
58
76
|
timeout?: number;
|
|
59
77
|
/** 命名空间隔离策略(服务级配置):
|
|
@@ -74,6 +92,8 @@ export interface ConfigType {
|
|
|
74
92
|
saveTypeFolderPath: string;
|
|
75
93
|
/** 存放生成的 api 文件的文件夹路径 */
|
|
76
94
|
saveApiListFolderPath: string;
|
|
95
|
+
/** anl mock 生成选项 */
|
|
96
|
+
mock?: MockConfig;
|
|
77
97
|
/** 兼容旧配置的 swagger json 地址(已迁移到 swaggerConfig 中) */
|
|
78
98
|
swaggerJsonUrl?: string;
|
|
79
99
|
/** swagger 服务器列表 */
|
package/lib/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=require(`./_virtual/rolldown_runtime.cjs`),t=require(`./package.cjs`),n=require(`./git-local-config/index.cjs`),r=require(`./skill-init/index.cjs`),
|
|
1
|
+
const e=require(`./_virtual/rolldown_runtime.cjs`),t=require(`./package.cjs`),n=require(`./git-local-config/index.cjs`),r=require(`./mock-generator/index.cjs`),i=require(`./skill-init/index.cjs`),a=require(`./standard/lint-init.cjs`),o=require(`./swagger-codegen/index.cjs`);let s=require(`commander`),c=require(`inquirer`);c=e.__toESM(c),s.program.version(`${t.version}`,`-v --version`).usage(`<command> [options]`),s.program.command(`type`).description(`auto interface`).option(`-s, --show <what>`,`show interface list after generation: miss | gen`).option(`-f, --format [config]`,`enable prettier formatting after generation; optionally specify a prettier config file path (e.g. --format .prettierrc.mjs)`).option(`-l, --log-level <level>`,`set log output level: silent | error | warn | info | verbose`).option(`-S, --service <names>`,"only regenerate the specified swagger service(s); comma-separated. Match by `name` (preferred) or `apiListFileName` without extension. Other services are kept untouched.").option(`-t, --template <name>`,`request template written to <saveApiListFolderPath>/config: axios | fetch | wx | uniapp | taro. Overrides an.config.ts. Only takes effect when the config folder does not exist yet.`).action(e=>{let t=(e.show??``).toLowerCase().trim(),n=t===`miss`||t===`missing`||t===`m`||t===`exclude`||t===`x`?`miss`:t===`gen`||t===`generated`||t===`g`||t===`include`||t===`i`?`gen`:void 0,r=e.service?e.service.split(`,`).map(e=>e.trim()).filter(Boolean):void 0;new o.Main().initialize(n,e.format,e.logLevel,r,e.template).catch(e=>{console.error(e)})}),s.program.command(`mock`).description(`generate Mock.js JSON files from generated service response types`).option(`-S, --service <names>`,`generate specified services; comma-separated names or API file names without extension`).option(`-a, --all`,`generate all available services without prompting`).option(`-d, --mock-dir <path>`,`override mock.mockDir (default: mocks)`).option(`--overwrite`,`overwrite existing mocks; existing files are skipped by default`).option(`-l, --log-level <level>`,`set log output level: silent | error | warn | info | verbose`).action(e=>{r.mockHandle(e).catch(e=>{console.error(e instanceof Error?e.message:e),process.exitCode=1})}),s.program.command(`lint`).description(`install linting tools (eslint, stylelint, prettier, commitlint, vscode)`).action(()=>a.lintHandle()),s.program.command(`git`).description(`config git Local custom command`).action(async()=>{let{features:e}=await c.default.prompt([{type:`checkbox`,name:`features`,message:`Select the required Git features (multi-select):`,choices:[{name:`gitflow standard branch creation`,value:`gitflow`},{name:`automatically set commit subject`,value:`commitSubject`},{name:`custom git command`,value:`customGitCommand`}],pageSize:10}]);n.gitHandle(e).catch(e=>{console.error(e)})}),s.program.command(`skill`).description(`initialize an agent skill into the current project`).action(()=>{i.skillHandle().catch(e=>{console.error(e)})}),s.program.parse(process.argv);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=require(`../_virtual/rolldown_runtime.cjs`),t=require(`../utils/logger.cjs`);require(`../utils/index.cjs`);const n=require(`../swagger-codegen/config-template.cjs`),r=require(`../swagger-codegen/shared/naming.cjs`),i=require(`./reader.cjs`),a=require(`./template.cjs`),o=require(`./writer.cjs`);let s=require(`inquirer`);s=e.__toESM(s);let c=require(`jiti`),l=require(`node:fs`),u=require(`node:fs/promises`);u=e.__toESM(u);let d=require(`node:path`);d=e.__toESM(d);function f(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function p(e){let t=d.default.join(e,`an.config.ts`),n=d.default.join(e,`an.config.json`),r=(0,l.existsSync)(t)?t:n;if(!(0,l.existsSync)(r))throw Error(`No an.config.ts or an.config.json found. Run anl type first.`);try{let e=r===t?await(0,c.createJiti)(__filename,{interopDefault:!0}).import(t):JSON.parse(await u.default.readFile(n,`utf8`)),i=f(e)&&f(e.default)?e.default:e;if(!f(i))throw Error(`The configuration must export an object.`);return i}catch(e){throw Error(`Cannot load ${r}: ${e instanceof Error?e.message:String(e)}`)}}function m(e,t,n,r){let i=t??n;if(typeof i!=`string`||!i.trim())throw Error(`${r} must be a non-empty directory path.`);return d.default.resolve(e,i)}function h(e,t){for(let n of[e,t]){if(n!==void 0&&!f(n))throw Error(`mock must be an object.`);if(n?.responseDefaults!==void 0&&!f(n.responseDefaults))throw Error(`mock.responseDefaults must be an object.`)}let n={...e,...t,responseDefaults:{...e?.responseDefaults,...t?.responseDefaults}};for(let[e,t]of[[`arrayLength`,100],[`maxDepth`,30]]){let r=n[e];if(r!==void 0&&(!Number.isInteger(r)||r<1||r>t))throw Error(`mock.${e} must be an integer between 1 and ${t}.`)}return n}async function g(e,t){let n=new Map,a=t.swaggerConfig?Array.isArray(t.swaggerConfig)?t.swaggerConfig:[t.swaggerConfig]:[];for(let e of a){if(!f(e))throw Error(`Each swaggerConfig service must be an object.`);let r=e.apiListFileName??t.apiListFileName??`index.ts`;if(typeof r!=`string`||/[\\/]/.test(r)||r.includes(`..`))throw Error(`apiListFileName must be a single file name.`);if(n.has(r))throw Error(`Duplicate apiListFileName in swaggerConfig: ${r}`);n.set(r,e)}let o=await u.default.readdir(e,{withFileTypes:!0}).catch(t=>{throw t.code===`ENOENT`?Error(`API directory not found: ${e}. Run anl type first.`):t}),s=[],c=new Set,l=new Set;for(let a of o.sort((e,t)=>e.name.localeCompare(t.name))){if(!a.isFile()||!a.name.endsWith(`.ts`)||a.name.endsWith(`.d.ts`))continue;let o=d.default.join(e,a.name);if(!i.isGeneratedApiFile(o))continue;let u=n.get(a.name),f=r.computeSegment(a.name);if(u?.name!==void 0&&typeof u.name!=`string`)throw Error(`Service name must be a string.`);let p=u?.name?.trim()??``,m=p.length?p:f;if(!f||c.has(f.toLowerCase()))throw Error(`Conflicting mock service directory for ${a.name}`);if(l.has(m.toLowerCase()))throw Error(`Duplicate mock service name: ${m}`);c.add(f.toLowerCase()),l.add(m.toLowerCase()),s.push({name:m,segment:f,fileName:a.name,apiFile:o,options:h(t.mock,u?.mock)})}if(!s.length)throw Error(`No generated service API files found in ${e}. Run anl type first.`);return s}async function _(e,t,n=!!(process.stdin.isTTY&&process.stdout.isTTY)){if(t.all&&t.service!==void 0)throw Error(`--all and --service cannot be used together.`);if(t.service!==void 0){let n=t.service.split(`,`).map(e=>e.trim().toLowerCase()).filter(Boolean);if(!n.length)throw Error(`--service requires at least one service name.`);let r=new Map(e.map(e=>[e.name.toLowerCase(),e]));for(let t of e)r.has(t.segment.toLowerCase())||r.set(t.segment.toLowerCase(),t);let i=n.filter(e=>!r.has(e));if(i.length)throw Error(`Unknown generated service(s): ${i.join(`, `)}. Available: ${e.map(e=>e.name).join(`, `)}.`);let a=new Set(n.map(e=>r.get(e)));return e.filter(e=>a.has(e))}if(t.all||!n||e.length===1)return e;let{picked:r}=await s.default.prompt([{type:`checkbox`,name:`picked`,message:`Select services to generate mock files (empty = all):`,choices:e.map((e,t)=>({name:`${e.name} (${e.fileName})`,value:t})),pageSize:Math.min(20,e.length)}]);return r.length?e.filter((e,t)=>r.includes(t)):e}async function v(e={},r=process.cwd()){let s=await p(r),c=n.createDefaultConfig(),l=e.logLevel??s.logLevel??`info`;if(![`silent`,`error`,`warn`,`info`,`verbose`].includes(l))throw Error(`Invalid log level: ${l}`);t.setLogLevel(l);let u=m(r,s.saveApiListFolderPath,c.saveApiListFolderPath,`saveApiListFolderPath`),f=m(r,s.saveTypeFolderPath,c.saveTypeFolderPath,`saveTypeFolderPath`),h=m(r,e.mockDir??s.mock?.mockDir,`mocks`,`mock.mockDir`);if(h===d.default.parse(h).root)throw Error(`mockDir cannot be the filesystem root.`);let v=await _(await g(u,s),e);t.log.section(`anl mock`,h),t.log.print(`Services: ${v.map(e=>e.name).join(`, `)}`);let{checker:y,operations:b}=i.readMockOperations({projectRoot:r,apiDir:u,typeDir:f,apiFiles:v.map(e=>e.apiFile)}),x=[],S=await o.writeMockFiles(h,b.map(e=>{let t=v.find(t=>t.apiFile===e.sourceFile),n=a.renderMockOperation(y,e,t.options);return x.push(...n.warnings),{service:t.segment,operation:e,template:n.template}}),{overwrite:e.overwrite,...s.formatting});for(let e of S.files)t.log.verbose(e);if(x.length){t.log.warning(`${x.length} field(s) need review (unknown or recursive types). Use --log-level verbose for details.`);for(let e of x)t.log.verbose(e)}return t.log.success(`Mock files: ${S.generated} generated, ${S.overwritten} overwritten, ${S.skipped} skipped.`),{...S,warnings:x}}exports.mockHandle=v;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
const e=require(`../_virtual/rolldown_runtime.cjs`);let t=require(`node:path`);t=e.__toESM(t);let n=require(`typescript`);n=e.__toESM(n);const r=new Set([`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`HEAD`,`OPTIONS`,`SEARCH`,`TRACE`]);function i(e){let t=new Map;for(let i of e.statements){if(!n.default.isImportDeclaration(i))continue;let e=i.importClause?.namedBindings;if(!(!e||!n.default.isNamedImports(e)))for(let n of e.elements){let e=(n.propertyName??n.name).text;r.has(e)&&t.set(n.name.text,e)}}let i=[];for(let a of e.statements)if(!(!n.default.isVariableStatement(a)||!a.modifiers?.some(e=>e.kind===n.default.SyntaxKind.ExportKeyword)))for(let e of a.declarationList.declarations){if(!n.default.isIdentifier(e.name)||!e.initializer||!n.default.isArrowFunction(e.initializer))continue;let a=e.initializer.body,o=n.default.isBlock(a)?a.statements.find(n.default.isReturnStatement)?.expression:a;for(;o&&(n.default.isParenthesizedExpression(o)||n.default.isAwaitExpression(o));)o=o.expression;if(!o||!n.default.isCallExpression(o)||!n.default.isIdentifier(o.expression))continue;let s=t.get(o.expression.text)??o.expression.text;!r.has(s)||!o.typeArguments?.length||i.push({name:e.name,call:o,method:s})}return i}function a(e){let t=n.default.sys.readFile(e);return t!==void 0&&i(n.default.createSourceFile(e,t,n.default.ScriptTarget.Latest,!0)).length>0}function o(e,t){if(e&&(n.default.isStringLiteral(e)||n.default.isNoSubstitutionTemplateLiteral(e)))return e.text;if(e&&n.default.isTemplateExpression(e)){let r=e.head.text;for(let i of e.templateSpans){let e=i.expression;if(n.default.isCallExpression(e)&&n.default.isIdentifier(e.expression)&&e.expression.text===`encodeURIComponent`&&e.arguments.length===1&&(e=e.arguments[0]),!n.default.isIdentifier(e))throw Error(`Cannot resolve the URL parameter of ${t}: ${e.getText()}`);r+=`:${e.text}${i.literal.text}`}return r}throw Error(`Cannot resolve the static URL of ${t}. Regenerate the API file with anl type.`)}function s(e){let r=t.default.join(e,`tsconfig.json`),i={};if(n.default.sys.fileExists(r)){let t=n.default.readConfigFile(r,e=>n.default.sys.readFile(e));if(t.error)throw Error(n.default.flattenDiagnosticMessageText(t.error.messageText,`
|
|
2
|
+
`));let a=n.default.parseJsonConfigFileContent(t.config,n.default.sys,e),o=a.errors.filter(e=>e.code!==18003);if(o.length)throw Error(o.map(e=>n.default.flattenDiagnosticMessageText(e.messageText,`
|
|
3
|
+
`)).join(`
|
|
4
|
+
`));i=a.options}return{target:n.default.ScriptTarget.ESNext,module:n.default.ModuleKind.ESNext,moduleResolution:n.default.ModuleResolutionKind.Bundler,...i,noEmit:!0,skipLibCheck:!0,strictNullChecks:!0,types:[]}}function c(e){let r=new Map;for(let a of e.apiFiles){let e=n.default.sys.readFile(a);if(e===void 0)throw Error(`API file not found: ${a}. Run anl type first.`);let o=n.default.createSourceFile(a,e,n.default.ScriptTarget.Latest,!0),s=e,c=i(o).map(e=>e.call);for(let e of c.reverse()){let t=e.arguments[2];t&&n.default.isStringLiteral(t)&&(t.text===`data`||t.text===`axios`)&&(s=`${s.slice(0,t.getStart(o))}'serve'${s.slice(t.end)}`)}r.set(t.default.resolve(a),s)}let a=s(e.projectRoot),c=n.default.createCompilerHost(a),l=c.getSourceFile.bind(c);c.getSourceFile=(e,i,a,o)=>{let s=r.get(t.default.resolve(e));return s===void 0?l(e,i,a,o):n.default.createSourceFile(e,s,i,!0)};let u=[...e.apiFiles,...n.default.sys.readDirectory(e.typeDir,[`.ts`,`.tsx`],[`**/node_modules/**`]),...n.default.sys.readDirectory(e.apiDir,[`.d.ts`],[`**/node_modules/**`])],d=n.default.createProgram([...new Set(u)],a,c),f=d.getTypeChecker(),p=[];for(let t of e.apiFiles){let e=d.getSourceFile(t);if(!e)throw Error(`Cannot load API file: ${t}`);for(let r of i(e)){let e=r.call.typeArguments[0],i=f.getTypeFromTypeNode(e),a=f.getTypeAtLocation(r.call),s=f.getAwaitedType(a)??a;if((i.flags|s.flags)&n.default.TypeFlags.Any)throw Error(`Cannot resolve the response type of ${r.name.text} in ${t}. Check the API declarations and ResponseModel<T>.`);let c=f.getSymbolAtLocation(r.name);p.push({name:r.name.text,method:r.method,url:o(r.call.arguments[0],r.name.text),sourceFile:t,description:c?n.default.displayPartsToString(c.getDocumentationComment(f)):``,responseType:s,bodyType:i,node:e})}}return{checker:f,operations:p}}exports.isGeneratedApiFile=a,exports.readMockOperations=c;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=require(`../_virtual/rolldown_runtime.cjs`);let t=require(`typescript`);t=e.__toESM(t);function n(e,n){if(e.isStringLiteral()||e.isNumberLiteral())return e.value;if(e.flags&t.default.TypeFlags.BooleanLiteral)return n.typeToString(e)===`true`}function r(e){let t=e.tags?.get(`format`)?.toLowerCase(),n=e.tags?.get(`example`)?.replace(/^['"]|['"]$/g,``)??``;return t===`uuid`||/(^id$|Id$|_id$|uuid)/.test(e.name)||/^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/i.test(n)?`@guid`:t===`email`||/email/i.test(e.name)?`@email`:t===`uri`||t===`url`||/(^url$|Url$|_url$)/.test(e.name)?`@url`:t===`date`?`@date("yyyy-MM-dd")`:t===`time`?`@time("HH:mm:ss")`:t===`date-time`||/^\d{4}-\d{2}-\d{2}T/.test(n)?`@datetime("yyyy-MM-ddTHH:mm:ss")Z`:/(At|Time|Date)$|_(at|time|date)$/.test(e.name)||/^(date|time|datetime)$/i.test(e.name)?`@datetime`:`@string`}function i(e,t){return t.isArrayType(e)||e.isIntersection()&&e.types.some(e=>t.isArrayType(e))}function a(e,t){let n=t.getPropertiesOfType(e.responseType),r=t.getPropertyOfType(e.responseType,`data`),i=e.responseType.getSymbol()?.name===`ResponseModel`||!!r&&e.responseType!==e.bodyType&&t.getTypeOfSymbolAtLocation(r,e.node)===e.bodyType;return i&&n.length>1&&n.every(n=>t.getPropertyOfType(e.bodyType,n.name))&&n.filter(e=>e.name!==`data`).every(n=>{let r=t.getPropertyOfType(e.bodyType,n.name);return t.isTypeAssignableTo(t.getTypeOfSymbolAtLocation(r,e.node),t.getTypeOfSymbolAtLocation(n,e.node))})?{type:e.bodyType,wrapped:!0}:{type:e.responseType,wrapped:i}}function o(e,o,s={}){let c=new Set,l=s.maxDepth??8,u=s.arrayLength??1,d=a(o,e),f=1e4,p=(e,t)=>c.add(`${e.path}: ${t}`),m=(a,h,g,_)=>{if(f--<=0)return c.add(`${o.name}: template size limit reached; reduce arrayLength or maxDepth`),h.optional?void 0:null;if(a.flags&(t.default.TypeFlags.Never|t.default.TypeFlags.Undefined|t.default.TypeFlags.Void))return;if(a.flags&t.default.TypeFlags.Null)return null;let v=n(a,e);if(v!==void 0)return v;if(a.flags&t.default.TypeFlags.Boolean)return`@boolean`;if(a.isUnion()){let r=a.types.filter(e=>!(e.flags&(t.default.TypeFlags.Null|t.default.TypeFlags.Undefined|t.default.TypeFlags.Never|t.default.TypeFlags.Void)));if(!r.length)return a.types.some(e=>e.flags&t.default.TypeFlags.Null)?null:void 0;let i=r.map(t=>n(t,e));return r.length>1&&i.every(e=>e!==void 0)?`@pick(${JSON.stringify(i)})`:m(r[0],h,g,_)}if(a.flags&t.default.TypeFlags.String)return r(h);if(a.flags&(t.default.TypeFlags.TemplateLiteral|t.default.TypeFlags.StringMapping))return p(h,`string pattern cannot be inferred safely; emitted @string`),`@string`;if(a.flags&t.default.TypeFlags.Number)return/timestamp/i.test(h.name)?`@integer(1700000000000,2000000000000)`:`@integer(0,100)`;if(a.isIntersection()){let e=a.types.find(e=>e.flags&(t.default.TypeFlags.StringLike|t.default.TypeFlags.NumberLike|t.default.TypeFlags.BooleanLike));if(e)return m(e,h,g,_)}if(a.flags&(t.default.TypeFlags.Any|t.default.TypeFlags.Unknown))return p(h,`${e.typeToString(a)} has no inferable JSON structure; emitted null`),null;if(g.has(a)||_>=l)return p(h,g.has(a)?`recursive type truncated`:`maximum depth ${l} reached`),i(a,e)?[]:h.optional?void 0:null;let y=new Set(g).add(a);if(e.isTupleType(a))return e.getTypeArguments(a).map((e,t)=>m(e,{name:h.name,path:`${h.path}[${t}]`},y,_+1)??null);if(i(a,e)){let n=e.getIndexTypeOfType(a,t.default.IndexKind.Number);if(!n)return p(h,`array element type cannot be resolved; emitted []`),[];if(y.has(n))return p(h,`recursive array truncated to []`),[];let r=Number(h.tags?.get(`maxItems`)??u),i=Math.min(u,Number.isFinite(r)?Math.max(0,r):u);return Array.from({length:i},(e,t)=>m(n,{name:h.name,path:`${h.path}[${t}]`},y,_+1)??null)}if(a.getSymbol()?.name===`Date`)return`@datetime("yyyy-MM-ddTHH:mm:ss")Z`;if(a.flags&(t.default.TypeFlags.BigIntLike|t.default.TypeFlags.ESSymbolLike|t.default.TypeFlags.TypeParameter)||e.getSignaturesOfType(a,t.default.SignatureKind.Call).length)return p(h,`${e.typeToString(a)} is not a supported JSON type; emitted null`),h.optional?void 0:null;let b=e.getPropertiesOfType(a),x={};for(let n of b){let r=e.getTypeOfSymbolAtLocation(n,o.node),i=m(r,{name:n.name,path:`${h.path}.${n.name}`,optional:!!(n.flags&t.default.SymbolFlags.Optional),tags:new Map(n.getJsDocTags(e).map(e=>[e.name,t.default.displayPartsToString(e.text)]))},y,_+1);if(i!==void 0){if(_===0){let a=e.getNonNullableType(r);d.wrapped&&(n.name===`success`&&a.flags&t.default.TypeFlags.Boolean&&(i=!0),n.name===`code`&&a.flags&t.default.TypeFlags.Number&&(i=1e4),(n.name===`msg`||n.name===`message`)&&a.flags&t.default.TypeFlags.String&&(i=`success`),n.name===`timestamp`&&a.flags&t.default.TypeFlags.Number&&(i=Date.now())),s.responseDefaults&&Object.hasOwn(s.responseDefaults,n.name)&&(i=s.responseDefaults[n.name])}Object.defineProperty(x,n.name,{value:i,enumerable:!0,configurable:!0,writable:!0})}}if(!b.length){let n=e.getIndexTypeOfType(a,t.default.IndexKind.String)??e.getIndexTypeOfType(a,t.default.IndexKind.Number);if(n)if(n.flags&(t.default.TypeFlags.Unknown|t.default.TypeFlags.Any))p(h,`dictionary values have no inferable structure; emitted {}`);else{let r=e.getIndexTypeOfType(a,t.default.IndexKind.String)?`key`:`0`;x[r]=m(n,{name:r,path:`${h.path}.${r}`},y,_+1)??null}}return x};return{template:m(d.type,{name:``,path:o.name},new Set,0)??null,warnings:[...c]}}exports.renderMockOperation=o;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`../_virtual/rolldown_runtime.cjs`);let t=require(`node:fs/promises`);t=e.__toESM(t);let n=require(`node:path`);n=e.__toESM(n);function r(e,t){let n=t.split(`?`)[0].split(`/`).filter(Boolean).map(e=>e.startsWith(`:`)?`:parameter`:e).join(`/`);return`${e.toUpperCase()}:/${n}`}async function i(e){let a=await t.default.readdir(e,{withFileTypes:!0}).catch(e=>{if(e.code===`ENOENT`)return[];throw e}),o=[];for(let s of a){let a=n.default.join(e,s.name);if(s.isSymbolicLink())throw Error(`Refusing to scan a symbolic link inside mockDir: ${a}`);if(s.isDirectory())o.push(...await i(a));else if(s.isFile()&&/\.json$/i.test(s.name)){let e=await t.default.readFile(a,`utf8`),n=/^\s*\/\*([\s\S]*?)\*\//.exec(e)?.[1],i=n&&/@url\s+(\S+)/.exec(n)?.[1],s=/@method\s+(\S+)/.exec(n??``)?.[1]??`GET`;o.push({fileName:a,route:i?r(s,i):void 0})}}return o}async function a(e,a,o={}){let s=n.default.resolve(e),c=await i(s),l=new Map(c.map(e=>[e.fileName,e])),u=new Map;for(let e of c)e.route&&u.set(e.route,[...u.get(e.route)??[],e.fileName]);let d=new Set,f=new Set,p=[],m={generated:0,overwritten:0,skipped:0,files:[]};for(let e of a){let{operation:t}=e;if(!e.service||!t.name||[e.service,t.name].some(e=>/[\\/]/.test(e)||e.includes(`..`)))throw Error(`Invalid mock service or operation file name: ${e.service}/${t.name}`);if(!t.url.startsWith(`/`)||/\s|\*\//.test(t.url))throw Error(`Invalid mock URL for ${t.name}: ${t.url}`);let i=r(t.method,t.url);if(d.has(i))throw Error(`Multiple selected APIs match ${i}. mock-service-plugin cannot distinguish them by service.`);d.add(i);let a=n.default.join(s,e.service,`${t.name}.json`),c=n.default.join(s,`${t.name}.json`),h=new Set(u.get(i)??[]);if(l.has(a)&&h.add(a),l.has(c)&&h.add(c),h.size&&!o.overwrite){m.skipped++;continue}if(h.size>1)throw Error(`Multiple existing mocks conflict with ${t.name}; resolve these files before overwriting: ${[...h].join(`, `)}`);let g=[...h][0]??a;if(n.default.dirname(g)!==s&&!g.startsWith(`${n.default.join(s,e.service)}${n.default.sep}`))throw Error(`Refusing to overwrite a matching mock in another service directory: ${g}`);if(f.has(g))throw Error(`Multiple APIs would overwrite the same mock file: ${g}`);f.add(g);let _=`${`/**\n * ${(t.description||t.name).replace(/\*\//g,`* /`).replace(/[\r\n]+/g,` `)}\n * @url ${t.url}\n * @method ${t.method}\n */\n`}${JSON.stringify(e.template,null,o.indentation??` `)}\n`.replace(/\n/g,o.lineEnding??`
|
|
2
|
+
`);p.push({fileName:g,content:_,existed:l.has(g)})}for(let e of p){await t.default.mkdir(n.default.dirname(e.fileName),{recursive:!0});try{await t.default.writeFile(e.fileName,e.content,{flag:o.overwrite?`w`:`wx`})}catch(e){if(!o.overwrite&&e.code===`EEXIST`){m.skipped++;continue}throw e}e.existed?m.overwritten++:m.generated++,m.files.push(e.fileName)}return m}exports.writeMockFiles=a;
|
package/lib/package.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e=`26.
|
|
1
|
+
var e=`26.1008.0`;Object.defineProperty(exports,`version`,{enumerable:!0,get:function(){return e}});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const e=require(`../../utils/logger.cjs`),t=require(`../../utils/index.cjs`),n=require(`../shared/
|
|
2
|
-
`)}\n} as const;\n\nexport type ${
|
|
3
|
-
`)}\n}`}catch(t){return e.log.error(`JSON 解析失败: ${t instanceof Error?t.message:String(t)}`),``}}normalizeEnumValues(e){return e.map((e,t)=>{if(typeof e==`string`||typeof e==`number`)return e;if(e&&typeof e==`object`){let n=e;if(n.value!==void 0&&(typeof n.value==`string`||typeof n.value==`number`))return n.value;if(n.key!==void 0&&(typeof n.key==`string`||typeof n.key==`number`))return n.key;if(n.id!==void 0&&(typeof n.id==`string`||typeof n.id==`number`))return n.id;try{return JSON.stringify(n)}catch{return`ENUM_${t}`}}return`ENUM_${t}`})}parseEnum(e,t){if(!Array.isArray(e.enum))return null;let i=[`integer`,`number`],a=this.normalizeEnumValues(e.enum),o=!!(e.type&&i.includes(e.type)),s=e.type===`string`&&a.every(e=>typeof e==`string`&&!isNaN(Number(e))),{customNames:c,descriptionMap:l,rawEnumJson:u}=this.extractEnumMetadata(e),d=this.config.enmuConfig.erasableSyntaxOnly;if(u){let e=this.convertJsonToEnumString(u,t);if(e)return{headerRef:``,renderStr:e}}let f=a.map((e,t)=>{let
|
|
4
|
-
`):`${
|
|
1
|
+
const e=require(`../../utils/logger.cjs`),t=require(`../../utils/index.cjs`),n=require(`../shared/naming.cjs`),r=require(`../shared/format.cjs`),i=require(`../shared/schema-utils.cjs`);var a=class{config;enumsMap=new Map;constructor(e){this.config=e}nullable(e){return i.nullableSuffix(e)}extractEnumMetadata(e){let n={},{enmuConfig:r}=this.config,i=e;if(e.example&&t.isValidJSON(e.example)&&(n.rawEnumJson=e.example),r?.varnames){let e=i[r.varnames];Array.isArray(e)&&(n.customNames=e.map(e=>`${e}`))}if(r?.comment){let e=i[r.comment];if(e&&typeof e==`object`&&!Array.isArray(e)){let t={};Object.entries(e).sort((e,t)=>e[0].localeCompare(t[0])).forEach(([e,n])=>{typeof n==`string`&&(t[e]=n)}),Object.keys(t).length&&(n.descriptionMap=t)}}return n}resolveEnumMemberName(e,t,r){let{customNames:i,isNumericEnum:a,treatStringAsNumeric:o}=r,s=i?.[t];return typeof s==`string`&&s.trim()?n.sanitizeIdentifierName(s):a||typeof e==`string`&&o?`NUMBER_${e}`:typeof e==`string`&&e?n.sanitizeIdentifierName(e.toUpperCase()):`ENUM_${t}`}convertJsonToEnumString(t,i){try{let e=JSON.parse(t);return this.config.enmuConfig.erasableSyntaxOnly?`export const ${i} = {\n${Object.entries(e).sort((e,t)=>e[0].localeCompare(t[0])).map(([e,t])=>`${r.getIndentation(this.config)}${n.sanitizeIdentifierName(e)}: '${String(t)}'`).join(`,
|
|
2
|
+
`)}\n} as const;\n\nexport type ${n.getEnumTypeName(this.config,i)} = typeof ${i}[keyof typeof ${i}];`:`export enum ${i} {\n${Object.entries(e).sort((e,t)=>e[0].localeCompare(t[0])).map(([e,t])=>`${r.getIndentation(this.config)}${n.sanitizeIdentifierName(e)} = '${String(t)}'`).join(`,
|
|
3
|
+
`)}\n}`}catch(t){return e.log.error(`JSON 解析失败: ${t instanceof Error?t.message:String(t)}`),``}}normalizeEnumValues(e){return e.map((e,t)=>{if(typeof e==`string`||typeof e==`number`)return e;if(e&&typeof e==`object`){let n=e;if(n.value!==void 0&&(typeof n.value==`string`||typeof n.value==`number`))return n.value;if(n.key!==void 0&&(typeof n.key==`string`||typeof n.key==`number`))return n.key;if(n.id!==void 0&&(typeof n.id==`string`||typeof n.id==`number`))return n.id;try{return JSON.stringify(n)}catch{return`ENUM_${t}`}}return`ENUM_${t}`})}parseEnum(e,t){if(!Array.isArray(e.enum))return null;let i=[`integer`,`number`],a=this.normalizeEnumValues(e.enum),o=!!(e.type&&i.includes(e.type)),s=e.type===`string`&&a.every(e=>typeof e==`string`&&!isNaN(Number(e))),{customNames:c,descriptionMap:l,rawEnumJson:u}=this.extractEnumMetadata(e),d=this.config.enmuConfig.erasableSyntaxOnly;if(u){let e=this.convertJsonToEnumString(u,t);if(e)return{headerRef:``,renderStr:e}}let f=a.map((e,t)=>{let n=this.resolveEnumMemberName(e,t,{customNames:c,isNumericEnum:o,treatStringAsNumeric:s}),i=/^[A-Za-z_$][\w$]*$/.test(n)?n:JSON.stringify(n),a=o?`${e}`:JSON.stringify(String(e)),u=d?`${i}: ${a},`:`${i} = ${a},`,f=l?.[String(e)];return f?[`${r.getIndentation(this.config)}/** ${f} */`,`${r.getIndentation(this.config)}${u}`].join(`
|
|
4
|
+
`):`${r.getIndentation(this.config)}${u}`});return f.length?d?{headerRef:``,renderStr:[`export const ${t} = {`,...f,`} as const;`,``,`export type ${n.getEnumTypeName(this.config,t)} = typeof ${t}[keyof typeof ${t}];`].join(`
|
|
5
5
|
`)}:{headerRef:``,renderStr:[`export enum ${t} {`,...f,`}`].join(`
|
|
6
|
-
`)}:null}handleEnum(e,i,a=!1){let o=
|
|
6
|
+
`)}:null}handleEnum(e,i,a=!1){let o=n.resolveSchemaName(i),s=n.getEnumTypeName(this.config,o),c=n.typeNameToFileName(o),l=n.appendEnumSegment(n.adjustImportPathForSegment(this.config.importEnumPath??``,n.getServerSegment(this.config)),n.getEnumSegment(this.config));if(t.isValidJSON(e.example)){let t=this.convertJsonToEnumString(e.example,o);return this.enumsMap.has(c)||this.enumsMap.set(c,{fileName:c,content:t}),{headerRef:`import type { ${s} } from '${l}';`,renderStr:`${r.getIndentation(this.config)}${i}${a?``:`?`}: ${s}${this.nullable(e.nullable)};`,comment:t,typeName:s}}let u=this.parseEnum(e,o);return u?(this.enumsMap.has(c)||this.enumsMap.set(c,{fileName:c,content:u.renderStr}),{headerRef:`import type { ${s} } from '${l}';`,renderStr:`${r.getIndentation(this.config)}${i}${a?``:`?`}: ${s}${this.nullable(e.nullable)};`,typeName:s}):null}addEnumByName(e,t){let r=n.typeNameToFileName(e);this.enumsMap.has(r)||this.enumsMap.set(r,{fileName:r,content:t})}hasEnum(e){let t=n.typeNameToFileName(e);return this.enumsMap.has(t)}getEnumTypeName(e){return n.getEnumTypeName(this.config,e)}getEnumImport(e){return`import type { ${n.getEnumTypeName(this.config,e)} } from '${n.appendEnumSegment(n.adjustImportPathForSegment(this.config.importEnumPath??``,n.getServerSegment(this.config)),n.getEnumSegment(this.config))}';`}};exports.EnumParser=a;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
const e=require(`../../utils/logger.cjs`);require(`../../utils/index.cjs`);const t=require(`../shared/
|
|
1
|
+
const e=require(`../../utils/logger.cjs`);require(`../../utils/index.cjs`);const t=require(`../shared/naming.cjs`),n=require(`../shared/format.cjs`),r=require(`../shared/schema-type.cjs`),i=require(`./enum-parser.cjs`);var a=class{schemas;config;enumParser;schemaNames;originalNames;schemasMap=new Map;constructor(e,n,r){this.diagnostics=r,this.schemas=e,this.config=n,this.enumParser=new i.EnumParser(n),this.schemaNames=t.createSchemaNameMap(e,n.enmuConfig.erasableSyntaxOnly),this.originalNames=new Map([...this.schemaNames].map(([e,t])=>[t,e]))}stringifyValue(e){if(typeof e==`string`)return e;try{return JSON.stringify(e)}catch{return String(e)}}buildDocComment(e,t){let n=[],{title:r,description:i}=e;r&&n.push(`@title ${r}`),i&&i.split(`
|
|
2
2
|
`).forEach((e,t)=>n.push(t===0?`@description ${e}`:e)),`deprecated`in e&&e.deprecated&&n.push(`@deprecated`),`default`in e&&e.default!==void 0&&n.push(`@default ${this.stringifyValue(e.default)}`),`example`in e&&e.example!==void 0&&n.push(`@example ${this.stringifyValue(e.example)}`),`format`in e&&e.format&&n.push(`@format ${e.format}`);let a=[[`pattern`,`@pattern`],[`minimum`,`@minimum`],[`maximum`,`@maximum`],[`exclusiveMinimum`,`@exclusiveMinimum`],[`exclusiveMaximum`,`@exclusiveMaximum`],[`minLength`,`@minLength`],[`maxLength`,`@maxLength`],[`minItems`,`@minItems`],[`maxItems`,`@maxItems`]];if(a.some(([t])=>e[t]!==void 0)){let t=a.map(([t,n])=>e[t]===void 0?``:`${n} ${this.stringifyValue(e[t])}`).filter(Boolean);n.push(...t)}if(!n.length)return``;let o=[`/**`];return n.forEach(e=>{e&&(e=e.replace(/\*\//g,`* /`),e.includes(`
|
|
3
3
|
`)?e.split(`
|
|
4
4
|
`).forEach(e=>o.push(` * ${e}`)):o.push(` * ${e}`))}),o.push(` */`),o.join(`
|
|
5
|
-
`)}nameTheHumpCenterStroke(e){let
|
|
5
|
+
`)}nameTheHumpCenterStroke(e){let n=e,i=this.schemaNames.get(n),a={typeName:i,fileName:t.typeNameToFileName(i),dataType:``};if(this.schemas){let e=this.schemas[n];e&&r.isNamedEnumSchema(e)?a.dataType=`enum`:a.dataType=e?.type}return a}parseRef(e){if(!e?.trim())return{headerRefStr:``,typeName:``,dataType:``};let{fileName:n,typeName:r,dataType:i=``}=this.nameTheHumpCenterStroke(e),a;if(i===`enum`){let e=t.getEnumTypeName(this.config,r),n=t.getServerSegment(this.config);a=`import type { ${e} } from '${t.appendEnumSegment(t.adjustImportPathForSegment(this.config.importEnumPath??``,n),t.getEnumSegment(this.config))}';`}else a=`import type { ${r} } from './${n}';`;return{headerRefStr:a,typeName:r,dataType:i}}renderType(i,a,o,s,c=!1){let l=new Set,u=r.renderSchemaType(i,{path:`#/components/schemas/${(this.originalNames.get(a)??a).replace(/~/g,`~0`).replace(/\//g,`~1`)}`,schemaPath:e=>this.diagnostics?.pathFor(e),direction:s,schemas:this.schemas,exactObject:c,reference:e=>{let n=r.resolveLocalSchemaReference(e,this.schemas,e=>l.add(e));if(!n)return`unknown`;let{headerRefStr:i,typeName:c,dataType:u}=this.parseRef(n.name);return c!==a&&!o.includes(i)&&o.push(i),u===`enum`?t.getEnumTypeName(this.config,c):r.directionalTypeName(c,n.schema,this.schemas,s)},property:(e,t,n)=>{if(`$ref`in e||!r.isNamedEnumSchema(e))return;let i=this.enumParser.handleEnum({...e,nullable:!1},t,n);return i?.headerRef&&!o.includes(i.headerRef)&&o.push(i.headerRef),i?.typeName},comment:e=>this.buildDocComment(e),indentation:n.getIndentation(this.config),diagnostic:(t,n,r)=>{this.diagnostics?this.diagnostics.add(t,n,r):r===`runtime`?e.log.verbose(`${n}: ${t}`):l.add(`${n}: ${t}`)}});for(let t of l)e.log.warn(`${a}: ${t}`);return u}generateContent(e,t){let i=[],a=this.renderType(e,t,i),o=[];for(let n of[`request`,`response`])r.schemaNeedsDirection(e,this.schemas,n)&&o.push(`export type ${n===`request`?`Request`:`Response`} = ${this.renderType(e,t,i,n)};`);r.hasMixedAdditionalProperties(e)&&o.push(`export type Exact<Value extends ${t}> = Value & (${this.renderType(e,t,i,void 0,!0)});`);let s=o.join(`
|
|
6
6
|
`).split(`
|
|
7
|
-
`).map(e=>`${
|
|
8
|
-
`),c=o.length?`\nexport namespace ${
|
|
9
|
-
`)}\n\n${e}`:e).replace(/\n/g,
|
|
7
|
+
`).map(e=>`${n.getIndentation(this.config)}${e}`).join(`
|
|
8
|
+
`),c=o.length?`\nexport namespace ${t} {\n${s}\n}`:``,l=e=>(i.length?`${i.join(`
|
|
9
|
+
`)}\n\n${e}`:e).replace(/\n/g,n.getLineEnding(this.config)),u=!(`$ref`in e)&&e.type===`object`&&!e.nullable&&!e.allOf&&!e.anyOf&&!e.oneOf&&!e.enum;return u&&a.startsWith(`{`)?l(`export interface ${t} ${a}${c}`):u&&a.startsWith(`Record<`)&&a.includes(t)?l(`export interface ${t} extends ${a} {}${c}`):l(`export type ${t} = ${a};${c}`)}main(){if(!this.schemas)return e.log.warn(`schemas 为空`),{enumsMap:this.enumParser.enumsMap,schemasMap:this.schemasMap};let n=Object.keys(this.schemas).sort();for(let e of n){let n=this.schemas[e];if(!(`$ref`in n)&&r.isNamedEnumSchema(n)){let t=this.schemaNames.get(e),r=this.enumParser.parseEnum(n,t);r?.renderStr&&!this.enumParser.hasEnum(t)&&this.enumParser.addEnumByName(t,r.renderStr);continue}let i=this.schemaNames.get(e),a=t.typeNameToFileName(i),o=this.generateContent(n,i);o&&((o.includes(`export enum `)||o.includes(`export const `)&&o.includes(`as const`))&&!this.enumParser.hasEnum(i)?this.enumParser.addEnumByName(i,o):this.schemasMap.set(i,{fileName:a,content:o}))}return{enumsMap:this.enumParser.enumsMap,schemasMap:this.schemasMap}}};exports.ComponentSchemaResolver=a;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=require(`../../_virtual/rolldown_runtime.cjs`),t=require(`../../utils/logger.cjs`),n=require(`../../utils/index.cjs`),r=require(`../shared/
|
|
1
|
+
const e=require(`../../_virtual/rolldown_runtime.cjs`),t=require(`../../utils/logger.cjs`),n=require(`../../utils/index.cjs`),r=require(`../shared/constants.cjs`),i=require(`../shared/naming.cjs`),a=require(`../shared/writer.cjs`);let o=require(`fs`);o=e.__toESM(o);var s=class{config;appendMode;constructor(e,t){this.config=e,this.appendMode=t?.appendMode??!1}async writeSchemas(e){let o=[],s=i.getServerSegment(this.config),c=s?`${s}/`:``,l=`${this.config.saveTypeFolderPath}/models/${c}`;await n.runWithConcurrency(Array.from(e.values()).sort((e,t)=>e.fileName.localeCompare(t.fileName)),32,async({fileName:e,content:i})=>{o.push(`export * from './${e}';`);let a=`${l}${e}.ts`;try{await n.writeFileRecursive(a,i),t.log.info(`${a.padEnd(r.PAD_END)} - Write done!`)}catch(e){t.log.warning(`${a} - Write failed: ${e instanceof Error?e.message:String(e)}`)}}),o.sort(),await a.writeIndexFileWithDedup(`${l}index.ts`,o,{appendMode:this.appendMode}),t.log.step(`Components parsed & written`)}async writeEnums(e){let s=[],c=i.getEnumSegment(this.config),l=c?`${c}/`:``,u=`${this.config.saveEnumFolderPath}/${l}`;await n.runWithConcurrency(Array.from(e.values()).sort((e,t)=>e.fileName.localeCompare(t.fileName)),32,async({fileName:e,content:i})=>{s.push(`export * from './${e}';`);let a=`${u}${e}.ts`;if(this.appendMode&&!c)try{await o.default.promises.readFile(a,`utf8`)!==i&&t.log.warning(`Enum 文件冲突:${a} 已存在且内容不同,将被当前服务覆盖。请检查不同 swagger 服务是否定义了同名但语义不一致的枚举,或开启 enumIsolation: 'segment'。`)}catch{}await n.writeFileRecursive(a,i),t.log.info(`${a.padEnd(r.PAD_END)} - Write done!`)}),s.sort(),await a.writeIndexFileWithDedup(`${u}index.ts`,s,{appendMode:this.appendMode}),t.log.step(`Enums written`)}};exports.ComponentWriter=s;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
const e=require(`../_virtual/rolldown_runtime.cjs`),t=require(`../utils/logger.cjs`),n=require(`../utils/index.cjs`),r=require(`./shared/
|
|
1
|
+
const e=require(`../_virtual/rolldown_runtime.cjs`),t=require(`../utils/logger.cjs`),n=require(`../utils/index.cjs`),r=require(`./shared/constants.cjs`),i=require(`./config-template.cjs`),a=require(`./shared/naming.cjs`),o=require(`./shared/writer.cjs`),s=require(`./components/index.cjs`),c=require(`./get-data.cjs`),l=require(`./path/index.cjs`),u=require(`./shared/openapi-document.cjs`),d=require(`./shared/prettier.cjs`),f=require(`./shared/request-template.cjs`),p=require(`./shared/schema-diagnostics.cjs`);let m=require(`inquirer`);m=e.__toESM(m);let h=require(`fs`);h=e.__toESM(h);let g=require(`path`);g=e.__toESM(g);let _=require(`chalk`);_=e.__toESM(_);let v=require(`jiti`),y;const b=process.env.NODE_ENV===`debug`,x=b?g.default.join(__dirname,`..`,`..`,`postbuild-assets`,`request-templates`):g.default.join(__dirname,`..`,`request-templates`),S=i.createDefaultConfig();b&&(S.saveTypeFolderPath=`apps/types`,S.saveApiListFolderPath=`apps/types`,S.saveEnumFolderPath=`apps/enums`);var C=class{schemas={};paths={};async handle(e,n,r){let i=a.getServiceTag(e);t.log.section(i||`service`,e.swaggerJsonUrl);try{t.spinner.start(`Fetching Swagger data...`);let i=await c.getSwaggerJson(e);if(!i)throw t.spinner.error(`Failed to fetch Swagger data`),Error(`无法获取 Swagger 数据`);let a=u.normalizeOpenApiDocument(i,e=>t.log.warn(e));t.spinner.success(`Swagger data fetched`),this.schemas=a.components?.schemas??{},this.paths=a.paths??{};let o=new p.SchemaDiagnostics(a),d=new s.default(this.schemas,e,{appendMode:n},o),f=new l.default(this.paths,a.components?.parameters,this.schemas,e,o);return t.spinner.start(`Generating types and APIs...`),await d.handle(),await f.handle(),t.spinner.success(`Types and APIs generated`),o.flush((e,n)=>{n===`info`?t.log.print(e):n===`verbose`?t.log.verbose(e):t.log.warn(e)}),r===`gen`?f.getGeneratedInterfacesForOutput():r===`miss`?f.getMissingInterfacesForOutput():null}catch(e){throw t.spinner.error(`Swagger generation failed`),e instanceof Error?Error(`Handle Swagger data failed: ${e.message}`):Error(`Handle Swagger data failed: unknown error`)}}getSystemLocale(){try{return Intl.DateTimeFormat().resolvedOptions().locale.toLowerCase()}catch{return(process.env.LANG??process.env.LC_ALL??process.env.LC_MESSAGES??``).toLowerCase()}}showLegacyConfigHint(e){let n={url:e.swaggerJsonUrl??`https://your.swagger.json`,publicPrefix:e.publicPrefix??``,apiListFileName:e.apiListFileName??`index.ts`,headers:e.headers??{}},r=this.getSystemLocale();r.startsWith(`zh`)||r.includes(`chinese`)?(t.log.print(`
|
|
2
2
|
检测到旧版配置,请更新 an.config.json:`),t.log.print(`1) 将 swaggerJsonUrl / publicPrefix / headers 移到 swaggerConfig 字段。`),t.log.print(`2) 单个服务可直接填写对象,多个服务请使用数组,并确保 apiListFileName 唯一。`),t.log.print(`示例:`),t.log.print(JSON.stringify({swaggerConfig:n},null,2)),t.log.print(``)):(t.log.print(`
|
|
3
|
-
Legacy configuration detected, please update an.config.json:`),t.log.print(`1) Move swaggerJsonUrl / publicPrefix / headers to swaggerConfig field.`),t.log.print(`2) Single service can be an object directly, multiple services should use an array, and ensure apiListFileName is unique.`),t.log.print(`Example:`),t.log.print(JSON.stringify({swaggerConfig:n},null,2)),t.log.print(``))}normalizeswaggerConfig(e,t){let n=!1,r=t?e.swaggerConfig:void 0;r||=(n=!0,{url:e.swaggerJsonUrl??``,publicPrefix:e.publicPrefix??``,apiListFileName:e.apiListFileName??`index.ts`,headers:e.headers??{},modulePrefix:e.modulePrefix});let i=(t,r)=>{let i=t.url||e.swaggerJsonUrl;if(!i)throw Error(`swaggerConfig[${r}] 缺少 url,请补充后重试。`);let a=t.publicPrefix??e.publicPrefix??``;!t.url&&e.swaggerJsonUrl&&(n=!0);let o=(t.apiListFileName??e.apiListFileName??`index.ts`).trim()||`index.ts`;if(/[\\/]/.test(o)||o.includes(`..`))throw Error(`swaggerConfig[${r}].apiListFileName 非法:"${o}",请使用单个文件名(如 "bff.ts"),不要包含路径分隔符或 ".."。`);let s=t.headers??e.headers??{},c=t.dataLevel??e.dataLevel??`serve`,l=t.parameterSeparator??e.parameterSeparator??`_`,u=t.includeInterface??e.includeInterface??[],d=t.excludeInterface??e.excludeInterface??[],f=t.includeTags??e.includeTags,p=t.excludeTags??e.excludeTags,m={url:i,publicPrefix:a,apiListFileName:o,headers:s,dataLevel:c,parameterSeparator:l,includeInterface:u,excludeInterface:d,modulePrefix:t.modulePrefix??e.modulePrefix??``,responseModelTransform:t.responseModelTransform??e.responseModelTransform,timeout:t.timeout??e.timeout,namespaceIsolation:t.namespaceIsolation??e.namespaceIsolation??`segment`,enumIsolation:t.enumIsolation??e.enumIsolation??`segment`};return t.name?.trim()&&(m.name=t.name.trim()),f!==void 0&&(m.includeTags=f),p!==void 0&&(m.excludeTags=p),m},a=Array.isArray(r)?r.map((e,t)=>i(e,t)):[i(r,0)];if(a.length===0)throw Error(`swaggerConfig 不能为空,请至少配置一个 swagger 服务。`);if(a.length>1){let e=new Set;a.forEach(t=>{if(e.has(t.apiListFileName))throw Error(`swaggerConfig 中 apiListFileName 重复:${t.apiListFileName},请为每个服务设置唯一文件名。`);e.add(t.apiListFileName)});let t=new Set;a.forEach(e=>{if(e.name){if(t.has(e.name))throw Error(`swaggerConfig 中 name 重复:${e.name},请为每个服务设置唯一名称。`);t.add(e.name)}})}return n&&this.showLegacyConfigHint(e),a}buildServerConfig(e,t,n,r){return{...e,swaggerJsonUrl:t.url,publicPrefix:t.publicPrefix??e.publicPrefix,headers:t.headers,apiListFileName:t.apiListFileName,dataLevel:t.dataLevel,parameterSeparator:t.parameterSeparator,includeInterface:t.includeInterface,excludeInterface:t.excludeInterface,includeTags:t.includeTags,excludeTags:t.excludeTags,modulePrefix:t.modulePrefix,responseModelTransform:t.responseModelTransform??e.responseModelTransform,timeout:t.timeout??e.timeout,namespaceIsolation:t.namespaceIsolation??e.namespaceIsolation??`segment`,enumIsolation:t.enumIsolation??e.enumIsolation??`segment`,swaggerConfig:t,__segment:n,__namespacePrefix:r}}async loadTsConfig(e){try{let t=await(0,v.createJiti)(__filename,{interopDefault:!0}).import(e),n=`default`in t&&t.default?t.default:t;return y=!0,n}catch(e){y=!0;let t=e instanceof Error?e.message:String(e);throw Error(`配置文件加载失败,请检查 an.config.ts 文件: ${t}`)}}async loadJsonConfig(e){try{let t=await h.default.promises.readFile(e,`utf8`);y=!0;try{return JSON.parse(t)}catch(e){throw y=!0,Error(`配置文件格式错误,请检查 an.config.json 的 JSON 格式是否正确: ${e instanceof Error?e.message:String(e)}`)}}catch(e){throw e instanceof Error&&`code`in e&&e.code,e}}async getConfig(e,
|
|
4
|
-
检测到多个 swagger 服务,请选择本次要重新生成的服务:`)),w=await this.promptSelectServices(g),T=w.length!==g.length):(w=g.map((e,t)=>t),T=!1),this.printExecutionPlan(g,w,T),await h.default.promises.mkdir(u.saveApiListFolderPath,{recursive:!0});let E=s?f.normalizeRequestTemplate(s,`CLI --template`):f.normalizeRequestTemplate(u.requestTemplate,`an.config`);await f.copyAjaxConfigFiles(u.saveApiListFolderPath,E,x),T?await this.cleanSelectedTargets(u,g,w,b,v):(await n.clearDirExcept(u.saveApiListFolderPath,[`config`]),await n.clearDir(u.saveTypeFolderPath),await n.clearDir(u.saveEnumFolderPath));let D=[];for(let t=0;t<w.length;t++){let n=w[t],r=this.buildServerConfig(u,g[n],b[n],C[n]),i=T?!0:t>0,a=await this.handle(r,i,e);e&&a&&D.push({serverUrl:g[n].url,list:a})}if(v&&(await this.writeTopLevelModelsBarrel(u,b,w,T),w.some(e=>
|
|
5
|
-
`)}}catch(e){let n=e instanceof Error?e.message:`Unknown error`;t.log.error(`Initialization failed: ${n}`),process.exitCode=1}}printExecutionPlan(e,n,
|
|
3
|
+
Legacy configuration detected, please update an.config.json:`),t.log.print(`1) Move swaggerJsonUrl / publicPrefix / headers to swaggerConfig field.`),t.log.print(`2) Single service can be an object directly, multiple services should use an array, and ensure apiListFileName is unique.`),t.log.print(`Example:`),t.log.print(JSON.stringify({swaggerConfig:n},null,2)),t.log.print(``))}normalizeswaggerConfig(e,t){let n=!1,r=t?e.swaggerConfig:void 0;r||=(n=!0,{url:e.swaggerJsonUrl??``,publicPrefix:e.publicPrefix??``,apiListFileName:e.apiListFileName??`index.ts`,headers:e.headers??{},modulePrefix:e.modulePrefix});let i=(t,r)=>{let i=t.url||e.swaggerJsonUrl;if(!i)throw Error(`swaggerConfig[${r}] 缺少 url,请补充后重试。`);let a=t.publicPrefix??e.publicPrefix??``;!t.url&&e.swaggerJsonUrl&&(n=!0);let o=(t.apiListFileName??e.apiListFileName??`index.ts`).trim()||`index.ts`;if(/[\\/]/.test(o)||o.includes(`..`))throw Error(`swaggerConfig[${r}].apiListFileName 非法:"${o}",请使用单个文件名(如 "bff.ts"),不要包含路径分隔符或 ".."。`);let s=t.headers??e.headers??{},c=t.dataLevel??e.dataLevel??`serve`,l=t.parameterSeparator??e.parameterSeparator??`_`,u=t.includeInterface??e.includeInterface??[],d=t.excludeInterface??e.excludeInterface??[],f=t.includeTags??e.includeTags,p=t.excludeTags??e.excludeTags,m={url:i,publicPrefix:a,apiListFileName:o,headers:s,dataLevel:c,parameterSeparator:l,includeInterface:u,excludeInterface:d,modulePrefix:t.modulePrefix??e.modulePrefix??``,responseModelTransform:t.responseModelTransform??e.responseModelTransform,timeout:t.timeout??e.timeout,namespaceIsolation:t.namespaceIsolation??e.namespaceIsolation??`segment`,enumIsolation:t.enumIsolation??e.enumIsolation??`segment`};return t.name?.trim()&&(m.name=t.name.trim()),f!==void 0&&(m.includeTags=f),p!==void 0&&(m.excludeTags=p),m},a=Array.isArray(r)?r.map((e,t)=>i(e,t)):[i(r,0)];if(a.length===0)throw Error(`swaggerConfig 不能为空,请至少配置一个 swagger 服务。`);if(a.length>1){let e=new Set;a.forEach(t=>{if(e.has(t.apiListFileName))throw Error(`swaggerConfig 中 apiListFileName 重复:${t.apiListFileName},请为每个服务设置唯一文件名。`);e.add(t.apiListFileName)});let t=new Set;a.forEach(e=>{if(e.name){if(t.has(e.name))throw Error(`swaggerConfig 中 name 重复:${e.name},请为每个服务设置唯一名称。`);t.add(e.name)}})}return n&&this.showLegacyConfigHint(e),a}buildServerConfig(e,t,n,r){return{...e,swaggerJsonUrl:t.url,publicPrefix:t.publicPrefix??e.publicPrefix,headers:t.headers,apiListFileName:t.apiListFileName,dataLevel:t.dataLevel,parameterSeparator:t.parameterSeparator,includeInterface:t.includeInterface,excludeInterface:t.excludeInterface,includeTags:t.includeTags,excludeTags:t.excludeTags,modulePrefix:t.modulePrefix,responseModelTransform:t.responseModelTransform??e.responseModelTransform,timeout:t.timeout??e.timeout,namespaceIsolation:t.namespaceIsolation??e.namespaceIsolation??`segment`,enumIsolation:t.enumIsolation??e.enumIsolation??`segment`,swaggerConfig:t,__segment:n,__namespacePrefix:r}}async loadTsConfig(e){try{let t=await(0,v.createJiti)(__filename,{interopDefault:!0}).import(e),n=`default`in t&&t.default?t.default:t;return y=!0,n}catch(e){y=!0;let t=e instanceof Error?e.message:String(e);throw Error(`配置文件加载失败,请检查 an.config.ts 文件: ${t}`)}}async loadJsonConfig(e){try{let t=await h.default.promises.readFile(e,`utf8`);y=!0;try{return JSON.parse(t)}catch(e){throw y=!0,Error(`配置文件格式错误,请检查 an.config.json 的 JSON 格式是否正确: ${e instanceof Error?e.message:String(e)}`)}}catch(e){throw e instanceof Error&&`code`in e&&e.code,e}}async getConfig(e,a){let o=g.default.join(e,`an.config.ts`),s=g.default.join(e,`an.config.json`);if(h.default.existsSync(o))return t.log.info(`检测到 an.config.ts 配置文件。`),this.loadTsConfig(o);if(h.default.existsSync(s))return this.loadJsonConfig(s);y=!1,t.log.warning(`配置文件不存在,将自动创建配置文件。`);let c=r.DEFAULT_REQUEST_TEMPLATE;if(a)c=f.normalizeRequestTemplate(a,`CLI --template`);else if(process.stdin.isTTY&&process.stdout.isTTY){let{picked:e}=await m.default.prompt([{type:`list`,name:`picked`,message:`请选择请求模板(决定 <saveApiListFolderPath>/config/ 下的底层实现):`,choices:[{name:`axios —— 基于 axios(浏览器/Node,需 npm i axios)`,value:`axios`},{name:`fetch —— 基于原生 fetch(浏览器/现代 Node,无额外依赖)`,value:`fetch`},{name:`wx —— 基于 wx.request(微信小程序)`,value:`wx`},{name:`uniapp —— 基于 uni.request(uni-app 跨端)`,value:`uniapp`},{name:`taro —— 基于 Taro.request(Taro 3+ 跨端)`,value:`taro`}],default:`axios`}]);c=e}else t.log.warning(`未指定 --template 且当前非交互终端,requestTemplate 默认为 "${r.DEFAULT_REQUEST_TEMPLATE}"。可通过 --template 指定或修改 an.config.ts 后重跑。`);let l=i.createDefaultConfig(c);await n.writeFileRecursive(o,i.renderTsConfig(l));try{await h.default.promises.mkdir(l.saveApiListFolderPath,{recursive:!0}),await f.copyAjaxConfigFiles(l.saveApiListFolderPath,c,x),t.log.success(`配置文件已创建(requestTemplate=${c}),并已同步初始化 ${l.saveApiListFolderPath}/config/。请检查 an.config.ts 后重新运行以生成 API。`)}catch(e){t.log.warning(`初始化 ${l.saveApiListFolderPath}/config/ 失败:${e instanceof Error?e.message:String(e)}。配置文件已创建,请修正后重跑。`)}return l}resolveSelectedServiceIndices(e,t){let n=new Map;e.forEach((e,t)=>{e.name&&n.set(e.name.toLowerCase(),t)}),e.forEach((e,t)=>{let r=a.computeSegment(e.apiListFileName).toLowerCase();r&&!n.has(r)&&n.set(r,t)});let r=new Set,i=[];for(let e of t){let t=e.toLowerCase(),a=n.get(t);a===void 0?i.push(e):r.add(a)}if(i.length){let t=e.map(e=>e.name??a.computeSegment(e.apiListFileName)).filter(Boolean).join(`, `);throw Error(`未找到匹配的 swagger 服务:${i.join(`, `)}。可用服务:${t||`<无>`}`)}return Array.from(r).sort((e,t)=>e-t)}async promptSelectServices(e){let t=e.map((e,t)=>{let n=a.getServiceIdentifier(e,t);return{name:`${n} (${e.apiListFileName} ← ${e.url})`,value:t,short:n}}),{picked:n}=await m.default.prompt([{type:`checkbox`,name:`picked`,message:`请选择需要重新生成的 swagger 服务(空选 = 全部):`,choices:t,pageSize:Math.min(20,t.length)}]);return!n||n.length===0?e.map((e,t)=>t):n.sort((e,t)=>e-t)}async initialize(e,r,i,o,s){let c=process.cwd();try{let l=await this.getConfig(c,s),u={...S,...l},p=i??u.logLevel;p&&t.setLogLevel(p);let m=Object.prototype.hasOwnProperty.call(l,`swaggerConfig`),g=this.normalizeswaggerConfig(u,m);if(!y)return;let v=g.length>1,b=v?g.map(e=>a.computeSegment(e.apiListFileName)):g.map(()=>``);if(v){let e=new Map;b.forEach((t,n)=>{if(!t)throw Error(`swaggerConfig[${n}].apiListFileName="${g[n].apiListFileName}" 无法派生有效 segment(清洗后为空),请使用包含字母/数字的文件名。`);if(t.toLowerCase()===`index`)throw Error(`swaggerConfig[${n}].apiListFileName="${g[n].apiListFileName}" 派生 segment 为 "index",会与顶层 models/index.ts 形成自引用,请改为其他文件名(如 "main.ts" / "default.ts")。`);if(e.has(t))throw Error(`swaggerConfig 多个服务的 apiListFileName 在派生 segment 时冲突:"${g[e.get(t)].apiListFileName}" 与 "${g[n].apiListFileName}" 都解析为 "${t}",请改名以避免目录覆盖。`);e.set(t,n)})}let C=g.map(e=>(e.namespaceIsolation??u.namespaceIsolation??`segment`)===`none`?``:a.segmentToNamespacePrefix(a.computeSegment(e.apiListFileName)));if(v){let e=g.map((e,t)=>({s:e,idx:t})).filter(({s:e})=>(e.namespaceIsolation??u.namespaceIsolation??`segment`)===`none`).map(({s:e})=>e.apiListFileName);e.length>0&&t.log.warning(`检测到多服务场景下以下服务关闭了 namespace 前缀(namespaceIsolation: 'none'):${e.join(`, `)}。 不同服务若存在同名 path,将会因 declare namespace 全局合并而互相污染类型,请确认是否符合预期。`);let n=new Map;C.forEach((e,t)=>{if(e){if(n.has(e))throw Error(`swaggerConfig 多个服务派生的 namespace 前缀冲突:"${g[n.get(e)].apiListFileName}" 与 "${g[t].apiListFileName}" 都解析为前缀 "${e}",请改名以避免类型同名合并。`);n.set(e,t)}})}let w,T;o?.length?(w=this.resolveSelectedServiceIndices(g,o),T=w.length!==g.length):g.length>1&&process.stdin.isTTY&&process.stdout.isTTY?(t.log.print(_.default.cyan(`
|
|
4
|
+
检测到多个 swagger 服务,请选择本次要重新生成的服务:`)),w=await this.promptSelectServices(g),T=w.length!==g.length):(w=g.map((e,t)=>t),T=!1),this.printExecutionPlan(g,w,T),await h.default.promises.mkdir(u.saveApiListFolderPath,{recursive:!0});let E=s?f.normalizeRequestTemplate(s,`CLI --template`):f.normalizeRequestTemplate(u.requestTemplate,`an.config`);await f.copyAjaxConfigFiles(u.saveApiListFolderPath,E,x),T?await this.cleanSelectedTargets(u,g,w,b,v):(await n.clearDirExcept(u.saveApiListFolderPath,[`config`]),await n.clearDir(u.saveTypeFolderPath),await n.clearDir(u.saveEnumFolderPath));let D=[];for(let t=0;t<w.length;t++){let n=w[t],r=this.buildServerConfig(u,g[n],b[n],C[n]),i=T?!0:t>0,a=await this.handle(r,i,e);e&&a&&D.push({serverUrl:g[n].url,list:a})}if(v&&(await this.writeTopLevelModelsBarrel(u,b,w,T),w.some(e=>a.isEnumIsolated(g[e])&&b[e])&&await this.writeTopLevelEnumsBarrel(u,b,g,w,T),g.every(a.isEnumIsolated)&&await this.warnLegacyTopLevelEnums(u)),r!==void 0&&r!==!1&&await d.formatGeneratedFiles(u,r,T?{servers:g,selectedIndices:w,segments:b,isolateBySegment:v}:void 0),t.log.banner(`All done — see you next time!`),e&&D.length>0){let n=e===`miss`?`excludeInterface`:`includeInterface`;for(let{serverUrl:e,list:r}of D)w.length>1?t.log.print(_.default.cyan(`\n[${n}] ${e}`)):t.log.print(_.default.cyan(`\n[${n}]`)),t.log.print(JSON.stringify(r,null,2));t.log.print(`
|
|
5
|
+
`)}}catch(e){let n=e instanceof Error?e.message:`Unknown error`;t.log.error(`Initialization failed: ${n}`),process.exitCode=1}}printExecutionPlan(e,n,r){let i=new Set(n);t.log.print(_.default.cyan(r?`
|
|
6
6
|
[anl type] 选择型生成,仅处理以下服务:`:`
|
|
7
|
-
[anl type] 全量生成,将处理所有服务:`)),e.forEach((e,n)=>{let
|
|
7
|
+
[anl type] 全量生成,将处理所有服务:`)),e.forEach((e,n)=>{let r=a.getServiceIdentifier(e,n);i.has(n)?t.log.print(` ${_.default.green(`●`)} ${r} (${e.apiListFileName}) ${_.default.gray(e.url)}`):t.log.print(` ${_.default.gray(`○ skip `)}${r} (${e.apiListFileName}) ${_.default.gray(e.url)}`)}),t.log.print(``)}async cleanSelectedTargets(e,t,r,i,o){for(let s of r)if(await n.clearDir(`${e.saveApiListFolderPath}/${t[s].apiListFileName}`),o){let r=i[s];r&&(await n.clearDir(`${e.saveTypeFolderPath}/connectors/${r}`),await n.clearDir(`${e.saveTypeFolderPath}/models/${r}`),a.isEnumIsolated(t[s])&&await n.clearDir(`${e.saveEnumFolderPath}/${r}`))}else await n.clearDir(`${e.saveTypeFolderPath}/connectors`),await n.clearDir(`${e.saveTypeFolderPath}/models`)}async writeTopLevelModelsBarrel(e,r,i,a){let s=`${e.saveTypeFolderPath}/models/index.ts`,c=a?i.map(e=>r[e]).filter(Boolean):r.filter(Boolean);await n.writeFileRecursive(s,o.mergeNamespaceExports(a?await o.readIndexLines(s):[],c,c,a).join(`
|
|
8
8
|
`)+`
|
|
9
|
-
`),t.log.info(`${s} - Top-level models barrel ${
|
|
9
|
+
`),t.log.info(`${s} - Top-level models barrel ${a?`merged`:`written`}.`)}async writeTopLevelEnumsBarrel(e,r,i,s,c){let l=`${e.saveEnumFolderPath}/index.ts`,u=(c?s:r.map((e,t)=>t)).filter(e=>a.isEnumIsolated(i[e])).map(e=>r[e]).filter(Boolean),d=Array.from(new Set(u)),f=(await Promise.all(d.map(async t=>{let n=`${e.saveEnumFolderPath}/${t}`;try{return(await h.default.promises.readdir(n,{withFileTypes:!0})).some(e=>e.isFile()&&/\.(d\.)?ts$/.test(e.name)&&e.name!==`index.ts`)?t:``}catch{return``}}))).filter(Boolean);await n.writeFileRecursive(l,o.mergeNamespaceExports(await o.readIndexLines(l),d,f).join(`
|
|
10
10
|
`)+`
|
|
11
11
|
`),t.log.info(`${l} - Top-level enums barrel ${c?`merged`:`written`}.`)}async warnLegacyTopLevelEnums(e){let n=e.saveEnumFolderPath;try{let e=(await h.default.promises.readdir(n,{withFileTypes:!0})).filter(e=>e.isFile()&&e.name.endsWith(`.ts`)&&e.name!==`index.ts`).map(e=>e.name);e.length>0&&t.log.warning(`检测到 ${n} 顶层仍存在历史枚举文件(${e.slice(0,5).join(`, `)}${e.length>5?` ...`:``}),共 ${e.length} 个。\n若已切换到 enumIsolation: 'segment'(默认),建议执行 \`anl type\` 全量重生成或手动清理这些文件,避免与 ${n}/<segment>/ 下的隔离枚举混用。`)}catch{}}};b&&new C().initialize().catch(e=>{console.error(e)}),exports.Main=C;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
const e=require(`../../utils/logger.cjs`);require(`../../utils/index.cjs`);const t=require(`../shared/
|
|
1
|
+
const e=require(`../../utils/logger.cjs`);require(`../../utils/index.cjs`);const t=require(`../shared/naming.cjs`),n=require(`../shared/format.cjs`),r=require(`../shared/http.cjs`),i=require(`./naming.cjs`),a=require(`./schema-resolver.cjs`),o=require(`./writer.cjs`);var s=function(e){return e.GET=`get`,e.PUT=`put`,e.POST=`post`,e.DELETE=`delete`,e.OPTIONS=`options`,e.HEAD=`head`,e.PATCH=`patch`,e.TRACE=`trace`,e}(s||{});const c={typeMapping:new Map([[`integer`,`number`],[`string`,`string`],[`boolean`,`boolean`],[`binary`,`File`],[`number`,`number`],[`null`,`null`],[`undefined`,`undefined`]]),errorHandling:{throwOnError:!1,logErrors:!0}};var l=class{pathsObject={};nonArrayType=[`boolean`,`object`,`number`,`string`,`integer`];pathKey=``;contentBody={payload:{path:[],query:[],header:[],body:[]},response:``,_response:``,fileName:``,method:``,requestPath:``,summary:``,description:``,apiName:``,typeName:``,deprecated:!1,contentType:`application/json`};Map=new Map;config;errors=[];parameters={};schemas={};schemaResolver;writer;apiListFileContent=[];allowedInterfaceKeys=new Set;allInterfaceKeys=new Set;constructor(e,t,r,i,s){this.pathsObject=e,this.parameters=t??{},this.schemas=r??{},this.config=n.applyFormattingDefaults({...c,...i,typeMapping:new Map([...c.typeMapping??[],...i.typeMapping??[]])}),this.schemaResolver=new a.SchemaResolver(this.config,this.schemas,this.parameters,this.handleError.bind(this),s),this.writer=new o.PathWriter(this.config)}handleError(t){if(!t.path&&this.contentBody.requestPath&&(t.path=this.contentBody.requestPath),!t.method&&this.contentBody.method&&(t.method=this.contentBody.method),this.errors.push(t),this.config.errorHandling?.logErrors&&e.log.error(e.formatParseError(t)),this.config.errorHandling?.throwOnError)throw Error(`${t.type}: ${t.message}`)}getIndentation(){return n.getIndentation(this.config)}getDoubleIndentation(){let e=this.getIndentation();return e+e}generateParamComment(e,t){let n=[],r=[];if(e.description&&r.push(e.description),e.deprecated&&r.push(`@deprecated`),e.schema&&typeof e.schema==`object`&&`example`in e.schema){let t=e.schema.example,n=typeof t==`string`?t:JSON.stringify(t);r.push(`@example ${n}`)}if(e.schema&&typeof e.schema==`object`&&`default`in e.schema){let t=e.schema.default,n=typeof t==`string`?t:JSON.stringify(t);r.push(`@default ${n}`)}return r.length>0&&(r.length===1&&!r[0].includes(`
|
|
2
2
|
`)?n.push(`${t}/** ${r[0]} */`):(n.push(`${t}/**`),r.forEach(e=>{e.includes(`
|
|
3
3
|
`)?e.split(`
|
|
4
|
-
`).forEach(e=>{n.push(`${t} * ${e}`)}):n.push(`${t} * ${e}`)}),n.push(`${t} */`))),n}parametersItemHandle(r,i,a,o){let s=this.getDoubleIndentation(),c=`$ref`in r?r:null,l=`name`in r?r:null;if(c?.$ref&&c.$ref.startsWith(`#/components/parameters/`)&&this.parameters){let e=c.$ref.replace(`#/components/parameters/`,``),t=this.parameters[e];this.parametersItemHandle(t,i,a,o)}if(l){if(!l.schema){this.handleError({type:`PARAMETERS`,message:`Parameter "${l.name}" has no schema defined, skipping`});return}let r=this.schemaResolver.main(l.schema,`request`);if(!r||typeof r!=`string`){this.handleError({type:`PARAMETERS`,message:`Failed to parse schema for parameter "${l.name}"`,details:r});return}if(l.in===`path`){this.generateParamComment(l,s).forEach(e=>i.push(e));let e=
|
|
5
|
-
`)} }`:e,
|
|
4
|
+
`).forEach(e=>{n.push(`${t} * ${e}`)}):n.push(`${t} * ${e}`)}),n.push(`${t} */`))),n}parametersItemHandle(r,i,a,o){let s=this.getDoubleIndentation(),c=`$ref`in r?r:null,l=`name`in r?r:null;if(c?.$ref&&c.$ref.startsWith(`#/components/parameters/`)&&this.parameters){let e=c.$ref.replace(`#/components/parameters/`,``),t=this.parameters[e];this.parametersItemHandle(t,i,a,o)}if(l){if(!l.schema){this.handleError({type:`PARAMETERS`,message:`Parameter "${l.name}" has no schema defined, skipping`});return}let r=this.schemaResolver.main(l.schema,`request`);if(!r||typeof r!=`string`){this.handleError({type:`PARAMETERS`,message:`Failed to parse schema for parameter "${l.name}"`,details:r});return}if(l.in===`path`){this.generateParamComment(l,s).forEach(e=>i.push(e));let e=t.sanitizeIdentifierName(l.name);i.push(`${s}type ${e} = ${n.indentContinuationLines(r,s)};`),this.contentBody.payload._path?this.contentBody.payload._path[e]=r:this.contentBody.payload._path={[e]:r}}else if(l.in===`query`){this.generateParamComment(l,s).forEach(e=>a.push(e));let e=l.required===!0?``:`?`,i=t.formatPropertyName(l.name);a.push(`${s}${i}${e}: ${n.indentContinuationLines(r,s)};`),this.contentBody.payload._query?this.contentBody.payload._query[l.name]=r:this.contentBody.payload._query={[l.name]:r}}else if(l.in===`header`){this.generateParamComment(l,s).forEach(e=>o.push(e));let e=l.required===!0?``:`?`,i=t.formatPropertyName(l.name);o.push(`${s}${i}${e}: ${n.indentContinuationLines(r,s)};`),this.contentBody.payload._header?this.contentBody.payload._header[l.name]=r:this.contentBody.payload._header={[l.name]:r}}else l.in===`cookie`?e.log.verbose(`Cookie parameter "${l.name}" detected but not added to type definition`):this.handleError({type:`PARAMETERS`,message:`Unknown parameter location "${l.in}" for parameter "${l.name}"`})}}requestParametersParse(e){let t=this.getIndentation(),n=[],r=[],i=[];e?.map(e=>this.parametersItemHandle(e,n,r,i)),n.length!==0&&(n.unshift(`${t}namespace Path {`),n.push(`${t}}`)),r.length!==0&&(r.unshift(`${t}interface Query {`),r.push(`${t}}`)),i.length!==0&&(i.unshift(`${t}interface Header {`),i.push(`${t}}`)),this.contentBody.payload.path=n,this.contentBody.payload.query=r,this.contentBody.payload.header=i}pickPreferredRequestMediaType(e){if(!(!e||typeof e!=`object`)){for(let t of[`multipart/form-data`,`application/x-www-form-urlencoded`,`application/json`])if(e[t])return t;return Object.keys(e).sort()[0]}}pickRequestBodyContent(e){let t=e.content;if(!t||typeof t!=`object`)return{mediaType:`application/json`,schema:null};let n=this.pickPreferredRequestMediaType(t),i=n&&r.SUPPORTED_REQUEST_TYPES_ALL.includes(n)?n:`application/json`,a=n?t[n]:null;return{mediaType:i,schema:a&&typeof a==`object`&&a.schema?a.schema:null}}requestBodyObjectParse(e){let t=this.getIndentation(),{schema:r}=this.pickRequestBodyContent(e);if(r){let e=this.schemaResolver.main(r,`request`);return`${t}type Body = ${n.indentContinuationLines(Array.isArray(e)?`{ ${e.join(`
|
|
5
|
+
`)} }`:e,t)};`}}requestBodyParse(e){if(!e)return`{}`;let t=`$ref`in e?e:null,n=`content`in e?e:null;if(t){let e=this.schemaResolver.referenceObjectParse(t,`request`);return`${this.getIndentation()}type Body = ${e}`}return n&&Object.keys(e).length!==0?this.requestBodyObjectParse(n):`{}`}normalizemodulePrefix(e){if(!e||e.trim()===``)return``;let t=e.trim();return t=t.replace(/\/+$/g,``),t.startsWith(`/`)||(t=`/`+t),t}apiRequestItemHandle(e){let{payload:t,requestPath:n,_response:i,method:a,typeName:o,apiName:s,contentType:c}=e,{_path:l,_query:u,body:d}=t,f=e.dataLevel??this.config.dataLevel??`serve`,p=this.normalizemodulePrefix(this.config.modulePrefix),m=()=>{let e=[],t=Object.keys(l??{}).sort();for(let n of t)e.push(`${n}: ${o}.Path.${n}`);let n=e.join(e.length>1?`,`:``);return n===``?n:n+`,`},h=()=>{let e=u?`query: ${o}.Query,`:``;return e===``?``:`${e}`},g=()=>{let e=d.length>0?`body: ${o}.Body,`:``;return e===``?``:`${e}`},_=m(),v=h(),y=g(),b=()=>{let e=r.SUPPORTED_REQUEST_UPLOAD_TYPES.includes(c)?`headers: { 'Content-Type': '${c}' }`:void 0;return[`{`,e?`${e},`:``,`...params, `,v===``?``:`query,`,y===``?``:`body,`,`},`].join(``)},x=(_+v+y).replace(/,$/,``);return[`export const ${s} = `,`(`,x,x===``?`params?: IRequestFnParams`:`, params?: IRequestFnParams`,`)`,` => `,a,`${i?`<${o}.Response>`:``}`,`(`,`\`${p}${n}\`,`,b(),`'${f}'`,`);`].join(``)}pickPreferredResponse(e){let t=e[200];if(t)return t;let n=Object.keys(e).filter(e=>/^2\d\d$/.test(e)).sort((e,t)=>Number(e)-Number(t));if(n.length>0)return e[n[0]];let r=e.default;if(r)return r}responseHandle(e){let t=this.pickPreferredResponse(e);if(!t){this.contentBody.response=`type Response = unknown`,this.contentBody._response=`unknown`;return}let r=`content`in t?t:null,i=`$ref`in t?t:null;if(r===null&&i===null&&(this.contentBody.response=`type Response = unknown`,this.contentBody._response=`unknown`),i){let e=this.schemaResolver.referenceObjectParse(i,`response`);this.config.responseModelTransform&&(e=this.schemaResolver.transformResponseModel(e,this.config.responseModelTransform)),this.contentBody.response=`type Response = ${e}`,this.contentBody._response=e}if(r){let e=this.schemaResolver.responseObjectParse(r);if(this.config.responseModelTransform&&(e=this.schemaResolver.transformResponseModel(e,this.config.responseModelTransform)),Array.isArray(e)){if(e.length===1&&e[0]===`unknown`)this.contentBody.response=`type Response = ${e.join(`
|
|
6
6
|
`)};`;else{let t=this.config.formatting?.lineEnding,n=this.config.formatting?.indentation;this.contentBody.response=`interface Response {${t}${e.join(`
|
|
7
7
|
`)}${t}${n}};`}this.contentBody._response=`${e.join(`
|
|
8
|
-
`)}`}else this.contentBody.response=`type Response = ${
|
|
8
|
+
`)}`}else this.contentBody.response=`type Response = ${n.indentContinuationLines(e,this.getIndentation())};`,this.contentBody._response=`${e}`}}requestHandle(e){if(e.parameters&&this.requestParametersParse(e.parameters),e.requestBody){let t=this.requestBodyParse(e.requestBody);if(Array.isArray(t))this.contentBody.payload.body=t;else if(t){let e=t?.split(`
|
|
9
9
|
`)||[];this.contentBody.payload.body=e}}}computeAllowedInterfaceKeys(){let e=[],t=Object.keys(this.pathsObject).sort();for(let n of t){let t=this.pathsObject[n];if(!t)continue;let r=Object.keys(t).sort();for(let i of r){let r=t[i];if(!r||typeof r!=`object`||!Object.values(s).includes(i))continue;let a=`${n}|${String(i).toUpperCase()}`,o=Array.isArray(r.tags)?r.tags:[];e.push({key:a,pathKey:n,method:i,tags:o})}}this.allInterfaceKeys=new Set(e.map(e=>e.key));let n;n=this.config.excludeTags&&this.config.excludeTags.length>0?new Set(e.filter(e=>!e.tags.some(e=>this.config.excludeTags.includes(e))).map(e=>e.key)):new Set(e.map(e=>e.key));let r;if(this.config.excludeInterface&&this.config.excludeInterface.length>0){let e=new Set(this.config.excludeInterface.map(e=>`${e.path}|${e.method.toUpperCase()}`));r=new Set([...n].filter(t=>!e.has(t)))}else r=new Set(n);let i;i=this.config.includeTags&&this.config.includeTags.length>0?new Set(e.filter(e=>r.has(e.key)&&e.tags.some(e=>this.config.includeTags.includes(e))).map(e=>e.key)):new Set;let a;if(this.config.includeInterface&&this.config.includeInterface.length>0){let e=new Set(this.config.includeInterface.map(e=>`${e.path}|${e.method.toUpperCase()}`));a=new Set([...r].filter(t=>e.has(t)))}else a=new Set;return i.size===0&&a.size===0?new Set(r):new Set([...i,...a])}parsePathItemObject(e,t,n){if(!e)return;let r=Object.keys(e).sort();for(let a of r){let r=e[a];if(r){let e=a.toUpperCase(),o=t+`|`+e;if(!this.allowedInterfaceKeys.has(o))continue;let s=this.config.includeInterface?.find(e=>t===e.path&&e.method===a),{apiName:c,typeName:l,fileName:u,path:d}=i.convertEndpointString(o,this.config),f=r.requestBody&&`content`in r.requestBody?this.pickRequestBodyContent(r.requestBody).mediaType:`application/json`;this.contentBody={payload:{path:[],query:[],header:[],body:[]},response:``,_response:``,fileName:u,method:e,typeName:l,requestPath:d,apiName:c,summary:r.summary,description:r.description,deprecated:r.deprecated??!1,contentType:f,dataLevel:s?.dataLevel},this.requestHandle(r),this.responseHandle(r.responses),r.summary&&n.push([`/**`,`
|
|
10
10
|
`,r.deprecated?` * @deprecated ${r.summary}`:` * ${r.summary}`,`
|
|
11
11
|
`,` */`].join(``));let p=this.apiRequestItemHandle(this.contentBody);if(n.push(p,``),!this.Map.has(o)){let{payload:e}=this.contentBody,t={...this.contentBody,payload:{...e,path:[...e.path],query:[...e.query],header:[...e.header],body:[...e.body]}};this.Map.set(o,t)}}}}parseData(){this.allowedInterfaceKeys=this.computeAllowedInterfaceKeys();let e=Object.keys(this.pathsObject).sort();for(let t of e){let e=this.pathsObject[t];e&&this.parsePathItemObject(e,t,this.apiListFileContent)}return this.Map}async writeFile(){if(await this.writer.write(this.Map,this.apiListFileContent,[]),this.Map=new Map,this.errors.length>0){e.log.warning(`Completed with ${this.errors.length} error(s):`);let t=new Map;for(let e of this.errors){let n=e.type;t.has(n)||t.set(n,[]),t.get(n).push(e)}for(let[n,r]of t){e.log.warning(` ${n}: ${r.length} error(s)`);for(let t of r){let n=[t.method,t.path].filter(Boolean).join(` `);e.log.warn(` ${n?n+` - `:``}${t.message}`)}}}}async handle(){try{this.parseData(),await this.writeFile()}catch(e){if(this.handleError({type:`SCHEMA`,message:`Failed to handle schema`,details:e}),this.config.errorHandling?.throwOnError)throw e}}getGeneratedInterfacesForOutput(){return[...this.allowedInterfaceKeys].sort().map(e=>{let t=e.lastIndexOf(`|`);return{path:e.slice(0,t),method:e.slice(t+1).toLowerCase()}})}getMissingInterfacesForOutput(){return[...this.allInterfaceKeys].filter(e=>!this.allowedInterfaceKeys.has(e)).sort().map(e=>{let t=e.lastIndexOf(`|`);return{path:e.slice(0,t),method:e.slice(t+1).toLowerCase()}})}};exports.default=l;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`../../utils/logger.cjs`);require(`../../utils/index.cjs`);const t=require(`../shared/
|
|
2
|
-
`);a.push(`${i}${r}?: {${t}\n${i}};`)}else a.push(`${i}${r}?: ${
|
|
1
|
+
const e=require(`../../utils/logger.cjs`);require(`../../utils/index.cjs`);const t=require(`../shared/naming.cjs`),n=require(`../shared/format.cjs`),r=require(`../shared/schema-type.cjs`),i=require(`../shared/schema-utils.cjs`),a=require(`../shared/http.cjs`);var o=class{config;schemas;parameters;referenceCache=new Map;schemaDiagnostics=new Set;schemaNames;resolvedToOriginalName=new Map;handleError;constructor(e,n,r,i,a){this.diagnostics=a,this.config=e,this.schemas=n??{},this.schemaNames=t.createSchemaNameMap(n,e.enmuConfig.erasableSyntaxOnly),this.resolvedToOriginalName=new Map([...this.schemaNames].map(([e,t])=>[t,e])),this.parameters=r??{},this.handleError=i}stringifySchemaResult(e){if(Array.isArray(e)){let t=n.getLineEnding(this.config),r=n.getIndentation(this.config);return`{${t}${e.join(t)}${t}${r}}`}return e}handleComplexType(e){return this.stringifySchemaResult(this.main(e))}referenceObjectParse(e,n){try{let i=e.$ref,a=`${n??`shared`}:${i}`,o=this.referenceCache.get(a);if(o)return o;let s=r.resolveLocalSchemaReference(i,this.schemas,e=>this.handleError({type:`REFERENCE`,message:e}));if(!s)return`unknown`;let c=s.name,l=this.schemaNames.get(c),u=t.typeNameToFileName(l),d=this.schemas?.[c],f=!1;if(d&&!(`$ref`in d)){let e=`properties`in d||d.type===`object`,t=d.type===`array`||`items`in d;r.isNamedEnumSchema(d)&&!e&&!t&&(f=!0)}let p=f?t.getEnumTypeName(this.config,l):r.directionalTypeName(l,s.schema,this.schemas,n),m=t.getServerSegment(this.config),h=t.appendEnumSegment(t.adjustImportPathForSegment(this.config.importEnumPath??``,m),t.getEnumSegment(this.config)),g=m?`../../models/${m}`:`../models`,_=f?`import('${h}/${u}').${p}`:`import('${g}/${u}').${p}`;return this.referenceCache.set(a,_),_}catch(e){return this.handleError({type:`REFERENCE`,message:`Failed to parse reference object`,details:e}),`unknown`}}nonArraySchemaObjectParse(e){return this.main(e)}arraySchemaObjectParse(e){return e.type===`array`?this.stringifySchemaResult(this.main(e)):``}propertiesParse(e){return i.formatObjectProperties(e,this.config,e=>this.main(e))}transformResponseModel(e,t){if(!t)return e;if(t.modelPattern&&typeof e==`string`){let n=/^import\('[^']+'\)\.([\w$]+)(?:\.(?:Request|Response))?$/.exec(e),r=n?n[1]:e,i=new RegExp(t.modelPattern);if(!i.test(this.resolvedToOriginalName.get(r)??r)&&!i.test(r))return e}try{switch(t.type){case`unwrap`:{let n=t.dataField??`data`;if(Array.isArray(e))return this.handleError({type:`RESPONSE`,message:`Response model unwrap is not supported for inline object response types`}),e;let r=/^import\('([^']+)'\)\.([\w$]+)(?:\.(?:Request|Response))?$/.exec(e);if(r){let[,e,t]=r,i=this.resolvedToOriginalName.get(t)??t,a=this.schemas?.[i];if(a&&!(`$ref`in a)){let e=a;if(e.properties?.[n]){let t=e.properties[n];return this.main(t,`response`)}else this.handleError({type:`RESPONSE`,message:`Field "${n}" not found in response type "${t}"`})}}return e}case`wrap`:{if(!t.wrapperFields)return this.handleError({type:`RESPONSE`,message:`wrapperFields is required when using wrap transform`}),e;let r=n.getIndentation(this.config),i=r+r,a=[],o=t.dataField??`data`;for(let[r,s]of Object.entries(t.wrapperFields))if(r===o)if(Array.isArray(e)){let t=e.join(`
|
|
2
|
+
`);a.push(`${i}${r}?: {${t}\n${i}};`)}else a.push(`${i}${r}?: ${n.indentContinuationLines(e,i)};`);else a.push(`${i}${r}?: ${s};`);return a}case`replace`:return t.wrapperType?t.wrapperType:(this.handleError({type:`RESPONSE`,message:`wrapperType is required when using replace transform`}),e);default:return e}}catch(t){return this.handleError({type:`RESPONSE`,message:`Failed to transform response model`,details:t}),e}}responseObjectParse(e){try{let t=e.content;if(!t)return`unknown`;let n;for(let e of a.SUPPORTED_REQUEST_TYPES_ALL)if(t[e]?.schema){n=t[e].schema;break}return n??=Object.keys(t).sort().map(e=>t[e].schema).find(e=>e!==void 0),n?this.main(n,`response`):`unknown`}catch(e){return this.handleError({type:`RESPONSE`,message:`Failed to parse response object`,details:e}),`unknown`}}main(t,i){try{return t?r.renderSchemaType(t,{schemaPath:e=>this.diagnostics?.pathFor(e),indentation:n.getIndentation(this.config),direction:i,schemas:this.schemas,reference:e=>this.referenceObjectParse({$ref:e},i),mapping:e=>this.config.typeMapping?.get(e.format??``)??this.config.typeMapping?.get(e.type??``),diagnostic:(t,n,r)=>{if(this.diagnostics){this.diagnostics.add(t,n,r);return}let i=`${r}: ${n}: ${t}`;this.schemaDiagnostics.has(i)||(this.schemaDiagnostics.add(i),r===`runtime`?e.log.verbose(`${n}: ${t}`):e.log.warn(`${n}: ${t}`))}}):`unknown`}catch(e){return this.handleError({type:`SCHEMA`,message:`Failed to parse schema`,details:e}),`unknown`}}};exports.SchemaResolver=o;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const e=require(`../../utils/logger.cjs`),t=require(`../../utils/index.cjs`),n=require(`../shared/
|
|
2
|
-
`).map(e=>e.replace(/\*\//g,`*\\/`));return t.length===1?[`/** ${t[0]} */`]:[`/**`,...t.map(e=>` * ${e}`),` */`]}constructor(e){this.config=e}async write(a,o,s){let c=this.config.saveTypeFolderPath,l=r.getServerSegment(this.config),u=l?`${c}/connectors/${l}`:`${c}/connectors`,d=async(r,a)=>{let{payload:o,response:c,fileName:l}=a,[,d]=r.split(`|`);s.includes(d)||s.push(d);let f=this.formatFileHeaderComment(a.description),p=[...f,...f.length?[``]:[],`declare namespace ${a.typeName} {`,...o.path,...o.query,...o.header,...o.body,`${
|
|
3
|
-
`).replace(/\r?\n/g,
|
|
1
|
+
const e=require(`../../utils/logger.cjs`),t=require(`../../utils/index.cjs`),n=require(`../shared/constants.cjs`),r=require(`../shared/naming.cjs`),i=require(`../shared/format.cjs`);var a=class{config;formatFileHeaderComment(e){if(!e)return[];let t=e.split(`
|
|
2
|
+
`).map(e=>e.replace(/\*\//g,`*\\/`));return t.length===1?[`/** ${t[0]} */`]:[`/**`,...t.map(e=>` * ${e}`),` */`]}constructor(e){this.config=e}async write(a,o,s){let c=this.config.saveTypeFolderPath,l=r.getServerSegment(this.config),u=l?`${c}/connectors/${l}`:`${c}/connectors`,d=async(r,a)=>{let{payload:o,response:c,fileName:l}=a,[,d]=r.split(`|`);s.includes(d)||s.push(d);let f=this.formatFileHeaderComment(a.description),p=[...f,...f.length?[``]:[],`declare namespace ${a.typeName} {`,...o.path,...o.query,...o.header,...o.body,`${i.getIndentation(this.config)}${c}`,`}`],m=`${u}/${l}.d.ts`;await t.writeFileRecursive(m,p.join(`
|
|
3
|
+
`).replace(/\r?\n/g,i.getLineEnding(this.config))),e.log.info(`${m.padEnd(n.PAD_END)} - Write done!`)};await t.runWithConcurrency(Array.from(a.entries()).sort((e,t)=>e[0].localeCompare(t[0])),32,([e,t])=>d(e,t)),s.sort(),o.unshift(`import { ${s.join(`, `)} } from '${this.config.requestMethodsImportPath||`./api`}';`,`
|
|
4
4
|
`);let f=this.config.apiListFileName??`index.ts`,p=`${this.config.saveApiListFolderPath}/${f}`;await t.clearDir(p),await t.writeFileRecursive(p,o.join(`
|
|
5
5
|
`)),e.log.step(`Paths parsed & written`)}};exports.PathWriter=a;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const e=require(`../../_virtual/rolldown_runtime.cjs`),t=require(`../../utils/logger.cjs`);require(`../../utils/index.cjs`);const n=require(`./
|
|
1
|
+
const e=require(`../../_virtual/rolldown_runtime.cjs`),t=require(`../../utils/logger.cjs`);require(`../../utils/index.cjs`);const n=require(`./constants.cjs`),r=require(`./naming.cjs`);let i=require(`fs`);i=e.__toESM(i);let a=require(`path`);a=e.__toESM(a);let o=require(`chalk`);o=e.__toESM(o);let s=require(`cross-spawn`);async function c(){let e=a.default.join(process.cwd(),`node_modules`,`.bin`,process.platform===`win32`?`prettier.cmd`:`prettier`);try{return await i.default.promises.access(e,i.default.constants.X_OK),t.log.info(`Using local prettier: ${e}`),{command:e,args:[]}}catch{return{command:`npx`,args:[`prettier`]}}}async function l(){for(let e of n.PRETTIER_CONFIG_FILES){let n=a.default.join(process.cwd(),e);try{return await i.default.promises.access(n),t.log.info(`Auto-detected prettier config: ${e}`),n}catch{continue}}try{let e=a.default.join(process.cwd(),`package.json`);if(JSON.parse(await i.default.promises.readFile(e,`utf8`)).prettier)return t.log.info(`Using prettier config from package.json`),e}catch{return null}return null}async function u(e){if(typeof e==`string`&&e.trim()){let n=a.default.resolve(process.cwd(),e.trim());try{return await i.default.promises.access(n),[`--config`,n]}catch{t.log.warning(`Prettier config file not found: ${e}, falling back to auto-detection...`)}}let n=await l();return n?[`--config`,n]:[]}async function d(e,t){let n=[];if(t){let{servers:i,selectedIndices:a,segments:o,isolateBySegment:s}=t;for(let t of a){n.push({path:`${e.saveApiListFolderPath}/${i[t].apiListFileName}`});let a=s?o[t]:``;for(let t of[`connectors`,`models`])n.push({path:`${e.saveTypeFolderPath}/${t}${a?`/${a}`:``}`,pattern:`**/*.{ts,d.ts}`});let c=a&&r.isEnumIsolated(i[t])?a:``;n.push({path:`${e.saveEnumFolderPath}${c?`/${c}`:``}`,pattern:c?`**/*.{ts,d.ts}`:`*.ts`})}}else n.push({path:e.saveTypeFolderPath,pattern:`**/*.{ts,d.ts}`},{path:e.saveApiListFolderPath,pattern:`**/*.ts`},{path:e.saveEnumFolderPath,pattern:`**/*.ts`});let a=new Set;for(let e of n)try{await i.default.promises.access(e.path),a.add(`${e.path}${e.pattern?`/${e.pattern}`:``}`.replace(/\\/g,`/`))}catch{continue}return[...a]}async function f(e,n,r){let i=await d(e,r);if(!i.length){t.log.warning(r?`No files to format for the selected services.`:`No generated directories found to format.`);return}let a=await c(),l=await u(n),f=[...a.args,`--write`,...i,...l],p=[a.command,...f].map(e=>JSON.stringify(e)).join(` `);try{t.spinner.start(r?`Formatting selected files...`:`Formatting generated files...`);let{stdout:e,stderr:n}=await new Promise((e,t)=>{let n=(0,s.spawn)(a.command,f,{stdio:[`ignore`,`pipe`,`pipe`]}),r=``,i=``;n.stdout?.setEncoding(`utf8`).on(`data`,e=>{r+=e}),n.stderr?.setEncoding(`utf8`).on(`data`,e=>{i+=e}),n.on(`error`,t),n.on(`close`,n=>{n===0?e({stdout:r,stderr:i}):t(Error(i||`Prettier exited with code ${String(n)}`))})});e&&t.log.print(e),n&&t.log.print(n,`
|
|
2
2
|
$`,o.default.yellow(p),`
|
|
3
3
|
`),t.spinner.success(`File formatting successful`),t.log.print(`
|
|
4
4
|
`)}catch(e){t.spinner.error(`Format failed`),t.log.print(e),t.log.error(`Format failed. Executable and arguments:`),t.log.print(`$`,o.default.yellow(p))}}exports.formatGeneratedFiles=f;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`./
|
|
1
|
+
const e=require(`./naming.cjs`),t=require(`./format.cjs`);function n(e){return e?` | null`:``}function r(n,r,i){if(!n)return[];let a=t.getIndentation(r),o=a+a,s=[],c=Object.keys(n).sort();for(let t of c){let r=n[t],a=i(r),c=e.formatPropertyName(t);Array.isArray(a)?s.push(`${o}${c}: {${a.join(`
|
|
2
2
|
`)}};`):s.push(`${o}${c}: ${a};`)}return s}exports.formatObjectProperties=r,exports.nullableSuffix=n;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anl",
|
|
3
|
-
"version": "26.
|
|
3
|
+
"version": "26.1008.0",
|
|
4
4
|
"description": "FE command line tool",
|
|
5
5
|
"main": "bin/an-cli.js",
|
|
6
6
|
"exports": {
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"preinstall": "npx only-allow pnpm",
|
|
25
25
|
"dev": "tsdown --watch",
|
|
26
26
|
"build": "rimraf lib && tsdown",
|
|
27
|
+
"test": "pnpm run build && node --test tests/swagger-codegen.test.cjs tests/mock-generator.test.cjs",
|
|
27
28
|
"test:codegen": "pnpm run build && node --test tests/swagger-codegen.test.cjs",
|
|
29
|
+
"test:mock": "pnpm run build && node --test tests/mock-generator.test.cjs",
|
|
28
30
|
"pub": "bash publish.sh",
|
|
29
31
|
"ts": "tsc ./src/int.ts --noEmit --watch",
|
|
30
32
|
"blink": "pnpm run build && pnpm link",
|
|
@@ -60,12 +62,12 @@
|
|
|
60
62
|
"globals": "^17.2.0",
|
|
61
63
|
"husky": "^8.0.3",
|
|
62
64
|
"lint-staged": "^16.2.7",
|
|
65
|
+
"mockjs": "^1.1.0",
|
|
63
66
|
"openapi-types": "^12.1.3",
|
|
64
67
|
"prettier": "^3.7.4",
|
|
65
68
|
"rimraf": "^5.0.7",
|
|
66
69
|
"ts-node": "^10.9.2",
|
|
67
70
|
"tsdown": "^0.20.1",
|
|
68
|
-
"typescript": "^5.9.3",
|
|
69
71
|
"typescript-eslint": "^8.54.0"
|
|
70
72
|
},
|
|
71
73
|
"dependencies": {
|
|
@@ -82,7 +84,8 @@
|
|
|
82
84
|
"ora": "5.*",
|
|
83
85
|
"pinyin-pro": "^3.28.0",
|
|
84
86
|
"progress-estimator": "^0.3.0",
|
|
85
|
-
"shelljs": "^0.8.5"
|
|
87
|
+
"shelljs": "^0.8.5",
|
|
88
|
+
"typescript": "^5.9.3"
|
|
86
89
|
},
|
|
87
90
|
"keywords": [
|
|
88
91
|
"cli",
|