Skip to content

Commit ee1588c

Browse files
support tsconfig.json
1 parent 5040fb8 commit ee1588c

File tree

6 files changed

+115
-5
lines changed

6 files changed

+115
-5
lines changed

README.MD

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ This is a tool to generate swagger files from a [typescript-rest](https://github
88

99
**Table of Contents**
1010

11-
- [Swagger for Typescript-Rest](#)
11+
- [Swagger for Typescript-rest](#swagger-for-typescript-rest)
1212
- [Installation](#installation)
1313
- [Usage](#usage)
1414
- [Swagger Decorators](#swagger-decorators)
@@ -17,6 +17,7 @@ This is a tool to generate swagger files from a [typescript-rest](https://github
1717
- [@Tags](#tags)
1818
- [@Security](#security)
1919
- [@Produces](#produces)
20+
- [@IsInt, @IsLong, @IsFloat, @IsDouble](#isint-islong-isfloat-isdouble)
2021
- [SwaggerConfig.json](#swaggerconfigjson)
2122

2223
## Installation
@@ -29,6 +30,8 @@ npm install typescript-rest-swagger -g
2930

3031
```bash
3132
swaggerGen -c ./swaggerConfig.json
33+
swaggerGen -c ./swaggerConfig.json -t # load {cwd}/tsconfig.json
34+
swaggerGen -c ./swaggerConfig.json -p ./tsconfig.json # load custom tsconfig.json
3235
```
3336

3437
Where the [swaggerConfig.json](#swaggerconfigjson) file, contains settings about the swagger generation. For example:
@@ -42,6 +45,21 @@ Where the [swaggerConfig.json](#swaggerconfigjson) file, contains settings about
4245
}
4346
```
4447

48+
Where the [tsconfig.json](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) file contains compilerOptions. For example:
49+
50+
```json
51+
{
52+
"compilerOptions": {
53+
"baseUrl": ".",
54+
"paths": {
55+
"@/*": ["src/*"]
56+
}
57+
}
58+
}
59+
```
60+
61+
For example above options are required for `swaggerGen` to understand relative imports like `import something from '@/something'`.
62+
4563
### Swagger Decorators
4664

4765
The documentation will be generated consulting all [typescript-rest](https://github.com/thiagobustamante/typescript-rest) decorators present on your code.

src/cli.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#!/usr/bin/env node
22
'use strict';
33

4+
import { isAbsolute, join } from 'path';
5+
import * as ts from 'typescript';
46
import { ArgumentParser } from 'argparse';
57
import { Config, SwaggerConfig } from './config';
68
import { MetadataGenerator } from './metadata/metadataGenerator';
@@ -27,11 +29,29 @@ parser.addArgument(
2729
}
2830
);
2931

32+
parser.addArgument(
33+
['-t', '--tsconfig'],
34+
{
35+
action: 'storeTrue',
36+
defaultValue: false,
37+
help: 'Load tsconfig.json file',
38+
}
39+
);
40+
41+
parser.addArgument(
42+
['-p', '--tsconfig_path'],
43+
{
44+
help: 'The tsconfig file (tsconfig.json) path. Default to {cwd}/tsconfig.json.',
45+
}
46+
);
47+
3048
const parameters = parser.parseArgs();
3149
const config = getConfig(parameters.config);
50+
const compilerOptions = getCompilerOptions(parameters.tsconfig, parameters.tsconfig_path);
3251

3352
const swaggerConfig = validateSwaggerConfig(config.swagger);
34-
const metadata = new MetadataGenerator(swaggerConfig.entryFile).generate();
53+
54+
const metadata = new MetadataGenerator(swaggerConfig.entryFile, compilerOptions).generate();
3555
new SpecGenerator(metadata, swaggerConfig).generate(swaggerConfig.outputDirectory, swaggerConfig.yaml)
3656
.then(() => {
3757
console.info ('Generation completed.');
@@ -74,3 +94,40 @@ function validateSwaggerConfig(conf: SwaggerConfig): SwaggerConfig {
7494

7595
return conf;
7696
}
97+
98+
function getCompilerOptions(loadTsconfig: boolean, tsconfigPath?: string | null): ts.CompilerOptions {
99+
if (!loadTsconfig && tsconfigPath) {
100+
loadTsconfig = true;
101+
}
102+
if (!loadTsconfig) {
103+
return {};
104+
}
105+
const cwd = process.cwd();
106+
const defaultTsconfigPath = join(cwd, 'tsconfig.json');
107+
tsconfigPath = tsconfigPath
108+
? getAbsolutePath(tsconfigPath, cwd)
109+
: defaultTsconfigPath;
110+
try {
111+
const tsConfig = require(tsconfigPath);
112+
if (!tsConfig) {
113+
throw new Error('Invalid tsconfig');
114+
}
115+
return tsConfig.compilerOptions || {};
116+
} catch (err) {
117+
if (err.code === 'MODULE_NOT_FOUND') {
118+
throw Error(`No tsconfig file found at '${tsconfigPath}'`);
119+
} else if (err.name === 'SyntaxError') {
120+
throw Error(`Invalid JSON syntax in tsconfig at '${tsconfigPath}': ${err.message}`);
121+
} else {
122+
throw Error(`Unhandled error encountered loading tsconfig '${tsconfigPath}': ${err.message}`);
123+
}
124+
}
125+
}
126+
127+
function getAbsolutePath(path: string, basePath: string): string {
128+
if (isAbsolute(path)) {
129+
return path;
130+
} else {
131+
return join(basePath, path);
132+
}
133+
}

src/metadata/metadataGenerator.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ export class MetadataGenerator {
99
private referenceTypes: { [typeName: string]: ReferenceType } = {};
1010
private circularDependencyResolvers = new Array<(referenceTypes: { [typeName: string]: ReferenceType }) => void>();
1111

12-
constructor(entryFile: string) {
13-
this.program = ts.createProgram([entryFile], {});
12+
constructor(entryFile: string, compilerOptions: ts.CompilerOptions) {
13+
this.program = ts.createProgram([entryFile], compilerOptions);
1414
this.typeChecker = this.program.getTypeChecker();
1515
MetadataGenerator.current = this;
1616
}

test/data/TestInterface.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface TestInterface {
2+
a: string;
3+
b: number;
4+
}

test/data/apis.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
} from 'typescript-rest';
1111

1212
import * as swagger from '../../src/decorators';
13+
import { TestInterface } from '@/TestInterface'; // to test compilerOptions.paths
1314

1415
interface Address {
1516
street: string;
@@ -91,6 +92,12 @@ export class MyService {
9192
) {
9293
return;
9394
}
95+
96+
@POST
97+
@Path('test-compiler-options')
98+
async testCompilerOptions(payload: TestInterface): Promise<TestInterface> {
99+
return { a: 'string', b: 123 };
100+
}
94101
}
95102

96103
class BaseService {

test/unit/definitions.spec.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ const expect = chai.expect;
99
const jsonata = require('jsonata');
1010

1111
describe('Definition generation', () => {
12-
const metadata = new MetadataGenerator('./test/data/apis.ts').generate();
12+
const compilerOptions = {
13+
baseUrl: '.',
14+
paths: {
15+
'@/*': ['test/data/*'],
16+
},
17+
};
18+
const metadata = new MetadataGenerator('./test/data/apis.ts', compilerOptions).generate();
1319
const spec = new SpecGenerator(metadata, getDefaultOptions()).getSpec();
1420

1521
describe('MyService', () => {
@@ -204,6 +210,24 @@ describe('Definition generation', () => {
204210
// tslint:disable-next-line:no-unused-expression
205211
expect(expression.evaluate(spec)).to.not.exist;
206212
});
213+
214+
it('should support compilerOptions', () => {
215+
let expression = jsonata('definitions.TestInterface');
216+
expect(expression.evaluate(spec)).to.eql({
217+
description: '',
218+
properties: {
219+
a: { type: 'string', description: '' },
220+
b: { type: 'number', format: 'double', description: '' }
221+
},
222+
required: [ 'a', 'b' ],
223+
type: 'object',
224+
});
225+
expect(spec.paths).to.have.property('/mypath/test-compiler-options');
226+
expression = jsonata('paths."/mypath/test-compiler-options".post.responses."200".schema');
227+
expect(expression.evaluate(spec)).to.eql({ $ref: '#/definitions/TestInterface' });
228+
expression = jsonata('paths."/mypath/test-compiler-options".post.parameters[0].schema');
229+
expect(expression.evaluate(spec)).to.eql({ $ref: '#/definitions/TestInterface' });
230+
});
207231
});
208232

209233
describe('PrimitiveEndpoint', () => {

0 commit comments

Comments
 (0)