Skip to content

Commit bc24415

Browse files
authored
Add --version flag, add CLI tests (#970)
1 parent 982ede1 commit bc24415

File tree

5 files changed

+134
-23
lines changed

5 files changed

+134
-23
lines changed

README.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ The following flags can be appended to the CLI command.
140140

141141
| Option | Alias | Default | Description |
142142
| :----------------------------- | :---- | :------: | :--------------------------------------------------------------------------------------------------------------------------- |
143+
| `--help` | | | Display inline help message and exit |
144+
| `--version` | | | Display this library’s version and exit |
143145
| `--output [location]` | `-o` | (stdout) | Where should the output file be saved? |
144146
| `--auth [token]` | | | Provide an auth token to be passed along in the request (only if accessing a private schema) |
145147
| `--header` | `-x` | | Provide an array of or singular headers as an alternative to a JSON object. Each header must follow the `key: value` pattern |
@@ -271,16 +273,10 @@ By default, openapiTS will generate `updated_at?: string;` because it’s not su
271273

272274
```js
273275
const types = openapiTS(mySchema, {
274-
/** transform: runs before Schema Object is converted into a TypeScript type */
275276
transform(node: SchemaObject, options): string {
276277
if ("format" in node && node.format === "date-time") {
277-
return "Date"; // return the TypeScript “Date” type, as a string
278+
return "Date";
278279
}
279-
// if no string returned, handle it normally
280-
},
281-
/** post-transform: runs after TypeScript type has been transformed */
282-
postTransform(type: string, options): string {
283-
// if no string returned, keep TypeScript type as-is
284280
},
285281
});
286282
```
@@ -289,6 +285,8 @@ This will generate `updated_at?: Date` instead. Note that you will still have to
289285

290286
There are many other uses for this besides checking `format`. Because this must return a **string** you can produce any arbitrary TypeScript code you’d like (even your own custom types).
291287

288+
✨ Don’t forget about `postTransform()` as well! It works the same way, but runs _after_ the TypeScript transformation so you can extend/modify types as-needed.
289+
292290
## 🏅 Project Goals
293291

294292
1. Support converting any valid OpenAPI schema to TypeScript types, no matter how complicated.

bin/cli.js

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ const HELP = `Usage
1414
$ openapi-typescript [input] [options]
1515
1616
Options
17-
--help display this
17+
--help Display this
18+
--version Display the version
1819
--output, -o Specify output file (default: stdout)
1920
--auth (optional) Provide an authentication token for private URL
2021
--headersObject, -h (optional) Provide a JSON object as string of HTTP headers for remote schema request
@@ -42,13 +43,15 @@ function errorAndExit(errorMessage) {
4243
throw new Error(errorMessage);
4344
}
4445

45-
const [, , input, ...args] = process.argv;
46+
const [, , ...args] = process.argv;
4647
if (args.includes("-ap")) errorAndExit(`The -ap alias has been deprecated. Use "--additional-properties" instead.`);
4748
if (args.includes("-it")) errorAndExit(`The -it alias has been deprecated. Use "--immutable-types" instead.`);
4849

4950
const flags = parser(args, {
5051
array: ["header"],
5152
boolean: [
53+
"help",
54+
"version",
5255
"defaultNonNullable",
5356
"immutableTypes",
5457
"contentNever",
@@ -57,7 +60,6 @@ const flags = parser(args, {
5760
"pathParamsAsTypes",
5861
"alphabetize",
5962
],
60-
number: ["version"],
6163
string: ["auth", "header", "headersObject", "httpMethod"],
6264
alias: {
6365
header: ["x"],
@@ -131,33 +133,37 @@ async function generateSchema(pathToSpec) {
131133
}
132134

133135
async function main() {
134-
if (flags.help) {
136+
if ("help" in flags) {
135137
console.info(HELP);
136138
process.exit(0);
137139
}
140+
const packageJSON = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf8"));
141+
if ("version" in flags) {
142+
console.info(`v${packageJSON.version}`);
143+
process.exit(0);
144+
}
138145

139146
let output = flags.output ? OUTPUT_FILE : OUTPUT_STDOUT; // FILE or STDOUT
140147
let outputFile = new URL(flags.output, CWD);
141148
let outputDir = new URL(".", outputFile);
142149

143-
const pathToSpec = input;
144-
145150
if (output === OUTPUT_FILE) {
146-
const packageJSON = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf8"));
147151
console.info(`✨ ${BOLD}openapi-typescript ${packageJSON.version}${RESET}`); // only log if we’re NOT writing to stdout
148152
}
149153

150-
// handle remote schema, exit
151-
if (HTTP_RE.test(pathToSpec)) {
154+
const pathToSpec = flags._[0];
155+
156+
// handle stdin schema, exit
157+
if (!pathToSpec) {
152158
if (output !== "." && output === OUTPUT_FILE) fs.mkdirSync(outputDir, { recursive: true });
153-
await generateSchema(pathToSpec);
159+
await generateSchema(process.stdin);
154160
return;
155161
}
156162

157-
// handle stdin schema, exit
158-
if (pathToSpec === "-") {
163+
// handle remote schema, exit
164+
if (HTTP_RE.test(pathToSpec)) {
159165
if (output !== "." && output === OUTPUT_FILE) fs.mkdirSync(outputDir, { recursive: true });
160-
await generateSchema(process.stdin);
166+
await generateSchema(pathToSpec);
161167
return;
162168
}
163169

@@ -167,12 +173,12 @@ async function main() {
167173

168174
// error: no matches for glob
169175
if (inputSpecPaths.length === 0) {
170-
errorAndExit(` Could not find any specs matching "${pathToSpec}". Please check that the path is correct.`);
176+
errorAndExit(` Could not find any specs matching "${pathToSpec}". Please check that the path is correct.`);
171177
}
172178

173179
// error: tried to glob output to single file
174180
if (isGlob && output === OUTPUT_FILE && fs.existsSync(outputDir) && fs.lstatSync(outputDir).isFile()) {
175-
errorAndExit(` Expected directory for --output if using glob patterns. Received "${flags.output}".`);
181+
errorAndExit(` Expected directory for --output if using glob patterns. Received "${flags.output}".`);
176182
}
177183

178184
// generate schema(s) in parallel

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "openapi-typescript",
33
"description": "Generate TypeScript types from Swagger OpenAPI specs",
4-
"version": "5.4.0",
4+
"version": "6.0.0",
55
"author": "drew@pow.rs",
66
"license": "MIT",
77
"bin": {
@@ -66,6 +66,7 @@
6666
"eslint": "^8.26.0",
6767
"eslint-config-prettier": "^8.5.0",
6868
"eslint-plugin-prettier": "^4.2.1",
69+
"execa": "^6.1.0",
6970
"prettier": "^2.7.1",
7071
"typescript": "^4.8.4",
7172
"vitest": "^0.24.5"

pnpm-lock.yaml

Lines changed: 65 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/cli.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { execa } from "execa";
2+
import fs from "node:fs";
3+
import { URL } from "node:url";
4+
5+
const cwd = new URL("../", import.meta.url);
6+
const cmd = "./bin/cli.js";
7+
8+
describe("CLI", () => {
9+
describe("snapshots", () => {
10+
test("GitHub API", async () => {
11+
const expected = fs.readFileSync(new URL("./examples/github-api.ts", cwd), "utf8").trim();
12+
const { stdout } = await execa(cmd, ["./test/fixtures/github-api.yaml"], { cwd });
13+
expect(stdout).toBe(expected);
14+
});
15+
test("Stripe API", async () => {
16+
const expected = fs.readFileSync(new URL("./examples/stripe-api.ts", cwd), "utf8").trim();
17+
const { stdout } = await execa(cmd, ["./test/fixtures/stripe-api.yaml"], {
18+
cwd,
19+
});
20+
expect(stdout).toBe(expected);
21+
});
22+
test("stdin", async () => {
23+
const expected = fs.readFileSync(new URL("./examples/stripe-api.ts", cwd), "utf8").trim();
24+
const input = fs.readFileSync(new URL("./test/fixtures/stripe-api.yaml", cwd));
25+
const { stdout } = await execa(cmd, { input });
26+
expect(stdout).toBe(expected);
27+
});
28+
});
29+
30+
describe("flags", () => {
31+
test("--help", async () => {
32+
const { stdout } = await execa(cmd, ["--help"], { cwd });
33+
expect(stdout).toEqual(expect.stringMatching(/^Usage\n\s+\$ openapi-typescript \[input\] \[options\]/));
34+
});
35+
36+
test("--version", async () => {
37+
const { stdout } = await execa(cmd, ["--version"], { cwd });
38+
expect(stdout).toEqual(expect.stringMatching(/^v[\d.]+(-.*)?$/));
39+
});
40+
});
41+
});

0 commit comments

Comments
 (0)