Skip to content

Commit

Permalink
feat(cli): add option for usage as executable
Browse files Browse the repository at this point in the history
  • Loading branch information
elyukai committed May 28, 2023
1 parent 179bdd7 commit 53b2858
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 1 deletion.
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ npm i -D optolith-tsjsonschemamd

## Usage

### Programmatic Usage

```ts
import { generate } from "optolith-tsjsonschemamd";
import { jsonSchema, markdown } from "optolith-tsjsonschemamd/renderers";
Expand Down Expand Up @@ -45,14 +47,24 @@ generate({

It does not do any clean-up in the target directories, it only overwrites existing files. You can activate this explicitly, though, for all outputs and for each output individually.

The output directory structure as well as the contents of the files **mirror the TypeScript sources**. The relative directory structure inside the specified root source directory is mirrored and the contents are simply mapped, so that the output will not have any duplicate types, which are referenced by their relative path instead (except for generic types, see below).
The output directory structure as well as the contents of the files **mirror the TypeScript sources**. The relative directory structure inside the specified root source directory is mirrored, and the contents are simply mapped, so that the output will not have any duplicate types, which are referenced by their relative path instead (except for generic types, see below).

This also implies that all types must be present in the specified root source directory or its subdirectories, otherwise references/links in output files will not work.

You can also build your own renderer by conforming to the `Renderer` type that can be imported.

An error is thrown if the tool encounters an unsupported structure.

### CLI Usage

This package can also be used via the command line.

```sh
otjsmd [-w | --watch] [-c <path-to-config> | --config <path-to-config>]
```

Options must be defined in an ECMAScript module files, which defaults to a file called `otjsmd.config.js` in the directory where the command is run. You can specify a different path using the respective option. Supply the watch option to rebuild whenever a source file changes.

### Main type

A module comment may indicate the main type of the module, which can be used by directly importing the JSON Schema without the need to specify the definition inside. The type is referenced to by its name.
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
"./config": "./lib/config.js",
"./renderers": "./lib/renderers.js"
},
"bin": {
"otjsmd": "./lib/cli.js"
},
"scripts": {
"build": "tsc",
"watch": "tsc -w",
Expand Down
56 changes: 56 additions & 0 deletions src/bin/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env node
import { watch } from "node:fs/promises"
import { resolve } from "node:path"
import { argv, cwd } from "node:process"
import { GeneratorOptions, generate } from "../main.js"

const cliOptions = argv.slice(2)
.reduce<[lastOption: string | undefined, options: Map<string, string[]>]>(
([lastOption, map], arg) => {
if (/^-{1,2}/.test(arg)) {
return [arg, map.set(arg, [])]
} else if (lastOption) {
return [undefined, map.set(lastOption, [...map.get(lastOption)!, arg])]
} else {
return [undefined, map]
}
},
[undefined, new Map()]
)[1]

const optionsPath = resolve(cwd(), cliOptions.get("c")?.[0] ?? cliOptions.get("config")?.[0] ?? "otjsmd.config.js")
const options = (await import(optionsPath)).default as GeneratorOptions

if (cliOptions.has("w") || cliOptions.has("watch")) {
try {
generate(options)
}
catch (err) {
console.error(err)
}
finally {
console.log("Watching for changes ...")

const generate$ = debounce(generate, 300)

for await (const _ of watch(options.sourceDir, { recursive: true })) {
try {
generate$(options)
}
catch (err) {
console.error(err)
}
}
}
}
else {
generate(options)
}

function debounce<T extends any[]>(this: any, f: (...args: T) => void, timeout: number): (...args: T) => void {
let timer: ReturnType<typeof setTimeout>
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => { f.apply(this, args) }, timeout)
}
}

0 comments on commit 53b2858

Please sign in to comment.