api-farmer 0.1.3 → 0.1.4

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.
@@ -1,131 +0,0 @@
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();
7
-
8
- // src/constants.ts
9
- import { resolve } from "path";
10
- var CWD = process.cwd();
11
- var CUSTOM_TEMPLATE_FILE = resolve(CWD, `./api-farmer.ejs`);
12
- var CLI_PACKAGE_JSON = resolve(__dirname, "../package.json");
13
- var SUPPORTED_HTTP_METHODS = ["get", "post", "put", "delete", "patch", "options", "head"];
14
-
15
- // src/utils.ts
16
- import { resolve as resolve2 } from "path";
17
- import { createAxle } from "@varlet/axle";
18
- import fse from "fs-extra";
19
- import { tryParseJSON } from "rattail";
20
- import { logger } from "rslog";
21
- import swagger from "swagger2openapi";
22
- import yaml from "yaml";
23
- function createStatusCodesByStrategy(strategy) {
24
- return {
25
- strict: {
26
- get: 200,
27
- post: 201,
28
- put: 200,
29
- delete: 204,
30
- patch: 200,
31
- options: 204,
32
- head: 200
33
- },
34
- loose: {
35
- get: 200,
36
- post: 200,
37
- put: 200,
38
- delete: 200,
39
- patch: 200,
40
- options: 200,
41
- head: 200
42
- },
43
- smart: {
44
- get: 200,
45
- post: 200,
46
- put: 200,
47
- delete: 200,
48
- patch: 200,
49
- options: 200,
50
- head: 200
51
- }
52
- }[strategy];
53
- }
54
- async function readSchema(input) {
55
- const content = await readSchemaContent(input);
56
- const jsonSchema = tryParseJSON(content);
57
- const swaggerOrOpenapiSchema = jsonSchema ? jsonSchema : yaml.parse(content);
58
- const schema = swaggerOrOpenapiSchema.swagger ? (await swagger.convert(swaggerOrOpenapiSchema, {})).openapi : swaggerOrOpenapiSchema;
59
- return schema;
60
- }
61
- function getSchemaNode(schema, path2) {
62
- const paths = path2.split("/");
63
- return paths.reduce((node, path3) => node[path3], schema);
64
- }
65
- async function readSchemaContent(input) {
66
- if (isRemoteSchema(input)) {
67
- try {
68
- logger.info("Fetching remote schema...");
69
- const { data } = await createAxle().get(input);
70
- return JSON.stringify(data);
71
- } catch {
72
- throw new Error("Failed to fetch remote schema");
73
- }
74
- }
75
- const path2 = resolve2(CWD, input);
76
- const content = fse.readFileSync(path2, "utf-8");
77
- return content;
78
- }
79
- function isRemoteSchema(path2) {
80
- return path2.startsWith("http://") || path2.startsWith("https://");
81
- }
82
- function readTemplateFile(preset = "axle") {
83
- if (fse.existsSync(CUSTOM_TEMPLATE_FILE)) {
84
- return fse.readFileSync(CUSTOM_TEMPLATE_FILE, "utf-8");
85
- }
86
- return fse.readFileSync(resolve2(__dirname, `../templates/${preset}.ejs`), "utf-8");
87
- }
88
- function getCliVersion() {
89
- return fse.readJsonSync(CLI_PACKAGE_JSON).version;
90
- }
91
- function isRequiredRequestBody(value) {
92
- return "required" in value && value.required === true;
93
- }
94
- function findObjectKey(object, targetKeys) {
95
- return Object.keys(object).find((key) => targetKeys.includes(key));
96
- }
97
- function getRequestBodyContentType(value) {
98
- if (!("content" in value)) {
99
- return "";
100
- }
101
- return findObjectKey(value.content, ["application/json", "application/x-www-form-urlencoded", "multipart/form-data"]);
102
- }
103
- function getResponseMetadataItems(operation, validateStatus) {
104
- const responses = operation.responses ?? {};
105
- const validStatusResults = Object.keys(responses).sort((a, b) => Number(a) - Number(b)).filter((key) => validateStatus(Number(key))).map(Number);
106
- const metadataItems = validStatusResults.map((status) => {
107
- const content = operation.responses?.[status]?.content ?? {};
108
- const responseContentType = findObjectKey(content, ["application/json", "application/octet-stream", "*/*"]);
109
- return {
110
- status,
111
- responseContentType
112
- };
113
- }).filter((result) => result.responseContentType);
114
- return metadataItems;
115
- }
116
-
117
- export {
118
- CWD,
119
- SUPPORTED_HTTP_METHODS,
120
- createStatusCodesByStrategy,
121
- readSchema,
122
- getSchemaNode,
123
- readSchemaContent,
124
- isRemoteSchema,
125
- readTemplateFile,
126
- getCliVersion,
127
- isRequiredRequestBody,
128
- findObjectKey,
129
- getRequestBodyContentType,
130
- getResponseMetadataItems
131
- };
@@ -1,338 +0,0 @@
1
- import {
2
- CWD,
3
- SUPPORTED_HTTP_METHODS,
4
- getRequestBodyContentType,
5
- getResponseMetadataItems,
6
- isRequiredRequestBody,
7
- readSchema,
8
- readTemplateFile
9
- } from "./chunk-2227C45C.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
- import ts from "typescript";
20
-
21
- // src/config.ts
22
- import { loadConfig } from "unconfig";
23
- function defineConfig(config) {
24
- return config;
25
- }
26
- async function getConfig() {
27
- const { config } = await loadConfig({
28
- sources: [
29
- {
30
- files: "api-farmer.config"
31
- }
32
- ]
33
- });
34
- return config ?? {};
35
- }
36
-
37
- // src/transformer.ts
38
- import pluralize from "pluralize";
39
- import { camelize, pascalCase } from "rattail";
40
- function transformModuleName({ name }) {
41
- return camelize(name);
42
- }
43
- function transformUrl({ path }) {
44
- return path.replace(/{/g, ":").replace(/}/g, "");
45
- }
46
- function transformComment({
47
- summary,
48
- description,
49
- path,
50
- method
51
- }) {
52
- return `
53
- /**${summary ? `
54
- * ${summary}` : ""}${description && summary !== description ? `
55
- * @description ${description}
56
- *` : ""}
57
- * @url ${path}
58
- * @method ${method.toLocaleUpperCase()}
59
- */
60
- `.trim();
61
- }
62
- function transformVerb({ method }) {
63
- switch (method) {
64
- case "post":
65
- return "Create";
66
- case "put":
67
- return "Update";
68
- default:
69
- return pascalCase(method);
70
- }
71
- }
72
- function transformEntity({ path, method, uncountableNouns }) {
73
- const words = path.split("/").filter(Boolean);
74
- return words.reduce((entity, word, index) => {
75
- if (word.includes("{")) {
76
- return entity;
77
- }
78
- word = word.replace(/\.([a-z])/g, (_, p) => p.toUpperCase());
79
- const isUncountableNoun = uncountableNouns.includes(word);
80
- word = pascalCase(word);
81
- word = isUncountableNoun ? word : pluralize.singular(word);
82
- if (method === "get" && index === words.length - 1) {
83
- word = isUncountableNoun ? `${word}List` : pluralize.plural(word);
84
- }
85
- return `${entity}${word}`;
86
- }, "");
87
- }
88
- function transformFn({ verb, entity }) {
89
- return `api${verb}${entity}`;
90
- }
91
- function transformType({ verb, entity }) {
92
- return `Api${verb}${entity}`;
93
- }
94
- function transformTypeValue({ fullPath, method }) {
95
- return `paths['${fullPath}']['${method}']`;
96
- }
97
- function transformTypeQuery({ type }) {
98
- return `${type}Query`;
99
- }
100
- function transformTypeQueryValue({
101
- type
102
- }) {
103
- return `${type}['parameters']['query']`;
104
- }
105
- function transformTypeRequestBody({
106
- type
107
- }) {
108
- return `${type}RequestBody`;
109
- }
110
- function transformTypeRequestBodyValue({
111
- type,
112
- required,
113
- requestContentType
114
- }) {
115
- return required ? `${type}['requestBody']['content']['${requestContentType}']` : `NonNullable<${type}['requestBody']>['content']['${requestContentType}'] | undefined`;
116
- }
117
- function transformTypeResponseBody({
118
- type
119
- }) {
120
- return `${type}ResponseBody`;
121
- }
122
- function transformTypeResponseBodyValue({
123
- type,
124
- responseMetadataItems
125
- }) {
126
- return responseMetadataItems.map(({ status, responseContentType }) => `${type}['responses']['${status}']['content']['${responseContentType}']`).join(" | ");
127
- }
128
- function createTransformer() {
129
- return {
130
- moduleName: transformModuleName,
131
- verb: transformVerb,
132
- url: transformUrl,
133
- comment: transformComment,
134
- entity: transformEntity,
135
- fn: transformFn,
136
- type: transformType,
137
- typeValue: transformTypeValue,
138
- typeQuery: transformTypeQuery,
139
- typeQueryValue: transformTypeQueryValue,
140
- typeRequestBody: transformTypeRequestBody,
141
- typeRequestBodyValue: transformTypeRequestBodyValue,
142
- typeResponseBody: transformTypeResponseBody,
143
- typeResponseBodyValue: transformTypeResponseBodyValue
144
- };
145
- }
146
-
147
- // src/generate.ts
148
- function transformPayloads(pathItems, options) {
149
- const { transformer, path, fullPath, base, uncountableNouns, validateStatus, excludeDeprecated } = options;
150
- return Object.entries(pathItems).filter(([key]) => SUPPORTED_HTTP_METHODS.includes(key)).filter(([, operation]) => !(excludeDeprecated && operation.deprecated)).reduce((payloads, [method, operation]) => {
151
- const url = transformer.url({ path, base, fullPath });
152
- const args = { path, base, fullPath, url, method, uncountableNouns, operation };
153
- const entity = transformer.entity(args);
154
- const verb = transformer.verb(args);
155
- const comment = transformer.comment({ ...args, ...operation });
156
- const requestContentType = operation.requestBody ? getRequestBodyContentType(operation.requestBody) : void 0;
157
- const responseMetadataItems = getResponseMetadataItems(operation, validateStatus);
158
- const fn = transformer.fn({ ...args, verb, entity });
159
- const type = transformer.type({ ...args, verb, entity });
160
- const typeValue = transformer.typeValue({ ...args, verb, entity });
161
- const typeQuery = transformer.typeQuery({ ...args, type, verb, entity });
162
- const typeQueryValue = transformer.typeQueryValue({ ...args, type, verb, entity });
163
- const typeRequestBody = transformer.typeRequestBody({ ...args, type, verb, entity });
164
- const typeRequestBodyValue = operation.requestBody && requestContentType ? transformer.typeRequestBodyValue({
165
- ...args,
166
- type,
167
- verb,
168
- entity,
169
- required: isRequiredRequestBody(operation.requestBody),
170
- requestContentType
171
- }) : "undefined";
172
- const typeResponseBody = transformer.typeResponseBody({ ...args, type, verb, entity });
173
- const typeResponseBodyValue = responseMetadataItems.length > 0 ? transformer.typeResponseBodyValue({ ...args, type, verb, entity, responseMetadataItems }) : "undefined";
174
- payloads.push({
175
- comment,
176
- fn,
177
- url,
178
- method,
179
- verb,
180
- entity,
181
- requestContentType,
182
- type,
183
- typeValue,
184
- typeQuery,
185
- typeQueryValue,
186
- typeRequestBody,
187
- typeRequestBodyValue,
188
- typeResponseBody,
189
- typeResponseBodyValue
190
- });
191
- return payloads;
192
- }, []);
193
- }
194
- function partitionApiModules(schema, options) {
195
- const { base, transformer, uncountableNouns, validateStatus, excludeDeprecated } = options;
196
- const schemaPaths = schema.paths ?? {};
197
- const schemaPathKeys = base ? Object.keys(schemaPaths).map((key) => key.replace(base, "")) : Object.keys(schemaPaths);
198
- const keyToPaths = groupBy(schemaPathKeys, (key) => key.split("/")[1]);
199
- const apiModules = Object.entries(keyToPaths).reduce((apiModules2, [name, paths]) => {
200
- const payloads = paths.reduce((payloads2, path) => {
201
- const fullPath = base ? base + path : path;
202
- const pathItems = schemaPaths[fullPath];
203
- payloads2.push(
204
- ...transformPayloads(pathItems, {
205
- ...options,
206
- path,
207
- fullPath,
208
- transformer,
209
- uncountableNouns,
210
- validateStatus,
211
- excludeDeprecated
212
- })
213
- );
214
- return payloads2;
215
- }, []);
216
- apiModules2.push({ name: transformer.moduleName({ name }), payloads });
217
- return apiModules2;
218
- }, []);
219
- return apiModules;
220
- }
221
- function renderApiModules(apiModules, options) {
222
- const { output, ts: ts2, typesOnly, overrides, preset } = options;
223
- const templateFile = readTemplateFile(preset);
224
- const typesFilename = options.typesFilename.replace(".ts", "");
225
- return Promise.all(
226
- apiModules.map(
227
- (apiModule) => new Promise((promiseResolve) => {
228
- const data = {
229
- apiModule,
230
- typesFilename,
231
- ts: ts2,
232
- typesOnly
233
- };
234
- prettier.format(ejs.render(templateFile, data), {
235
- parser: "typescript",
236
- semi: false,
237
- singleQuote: true,
238
- printWidth: 120
239
- }).then((content) => {
240
- const path = resolve(output, `${apiModule.name}.${ts2 ? "ts" : "js"}`);
241
- const shouldSkip = (!overrides || isArray(overrides) && !overrides.includes(apiModule.name)) && fse.existsSync(path);
242
- if (shouldSkip) {
243
- logger.warn(`File already exists, skip: ${path}`);
244
- promiseResolve(content);
245
- return;
246
- }
247
- fse.outputFileSync(path, content);
248
- logger.success(`Generated ${path}`);
249
- promiseResolve(content);
250
- });
251
- })
252
- )
253
- );
254
- }
255
- async function generateTypes(schema, output, typesFilename, openapiTsOptions) {
256
- const BLOB = ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("Blob"));
257
- const NULL = ts.factory.createLiteralTypeNode(ts.factory.createNull());
258
- const ast = await openapiTS(schema, {
259
- defaultNonNullable: false,
260
- transform(schemaObject) {
261
- if (schemaObject.format === "binary") {
262
- return schemaObject.nullable ? ts.factory.createUnionTypeNode([BLOB, NULL]) : BLOB;
263
- }
264
- },
265
- ...openapiTsOptions
266
- });
267
- const contents = astToString(ast);
268
- const typesFilepath = resolve(CWD, output, typesFilename);
269
- fse.outputFileSync(typesFilepath, contents);
270
- logger.success(`Generated ${typesFilepath}`);
271
- }
272
- async function generate(userOptions = {}) {
273
- const config = await getConfig();
274
- const options = merge(config, userOptions);
275
- const {
276
- base,
277
- ts: ts2 = true,
278
- typesOnly = false,
279
- overrides = true,
280
- preset = "axle",
281
- input = "./schema.json",
282
- output = "./src/apis/generated",
283
- typesFilename = "_types.ts",
284
- clean = false,
285
- validateStatus = (status) => status >= 200 && status < 300,
286
- transformer = {},
287
- uncountableNouns = [],
288
- openapiTsOptions = {},
289
- excludeDeprecated = false
290
- } = options;
291
- const mergedTransformer = { ...createTransformer(), ...transformer };
292
- const schema = await readSchema(input);
293
- if (clean) {
294
- fse.removeSync(resolve(CWD, output));
295
- logger.info(`Cleaned output directory: ${resolve(CWD, output)}`);
296
- }
297
- logger.info("Generating API modules...");
298
- if (openapiTsOptions.excludeDeprecated === void 0) {
299
- openapiTsOptions.excludeDeprecated = excludeDeprecated;
300
- }
301
- if (ts2) {
302
- await generateTypes(schema, output, typesFilename, openapiTsOptions);
303
- }
304
- const apiModules = partitionApiModules(schema, {
305
- base,
306
- uncountableNouns,
307
- transformer: mergedTransformer,
308
- validateStatus,
309
- excludeDeprecated
310
- });
311
- await renderApiModules(apiModules, { output, typesFilename, ts: ts2, typesOnly, overrides, preset });
312
- logger.success("Done");
313
- }
314
-
315
- export {
316
- defineConfig,
317
- getConfig,
318
- transformModuleName,
319
- transformUrl,
320
- transformComment,
321
- transformVerb,
322
- transformEntity,
323
- transformFn,
324
- transformType,
325
- transformTypeValue,
326
- transformTypeQuery,
327
- transformTypeQueryValue,
328
- transformTypeRequestBody,
329
- transformTypeRequestBodyValue,
330
- transformTypeResponseBody,
331
- transformTypeResponseBodyValue,
332
- createTransformer,
333
- transformPayloads,
334
- partitionApiModules,
335
- renderApiModules,
336
- generateTypes,
337
- generate
338
- };