├── .babelrc ├── .gitignore ├── LICENSE.md ├── NOTICE.md ├── README.md ├── baselines ├── anyFallback │ ├── anyFallback.d.ts │ └── anyFallback.js ├── arrayOf │ ├── basic.d.ts │ └── basic.js ├── basic │ ├── basic.d.ts │ ├── basic.js │ ├── index.d.ts │ ├── required.d.ts │ └── required.js ├── compose │ ├── module.d.ts │ ├── module.js │ ├── multiple.d.ts │ ├── multiple.js │ ├── single.d.ts │ └── single.js ├── func │ ├── basic.d.ts │ └── basic.js ├── inheritance │ ├── base.d.ts │ ├── base.js │ ├── basic.d.ts │ └── basic.js ├── method │ ├── method.d.ts │ └── method.js ├── oneOf │ ├── basic.d.ts │ └── basic.js ├── oneOfType │ ├── basic.d.ts │ ├── basic.js │ ├── withShape.d.ts │ └── withShape.js └── shape │ ├── basic.d.ts │ ├── basic.js │ ├── nested.d.ts │ ├── nested.js │ ├── withOneOfType.d.ts │ └── withOneOfType.js ├── package.json ├── src ├── dts-dom │ └── index.ts ├── index.ts ├── typings │ ├── comment-parser.d.ts │ └── react-docgen.d.ts └── utils.ts ├── test ├── anyFallback.test.ts ├── arrayOf.test.ts ├── basic.test.ts ├── compose.test.ts ├── func.test.ts ├── inheritance.test.ts ├── method.test.ts ├── mocha.opts ├── oneOf.test.ts ├── oneOfType.ts └── shape.test.ts ├── tsconfig.json ├── tslint.json └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ], 6 | "plugins": [ 7 | [ 8 | "@babel/plugin-proposal-decorators", 9 | { 10 | "legacy": true 11 | } 12 | ], 13 | [ 14 | "@babel/plugin-proposal-class-properties", 15 | { 16 | "loose": true 17 | } 18 | ], 19 | [ 20 | "@babel/plugin-proposal-object-rest-spread", 21 | { 22 | "loose": true 23 | } 24 | ], 25 | "@babel/plugin-transform-object-assign", 26 | "@babel/plugin-transform-runtime" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | */tmp/*.d.ts 2 | node_modules 3 | bin/ 4 | *.log 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 16 | 17 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 18 | 19 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 20 | 21 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 22 | 23 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 24 | 25 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 26 | 27 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 28 | 29 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 30 | 31 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 32 | 33 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 34 | 35 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 36 | 37 | You must give any other recipients of the Work or Derivative Works a copy of this License; and 38 | 39 | You must cause any modified files to carry prominent notices stating that You changed the files; and 40 | 41 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 42 | 43 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 44 | 45 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 46 | 47 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 48 | 49 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 50 | 51 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 52 | 53 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 54 | 55 | END OF TERMS AND CONDITIONS 56 | -------------------------------------------------------------------------------- /NOTICE.md: -------------------------------------------------------------------------------- 1 | This project uses [dts-dom](https://github.com/RyanCavanaugh/dts-dom) library distributed under Apache License, Version 2.0 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-dts-generator 2 | 3 | Simple `.d.ts` generator for React components. Try with [Repl.](https://repl.it/@mozkarakoc/react-dts-generator) 4 | 5 | 6 | ## Installation 7 | 8 | ```sh 9 | ## npm 10 | npm install react-dts-generator 11 | ## yarn 12 | yarn add react-dts-generator 13 | ``` 14 | 15 | 16 | ## Usage 17 | 18 | ```js 19 | import { generate } from 'react-dts-generator'; 20 | const result = generate(options); 21 | ``` 22 | 23 | ### Options 24 | 25 | #### `input: string` 26 | 27 | Path of the `.js` file that contains React Component. `react-dts-generator` use the [`react-docgen`](https://github.com/reactjs/react-docgen) library to generate props and methods. The input file format guideline: 28 | 29 | - Modules have to export a single component, and only that component is analyzed. 30 | - When using `React.createClass`, the component definition (the value passed to it) must resolve to an object literal. 31 | - When using classes, the class must either `extend React.Component` _or_ define a `render()` method. 32 | - `propTypes` must be an object literal or resolve to an object literal in the same file. 33 | - The `return` statement in `getDefaultProps` must contain an object literal. 34 | 35 | #### `output: string` 36 | 37 | The `.d.ts` file that contains typescript definitions. If not specified output file will be exported to the same location of the input file. 38 | 39 | 40 | #### `isBaseClass?: boolean` 41 | 42 | If the input component is a base class for another component the type definition could be generated with generic prop types like below. Then, another component could pass own props to the base class definition. 43 | 44 | **Input** 45 | ```jsx 46 | import React from 'react'; 47 | import PropTypes from 'prop-types'; 48 | 49 | class BaseClass extends React.Component { 50 | constructor(props, context) { 51 | super(props, context); 52 | this.bar = this.bar.bind(this); 53 | } 54 | 55 | foo = () => { } 56 | bar() { } 57 | 58 | render() { 59 | return
BaseClass
; 60 | } 61 | } 62 | 63 | BaseClass.propTypes = { 64 | foo: PropTypes.any, 65 | } 66 | 67 | export default BaseClass; 68 | ``` 69 | **Generate** 70 | 71 | ```js 72 | const result = generate({ 73 | input: 'path-to-input', 74 | isBaseClass: true, 75 | }); 76 | ``` 77 | 78 | **Output** 79 | ```ts 80 | import * as React from "react"; 81 | 82 | export interface BaseClassProps { 83 | foo?: any; 84 | } 85 | 86 | export default class BaseClass extends React.Component { 87 | foo(): any; 88 | bar(): any; 89 | } 90 | 91 | ``` 92 | 93 | #### `extends?: { includePropsAsGeneric: boolean, import: ImportType }` 94 | 95 | If the input component inherits from another component, the base class could import from outside. 96 | 97 | - ```includePropsAsGeneric: boolean``` 98 | Should the props of the input component pass to the base class as generic? 99 | 100 | - ```import: ImportType``` 101 | Indicates where the base class located. 102 | 103 | **Input** 104 | 105 | ```jsx 106 | import * as React from 'react'; 107 | import * as PropTypes from 'prop-types'; 108 | import BaseClass from './base'; 109 | 110 | class TestClass extends BaseClass { 111 | render() { 112 | return
TestClass
; 113 | } 114 | } 115 | 116 | TestClass.propTypes = { 117 | foo: PropTypes.any, 118 | } 119 | 120 | export default TestClass; 121 | ``` 122 | **Generate** 123 | 124 | ```js 125 | const result = generate({ 126 | input: 'path-to-input', 127 | extends: { 128 | import: { 129 | default: 'BaseClass', 130 | from: './base', 131 | }, 132 | includePropsAsGeneric: true, 133 | }, 134 | }); 135 | ``` 136 | 137 | **Output** 138 | ```ts 139 | import * as React from "react"; 140 | import BaseClass from "./base"; 141 | 142 | export interface TestClassProps { 143 | foo?: any; 144 | } 145 | 146 | export default class TestClass extends BaseClass {} 147 | ``` 148 | 149 | #### `propTypesComposition: ImportType[]` 150 | 151 | If the component propTypes has composes by another component's propTypes, and typescript definitions of the other component were already generated they could be imported and generated types extend from them. 152 | 153 | ```jsx 154 | TestClass.propTypes = { 155 | ...BaseClass.propTypes, 156 | foo: PropTypes.any, 157 | } 158 | ``` 159 | 160 | ```js 161 | const result = generate({ 162 | input: 'path-to-input', 163 | propTypesComposition: [{ 164 | named: 'BaseClass', 165 | from: '../base-props-path', 166 | }], 167 | }); 168 | ``` 169 | 170 | ### Samples 171 | 172 | Checkout the [baselines](https://github.com/KuveytTurk/react-dts-generator/tree/master/baselines) and [tests](https://github.com/KuveytTurk/react-dts-generator/tree/master/test). 173 | 174 | 175 | 176 | ### Known Issues 177 | 178 | - These propTypes generated as `any` 179 | - `PropTypes.symbol` 180 | - `PropTypes.elementType` 181 | - `PropTypes.instanceOf` 182 | - `PropTypes.objectOf` 183 | - `PropTypes.exact` 184 | 185 | - Custom propTypes is not supported. -------------------------------------------------------------------------------- /baselines/anyFallback/anyFallback.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export interface AnyFallbackComponentProps { 4 | invalidOptionalOneOf?: any; 5 | invalidOneOf: any; 6 | } 7 | 8 | export default class AnyFallbackComponent extends React.Component< 9 | AnyFallbackComponentProps 10 | > {} 11 | -------------------------------------------------------------------------------- /baselines/anyFallback/anyFallback.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | 4 | import { someObject } from 'some-external-file-or-package'; 5 | 6 | const AnyFallbackComponent = () => { 7 | return
AnyFallbackComponent
; 8 | }; 9 | 10 | AnyFallbackComponent.propTypes = { 11 | invalidOptionalOneOf: PropTypes.oneOf(Object.values(someObject)), 12 | invalidOneOf: PropTypes.oneOf(Object.values(someObject)).isRequired, 13 | }; 14 | 15 | export default AnyFallbackComponent; 16 | -------------------------------------------------------------------------------- /baselines/arrayOf/basic.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export interface ArrayOfComponentProps { 4 | arrayOfString?: string[]; 5 | arrayOfNumber: number[]; 6 | } 7 | 8 | export default class ArrayOfComponent extends React.Component< 9 | ArrayOfComponentProps 10 | > {} 11 | -------------------------------------------------------------------------------- /baselines/arrayOf/basic.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | 4 | const ArrayOfComponent = () => { 5 | return
ArrayOfComponent
; 6 | }; 7 | 8 | ArrayOfComponent.propTypes = { 9 | arrayOfString: PropTypes.arrayOf(PropTypes.string), 10 | arrayOfNumber: PropTypes.arrayOf(PropTypes.number).isRequired, 11 | }; 12 | 13 | export default ArrayOfComponent; 14 | -------------------------------------------------------------------------------- /baselines/basic/basic.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export interface BasicComponentProps { 4 | arrayProp?: any[]; 5 | boolProp?: boolean; 6 | numberProp?: number; 7 | objectProp?: object; 8 | stringProp?: string; 9 | anyProp?: any; 10 | elementProp?: React.ReactElement; 11 | nodeProp?: React.ReactNode; 12 | } 13 | 14 | export default class BasicComponent extends React.Component< 15 | BasicComponentProps 16 | > {} 17 | -------------------------------------------------------------------------------- /baselines/basic/basic.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | 4 | const BasicComponent = () => { 5 | return
BasicComponent
; 6 | }; 7 | 8 | BasicComponent.propTypes = { 9 | arrayProp: PropTypes.array, 10 | boolProp: PropTypes.bool, 11 | numberProp: PropTypes.number, 12 | objectProp: PropTypes.object, 13 | stringProp: PropTypes.string, 14 | anyProp: PropTypes.any, 15 | elementProp: PropTypes.element, 16 | nodeProp: PropTypes.node, 17 | }; 18 | 19 | export default BasicComponent; 20 | -------------------------------------------------------------------------------- /baselines/basic/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from './basic'; 2 | export * from './required'; -------------------------------------------------------------------------------- /baselines/basic/required.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export interface RequiredComponentProps { 4 | requiredProp: string; 5 | numberProp?: number; 6 | } 7 | 8 | export default class RequiredComponent extends React.Component< 9 | RequiredComponentProps 10 | > {} 11 | -------------------------------------------------------------------------------- /baselines/basic/required.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | 4 | const RequiredComponent = () => { 5 | return
RequiredComponent
; 6 | }; 7 | 8 | RequiredComponent.propTypes = { 9 | requiredProp: PropTypes.string.isRequired, 10 | numberProp: PropTypes.number, 11 | }; 12 | 13 | export default RequiredComponent; 14 | -------------------------------------------------------------------------------- /baselines/compose/module.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { BasicComponentProps } from "../basic/basic"; 3 | import { ComponentBaseProps } from "@kuveytturk/boa-base/ComponentBase"; 4 | 5 | export interface ComposedComponentProps 6 | extends BasicComponentProps, 7 | ComponentBaseProps { 8 | temp?: any; 9 | } 10 | 11 | export default class ComposedComponent extends React.Component< 12 | ComposedComponentProps 13 | > {} 14 | -------------------------------------------------------------------------------- /baselines/compose/module.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | import BasicComponent from '../basic/basic'; 4 | import ComponentBase from '@kuveytturk/boa-base/ComponentBase' 5 | 6 | class ComposedComponent extends React.Component { 7 | render() { 8 | return
ComposedComponent
; 9 | } 10 | }; 11 | 12 | ComposedComponent.propTypes = { 13 | ...BasicComponent.propTypes, 14 | ...ComponentBase.propTypes, 15 | temp: PropTypes.any, 16 | }; 17 | 18 | export default ComposedComponent; 19 | -------------------------------------------------------------------------------- /baselines/compose/multiple.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { BasicComponentProps, RequiredComponentProps } from "../basic"; 3 | 4 | export interface ComposedComponentProps 5 | extends BasicComponentProps, 6 | RequiredComponentProps { 7 | temp?: any; 8 | } 9 | 10 | export default class ComposedComponent extends React.Component< 11 | ComposedComponentProps 12 | > {} 13 | -------------------------------------------------------------------------------- /baselines/compose/multiple.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | import BasicComponent from '../basic/basic'; 4 | import RequiredComponent from '../basic/required'; 5 | 6 | class ComposedComponent extends React.Component { 7 | render() { 8 | return
ComposedComponent
; 9 | } 10 | }; 11 | 12 | ComposedComponent.propTypes = { 13 | ...BasicComponent.propTypes, 14 | ...RequiredComponent.propTypes, 15 | temp: PropTypes.any, 16 | }; 17 | 18 | export default ComposedComponent; 19 | -------------------------------------------------------------------------------- /baselines/compose/single.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { BasicComponentProps } from "../basic/basic"; 3 | 4 | export interface ComposedComponentProps extends BasicComponentProps { 5 | temp?: any; 6 | } 7 | 8 | export default class ComposedComponent extends React.Component< 9 | ComposedComponentProps 10 | > {} 11 | -------------------------------------------------------------------------------- /baselines/compose/single.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | import BasicComponent from '../basic/basic'; 4 | 5 | class ComposedComponent extends React.Component { 6 | render() { 7 | return
ComposedComponent
; 8 | } 9 | }; 10 | 11 | ComposedComponent.propTypes = { 12 | ...BasicComponent.propTypes, 13 | temp: PropTypes.any, 14 | }; 15 | 16 | export default ComposedComponent; 17 | -------------------------------------------------------------------------------- /baselines/func/basic.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export interface FuncComponentProps { 4 | checkResult?(stringParam: string): boolean; 5 | onChange(number: number): void; 6 | } 7 | 8 | export default class FuncComponent extends React.Component< 9 | FuncComponentProps 10 | > {} 11 | -------------------------------------------------------------------------------- /baselines/func/basic.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | 4 | const FuncComponent = () => { 5 | return
FuncComponent
; 6 | }; 7 | 8 | FuncComponent.propTypes = { 9 | /** 10 | * @param {string} stringParam 11 | * @return {bool} result 12 | */ 13 | checkResult: PropTypes.func, 14 | /** 15 | * @param {number} number - The param value. 16 | */ 17 | onChange: PropTypes.func.isRequired, 18 | 19 | }; 20 | 21 | export default FuncComponent; 22 | -------------------------------------------------------------------------------- /baselines/inheritance/base.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export interface BaseClassProps { 4 | foo?: any; 5 | } 6 | 7 | export default class BaseClass extends React.Component { 8 | foo(): any; 9 | bar(): any; 10 | } 11 | -------------------------------------------------------------------------------- /baselines/inheritance/base.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | class BaseClass extends React.Component { 5 | constructor(props, context) { 6 | super(props, context); 7 | this.bar = this.bar.bind(this); 8 | } 9 | 10 | foo = () => { } 11 | bar() { } 12 | 13 | render() { 14 | return
BaseClass
; 15 | } 16 | } 17 | 18 | BaseClass.propTypes = { 19 | foo: PropTypes.any, 20 | } 21 | 22 | export default BaseClass; 23 | -------------------------------------------------------------------------------- /baselines/inheritance/basic.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import BaseClass from "./base"; 3 | 4 | export interface TestClassProps { 5 | foo?: any; 6 | } 7 | 8 | export default class TestClass extends BaseClass {} 9 | -------------------------------------------------------------------------------- /baselines/inheritance/basic.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | import BaseClass from './base'; 4 | 5 | class TestClass extends BaseClass { 6 | render() { 7 | return
TestClass
; 8 | } 9 | } 10 | 11 | TestClass.propTypes = { 12 | foo: PropTypes.any, 13 | } 14 | 15 | export default TestClass; 16 | -------------------------------------------------------------------------------- /baselines/method/method.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export interface MethodComponentProps { 4 | temp?: any; 5 | } 6 | 7 | export default class MethodComponent extends React.Component< 8 | MethodComponentProps 9 | > { 10 | foo(number: number): number; 11 | bar(): string; 12 | multiple(first: number, second: string): any[]; 13 | getInstance(): this; 14 | } 15 | -------------------------------------------------------------------------------- /baselines/method/method.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | 4 | class MethodComponent extends React.Component { 5 | constructor(props, context) { 6 | super(props, context); 7 | this.foo = this.foo.bind(this); 8 | } 9 | 10 | static propTypes = { 11 | temp: PropTypes.any, 12 | }; 13 | 14 | /** 15 | * @param {number} number - The param value. 16 | * @return {number} returns a number value 17 | */ 18 | foo(number) { 19 | return number; 20 | } 21 | 22 | /** 23 | * @return {string} returns a string 24 | */ 25 | bar = () => { 26 | return 'bar'; 27 | } 28 | 29 | /** 30 | * 31 | * @param {number} first 32 | * @param {string} second 33 | * @returns {array} 34 | */ 35 | multiple(first, second) { 36 | return [first, second]; 37 | } 38 | 39 | /** 40 | * @returns {this} 41 | */ 42 | getInstance() { 43 | return this; 44 | } 45 | } 46 | 47 | export default MethodComponent; 48 | -------------------------------------------------------------------------------- /baselines/oneOf/basic.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export interface OneOfComponentProps { 4 | oneOfString?: "foo" | "bar"; 5 | oneOfNumber: 1 | 2 | 3; 6 | } 7 | 8 | export default class OneOfComponent extends React.Component< 9 | OneOfComponentProps 10 | > {} 11 | -------------------------------------------------------------------------------- /baselines/oneOf/basic.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | 4 | const OneOfComponent = () => { 5 | return
OneOfComponent
; 6 | }; 7 | 8 | OneOfComponent.propTypes = { 9 | oneOfString: PropTypes.oneOf(['foo', 'bar']), 10 | oneOfNumber: PropTypes.oneOf([1, 2, 3]).isRequired, 11 | }; 12 | 13 | export default OneOfComponent; 14 | -------------------------------------------------------------------------------- /baselines/oneOfType/basic.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export interface OneOfTypeComponentProps { 4 | oneOfStringOrNumber?: string | number; 5 | anyType?: any; 6 | oneOfBoolOrObject: boolean | object; 7 | } 8 | 9 | export default class OneOfTypeComponent extends React.Component< 10 | OneOfTypeComponentProps 11 | > {} 12 | -------------------------------------------------------------------------------- /baselines/oneOfType/basic.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | 4 | const OneOfTypeComponent = () => { 5 | return
OneOfTypeComponent
; 6 | }; 7 | 8 | OneOfTypeComponent.propTypes = { 9 | oneOfStringOrNumber: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 10 | anyType: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), 11 | oneOfBoolOrObject: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]).isRequired, 12 | }; 13 | 14 | export default OneOfTypeComponent; 15 | -------------------------------------------------------------------------------- /baselines/oneOfType/withShape.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | declare interface Baz { 4 | foo?: number; 5 | bar?: string; 6 | } 7 | 8 | declare interface ObjectOrShape { 9 | foo?: number; 10 | bar?: string; 11 | baz?: Baz; 12 | } 13 | 14 | export interface WithShapeProps { 15 | objectOrShape?: object | ObjectOrShape; 16 | } 17 | 18 | export default class WithShape extends React.Component {} 19 | -------------------------------------------------------------------------------- /baselines/oneOfType/withShape.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | 4 | const WithShape = () => { 5 | return
WithShape
; 6 | }; 7 | 8 | WithShape.propTypes = { 9 | objectOrShape: PropTypes.oneOfType([ 10 | PropTypes.object, 11 | PropTypes.shape({ 12 | foo: PropTypes.number, 13 | bar: PropTypes.string, 14 | baz: PropTypes.shape({ 15 | foo: PropTypes.number, 16 | bar: PropTypes.string, 17 | }), 18 | }), 19 | ]), 20 | }; 21 | 22 | export default WithShape; 23 | -------------------------------------------------------------------------------- /baselines/shape/basic.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | declare interface First { 4 | foo?: number; 5 | bar?: string; 6 | } 7 | 8 | declare interface Second { 9 | foo?: object; 10 | bar?: boolean; 11 | } 12 | 13 | export interface ShapeComponentProps { 14 | first: First; 15 | second?: Second; 16 | } 17 | 18 | export default class ShapeComponent extends React.Component< 19 | ShapeComponentProps 20 | > {} 21 | -------------------------------------------------------------------------------- /baselines/shape/basic.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | 4 | const ShapeComponent = () => { 5 | return
ShapeComponent
; 6 | }; 7 | 8 | ShapeComponent.propTypes = { 9 | first: PropTypes.shape({ 10 | foo: PropTypes.number, 11 | bar: PropTypes.string 12 | }).isRequired, 13 | second: PropTypes.shape({ 14 | foo: PropTypes.object, 15 | bar: PropTypes.bool 16 | }) 17 | }; 18 | 19 | export default ShapeComponent; 20 | -------------------------------------------------------------------------------- /baselines/shape/nested.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | declare interface Baz { 4 | foo?: number; 5 | bar?: string; 6 | } 7 | 8 | declare interface Nested { 9 | foo?: number; 10 | bar?: string; 11 | baz?: Baz; 12 | } 13 | 14 | export interface NestedShapeProps { 15 | nested?: Nested; 16 | } 17 | 18 | export default class NestedShape extends React.Component {} 19 | -------------------------------------------------------------------------------- /baselines/shape/nested.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | 4 | const NestedShape = () => { 5 | return
NestedShape
; 6 | }; 7 | 8 | NestedShape.propTypes = { 9 | nested: PropTypes.shape({ 10 | foo: PropTypes.number, 11 | bar: PropTypes.string, 12 | baz: PropTypes.shape({ 13 | foo: PropTypes.number, 14 | bar: PropTypes.string, 15 | }), 16 | }), 17 | }; 18 | 19 | export default NestedShape; 20 | -------------------------------------------------------------------------------- /baselines/shape/withOneOfType.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | declare interface Shape { 4 | foo?: number; 5 | bar: string; 6 | } 7 | 8 | declare interface First { 9 | foo?: number | boolean; 10 | bar?: string; 11 | baz?: any; 12 | shape?: object | Shape; 13 | } 14 | 15 | export interface OneOfTypeProps { 16 | first?: First; 17 | } 18 | 19 | export default class OneOfType extends React.Component {} 20 | -------------------------------------------------------------------------------- /baselines/shape/withOneOfType.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as PropTypes from 'prop-types'; 3 | 4 | const OneOfType = () => { 5 | return
OneOfType
; 6 | }; 7 | 8 | OneOfType.propTypes = { 9 | first: PropTypes.shape({ 10 | foo: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]), 11 | bar: PropTypes.string, 12 | baz: PropTypes.oneOfType([PropTypes.any, PropTypes.object]), 13 | shape: PropTypes.oneOfType([PropTypes.object, PropTypes.shape({ 14 | foo: PropTypes.number, 15 | bar: PropTypes.string.isRequired, 16 | })]), 17 | }) 18 | }; 19 | 20 | export default OneOfType; 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "kuveytturk", 3 | "description": "Simple .d.ts generator for React components.", 4 | "license": "Apache-2.0", 5 | "name": "react-dts-generator", 6 | "version": "0.1.3", 7 | "main": "./bin/src/index.js", 8 | "types": "./bin/src/index.d.ts", 9 | "files": [ 10 | "bin/src/**/*.*" 11 | ], 12 | "keywords": [ 13 | "react", 14 | "typescript", 15 | "dts", 16 | "definitelytyped", 17 | "typings" 18 | ], 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/KuveytTurk/react-dts-generator.git" 22 | }, 23 | "bugs": { 24 | "url": "https://github.com/KuveytTurk/react-dts-generator/issues" 25 | }, 26 | "scripts": { 27 | "test": "mocha bin/test/*.js", 28 | "build": "rimraf bin && tsc", 29 | "lint": "tslint -p tsconfig.json \"./{src,baselines,test}/**/*.{ts,tsx}\"", 30 | "lint:fix": "tslint -p tsconfig.json \"./{src,baselines,test}/**/*.{ts,tsx}\" --fix " 31 | }, 32 | "dependencies": { 33 | "comment-parser": "^0.5.4", 34 | "prettier": "^1.16.4", 35 | "react-docgen": "^4.0.1", 36 | "typescript": "^3.3.4000" 37 | }, 38 | "devDependencies": { 39 | "@babel/core": "^7.3.4", 40 | "@babel/node": "^7.2.2", 41 | "@babel/plugin-proposal-class-properties": "^7.3.4", 42 | "@babel/plugin-proposal-decorators": "^7.3.0", 43 | "@babel/plugin-proposal-object-rest-spread": "^7.3.4", 44 | "@babel/plugin-transform-object-assign": "^7.2.0", 45 | "@babel/plugin-transform-runtime": "^7.3.4", 46 | "@babel/preset-env": "^7.3.4", 47 | "@babel/preset-react": "^7.0.0", 48 | "@babel/register": "^7.0.0", 49 | "@kuveytturk/boa-base": "^0.1.3", 50 | "@types/chai": "^4.1.7", 51 | "@types/mocha": "^5.2.6", 52 | "@types/node": "^11.11.4", 53 | "@types/parse-git-config": "^3.0.0", 54 | "@types/prettier": "^1.16.1", 55 | "@types/prop-types": "^15.7.0", 56 | "@types/react": "^16.8.8", 57 | "@types/yargs": "^12.0.10", 58 | "chai": "^4.2.0", 59 | "cross-env": "^5.2.0", 60 | "diff": "^4.0.1", 61 | "mocha": "^6.0.2", 62 | "prop-types": "^15.7.2", 63 | "react": "^16.8.3", 64 | "rimraf": "^2.6.3", 65 | "tslint": "^5.14.0", 66 | "tslint-config-prettier": "^1.18.0" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/dts-dom/index.ts: -------------------------------------------------------------------------------- 1 | export interface DeclarationBase { 2 | jsDocComment?: string; 3 | comment?: string; 4 | flags?: DeclarationFlags; 5 | } 6 | 7 | export interface EnumMemberDeclaration extends DeclarationBase { 8 | kind: "enum-value"; 9 | name: string; 10 | value?: string | number; 11 | } 12 | 13 | export interface EnumDeclaration extends DeclarationBase { 14 | kind: "enum"; 15 | name: string; 16 | members: EnumMemberDeclaration[]; 17 | constant: boolean; 18 | } 19 | 20 | export interface PropertyDeclaration extends DeclarationBase { 21 | kind: "property"; 22 | name: string; 23 | type: Type; 24 | } 25 | 26 | export interface Parameter { 27 | kind: "parameter"; 28 | name: string; 29 | type: Type; 30 | flags?: ParameterFlags; 31 | } 32 | 33 | export interface TypeParameter { 34 | kind: "type-parameter"; 35 | name: string; 36 | baseType?: ObjectTypeReference | TypeParameter; 37 | } 38 | 39 | export interface IndexSignature extends DeclarationBase { 40 | kind: "index-signature"; 41 | name: string; 42 | indexType: ("string" | "number"); 43 | valueType: Type; 44 | } 45 | 46 | export interface CallSignature extends DeclarationBase { 47 | kind: "call-signature"; 48 | parameters: Parameter[]; 49 | returnType: Type; 50 | typeParameters: TypeParameter[]; 51 | } 52 | 53 | export interface MethodDeclaration extends DeclarationBase { 54 | kind: "method"; 55 | name: string; 56 | parameters: Parameter[]; 57 | returnType: Type; 58 | typeParameters: TypeParameter[]; 59 | } 60 | 61 | export interface FunctionDeclaration extends DeclarationBase { 62 | kind: "function"; 63 | name: string; 64 | parameters: Parameter[]; 65 | returnType: Type; 66 | typeParameters: TypeParameter[]; 67 | } 68 | 69 | export interface ConstructorDeclaration extends DeclarationBase { 70 | kind: "constructor"; 71 | parameters: Parameter[]; 72 | } 73 | 74 | export interface ClassDeclaration extends DeclarationBase { 75 | kind: "class"; 76 | name: string; 77 | members: ClassMember[]; 78 | implements: InterfaceDeclaration[]; 79 | typeParameters: TypeParameter[]; 80 | baseType?: BaseTypeReference; 81 | } 82 | 83 | export interface InterfaceDeclaration extends DeclarationBase { 84 | kind: "interface"; 85 | name: string; 86 | members: ObjectTypeMember[]; 87 | baseTypes: BaseTypeReference[]; 88 | } 89 | 90 | export interface Imports extends DeclarationBase { 91 | kind: "imports"; 92 | members: Import[]; 93 | } 94 | 95 | export interface ImportAllDeclaration extends DeclarationBase { 96 | kind: "importAll"; 97 | name: string; 98 | from: string; 99 | } 100 | 101 | export interface ImportNamedDeclaration extends DeclarationBase { 102 | kind: "importNamed"; 103 | name: string; 104 | as?: string; 105 | from: string; 106 | } 107 | 108 | export interface ImportDefaultDeclaration extends DeclarationBase { 109 | kind: "importDefault"; 110 | name: string; 111 | from: string; 112 | } 113 | 114 | export interface ImportEqualsDeclaration extends DeclarationBase { 115 | kind: "import="; 116 | name: string; 117 | from: string; 118 | } 119 | 120 | export interface ImportDeclaration extends DeclarationBase { 121 | kind: "import"; 122 | from: string; 123 | } 124 | 125 | export interface NamespaceDeclaration extends DeclarationBase { 126 | kind: "namespace"; 127 | name: string; 128 | members: NamespaceMember[]; 129 | } 130 | 131 | export interface ConstDeclaration extends DeclarationBase { 132 | kind: "const"; 133 | name: string; 134 | type: Type; 135 | } 136 | 137 | export interface VariableDeclaration extends DeclarationBase { 138 | kind: "var"; 139 | name: string; 140 | type: Type; 141 | } 142 | 143 | export interface ExportEqualsDeclaration extends DeclarationBase { 144 | kind: "export="; 145 | target: string; 146 | } 147 | 148 | export interface ExportDefaultDeclaration extends DeclarationBase { 149 | kind: "exportDefault"; 150 | name: string; 151 | } 152 | 153 | export interface ExportNameDeclaration extends DeclarationBase { 154 | kind: "exportName"; 155 | name: string; 156 | as?: string; 157 | } 158 | 159 | export interface ModuleDeclaration extends DeclarationBase { 160 | kind: "module"; 161 | name: string; 162 | members: ModuleMember[]; 163 | } 164 | 165 | export interface ObjectType { 166 | kind: "object"; 167 | members: ObjectTypeMember[]; 168 | } 169 | 170 | export interface UnionType { 171 | kind: "union"; 172 | members: Type[]; 173 | } 174 | 175 | export interface IntersectionType { 176 | kind: "intersection"; 177 | members: Type[]; 178 | } 179 | 180 | export interface FunctionType { 181 | kind: "function-type"; 182 | parameters: Parameter[]; 183 | returnType: Type; 184 | } 185 | 186 | export interface TypeAliasDeclaration extends DeclarationBase { 187 | kind: "alias"; 188 | name: string; 189 | type: Type; 190 | typeParameters: TypeParameter[]; 191 | } 192 | 193 | export interface ArrayTypeReference { 194 | kind: "array"; 195 | type: Type; 196 | } 197 | 198 | export interface NamedTypeReference { 199 | kind: "name"; 200 | name: string; 201 | } 202 | 203 | export interface TypeofReference { 204 | kind: "typeof"; 205 | type: NamedTypeReference; 206 | } 207 | 208 | export interface StringLiteral { 209 | kind: "string-literal"; 210 | value: string; 211 | } 212 | 213 | export interface NumberLiteral { 214 | kind: "number-literal"; 215 | value: number; 216 | } 217 | 218 | export interface TripleSlashReferencePathDirective { 219 | kind: "triple-slash-reference-path", 220 | path: string; 221 | } 222 | 223 | export interface TripleSlashReferenceTypesDirective { 224 | kind: "triple-slash-reference-types", 225 | types: string; 226 | } 227 | 228 | export interface TripleSlashReferenceNoDefaultLibDirective { 229 | kind: "triple-slash-reference-no-default-lib", 230 | value: boolean; 231 | } 232 | 233 | export interface TripleSlashAmdModuleDirective { 234 | kind: "triple-slash-amd-module", 235 | name?: string; 236 | } 237 | 238 | export type PrimitiveType = "string" | "number" | "boolean" | "any" | "void" | "object" | "null" | "undefined" | "true" | "false" | StringLiteral | NumberLiteral; 239 | 240 | export type ThisType = "this"; 241 | 242 | export type TripleSlashDirective = TripleSlashReferencePathDirective | TripleSlashReferenceTypesDirective | TripleSlashReferenceNoDefaultLibDirective | TripleSlashAmdModuleDirective; 243 | 244 | export type TypeReference = TopLevelDeclaration | NamedTypeReference | ArrayTypeReference | PrimitiveType; 245 | 246 | export type ObjectTypeReference = ClassDeclaration | InterfaceDeclaration; 247 | export type BaseTypeReference = ObjectTypeReference | string; 248 | export type ObjectTypeMember = PropertyDeclaration | MethodDeclaration | IndexSignature | CallSignature; 249 | export type ClassMember = PropertyDeclaration | MethodDeclaration | IndexSignature | ConstructorDeclaration; 250 | 251 | export type Type = TypeReference | UnionType | IntersectionType | PrimitiveType | ObjectType | TypeofReference | FunctionType | TypeParameter | ThisType | BaseTypeReference; 252 | 253 | export type Import = ImportAllDeclaration | ImportDefaultDeclaration | ImportNamedDeclaration | ImportEqualsDeclaration | ImportDeclaration; 254 | 255 | export type NamespaceMember = InterfaceDeclaration | TypeAliasDeclaration | ClassDeclaration | NamespaceDeclaration | ConstDeclaration | VariableDeclaration | FunctionDeclaration; 256 | export type ModuleMember = InterfaceDeclaration | TypeAliasDeclaration | ClassDeclaration | NamespaceDeclaration | ConstDeclaration | VariableDeclaration | FunctionDeclaration | Import | ExportEqualsDeclaration | ExportDefaultDeclaration; 257 | export type TopLevelDeclaration = NamespaceMember | ExportEqualsDeclaration | ExportDefaultDeclaration | ExportNameDeclaration | ModuleDeclaration | EnumDeclaration | Import | Imports; 258 | 259 | export enum DeclarationFlags { 260 | None = 0, 261 | Private = 1 << 0, 262 | Protected = 1 << 1, 263 | Static = 1 << 2, 264 | Optional = 1 << 3, 265 | Export = 1 << 4, 266 | Abstract = 1 << 5, 267 | ExportDefault = 1 << 6, 268 | ReadOnly = 1 << 7, 269 | } 270 | 271 | export enum ParameterFlags { 272 | None = 0, 273 | Optional = 1 << 0, 274 | Rest = 1 << 1 275 | } 276 | 277 | export const config = { 278 | wrapJsDocComments: true, 279 | outputEol: '\r\n', 280 | }; 281 | 282 | export const create = { 283 | interface(name: string, flags = DeclarationFlags.None): InterfaceDeclaration { 284 | return { 285 | name, 286 | baseTypes: [], 287 | kind: "interface", 288 | members: [], 289 | flags 290 | }; 291 | }, 292 | 293 | class(name: string, flags = DeclarationFlags.None): ClassDeclaration { 294 | return { 295 | kind: 'class', 296 | name, 297 | members: [], 298 | implements: [], 299 | typeParameters: [], 300 | flags 301 | }; 302 | }, 303 | 304 | typeParameter(name: string, baseType?: ObjectTypeReference | TypeParameter): TypeParameter { 305 | return { 306 | kind: 'type-parameter', 307 | name, baseType 308 | }; 309 | }, 310 | 311 | enum(name: string, constant: boolean = false, flags = DeclarationFlags.None): EnumDeclaration { 312 | return { 313 | kind: 'enum', 314 | name, constant, 315 | members: [], 316 | flags 317 | }; 318 | }, 319 | 320 | enumValue(name: string, value?: string | number): EnumMemberDeclaration { 321 | return { 322 | kind: 'enum-value', 323 | name, 324 | value 325 | }; 326 | }, 327 | 328 | property(name: string, type: Type, flags = DeclarationFlags.None): PropertyDeclaration { 329 | return { 330 | kind: "property", 331 | name, type, flags 332 | }; 333 | }, 334 | 335 | method(name: string, parameters: Parameter[], returnType: Type, flags = DeclarationFlags.None): MethodDeclaration { 336 | return { 337 | kind: "method", 338 | typeParameters: [], 339 | name, parameters, returnType, flags 340 | }; 341 | }, 342 | 343 | callSignature(parameters: Parameter[], returnType: Type): CallSignature { 344 | return { 345 | kind: "call-signature", 346 | typeParameters: [], 347 | parameters, returnType 348 | }; 349 | }, 350 | 351 | function(name: string, parameters: Parameter[], returnType: Type, flags = DeclarationFlags.None): FunctionDeclaration { 352 | return { 353 | kind: "function", 354 | typeParameters: [], 355 | name, parameters, returnType, flags 356 | }; 357 | }, 358 | 359 | functionType(parameters: Parameter[], returnType: Type): FunctionType { 360 | return { 361 | kind: "function-type", 362 | parameters, returnType 363 | }; 364 | }, 365 | 366 | parameter(name: string, type: Type, flags = ParameterFlags.None): Parameter { 367 | return { 368 | kind: "parameter", 369 | name, type, flags 370 | }; 371 | }, 372 | 373 | constructor(parameters: Parameter[], flags = DeclarationFlags.None): ConstructorDeclaration { 374 | return { 375 | kind: "constructor", 376 | parameters, 377 | flags 378 | }; 379 | }, 380 | 381 | const(name: string, type: Type, flags = DeclarationFlags.None): ConstDeclaration { 382 | return { 383 | kind: "const", name, type, flags 384 | }; 385 | }, 386 | 387 | variable(name: string, type: Type): VariableDeclaration { 388 | return { 389 | kind: "var", name, type 390 | }; 391 | }, 392 | 393 | alias(name: string, type: Type, flags = DeclarationFlags.None): TypeAliasDeclaration { 394 | return { 395 | kind: "alias", name, type, 396 | typeParameters: [], flags 397 | }; 398 | }, 399 | 400 | namespace(name: string): NamespaceDeclaration { 401 | return { 402 | kind: "namespace", name, 403 | members: [] 404 | }; 405 | }, 406 | 407 | objectType(members: ObjectTypeMember[]): ObjectType { 408 | return { 409 | kind: "object", 410 | members 411 | }; 412 | }, 413 | 414 | indexSignature(name: string, indexType: ('string' | 'number'), valueType: Type): IndexSignature { 415 | return { 416 | kind: 'index-signature', 417 | name, indexType, valueType 418 | } 419 | }, 420 | 421 | array(type: Type): ArrayTypeReference { 422 | return { 423 | kind: "array", 424 | type 425 | }; 426 | }, 427 | 428 | namedTypeReference(name: string): NamedTypeReference { 429 | return { 430 | kind: 'name', 431 | name 432 | }; 433 | }, 434 | 435 | exportEquals(target: string): ExportEqualsDeclaration { 436 | return { 437 | kind: 'export=', 438 | target 439 | }; 440 | }, 441 | 442 | exportDefault(name: string): ExportDefaultDeclaration { 443 | return { 444 | kind: 'exportDefault', 445 | name 446 | }; 447 | }, 448 | 449 | exportName(name: string, as?: string): ExportNameDeclaration { 450 | return { 451 | kind: "exportName", 452 | name, 453 | as 454 | }; 455 | }, 456 | 457 | module(name: string): ModuleDeclaration { 458 | return { 459 | kind: 'module', 460 | name, 461 | members: [] 462 | }; 463 | }, 464 | 465 | importAll(name: string, from: string): ImportAllDeclaration { 466 | return { 467 | kind: 'importAll', 468 | name, 469 | from 470 | }; 471 | }, 472 | 473 | importDefault(name: string, from: string): ImportDefaultDeclaration { 474 | return { 475 | kind: 'importDefault', 476 | name, 477 | from 478 | }; 479 | }, 480 | 481 | importNamed(name: string, as: string, from?: string): ImportNamedDeclaration { 482 | return { 483 | kind: 'importNamed', 484 | name, 485 | as: typeof from !== 'undefined' ? as : undefined, 486 | from: typeof from !== 'undefined' ? from : as 487 | }; 488 | }, 489 | 490 | importEquals(name: string, from: string): ImportEqualsDeclaration { 491 | return { 492 | kind: 'import=', 493 | name, 494 | from 495 | }; 496 | }, 497 | 498 | import(from: string): ImportDeclaration { 499 | return { 500 | kind: 'import', 501 | from 502 | }; 503 | }, 504 | 505 | imports(imports: Import[]): Imports { 506 | return { 507 | kind: 'imports', 508 | members: imports 509 | }; 510 | }, 511 | 512 | union(members: Type[]): UnionType { 513 | return { 514 | kind: 'union', 515 | members 516 | }; 517 | }, 518 | 519 | intersection(members: Type[]): IntersectionType { 520 | return { 521 | kind: 'intersection', 522 | members 523 | }; 524 | }, 525 | 526 | typeof(type: NamedTypeReference): TypeofReference { 527 | return { 528 | kind: 'typeof', 529 | type 530 | }; 531 | }, 532 | 533 | tripleSlashReferencePathDirective(path: string): TripleSlashReferencePathDirective { 534 | return { 535 | kind: "triple-slash-reference-path", 536 | path 537 | }; 538 | }, 539 | 540 | tripleSlashReferenceTypesDirective(types: string): TripleSlashReferenceTypesDirective { 541 | return { 542 | kind: "triple-slash-reference-types", 543 | types 544 | }; 545 | }, 546 | 547 | tripleSlashReferenceNoDefaultLibDirective(value: boolean = true): TripleSlashReferenceNoDefaultLibDirective { 548 | return { 549 | kind: "triple-slash-reference-no-default-lib", 550 | value 551 | }; 552 | }, 553 | 554 | tripleSlashAmdModuleDirective(name?: string): TripleSlashAmdModuleDirective { 555 | return { 556 | kind: "triple-slash-amd-module", 557 | name 558 | }; 559 | } 560 | }; 561 | 562 | export const type = { 563 | array(type: Type): ArrayTypeReference { 564 | return { 565 | kind: "array", 566 | type 567 | } 568 | }, 569 | stringLiteral(string: string): PrimitiveType { 570 | return { 571 | kind: "string-literal", 572 | value: string 573 | } 574 | }, 575 | numberLiteral(number: number): PrimitiveType { 576 | return { 577 | kind: "number-literal", 578 | value: number 579 | } 580 | }, 581 | string: "string", 582 | number: "number", 583 | boolean: "boolean", 584 | any: "any", 585 | void: "void", 586 | object: "object", 587 | null: "null", 588 | undefined: "undefined", 589 | true: "true", 590 | false: "false", 591 | this: "this" 592 | }; 593 | 594 | export const reservedWords = ['abstract', 'await', 'boolean', 'break', 'byte', 'case', 595 | 'catch', 'char', 'class', 'const', 'continue', 'debugger', 'default', 596 | 'delete', 'do', 'double', 'else', 'enum', 'export', 'extends', 'false', 597 | 'final', 'finally', 'float', 'for', 'function', 'goto', 'if', 'implements', 598 | 'import', 'in', 'instanceof', 'int', 'interface', 'let', 'long', 'native', 599 | 'new', 'null', 'package', 'private', 'protected', 'public', 'return', 'short', 600 | 'static', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 601 | 'transient', 'true', 'try', 'typeof', 'var', 'void', 'volatile', 'while', 'with', 'yield']; 602 | 603 | /** IdentifierName can be written as unquoted property names, but may be reserved words. */ 604 | export function isIdentifierName(s: string) { 605 | return /^[$A-Z_][0-9A-Z_$]*$/i.test(s); 606 | } 607 | 608 | /** Identifiers are e.g. legal variable names. They may not be reserved words */ 609 | export function isIdentifier(s: string) { 610 | return isIdentifierName(s) && reservedWords.indexOf(s) < 0; 611 | } 612 | 613 | function quoteIfNeeded(s: string) { 614 | if (isIdentifierName(s)) { 615 | return s; 616 | } else { 617 | // JSON.stringify handles escaping quotes for us. Handy! 618 | return JSON.stringify(s); 619 | } 620 | } 621 | 622 | export enum ContextFlags { 623 | None = 0, 624 | Module = 1 << 0, 625 | InAmbientNamespace = 1 << 1 626 | } 627 | 628 | export interface EmitOptions { 629 | rootFlags?: ContextFlags; 630 | tripleSlashDirectives?: TripleSlashDirective[]; 631 | } 632 | 633 | export function emit(rootDecl: TopLevelDeclaration, { rootFlags = ContextFlags.None, tripleSlashDirectives = [] }: EmitOptions = {}): string { 634 | let output = ""; 635 | let indentLevel = 0; 636 | 637 | const isModuleWithModuleFlag = rootDecl.kind === 'module' && rootFlags === ContextFlags.Module; 638 | // For a module root declaration we must omit the module flag. 639 | const contextStack: ContextFlags[] = isModuleWithModuleFlag ? [] : [rootFlags]; 640 | 641 | tripleSlashDirectives.forEach(writeTripleSlashDirective); 642 | 643 | writeDeclaration(rootDecl); 644 | newline(); 645 | return output; 646 | 647 | function getContextFlags() { 648 | return contextStack.reduce((a, b) => a | b, ContextFlags.None); 649 | } 650 | 651 | function tab() { 652 | for (let i = 0; i < indentLevel; i++) { 653 | output = output + ' '; 654 | } 655 | } 656 | 657 | function print(s: string) { 658 | output = output + s; 659 | } 660 | 661 | function start(s: string) { 662 | tab(); 663 | print(s); 664 | } 665 | 666 | function classFlagsToString(flags: DeclarationFlags | undefined = DeclarationFlags.None): string { 667 | let out = ''; 668 | 669 | if (flags && flags & DeclarationFlags.Abstract) { 670 | out += 'abstract '; 671 | } 672 | 673 | return out; 674 | } 675 | 676 | function memberFlagsToString(flags: DeclarationFlags | undefined = DeclarationFlags.None): string { 677 | let out = ''; 678 | 679 | if (flags & DeclarationFlags.Private) { 680 | out += 'private '; 681 | } 682 | else if (flags & DeclarationFlags.Protected) { 683 | out += 'protected '; 684 | } 685 | 686 | if (flags & DeclarationFlags.Static) { 687 | out += 'static '; 688 | } 689 | 690 | if (flags & DeclarationFlags.Abstract) { 691 | out += 'abstract '; 692 | } 693 | 694 | if (flags & DeclarationFlags.ReadOnly) { 695 | out += 'readonly '; 696 | } 697 | 698 | return out; 699 | } 700 | 701 | function startWithDeclareOrExport(s: string, flags: DeclarationFlags | undefined = DeclarationFlags.None) { 702 | if (getContextFlags() & ContextFlags.InAmbientNamespace) { 703 | // Already in an all-export context 704 | start(s); 705 | } else if (flags & DeclarationFlags.Export) { 706 | start(`export ${s}`); 707 | } else if (flags & DeclarationFlags.ExportDefault) { 708 | start(`export default ${s}`); 709 | } else if (getContextFlags() & ContextFlags.Module) { 710 | start(s); 711 | } else { 712 | start(`declare ${s}`); 713 | } 714 | } 715 | 716 | function newline() { 717 | output = output + config.outputEol; 718 | } 719 | 720 | function needsParens(d: Type) { 721 | if (typeof d === 'string') { 722 | return false; 723 | } 724 | switch (d.kind) { 725 | case "array": 726 | case "alias": 727 | case "interface": 728 | case "class": 729 | case "union": 730 | return true; 731 | default: 732 | return false; 733 | } 734 | } 735 | 736 | function printDeclarationComments(decl: DeclarationBase) { 737 | if (decl.comment) { 738 | start(`// ${decl.comment}`); 739 | newline(); 740 | } 741 | if (decl.jsDocComment) { 742 | if (config.wrapJsDocComments) { 743 | start('/**'); 744 | newline(); 745 | for (const line of decl.jsDocComment.split(/\r?\n/g)) { 746 | start(` * ${line}`); 747 | newline(); 748 | } 749 | start(' */'); 750 | } 751 | else { 752 | start(decl.jsDocComment); 753 | } 754 | 755 | newline(); 756 | } 757 | } 758 | 759 | function hasFlag(haystack: T | undefined, needle: T): boolean; 760 | function hasFlag(haystack: number | undefined, needle: number) { 761 | if (haystack === undefined) { 762 | return false; 763 | } 764 | return !!(needle & haystack); 765 | } 766 | 767 | function printObjectTypeMembers(members: ObjectTypeMember[]) { 768 | print('{'); 769 | newline(); 770 | indentLevel++; 771 | for (const member of members) { 772 | printMember(member); 773 | } 774 | indentLevel--; 775 | tab(); 776 | print('}'); 777 | 778 | function printMember(member: ObjectTypeMember) { 779 | switch (member.kind) { 780 | case 'index-signature': 781 | printDeclarationComments(member); 782 | tab(); 783 | print(`[${member.name}: `); 784 | writeReference(member.indexType); 785 | print(']: '); 786 | writeReference(member.valueType); 787 | print(';'); 788 | newline(); 789 | return; 790 | case "call-signature": { 791 | printDeclarationComments(member); 792 | tab(); 793 | writeTypeParameters(member.typeParameters); 794 | print("("); 795 | let first = true; 796 | for (const param of member.parameters) { 797 | if (!first) print(", "); 798 | first = false; 799 | print(param.name); 800 | print(": "); 801 | writeReference(param.type); 802 | } 803 | print("): "); 804 | writeReference(member.returnType); 805 | print(";"); 806 | newline(); 807 | return; 808 | } 809 | case 'method': 810 | printDeclarationComments(member); 811 | tab(); 812 | print(quoteIfNeeded(member.name)); 813 | if (hasFlag(member.flags, DeclarationFlags.Optional)) print('?'); 814 | writeTypeParameters(member.typeParameters); 815 | print('('); 816 | let first = true; 817 | for (const param of member.parameters) { 818 | if (!first) print(', '); 819 | first = false; 820 | writeParameter(param); 821 | } 822 | print('): '); 823 | writeReference(member.returnType); 824 | print(';'); 825 | newline(); 826 | return; 827 | case 'property': 828 | printDeclarationComments(member); 829 | tab(); 830 | if (hasFlag(member.flags, DeclarationFlags.ReadOnly)) print('readonly '); 831 | print(quoteIfNeeded(member.name)); 832 | if (hasFlag(member.flags, DeclarationFlags.Optional)) print('?'); 833 | print(': '); 834 | writeReference(member.type); 835 | print(';'); 836 | newline(); 837 | return; 838 | } 839 | throw new Error(`Unknown member kind ${(member as ObjectTypeMember).kind}`); 840 | } 841 | } 842 | 843 | function writeUnionReference(d: Type) { 844 | if (typeof d !== "string" && d.kind === "function-type") { 845 | print('(') 846 | writeReference(d) 847 | print(')') 848 | } else { 849 | writeReference(d) 850 | } 851 | } 852 | 853 | function writeReference(d: Type) { 854 | if (typeof d === 'string') { 855 | print(d); 856 | } else { 857 | const e = d; 858 | switch (e.kind) { 859 | case "type-parameter": 860 | case "class": 861 | case "interface": 862 | case "name": 863 | case "alias": 864 | print(e.name); 865 | break; 866 | 867 | case "array": 868 | if (needsParens(e.type)) print('('); 869 | writeReference(e.type); 870 | if (needsParens(e.type)) print(')'); 871 | print('[]'); 872 | break; 873 | 874 | case "object": 875 | printObjectTypeMembers(e.members); 876 | break; 877 | 878 | case "string-literal": 879 | print(JSON.stringify(e.value)); 880 | break; 881 | 882 | case "number-literal": 883 | if (isNaN(e.value)) print("typeof NaN"); 884 | else if (!isFinite(e.value)) print("typeof Infinity"); 885 | else print(e.value.toString()); 886 | break; 887 | 888 | case "function-type": 889 | writeFunctionType(e); 890 | break; 891 | 892 | case "union": 893 | writeDelimited(e.members, ' | ', writeUnionReference); 894 | break; 895 | 896 | case "intersection": 897 | writeDelimited(e.members, ' & ', writeUnionReference) 898 | break; 899 | 900 | case "typeof": 901 | print("typeof "); 902 | writeReference(e.type); 903 | break; 904 | 905 | default: 906 | throw new Error(`Unknown kind ${d.kind}`); 907 | } 908 | 909 | } 910 | } 911 | 912 | function writeTypeParameters(params: TypeParameter[]) { 913 | if (params.length === 0) return; 914 | 915 | print('<'); 916 | 917 | let first = true; 918 | 919 | for (const p of params) { 920 | if (!first) print(', '); 921 | 922 | print(p.name); 923 | 924 | if (p.baseType) { 925 | print(' extends '); 926 | 927 | if (p.baseType.kind === 'type-parameter') 928 | print(p.baseType.name); 929 | else 930 | writeReference(p.baseType); 931 | } 932 | 933 | first = false; 934 | } 935 | 936 | print('>'); 937 | } 938 | 939 | function writeInterface(d: InterfaceDeclaration) { 940 | printDeclarationComments(d); 941 | startWithDeclareOrExport(`interface ${d.name} `, d.flags); 942 | if (d.baseTypes && d.baseTypes.length) { 943 | print(`extends `); 944 | let first = true; 945 | for (const baseType of d.baseTypes) { 946 | if (!first) print(', '); 947 | writeReference(baseType); 948 | first = false; 949 | } 950 | } 951 | printObjectTypeMembers(d.members); 952 | newline(); 953 | } 954 | 955 | function writeFunctionType(f: FunctionType) { 956 | print('('); 957 | writeDelimited(f.parameters, ', ', writeParameter); 958 | print(')'); 959 | print('=>'); 960 | writeReference(f.returnType); 961 | } 962 | 963 | function writeFunction(f: FunctionDeclaration) { 964 | printDeclarationComments(f); 965 | if (!isIdentifier(f.name)) { 966 | start(`/* Illegal function name '${f.name}' can't be used here`); 967 | newline(); 968 | } 969 | 970 | startWithDeclareOrExport(`function ${f.name}`, f.flags); 971 | writeTypeParameters(f.typeParameters); 972 | print('(') 973 | writeDelimited(f.parameters, ', ', writeParameter); 974 | print('): '); 975 | writeReference(f.returnType); 976 | print(';'); 977 | newline(); 978 | 979 | if (!isIdentifier(f.name)) { 980 | start(`*/`); 981 | newline(); 982 | } 983 | } 984 | 985 | function writeParameter(p: Parameter) { 986 | const flags = p.flags || DeclarationFlags.None; 987 | print(`${flags & ParameterFlags.Rest ? '...' : ''}${p.name}${flags & ParameterFlags.Optional ? '?' : ''}: `); 988 | writeReference(p.type); 989 | } 990 | 991 | function writeDelimited(arr: T[], sep: string, printer: (x: T) => void) { 992 | let first = true; 993 | for (const el of arr) { 994 | if (!first) { 995 | print(sep); 996 | } 997 | printer(el); 998 | first = false; 999 | } 1000 | } 1001 | 1002 | function writeClass(c: ClassDeclaration) { 1003 | printDeclarationComments(c); 1004 | startWithDeclareOrExport(`${classFlagsToString(c.flags)}class ${c.name}`, c.flags); 1005 | writeTypeParameters(c.typeParameters); 1006 | if (c.baseType) { 1007 | print(' extends '); 1008 | writeReference(c.baseType); 1009 | } 1010 | if (c.implements && c.implements.length) { 1011 | print(' implements '); 1012 | let first = true; 1013 | for (const impl of c.implements) { 1014 | if (!first) print(', '); 1015 | writeReference(impl); 1016 | first = false; 1017 | } 1018 | } 1019 | print(' {'); 1020 | newline(); 1021 | indentLevel++; 1022 | for (const m of c.members) { 1023 | writeClassMember(m); 1024 | newline(); 1025 | } 1026 | indentLevel--; 1027 | start('}'); 1028 | newline(); 1029 | } 1030 | 1031 | function writeClassMember(c: ClassMember) { 1032 | switch (c.kind) { 1033 | case "property": 1034 | return writePropertyDeclaration(c); 1035 | case "method": 1036 | return writeMethodDeclaration(c); 1037 | case "constructor": 1038 | return writeConstructorDeclaration(c); 1039 | } 1040 | } 1041 | 1042 | function writeConstructorDeclaration(ctor: ConstructorDeclaration) { 1043 | printDeclarationComments(ctor); 1044 | start('constructor('); 1045 | writeDelimited(ctor.parameters, ', ', writeParameter); 1046 | print(');') 1047 | newline(); 1048 | } 1049 | 1050 | function writePropertyDeclaration(p: PropertyDeclaration) { 1051 | printDeclarationComments(p); 1052 | start(`${memberFlagsToString(p.flags)}${quoteIfNeeded(p.name)}: `); 1053 | writeReference(p.type); 1054 | print(';'); 1055 | newline(); 1056 | } 1057 | 1058 | function writeMethodDeclaration(m: MethodDeclaration) { 1059 | printDeclarationComments(m); 1060 | start(`${memberFlagsToString(m.flags)}${quoteIfNeeded(m.name)}`); 1061 | writeTypeParameters(m.typeParameters); 1062 | print('('); 1063 | writeDelimited(m.parameters, ', ', writeParameter); 1064 | print('): '); 1065 | writeReference(m.returnType); 1066 | print(';'); 1067 | } 1068 | 1069 | function writeNamespace(ns: NamespaceDeclaration) { 1070 | printDeclarationComments(ns); 1071 | startWithDeclareOrExport(`namespace ${ns.name} {`, ns.flags); 1072 | contextStack.push(ContextFlags.InAmbientNamespace); 1073 | newline(); 1074 | indentLevel++; 1075 | for (const member of ns.members) { 1076 | writeDeclaration(member); 1077 | newline(); 1078 | } 1079 | indentLevel--; 1080 | start(`}`); 1081 | contextStack.pop(); 1082 | newline(); 1083 | } 1084 | 1085 | function writeConst(c: ConstDeclaration) { 1086 | printDeclarationComments(c); 1087 | startWithDeclareOrExport(`const ${c.name}: `, c.flags); 1088 | writeReference(c.type); 1089 | print(';'); 1090 | newline(); 1091 | } 1092 | 1093 | function writeVar(c: VariableDeclaration) { 1094 | printDeclarationComments(c); 1095 | startWithDeclareOrExport(`var ${c.name}: `, c.flags); 1096 | writeReference(c.type); 1097 | print(';'); 1098 | newline(); 1099 | } 1100 | 1101 | function writeAlias(a: TypeAliasDeclaration) { 1102 | printDeclarationComments(a); 1103 | startWithDeclareOrExport(`type ${a.name}`, a.flags); 1104 | writeTypeParameters(a.typeParameters); 1105 | print(' = '); 1106 | writeReference(a.type); 1107 | print(';'); 1108 | newline(); 1109 | } 1110 | 1111 | function writeExportEquals(e: ExportEqualsDeclaration) { 1112 | start(`export = ${e.target};`); 1113 | newline(); 1114 | } 1115 | 1116 | function writeExportDefault(e: ExportDefaultDeclaration) { 1117 | start(`export default ${e.name};`); 1118 | newline(); 1119 | } 1120 | 1121 | function writeExportName(e: ExportNameDeclaration) { 1122 | start(`export { ${e.name}`); 1123 | if (e.as) { 1124 | print(` as ${e.as}`); 1125 | } 1126 | print(' };'); 1127 | newline(); 1128 | } 1129 | 1130 | function writeModule(m: ModuleDeclaration) { 1131 | printDeclarationComments(m); 1132 | startWithDeclareOrExport(`module '${m.name}' {`, m.flags); 1133 | contextStack.push(ContextFlags.Module); 1134 | newline(); 1135 | indentLevel++; 1136 | for (const member of m.members) { 1137 | writeDeclaration(member); 1138 | newline(); 1139 | } 1140 | indentLevel--; 1141 | start(`}`); 1142 | contextStack.pop(); 1143 | newline(); 1144 | } 1145 | 1146 | function groupBy(xs: any[], key: string): any { 1147 | return xs.reduce(function (rv, x) { 1148 | (rv[x[key]] = rv[x[key]] || []).push(x); 1149 | return rv; 1150 | }, {}); 1151 | }; 1152 | 1153 | function writeImports(i: Imports) { 1154 | const groups = groupBy(i.members, 'from'); 1155 | 1156 | Object.keys(groups).forEach(key => { 1157 | const groupedImports = groups[key] as Import[]; 1158 | const namedImports: ImportNamedDeclaration[] = []; 1159 | let defaultImport: ImportDefaultDeclaration | undefined; 1160 | let importAll: ImportAllDeclaration | undefined; 1161 | 1162 | groupedImports.forEach(i => { 1163 | if (i.kind === 'importNamed') { 1164 | const tmp = i as ImportNamedDeclaration; 1165 | const named = namedImports.find(x => x.name === tmp.name); 1166 | if (!named) { 1167 | namedImports.push(tmp); 1168 | } 1169 | } else if (i.kind === 'importDefault' && !defaultImport) { 1170 | defaultImport = i as ImportDefaultDeclaration; 1171 | } else if (i.kind === 'importAll') { 1172 | importAll = i as ImportAllDeclaration; 1173 | } 1174 | }); 1175 | 1176 | if (importAll) { 1177 | writeImportAll(importAll); 1178 | } else if (defaultImport && namedImports.length > 0) { 1179 | writeNamedImportsWithDefault(namedImports, defaultImport); 1180 | } else if (defaultImport) { 1181 | writeImportDefault(defaultImport); 1182 | } else { 1183 | writeNamedImports(namedImports); 1184 | } 1185 | }); 1186 | 1187 | } 1188 | 1189 | function writeImportAll(i: ImportAllDeclaration) { 1190 | start(`import * as ${i.name} from '${i.from}';`); 1191 | newline(); 1192 | } 1193 | 1194 | function writeImportDefault(i: ImportDefaultDeclaration) { 1195 | start(`import ${i.name} from '${i.from}';`); 1196 | newline(); 1197 | } 1198 | 1199 | function writeImportNamed(i: ImportNamedDeclaration) { 1200 | start(`import { ${i.name}`); 1201 | if (i.as) { 1202 | print(` as ${i.as}`); 1203 | } 1204 | print(` } from '${i.from}';`); 1205 | newline(); 1206 | } 1207 | 1208 | 1209 | function writeNamedImports(i: ImportNamedDeclaration[]) { 1210 | let namedImports: string = ''; 1211 | i.forEach(x => namedImports += `${x.name}, `); 1212 | namedImports = namedImports.slice(0, -2); 1213 | start(`import { ${namedImports}`); 1214 | print(` } from '${i[0].from}';`); 1215 | newline(); 1216 | } 1217 | 1218 | function writeNamedImportsWithDefault(i: ImportNamedDeclaration[], d: ImportDefaultDeclaration) { 1219 | let namedImports: string = ''; 1220 | i.forEach(x => namedImports += `${x.name}, `); 1221 | namedImports = namedImports.slice(0, -2); 1222 | start(`import ${d.name}, { ${namedImports}`); 1223 | print(` } from '${d.from}';`); 1224 | newline(); 1225 | } 1226 | 1227 | 1228 | function writeImportEquals(i: ImportEqualsDeclaration) { 1229 | start(`import ${i.name} = require('${i.from}');`); 1230 | newline(); 1231 | } 1232 | 1233 | function writeImport(i: ImportDeclaration) { 1234 | start(`import '${i.from}';`); 1235 | newline(); 1236 | } 1237 | 1238 | function writeEnum(e: EnumDeclaration) { 1239 | printDeclarationComments(e); 1240 | startWithDeclareOrExport(`${e.constant ? 'const ' : ''}enum ${e.name} {`, e.flags); 1241 | newline(); 1242 | indentLevel++; 1243 | for (const member of e.members) { 1244 | writeEnumValue(member); 1245 | } 1246 | indentLevel--; 1247 | start(`}`); 1248 | newline(); 1249 | } 1250 | 1251 | function writeEnumValue(e: EnumMemberDeclaration) { 1252 | printDeclarationComments(e); 1253 | start(e.name); 1254 | 1255 | if (e.value !== undefined) { 1256 | if (typeof e.value === 'string') { 1257 | print(` = "${e.value}"`); 1258 | } else { 1259 | print(` = ${e.value}`); 1260 | } 1261 | } 1262 | 1263 | print(','); 1264 | newline(); 1265 | } 1266 | 1267 | function writeTripleSlashDirective(t: TripleSlashDirective) { 1268 | const type = t.kind === "triple-slash-amd-module" ? "amd-module" : "reference"; 1269 | start(`/// <${type}`); 1270 | 1271 | switch (t.kind) { 1272 | case "triple-slash-reference-path": 1273 | print(` path="${t.path}"`); 1274 | break; 1275 | case "triple-slash-reference-types": 1276 | print(` types="${t.types}"`); 1277 | break; 1278 | case "triple-slash-reference-no-default-lib": 1279 | print(` no-default-lib="${t.value}"`); 1280 | break; 1281 | case "triple-slash-amd-module": 1282 | if (t.name) { 1283 | print(` name="${t.name}"`); 1284 | } 1285 | break; 1286 | default: 1287 | throw new Error(`Unknown triple slash directive kind ${(t as TripleSlashDirective).kind}`); 1288 | } 1289 | 1290 | print(" />"); 1291 | newline(); 1292 | } 1293 | 1294 | function writeDeclaration(d: TopLevelDeclaration) { 1295 | if (typeof d === 'string') { 1296 | return print(d); 1297 | } else { 1298 | switch (d.kind) { 1299 | case "interface": 1300 | return writeInterface(d); 1301 | case "function": 1302 | return writeFunction(d); 1303 | case "class": 1304 | return writeClass(d); 1305 | case "namespace": 1306 | return writeNamespace(d); 1307 | case "const": 1308 | return writeConst(d); 1309 | case "var": 1310 | return writeVar(d); 1311 | case "alias": 1312 | return writeAlias(d); 1313 | case "export=": 1314 | return writeExportEquals(d); 1315 | case "exportDefault": 1316 | return writeExportDefault(d); 1317 | case "exportName": 1318 | return writeExportName(d); 1319 | case "module": 1320 | return writeModule(d); 1321 | case "imports": 1322 | return writeImports(d); 1323 | case "importAll": 1324 | return writeImportAll(d); 1325 | case "importDefault": 1326 | return writeImportDefault(d); 1327 | case "importNamed": 1328 | return writeImportNamed(d); 1329 | case "import=": 1330 | return writeImportEquals(d); 1331 | case "import": 1332 | return writeImport(d); 1333 | case "enum": 1334 | return writeEnum(d); 1335 | default: 1336 | throw new Error(`Unknown declaration kind ${(d as TopLevelDeclaration).kind}`); 1337 | } 1338 | } 1339 | } 1340 | } 1341 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as prettier from 'prettier'; 3 | import { parse as DocParser } from 'react-docgen'; 4 | import * as dom from './dts-dom'; 5 | import * as Utils from './utils'; 6 | 7 | export interface ImportType { 8 | named?: string; 9 | default?: string; 10 | from: string; 11 | } 12 | 13 | export interface Extends { 14 | includePropsAsGeneric?: boolean; 15 | import: ImportType; 16 | } 17 | 18 | export interface Options { 19 | input: string; 20 | output: string; 21 | isBaseClass?: boolean; 22 | propTypesComposition?: ImportType[]; 23 | extends?: Extends; 24 | imports?: ImportType[]; 25 | } 26 | 27 | export function generate(options: Options): string { 28 | let result: string = ''; 29 | let baseType: string = 'React.Component'; 30 | 31 | const { input, output, isBaseClass, propTypesComposition, imports } = options; 32 | 33 | const content: string = fs.readFileSync(input, 'utf8'); 34 | const componentInfo = DocParser(content); 35 | const className = isBaseClass ? Utils.writeGeneric(componentInfo.displayName, 'T = any') : componentInfo.displayName; 36 | 37 | const importDefinitions: dom.Import[] = []; 38 | const interfaceDefinitions: dom.InterfaceDeclaration[] = []; 39 | 40 | if (componentInfo) { 41 | const propsIntefaceName = `${componentInfo.displayName}Props`; 42 | const propsDefinition = dom.create.interface(propsIntefaceName, dom.DeclarationFlags.Export); 43 | const importDefinition = dom.create.importAll('React', 'react'); 44 | const classDefinition = dom.create.class(className, dom.DeclarationFlags.ExportDefault); 45 | 46 | importDefinitions.push(importDefinition); 47 | 48 | if (imports && imports.length > 0) { 49 | imports.forEach(x => { 50 | importDefinitions.push(Utils.createImport(x.from, x.default, x.named)); 51 | }); 52 | } 53 | 54 | if (componentInfo.props) { 55 | const props = componentInfo.props; 56 | const keys = Object.keys(props); 57 | 58 | if (keys.length > 0) { 59 | keys.forEach(key => { 60 | const prop = { ...props[key], name: key }; 61 | if (!prop.type) { 62 | return; 63 | } 64 | 65 | const propResult = Utils.generateProp(prop); 66 | if (propResult) { 67 | const { property, interfaces } = propResult; 68 | propsDefinition.members.push(property); 69 | if (interfaces && interfaces.length > 0) { 70 | interfaceDefinitions.push(...interfaces); 71 | } 72 | } 73 | }); 74 | } 75 | 76 | baseType = Utils.writeGeneric('React.Component', isBaseClass ? 'T' : propsIntefaceName); 77 | interfaceDefinitions.push(propsDefinition); 78 | } 79 | 80 | if (propTypesComposition && propTypesComposition.length > 0) { 81 | propsDefinition.baseTypes = []; 82 | propTypesComposition.forEach(x => { 83 | importDefinitions.push(Utils.createImport(x.from, x.default, x.named)); 84 | propsDefinition.baseTypes.push(x.default as string || x.named as string); 85 | }); 86 | } 87 | 88 | if (options.extends) { 89 | if (options.extends.import) { 90 | const { from, named } = options.extends.import; 91 | importDefinitions.push(Utils.createImport(from, options.extends.import.default, named)); 92 | const baseTypeName = named as string || options.extends.import.default as string; 93 | const genericName = isBaseClass ? 'T' : propsIntefaceName; 94 | baseType = Utils.writeGeneric(baseTypeName, genericName); 95 | } 96 | } 97 | 98 | if (componentInfo.methods) { 99 | componentInfo.methods.forEach(method => { 100 | const { params, returns } = method; 101 | const parameters: dom.Parameter[] = []; 102 | if (params && params.length > 0) { 103 | params.forEach(param => { 104 | const type = param.type ? param.type.name : 'any'; 105 | parameters.push(dom.create.parameter(param.name, Utils.getType(type))); 106 | }); 107 | } 108 | const returnType = returns ? returns.type.name : 'any'; 109 | classDefinition.members.push(dom.create.method(method.name, parameters, Utils.getType(returnType))); 110 | }); 111 | } 112 | 113 | result += dom.emit(dom.create.imports(importDefinitions)); 114 | interfaceDefinitions.forEach(x => result += dom.emit(x)); 115 | classDefinition.baseType = baseType; 116 | result += dom.emit(classDefinition); 117 | 118 | if (result) { 119 | const fileName = output || input.split('.')[0] + '.d.ts'; 120 | result = prettier.format(result, { parser: 'typescript' }); 121 | fs.writeFileSync(fileName, result, { flag: 'w', encoding: 'utf8' }); 122 | return result; 123 | } 124 | } 125 | 126 | return ''; 127 | } 128 | -------------------------------------------------------------------------------- /src/typings/comment-parser.d.ts: -------------------------------------------------------------------------------- 1 | declare module "comment-parser" { 2 | export interface Comment { 3 | tags: Tag[]; 4 | line: number; 5 | description: string; 6 | source: string; 7 | } 8 | export interface Tag { 9 | tag: string; 10 | name: string; 11 | optional: boolean; 12 | type: string; 13 | description: string; 14 | line: number; 15 | source: string; 16 | } 17 | export interface Options { 18 | parsers?: [(str: string, data: any) => { source: string, data: any }]; 19 | dotted_names?: boolean; 20 | } 21 | 22 | export default function parse(str: string, opts?: Options): [Comment]; 23 | } -------------------------------------------------------------------------------- /src/typings/react-docgen.d.ts: -------------------------------------------------------------------------------- 1 | declare module "react-docgen" { 2 | export type ValueArray = Value[]; 3 | export type ValueType = Props | Prop | ValueArray; 4 | 5 | export interface ComponentInfo { 6 | displayName: string; 7 | props: Props; 8 | methods: Method[]; 9 | } 10 | 11 | export interface Props { 12 | [key: string]: Prop 13 | } 14 | 15 | export interface Prop { 16 | name: string; 17 | required: boolean; 18 | type: Type; 19 | description: string; 20 | value: ValueType; 21 | } 22 | 23 | export interface Value { 24 | value: number | string | Props; 25 | computed: boolean; 26 | name: string; 27 | } 28 | 29 | export interface Type { 30 | name: string; 31 | value: ValueType; 32 | } 33 | 34 | export interface Method { 35 | params: Param[]; 36 | returns: Returns; 37 | name: string; 38 | } 39 | 40 | export interface Param { 41 | type: Type; 42 | name: string; 43 | } 44 | 45 | export interface Returns { 46 | type: Type; 47 | } 48 | 49 | export function parse(content: string): ComponentInfo; 50 | } 51 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import * as dom from './dts-dom'; 2 | import CommentParser from 'comment-parser'; 3 | import { Prop, Props, ValueArray } from 'react-docgen'; 4 | 5 | export type PropResult = PropDeclaration | undefined; 6 | export interface PropDeclaration { 7 | property: dom.ObjectTypeMember; 8 | interfaces?: dom.InterfaceDeclaration[]; 9 | } 10 | 11 | export function getType(type: string): dom.Type { 12 | switch (type.toLowerCase()) { 13 | case 'any': return dom.type.any; 14 | case 'array': return dom.type.array(dom.type.any); 15 | case 'bool': return dom.type.boolean; 16 | case 'number': return dom.type.number; 17 | case 'object': return dom.type.object; 18 | case 'string': return dom.type.string; 19 | case 'this': return dom.type.this; 20 | case 'element': return 'React.ReactElement'; 21 | case 'node': return 'React.ReactNode'; 22 | case 'shape': return type; 23 | default: return dom.type.any; 24 | } 25 | } 26 | 27 | export function generateProp(prop: Prop): PropResult { 28 | function generate(name: string, type: dom.Type, flag: dom.DeclarationFlags): PropResult { 29 | const property = dom.create.property(name, type, flag); 30 | return { property }; 31 | } 32 | 33 | function generateFunc(name: string, description: string, flag: dom.DeclarationFlags): PropResult { 34 | const result = CommentParser(makeComment(description)); 35 | if (result && result.length > 0) { 36 | const signature = result[0]; 37 | const parameters: dom.Parameter[] = []; 38 | let returnType: dom.Type = dom.type.void; 39 | 40 | signature.tags.forEach(item => { 41 | if (item.tag === 'param') { 42 | const type = item.type ? item.type : 'any'; 43 | parameters.push(dom.create.parameter(item.name, getType(type))); 44 | } else if (item.tag === 'return') { 45 | returnType = getType(item.type); 46 | } 47 | }); 48 | const property = dom.create.method(name, parameters, returnType, flag); 49 | return { property }; 50 | } 51 | } 52 | 53 | function generateShape(name: string, props: Props, flag: dom.DeclarationFlags): PropDeclaration { 54 | const interfaces: dom.InterfaceDeclaration[] = []; 55 | const shapeDefinition = generateShapeInterface(name, props, interfaces); 56 | const property = dom.create.property(name, shapeDefinition.name, flag); 57 | return { property, interfaces }; 58 | } 59 | 60 | function generateArrayOf(name: string, prop: Prop, flag: dom.DeclarationFlags): PropDeclaration { 61 | const property = dom.create.property(name, dom.type.array(getType(prop.name)), flag); 62 | return { property }; 63 | } 64 | 65 | function generateOneOf(name: string, values: ValueArray, flag: dom.DeclarationFlags): PropDeclaration { 66 | let unions = ''; 67 | values.forEach(item => unions += `${item.value as string} | `); 68 | unions = unions.substr(0, unions.length - 3); 69 | const property = dom.create.property(name, unions, flag); 70 | return { property }; 71 | } 72 | 73 | function generateOneOfType(name: string, values: ValueArray, flag: dom.DeclarationFlags): PropDeclaration { 74 | let isAnyType: boolean = false; 75 | const unionTypes: dom.Type[] = []; 76 | const interfaces: dom.InterfaceDeclaration[] = []; 77 | 78 | values.forEach(item => { 79 | const t = getType(item.name); 80 | if (t === dom.type.any) { 81 | isAnyType = true; 82 | } else if (t === 'shape') { 83 | const shapeDefinition = generateShapeInterface(name, item.value as Props, interfaces); 84 | unionTypes.push(shapeDefinition.name); 85 | } else { 86 | unionTypes.push(getType(item.name)); 87 | } 88 | }); 89 | const union = dom.create.union(unionTypes); 90 | const property = dom.create.property(name, isAnyType ? dom.type.any : union, flag); 91 | return { property, interfaces }; 92 | } 93 | 94 | function generateShapeInterface(name: string, props: Props, shapes: dom.InterfaceDeclaration[]): dom.InterfaceDeclaration { 95 | const interfaceName = getDeclarationName(name); 96 | const shapeDefinition = dom.create.interface(interfaceName); 97 | 98 | Object.keys(props).forEach(key => { 99 | const { required, name } = props[key]; 100 | const flag = required ? dom.DeclarationFlags.None : dom.DeclarationFlags.Optional; 101 | const type = getType(name); 102 | 103 | if (type === 'shape') { 104 | const childShape = generateShapeInterface(key, props[key].value as Props, shapes); 105 | shapeDefinition.members.push(dom.create.property(key, childShape.name, flag)); 106 | } else if (name === 'union') { 107 | const union = generateOneOfType(key, props[key].value as ValueArray, flag); 108 | shapeDefinition.members.push(union.property); 109 | shapes.push(...union.interfaces || []); 110 | } else { 111 | shapeDefinition.members.push(dom.create.property(key, type, flag)); 112 | } 113 | 114 | }); 115 | 116 | shapes.push(shapeDefinition); 117 | return shapeDefinition; 118 | } 119 | 120 | function makeComment(doc: string): string { 121 | return `/**\r\n ${doc} \r\n*/`; 122 | } 123 | 124 | function getDeclarationName(prop: string): string { 125 | return prop.charAt(0).toUpperCase() + prop.slice(1); 126 | } 127 | 128 | const { name, required, type, description } = prop; 129 | const flag = required ? dom.DeclarationFlags.None : dom.DeclarationFlags.Optional; 130 | 131 | try { 132 | switch (type.name.toLowerCase()) { 133 | case 'any': return generate(name, dom.type.any, flag); 134 | case 'bool': return generate(name, dom.type.boolean, flag); 135 | case 'number': return generate(name, dom.type.number, flag); 136 | case 'object': return generate(name, dom.type.object, flag); 137 | case 'string': return generate(name, dom.type.string, flag); 138 | case 'this': return generate(name, dom.type.this, flag); 139 | case 'array': return generate(name, dom.type.array(dom.type.any), flag); 140 | case 'element': return generate(name, 'React.ReactElement', flag); 141 | case 'node': return generate(name, 'React.ReactNode', flag); 142 | case 'func': return generateFunc(name, description, flag); 143 | case 'shape': return generateShape(name, type.value as Props, flag); 144 | case 'arrayof': return generateArrayOf(name, type.value as Prop, flag); 145 | case 'enum': return generateOneOf(name, type.value as ValueArray, flag); 146 | case 'union': return generateOneOfType(name, type.value as ValueArray, flag); 147 | default: return generate(name, dom.type.any, flag); 148 | } 149 | } catch (e) { 150 | return generate(name, dom.type.any, flag); 151 | } 152 | } 153 | 154 | export function writeGeneric(out: string, type: string): string { 155 | return `${out}<${type}>`; 156 | } 157 | 158 | export function createImport(from: string, defaultImport?: string, namedImport?: string): dom.Import { 159 | if (defaultImport) { 160 | return dom.create.importDefault(defaultImport, from); 161 | } else if (namedImport) { 162 | return dom.create.importNamed(namedImport, from); 163 | } 164 | return dom.create.import(from); 165 | } 166 | -------------------------------------------------------------------------------- /test/anyFallback.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { assert } from 'chai'; 4 | import { generate } from '../src'; 5 | 6 | describe('anyFallback test', () => { 7 | before(() => { 8 | if (!fs.existsSync(path.join(__dirname, 'tmp'))) { 9 | fs.mkdirSync(path.join(__dirname, 'tmp')); 10 | } 11 | if (!fs.existsSync(path.join(__dirname, 'tmp', 'anyFallback'))) { 12 | fs.mkdirSync(path.join(__dirname, 'tmp', 'anyFallback')); 13 | } 14 | }); 15 | 16 | it('should create typings with basic', () => { 17 | const result = generate({ 18 | input: path.join(__dirname, '..', '..', 'baselines', 'anyFallback', 'anyFallback.js'), 19 | output: path.join(__dirname, 'tmp', 'anyFallback', 'anyFallback.d.ts'), 20 | }); 21 | 22 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'anyFallback', 'anyFallback.d.ts'); 23 | const baseline = fs.readFileSync(basePath, 'utf-8'); 24 | 25 | assert.strictEqual(result, baseline); 26 | 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/arrayOf.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { assert } from 'chai'; 4 | import { generate } from '../src'; 5 | 6 | describe('ArrayOf test', () => { 7 | before(() => { 8 | if (!fs.existsSync(path.join(__dirname, 'tmp'))) { 9 | fs.mkdirSync(path.join(__dirname, 'tmp')); 10 | } 11 | if (!fs.existsSync(path.join(__dirname, 'tmp', 'arrayOf'))) { 12 | fs.mkdirSync(path.join(__dirname, 'tmp', 'arrayOf')); 13 | } 14 | }); 15 | 16 | it('should create typings with basic', () => { 17 | const result = generate({ 18 | input: path.join(__dirname, '..', '..', 'baselines', 'arrayOf', 'basic.js'), 19 | output: path.join(__dirname, 'tmp', 'arrayOf', 'basic.d.ts'), 20 | }); 21 | 22 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'arrayOf', 'basic.d.ts'); 23 | const baseline = fs.readFileSync(basePath, 'utf-8'); 24 | 25 | assert.strictEqual(result, baseline); 26 | 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/basic.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { assert } from 'chai'; 4 | import { generate } from '../src'; 5 | 6 | describe('Basic test', () => { 7 | before(() => { 8 | if (!fs.existsSync(path.join(__dirname, 'tmp'))) { 9 | fs.mkdirSync(path.join(__dirname, 'tmp')); 10 | } 11 | if (!fs.existsSync(path.join(__dirname, 'tmp', 'basic'))) { 12 | fs.mkdirSync(path.join(__dirname, 'tmp', 'basic')); 13 | } 14 | }); 15 | 16 | it('should create typings with basically', () => { 17 | const result = generate({ 18 | input: path.join(__dirname, '..', '..', 'baselines', 'basic', 'basic.js'), 19 | output: path.join(__dirname, 'tmp', 'basic', 'basic.d.ts'), 20 | }); 21 | 22 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'basic', 'basic.d.ts'); 23 | const baseline = fs.readFileSync(basePath, 'utf-8'); 24 | 25 | assert.strictEqual(result, baseline); 26 | }); 27 | 28 | it('should create typings with required', () => { 29 | const result = generate({ 30 | input: path.join(__dirname, '..', '..', 'baselines', 'basic', 'required.js'), 31 | output: path.join(__dirname, 'tmp', 'basic', 'required.d.ts'), 32 | }); 33 | 34 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'basic', 'required.d.ts'); 35 | const baseline = fs.readFileSync(basePath, 'utf-8'); 36 | 37 | assert.strictEqual(result, baseline); 38 | 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /test/compose.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { assert } from 'chai'; 4 | import { generate } from '../src'; 5 | 6 | describe('Compose test', () => { 7 | before(() => { 8 | if (!fs.existsSync(path.join(__dirname, 'tmp'))) { 9 | fs.mkdirSync(path.join(__dirname, 'tmp')); 10 | } 11 | 12 | if (!fs.existsSync(path.join(__dirname, 'tmp', 'compose'))) { 13 | fs.mkdirSync(path.join(__dirname, 'tmp', 'compose')); 14 | } 15 | }); 16 | 17 | it('should create typings with single compose', () => { 18 | const result = generate({ 19 | input: path.join(__dirname, '..', '..', 'baselines', 'compose', 'single.js'), 20 | output: path.join(__dirname, 'tmp', 'compose', 'single.d.ts'), 21 | propTypesComposition: [{ 22 | from: '../basic/basic', 23 | named: 'BasicComponentProps', 24 | }], 25 | }); 26 | 27 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'compose', 'single.d.ts'); 28 | const baseline = fs.readFileSync(basePath, 'utf-8'); 29 | 30 | assert.strictEqual(result, baseline); 31 | 32 | }); 33 | 34 | it('should create typings with multiple compose', () => { 35 | const result = generate({ 36 | input: path.join(__dirname, '..', '..', 'baselines', 'compose', 'multiple.js'), 37 | output: path.join(__dirname, 'tmp', 'compose', 'multiple.d.ts'), 38 | propTypesComposition: [{ 39 | from: '../basic', 40 | named: 'BasicComponentProps', 41 | }, 42 | { 43 | from: '../basic', 44 | named: 'RequiredComponentProps', 45 | }], 46 | }); 47 | 48 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'compose', 'multiple.d.ts'); 49 | const baseline = fs.readFileSync(basePath, 'utf-8'); 50 | 51 | assert.strictEqual(result, baseline); 52 | }); 53 | 54 | it('should create typings with module compose', () => { 55 | const result = generate({ 56 | input: path.join(__dirname, '..', '..', 'baselines', 'compose', 'module.js'), 57 | output: path.join(__dirname, 'tmp', 'compose', 'module.d.ts'), 58 | propTypesComposition: [{ 59 | from: '../basic/basic', 60 | named: 'BasicComponentProps', 61 | }, 62 | { 63 | from: '@kuveytturk/boa-base/ComponentBase', 64 | named: 'ComponentBaseProps', 65 | }], 66 | 67 | }); 68 | 69 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'compose', 'module.d.ts'); 70 | const baseline = fs.readFileSync(basePath, 'utf-8'); 71 | 72 | assert.strictEqual(result, baseline); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/func.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { assert } from 'chai'; 4 | import { generate } from '../src'; 5 | 6 | describe('Func props', () => { 7 | before(() => { 8 | if (!fs.existsSync(path.join(__dirname, 'tmp'))) { 9 | fs.mkdirSync(path.join(__dirname, 'tmp')); 10 | } 11 | if (!fs.existsSync(path.join(__dirname, 'tmp', 'func'))) { 12 | fs.mkdirSync(path.join(__dirname, 'tmp', 'func')); 13 | } 14 | }); 15 | 16 | it('should create func props', () => { 17 | const result = generate({ 18 | input: path.join(__dirname, '..', '..', 'baselines', 'func', 'basic.js'), 19 | output: path.join(__dirname, 'tmp', 'func', 'basic.d.ts'), 20 | }); 21 | 22 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'func', 'basic.d.ts'); 23 | const baseline = fs.readFileSync(basePath, 'utf-8'); 24 | 25 | assert.strictEqual(result, baseline); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/inheritance.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { assert } from 'chai'; 4 | import { generate } from '../src'; 5 | 6 | describe('Inheritance test', () => { 7 | before(() => { 8 | if (!fs.existsSync(path.join(__dirname, 'tmp'))) { 9 | fs.mkdirSync(path.join(__dirname, 'tmp')); 10 | } 11 | if (!fs.existsSync(path.join(__dirname, 'tmp', 'inheritance'))) { 12 | fs.mkdirSync(path.join(__dirname, 'tmp', 'inheritance')); 13 | } 14 | }); 15 | 16 | it('should create base type', () => { 17 | const result = generate({ 18 | input: path.join(__dirname, '..', '..', 'baselines', 'inheritance', 'base.js'), 19 | isBaseClass: true, 20 | output: path.join(__dirname, 'tmp', 'inheritance', 'base.d.ts'), 21 | }); 22 | 23 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'inheritance', 'base.d.ts'); 24 | const baseline = fs.readFileSync(basePath, 'utf-8'); 25 | 26 | assert.strictEqual(result, baseline); 27 | }); 28 | 29 | it('should create with inheritance', () => { 30 | const result = generate({ 31 | extends: { 32 | import: { 33 | default: 'BaseClass', 34 | from: './base', 35 | }, 36 | includePropsAsGeneric: true, 37 | }, 38 | input: path.join(__dirname, '..', '..', 'baselines', 'inheritance', 'basic.js'), 39 | output: path.join(__dirname, 'tmp', 'inheritance', 'basic.d.ts'), 40 | }); 41 | 42 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'inheritance', 'basic.d.ts'); 43 | const baseline = fs.readFileSync(basePath, 'utf-8'); 44 | 45 | assert.strictEqual(result, baseline); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/method.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { assert } from 'chai'; 4 | import { generate } from '../src'; 5 | 6 | describe('Instance methods', () => { 7 | before(() => { 8 | if (!fs.existsSync(path.join(__dirname, 'tmp'))) { 9 | fs.mkdirSync(path.join(__dirname, 'tmp')); 10 | } 11 | if (!fs.existsSync(path.join(__dirname, 'tmp', 'method'))) { 12 | fs.mkdirSync(path.join(__dirname, 'tmp', 'method')); 13 | } 14 | }); 15 | 16 | it('should create instance methods', () => { 17 | const result = generate({ 18 | input: path.join(__dirname, '..', '..', 'baselines', 'method', 'method.js'), 19 | output: path.join(__dirname, 'tmp', 'method', 'method.d.ts'), 20 | }); 21 | 22 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'method', 'method.d.ts'); 23 | const baseline = fs.readFileSync(basePath, 'utf-8'); 24 | 25 | assert.strictEqual(result, baseline); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require @babel/register 2 | --reporter spec 3 | --recursive 4 | -------------------------------------------------------------------------------- /test/oneOf.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { assert } from 'chai'; 4 | import { generate } from '../src'; 5 | 6 | describe('OneOf test', () => { 7 | before(() => { 8 | if (!fs.existsSync(path.join(__dirname, 'tmp'))) { 9 | fs.mkdirSync(path.join(__dirname, 'tmp')); 10 | } 11 | if (!fs.existsSync(path.join(__dirname, 'tmp', 'oneOf'))) { 12 | fs.mkdirSync(path.join(__dirname, 'tmp', 'oneOf')); 13 | } 14 | }); 15 | 16 | it('should create typings with basic', () => { 17 | const result = generate({ 18 | input: path.join(__dirname, '..', '..', 'baselines', 'oneOf', 'basic.js'), 19 | output: path.join(__dirname, 'tmp', 'oneOf', 'basic.d.ts'), 20 | }); 21 | 22 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'oneOf', 'basic.d.ts'); 23 | const baseline = fs.readFileSync(basePath, 'utf-8'); 24 | 25 | assert.strictEqual(result, baseline); 26 | 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/oneOfType.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { assert } from 'chai'; 4 | import { generate } from '../src'; 5 | 6 | describe('OneOfType test', () => { 7 | before(() => { 8 | if (!fs.existsSync(path.join(__dirname, 'tmp'))) { 9 | fs.mkdirSync(path.join(__dirname, 'tmp')); 10 | } 11 | if (!fs.existsSync(path.join(__dirname, 'tmp', 'oneOfType'))) { 12 | fs.mkdirSync(path.join(__dirname, 'tmp', 'oneOfType')); 13 | } 14 | }); 15 | 16 | it('should create typings with basic', () => { 17 | const result = generate({ 18 | input: path.join(__dirname, '..', '..', 'baselines', 'oneOfType', 'basic.js'), 19 | output: path.join(__dirname, 'tmp', 'oneOfType', 'basic.d.ts'), 20 | }); 21 | 22 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'oneOfType', 'basic.d.ts'); 23 | const baseline = fs.readFileSync(basePath, 'utf-8'); 24 | 25 | assert.strictEqual(result, baseline); 26 | 27 | }); 28 | 29 | it('should create typings with shape', () => { 30 | const result = generate({ 31 | input: path.join(__dirname, '..', '..', 'baselines', 'oneOfType', 'withShape.js'), 32 | output: path.join(__dirname, 'tmp', 'oneOfType', 'withShape.d.ts'), 33 | }); 34 | 35 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'oneOfType', 'withShape.d.ts'); 36 | const baseline = fs.readFileSync(basePath, 'utf-8'); 37 | 38 | assert.strictEqual(result, baseline); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /test/shape.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { assert } from 'chai'; 4 | import { generate } from '../src'; 5 | 6 | describe('Shape test', () => { 7 | before(() => { 8 | if (!fs.existsSync(path.join(__dirname, 'tmp'))) { 9 | fs.mkdirSync(path.join(__dirname, 'tmp')); 10 | } 11 | if (!fs.existsSync(path.join(__dirname, 'tmp', 'shape'))) { 12 | fs.mkdirSync(path.join(__dirname, 'tmp', 'shape')); 13 | } 14 | }); 15 | 16 | it('should create typings with basically', () => { 17 | const result = generate({ 18 | input: path.join(__dirname, '..', '..', 'baselines', 'shape', 'basic.js'), 19 | output: path.join(__dirname, 'tmp', 'shape', 'basic.d.ts'), 20 | }); 21 | 22 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'shape', 'basic.d.ts'); 23 | const baseline = fs.readFileSync(basePath, 'utf-8'); 24 | 25 | assert.strictEqual(result, baseline); 26 | }); 27 | 28 | it('should create typings with nested shape', () => { 29 | const result = generate({ 30 | input: path.join(__dirname, '..', '..', 'baselines', 'shape', 'nested.js'), 31 | output: path.join(__dirname, 'tmp', 'shape', 'nested.d.ts'), 32 | }); 33 | 34 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'shape', 'nested.d.ts'); 35 | const baseline = fs.readFileSync(basePath, 'utf-8'); 36 | 37 | assert.strictEqual(result, baseline); 38 | }); 39 | 40 | it('should create typings with oneOfType', () => { 41 | const result = generate({ 42 | input: path.join(__dirname, '..', '..', 'baselines', 'shape', 'withOneOfType.js'), 43 | output: path.join(__dirname, 'tmp', 'shape', 'withOneOfType.d.ts'), 44 | }); 45 | 46 | const basePath = path.join(__dirname, '..', '..', 'baselines', 'shape', 'withOneOfType.d.ts'); 47 | const baseline = fs.readFileSync(basePath, 'utf-8'); 48 | 49 | assert.strictEqual(result, baseline); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "src", 4 | "test" 5 | ], 6 | "compilerOptions": { 7 | "lib": [ 8 | "es6", 9 | "dom" 10 | ], 11 | "module": "commonjs", 12 | "target": "es6", 13 | "jsx": "react", 14 | "noImplicitAny": true, 15 | "strictNullChecks": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "sourceMap": false, 19 | "outDir": "./bin", 20 | "newLine": "LF", 21 | "esModuleInterop": true, 22 | "declaration": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:latest", 4 | "tslint-config-prettier" 5 | ], 6 | "rules": { 7 | "interface-name": [ 8 | true, 9 | "never-prefix" 10 | ], 11 | "member-access": [ 12 | true, 13 | "no-public" 14 | ], 15 | "no-angle-bracket-type-assertion": false, 16 | "no-shadowed-variable": false, 17 | "variable-name": false, 18 | "no-implicit-dependencies": false, 19 | "ordered-imports": false, 20 | "no-submodule-imports": false 21 | }, 22 | "linterOptions": { 23 | "exclude": [ 24 | "src/dts-dom/*", 25 | "src/typings/*" 26 | ] 27 | } 28 | } --------------------------------------------------------------------------------