├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── package.json
├── src
├── ClassSpec.ts
├── EnumSpec.ts
├── FunctionSpec.ts
├── IdentifierSpec.ts
├── IndexSpec.ts
├── ModuleSpec.ts
├── Parser.ts
├── SignatureSpec.ts
├── TypeSpec.ts
└── index.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/
3 | package-lock.json
4 | *.lock
5 | *.log.*
6 | *.log
7 | *.tgz
8 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | src/
3 | package-lock.json
4 | .travis.yml
5 | *.lock
6 | *.log.*
7 | *.log
8 | *.tgz
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - "5.6"
5 | - "4.3"
6 | - "0.12"
7 | - "0.10"
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 BusFaster Ltd
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | readts
2 | ======
3 |
4 | [](http://travis-ci.org/charto/readts)
5 | [](https://www.npmjs.com/package/readts)
6 |
7 | This is a TypeScript exported class, function, type and documentation parser.
8 | It outputs everything needed to automatically generate documentation and better understand a project's public API.
9 | Information is extracted using TypeScript's [Compiler API](https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API).
10 |
11 | Usage
12 | -----
13 |
14 | ```typescript
15 | import * as readts from 'readts';
16 |
17 | var parser = new readts.Parser();
18 |
19 | // Read configuration used in the project we want to analyze.
20 | var config = parser.parseConfig('tsconfig.json');
21 |
22 | // Modify configuration as needed, for example to avoid writing compiler output to disk.
23 | config.options.noEmit = true;
24 |
25 | // Parse the project.
26 | var tree = parser.parse(config);
27 | ```
28 |
29 | The output is a list of [`ModuleSpec`](#api-ModuleSpec) objects, each with exported classes and interfaces ([`ClassSpec`](#api-ClassSpec)),
30 | functions ([`FunctionSpec`](#api-FunctionSpec)) and variables ([`IdentifierSpec`](#api-IdentifierSpec)).
31 | Types of variables and function parameters are parsed to [`TypeSpec`](#api-TypeSpec) objects,
32 | pointing to correct [`ClassSpec`](#api-ClassSpec) objects if applicable.
33 | This allows cross-linking types to their definitions in generated documentation.
34 |
35 | See [charto/docts](https://github.com/charto/docts) for a documentation generator using this parser.
36 |
37 | API
38 | ===
39 | Docs generated using [`docts`](https://github.com/charto/docts)
40 | >
41 | >
42 | > ### Class [`ClassSpec`](#api-ClassSpec)
43 | > Class or interface and its members.
44 | > Source code: [`<>`](http://github.com/charto/readts/blob/62754ee/src/ClassSpec.ts#L11-L74)
45 | >
46 | > Methods:
47 | > > **new( )** ⇒ [ClassSpec](#api-ClassSpec)
[`<>`](http://github.com/charto/readts/blob/62754ee/src/ClassSpec.ts#L14-L21)
48 | > > ▪ spec SymbolSpec
49 | >
50 | > Properties:
51 | > > **.name** string
52 | > > Class name.
53 | > > **.pos** [SourcePos](#api-SourcePos)
54 |
55 | > > **.construct** [FunctionSpec](#api-FunctionSpec)
56 | > > Constructor function.
57 | > > **.index** [IndexSpec](#api-IndexSpec)
58 | > > Index signature.
59 | > > **.methodList** [FunctionSpec](#api-FunctionSpec)[]
60 | > > Public methods.
61 | > > **.propertyList** [IdentifierSpec](#api-IdentifierSpec)[]
62 | > > Public properties.
63 | > > **.extendList** [ClassSpec](#api-ClassSpec)[]
64 | > > Class extends
65 | > > **.exports** [ModuleSpec](#api-ModuleSpec)
66 | > > Class exports
67 | > > **.doc** string
68 | > > JSDoc comment.
69 | >
70 | >
71 | > ### Class [`EnumSpec`](#api-EnumSpec)
72 | > Enum and its members.
73 | > Source code: [`<>`](http://github.com/charto/readts/blob/ce93baf/src/EnumSpec.ts#L11-L37)
74 | >
75 | > Methods:
76 | > > **new( )** ⇒ [EnumSpec](#api-EnumSpec)
[`<>`](http://github.com/charto/readts/blob/ce93baf/src/EnumSpec.ts#L14-L20)
77 | > > ▪ spec SymbolSpec
78 | > > **.addMember( )** ⇒ void
[`<>`](http://github.com/charto/readts/blob/ce93baf/src/EnumSpec.ts#L22-L26)
79 | > > ▪ spec [IdentifierSpec](#api-IdentifierSpec)
80 | >
81 | > Properties:
82 | > > **.name** string
83 | > > Class name.
84 | > > **.pos** [SourcePos](#api-SourcePos)
85 |
86 | > > **.memberList** [IdentifierSpec](#api-IdentifierSpec)[]
87 | > > Public properties.
88 | > > **.doc** string
89 | > > JSDoc comment.
90 | >
91 | >
92 | > ### Interface [`FormatHooks`](#api-FormatHooks)
93 | > Hooks to change how parts of type definitions are converted to strings.
94 | > Source code: [`<>`](http://github.com/charto/readts/blob/ce93baf/src/TypeSpec.ts#L11-L20)
95 | >
96 | > Properties:
97 | > > **.unknown**? FormatHook
98 | > > **.ref**? FormatHook
99 | > > **.array**? FormatHook
100 | > > **.union**? FormatHook
101 | > > **.intersection**? FormatHook
102 | > > **.generic**? FormatHook
103 | >
104 | >
105 | > ### Class [`FunctionSpec`](#api-FunctionSpec)
106 | > Function or method with any number of overloaded signatures.
107 | > Source code: [`<>`](http://github.com/charto/readts/blob/c857e04/src/FunctionSpec.ts#L11-L30)
108 | >
109 | > Methods:
110 | > > **new( )** ⇒ [FunctionSpec](#api-FunctionSpec)
[`<>`](http://github.com/charto/readts/blob/c857e04/src/FunctionSpec.ts#L14-L18)
111 | > > ▪ spec SymbolSpec
112 | >
113 | > Properties:
114 | > > **.name** string
115 | > > Function name.
116 | > > **.signatureList** [SignatureSpec](#api-SignatureSpec)[]
117 | > > List of signatures, one for each overload.
118 | >
119 | >
120 | > ### Class [`IdentifierSpec`](#api-IdentifierSpec)
121 | > Property, function / method parameter or variable.
122 | > Source code: [`<>`](http://github.com/charto/readts/blob/ce93baf/src/IdentifierSpec.ts#L11-L34)
123 | >
124 | > Properties:
125 | > > **.name** string
126 | > > Identifier name.
127 | > > **.pos** [SourcePos](#api-SourcePos)
128 | > > **.type** [TypeSpec](#api-TypeSpec)
129 | > > Type definition.
130 | > > **.value**? any
131 | > > Literal type value
132 | > > **.optional** boolean
133 | > > Interface members and function / method parameters may be optional.
134 | > > **.doc** string
135 | > > JSDoc comment.
136 | >
137 | >
138 | > ### Class [`IndexSpec`](#api-IndexSpec)
139 | > Index signature.
140 | > Source code: [`<>`](http://github.com/charto/readts/blob/62754ee/src/IndexSpec.ts#L9-L21)
141 | >
142 | > Properties:
143 | > > **.signature** [TypeSpec](#api-TypeSpec)
144 | > > Singature type.
145 | > > **.value** [TypeSpec](#api-TypeSpec)
146 | > > Value type
147 | >
148 | >
149 | > ### Class [`ModuleSpec`](#api-ModuleSpec)
150 | > Module or source file.
151 | > Source code: [`<>`](http://github.com/charto/readts/blob/ce93baf/src/ModuleSpec.ts#L11-L58)
152 | >
153 | > Methods:
154 | > > **.isEmpty( )** ⇒ boolean
[`<>`](http://github.com/charto/readts/blob/ce93baf/src/ModuleSpec.ts#L38-L46)
155 | > > Test if nothing is exported.
156 | >
157 | > Properties:
158 | > > **.enumList** [EnumSpec](#api-EnumSpec)[]
159 | > > Definitions of exported enums.
160 | > > **.classList** [ClassSpec](#api-ClassSpec)[]
161 | > > Definitions of exported classes.
162 | > > **.interfaceList** [ClassSpec](#api-ClassSpec)[]
163 | > > Definitions of exported interfaces.
164 | > > **.functionList** [FunctionSpec](#api-FunctionSpec)[]
165 | > > Definitions of exported functions.
166 | > > **.variableList** [IdentifierSpec](#api-IdentifierSpec)[]
167 | > > Definitions of exported variables.
168 | >
169 | >
170 | > ### Class [`Parser`](#api-Parser)
171 | > Main parser class with public methods, also holding its internal state.
172 | > Source code: [`<>`](http://github.com/charto/readts/blob/f0d51fd/src/Parser.ts#L36-L402)
173 | >
174 | > Methods:
175 | > > **.parseConfig( )** ⇒ ParsedCommandLine
[`<>`](http://github.com/charto/readts/blob/f0d51fd/src/Parser.ts#L39-L44)
176 | > > Parse a tsconfig.json file using TypeScript services API.
177 | > > ▪ tsconfigPath string
178 | > > **.parse( )** ⇒ [ModuleSpec](#api-ModuleSpec)[]
[`<>`](http://github.com/charto/readts/blob/f0d51fd/src/Parser.ts#L48-L75)
179 | > > Parse a TypeScript project using TypeScript services API and configuration.
180 | > > ▪ config ParsedCommandLine
181 | > > ▫ nameFilter? (pathName: string) => boolean
182 | > > ▫ extension? string
183 | >
184 | >
185 | > ### Interface [`RefSpec`](#api-RefSpec)
186 | > Source code: [`<>`](http://github.com/charto/readts/blob/f0d51fd/src/Parser.ts#L25-L32)
187 | >
188 | > Properties:
189 | > > **.name**? string
190 | > > **.symbol**? Symbol
191 | > > **.class**? [ClassSpec](#api-ClassSpec)
192 | > > **.enum**? [EnumSpec](#api-EnumSpec)
193 | >
194 | >
195 | > ### Class [`SignatureSpec`](#api-SignatureSpec)
196 | > Function or method signature defining input and output types.
197 | > Source code: [`<>`](http://github.com/charto/readts/blob/c857e04/src/SignatureSpec.ts#L11-L33)
198 | >
199 | > Methods:
200 | > > **new( )** ⇒ [SignatureSpec](#api-SignatureSpec)
[`<>`](http://github.com/charto/readts/blob/c857e04/src/SignatureSpec.ts#L14-L18)
201 | > > ▪ pos [SourcePos](#api-SourcePos)
202 | > > ▪ returnType [TypeSpec](#api-TypeSpec)
203 | > > ▪ doc string
204 | >
205 | > Properties:
206 | > > **.pos** [SourcePos](#api-SourcePos)
207 | > > **.paramList** [IdentifierSpec](#api-IdentifierSpec)[]
208 | > > List of parameters.
209 | > > **.returnType** [TypeSpec](#api-TypeSpec)
210 | > > Return type definition.
211 | > > **.doc** string
212 | > > JSDoc comment.
213 | >
214 | >
215 | > ### Interface [`SourcePos`](#api-SourcePos)
216 | > Source code: [`<>`](http://github.com/charto/readts/blob/f0d51fd/src/Parser.ts#L8-L12)
217 | >
218 | > Properties:
219 | > > **.sourcePath** string
220 | > > **.firstLine** number
221 | > > **.lastLine** number
222 | >
223 | >
224 | > ### Class [`TypeSpec`](#api-TypeSpec)
225 | > Type definition.
226 | > Source code: [`<>`](http://github.com/charto/readts/blob/ce93baf/src/TypeSpec.ts#L24-L163)
227 | >
228 | > Methods:
229 | > > **.format( )** ⇒ string
[`<>`](http://github.com/charto/readts/blob/ce93baf/src/TypeSpec.ts#L106-L147)
230 | > > Convert to string, with optional hooks replacing default formatting code.
231 | > > ▫ hooks? [FormatHooks](#api-FormatHooks)
232 | > > ▫ needParens? boolean
233 | >
234 | > Properties:
235 | > > **.name** string
236 | > > Name of the type, only present if not composed of other type or class etc.
237 | > > **.value**? string | number
238 | > > Value of the type, only present if literal type
239 | > > **.ref** [RefSpec](#api-RefSpec)
240 | > > Definition of what the type points to, if available.
241 | > > **.unionOf** [TypeSpec](#api-TypeSpec)[]
242 | > > If the type is a union, list of the possible types.
243 | > > **.intersectionOf** [TypeSpec](#api-TypeSpec)[]
244 | > > If the type is an intersection, list of the possible types.
245 | > > **.arrayOf** [TypeSpec](#api-TypeSpec)
246 | > > If the type is an array, its element type.
247 | > > **.argumentList** [TypeSpec](#api-TypeSpec)[]
248 | > > Arguments of a generic type.
249 |
250 | License
251 | =======
252 |
253 | [The MIT License](https://raw.githubusercontent.com/charto/readts/master/LICENSE)
254 |
255 | Copyright (c) 2016 BusFaster Ltd
256 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "readts",
3 | "version": "0.2.0",
4 | "description": "TypeScript exported definitions parser",
5 | "main": "dist/index.js",
6 | "typings": "dist/index.d.ts",
7 | "scripts": {
8 | "tsc": "tsc",
9 | "docts": "docts",
10 | "prepublish": "tsc",
11 | "test": "node dist/index.js"
12 | },
13 | "author": "Juha Järvi",
14 | "license": "MIT",
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/charto/readts.git"
18 | },
19 | "bugs": {
20 | "url": "https://github.com/charto/readts/issues"
21 | },
22 | "homepage": "https://github.com/charto/readts#readme",
23 | "devDependencies": {
24 | "@types/node": "^8.9.4",
25 | "docts": "~0.2.0"
26 | },
27 | "dependencies": {
28 | "typescript": "^2.7.2"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/ClassSpec.ts:
--------------------------------------------------------------------------------
1 | // This file is part of readts, copyright (c) 2016 BusFaster Ltd.
2 | // Released under the MIT license, see LICENSE.
3 |
4 | import * as ts from 'typescript';
5 | import * as readts from './index';
6 |
7 | import { SymbolSpec } from './Parser';
8 |
9 | /** Class or interface and its members. */
10 |
11 | export class ClassSpec {
12 | /** @ignore internal use. */
13 |
14 | constructor(spec: SymbolSpec) {
15 | this.name = spec.name;
16 | this.pos = spec.pos;
17 | this.symbol = spec.symbol;
18 | this.exports = new readts.ModuleSpec();
19 |
20 | if(spec.doc) this.doc = spec.doc;
21 | }
22 |
23 | /** Add constructor signature. @ignore internal use. */
24 |
25 | addConstructor(spec: readts.SignatureSpec) {
26 | if(!this.construct) this.construct = new readts.FunctionSpec(null);
27 |
28 | this.construct.addSignature(spec);
29 | }
30 |
31 | /** Add method. @ignore internal use. */
32 |
33 | addMethod(spec: readts.FunctionSpec) {
34 | if(!this.methodList) this.methodList = [];
35 |
36 | this.methodList.push(spec);
37 | }
38 |
39 | /** Add property. @ignore internal use. */
40 |
41 | addProperty(spec: readts.IdentifierSpec) {
42 | if(!this.propertyList) this.propertyList = [];
43 |
44 | this.propertyList.push(spec);
45 | }
46 |
47 | /** Add extend. @ignore internal use. */
48 |
49 | addExtend(spec: readts.ClassSpec) {
50 | if(!this.extendList) this.extendList = [];
51 |
52 | this.extendList.push(spec);
53 | }
54 |
55 | /** Class name. */
56 | name: string;
57 | pos: readts.SourcePos;
58 | /** Symbol from TypeScript services. @ignore internal use. */
59 | symbol: ts.Symbol;
60 | /** Constructor function. */
61 | construct: readts.FunctionSpec;
62 | /** Index signature. */
63 | index: readts.IndexSpec;
64 | /** Public methods. */
65 | methodList: readts.FunctionSpec[];
66 | /** Public properties. */
67 | propertyList: readts.IdentifierSpec[];
68 | /** Class extends */
69 | extendList: readts.ClassSpec[];
70 | /** Class exports */
71 | exports: readts.ModuleSpec;
72 | /** JSDoc comment. */
73 | doc: string;
74 | }
75 |
--------------------------------------------------------------------------------
/src/EnumSpec.ts:
--------------------------------------------------------------------------------
1 | // This file is part of readts, copyright (c) 2018 Arttu Liimola.
2 | // Released under the MIT license, see LICENSE.
3 |
4 | import * as ts from 'typescript';
5 | import * as readts from './index';
6 |
7 | import { SymbolSpec } from './Parser';
8 |
9 | /** Enum and its members. */
10 |
11 | export class EnumSpec {
12 | /** @ignore internal use. */
13 |
14 | constructor(spec: SymbolSpec) {
15 | this.name = spec.name;
16 | this.pos = spec.pos;
17 | this.symbol = spec.symbol;
18 |
19 | if(spec.doc) this.doc = spec.doc;
20 | }
21 |
22 | addMember(spec: readts.IdentifierSpec) {
23 | if(!this.memberList) this.memberList = [];
24 |
25 | this.memberList.push(spec);
26 | }
27 |
28 | /** Class name. */
29 | name: string;
30 | pos: readts.SourcePos;
31 | /** Symbol from TypeScript services. @ignore internal use. */
32 | symbol: ts.Symbol;
33 | /** Public properties. */
34 | memberList: readts.IdentifierSpec[];
35 | /** JSDoc comment. */
36 | doc: string;
37 | }
38 |
--------------------------------------------------------------------------------
/src/FunctionSpec.ts:
--------------------------------------------------------------------------------
1 | // This file is part of readts, copyright (c) 2016 BusFaster Ltd.
2 | // Released under the MIT license, see LICENSE.
3 |
4 | import * as ts from 'typescript';
5 | import * as readts from './index';
6 |
7 | import { SymbolSpec } from './Parser';
8 |
9 | /** Function or method with any number of overloaded signatures. */
10 |
11 | export class FunctionSpec {
12 | /** @ignore internal use. */
13 |
14 | constructor(spec: SymbolSpec) {
15 | if(spec) {
16 | this.name = spec.name;
17 | }
18 | }
19 |
20 | /** Add a new signature. @ignore internal use. */
21 |
22 | addSignature(spec: readts.SignatureSpec) {
23 | this.signatureList.push(spec);
24 | }
25 |
26 | /** Function name. */
27 | name: string;
28 | /** List of signatures, one for each overload. */
29 | signatureList: readts.SignatureSpec[] = [];
30 | }
31 |
--------------------------------------------------------------------------------
/src/IdentifierSpec.ts:
--------------------------------------------------------------------------------
1 | // This file is part of readts, copyright (c) 2016 BusFaster Ltd.
2 | // Released under the MIT license, see LICENSE.
3 |
4 | import * as ts from 'typescript';
5 | import * as readts from './index';
6 |
7 | import { SymbolSpec } from './Parser';
8 |
9 | /** Property, function / method parameter or variable. */
10 |
11 | export class IdentifierSpec {
12 | /** @ignore internal use. */
13 |
14 | constructor(spec: SymbolSpec, type: readts.TypeSpec, optional: boolean) {
15 | this.name = spec.name;
16 | this.type = type;
17 | this.value = type.value;
18 | this.optional = optional;
19 | this.pos = spec.pos;
20 | if(spec.doc) this.doc = spec.doc;
21 | }
22 |
23 | /** Identifier name. */
24 | name: string;
25 | pos: readts.SourcePos;
26 | /** Type definition. */
27 | type: readts.TypeSpec;
28 | /** Literal type value */
29 | value?: any;
30 | /** Interface members and function / method parameters may be optional. */
31 | optional: boolean;
32 | /** JSDoc comment. */
33 | doc: string;
34 | }
35 |
--------------------------------------------------------------------------------
/src/IndexSpec.ts:
--------------------------------------------------------------------------------
1 | // This file is part of readts, copyright (c) 2018 Arttu Liimola.
2 | // Released under the MIT license, see LICENSE.
3 |
4 | import * as ts from 'typescript';
5 | import * as readts from './index';
6 |
7 | /** Index signature. */
8 |
9 | export class IndexSpec {
10 | /** @ignore internal use. */
11 |
12 | constructor(signature: readts.TypeSpec, value: readts.TypeSpec) {
13 | this.signature = signature;
14 | this.value = value;
15 | }
16 |
17 | /** Singature type. */
18 | signature: readts.TypeSpec;
19 | /** Value type */
20 | value: readts.TypeSpec;
21 | }
22 |
--------------------------------------------------------------------------------
/src/ModuleSpec.ts:
--------------------------------------------------------------------------------
1 | // This file is part of readts, copyright (c) 2016 BusFaster Ltd.
2 | // Released under the MIT license, see LICENSE.
3 |
4 | import * as ts from 'typescript';
5 | import * as readts from './index';
6 |
7 | import { SymbolSpec } from './Parser';
8 |
9 | /** Module or source file. */
10 |
11 | export class ModuleSpec {
12 | /** Add an exported enum. @ignore internal use. */
13 |
14 | addEnum(spec: readts.EnumSpec) {
15 | this.enumList.push(spec);
16 | }
17 |
18 | /** Add an exported class. @ignore internal use. */
19 |
20 | addClass(spec: readts.ClassSpec) {
21 | this.classList.push(spec);
22 | }
23 |
24 | /** Add an exported interface. @ignore internal use. */
25 |
26 | addInterface(spec: readts.ClassSpec) {
27 | this.interfaceList.push(spec);
28 | }
29 |
30 | /** Add an exported function. @ignore internal use. */
31 |
32 | addFunction(spec: readts.FunctionSpec) {
33 | this.functionList.push(spec);
34 | }
35 |
36 | /** Test if nothing is exported. */
37 |
38 | isEmpty() {
39 | return(
40 | !this.enumList.length &&
41 | !this.classList.length &&
42 | !this.interfaceList.length &&
43 | !this.functionList.length &&
44 | !this.variableList.length
45 | );
46 | }
47 |
48 | /** Definitions of exported enums. */
49 | enumList: readts.EnumSpec[] = [];
50 | /** Definitions of exported classes. */
51 | classList: readts.ClassSpec[] = [];
52 | /** Definitions of exported interfaces. */
53 | interfaceList: readts.ClassSpec[] = [];
54 | /** Definitions of exported functions. */
55 | functionList: readts.FunctionSpec[] = [];
56 | /** Definitions of exported variables. */
57 | variableList: readts.IdentifierSpec[] = [];
58 | }
59 |
--------------------------------------------------------------------------------
/src/Parser.ts:
--------------------------------------------------------------------------------
1 | // This file is part of readts, copyright (c) 2016 BusFaster Ltd.
2 | // Released under the MIT license, see LICENSE.
3 |
4 | import * as path from 'path';
5 | import * as ts from 'typescript';
6 | import * as readts from './index';
7 |
8 | export interface SourcePos {
9 | sourcePath: string;
10 | firstLine: number;
11 | lastLine: number;
12 | }
13 |
14 | /** @ignore internal use. */
15 |
16 | export interface SymbolSpec {
17 | name: string;
18 | symbol: ts.Symbol;
19 | declaration: ts.Declaration;
20 | type: ts.Type;
21 | pos: SourcePos;
22 | doc: string;
23 | }
24 |
25 | export interface RefSpec {
26 | [key: string]: any;
27 |
28 | name?: string;
29 | symbol?: ts.Symbol;
30 | class?: readts.ClassSpec;
31 | enum?: readts.EnumSpec;
32 | }
33 |
34 | /** Main parser class with public methods, also holding its internal state. */
35 |
36 | export class Parser {
37 | /** Parse a tsconfig.json file using TypeScript services API. */
38 |
39 | parseConfig(tsconfigPath: string) {
40 | var configJson = ts.parseConfigFileTextToJson(tsconfigPath, ts.sys.readFile(tsconfigPath)).config;
41 | var config = ts.parseJsonConfigFileContent(configJson, ts.sys, tsconfigPath.replace(/[^/]+$/, ''), {}, tsconfigPath);
42 |
43 | return(config);
44 | }
45 |
46 | /** Parse a TypeScript project using TypeScript services API and configuration. */
47 |
48 | parse(
49 | config: ts.ParsedCommandLine,
50 | nameFilter?: (pathName: string) => boolean,
51 | extension?: string
52 | ): readts.ModuleSpec[] {
53 | var sourceNum = 0;
54 |
55 | this.program = ts.createProgram(config.fileNames, config.options);
56 | this.checker = this.program.getTypeChecker();
57 | this.moduleList = [];
58 | this.symbolTbl = {};
59 |
60 | for(var source of this.program.getSourceFiles()) {
61 | // Skip contents of the default library.
62 | if(sourceNum++ == 0) continue;
63 |
64 | // Call optional filter to check if file should be parsed.
65 | if(
66 | !nameFilter ||
67 | !extension ||
68 | nameFilter(this.getOwnEmitOutputFilePath(source, this.program, extension))
69 | ) {
70 | this.parseSource(source);
71 | }
72 | }
73 |
74 | return(this.moduleList);
75 | }
76 |
77 | /** Convert an otherwise unrecognized type to string. @ignore internal use. */
78 |
79 | typeToString(type: ts.Type) {
80 | return(this.checker.typeToString(type));
81 | }
82 |
83 | /** Get or change reference for a symbol. @ignore internal use. */
84 |
85 | getRef(symbol: ts.Symbol, ref?: RefSpec) {
86 | var name = symbol.getName();
87 | var symbolList = this.symbolTbl[name];
88 |
89 | if(!symbolList) {
90 | symbolList = [];
91 | this.symbolTbl[name] = symbolList;
92 | } else {
93 | for(var match of symbolList) {
94 | if(symbol == match.symbol) {
95 | if(ref) for(var key of Object.keys(ref)) match[key] = ref[key];
96 |
97 | return(match);
98 | }
99 | }
100 | }
101 |
102 | if(!ref) ref = {};
103 |
104 | ref.name = name;
105 | ref.symbol = symbol;
106 | symbolList.push(ref);
107 |
108 | return(ref);
109 | }
110 |
111 | private parseType(type: ts.Type) {
112 | var spec = new readts.TypeSpec(type, this);
113 |
114 | return(spec);
115 | }
116 |
117 | private parseSource(source: ts.SourceFile) {
118 | var symbol = this.checker.getSymbolAtLocation(source);
119 | if(!symbol) return;
120 |
121 | var exportTbl = symbol.exports;
122 |
123 | for(var name of this.getKeys(exportTbl).sort()) {
124 | var spec = this.parseSymbol(exportTbl.get(name));
125 |
126 | // Resolve aliases.
127 | while(1) {
128 | var symbolFlags = spec.symbol.getFlags();
129 |
130 | if(symbolFlags & ts.SymbolFlags.Alias) {
131 | spec = this.parseSymbol(this.checker.getAliasedSymbol(spec.symbol));
132 | } else break;
133 | }
134 |
135 | if(spec.declaration) {
136 | var module = new readts.ModuleSpec();
137 |
138 | this.parseDeclaration(spec, module);
139 |
140 | this.moduleList.push(module);
141 | }
142 | }
143 | }
144 |
145 | /** Extract declared function, class or interface from a symbol. */
146 |
147 | private parseDeclaration(spec: SymbolSpec, moduleSpec: readts.ModuleSpec) {
148 | var node = spec.declaration as ts.Node;
149 |
150 | switch(node.kind) {
151 | case ts.SyntaxKind.FunctionDeclaration:
152 | if(spec) {
153 | var functionSpec = this.parseFunction(spec);
154 | if(functionSpec) moduleSpec.addFunction(functionSpec);
155 | }
156 | break;
157 | case ts.SyntaxKind.EnumDeclaration:
158 | if (spec) {
159 | var enumSpec = this.parseEnum(spec);
160 | if(enumSpec) moduleSpec.addEnum(enumSpec);
161 | }
162 | break;
163 | case ts.SyntaxKind.ClassDeclaration:
164 | case ts.SyntaxKind.InterfaceDeclaration:
165 | if(spec) {
166 | var classSpec = this.parseClass(spec);
167 | if(classSpec) {
168 | if(node.kind == ts.SyntaxKind.InterfaceDeclaration) {
169 | moduleSpec.addInterface(classSpec);
170 | } else moduleSpec.addClass(classSpec);
171 | }
172 | }
173 | break;
174 | }
175 | }
176 |
177 | private parseComment(symbol: ts.Symbol | ts.Signature) {
178 | return(ts.displayPartsToString(symbol.getDocumentationComment(this.checker)).trim());
179 | }
180 |
181 | private parsePos(node: ts.Declaration): SourcePos {
182 | var source = node.getSourceFile();
183 |
184 | return({
185 | sourcePath: source.fileName,
186 | firstLine: ts.getLineAndCharacterOfPosition(source, node.getStart()).line + 1,
187 | lastLine: ts.getLineAndCharacterOfPosition(source, node.getEnd()).line + 1
188 | });
189 | }
190 |
191 | private parseSymbol(symbol: ts.Symbol) {
192 | var declaration = symbol.valueDeclaration;
193 | var type: ts.Type = null;
194 | var pos: SourcePos = null;
195 |
196 | // Interfaces have no value declaration.
197 | if(!declaration) declaration = symbol.getDeclarations()[0];
198 |
199 | // On merged declaration with enums, valueDeclaration is ModuleDeclaration.
200 | if(declaration.kind == ts.SyntaxKind.ModuleDeclaration) {
201 | const first = symbol.getDeclarations()[0];
202 |
203 | if(first.kind == ts.SyntaxKind.InterfaceDeclaration)
204 | declaration = first;
205 | }
206 |
207 | if(declaration) {
208 | pos = this.parsePos(declaration);
209 | type = this.checker.getTypeOfSymbolAtLocation(symbol, declaration);
210 | }
211 |
212 | var spec: SymbolSpec = {
213 | symbol: symbol,
214 | declaration: declaration,
215 | type: type,
216 | name: symbol.getName(),
217 | pos: pos,
218 | doc: this.parseComment(symbol)
219 | };
220 |
221 | return(spec);
222 | }
223 |
224 | private parseEnum(spec: SymbolSpec) {
225 | var enumSpec = new readts.EnumSpec(spec);
226 |
227 | this.getRef(spec.symbol, { enum: enumSpec });
228 |
229 | if(spec.symbol.getFlags() & ts.SymbolFlags.HasExports) {
230 | var exportTbl = spec.symbol.exports;
231 |
232 | for(let key of this.getKeys(exportTbl)) {
233 | const symbol = exportTbl.get(key);
234 |
235 | const spec = this.parseSymbol(exportTbl.get(key));
236 |
237 | if(!spec) continue;
238 |
239 | if(spec.symbol.getFlags() & ts.SymbolFlags.EnumMember) {
240 | enumSpec.addMember(this.parseIdentifier(spec, false));
241 | }
242 | }
243 | }
244 |
245 | return(enumSpec);
246 | }
247 |
248 | private parseClass(spec: SymbolSpec) {
249 | var classSpec = new readts.ClassSpec(spec);
250 |
251 | this.getRef(spec.symbol, { class: classSpec });
252 |
253 | // Interfaces have no value type.
254 | if(spec.type) {
255 | for(var signature of spec.type.getConstructSignatures()) {
256 | classSpec.addConstructor(this.parseSignature(signature));
257 | }
258 | }
259 |
260 | var memberTbl = spec.symbol.members;
261 |
262 | for(let key of this.getKeys(memberTbl)) {
263 | const spec = this.parseSymbol(memberTbl.get(key));
264 |
265 | if(!spec) continue;
266 |
267 | if(spec.declaration) {
268 | if(ts.getCombinedModifierFlags(spec.declaration) & ts.ModifierFlags.Private) continue;
269 | }
270 |
271 | const symbolFlags = spec.symbol.getFlags();
272 |
273 | if(symbolFlags & ts.SymbolFlags.Method) {
274 | classSpec.addMethod(this.parseFunction(spec));
275 | } else if(symbolFlags & ts.SymbolFlags.Property) {
276 | classSpec.addProperty(this.parseIdentifier(spec, !!(symbolFlags & ts.SymbolFlags.Optional)));
277 | } else if(ts.isIndexSignatureDeclaration(spec.declaration)) {
278 | classSpec.index = this.parseIndex(spec);
279 | }
280 | }
281 |
282 | const heritageClauses = (spec.declaration).heritageClauses;
283 |
284 | if(heritageClauses) {
285 | for(let heritageClause of heritageClauses) {
286 | for(let type of heritageClause.types) {
287 | const symbol = this.checker.getSymbolAtLocation(type.expression);
288 |
289 | if(symbol && symbol.declarations.length) {
290 | const ref = this.getRef(symbol);
291 |
292 | if(ref.class) {
293 | classSpec.addExtend(ref.class);
294 | }
295 | }
296 | }
297 | }
298 | }
299 |
300 | if(spec.symbol.getFlags() & ts.SymbolFlags.HasExports) {
301 | var exportTbl = spec.symbol.exports;
302 |
303 | for(let key of this.getKeys(exportTbl)) {
304 | const symbol = exportTbl.get(key);
305 |
306 | if(!(symbol.getFlags() & ts.SymbolFlags.ClassMember)) {
307 | const spec = this.parseSymbol(exportTbl.get(key));
308 |
309 | if(!spec) continue;
310 |
311 | this.parseDeclaration(spec, classSpec.exports);
312 | }
313 | }
314 | }
315 |
316 | return(classSpec);
317 | }
318 |
319 | private parseFunction(spec: SymbolSpec) {
320 | var funcSpec = new readts.FunctionSpec(spec);
321 |
322 | for(var signature of spec.type.getCallSignatures()) {
323 | funcSpec.addSignature(this.parseSignature(signature));
324 | }
325 |
326 | return(funcSpec);
327 | }
328 |
329 | private parseIndex(spec: SymbolSpec) {
330 | var declaration = spec.declaration as ts.IndexSignatureDeclaration;
331 | var parameter = declaration.parameters[0] as ts.ParameterDeclaration;
332 |
333 | // ParameterDeclaration does have symbol, even though it is not on interfaces.
334 | var singatureType = this.parseType(this.checker.getTypeOfSymbolAtLocation((parameter).symbol, parameter.type));
335 | var valueType = this.parseType(this.checker.getTypeAtLocation(declaration.type));
336 |
337 | var indexSpec = new readts.IndexSpec(singatureType, valueType);
338 | return(indexSpec);
339 | }
340 |
341 | /** Parse property, function / method parameter or variable. */
342 |
343 | private parseIdentifier(spec: SymbolSpec, optional: boolean) {
344 | var varSpec = new readts.IdentifierSpec(spec, this.parseType(spec.type), optional);
345 | return(varSpec);
346 | }
347 |
348 | /** Parse function / method signature. */
349 |
350 | private parseSignature(signature: ts.Signature) {
351 | var pos: SourcePos;
352 | var declaration = signature.getDeclaration();
353 |
354 | if(declaration) pos = this.parsePos(declaration);
355 |
356 | var signatureSpec = new readts.SignatureSpec(
357 | pos,
358 | this.parseType(signature.getReturnType()),
359 | this.parseComment(signature)
360 | );
361 |
362 | for(var param of signature.parameters) {
363 | var spec = this.parseSymbol(param);
364 | if(spec) signatureSpec.addParam(this.parseIdentifier(spec, this.checker.isOptionalParameter(spec.declaration as ts.ParameterDeclaration)));
365 | }
366 |
367 | return(signatureSpec);
368 | }
369 |
370 | private getKeys(map: ts.ReadonlyUnderscoreEscapedMap): ts.__String[] {
371 | const keys: ts.__String[] = [];
372 | map.forEach((_, key) => keys.push(key));
373 | return(keys);
374 | }
375 |
376 | private getOwnEmitOutputFilePath(sourceFile: ts.SourceFile, program: ts.Program, extension: string): string {
377 | var getCanonicalFileName = (ts as any).createGetCanonicalFileName(ts.sys.useCaseSensitiveFileNames);
378 | var host = {
379 | getCanonicalFileName,
380 | getCommonSourceDirectory: (program as any).getCommonSourceDirectory,
381 | getCompilerOptions: program.getCompilerOptions,
382 | getCurrentDirectory: program.getCurrentDirectory,
383 | };
384 | var outPath = (ts as any).getOwnEmitOutputFilePath(sourceFile, host, extension);
385 | return(path.resolve(program.getCurrentDirectory(), outPath));
386 | }
387 |
388 | private static isNodeExported(node: ts.Node) {
389 | return(
390 | !!(ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export) ||
391 | (node.parent && node.parent.kind == ts.SyntaxKind.SourceFile)
392 | );
393 | }
394 |
395 | /** TypeScript services API object. */
396 | private program: ts.Program;
397 | /** TypeScript services type checker. */
398 | private checker: ts.TypeChecker;
399 | /** List of modules found while parsing. */
400 | private moduleList: readts.ModuleSpec[];
401 | private symbolTbl: { [name: string]: RefSpec[] };
402 | }
403 |
--------------------------------------------------------------------------------
/src/SignatureSpec.ts:
--------------------------------------------------------------------------------
1 | // This file is part of readts, copyright (c) 2016 BusFaster Ltd.
2 | // Released under the MIT license, see LICENSE.
3 |
4 | import * as ts from 'typescript';
5 | import * as readts from './index';
6 |
7 | import { SymbolSpec } from './Parser';
8 |
9 | /** Function or method signature defining input and output types. */
10 |
11 | export class SignatureSpec {
12 | /** @ignore internal use. */
13 |
14 | constructor(pos: readts.SourcePos, returnType: readts.TypeSpec, doc: string) {
15 | this.pos = pos;
16 | this.returnType = returnType;
17 | if(doc) this.doc = doc;
18 | }
19 |
20 | /** Add a new parameter and type. @ignore internal use. */
21 |
22 | addParam(spec: readts.IdentifierSpec) {
23 | this.paramList.push(spec);
24 | }
25 |
26 | pos: readts.SourcePos;
27 | /** List of parameters. */
28 | paramList: readts.IdentifierSpec[] = [];
29 | /** Return type definition. */
30 | returnType: readts.TypeSpec;
31 | /** JSDoc comment. */
32 | doc: string;
33 | }
34 |
--------------------------------------------------------------------------------
/src/TypeSpec.ts:
--------------------------------------------------------------------------------
1 | // This file is part of readts, copyright (c) 2016 BusFaster Ltd.
2 | // Released under the MIT license, see LICENSE.
3 |
4 | import * as ts from 'typescript';
5 | import * as readts from './index';
6 |
7 | export type FormatHook = (spec: TypeSpec, output?: string, hooks?: FormatHooks) => string;
8 |
9 | /** Hooks to change how parts of type definitions are converted to strings. */
10 |
11 | export interface FormatHooks {
12 | [name: string]: FormatHook;
13 |
14 | unknown?: FormatHook;
15 | ref?: FormatHook;
16 | array?: FormatHook;
17 | union?: FormatHook;
18 | intersection?: FormatHook;
19 | generic?: FormatHook;
20 | }
21 |
22 | /** Type definition. */
23 |
24 | export class TypeSpec {
25 | /** Parse a type from TypeScript services. @ignore internal use. */
26 |
27 | constructor(type: ts.Type, parser: readts.Parser) {
28 | var tf = ts.TypeFlags;
29 | var oFlags = ts.ObjectFlags;
30 |
31 | // console.log(Object.keys(tf).map((name: string) => type.flags & tf[name] ? name : null).filter((name) => !!name).join(' | '));
32 |
33 | if(type.flags & tf.EnumLiteral){
34 | if(type.flags & tf.Union) {
35 | this.ref = parser.getRef(type.symbol);
36 | } else {
37 | this.parseEnum(type);
38 | }
39 | } else if(this.isSimpleType(type)) {
40 | this.name = parser.typeToString(type);
41 | } else if (this.isTypeReference(type)) {
42 | this.parseReference(type, parser);
43 | } else if (this.isClassLikeType(type)) {
44 | this.parseClass(type, parser);
45 | } else if (this.isObjectType(type) && (type.objectFlags & oFlags.Tuple)) {
46 | } else if (type.flags & tf.Union) {
47 | this.unionOf = this.parseList((type as ts.UnionOrIntersectionType).types, parser);
48 | } else if (type.flags & tf.Intersection) {
49 | this.intersectionOf = this.parseList((type as ts.UnionOrIntersectionType).types, parser);
50 | }
51 | }
52 |
53 | private parseEnum(type: ts.Type) {
54 | this.name = type.flags & ts.TypeFlags.NumberLiteral ? 'number' : 'string';
55 | this.value = (type).value;
56 | }
57 |
58 | private parseClass(type: ts.Type, parser: readts.Parser) {
59 | this.ref = parser.getRef(type.symbol);
60 | }
61 |
62 | private parseReference(type: ts.TypeReference, parser: readts.Parser) {
63 | // Hack to recognize arrays, TypeScript services doesn't seem to export
64 | // the array symbol it uses internally to detect array types
65 | // so just check the name.
66 | if(type.target.symbol.getName() == 'Array' && type.typeArguments) {
67 | this.arrayOf = new TypeSpec(type.typeArguments[0], parser);
68 | } else {
69 | this.parseClass(type, parser);
70 | if(type.typeArguments) this.argumentList = this.parseList(type.typeArguments, parser);
71 | }
72 | }
73 |
74 | private parseList(typeList: ts.Type[], parser: readts.Parser) {
75 | return(typeList.map((type: ts.Type) => new TypeSpec(type, parser)));
76 | }
77 |
78 | private isSimpleType(type: ts.Type): boolean {
79 | if (!!(type.flags & ((ts.TypeFlags as any).Intrinsic | ts.TypeFlags.StringLiteral))) {
80 | return true;
81 | }
82 | if ((type as any).isThisType) {
83 | return true;
84 | }
85 | if (this.isObjectType(type) && !!(type.objectFlags & ts.ObjectFlags.Anonymous)) {
86 | return true;
87 | }
88 | return false;
89 | }
90 |
91 | private isClassLikeType(type: ts.Type): boolean {
92 | return !!(type.flags & (ts.TypeFlags.Enum | ts.TypeFlags.TypeParameter))
93 | || (this.isObjectType(type) && !!(type.objectFlags & ts.ObjectFlags.ClassOrInterface));
94 | }
95 |
96 | private isObjectType(type: ts.Type): type is ts.ObjectType {
97 | return !!(type.flags & ts.TypeFlags.Object);
98 | }
99 |
100 | private isTypeReference(type: ts.Type): type is ts.TypeReference {
101 | return this.isObjectType(type) && !!(type.objectFlags & ts.ObjectFlags.Reference);
102 | }
103 |
104 | /** Convert to string, with optional hooks replacing default formatting code. */
105 |
106 | format(hooks?: FormatHooks, needParens?: boolean): string {
107 | var output: string;
108 | var hook: FormatHook;
109 |
110 | if(!hooks) hooks = {};
111 |
112 | if(this.name) {
113 | hook = hooks.unknown;
114 | output = this.name;
115 | }
116 |
117 | if(this.ref) {
118 | output = this.ref.name;
119 | if(hooks.ref) output = hooks.ref(this, output, hooks);
120 |
121 | if(this.argumentList) {
122 | hook = hooks.generic;
123 | output += '<' + this.argumentList.map((spec: TypeSpec) => spec.format(hooks, false)).join(', ') + '>';
124 | }
125 | }
126 |
127 | if(this.arrayOf) {
128 | hook = hooks.array;
129 | output = this.arrayOf.format(hooks, true) + '[]';
130 | }
131 |
132 | if(output) return(hook ? hook(this, output, hooks) : output);
133 |
134 | if(this.unionOf) {
135 | hook = hooks.union;
136 | output = this.unionOf.map((spec: TypeSpec) => spec.format(hooks, true)).join(' | ');
137 | }
138 |
139 | if(this.intersectionOf) {
140 | hook = hooks.intersection;
141 | output = this.intersectionOf.map((spec: TypeSpec) => spec.format(hooks, true)).join(' & ');
142 | }
143 |
144 | if(needParens) output = '(' + output + ')';
145 |
146 | return(hook ? hook(this, output, hooks) : output);
147 | }
148 |
149 | /** Name of the type, only present if not composed of other type or class etc. */
150 | name: string;
151 | /** Value of the type, only present if literal type */
152 | value?: string | number;
153 | /** Definition of what the type points to, if available. */
154 | ref: readts.RefSpec;
155 | /** If the type is a union, list of the possible types. */
156 | unionOf: TypeSpec[];
157 | /** If the type is an intersection, list of the possible types. */
158 | intersectionOf: TypeSpec[];
159 | /** If the type is an array, its element type. */
160 | arrayOf: TypeSpec;
161 | /** Arguments of a generic type. */
162 | argumentList: TypeSpec[];
163 | }
164 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | // This file is part of readts, copyright (c) 2016 BusFaster Ltd.
2 | // Released under the MIT license, see LICENSE.
3 |
4 | export { IdentifierSpec } from './IdentifierSpec';
5 | export { SignatureSpec } from './SignatureSpec';
6 | export { FunctionSpec } from './FunctionSpec';
7 | export { EnumSpec } from './EnumSpec';
8 | export { ClassSpec } from './ClassSpec';
9 | export { ModuleSpec } from './ModuleSpec';
10 | export { IndexSpec } from './IndexSpec';
11 | export { TypeSpec, FormatHook, FormatHooks } from './TypeSpec';
12 | export { Parser, SourcePos, RefSpec } from './Parser';
13 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": true,
3 | "compilerOptions": {
4 | "declaration": true,
5 | "experimentalDecorators": true,
6 | "module": "commonjs",
7 | "moduleResolution": "node",
8 | "noImplicitAny": true,
9 | "noImplicitThis": true,
10 | "outDir": "dist",
11 | "removeComments": false,
12 | "target": "es5"
13 | },
14 | "files": [
15 | "src/index.ts"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------