api-farmer 0.0.18 → 0.0.20
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 +10 -10
- package/dist/chunk-5YXY7GCE.js +280 -0
- package/dist/{chunk-SKKHIIUE.js → chunk-NFK24PSA.js} +8 -5
- package/dist/cli.cjs +80 -101943
- package/dist/cli.js +2 -3
- package/dist/{generate-IJGTBA2I.js → generate-IQIB2PJD.js} +2 -3
- package/dist/index.cjs +186 -102087
- package/dist/index.js +2 -3
- package/package.json +5 -4
- package/templates/axios.ejs +2 -2
- package/dist/acorn-O63CA7L4.js +0 -3047
- package/dist/angular-U44OZXUW.js +0 -2794
- package/dist/babel-VWJW3TNP.js +0 -6946
- package/dist/chunk-6OIOYGN7.js +0 -17
- package/dist/chunk-K46PCBCF.js +0 -22981
- package/dist/estree-OIZOUIW7.js +0 -4374
- package/dist/flow-TDKVTHFE.js +0 -26897
- package/dist/glimmer-AWQE7API.js +0 -2847
- package/dist/graphql-IJWEVF4G.js +0 -1247
- package/dist/html-7SW5PDZO.js +0 -2716
- package/dist/markdown-2BNVGUT3.js +0 -3484
- package/dist/meriyah-L52O7E6A.js +0 -2474
- package/dist/postcss-AOWY4ITF.js +0 -5057
- package/dist/typescript-ANND5E4Y.js +0 -13104
- package/dist/yaml-MB3PL3NY.js +0 -4223
package/README.md
CHANGED
|
@@ -56,6 +56,16 @@ npx af
|
|
|
56
56
|
|
|
57
57
|
Some simple usage examples can be found [here](fixtures)
|
|
58
58
|
|
|
59
|
+
### Custom code generation templates
|
|
60
|
+
|
|
61
|
+
Create `api-farmer.ejs` in the project root, which will replace the `preset` template.
|
|
62
|
+
The template format can refer to the preset template listed below:
|
|
63
|
+
|
|
64
|
+
- [Axle](templates/axle.ejs)
|
|
65
|
+
- [Axios](templates/axios.ejs)
|
|
66
|
+
|
|
67
|
+
And see the bottom of the document for template variable definitions.
|
|
68
|
+
|
|
59
69
|
### Transformer API
|
|
60
70
|
|
|
61
71
|
You can use the Transformer API to further define template variables, which will override the default transformation rules.
|
|
@@ -86,16 +96,6 @@ export default defineConfig({
|
|
|
86
96
|
})
|
|
87
97
|
```
|
|
88
98
|
|
|
89
|
-
### Custom EJS Template
|
|
90
|
-
|
|
91
|
-
Create `api-farmer.ejs` in the project root, which will replace the `preset` template.
|
|
92
|
-
The template format can refer to the preset template listed below:
|
|
93
|
-
|
|
94
|
-
- [Axle](templates/axle.ejs)
|
|
95
|
-
- [Axios](templates/axios.ejs)
|
|
96
|
-
|
|
97
|
-
See the bottom of the document for template variable definitions.
|
|
98
|
-
|
|
99
99
|
### Configuration Options
|
|
100
100
|
|
|
101
101
|
```ts
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CWD,
|
|
3
|
+
SUPPORTED_HTTP_METHODS,
|
|
4
|
+
getValidResponseMetadataItems,
|
|
5
|
+
hasQueryParameter,
|
|
6
|
+
isRequiredRequestBody,
|
|
7
|
+
readSchema,
|
|
8
|
+
readTemplateFile
|
|
9
|
+
} from "./chunk-NFK24PSA.js";
|
|
10
|
+
|
|
11
|
+
// src/generate.ts
|
|
12
|
+
import { resolve } from "path";
|
|
13
|
+
import ejs from "ejs";
|
|
14
|
+
import fse from "fs-extra";
|
|
15
|
+
import openapiTS, { astToString } from "openapi-typescript";
|
|
16
|
+
import prettier from "prettier";
|
|
17
|
+
import { groupBy, isArray, merge } from "rattail";
|
|
18
|
+
import { logger } from "rslog";
|
|
19
|
+
|
|
20
|
+
// src/config.ts
|
|
21
|
+
import { loadConfig } from "unconfig";
|
|
22
|
+
function defineConfig(config) {
|
|
23
|
+
return config;
|
|
24
|
+
}
|
|
25
|
+
async function getConfig() {
|
|
26
|
+
const { config } = await loadConfig({
|
|
27
|
+
sources: [
|
|
28
|
+
{
|
|
29
|
+
files: "api-farmer.config"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
});
|
|
33
|
+
return config ?? {};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/transformer.ts
|
|
37
|
+
import pluralize from "pluralize";
|
|
38
|
+
import { camelize, pascalCase } from "rattail";
|
|
39
|
+
function transformModuleName({ name }) {
|
|
40
|
+
return camelize(name);
|
|
41
|
+
}
|
|
42
|
+
function transformUrl({ path, base }) {
|
|
43
|
+
return (base ? path.replace(base, "") : path).replace(/{/g, ":").replace(/}/g, "");
|
|
44
|
+
}
|
|
45
|
+
function transformVerb({ method }) {
|
|
46
|
+
switch (method) {
|
|
47
|
+
case "post":
|
|
48
|
+
return "Create";
|
|
49
|
+
case "put":
|
|
50
|
+
return "Update";
|
|
51
|
+
default:
|
|
52
|
+
return pascalCase(method);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function transformEntity({ path, method, base }) {
|
|
56
|
+
path = base ? path.replace(base, "") : path;
|
|
57
|
+
const words = path.split("/").filter(Boolean);
|
|
58
|
+
return words.reduce((entity, word, index) => {
|
|
59
|
+
if (word.includes("{")) {
|
|
60
|
+
return entity;
|
|
61
|
+
}
|
|
62
|
+
word = word.replace(/\.([a-z])/g, (_, p) => p.toUpperCase());
|
|
63
|
+
word = pluralize.singular(pascalCase(word));
|
|
64
|
+
if (method === "get" && index === words.length - 1) {
|
|
65
|
+
word = pluralize.plural(word);
|
|
66
|
+
}
|
|
67
|
+
return `${entity}${word}`;
|
|
68
|
+
}, "");
|
|
69
|
+
}
|
|
70
|
+
function transformFn({ verb, entity }) {
|
|
71
|
+
return `api${verb}${entity}`;
|
|
72
|
+
}
|
|
73
|
+
function transformType({ verb, entity }) {
|
|
74
|
+
return `Api${verb}${entity}`;
|
|
75
|
+
}
|
|
76
|
+
function transformTypeValue({ path, method }) {
|
|
77
|
+
return `paths['${path}']['${method}']`;
|
|
78
|
+
}
|
|
79
|
+
function transformTypeQuery({ type }) {
|
|
80
|
+
return `${type}Query`;
|
|
81
|
+
}
|
|
82
|
+
function transformTypeQueryValue({
|
|
83
|
+
type
|
|
84
|
+
}) {
|
|
85
|
+
return `${type}['parameters']['query']`;
|
|
86
|
+
}
|
|
87
|
+
function transformTypeRequestBody({
|
|
88
|
+
type
|
|
89
|
+
}) {
|
|
90
|
+
return `${type}RequestBody`;
|
|
91
|
+
}
|
|
92
|
+
function transformTypeRequestBodyValue({
|
|
93
|
+
type,
|
|
94
|
+
required
|
|
95
|
+
}) {
|
|
96
|
+
return required ? `${type}['requestBody']['content']['application/json']` : `NonNullable<${type}['requestBody']>['content']['application/json'] | undefined`;
|
|
97
|
+
}
|
|
98
|
+
function transformTypeResponseBody({
|
|
99
|
+
type
|
|
100
|
+
}) {
|
|
101
|
+
return `${type}ResponseBody`;
|
|
102
|
+
}
|
|
103
|
+
function transformTypeResponseBodyValue({
|
|
104
|
+
type,
|
|
105
|
+
responseMetadataItems
|
|
106
|
+
}) {
|
|
107
|
+
return responseMetadataItems.map(({ status, mime }) => `${type}['responses']['${status}']['content']['${mime}']`).join(" | ");
|
|
108
|
+
}
|
|
109
|
+
function createTransformer() {
|
|
110
|
+
return {
|
|
111
|
+
moduleName: transformModuleName,
|
|
112
|
+
verb: transformVerb,
|
|
113
|
+
url: transformUrl,
|
|
114
|
+
entity: transformEntity,
|
|
115
|
+
fn: transformFn,
|
|
116
|
+
type: transformType,
|
|
117
|
+
typeValue: transformTypeValue,
|
|
118
|
+
typeQuery: transformTypeQuery,
|
|
119
|
+
typeQueryValue: transformTypeQueryValue,
|
|
120
|
+
typeRequestBody: transformTypeRequestBody,
|
|
121
|
+
typeRequestBodyValue: transformTypeRequestBodyValue,
|
|
122
|
+
typeResponseBody: transformTypeResponseBody,
|
|
123
|
+
typeResponseBodyValue: transformTypeResponseBodyValue
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/generate.ts
|
|
128
|
+
function transformPayloads(pathItems, options) {
|
|
129
|
+
const { transformer, path, base, validateStatus } = options;
|
|
130
|
+
return Object.entries(pathItems).filter(([key]) => SUPPORTED_HTTP_METHODS.includes(key)).reduce((payloads, [method, operation]) => {
|
|
131
|
+
const url = transformer.url({ path, base });
|
|
132
|
+
const args = { path, base, url, method, operation };
|
|
133
|
+
const entity = transformer.entity(args);
|
|
134
|
+
const verb = transformer.verb(args);
|
|
135
|
+
const fn = transformer.fn({ ...args, verb, entity });
|
|
136
|
+
const type = transformer.type({ ...args, verb, entity });
|
|
137
|
+
const typeValue = transformer.typeValue({ ...args, verb, entity });
|
|
138
|
+
const typeQuery = transformer.typeQuery({ ...args, type, verb, entity });
|
|
139
|
+
const typeQueryValue = hasQueryParameter(operation) ? transformer.typeQueryValue({ ...args, type, verb, entity }) : "undefined";
|
|
140
|
+
const typeRequestBody = transformer.typeRequestBody({ ...args, type, verb, entity });
|
|
141
|
+
const typeRequestBodyValue = operation.requestBody ? transformer.typeRequestBodyValue({
|
|
142
|
+
...args,
|
|
143
|
+
type,
|
|
144
|
+
verb,
|
|
145
|
+
entity,
|
|
146
|
+
required: isRequiredRequestBody(operation.requestBody)
|
|
147
|
+
}) : "undefined";
|
|
148
|
+
const typeResponseBody = transformer.typeResponseBody({ ...args, type, verb, entity });
|
|
149
|
+
const responseMetadataItems = getValidResponseMetadataItems(operation, validateStatus);
|
|
150
|
+
const typeResponseBodyValue = responseMetadataItems.length > 0 ? transformer.typeResponseBodyValue({ ...args, type, verb, entity, responseMetadataItems }) : "undefined";
|
|
151
|
+
payloads.push({
|
|
152
|
+
fn,
|
|
153
|
+
url,
|
|
154
|
+
method,
|
|
155
|
+
verb,
|
|
156
|
+
entity,
|
|
157
|
+
type,
|
|
158
|
+
typeValue,
|
|
159
|
+
typeQuery,
|
|
160
|
+
typeQueryValue,
|
|
161
|
+
typeRequestBody,
|
|
162
|
+
typeRequestBodyValue,
|
|
163
|
+
typeResponseBody,
|
|
164
|
+
typeResponseBodyValue
|
|
165
|
+
});
|
|
166
|
+
return payloads;
|
|
167
|
+
}, []);
|
|
168
|
+
}
|
|
169
|
+
function partitionApiModules(schema, options) {
|
|
170
|
+
const { base, transformer, validateStatus } = options;
|
|
171
|
+
const schemaPaths = schema.paths ?? {};
|
|
172
|
+
const schemaPathKeys = base ? Object.keys(schemaPaths).map((key) => key.replace(base, "")) : Object.keys(schemaPaths);
|
|
173
|
+
const keyToPaths = groupBy(schemaPathKeys, (key) => key.split("/")[1]);
|
|
174
|
+
const apiModules = Object.entries(keyToPaths).reduce((apiModules2, [name, paths]) => {
|
|
175
|
+
const payloads = paths.reduce((payloads2, path) => {
|
|
176
|
+
const pathItems = schemaPaths[path];
|
|
177
|
+
payloads2.push(
|
|
178
|
+
...transformPayloads(pathItems, { ...options, path: base ? base + path : path, transformer, validateStatus })
|
|
179
|
+
);
|
|
180
|
+
return payloads2;
|
|
181
|
+
}, []);
|
|
182
|
+
apiModules2.push({ name: transformer.moduleName({ name }), payloads });
|
|
183
|
+
return apiModules2;
|
|
184
|
+
}, []);
|
|
185
|
+
return apiModules;
|
|
186
|
+
}
|
|
187
|
+
function renderApiModules(apiModules, options) {
|
|
188
|
+
const { output, ts, typesOnly, overrides, preset } = options;
|
|
189
|
+
const templateFile = readTemplateFile(preset);
|
|
190
|
+
const typesFilename = options.typesFilename.replace(".ts", "");
|
|
191
|
+
return Promise.all(
|
|
192
|
+
apiModules.map(
|
|
193
|
+
(apiModule) => new Promise((promiseResolve) => {
|
|
194
|
+
const data = {
|
|
195
|
+
apiModule,
|
|
196
|
+
typesFilename,
|
|
197
|
+
ts,
|
|
198
|
+
typesOnly
|
|
199
|
+
};
|
|
200
|
+
prettier.format(ejs.render(templateFile, data), {
|
|
201
|
+
parser: "typescript",
|
|
202
|
+
semi: false,
|
|
203
|
+
singleQuote: true,
|
|
204
|
+
printWidth: 120
|
|
205
|
+
}).then((content) => {
|
|
206
|
+
const path = resolve(output, `${apiModule.name}.${ts ? "ts" : "js"}`);
|
|
207
|
+
const shouldSkip = (!overrides || isArray(overrides) && !overrides.includes(apiModule.name)) && fse.existsSync(path);
|
|
208
|
+
if (shouldSkip) {
|
|
209
|
+
logger.warn(`File already exists, skip: ${path}`);
|
|
210
|
+
promiseResolve(content);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
fse.outputFileSync(path, content);
|
|
214
|
+
logger.success(`Generated ${path}`);
|
|
215
|
+
promiseResolve(content);
|
|
216
|
+
});
|
|
217
|
+
})
|
|
218
|
+
)
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
async function generateTypes(schema, output, typesFilename) {
|
|
222
|
+
const ast = await openapiTS(schema);
|
|
223
|
+
const contents = astToString(ast);
|
|
224
|
+
const typesFilepath = resolve(CWD, output, typesFilename);
|
|
225
|
+
fse.outputFileSync(typesFilepath, contents);
|
|
226
|
+
logger.success(`Generated ${typesFilepath}`);
|
|
227
|
+
}
|
|
228
|
+
async function generate(userOptions = {}) {
|
|
229
|
+
const config = await getConfig();
|
|
230
|
+
const options = merge(config, userOptions);
|
|
231
|
+
const {
|
|
232
|
+
base,
|
|
233
|
+
ts = true,
|
|
234
|
+
typesOnly = false,
|
|
235
|
+
overrides = true,
|
|
236
|
+
preset = "axle",
|
|
237
|
+
input = "./schema.json",
|
|
238
|
+
output = "./src/apis/generated",
|
|
239
|
+
typesFilename = "_types.ts",
|
|
240
|
+
validateStatus = (status) => status >= 200 && status < 300,
|
|
241
|
+
transformer = {}
|
|
242
|
+
} = options;
|
|
243
|
+
const mergedTransformer = { ...createTransformer(), ...transformer };
|
|
244
|
+
const schema = await readSchema(input);
|
|
245
|
+
logger.info("Generating API modules...");
|
|
246
|
+
if (ts) {
|
|
247
|
+
await generateTypes(schema, output, typesFilename);
|
|
248
|
+
}
|
|
249
|
+
const apiModules = partitionApiModules(schema, {
|
|
250
|
+
base,
|
|
251
|
+
transformer: mergedTransformer,
|
|
252
|
+
validateStatus
|
|
253
|
+
});
|
|
254
|
+
await renderApiModules(apiModules, { output, typesFilename, ts, typesOnly, overrides, preset });
|
|
255
|
+
logger.success("Done");
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export {
|
|
259
|
+
defineConfig,
|
|
260
|
+
getConfig,
|
|
261
|
+
transformModuleName,
|
|
262
|
+
transformUrl,
|
|
263
|
+
transformVerb,
|
|
264
|
+
transformEntity,
|
|
265
|
+
transformFn,
|
|
266
|
+
transformType,
|
|
267
|
+
transformTypeValue,
|
|
268
|
+
transformTypeQuery,
|
|
269
|
+
transformTypeQueryValue,
|
|
270
|
+
transformTypeRequestBody,
|
|
271
|
+
transformTypeRequestBodyValue,
|
|
272
|
+
transformTypeResponseBody,
|
|
273
|
+
transformTypeResponseBodyValue,
|
|
274
|
+
createTransformer,
|
|
275
|
+
transformPayloads,
|
|
276
|
+
partitionApiModules,
|
|
277
|
+
renderApiModules,
|
|
278
|
+
generateTypes,
|
|
279
|
+
generate
|
|
280
|
+
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
// node_modules/.pnpm/tsup@8.3.5_jiti@2.4.2_postcss@8.5.1_tsx@4.19.2_typescript@5.3.3_yaml@2.7.0/node_modules/tsup/assets/esm_shims.js
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import path from "path";
|
|
4
|
+
var getFilename = () => fileURLToPath(import.meta.url);
|
|
5
|
+
var getDirname = () => path.dirname(getFilename());
|
|
6
|
+
var __dirname = /* @__PURE__ */ getDirname();
|
|
4
7
|
|
|
5
8
|
// src/constants.ts
|
|
6
9
|
import { resolve } from "path";
|
|
@@ -47,8 +50,8 @@ function createStatusCodesByStrategy(strategy) {
|
|
|
47
50
|
}
|
|
48
51
|
async function readSchema(input) {
|
|
49
52
|
const isYaml = input.endsWith(".yaml");
|
|
50
|
-
const
|
|
51
|
-
const content = fse.readFileSync(
|
|
53
|
+
const path2 = resolve2(CWD, input);
|
|
54
|
+
const content = fse.readFileSync(path2, "utf-8");
|
|
52
55
|
const swaggerOrOpenapiSchema = isYaml ? yaml.parse(content) : JSON.parse(content);
|
|
53
56
|
const schema = swaggerOrOpenapiSchema.swagger ? (await swagger.convert(swaggerOrOpenapiSchema, {})).openapi : swaggerOrOpenapiSchema;
|
|
54
57
|
return schema;
|