apidoc-to-openapi 0.1.1

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 ADDED
@@ -0,0 +1,76 @@
1
+ # apidoc-to-openapi
2
+
3
+ A CLI utility that scans source files with `apidoc` and converts parsed apidoc metadata into an OpenAPI 3.0 document (`json` or `yaml`).
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install
9
+ ```
10
+
11
+ You can run it directly with:
12
+
13
+ ```bash
14
+ npx apidoc-to-openapi --src ./src --output ./openapi.yaml
15
+ ```
16
+
17
+ Or after a global/local link:
18
+
19
+ ```bash
20
+ apidoc-to-openapi --src ./src --output ./openapi.yaml
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ```bash
26
+ apidoc-to-openapi --src <dir> [options]
27
+ ```
28
+
29
+ ### Options
30
+
31
+ - `-s, --src <dir>`: Source directory for apidoc scanning (required).
32
+ - `-o, --output <file>`: Output file path. When omitted, writes to stdout.
33
+ - `-f, --format <json|yaml>`: Output format. Auto-detected from output extension when omitted.
34
+ - `--title <text>`: Override `info.title`.
35
+ - `--api-version <text>`: Override `info.version`.
36
+ - `--description <text>`: Override `info.description`.
37
+ - `--server <url>`: Add server URL (repeatable).
38
+ - `--include <list>`: `apidoc` include filters, comma-separated.
39
+ - `--exclude <list>`: `apidoc` exclude filters, comma-separated.
40
+ - `--silent`: Silence apidoc log output.
41
+ - `--no-pretty`: Minified JSON output.
42
+ - `-h, --help`: Show help.
43
+
44
+ ## Examples
45
+
46
+ Generate YAML file:
47
+
48
+ ```bash
49
+ apidoc-to-openapi --src ./src --output ./openapi.yaml
50
+ ```
51
+
52
+ Generate JSON to stdout:
53
+
54
+ ```bash
55
+ apidoc-to-openapi --src ./src --format json > openapi.json
56
+ ```
57
+
58
+ Override OpenAPI info:
59
+
60
+ ```bash
61
+ apidoc-to-openapi \
62
+ --src ./src \
63
+ --output ./openapi.yaml \
64
+ --title "My Service API" \
65
+ --api-version "2.1.0" \
66
+ --server "https://api.example.com"
67
+ ```
68
+
69
+ ## Notes
70
+
71
+ - This converter targets common apidoc tags (`@api`, `@apiParam`, `@apiSuccess`, `@apiError`, `@apiHeader`).
72
+ - The generated schema is best-effort for nested field names and array notation (for example `items[].id`).
73
+ - Array-like type hints such as `Array`, `List`, `ArrayList`, `List<String>`, `Array<Object>` are recognized, and child fields like `data.id` are mapped to array item properties.
74
+ - Description prefixes like `[direct|直销,distribution|分销] 销售方式` are parsed into OpenAPI `enum` (using the value before `|`) and optional `x-enumDescriptions`.
75
+ - `apidoc` project metadata fields (`baseurl`, `baseUrl`, `url`) are used as a base path prefix for every OpenAPI path (for example `/dapi/v2/prodev` + `/task_service/sync_task_to_feishu_bitable`).
76
+ - If `apidoc` parsing fails, the CLI exits with a non-zero status.
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runCli } from "../src/cli.js";
4
+
5
+ const exitCode = await runCli(process.argv.slice(2));
6
+ process.exit(exitCode);
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "apidoc-to-openapi",
3
+ "version": "0.1.1",
4
+ "description": "CLI to convert apidoc comments into OpenAPI JSON/YAML.",
5
+ "private": false,
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "bin": {
9
+ "apidoc-to-openapi": "./bin/apidoc-to-openapi.js"
10
+ },
11
+ "keywords": [
12
+ "apidoc",
13
+ "openapi",
14
+ "swagger",
15
+ "cli",
16
+ "api-documentation"
17
+ ],
18
+ "files": [
19
+ "bin",
20
+ "src",
21
+ "README.md"
22
+ ],
23
+ "scripts": {
24
+ "check": "node --check src/cli.js && node --check src/converter.js && node --check src/options.js",
25
+ "prepublishOnly": "npm run check"
26
+ },
27
+ "engines": {
28
+ "node": ">=18"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "dependencies": {
34
+ "apidoc": "^1.2.0",
35
+ "yaml": "^2.5.1"
36
+ }
37
+ }
package/src/cli.js ADDED
@@ -0,0 +1,115 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ import { createDoc } from "apidoc";
5
+
6
+ import { apidocDataToOpenApi, serializeOpenApi } from "./converter.js";
7
+ import { parseCliArgs, printHelp } from "./options.js";
8
+
9
+ function parsePossiblyJson(value) {
10
+ if (typeof value !== "string") {
11
+ return value;
12
+ }
13
+
14
+ const trimmed = value.trim();
15
+ if (!trimmed) {
16
+ return value;
17
+ }
18
+
19
+ if (
20
+ !(trimmed.startsWith("{") || trimmed.startsWith("["))
21
+ ) {
22
+ return value;
23
+ }
24
+
25
+ try {
26
+ return JSON.parse(trimmed);
27
+ } catch {
28
+ return value;
29
+ }
30
+ }
31
+
32
+ function buildApidocConfig(options) {
33
+ const config = {
34
+ src: path.resolve(process.cwd(), options.src),
35
+ dryRun: true,
36
+ silent: options.silent,
37
+ };
38
+
39
+ if (options.includeFilters.length > 0) {
40
+ config.includeFilters = options.includeFilters;
41
+ }
42
+
43
+ if (options.excludeFilters.length > 0) {
44
+ config.excludeFilters = options.excludeFilters;
45
+ }
46
+
47
+ return config;
48
+ }
49
+
50
+ async function writeOutput(outputPath, content) {
51
+ const absoluteOutputPath = path.resolve(process.cwd(), outputPath);
52
+ const outputDir = path.dirname(absoluteOutputPath);
53
+ await fs.mkdir(outputDir, { recursive: true });
54
+ await fs.writeFile(absoluteOutputPath, content, "utf8");
55
+ }
56
+
57
+ export async function runCli(argv = process.argv.slice(2)) {
58
+ let options;
59
+ try {
60
+ options = parseCliArgs(argv);
61
+ } catch (error) {
62
+ process.stderr.write(`Error: ${error.message}\n\n`);
63
+ printHelp();
64
+ return 1;
65
+ }
66
+
67
+ if (options.help) {
68
+ printHelp();
69
+ return 0;
70
+ }
71
+
72
+ if (!options.src) {
73
+ process.stderr.write("Error: --src is required.\n\n");
74
+ printHelp();
75
+ return 1;
76
+ }
77
+
78
+ try {
79
+ const apidocConfig = buildApidocConfig(options);
80
+ const doc = createDoc(apidocConfig);
81
+
82
+ if (doc === false || typeof doc !== "object") {
83
+ process.stderr.write("Error: apidoc parsing failed.\n");
84
+ return 2;
85
+ }
86
+
87
+ const openApi = apidocDataToOpenApi({
88
+ docData: parsePossiblyJson(doc.data),
89
+ project: parsePossiblyJson(doc.project),
90
+ title: options.title,
91
+ apiVersion: options.apiVersion,
92
+ description: options.description,
93
+ servers: options.servers,
94
+ });
95
+
96
+ const content = serializeOpenApi(openApi, options.format, options.pretty);
97
+
98
+ if (options.output) {
99
+ await writeOutput(options.output, content);
100
+ process.stdout.write(
101
+ `OpenAPI document written to ${path.resolve(process.cwd(), options.output)}\n`,
102
+ );
103
+ return 0;
104
+ }
105
+
106
+ process.stdout.write(content);
107
+ if (!content.endsWith("\n")) {
108
+ process.stdout.write("\n");
109
+ }
110
+ return 0;
111
+ } catch (error) {
112
+ process.stderr.write(`Error: ${error.message}\n`);
113
+ return 2;
114
+ }
115
+ }