diff --git a/.gitignore b/.gitignore index af8a6a6..6897440 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules dist .DS_Store -coverage/ \ No newline at end of file +coverage/ +*.log diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..6835724 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +test/ +dist/ +!test/transformers.test.ts diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..f3fc57b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "singleQuote": true, + "printWidth": 120, + "tabWidth": 4, + "trailingComma": "all" +} diff --git a/.travis.yml b/.travis.yml index 0d26c0d..57e0f7f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,3 @@ language: node_js -node_js: 7 -cache: - directories: - - node_modules \ No newline at end of file +node_js: stable +cache: yarn diff --git a/.vscode/launch.json b/.vscode/launch.json index 81d50c3..fff8a01 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,15 +7,13 @@ "env": { "NODE_ENV": "test" }, - "externalConsole": false, + "console": "internalConsole", "name": "Run Tests", - "outDir": "${workspaceRoot}/dist", - "preLaunchTask": "compile", + "outFiles": ["${workspaceRoot}/dist"], + "preLaunchTask": "tsc", "program": "${workspaceRoot}/node_modules/.bin/jest", "request": "launch", - "runtimeArgs": [ - - ], + "runtimeArgs": [], "runtimeExecutable": null, "sourceMaps": true, "stopOnEntry": false, @@ -28,4 +26,4 @@ "port": 5858 } ] -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a1e2ab6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "**/node_modules": true, + "**/dist": true + }, + "search.exclude": { + "**/node_modules": true, + "**/dist": true + }, + "typescript.tsdk": "node_modules/typescript/lib" +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 926b1dd..319e8a4 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -7,4 +7,4 @@ "args": ["-p", "."], "showOutput": "silent", "problemMatcher": "$tsc" -} \ No newline at end of file +} diff --git a/LICENSE.md b/LICENSE.md index 906b9c0..806595a 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,182 +1,181 @@ - Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "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. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "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. - - "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). - - "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. - - "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." - - "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. - - 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. - - 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. - - 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: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) 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 - - (d) 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. - - 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. - - 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. - - 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. - - 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. - - 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. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "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. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "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. + + "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). + + "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. + + "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." + + "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. + +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. + +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. + +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: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) 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 + + (d) 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. + +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. + +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. + +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. + +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. + +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. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" @@ -187,16 +186,16 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2014 Lyft, Inc. +Copyright 2014 Lyft, Inc. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md index 4df874c..535d695 100644 --- a/README.md +++ b/README.md @@ -4,60 +4,71 @@ Transforms React code written in JavaScript to TypeScript. -## Features: +[**🖥 Download the VSCode Extension**](https://marketplace.visualstudio.com/items?itemName=mohsen1.react-javascript-to-typescript-transform-vscode) -* Proxies `PropTypes` to `React.Component` generic type and removes PropTypes -* Provides state typing for `React.Component` based on initial state and `setState()` calls in the component -* Hoist large interfaces for props and state out of `React.Component` into declared types -* Convert functional components with `PropTypes` property to TypeScript and uses propTypes to generate function type declaration +## Features: +* Proxies `PropTypes` to `React.Component` generic type and removes PropTypes +* Provides state typing for `React.Component` based on initial state and `setState()` calls in the component +* Hoist large interfaces for props and state out of `React.Component` into declared types +* Convert functional components with `PropTypes` property to TypeScript and uses propTypes to generate function type declaration ## Example **input** + ```jsx class MyComponent extends React.Component { - static propTypes = { - prop1: React.PropTypes.string.isRequired, - prop2: React.PropTypes.number - } - constructor() { - super(); - this.state = { foo: 1, bar: 'str' }; - } - render() { - return
{this.state.foo}, {this.state.bar}, {this.state.baz}
- } - onClick() { - this.setState({ baz: 3 }) - } + static propTypes = { + prop1: React.PropTypes.string.isRequired, + prop2: React.PropTypes.number, + }; + constructor() { + super(); + this.state = { foo: 1, bar: 'str' }; + } + render() { + return ( +
+ {this.state.foo}, {this.state.bar}, {this.state.baz} +
+ ); + } + onClick() { + this.setState({ baz: 3 }); + } } ``` **output** + ```tsx type MyComponentProps = { - prop1: string; - prop2: number | undefined; -} + prop1: string; + prop2?: number; +}; type MyComponentState = { - foo: number; - bar: string; - baz: number | undefined; -} + foo: number; + bar: string; + baz: number; +}; class MyComponent extends React.Component { - constructor() { - super(); - this.state = { foo: 1, bar: 'str' }; - } - render() { - return
{this.state.foo}, {this.state.bar}, {this.state.baz}
- } - onClick() { - this.setState({ baz: 3 }) - } + constructor() { + super(); + this.state = { foo: 1, bar: 'str' }; + } + render() { + return ( +
+ {this.state.foo}, {this.state.bar}, {this.state.baz} +
+ ); + } + onClick() { + this.setState({ baz: 3 }); + } } ``` @@ -73,10 +84,10 @@ npm install -g react-js-to-ts react-js-to-ts my-react-js-file.js ``` - ### VSCode plugin -[Download from VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=mohsen1.react-javascript-to-typescript-transform-vscode#review-details) +details +[Download from VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=mohsen1.react-javascript-to-typescript-transform-vscode#overview) ## Development @@ -91,13 +102,14 @@ npm test #### Watch mode Pass `-w` to `npm test` + ``` npm test -- -w ``` #### Only a single test case -Pass `-t` with transform name and case name space separated to `npm test` +Pass `-t` with transform name and case name space separated to `npm test` ``` npm test -- -t "react-js-make-props-and-state-transform propless-stateless" diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index b87d3a2..0000000 --- a/package-lock.json +++ /dev/null @@ -1,2019 +0,0 @@ -{ - "name": "react-js-to-ts", - "version": "1.0.0-alpha-5", - "lockfileVersion": 1, - "dependencies": { - "@types/chalk": { - "version": "https://registry.npmjs.org/@types/chalk/-/chalk-0.4.31.tgz", - "integrity": "sha1-ox10JBprHtu5c8822XooloNKUfk=", - "dev": true - }, - "@types/commander": { - "version": "https://registry.npmjs.org/@types/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-3QevH8NddoM+DaJupnor4Ii1+vw=", - "dev": true - }, - "@types/glob": { - "version": "https://registry.npmjs.org/@types/glob/-/glob-5.0.30.tgz", - "integrity": "sha1-ECZAnFYlqGiQdGAoCNCCsoZ7ilE=", - "dev": true - }, - "@types/jest": { - "version": "https://registry.npmjs.org/@types/jest/-/jest-19.2.3.tgz", - "integrity": "sha1-YXSAQOhYmokd/C7B0Wot10SCmA4=", - "dev": true - }, - "@types/minimatch": { - "version": "https://registry.npmjs.org/@types/minimatch/-/minimatch-2.0.29.tgz", - "integrity": "sha1-UALhT3Xi1x5WQoHfBDHIwbSio2o=", - "dev": true - }, - "@types/node": { - "version": "https://registry.npmjs.org/@types/node/-/node-7.0.22.tgz", - "integrity": "sha1-RZP02Ci91hKSlHjqQMZ7T0A8olU=", - "dev": true - }, - "@types/react": { - "version": "https://registry.npmjs.org/@types/react/-/react-15.0.24.tgz", - "integrity": "sha1-inUpncN5Bt8yfBjKkYv5elXnEjs=", - "dev": true - }, - "abab": { - "version": "https://registry.npmjs.org/abab/-/abab-1.0.3.tgz", - "integrity": "sha1-uB3l9ydOxOdW15fNg08wNkJyTl0=", - "dev": true - }, - "acorn": { - "version": "https://registry.npmjs.org/acorn/-/acorn-4.0.11.tgz", - "integrity": "sha1-7c2jvZN+dVZBDULtWGD2c5nHlMA=", - "dev": true - }, - "acorn-globals": { - "version": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", - "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", - "dev": true - }, - "ajv": { - "version": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true - }, - "align-text": { - "version": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true - }, - "amdefine": { - "version": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, - "ansi-escapes": { - "version": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", - "dev": true - }, - "ansi-regex": { - "version": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "anymatch": { - "version": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz", - "integrity": "sha1-o+Uvo5FoyCX/V7AkgSbOWo/5VQc=", - "dev": true - }, - "append-transform": { - "version": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", - "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", - "dev": true - }, - "argparse": { - "version": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", - "dev": true - }, - "arr-diff": { - "version": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true - }, - "arr-flatten": { - "version": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.3.tgz", - "integrity": "sha1-onTthawIhJtr14R8RYB0XcUa37E=", - "dev": true - }, - "array-equal": { - "version": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", - "dev": true - }, - "array-unique": { - "version": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "arrify": { - "version": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "asn1": { - "version": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true - }, - "assert-plus": { - "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true - }, - "async": { - "version": "https://registry.npmjs.org/async/-/async-2.4.1.tgz", - "integrity": "sha1-YqVrJ5yYoR0JhwlqAcw+6463u9c=", - "dev": true - }, - "asynckit": { - "version": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "aws-sign2": { - "version": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true - }, - "aws4": { - "version": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", - "dev": true - }, - "babel-code-frame": { - "version": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", - "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=", - "dev": true - }, - "babel-core": { - "version": "https://registry.npmjs.org/babel-core/-/babel-core-6.24.1.tgz", - "integrity": "sha1-jEKFZNzh4fQfszfsNPTDsCK1rYM=", - "dev": true - }, - "babel-generator": { - "version": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.24.1.tgz", - "integrity": "sha1-5xX0hsWN7SVknYiJRNUqoHxdlJc=", - "dev": true - }, - "babel-helpers": { - "version": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", - "dev": true - }, - "babel-jest": { - "version": "https://registry.npmjs.org/babel-jest/-/babel-jest-20.0.3.tgz", - "integrity": "sha1-5KA7E9wQOJ4UD8ZF0J/8TO0wFnE=", - "dev": true - }, - "babel-messages": { - "version": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true - }, - "babel-plugin-istanbul": { - "version": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.3.tgz", - "integrity": "sha1-buYoBBDc9Zx3R1GMPf2YaAlY8QI=", - "dev": true - }, - "babel-plugin-jest-hoist": { - "version": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.3.tgz", - "integrity": "sha1-r+3IU70/jcNUjqZx++adA8wsF2c=", - "dev": true - }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz", - "integrity": "sha1-0+MQtA72ZKNmIiAAl8bUQCmPK/4=", - "dev": true - }, - "babel-plugin-transform-strict-mode": { - "version": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", - "dev": true - }, - "babel-preset-jest": { - "version": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-20.0.3.tgz", - "integrity": "sha1-y6yq3stdaJyh4d4TYOv8ZoYsF4o=", - "dev": true - }, - "babel-register": { - "version": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.1.tgz", - "integrity": "sha1-fhDhOi9xBlvfrVoXh7pFvKbe118=", - "dev": true - }, - "babel-runtime": { - "version": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", - "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=", - "dev": true - }, - "babel-template": { - "version": "https://registry.npmjs.org/babel-template/-/babel-template-6.24.1.tgz", - "integrity": "sha1-BK5RTx+Ts6JTfyoPYKWkX7gwgzM=", - "dev": true - }, - "babel-traverse": { - "version": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.24.1.tgz", - "integrity": "sha1-qzZnP9NW+aCUhlnnszjV/q2zFpU=", - "dev": true - }, - "babel-types": { - "version": "https://registry.npmjs.org/babel-types/-/babel-types-6.24.1.tgz", - "integrity": "sha1-oTaHncFbNga9oNkMH8dDBML/CXU=", - "dev": true - }, - "babylon": { - "version": "https://registry.npmjs.org/babylon/-/babylon-6.17.1.tgz", - "integrity": "sha1-F/FP3fNhtpWYH+Z5OF5PHAHr2G8=", - "dev": true - }, - "balanced-match": { - "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=" - }, - "bcrypt-pbkdf": { - "version": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "dev": true, - "optional": true - }, - "boom": { - "version": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true - }, - "brace-expansion": { - "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", - "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=" - }, - "braces": { - "version": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true - }, - "browser-resolve": { - "version": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", - "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", - "dev": true, - "dependencies": { - "resolve": { - "version": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } - } - }, - "bser": { - "version": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", - "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", - "dev": true - }, - "builtin-modules": { - "version": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "callsites": { - "version": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true - }, - "camelcase": { - "version": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true, - "optional": true - }, - "caseless": { - "version": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "center-align": { - "version": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, - "optional": true - }, - "chalk": { - "version": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true - }, - "ci-info": { - "version": "https://registry.npmjs.org/ci-info/-/ci-info-1.0.0.tgz", - "integrity": "sha1-3FKF8rTiUYIWg2gcOBwziPRuxTQ=", - "dev": true - }, - "cliui": { - "version": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "optional": true, - "dependencies": { - "wordwrap": { - "version": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true, - "optional": true - } - } - }, - "co": { - "version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "code-point-at": { - "version": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "color-convert": { - "version": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", - "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", - "dev": true - }, - "color-name": { - "version": "https://registry.npmjs.org/color-name/-/color-name-1.1.2.tgz", - "integrity": "sha1-XIq3K2S9IhXWF66VWeuxSEdc+Y0=", - "dev": true - }, - "colors": { - "version": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - }, - "combined-stream": { - "version": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "dev": true - }, - "commander": { - "version": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=" - }, - "concat-map": { - "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "content-type-parser": { - "version": "https://registry.npmjs.org/content-type-parser/-/content-type-parser-1.0.1.tgz", - "integrity": "sha1-w+VpiMU8ZRJ/tG1AMqOpACRv3JQ=", - "dev": true - }, - "convert-source-map": { - "version": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", - "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=", - "dev": true - }, - "core-js": { - "version": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", - "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=", - "dev": true - }, - "cross-spawn": { - "version": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", - "dev": true - }, - "cryptiles": { - "version": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true - }, - "cssom": { - "version": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz", - "integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs=", - "dev": true - }, - "cssstyle": { - "version": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz", - "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=", - "dev": true - }, - "dashdash": { - "version": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "dependencies": { - "assert-plus": { - "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", - "dev": true - }, - "decamelize": { - "version": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "deep-is": { - "version": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "default-require-extensions": { - "version": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", - "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", - "dev": true - }, - "delayed-stream": { - "version": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "detect-indent": { - "version": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true - }, - "diff": { - "version": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", - "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", - "dev": true - }, - "ecc-jsbn": { - "version": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "dev": true, - "optional": true - }, - "errno": { - "version": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", - "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=", - "dev": true - }, - "error-ex": { - "version": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "dev": true - }, - "escape-string-regexp": { - "version": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { - "version": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", - "dev": true, - "dependencies": { - "esprima": { - "version": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "source-map": { - "version": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", - "dev": true, - "optional": true - } - } - }, - "esprima": { - "version": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - }, - "estraverse": { - "version": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", - "dev": true - }, - "esutils": { - "version": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "exec-sh": { - "version": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.0.tgz", - "integrity": "sha1-FPdd4/INKG75MwmbLOUKkDWc7xA=", - "dev": true - }, - "execa": { - "version": "https://registry.npmjs.org/execa/-/execa-0.5.1.tgz", - "integrity": "sha1-3j+4XLjW6RyFvLzrFkWBeFy1ezY=", - "dev": true - }, - "expand-brackets": { - "version": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true - }, - "expand-range": { - "version": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true - }, - "extend": { - "version": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true - }, - "extglob": { - "version": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true - }, - "extsprintf": { - "version": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", - "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=", - "dev": true - }, - "fast-levenshtein": { - "version": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fb-watchman": { - "version": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", - "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", - "dev": true - }, - "filename-regex": { - "version": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fileset": { - "version": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", - "dev": true - }, - "fill-range": { - "version": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", - "dev": true - }, - "find-up": { - "version": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true - }, - "for-in": { - "version": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true - }, - "forever-agent": { - "version": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "dev": true - }, - "fs-extra": { - "version": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", - "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=", - "dev": true - }, - "fs.realpath": { - "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "get-caller-file": { - "version": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", - "dev": true - }, - "get-stream": { - "version": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", - "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", - "dev": true - }, - "getpass": { - "version": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "dependencies": { - "assert-plus": { - "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "glob": { - "version": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=" - }, - "glob-all": { - "version": "https://registry.npmjs.org/glob-all/-/glob-all-3.1.0.tgz", - "integrity": "sha1-iRPd+17hrHgSZWJBsD1SF8ZLAqs=", - "dev": true, - "dependencies": { - "minimist": { - "version": "https://registry.npmjs.org/minimist/-/minimist-0.1.0.tgz", - "integrity": "sha1-md9lelJXTCHJBXSX33QnkLK0wN4=", - "dev": true - }, - "yargs": { - "version": "https://registry.npmjs.org/yargs/-/yargs-1.2.6.tgz", - "integrity": "sha1-nHtKgv1dWVsr8Xq23MQxNUMv40s=", - "dev": true - } - } - }, - "glob-base": { - "version": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true - }, - "glob-parent": { - "version": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true - }, - "globals": { - "version": "https://registry.npmjs.org/globals/-/globals-9.17.0.tgz", - "integrity": "sha1-DAymltm5u2lNLlRwvTd3fKrVAoY=", - "dev": true - }, - "graceful-fs": { - "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "graceful-readlink": { - "version": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" - }, - "growly": { - "version": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true - }, - "handlebars": { - "version": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz", - "integrity": "sha1-PTDHGLCaPZbyPqTMH0A8TTup/08=", - "dev": true, - "dependencies": { - "async": { - "version": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "source-map": { - "version": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true - } - } - }, - "har-schema": { - "version": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", - "dev": true - }, - "har-validator": { - "version": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "dev": true - }, - "has-ansi": { - "version": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true - }, - "has-flag": { - "version": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "hawk": { - "version": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true - }, - "hoek": { - "version": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, - "home-or-tmp": { - "version": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", - "dev": true - }, - "hosted-git-info": { - "version": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.4.2.tgz", - "integrity": "sha1-AHa59GonBQbduq6lZJaJdGBhKmc=", - "dev": true - }, - "html-encoding-sniffer": { - "version": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.1.tgz", - "integrity": "sha1-eb96eF6klf5mFl5zQVPzY/9UN9o=", - "dev": true - }, - "http-signature": { - "version": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true - }, - "iconv-lite": { - "version": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", - "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=", - "dev": true - }, - "inflight": { - "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=" - }, - "inherits": { - "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "invariant": { - "version": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", - "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", - "dev": true - }, - "invert-kv": { - "version": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true - }, - "is-arrayish": { - "version": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-buffer": { - "version": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", - "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", - "dev": true - }, - "is-builtin-module": { - "version": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true - }, - "is-ci": { - "version": "https://registry.npmjs.org/is-ci/-/is-ci-1.0.10.tgz", - "integrity": "sha1-9zkzayYyNlBhqdSCcM1WrjNpMY4=", - "dev": true - }, - "is-dotfile": { - "version": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.2.tgz", - "integrity": "sha1-LBMjg/ORmfjtwmjKAbmwB9IFzE0=", - "dev": true - }, - "is-equal-shallow": { - "version": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true - }, - "is-extendable": { - "version": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-finite": { - "version": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true - }, - "is-glob": { - "version": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true - }, - "is-number": { - "version": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true - }, - "is-posix-bracket": { - "version": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, - "is-stream": { - "version": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-typedarray": { - "version": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-utf8": { - "version": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "isarray": { - "version": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true - }, - "isstream": { - "version": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-api": { - "version": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.1.8.tgz", - "integrity": "sha1-qETlXG+a7uKS5/QpQhlvYLI9yT4=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.0.tgz", - "integrity": "sha1-ysoZ3srvNSW11jMdcB8/O3rUhSg=", - "dev": true - }, - "istanbul-lib-hook": { - "version": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.0.6.tgz", - "integrity": "sha1-wIZtHoHPLVMZJJUQEx/Bbe5JIx8=", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.1.tgz", - "integrity": "sha1-Fp4xvGLHeIUamUOd2Zw8wSGE02A=", - "dev": true - }, - "istanbul-lib-report": { - "version": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.0.tgz", - "integrity": "sha1-RExOzKmvqTz1hPVrEPGVv3aMB3A=", - "dev": true, - "dependencies": { - "supports-color": { - "version": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true - } - } - }, - "istanbul-lib-source-maps": { - "version": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.0.tgz", - "integrity": "sha1-jHcG1Jfib+62rz4MKP1bBmlZjQ4=", - "dev": true - }, - "istanbul-reports": { - "version": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.1.0.tgz", - "integrity": "sha1-HvO3lYiSGc+1+tFjZfbOEI1fjGY=", - "dev": true - }, - "jest": { - "version": "https://registry.npmjs.org/jest/-/jest-20.0.3.tgz", - "integrity": "sha1-5P0FTE8RcKEWoAdh2kz9tz8c3DM=", - "dev": true, - "dependencies": { - "jest-cli": { - "version": "https://registry.npmjs.org/jest-cli/-/jest-cli-20.0.3.tgz", - "integrity": "sha1-/ojdu3qfOhbQ7VUzmgokJPfw02E=", - "dev": true - } - } - }, - "jest-changed-files": { - "version": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-20.0.3.tgz", - "integrity": "sha1-k5TVzGXEOEBhSb7xv01Sto4D4/g=", - "dev": true - }, - "jest-config": { - "version": "https://registry.npmjs.org/jest-config/-/jest-config-20.0.3.tgz", - "integrity": "sha1-qTTyfup2SRWAHN2ib2+O7CrHkmY=", - "dev": true - }, - "jest-diff": { - "version": "https://registry.npmjs.org/jest-diff/-/jest-diff-20.0.3.tgz", - "integrity": "sha1-gfKI/Z5nXw+yPHXxwrGURf5YZhc=", - "dev": true - }, - "jest-docblock": { - "version": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-20.0.3.tgz", - "integrity": "sha1-F76phDQswz2DxQ++FUXqDvqkRxI=", - "dev": true - }, - "jest-environment-jsdom": { - "version": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-20.0.3.tgz", - "integrity": "sha1-BIqKwS7iJfcZBBdxODS7mZeH3pk=", - "dev": true - }, - "jest-environment-node": { - "version": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-20.0.3.tgz", - "integrity": "sha1-1Ii8RhKvLCRumG6K52caCZFj1AM=", - "dev": true - }, - "jest-haste-map": { - "version": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-20.0.3.tgz", - "integrity": "sha1-Y3fVN+rzTrX3USGmkcrj/egrqXE=", - "dev": true - }, - "jest-jasmine2": { - "version": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-20.0.3.tgz", - "integrity": "sha1-GMTp0CnaftGucnxVMABk0aBUKXQ=", - "dev": true - }, - "jest-matcher-utils": { - "version": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-20.0.3.tgz", - "integrity": "sha1-s6a443yld4A7CDKpixZPRLeBVhI=", - "dev": true - }, - "jest-matchers": { - "version": "https://registry.npmjs.org/jest-matchers/-/jest-matchers-20.0.3.tgz", - "integrity": "sha1-ymnbHDLbWm9wf6XgQBq7VXAN/WA=", - "dev": true - }, - "jest-message-util": { - "version": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-20.0.3.tgz", - "integrity": "sha1-auwoRDBvyw5udNV5bBAG2W/dgxw=", - "dev": true - }, - "jest-mock": { - "version": "https://registry.npmjs.org/jest-mock/-/jest-mock-20.0.3.tgz", - "integrity": "sha1-i8Bw6QQUqhVcEajWTIaaDVxx2lk=", - "dev": true - }, - "jest-regex-util": { - "version": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-20.0.3.tgz", - "integrity": "sha1-hburXRM+RGJbGfr4xqpRItCF12I=", - "dev": true - }, - "jest-resolve": { - "version": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-20.0.3.tgz", - "integrity": "sha1-N1MHqkD3hTLUD/ixfVMAsVGfjdQ=", - "dev": true - }, - "jest-resolve-dependencies": { - "version": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-20.0.3.tgz", - "integrity": "sha1-bhSntxevDyyzZnxUneQK8Bexcjo=", - "dev": true - }, - "jest-runtime": { - "version": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-20.0.3.tgz", - "integrity": "sha1-3d0iu8Qp4m5qltGs1GylVxSwklI=", - "dev": true, - "dependencies": { - "strip-bom": { - "version": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "jest-snapshot": { - "version": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-20.0.3.tgz", - "integrity": "sha1-W4R+GtsaTZCFKn+fElCG4YfHZWY=", - "dev": true - }, - "jest-util": { - "version": "https://registry.npmjs.org/jest-util/-/jest-util-20.0.3.tgz", - "integrity": "sha1-DAf32A2C9OWmfG+LnD/n9lz9Mq0=", - "dev": true - }, - "jest-validate": { - "version": "https://registry.npmjs.org/jest-validate/-/jest-validate-20.0.3.tgz", - "integrity": "sha1-0M/R3k9XnymEhJJcKA+PHZTsPKs=", - "dev": true - }, - "jodid25519": { - "version": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", - "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=", - "dev": true, - "optional": true - }, - "js-tokens": { - "version": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz", - "integrity": "sha1-COnxMkhKLEWjCQfp3E1VZ7fxFNc=", - "dev": true - }, - "js-yaml": { - "version": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.4.tgz", - "integrity": "sha1-UgtFZPhlc7qWZir4Woyvp7S1pvY=", - "dev": true - }, - "jsbn": { - "version": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, - "jsdom": { - "version": "https://registry.npmjs.org/jsdom/-/jsdom-9.12.0.tgz", - "integrity": "sha1-6MVG//ywbADUgzyoRBD+1/igl9Q=", - "dev": true - }, - "jsesc": { - "version": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - }, - "json-schema": { - "version": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-stable-stringify": { - "version": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true - }, - "json-stringify-safe": { - "version": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json5": { - "version": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "jsonfile": { - "version": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.0.tgz", - "integrity": "sha1-kufHRE5f/V+jLmqa6LhQNN+DR9A=", - "dev": true - }, - "jsonify": { - "version": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsprim": { - "version": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", - "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", - "dev": true, - "dependencies": { - "assert-plus": { - "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "kind-of": { - "version": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true - }, - "lazy-cache": { - "version": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true, - "optional": true - }, - "lcid": { - "version": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true - }, - "leven": { - "version": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", - "dev": true - }, - "levn": { - "version": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true - }, - "load-json-file": { - "version": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true - }, - "locate-path": { - "version": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true - }, - "lodash": { - "version": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", - "dev": true - }, - "longest": { - "version": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true - }, - "loose-envify": { - "version": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", - "dev": true - }, - "lru-cache": { - "version": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", - "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=", - "dev": true - }, - "make-error": { - "version": "https://registry.npmjs.org/make-error/-/make-error-1.3.0.tgz", - "integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y=", - "dev": true - }, - "makeerror": { - "version": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dev": true - }, - "mem": { - "version": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "dev": true - }, - "merge": { - "version": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz", - "integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo=", - "dev": true - }, - "micromatch": { - "version": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true - }, - "mime-db": { - "version": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", - "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=", - "dev": true - }, - "mime-types": { - "version": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", - "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", - "dev": true - }, - "mimic-fn": { - "version": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", - "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", - "dev": true - }, - "minimatch": { - "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=" - }, - "minimist": { - "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true - }, - "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "natural-compare": { - "version": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node-int64": { - "version": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "node-notifier": { - "version": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.1.2.tgz", - "integrity": "sha1-L6nhJgX6EACdRFSdb82KY93g5P8=", - "dev": true - }, - "normalize-package-data": { - "version": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.8.tgz", - "integrity": "sha1-2Bntoqne29H/pWPqQHHZNngilbs=", - "dev": true - }, - "normalize-path": { - "version": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true - }, - "npm-run-path": { - "version": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true - }, - "number-is-nan": { - "version": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "nwmatcher": { - "version": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.0.tgz", - "integrity": "sha1-tDiTYhcOfvl5jDx3FtgOvAEG/M8=", - "dev": true - }, - "oauth-sign": { - "version": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true - }, - "object-assign": { - "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object.omit": { - "version": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true - }, - "once": { - "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" - }, - "optimist": { - "version": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true - }, - "optionator": { - "version": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "dependencies": { - "wordwrap": { - "version": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - } - } - }, - "os-homedir": { - "version": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-locale": { - "version": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true - }, - "os-tmpdir": { - "version": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-finally": { - "version": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz", - "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=", - "dev": true - }, - "p-locate": { - "version": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true - }, - "p-map": { - "version": "https://registry.npmjs.org/p-map/-/p-map-1.1.1.tgz", - "integrity": "sha1-BfXkrpegaDcbwqXMhr+9vBnErno=", - "dev": true - }, - "parse-glob": { - "version": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true - }, - "parse-json": { - "version": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true - }, - "parse5": { - "version": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz", - "integrity": "sha1-m387DeMr543CQBsXVzzK8Pb1nZQ=", - "dev": true - }, - "path-exists": { - "version": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-key": { - "version": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", - "dev": true - }, - "path-type": { - "version": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true - }, - "performance-now": { - "version": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", - "dev": true - }, - "pify": { - "version": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true - }, - "pkg-dir": { - "version": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true - }, - "prelude-ls": { - "version": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "preserve": { - "version": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, - "pretty-format": { - "version": "https://registry.npmjs.org/pretty-format/-/pretty-format-20.0.3.tgz", - "integrity": "sha1-Ag41ClYKH+GpjcO+tsz/s4beixQ=", - "dev": true, - "dependencies": { - "ansi-styles": { - "version": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.0.0.tgz", - "integrity": "sha1-VATpOlRMT+x/BIJil3vr/jFV4ME=", - "dev": true - } - } - }, - "private": { - "version": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", - "integrity": "sha1-aM5eih7woju1cMwoU3tTMqumPvE=", - "dev": true - }, - "prr": { - "version": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", - "integrity": "sha1-GoS4WQgyVQFBGFPQCB7j+obikmo=", - "dev": true - }, - "pseudomap": { - "version": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "punycode": { - "version": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "qs": { - "version": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", - "dev": true - }, - "randomatic": { - "version": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.6.tgz", - "integrity": "sha1-EQ3Kv/OX6dz/fAeJzMCkmt8exbs=", - "dev": true - }, - "read-pkg": { - "version": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true - }, - "read-pkg-up": { - "version": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "dependencies": { - "find-up": { - "version": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true - }, - "path-exists": { - "version": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true - } - } - }, - "regenerator-runtime": { - "version": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", - "dev": true - }, - "regex-cache": { - "version": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz", - "integrity": "sha1-mxpsNdTQ3871cRrmUejp09cRQUU=", - "dev": true - }, - "remove-trailing-separator": { - "version": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz", - "integrity": "sha1-YV67lq9VlVLUv0BXyENtSGq2PMQ=", - "dev": true - }, - "repeat-element": { - "version": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", - "dev": true - }, - "repeat-string": { - "version": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "repeating": { - "version": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true - }, - "request": { - "version": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", - "dev": true - }, - "require-directory": { - "version": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "resolve": { - "version": "https://registry.npmjs.org/resolve/-/resolve-1.3.3.tgz", - "integrity": "sha1-ZVkHw0aahoDcLeOidaj91paR8OU=", - "dev": true - }, - "right-align": { - "version": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "optional": true - }, - "rimraf": { - "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", - "dev": true - }, - "safe-buffer": { - "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", - "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=", - "dev": true - }, - "sane": { - "version": "https://registry.npmjs.org/sane/-/sane-1.6.0.tgz", - "integrity": "sha1-lhDEUjB6E10pwf3+JUcDQYDEZ3U=", - "dev": true, - "dependencies": { - "bser": { - "version": "https://registry.npmjs.org/bser/-/bser-1.0.2.tgz", - "integrity": "sha1-OBEWlwsqbe6lZG3RXdcnhES1YWk=", - "dev": true - }, - "fb-watchman": { - "version": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-1.9.2.tgz", - "integrity": "sha1-okz0eCf4LTj7Waaa1wt247auc4M=", - "dev": true - }, - "minimist": { - "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "sax": { - "version": "https://registry.npmjs.org/sax/-/sax-1.2.2.tgz", - "integrity": "sha1-/YYxojvHgmvvXYcb24c3jJVkeCg=", - "dev": true - }, - "semver": { - "version": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true - }, - "set-blocking": { - "version": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "shellwords": { - "version": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.0.tgz", - "integrity": "sha1-Zq/Ue2oSky2Qccv9mKUueFzQuhQ=", - "dev": true - }, - "signal-exit": { - "version": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "slash": { - "version": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "sntp": { - "version": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true - }, - "source-map": { - "version": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", - "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", - "dev": true - }, - "source-map-support": { - "version": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz", - "integrity": "sha1-AyAt9lwG0r2MfsI2KhkwVv7407E=", - "dev": true - }, - "spdx-correct": { - "version": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", - "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", - "dev": true - }, - "spdx-expression-parse": { - "version": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", - "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", - "dev": true - }, - "spdx-license-ids": { - "version": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", - "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", - "dev": true - }, - "sprintf-js": { - "version": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.0.tgz", - "integrity": "sha1-/yo+T9BEl1Vf7Zezmg/YL6+zozw=", - "dev": true, - "dependencies": { - "assert-plus": { - "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "string-length": { - "version": "https://registry.npmjs.org/string-length/-/string-length-1.0.1.tgz", - "integrity": "sha1-VpcPscOFWOnnC3KL894mmsRa36w=", - "dev": true - }, - "string-width": { - "version": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true - }, - "stringstream": { - "version": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", - "dev": true - }, - "strip-ansi": { - "version": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true - }, - "strip-bom": { - "version": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true - }, - "strip-eof": { - "version": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-json-comments": { - "version": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "symbol-tree": { - "version": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", - "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", - "dev": true - }, - "test-exclude": { - "version": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.1.0.tgz", - "integrity": "sha1-BMpwtzkN04yY1KADoXOAbKeZHJE=", - "dev": true - }, - "throat": { - "version": "https://registry.npmjs.org/throat/-/throat-3.0.0.tgz", - "integrity": "sha1-58ZMhny7OEXxCHdkL3tgBVuOwNY=", - "dev": true - }, - "tmpl": { - "version": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true - }, - "to-fast-properties": { - "version": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - }, - "tough-cookie": { - "version": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", - "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", - "dev": true - }, - "tr46": { - "version": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", - "dev": true - }, - "trim-right": { - "version": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "ts-jest": { - "version": "https://registry.npmjs.org/ts-jest/-/ts-jest-20.0.4.tgz", - "integrity": "sha1-/+OYfUjIdizXtq6DUY0gPsoWdhg=", - "dev": true, - "dependencies": { - "camelcase": { - "version": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "cliui": { - "version": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "dependencies": { - "string-width": { - "version": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true - } - } - }, - "load-json-file": { - "version": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true - }, - "os-locale": { - "version": "https://registry.npmjs.org/os-locale/-/os-locale-2.0.0.tgz", - "integrity": "sha1-FZGN7VEFIrge565aMJ1U9jn8OaQ=", - "dev": true - }, - "path-type": { - "version": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true - }, - "read-pkg": { - "version": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true - }, - "read-pkg-up": { - "version": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true - }, - "string-width": { - "version": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz", - "integrity": "sha1-Y1xUNsxypuDDh87KJ41OLuxSaH4=", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": { - "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - } - } - }, - "strip-bom": { - "version": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "which-module": { - "version": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "yargs": { - "version": "https://registry.npmjs.org/yargs/-/yargs-8.0.1.tgz", - "integrity": "sha1-Qg73XoQMFFeoCtzKm8b6OEneUao=", - "dev": true - }, - "yargs-parser": { - "version": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", - "dev": true - } - } - }, - "ts-node": { - "version": "https://registry.npmjs.org/ts-node/-/ts-node-3.0.4.tgz", - "integrity": "sha1-oUdevyT9Ti7i+6ixqhYFuXe95QY=", - "dev": true, - "dependencies": { - "minimist": { - "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "tsconfig": { - "version": "https://registry.npmjs.org/tsconfig/-/tsconfig-6.0.0.tgz", - "integrity": "sha1-aw6DdgA9evGGT434+J3QBZ/80DI=", - "dev": true, - "dependencies": { - "strip-bom": { - "version": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "tslib": { - "version": "https://registry.npmjs.org/tslib/-/tslib-1.7.1.tgz", - "integrity": "sha1-vIAEFkaRkjp5/oN4u+s9ogF1OOw=", - "dev": true - }, - "tslint": { - "version": "https://registry.npmjs.org/tslint/-/tslint-5.3.2.tgz", - "integrity": "sha1-5WRZ+wlacwfxA7hAUhdPXju+9u0=", - "dev": true - }, - "tsutils": { - "version": "https://registry.npmjs.org/tsutils/-/tsutils-2.0.0.tgz", - "integrity": "sha1-rgykiktDG1wE5PcRqSJ6ouN+LgQ=", - "dev": true - }, - "tunnel-agent": { - "version": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true - }, - "tweetnacl": { - "version": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true - }, - "type-check": { - "version": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true - }, - "typescript": { - "version": "https://registry.npmjs.org/typescript/-/typescript-2.3.3.tgz", - "integrity": "sha1-ljnzw7QBSOjKl/4IpR3RiRu2viI=" - }, - "uglify-js": { - "version": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.27.tgz", - "integrity": "sha1-R3h/kSsPJC5bmENDvo416V9pTJw=", - "dev": true, - "optional": true, - "dependencies": { - "yargs": { - "version": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "optional": true - } - } - }, - "uglify-to-browserify": { - "version": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true, - "optional": true - }, - "universalify": { - "version": "https://registry.npmjs.org/universalify/-/universalify-0.1.0.tgz", - "integrity": "sha1-nrHEZR3rzGcMyU8adXYjMruWd3g=", - "dev": true - }, - "user-home": { - "version": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", - "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", - "dev": true - }, - "uuid": { - "version": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", - "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=", - "dev": true - }, - "v8flags": { - "version": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", - "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", - "dev": true - }, - "validate-npm-package-license": { - "version": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", - "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", - "dev": true - }, - "verror": { - "version": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", - "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", - "dev": true - }, - "walker": { - "version": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "dev": true - }, - "watch": { - "version": "https://registry.npmjs.org/watch/-/watch-0.10.0.tgz", - "integrity": "sha1-d3mLLaD5kQ1ZXxrOWwwiWFIfIdw=", - "dev": true - }, - "webidl-conversions": { - "version": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.1.tgz", - "integrity": "sha1-gBWherg+fhsxFjhIas6B2mziBqA=", - "dev": true - }, - "whatwg-encoding": { - "version": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.1.tgz", - "integrity": "sha1-PGxFGhmO567FWx7GHQkgxngBpfQ=", - "dev": true - }, - "whatwg-url": { - "version": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-4.8.0.tgz", - "integrity": "sha1-0pgaqRSMHgCkHFphMRZqtGg7vMA=", - "dev": true, - "dependencies": { - "webidl-conversions": { - "version": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", - "dev": true - } - } - }, - "which": { - "version": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", - "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", - "dev": true - }, - "which-module": { - "version": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true - }, - "window-size": { - "version": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true, - "optional": true - }, - "wordwrap": { - "version": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - }, - "worker-farm": { - "version": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.3.1.tgz", - "integrity": "sha1-QzMRK7SbF6oFC4eJXKayys9A5f8=", - "dev": true - }, - "wrap-ansi": { - "version": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true - }, - "wrappy": { - "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "xml-name-validator": { - "version": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz", - "integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=", - "dev": true - }, - "xtend": { - "version": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "y18n": { - "version": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yallist": { - "version": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", - "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", - "dev": true, - "dependencies": { - "camelcase": { - "version": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - }, - "cliui": { - "version": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true - } - } - }, - "yargs-parser": { - "version": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", - "dev": true, - "dependencies": { - "camelcase": { - "version": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - } - } - }, - "yn": { - "version": "https://registry.npmjs.org/yn/-/yn-1.3.0.tgz", - "integrity": "sha1-GwgSq7jYBdSJZvjfOF3J2syaGdg=", - "dev": true - } - } -} diff --git a/package.json b/package.json index 710ed2a..55994ac 100644 --- a/package.json +++ b/package.json @@ -1,50 +1,60 @@ { - "name": "react-js-to-ts", - "version": "1.1.0", - "description": "Convert React code from JavaScript to TypeScript", - "main": "dist/index.js", - "scripts": { - "pretest": "npm run build", - "test": "jest", - "coverage": "jest --coverage", - "posttest": "npm run lint", - "prelint": "npm run clean", - "lint": "tslint --type-check --project tsconfig.json --format codeFrame --exclude test/**/*.tsx", - "prepublish": "npm run build", - "clean": "rm -rf dist", - "prebuild": "npm run clean", - "build": "tsc --pretty" - }, - "jest": { - "mapCoverage": true, - "transform": { - ".ts": "/node_modules/ts-jest/preprocessor.js" + "name": "react-js-to-ts", + "version": "1.4.0", + "description": "Convert React code from JavaScript to TypeScript", + "main": "dist/index.js", + "scripts": { + "pretest": "npm run build", + "test": "jest", + "coverage": "jest --coverage", + "posttest": "npm run lint", + "prelint": "npm run clean", + "lint": "tslint --project tsconfig.json --format codeFrame --exclude test/**/*.tsx", + "prepublish": "npm run build", + "clean": "rm -rf dist", + "prebuild": "npm run clean", + "build": "tsc --pretty", + "precommit": "lint-staged", + "prettier": "prettier --write *.{js,json,css,md,ts,tsx}" }, - "testRegex": "test/runner.ts", - "moduleFileExtensions": [ - "ts", - "js" - ] - }, - "bin": "dist/cli.js", - "author": "Mohsen Azimi ", - "license": "Apache-2.0", - "dependencies": { - "chalk": "^1.1.3", - "commander": "^2.10.0", - "glob": "^7.1.2", - "typescript": "^2.4.0" - }, - "devDependencies": { - "@types/chalk": "^0.4.31", - "@types/commander": "^2.9.1", - "@types/glob": "^5.0.30", - "@types/jest": "^20.0.2", - "@types/node": "^8.0.2", - "@types/react": "^15.0.31", - "jest": "^20.0.4", - "ts-jest": "^20.0.6", - "ts-node": "^3.1.0", - "tslint": "^5.2.0" - } + "jest": { + "transform": { + ".ts": "/node_modules/ts-jest/preprocessor.js" + }, + "testRegex": "(/tests/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", + "moduleFileExtensions": ["ts", "js"] + }, + "lint-staged": { + "*.{js,json,css,md,ts,tsx}": ["node_modules/.bin/prettier --write", "git add"] + }, + "bin": "dist/cli.js", + "author": "Mohsen Azimi ", + "license": "Apache-2.0", + "dependencies": { + "chalk": "^2.4.1", + "commander": "^2.15.1", + "detect-indent": "^5.0.0", + "glob": "^7.1.2", + "lodash": "^4.17.10", + "prettier": "^1.12.1", + "typescript": "2.8.3" + }, + "devDependencies": { + "@types/chalk": "^2.2.0", + "@types/commander": "^2.9.1", + "@types/detect-indent": "^5.0.0", + "@types/glob": "^5.0.35", + "@types/jest": "^22.2.3", + "@types/lodash": "^4.14.109", + "@types/node": "^10.1.2", + "@types/prettier": "^1.12.2", + "@types/react": "^16.3.14", + "dedent": "^0.7.0", + "husky": "^0.14.3", + "jest": "^22.4.4", + "lint-staged": "^7.1.1", + "ts-jest": "^22.4.6", + "ts-node": "^6.0.3", + "tslint": "^5.10.0" + } } diff --git a/src/cli.ts b/src/cli.ts index 42ba40e..779f0a9 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -4,31 +4,95 @@ import * as program from 'commander'; import * as glob from 'glob'; import * as fs from 'fs'; import * as path from 'path'; +import * as prettier from 'prettier'; import { run } from '.'; +import { CompilationOptions } from './compiler'; + +function resolveGlobs(globPatterns: string[]): string[] { + const files: string[] = []; + function addFile(file: string) { + file = path.resolve(file); + if (files.indexOf(file) === -1) { + files.push(file); + } + } + globPatterns.forEach(pattern => { + if (/[{}*?+\[\]]/.test(pattern)) { + // Smells like globs + glob.sync(pattern, {}).forEach(file => { + addFile(file); + }); + } else { + addFile(pattern); + } + }); + return files; +} program .version('1.0.0') + .option('--arrow-parens ', 'Include parentheses around a sole arrow function parameter.', 'avoid') + .option('--no-bracket-spacing', 'Do not print spaces between brackets.', false) + .option('--jsx-bracket-same-line', 'Put > on the last line instead of at a new line.', false) + .option('--print-width ', 'The line length where Prettier will try wrap.', 80) + .option('--prose-wrap How to wrap prose. (markdown)', 'preserve') + .option('--no-semi', 'Do not print semicolons, except at the beginning of lines which may need them', false) + .option('--single-quote', 'Use single quotes instead of double quotes.', false) + .option('--tab-width ', 'Number of spaces per indentation level.', 2) + .option('--trailing-comma ', 'Print trailing commas wherever possible when multi-line.', 'none') + .option('--use-tabs', 'Indent with tabs instead of spaces.', false) + .option('--ignore-prettier-errors', 'Ignore (but warn about) errors in Prettier', false) + .option('--keep-original-files', 'Keep original files', false) + .option('--keep-temporary-files', 'Keep temporary files', false) .usage('[options] ') - .command('* ') - .action((globPattern) => { - if (!globPattern) { - throw new Error('You must provide a file name or glob pattern to transform'); + .command('* [glob/filename...]') + .action((globPatterns: string[]) => { + const prettierOptions: prettier.Options = { + arrowParens: program.arrowParens, + bracketSpacing: !program.noBracketSpacing, + jsxBracketSameLine: !!program.jsxBracketSameLine, + printWidth: parseInt(program.printWidth, 10), + proseWrap: program.proseWrap, + semi: !program.noSemi, + singleQuote: !!program.singleQuote, + tabWidth: parseInt(program.tabWidth, 10), + trailingComma: program.trailingComma, + useTabs: !!program.useTabs, + }; + const compilationOptions: CompilationOptions = { + ignorePrettierErrors: !!program.ignorePrettierErrors, + }; + const files = resolveGlobs(globPatterns); + if (!files.length) { + throw new Error('Nothing to do. You must provide file names or glob patterns to transform.'); } - const files = glob.sync(globPattern, {}); - for (const file of files) { - const filePath = path.resolve(file); + let errors = false; + for (const filePath of files) { + console.log(`Transforming ${filePath}...`); const newPath = filePath.replace(/\.jsx?$/, '.tsx'); - + const temporaryPath = filePath.replace(/\.jsx?$/, `_js2ts_${+new Date()}.tsx`); try { - fs.renameSync(filePath, newPath); - const result = run(newPath); + fs.copyFileSync(filePath, temporaryPath); + const result = run(temporaryPath, prettierOptions, compilationOptions); fs.writeFileSync(newPath, result); - } catch(error) { - console.warn(`Failed to convert ${file}`); + if (!program.keepOriginalFiles) { + fs.unlinkSync(filePath); + } + } catch (error) { + console.warn(`Failed to convert ${filePath}`); console.warn(error); + errors = true; + } + if (!program.keepTemporaryFiles) { + if (fs.existsSync(temporaryPath)) { + fs.unlinkSync(temporaryPath); + } } } + if (errors) { + process.exit(1); + } }); program.parse(process.argv); diff --git a/src/compiler.ts b/src/compiler.ts index 6b5b0d9..4953a7c 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1,20 +1,43 @@ +import * as os from 'os'; +import * as fs from 'fs'; import * as ts from 'typescript'; -import * as chalk from 'chalk'; +import chalk from 'chalk'; +import * as _ from 'lodash'; +import * as prettier from 'prettier'; +import * as detectIndent from 'detect-indent'; import { TransformFactoryFactory } from '.'; +export interface CompilationOptions { + ignorePrettierErrors: boolean; +} + +const DEFAULT_COMPILATION_OPTIONS: CompilationOptions = { + ignorePrettierErrors: false, +}; + +export { DEFAULT_COMPILATION_OPTIONS }; + /** * Compile and return result TypeScript * @param filePath Path to file to compile */ -export function compile(filePath: string, factoryFactories: TransformFactoryFactory[]) { +export function compile( + filePath: string, + factoryFactories: TransformFactoryFactory[], + incomingPrettierOptions: prettier.Options = {}, + compilationOptions: CompilationOptions = DEFAULT_COMPILATION_OPTIONS, +) { const compilerOptions: ts.CompilerOptions = { target: ts.ScriptTarget.ES2017, module: ts.ModuleKind.ES2015, }; const program = ts.createProgram([filePath], compilerOptions); - const sourceFiles = program.getSourceFiles().filter(sf => !sf.isDeclarationFile); + // `program.getSourceFiles()` will include those imported files, + // like: `import * as a from './file-a'`. + // We should only transform current file. + const sourceFiles = program.getSourceFiles().filter(sf => sf.fileName === filePath); const typeChecker = program.getTypeChecker(); const result = ts.transform( @@ -22,21 +45,96 @@ export function compile(filePath: string, factoryFactories: TransformFactoryFact factoryFactories.map(factoryFactory => factoryFactory(typeChecker), compilerOptions), ); - if (result.diagnostics && result.diagnostics.length) { - console.log(chalk.yellow(` + console.log( + chalk.yellow(` ======================= Diagnostics for ${filePath} ======================= - `)); + `), + ); for (const diag of result.diagnostics) { if (diag.file && diag.start) { const pos = diag.file.getLineAndCharacterOfPosition(diag.start); - console.log(`(${pos.line}, ${pos.character}) ${diag.messageText}`) + console.log(`(${pos.line}, ${pos.character}) ${diag.messageText}`); } } } - const printer = ts.createPrinter() + const printer = ts.createPrinter(); // TODO: fix the index 0 access... What if program have multiple source files? - return printer.printNode(ts.EmitHint.SourceFile, result.transformed[0], sourceFiles[0]); + const printed = printer.printNode(ts.EmitHint.SourceFile, result.transformed[0], sourceFiles[0]); + + const inputSource = fs.readFileSync(filePath, 'utf-8'); + const prettierOptions = getPrettierOptions(filePath, inputSource, incomingPrettierOptions); + + try { + return prettier.format(printed, prettierOptions); + } catch (prettierError) { + if (compilationOptions.ignorePrettierErrors) { + console.warn(`Prettier failed for ${filePath} (ignorePrettierErrors is on):`); + console.warn(prettierError); + return printed; + } + throw prettierError; + } +} + +/** + * Get Prettier options based on style of a JavaScript + * @param filePath Path to source file + * @param source Body of a JavaScript + * @param options Existing prettier option + */ +export function getPrettierOptions(filePath: string, source: string, options: prettier.Options): prettier.Options { + const resolvedOptions = prettier.resolveConfig.sync(filePath); + if (resolvedOptions) { + _.defaults(resolvedOptions, options); + return resolvedOptions; + } + const { amount: indentAmount, type: indentType } = detectIndent(source); + const sourceWidth = getCodeWidth(source, 80); + const semi = getUseOfSemi(source); + const quotations = getQuotation(source); + + _.defaults(Object.assign({}, options), { + tabWidth: indentAmount, + useTabs: indentType && indentType === 'tab', + printWidth: sourceWidth, + semi, + singleQuote: quotations === 'single', + }); + + return options; +} + +/** + * Given body of a source file, return its code width + * @param source + */ +function getCodeWidth(source: string, defaultWidth: number): number { + return source.split(os.EOL).reduce((result, line) => Math.max(result, line.length), defaultWidth); +} + +/** + * Detect if a source file is using semicolon + * @todo: use an actual parser. This is not a proper implementation + * @param source + * @return true if code is using semicolons + */ +function getUseOfSemi(source: string): boolean { + return source.indexOf(';') !== -1; +} + +/** + * Detect if a source file is using single quotes or double quotes + * @todo use an actual parser. This is not a proper implementation + * @param source + */ +function getQuotation(source: string): 'single' | 'double' { + const numberOfSingleQuotes = (source.match(/\'/g) || []).length; + const numberOfDoubleQuotes = (source.match(/\"/g) || []).length; + if (numberOfSingleQuotes > numberOfDoubleQuotes) { + return 'single'; + } + return 'double'; } diff --git a/src/helpers/build-prop-type-interface.ts b/src/helpers/build-prop-type-interface.ts new file mode 100644 index 0000000..df0730a --- /dev/null +++ b/src/helpers/build-prop-type-interface.ts @@ -0,0 +1,188 @@ +import * as ts from 'typescript'; + +/** + * Build props interface from propTypes object + * @example + * { + * foo: React.PropTypes.string.isRequired + * } + * + * becomes + * { + * foo: string; + * } + * @param objectLiteral + */ +export function buildInterfaceFromPropTypeObjectLiteral(objectLiteral: ts.ObjectLiteralExpression) { + const members = objectLiteral.properties + // We only need to process PropertyAssignment: + // { + // a: 123 // PropertyAssignment + // } + // + // filter out: + // { + // a() {}, // MethodDeclaration + // b, // ShorthandPropertyAssignment + // ...c, // SpreadAssignment + // get d() {}, // AccessorDeclaration + // } + .filter(ts.isPropertyAssignment) + // Ignore children, React types have it + .filter(property => property.name.getText() !== 'children') + .map(propertyAssignment => { + const name = propertyAssignment.name.getText(); + const initializer = propertyAssignment.initializer; + const isRequired = isPropTypeRequired(initializer); + const typeExpression = isRequired + ? // We have guaranteed the type in `isPropTypeRequired()` + (initializer as ts.PropertyAccessExpression).expression + : initializer; + const typeValue = getTypeFromReactPropTypeExpression(typeExpression); + + return ts.createPropertySignature( + [], + name, + isRequired ? undefined : ts.createToken(ts.SyntaxKind.QuestionToken), + typeValue, + undefined, + ); + }); + + return ts.createTypeLiteralNode(members); +} + +/** + * Turns React.PropTypes.* into TypeScript type value + * + * @param node React propTypes value + */ +function getTypeFromReactPropTypeExpression(node: ts.Expression): ts.TypeNode { + let result = null; + if (ts.isPropertyAccessExpression(node)) { + /** + * PropTypes.array, + * PropTypes.bool, + * PropTypes.func, + * PropTypes.number, + * PropTypes.object, + * PropTypes.string, + * PropTypes.symbol, (ignore) + * PropTypes.node, + * PropTypes.element, + * PropTypes.any, + */ + const text = node.getText().replace(/React\.PropTypes\./, ''); + + if (/string/.test(text)) { + result = ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword); + } else if (/any/.test(text)) { + result = ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); + } else if (/array/.test(text)) { + result = ts.createArrayTypeNode(ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)); + } else if (/bool/.test(text)) { + result = ts.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword); + } else if (/number/.test(text)) { + result = ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword); + } else if (/object/.test(text)) { + result = ts.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword); + } else if (/node/.test(text)) { + result = ts.createTypeReferenceNode('React.ReactNode', []); + } else if (/element/.test(text)) { + result = ts.createTypeReferenceNode('JSX.Element', []); + } else if (/func/.test(text)) { + const arrayOfAny = ts.createParameter( + [], + [], + ts.createToken(ts.SyntaxKind.DotDotDotToken), + 'args', + undefined, + ts.createArrayTypeNode(ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)), + undefined, + ); + result = ts.createFunctionTypeNode([], [arrayOfAny], ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)); + } + } else if (ts.isCallExpression(node)) { + /** + * PropTypes.instanceOf(), (ignore) + * PropTypes.oneOf(), // only support oneOf([1, 2]), oneOf(['a', 'b']) + * PropTypes.oneOfType(), + * PropTypes.arrayOf(), + * PropTypes.objectOf(), + * PropTypes.shape(), + */ + const text = node.expression.getText(); + if (/oneOf$/.test(text)) { + const argument = node.arguments[0]; + if (ts.isArrayLiteralExpression(argument)) { + if (argument.elements.every(elm => ts.isStringLiteral(elm) || ts.isNumericLiteral(elm))) { + result = ts.createUnionTypeNode( + (argument.elements as ts.NodeArray).map(elm => + ts.createLiteralTypeNode(elm), + ), + ); + } + } + } else if (/oneOfType$/.test(text)) { + const argument = node.arguments[0]; + if (ts.isArrayLiteralExpression(argument)) { + result = ts.createUnionOrIntersectionTypeNode( + ts.SyntaxKind.UnionType, + argument.elements.map(elm => getTypeFromReactPropTypeExpression(elm)), + ); + } + } else if (/arrayOf$/.test(text)) { + const argument = node.arguments[0]; + if (argument) { + result = ts.createArrayTypeNode(getTypeFromReactPropTypeExpression(argument)); + } + } else if (/objectOf$/.test(text)) { + const argument = node.arguments[0]; + if (argument) { + result = ts.createTypeLiteralNode([ + ts.createIndexSignature( + undefined, + undefined, + [ + ts.createParameter( + undefined, + undefined, + undefined, + 'key', + undefined, + ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + ), + ], + getTypeFromReactPropTypeExpression(argument), + ), + ]); + } + } else if (/shape$/.test(text)) { + const argument = node.arguments[0]; + if (ts.isObjectLiteralExpression(argument)) { + return buildInterfaceFromPropTypeObjectLiteral(argument); + } + } + } + + /** + * customProp, + * anything others + */ + if (result === null) { + result = ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); + } + + return result; +} + +/** + * Decide if node is required + * @param node React propTypes member node + */ +function isPropTypeRequired(node: ts.Expression) { + if (!ts.isPropertyAccessExpression(node)) return false; + + const text = node.getText().replace(/React\.PropTypes\./, ''); + return /\.isRequired/.test(text); +} diff --git a/src/helpers/index.ts b/src/helpers/index.ts index 18c7315..04a2c29 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -1,7 +1,7 @@ import * as ts from 'typescript'; -import * as kinds from './isKind'; +import * as _ from 'lodash'; -export * from './isKind'; +export * from './build-prop-type-interface'; /** * If a class declaration a react class? @@ -46,16 +46,18 @@ export function isReactComponent(classDeclaration: ts.ClassDeclaration, typeChec /** * Determine if a ts.HeritageClause is React HeritageClause * - * @example `extends React.Component<{}, void>` is a React HeritageClause + * @example `extends React.Component<{}, {}>` is a React HeritageClause * * @todo: this is lazy. Use the typeChecker instead * @param clause */ export function isReactHeritageClause(clause: ts.HeritageClause) { - return clause.token === ts.SyntaxKind.ExtendsKeyword && + return ( + clause.token === ts.SyntaxKind.ExtendsKeyword && clause.types.length === 1 && - kinds.isExpressionWithTypeArguments(clause.types[0]) && - /Component/.test(clause.types[0].expression.getText()); + ts.isExpressionWithTypeArguments(clause.types[0]) && + /Component/.test(clause.types[0].expression.getText()) + ); } /** @@ -65,11 +67,13 @@ export function isReactHeritageClause(clause: ts.HeritageClause) { * @param statement */ export function isReactPropTypeAssignmentStatement(statement: ts.Statement): statement is ts.ExpressionStatement { - return kinds.isExpressionStatement(statement) - && kinds.isBinaryExpression(statement.expression) - && statement.expression.operatorToken.kind === ts.SyntaxKind.FirstAssignment - && kinds.isPropertyAccessExpression(statement.expression.left) - && /\.propTypes$|\.propTypes\..+$/.test(statement.expression.left.getText()) + return ( + ts.isExpressionStatement(statement) && + ts.isBinaryExpression(statement.expression) && + statement.expression.operatorToken.kind === ts.SyntaxKind.FirstAssignment && + ts.isPropertyAccessExpression(statement.expression.left) && + /\.propTypes$|\.propTypes\..+$/.test(statement.expression.left.getText()) + ); } /** @@ -80,7 +84,7 @@ export function hasStaticModifier(classMember: ts.ClassElement) { if (!classMember.modifiers) { return false; } - const staticModifier = find(classMember.modifiers, (modifier) => { + const staticModifier = _.find(classMember.modifiers, modifier => { return modifier.kind == ts.SyntaxKind.StaticKeyword; }); return staticModifier !== undefined; @@ -93,43 +97,61 @@ export function hasStaticModifier(classMember: ts.ClassElement) { */ export function isPropTypesMember(classMember: ts.ClassElement, sourceFile: ts.SourceFile) { try { - return classMember.name !== undefined && classMember.name.getFullText(sourceFile) !== 'propTypes' + const name = + classMember.name !== undefined && ts.isIdentifier(classMember.name) ? classMember.name.escapedText : null; + return name === 'propTypes'; } catch (e) { return false; } } -// TODO: replace following functions with Lodash? -// --------------------------------------------------------------------------------------------------------- - /** - * Find an item in a collection with a matcher - * @param collection - * @param matcher + * Get component name off of a propType assignment statement + * @param propTypeAssignment + * @param sourceFile */ -export function find(collection: T[], matcher: (item: T) => boolean): T | undefined { - for (const item of collection) { - if (matcher(item)) { return item; } - } - - return undefined; +export function getComponentName(propTypeAssignment: ts.Statement, sourceFile: ts.SourceFile) { + const text = propTypeAssignment.getText(sourceFile); + return text.substr(0, text.indexOf('.')); } /** - * Look in a collection and see if collection has a specific item - * @param collection - * @param matcher + * Convert react stateless function to arrow function + * @example + * Before: + * function Hello(message) { + * return
{message}
+ * } + * + * After: + * const Hello = message => { + * return
{message}
+ * } */ -export function has(collection: T[], matcher: (item: T) => boolean): boolean { - if (!collection || !collection.length) { - return false; - } - - for (const item of collection) { - if (matcher(item)) { return true; } - } - - return false; +export function convertReactStatelessFunctionToArrowFunction( + statelessFunc: ts.FunctionDeclaration | ts.VariableStatement, +) { + if (ts.isVariableStatement(statelessFunc)) return statelessFunc; + + const funcName = statelessFunc.name || 'Component'; + const funcBody = statelessFunc.body || ts.createBlock([]); + + const initializer = ts.createArrowFunction( + undefined, + undefined, + statelessFunc.parameters, + undefined, + undefined, + funcBody, + ); + + return ts.createVariableStatement( + statelessFunc.modifiers, + ts.createVariableDeclarationList( + [ts.createVariableDeclaration(funcName, undefined, initializer)], + ts.NodeFlags.Const, + ), + ); } /** @@ -138,10 +160,12 @@ export function has(collection: T[], matcher: (item: T) => boolean): boolean * @param afterItem * @param newItem */ -export function insertAfter(collection: T[], afterItem: T, newItem: T) { - const index = collection.indexOf(afterItem) + 1; +export function insertAfter(collection: ArrayLike, afterItem: T, newItem: T) { + const index = _.indexOf(collection, afterItem) + 1; - return collection.slice(0, index).concat(newItem).concat(collection.slice(index)); + return _.slice(collection, 0, index) + .concat(newItem) + .concat(_.slice(collection, index)); } /** @@ -150,10 +174,12 @@ export function insertAfter(collection: T[], afterItem: T, newItem: T) { * @param beforeItem * @param newItem */ -export function insertBefore(collection: T[], beforeItem: T, newItem: T) { - const index = collection.indexOf(beforeItem); +export function insertBefore(collection: ArrayLike, beforeItem: T, newItems: T | T[]) { + const index = _.indexOf(collection, beforeItem); - return collection.slice(0, index).concat(newItem).concat(collection.slice(index)); + return _.slice(collection, 0, index) + .concat(newItems) + .concat(_.slice(collection, index)); } /** @@ -162,10 +188,11 @@ export function insertBefore(collection: T[], beforeItem: T, newItem: T) { * @param item * @param newItem */ -export function replaceItem(collection: T[], item: T, newItem: T) { - const index = collection.indexOf(item); - - return collection.slice(0, index).concat(newItem).concat(collection.slice(index + 1)); +export function replaceItem(collection: ArrayLike, item: T, newItem: T) { + const index = _.indexOf(collection, item); + return _.slice(collection, 0, index) + .concat(newItem) + .concat(_.slice(collection, index + 1)); } /** @@ -174,8 +201,7 @@ export function replaceItem(collection: T[], item: T, newItem: T) { * @param item * @param newItem */ -export function removeItem(collection: T[], item: T) { - const index = collection.indexOf(item); - - return collection.slice(0, index).concat(collection.slice(index + 1)); +export function removeItem(collection: ArrayLike, item: T) { + const index = _.indexOf(collection, item); + return _.slice(collection, 0, index).concat(_.slice(collection, index + 1)); } diff --git a/src/helpers/isKind.ts b/src/helpers/isKind.ts deleted file mode 100644 index 8756ad9..0000000 --- a/src/helpers/isKind.ts +++ /dev/null @@ -1,1185 +0,0 @@ -import * as ts from 'typescript'; - -/** - * Return true if node is `EndOfFileToken` - * @param node A TypeScript node - */ -export function isEndOfFileToken(node: ts.Node): node is ts.EndOfFileToken { - return node.kind === ts.SyntaxKind.EndOfFileToken; -} - -/** - * Return true if node is `NumericLiteral` - * @param node A TypeScript node - */ -export function isNumericLiteral(node: ts.Node): node is ts.NumericLiteral { - return node.kind === ts.SyntaxKind.NumericLiteral; -} - -/** - * Return true if node is `StringLiteral` - * @param node A TypeScript node - */ -export function isStringLiteral(node: ts.Node): node is ts.StringLiteral { - return node.kind === ts.SyntaxKind.StringLiteral; -} - -/** - * Return true if node is `JsxText` - * @param node A TypeScript node - */ -export function isJsxText(node: ts.Node): node is ts.JsxText { - return node.kind === ts.SyntaxKind.JsxText; -} - -/** - * Return true if node is `RegularExpressionLiteral` - * @param node A TypeScript node - */ -export function isRegularExpressionLiteral(node: ts.Node): node is ts.RegularExpressionLiteral { - return node.kind === ts.SyntaxKind.RegularExpressionLiteral; -} - -/** - * Return true if node is `NoSubstitutionTemplateLiteral` - * @param node A TypeScript node - */ -export function isNoSubstitutionTemplateLiteral(node: ts.Node): node is ts.NoSubstitutionTemplateLiteral { - return node.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral; -} - -/** - * Return true if node is `TemplateHead` - * @param node A TypeScript node - */ -export function isTemplateHead(node: ts.Node): node is ts.TemplateHead { - return node.kind === ts.SyntaxKind.TemplateHead; -} - -/** - * Return true if node is `TemplateMiddle` - * @param node A TypeScript node - */ -export function isTemplateMiddle(node: ts.Node): node is ts.TemplateMiddle { - return node.kind === ts.SyntaxKind.TemplateMiddle; -} - -/** - * Return true if node is `TemplateTail` - * @param node A TypeScript node - */ -export function isTemplateTail(node: ts.Node): node is ts.TemplateTail { - return node.kind === ts.SyntaxKind.TemplateTail; -} - -/** - * Return true if node is `DotDotDotToken` - * @param node A TypeScript node - */ -export function isDotDotDotToken(node: ts.Node): node is ts.DotDotDotToken { - return node.kind === ts.SyntaxKind.DotDotDotToken; -} - -/** - * Return true if node is `EqualsGreaterThanToken` - * @param node A TypeScript node - */ -export function isEqualsGreaterThanToken(node: ts.Node): node is ts.EqualsGreaterThanToken { - return node.kind === ts.SyntaxKind.EqualsGreaterThanToken; -} - -/** - * Return true if node is `AsteriskToken` - * @param node A TypeScript node - */ -export function isAsteriskToken(node: ts.Node): node is ts.AsteriskToken { - return node.kind === ts.SyntaxKind.AsteriskToken; -} - -/** - * Return true if node is `QuestionToken` - * @param node A TypeScript node - */ -export function isQuestionToken(node: ts.Node): node is ts.QuestionToken { - return node.kind === ts.SyntaxKind.QuestionToken; -} - -/** - * Return true if node is `ColonToken` - * @param node A TypeScript node - */ -export function isColonToken(node: ts.Node): node is ts.ColonToken { - return node.kind === ts.SyntaxKind.ColonToken; -} - -/** - * Return true if node is `AtToken` - * @param node A TypeScript node - */ -export function isAtToken(node: ts.Node): node is ts.AtToken { - return node.kind === ts.SyntaxKind.AtToken; -} - -/** - * Return true if node is `EqualsToken` - * @param node A TypeScript node - */ -export function isEqualsToken(node: ts.Node): node is ts.EqualsToken { - return node.kind === ts.SyntaxKind.EqualsToken; -} - -/** - * Return true if node is `Identifier` - * @param node A TypeScript node - */ -export function isIdentifier(node: ts.Node): node is ts.Identifier { - return node.kind === ts.SyntaxKind.Identifier; -} - -/** - * Return true if node is `QualifiedName` - * @param node A TypeScript node - */ -export function isQualifiedName(node: ts.Node): node is ts.QualifiedName { - return node.kind === ts.SyntaxKind.QualifiedName; -} - -/** - * Return true if node is `ComputedPropertyName` - * @param node A TypeScript node - */ -export function isComputedPropertyName(node: ts.Node): node is ts.ComputedPropertyName { - return node.kind === ts.SyntaxKind.ComputedPropertyName; -} - -/** - * Return true if node is `Decorator` - * @param node A TypeScript node - */ -export function isDecorator(node: ts.Node): node is ts.Decorator { - return node.kind === ts.SyntaxKind.Decorator; -} - -/** - * Return true if node is `PropertySignature` - * @param node A TypeScript node - */ -export function isPropertySignature(node: ts.Node): node is ts.PropertySignature { - return node.kind === ts.SyntaxKind.PropertySignature; -} - -/** - * Return true if node is `PropertyDeclaration` - * @param node A TypeScript node - */ -export function isPropertyDeclaration(node: ts.Node): node is ts.PropertyDeclaration { - return node.kind === ts.SyntaxKind.PropertyDeclaration; -} - -/** - * Return true if node is `MethodSignature` - * @param node A TypeScript node - */ -export function isMethodSignature(node: ts.Node): node is ts.MethodSignature { - return node.kind === ts.SyntaxKind.MethodSignature; -} - -/** - * Return true if node is `MethodDeclaration` - * @param node A TypeScript node - */ -export function isMethodDeclaration(node: ts.Node): node is ts.MethodDeclaration { - return node.kind === ts.SyntaxKind.MethodDeclaration; -} - -/** - * Return true if node is `ObjectBindingPattern` - * @param node A TypeScript node - */ -export function isObjectBindingPattern(node: ts.Node): node is ts.ObjectBindingPattern { - return node.kind === ts.SyntaxKind.ObjectBindingPattern; -} - -/** - * Return true if node is `ArrayBindingPattern` - * @param node A TypeScript node - */ -export function isArrayBindingPattern(node: ts.Node): node is ts.ArrayBindingPattern { - return node.kind === ts.SyntaxKind.ArrayBindingPattern; -} - -/** - * Return true if node is `BindingElement` - * @param node A TypeScript node - */ -export function isBindingElement(node: ts.Node): node is ts.BindingElement { - return node.kind === ts.SyntaxKind.BindingElement; -} - -/** - * Return true if node is `ArrayLiteralExpression` - * @param node A TypeScript node - */ -export function isArrayLiteralExpression(node: ts.Node): node is ts.ArrayLiteralExpression { - return node.kind === ts.SyntaxKind.ArrayLiteralExpression; -} - -/** - * Return true if node is `ObjectLiteralExpression` - * @param node A TypeScript node - */ -export function isObjectLiteralExpression(node: ts.Node): node is ts.ObjectLiteralExpression { - return node.kind === ts.SyntaxKind.ObjectLiteralExpression; -} - -/** - * Return true if node is `PropertyAccessExpression` - * @param node A TypeScript node - */ -export function isPropertyAccessExpression(node: ts.Node): node is ts.PropertyAccessExpression { - return node.kind === ts.SyntaxKind.PropertyAccessExpression; -} - -/** - * Return true if node is `ElementAccessExpression` - * @param node A TypeScript node - */ -export function isElementAccessExpression(node: ts.Node): node is ts.ElementAccessExpression { - return node.kind === ts.SyntaxKind.ElementAccessExpression; -} - -/** - * Return true if node is `CallExpression` - * @param node A TypeScript node - */ -export function isCallExpression(node: ts.Node): node is ts.CallExpression { - return node.kind === ts.SyntaxKind.CallExpression; -} - -/** - * Return true if node is `NewExpression` - * @param node A TypeScript node - */ -export function isNewExpression(node: ts.Node): node is ts.NewExpression { - return node.kind === ts.SyntaxKind.NewExpression; -} - -/** - * Return true if node is `TaggedTemplateExpression` - * @param node A TypeScript node - */ -export function isTaggedTemplateExpression(node: ts.Node): node is ts.TaggedTemplateExpression { - return node.kind === ts.SyntaxKind.TaggedTemplateExpression; -} - -/** - * Return true if node is `ParenthesizedExpression` - * @param node A TypeScript node - */ -export function isParenthesizedExpression(node: ts.Node): node is ts.ParenthesizedExpression { - return node.kind === ts.SyntaxKind.ParenthesizedExpression; -} - -/** - * Return true if node is `FunctionExpression` - * @param node A TypeScript node - */ -export function isFunctionExpression(node: ts.Node): node is ts.FunctionExpression { - return node.kind === ts.SyntaxKind.FunctionExpression; -} - -/** - * Return true if node is `ArrowFunction` - * @param node A TypeScript node - */ -export function isArrowFunction(node: ts.Node): node is ts.ArrowFunction { - return node.kind === ts.SyntaxKind.ArrowFunction; -} - -/** - * Return true if node is `DeleteExpression` - * @param node A TypeScript node - */ -export function isDeleteExpression(node: ts.Node): node is ts.DeleteExpression { - return node.kind === ts.SyntaxKind.DeleteExpression; -} - -/** - * Return true if node is `TypeOfExpression` - * @param node A TypeScript node - */ -export function isTypeOfExpression(node: ts.Node): node is ts.TypeOfExpression { - return node.kind === ts.SyntaxKind.TypeOfExpression; -} - -/** - * Return true if node is `VoidExpression` - * @param node A TypeScript node - */ -export function isVoidExpression(node: ts.Node): node is ts.VoidExpression { - return node.kind === ts.SyntaxKind.VoidExpression; -} - -/** - * Return true if node is `AwaitExpression` - * @param node A TypeScript node - */ -export function isAwaitExpression(node: ts.Node): node is ts.AwaitExpression { - return node.kind === ts.SyntaxKind.AwaitExpression; -} - -/** - * Return true if node is `PrefixUnaryExpression` - * @param node A TypeScript node - */ -export function isPrefixUnaryExpression(node: ts.Node): node is ts.PrefixUnaryExpression { - return node.kind === ts.SyntaxKind.PrefixUnaryExpression; -} - -/** - * Return true if node is `PostfixUnaryExpression` - * @param node A TypeScript node - */ -export function isPostfixUnaryExpression(node: ts.Node): node is ts.PostfixUnaryExpression { - return node.kind === ts.SyntaxKind.PostfixUnaryExpression; -} - -/** - * Return true if node is `BinaryExpression` - * @param node A TypeScript node - */ -export function isBinaryExpression(node: ts.Node): node is ts.BinaryExpression { - return node.kind === ts.SyntaxKind.BinaryExpression; -} - -/** - * Return true if node is `ConditionalExpression` - * @param node A TypeScript node - */ -export function isConditionalExpression(node: ts.Node): node is ts.ConditionalExpression { - return node.kind === ts.SyntaxKind.ConditionalExpression; -} - -/** - * Return true if node is `TemplateExpression` - * @param node A TypeScript node - */ -export function isTemplateExpression(node: ts.Node): node is ts.TemplateExpression { - return node.kind === ts.SyntaxKind.TemplateExpression; -} - -/** - * Return true if node is `YieldExpression` - * @param node A TypeScript node - */ -export function isYieldExpression(node: ts.Node): node is ts.YieldExpression { - return node.kind === ts.SyntaxKind.YieldExpression; -} - -/** - * Return true if node is `SpreadElement` - * @param node A TypeScript node - */ -export function isSpreadElement(node: ts.Node): node is ts.SpreadElement { - return node.kind === ts.SyntaxKind.SpreadElement; -} - -/** - * Return true if node is `ClassExpression` - * @param node A TypeScript node - */ -export function isClassExpression(node: ts.Node): node is ts.ClassExpression { - return node.kind === ts.SyntaxKind.ClassExpression; -} - -/** - * Return true if node is `OmittedExpression` - * @param node A TypeScript node - */ -export function isOmittedExpression(node: ts.Node): node is ts.OmittedExpression { - return node.kind === ts.SyntaxKind.OmittedExpression; -} - -/** - * Return true if node is `ExpressionWithTypeArguments` - * @param node A TypeScript node - */ -export function isExpressionWithTypeArguments(node: ts.Node): node is ts.ExpressionWithTypeArguments { - return node.kind === ts.SyntaxKind.ExpressionWithTypeArguments; -} - -/** - * Return true if node is `AsExpression` - * @param node A TypeScript node - */ -export function isAsExpression(node: ts.Node): node is ts.AsExpression { - return node.kind === ts.SyntaxKind.AsExpression; -} - -/** - * Return true if node is `NonNullExpression` - * @param node A TypeScript node - */ -export function isNonNullExpression(node: ts.Node): node is ts.NonNullExpression { - return node.kind === ts.SyntaxKind.NonNullExpression; -} - -/** - * Return true if node is `MetaProperty` - * @param node A TypeScript node - */ -export function isMetaProperty(node: ts.Node): node is ts.MetaProperty { - return node.kind === ts.SyntaxKind.MetaProperty; -} - -/** - * Return true if node is `TemplateSpan` - * @param node A TypeScript node - */ -export function isTemplateSpan(node: ts.Node): node is ts.TemplateSpan { - return node.kind === ts.SyntaxKind.TemplateSpan; -} - -/** - * Return true if node is `SemicolonClassElement` - * @param node A TypeScript node - */ -export function isSemicolonClassElement(node: ts.Node): node is ts.SemicolonClassElement { - return node.kind === ts.SyntaxKind.SemicolonClassElement; -} - -/** - * Return true if node is `Block` - * @param node A TypeScript node - */ -export function isBlock(node: ts.Node): node is ts.Block { - return node.kind === ts.SyntaxKind.Block; -} - -/** - * Return true if node is `VariableStatement` - * @param node A TypeScript node - */ -export function isVariableStatement(node: ts.Node): node is ts.VariableStatement { - return node.kind === ts.SyntaxKind.VariableStatement; -} - -/** - * Return true if node is `EmptyStatement` - * @param node A TypeScript node - */ -export function isEmptyStatement(node: ts.Node): node is ts.EmptyStatement { - return node.kind === ts.SyntaxKind.EmptyStatement; -} - -/** - * Return true if node is `ExpressionStatement` - * @param node A TypeScript node - */ -export function isExpressionStatement(node: ts.Node): node is ts.ExpressionStatement { - return node.kind === ts.SyntaxKind.ExpressionStatement; -} - -/** - * Return true if node is `IfStatement` - * @param node A TypeScript node - */ -export function isIfStatement(node: ts.Node): node is ts.IfStatement { - return node.kind === ts.SyntaxKind.IfStatement; -} - -/** - * Return true if node is `DoStatement` - * @param node A TypeScript node - */ -export function isDoStatement(node: ts.Node): node is ts.DoStatement { - return node.kind === ts.SyntaxKind.DoStatement; -} - -/** - * Return true if node is `WhileStatement` - * @param node A TypeScript node - */ -export function isWhileStatement(node: ts.Node): node is ts.WhileStatement { - return node.kind === ts.SyntaxKind.WhileStatement; -} - -/** - * Return true if node is `ForStatement` - * @param node A TypeScript node - */ -export function isForStatement(node: ts.Node): node is ts.ForStatement { - return node.kind === ts.SyntaxKind.ForStatement; -} - -/** - * Return true if node is `ForInStatement` - * @param node A TypeScript node - */ -export function isForInStatement(node: ts.Node): node is ts.ForInStatement { - return node.kind === ts.SyntaxKind.ForInStatement; -} - -/** - * Return true if node is `ForOfStatement` - * @param node A TypeScript node - */ -export function isForOfStatement(node: ts.Node): node is ts.ForOfStatement { - return node.kind === ts.SyntaxKind.ForOfStatement; -} - -/** - * Return true if node is `ContinueStatement` - * @param node A TypeScript node - */ -export function isContinueStatement(node: ts.Node): node is ts.ContinueStatement { - return node.kind === ts.SyntaxKind.ContinueStatement; -} - -/** - * Return true if node is `BreakStatement` - * @param node A TypeScript node - */ -export function isBreakStatement(node: ts.Node): node is ts.BreakStatement { - return node.kind === ts.SyntaxKind.BreakStatement; -} - -/** - * Return true if node is `ReturnStatement` - * @param node A TypeScript node - */ -export function isReturnStatement(node: ts.Node): node is ts.ReturnStatement { - return node.kind === ts.SyntaxKind.ReturnStatement; -} - -/** - * Return true if node is `WithStatement` - * @param node A TypeScript node - */ -export function isWithStatement(node: ts.Node): node is ts.WithStatement { - return node.kind === ts.SyntaxKind.WithStatement; -} - -/** - * Return true if node is `SwitchStatement` - * @param node A TypeScript node - */ -export function isSwitchStatement(node: ts.Node): node is ts.SwitchStatement { - return node.kind === ts.SyntaxKind.SwitchStatement; -} - -/** - * Return true if node is `LabeledStatement` - * @param node A TypeScript node - */ -export function isLabeledStatement(node: ts.Node): node is ts.LabeledStatement { - return node.kind === ts.SyntaxKind.LabeledStatement; -} - -/** - * Return true if node is `ThrowStatement` - * @param node A TypeScript node - */ -export function isThrowStatement(node: ts.Node): node is ts.ThrowStatement { - return node.kind === ts.SyntaxKind.ThrowStatement; -} - -/** - * Return true if node is `TryStatement` - * @param node A TypeScript node - */ -export function isTryStatement(node: ts.Node): node is ts.TryStatement { - return node.kind === ts.SyntaxKind.TryStatement; -} - -/** - * Return true if node is `DebuggerStatement` - * @param node A TypeScript node - */ -export function isDebuggerStatement(node: ts.Node): node is ts.DebuggerStatement { - return node.kind === ts.SyntaxKind.DebuggerStatement; -} - -/** - * Return true if node is `VariableDeclaration` - * @param node A TypeScript node - */ -export function isVariableDeclaration(node: ts.Node): node is ts.VariableDeclaration { - return node.kind === ts.SyntaxKind.VariableDeclaration; -} - -/** - * Return true if node is `VariableDeclarationList` - * @param node A TypeScript node - */ -export function isVariableDeclarationList(node: ts.Node): node is ts.VariableDeclarationList { - return node.kind === ts.SyntaxKind.VariableDeclarationList; -} - -/** - * Return true if node is `FunctionDeclaration` - * @param node A TypeScript node - */ -export function isFunctionDeclaration(node: ts.Node): node is ts.FunctionDeclaration { - return node.kind === ts.SyntaxKind.FunctionDeclaration; -} - -/** - * Return true if node is `ClassDeclaration` - * @param node A TypeScript node - */ -export function isClassDeclaration(node: ts.Node): node is ts.ClassDeclaration { - return node.kind === ts.SyntaxKind.ClassDeclaration; -} - -/** - * Return true if node is `InterfaceDeclaration` - * @param node A TypeScript node - */ -export function isInterfaceDeclaration(node: ts.Node): node is ts.InterfaceDeclaration { - return node.kind === ts.SyntaxKind.InterfaceDeclaration; -} - -/** - * Return true if node is `TypeAliasDeclaration` - * @param node A TypeScript node - */ -export function isTypeAliasDeclaration(node: ts.Node): node is ts.TypeAliasDeclaration { - return node.kind === ts.SyntaxKind.TypeAliasDeclaration; -} - -/** - * Return true if node is `EnumDeclaration` - * @param node A TypeScript node - */ -export function isEnumDeclaration(node: ts.Node): node is ts.EnumDeclaration { - return node.kind === ts.SyntaxKind.EnumDeclaration; -} - -/** - * Return true if node is `ModuleDeclaration` - * @param node A TypeScript node - */ -export function isModuleDeclaration(node: ts.Node): node is ts.ModuleDeclaration { - return node.kind === ts.SyntaxKind.ModuleDeclaration; -} - -/** - * Return true if node is `ModuleBlock` - * @param node A TypeScript node - */ -export function isModuleBlock(node: ts.Node): node is ts.ModuleBlock { - return node.kind === ts.SyntaxKind.ModuleBlock; -} - -/** - * Return true if node is `CaseBlock` - * @param node A TypeScript node - */ -export function isCaseBlock(node: ts.Node): node is ts.CaseBlock { - return node.kind === ts.SyntaxKind.CaseBlock; -} - -/** - * Return true if node is `NamespaceExportDeclaration` - * @param node A TypeScript node - */ -export function isNamespaceExportDeclaration(node: ts.Node): node is ts.NamespaceExportDeclaration { - return node.kind === ts.SyntaxKind.NamespaceExportDeclaration; -} - -/** - * Return true if node is `ImportEqualsDeclaration` - * @param node A TypeScript node - */ -export function isImportEqualsDeclaration(node: ts.Node): node is ts.ImportEqualsDeclaration { - return node.kind === ts.SyntaxKind.ImportEqualsDeclaration; -} - -/** - * Return true if node is `ImportDeclaration` - * @param node A TypeScript node - */ -export function isImportDeclaration(node: ts.Node): node is ts.ImportDeclaration { - return node.kind === ts.SyntaxKind.ImportDeclaration; -} - -/** - * Return true if node is `ImportClause` - * @param node A TypeScript node - */ -export function isImportClause(node: ts.Node): node is ts.ImportClause { - return node.kind === ts.SyntaxKind.ImportClause; -} - -/** - * Return true if node is `NamespaceImport` - * @param node A TypeScript node - */ -export function isNamespaceImport(node: ts.Node): node is ts.NamespaceImport { - return node.kind === ts.SyntaxKind.NamespaceImport; -} - -/** - * Return true if node is `NamedImports` - * @param node A TypeScript node - */ -export function isNamedImports(node: ts.Node): node is ts.NamedImports { - return node.kind === ts.SyntaxKind.NamedImports; -} - -/** - * Return true if node is `ImportSpecifier` - * @param node A TypeScript node - */ -export function isImportSpecifier(node: ts.Node): node is ts.ImportSpecifier { - return node.kind === ts.SyntaxKind.ImportSpecifier; -} - -/** - * Return true if node is `ExportAssignment` - * @param node A TypeScript node - */ -export function isExportAssignment(node: ts.Node): node is ts.ExportAssignment { - return node.kind === ts.SyntaxKind.ExportAssignment; -} - -/** - * Return true if node is `ExportDeclaration` - * @param node A TypeScript node - */ -export function isExportDeclaration(node: ts.Node): node is ts.ExportDeclaration { - return node.kind === ts.SyntaxKind.ExportDeclaration; -} - -/** - * Return true if node is `NamedExports` - * @param node A TypeScript node - */ -export function isNamedExports(node: ts.Node): node is ts.NamedExports { - return node.kind === ts.SyntaxKind.NamedExports; -} - -/** - * Return true if node is `ExportSpecifier` - * @param node A TypeScript node - */ -export function isExportSpecifier(node: ts.Node): node is ts.ExportSpecifier { - return node.kind === ts.SyntaxKind.ExportSpecifier; -} - -/** - * Return true if node is `MissingDeclaration` - * @param node A TypeScript node - */ -export function isMissingDeclaration(node: ts.Node): node is ts.MissingDeclaration { - return node.kind === ts.SyntaxKind.MissingDeclaration; -} - -/** - * Return true if node is `ExternalModuleReference` - * @param node A TypeScript node - */ -export function isExternalModuleReference(node: ts.Node): node is ts.ExternalModuleReference { - return node.kind === ts.SyntaxKind.ExternalModuleReference; -} - -/** - * Return true if node is `JsxElement` - * @param node A TypeScript node - */ -export function isJsxElement(node: ts.Node): node is ts.JsxElement { - return node.kind === ts.SyntaxKind.JsxElement; -} - -/** - * Return true if node is `JsxSelfClosingElement` - * @param node A TypeScript node - */ -export function isJsxSelfClosingElement(node: ts.Node): node is ts.JsxSelfClosingElement { - return node.kind === ts.SyntaxKind.JsxSelfClosingElement; -} - -/** - * Return true if node is `JsxOpeningElement` - * @param node A TypeScript node - */ -export function isJsxOpeningElement(node: ts.Node): node is ts.JsxOpeningElement { - return node.kind === ts.SyntaxKind.JsxOpeningElement; -} - -/** - * Return true if node is `JsxClosingElement` - * @param node A TypeScript node - */ -export function isJsxClosingElement(node: ts.Node): node is ts.JsxClosingElement { - return node.kind === ts.SyntaxKind.JsxClosingElement; -} - -/** - * Return true if node is `JsxAttribute` - * @param node A TypeScript node - */ -export function isJsxAttribute(node: ts.Node): node is ts.JsxAttribute { - return node.kind === ts.SyntaxKind.JsxAttribute; -} - -/** - * Return true if node is `JsxAttributes` - * @param node A TypeScript node - */ -export function isJsxAttributes(node: ts.Node): node is ts.JsxAttributes { - return node.kind === ts.SyntaxKind.JsxAttributes; -} - -/** - * Return true if node is `JsxSpreadAttribute` - * @param node A TypeScript node - */ -export function isJsxSpreadAttribute(node: ts.Node): node is ts.JsxSpreadAttribute { - return node.kind === ts.SyntaxKind.JsxSpreadAttribute; -} - -/** - * Return true if node is `JsxExpression` - * @param node A TypeScript node - */ -export function isJsxExpression(node: ts.Node): node is ts.JsxExpression { - return node.kind === ts.SyntaxKind.JsxExpression; -} - -/** - * Return true if node is `CaseClause` - * @param node A TypeScript node - */ -export function isCaseClause(node: ts.Node): node is ts.CaseClause { - return node.kind === ts.SyntaxKind.CaseClause; -} - -/** - * Return true if node is `DefaultClause` - * @param node A TypeScript node - */ -export function isDefaultClause(node: ts.Node): node is ts.DefaultClause { - return node.kind === ts.SyntaxKind.DefaultClause; -} - -/** - * Return true if node is `HeritageClause` - * @param node A TypeScript node - */ -export function isHeritageClause(node: ts.Node): node is ts.HeritageClause { - return node.kind === ts.SyntaxKind.HeritageClause; -} - -/** - * Return true if node is `CatchClause` - * @param node A TypeScript node - */ -export function isCatchClause(node: ts.Node): node is ts.CatchClause { - return node.kind === ts.SyntaxKind.CatchClause; -} - -/** - * Return true if node is `PropertyAssignment` - * @param node A TypeScript node - */ -export function isPropertyAssignment(node: ts.Node): node is ts.PropertyAssignment { - return node.kind === ts.SyntaxKind.PropertyAssignment; -} - -/** - * Return true if node is `ShorthandPropertyAssignment` - * @param node A TypeScript node - */ -export function isShorthandPropertyAssignment(node: ts.Node): node is ts.ShorthandPropertyAssignment { - return node.kind === ts.SyntaxKind.ShorthandPropertyAssignment; -} - -/** - * Return true if node is `SpreadAssignment` - * @param node A TypeScript node - */ -export function isSpreadAssignment(node: ts.Node): node is ts.SpreadAssignment { - return node.kind === ts.SyntaxKind.SpreadAssignment; -} - -/** - * Return true if node is `EnumMember` - * @param node A TypeScript node - */ -export function isEnumMember(node: ts.Node): node is ts.EnumMember { - return node.kind === ts.SyntaxKind.EnumMember; -} - -/** - * Return true if node is `SourceFile` - * @param node A TypeScript node - */ -export function isSourceFile(node: ts.Node): node is ts.SourceFile { - return node.kind === ts.SyntaxKind.SourceFile; -} - -/** - * Return true if node is `Bundle` - * @param node A TypeScript node - */ -export function isBundle(node: ts.Node): node is ts.Bundle { - return node.kind === ts.SyntaxKind.Bundle; -} - -/** - * Return true if node is `JSDocTypeExpression` - * @param node A TypeScript node - */ -export function isJSDocTypeExpression(node: ts.Node): node is ts.JSDocTypeExpression { - return node.kind === ts.SyntaxKind.JSDocTypeExpression; -} - -/** - * Return true if node is `JSDocAllType` - * @param node A TypeScript node - */ -export function isJSDocAllType(node: ts.Node): node is ts.JSDocAllType { - return node.kind === ts.SyntaxKind.JSDocAllType; -} - -/** - * Return true if node is `JSDocUnknownType` - * @param node A TypeScript node - */ -export function isJSDocUnknownType(node: ts.Node): node is ts.JSDocUnknownType { - return node.kind === ts.SyntaxKind.JSDocUnknownType; -} - -/** - * Return true if node is `JSDocArrayType` - * @param node A TypeScript node - */ -export function isJSDocArrayType(node: ts.Node): node is ts.JSDocArrayType { - return node.kind === ts.SyntaxKind.JSDocArrayType; -} - -/** - * Return true if node is `JSDocUnionType` - * @param node A TypeScript node - */ -export function isJSDocUnionType(node: ts.Node): node is ts.JSDocUnionType { - return node.kind === ts.SyntaxKind.JSDocUnionType; -} - -/** - * Return true if node is `JSDocTupleType` - * @param node A TypeScript node - */ -export function isJSDocTupleType(node: ts.Node): node is ts.JSDocTupleType { - return node.kind === ts.SyntaxKind.JSDocTupleType; -} - -/** - * Return true if node is `JSDocNullableType` - * @param node A TypeScript node - */ -export function isJSDocNullableType(node: ts.Node): node is ts.JSDocNullableType { - return node.kind === ts.SyntaxKind.JSDocNullableType; -} - -/** - * Return true if node is `JSDocNonNullableType` - * @param node A TypeScript node - */ -export function isJSDocNonNullableType(node: ts.Node): node is ts.JSDocNonNullableType { - return node.kind === ts.SyntaxKind.JSDocNonNullableType; -} - -/** - * Return true if node is `JSDocRecordType` - * @param node A TypeScript node - */ -export function isJSDocRecordType(node: ts.Node): node is ts.JSDocRecordType { - return node.kind === ts.SyntaxKind.JSDocRecordType; -} - -/** - * Return true if node is `JSDocRecordMember` - * @param node A TypeScript node - */ -export function isJSDocRecordMember(node: ts.Node): node is ts.JSDocRecordMember { - return node.kind === ts.SyntaxKind.JSDocRecordMember; -} - -/** - * Return true if node is `JSDocTypeReference` - * @param node A TypeScript node - */ -export function isJSDocTypeReference(node: ts.Node): node is ts.JSDocTypeReference { - return node.kind === ts.SyntaxKind.JSDocTypeReference; -} - -/** - * Return true if node is `JSDocOptionalType` - * @param node A TypeScript node - */ -export function isJSDocOptionalType(node: ts.Node): node is ts.JSDocOptionalType { - return node.kind === ts.SyntaxKind.JSDocOptionalType; -} - -/** - * Return true if node is `JSDocFunctionType` - * @param node A TypeScript node - */ -export function isJSDocFunctionType(node: ts.Node): node is ts.JSDocFunctionType { - return node.kind === ts.SyntaxKind.JSDocFunctionType; -} - -/** - * Return true if node is `JSDocVariadicType` - * @param node A TypeScript node - */ -export function isJSDocVariadicType(node: ts.Node): node is ts.JSDocVariadicType { - return node.kind === ts.SyntaxKind.JSDocVariadicType; -} - -/** - * Return true if node is `JSDocConstructorType` - * @param node A TypeScript node - */ -export function isJSDocConstructorType(node: ts.Node): node is ts.JSDocConstructorType { - return node.kind === ts.SyntaxKind.JSDocConstructorType; -} - -/** - * Return true if node is `JSDocThisType` - * @param node A TypeScript node - */ -export function isJSDocThisType(node: ts.Node): node is ts.JSDocThisType { - return node.kind === ts.SyntaxKind.JSDocThisType; -} - -/** - * Return true if node is `JSDocTag` - * @param node A TypeScript node - */ -export function isJSDocTag(node: ts.Node): node is ts.JSDocTag { - return node.kind === ts.SyntaxKind.JSDocTag; -} - -/** - * Return true if node is `JSDocAugmentsTag` - * @param node A TypeScript node - */ -export function isJSDocAugmentsTag(node: ts.Node): node is ts.JSDocAugmentsTag { - return node.kind === ts.SyntaxKind.JSDocAugmentsTag; -} - -/** - * Return true if node is `JSDocParameterTag` - * @param node A TypeScript node - */ -export function isJSDocParameterTag(node: ts.Node): node is ts.JSDocParameterTag { - return node.kind === ts.SyntaxKind.JSDocParameterTag; -} - -/** - * Return true if node is `JSDocReturnTag` - * @param node A TypeScript node - */ -export function isJSDocReturnTag(node: ts.Node): node is ts.JSDocReturnTag { - return node.kind === ts.SyntaxKind.JSDocReturnTag; -} - -/** - * Return true if node is `JSDocTypeTag` - * @param node A TypeScript node - */ -export function isJSDocTypeTag(node: ts.Node): node is ts.JSDocTypeTag { - return node.kind === ts.SyntaxKind.JSDocTypeTag; -} - -/** - * Return true if node is `JSDocTemplateTag` - * @param node A TypeScript node - */ -export function isJSDocTemplateTag(node: ts.Node): node is ts.JSDocTemplateTag { - return node.kind === ts.SyntaxKind.JSDocTemplateTag; -} - -/** - * Return true if node is `JSDocTypedefTag` - * @param node A TypeScript node - */ -export function isJSDocTypedefTag(node: ts.Node): node is ts.JSDocTypedefTag { - return node.kind === ts.SyntaxKind.JSDocTypedefTag; -} - -/** - * Return true if node is `JSDocPropertyTag` - * @param node A TypeScript node - */ -export function isJSDocPropertyTag(node: ts.Node): node is ts.JSDocPropertyTag { - return node.kind === ts.SyntaxKind.JSDocPropertyTag; -} - -/** - * Return true if node is `JSDocTypeLiteral` - * @param node A TypeScript node - */ -export function isJSDocTypeLiteral(node: ts.Node): node is ts.JSDocTypeLiteral { - return node.kind === ts.SyntaxKind.JSDocTypeLiteral; -} - -/** - * Return true if node is `JSDocLiteralType` - * @param node A TypeScript node - */ -export function isJSDocLiteralType(node: ts.Node): node is ts.JSDocLiteralType { - return node.kind === ts.SyntaxKind.JSDocLiteralType; -} - -/** - * Return true if node is `SyntaxList` - * @param node A TypeScript node - */ -export function isSyntaxList(node: ts.Node): node is ts.SyntaxList { - return node.kind === ts.SyntaxKind.SyntaxList; -} - -/** - * Return true if node is `NotEmittedStatement` - * @param node A TypeScript node - */ -export function isNotEmittedStatement(node: ts.Node): node is ts.NotEmittedStatement { - return node.kind === ts.SyntaxKind.NotEmittedStatement; -} - -/** - * Return true if node is `PartiallyEmittedExpression` - * @param node A TypeScript node - */ -export function isPartiallyEmittedExpression(node: ts.Node): node is ts.PartiallyEmittedExpression { - return node.kind === ts.SyntaxKind.PartiallyEmittedExpression; -} - -/** - * Return true if node is `IntersectionTypeNode` - * @param node A TypeScript node - */ -export function isIntersectionTypeNode(node: ts.TypeNode): node is ts.IntersectionTypeNode { - return node.kind === ts.SyntaxKind.IntersectionType; -} - -/** - * Return true if node is `LiteralTypeNode` - * @param node A TypeScript node - */ -export function isTypeLiteralNode(node: ts.TypeNode): node is ts.LiteralTypeNode { - return node.kind === ts.SyntaxKind.TypeLiteral; -} - -/** - * Return true if node is `GetAccessorDeclaration` - * @param node A TypeScript node - */ -export function isGetAccessorDeclaration(node: ts.Node): node is ts.GetAccessorDeclaration { - return node.kind === ts.SyntaxKind.GetAccessor; -} diff --git a/src/index.ts b/src/index.ts index 5d6d524..5c26c27 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,50 +1,46 @@ import * as ts from 'typescript'; +import * as prettier from 'prettier'; -import { compile } from './compiler'; -import { - reactJSMakePropsAndStateInterfaceTransformFactoryFactory, -} from './transforms/react-js-make-props-and-state-transform'; -import { - reactHoistGenericsTransformFactoryFactory, -} from './transforms/react-hoist-generics-transform'; -import { - reactRemovePropTypesAssignmentTransformFactoryFactory, -} from './transforms/react-remove-prop-types-assignment-transform'; -import { - reactMovePropTypesToClassTransformFactoryFactory, -} from './transforms/react-move-prop-types-to-class-transform'; -import { - collapseIntersectionInterfacesTransformFactoryFactory, -} from './transforms/collapse-intersection-interfaces-transform'; -import { - reactRemoveStaticPropTypesMemberTransformFactoryFactory, -} from './transforms/react-remove-static-prop-types-member-transform'; +import { compile, CompilationOptions, DEFAULT_COMPILATION_OPTIONS } from './compiler'; +import { reactJSMakePropsAndStateInterfaceTransformFactoryFactory } from './transforms/react-js-make-props-and-state-transform'; +import { reactRemovePropTypesAssignmentTransformFactoryFactory } from './transforms/react-remove-prop-types-assignment-transform'; +import { reactMovePropTypesToClassTransformFactoryFactory } from './transforms/react-move-prop-types-to-class-transform'; +import { collapseIntersectionInterfacesTransformFactoryFactory } from './transforms/collapse-intersection-interfaces-transform'; +import { reactRemoveStaticPropTypesMemberTransformFactoryFactory } from './transforms/react-remove-static-prop-types-member-transform'; +import { reactStatelessFunctionMakePropsTransformFactoryFactory } from './transforms/react-stateless-function-make-props-transform'; +import { reactRemovePropTypesImportTransformFactoryFactory } from './transforms/react-remove-prop-types-import'; export { reactMovePropTypesToClassTransformFactoryFactory, reactJSMakePropsAndStateInterfaceTransformFactoryFactory, - reactHoistGenericsTransformFactoryFactory, + reactStatelessFunctionMakePropsTransformFactoryFactory, collapseIntersectionInterfacesTransformFactoryFactory, reactRemovePropTypesAssignmentTransformFactoryFactory, reactRemoveStaticPropTypesMemberTransformFactoryFactory, + reactRemovePropTypesImportTransformFactoryFactory, compile, }; export const allTransforms = [ reactMovePropTypesToClassTransformFactoryFactory, reactJSMakePropsAndStateInterfaceTransformFactoryFactory, - reactHoistGenericsTransformFactoryFactory, + reactStatelessFunctionMakePropsTransformFactoryFactory, collapseIntersectionInterfacesTransformFactoryFactory, reactRemovePropTypesAssignmentTransformFactoryFactory, reactRemoveStaticPropTypesMemberTransformFactoryFactory, + reactRemovePropTypesImportTransformFactoryFactory, ]; -export type TransformFactoryFactory = (typeChecker: ts.TypeChecker) => ts.TransformerFactory; +export type TransformFactoryFactory = (typeChecker: ts.TypeChecker) => ts.TransformerFactory; /** * Run React JavaScript to TypeScript transform for file at `filePath` * @param filePath */ -export function run(filePath: string): string { - return compile(filePath, allTransforms); -} \ No newline at end of file +export function run( + filePath: string, + prettierOptions: prettier.Options = {}, + compilationOptions: CompilationOptions = DEFAULT_COMPILATION_OPTIONS, +): string { + return compile(filePath, allTransforms, prettierOptions, compilationOptions); +} diff --git a/src/transforms/collapse-intersection-interfaces-transform.ts b/src/transforms/collapse-intersection-interfaces-transform.ts index f04a9d1..cca02a4 100644 --- a/src/transforms/collapse-intersection-interfaces-transform.ts +++ b/src/transforms/collapse-intersection-interfaces-transform.ts @@ -1,4 +1,5 @@ import * as ts from 'typescript'; +import * as _ from 'lodash'; import * as helpers from '../helpers'; @@ -13,8 +14,8 @@ import * as helpers from '../helpers'; * type Foo = {foo: string; bar: number;} */ export function collapseIntersectionInterfacesTransformFactoryFactory( - typeChecker: ts.TypeChecker, - ): ts.TransformerFactory { + typeChecker: ts.TypeChecker, +): ts.TransformerFactory { return function collapseIntersectionInterfacesTransformFactory(context: ts.TransformationContext) { return function collapseIntersectionInterfacesTransform(sourceFile: ts.SourceFile) { const visited = ts.visitEachChild(sourceFile, visitor, context); @@ -23,7 +24,7 @@ export function collapseIntersectionInterfacesTransformFactoryFactory( return visited; function visitor(node: ts.Node) { - if (helpers.isTypeAliasDeclaration(node)) { + if (ts.isTypeAliasDeclaration(node)) { return visitTypeAliasDeclaration(node); } @@ -31,26 +32,121 @@ export function collapseIntersectionInterfacesTransformFactoryFactory( } function visitTypeAliasDeclaration(node: ts.TypeAliasDeclaration) { - if ( - helpers.isIntersectionTypeNode(node.type) - && node.type.types.every((type) => helpers.isTypeLiteralNode(type)) - ) { - const allMembers = node.type.types - .map((type: ts.TypeLiteralNode) => type.members) - .reduce((all, members) => ts.createNodeArray(all.concat(members)), ts.createNodeArray([])); - + if (ts.isIntersectionTypeNode(node.type)) { return ts.createTypeAliasDeclaration( [], [], node.name.text, [], - ts.createTypeLiteralNode(allMembers), + visitIntersectionTypeNode(node.type), ); } return node; } - } - } -} + function visitIntersectionTypeNode(node: ts.IntersectionTypeNode) { + // Only intersection of type literals can be colapsed. + // We are currently ignoring intersections such as `{foo: string} & {bar: string} & TypeRef` + // TODO: handle mix of type references and multiple literal types + if (!node.types.every(typeNode => ts.isTypeLiteralNode(typeNode))) { + return node; + } + + // We need cast `node.type.types` to `ts.NodeArray` + // because TypeScript can't figure out `node.type.types.every(ts.isTypeLiteralNode)` + const types = node.types as ts.NodeArray; + + // Build a map of member names to all of types found in intersectioning type literals + // For instance {foo: string, bar: number} & { foo: number } will result in a map like this: + // Map { + // 'foo' => Set { 'string', 'number' }, + // 'bar' => Set { 'number' } + // } + const membersMap = new Map>(); + + // A sepecial member of type literal nodes is index signitures which don't have a name + // We use this symbol to track it in our members map + const INDEX_SIGNITUTRE_MEMBER = Symbol('Index signiture member'); + + // Keep a reference of first index signiture member parameters. (ignore rest) + let indexMemberParameter: ts.NodeArray | null = null; + + // Iterate through all of type literal nodes members and add them to the members map + types.forEach(typeNode => { + typeNode.members.forEach(member => { + if (ts.isIndexSignatureDeclaration(member)) { + if (member.type !== undefined) { + if (membersMap.has(INDEX_SIGNITUTRE_MEMBER)) { + membersMap.get(INDEX_SIGNITUTRE_MEMBER)!.add(member.type); + } else { + indexMemberParameter = member.parameters; + membersMap.set(INDEX_SIGNITUTRE_MEMBER, new Set([member.type])); + } + } + } else if (ts.isPropertySignature(member)) { + if (member.type !== undefined) { + let memberName = member.name.getText(sourceFile); + + // For unknown reasons, member.name.getText() is returning nothing in some cases + // This is probably because previous transformers did something with the AST that + // index of text string of member identifier is lost + // TODO: investigate + if (!memberName) { + memberName = (member.name as any).escapedText; + } + + if (membersMap.has(memberName)) { + membersMap.get(memberName)!.add(member.type); + } else { + membersMap.set(memberName, new Set([member.type])); + } + } + } + }); + }); + + // Result type literal members list + const finalMembers: Array = []; + + // Put together the map into a type literal that has member per each map entery and type of that + // member is a union of all types in vlues for that member name in members map + // if a member has only one type, create a simple type literal for it + for (const [name, types] of membersMap.entries()) { + if (typeof name === 'symbol') { + continue; + } + // if for this name there is only one type found use the first type, otherwise make a union of all types + let resultType = types.size === 1 ? Array.from(types)[0] : createUnionType(Array.from(types)); + + finalMembers.push(ts.createPropertySignature([], name, undefined, resultType, undefined)); + } + + // Handle index signiture member + if (membersMap.has(INDEX_SIGNITUTRE_MEMBER)) { + const indexTypes = Array.from(membersMap.get(INDEX_SIGNITUTRE_MEMBER)!); + let indexType = indexTypes[0]; + if (indexTypes.length > 1) { + indexType = createUnionType(indexTypes); + } + const indexSigniture = ts.createIndexSignature([], [], indexMemberParameter!, indexType); + finalMembers.push(indexSigniture); + } + + // Generate one single type literal node + return ts.createTypeLiteralNode(finalMembers); + } + + /** + * Create a union type from multiple type nodes + * @param types + */ + function createUnionType(types: ts.TypeNode[]) { + // first dedupe literal types + // TODO: this only works if all types are primitive types like string or number + const uniqueTypes = _.uniqBy(types, type => type.kind); + return ts.createUnionOrIntersectionTypeNode(ts.SyntaxKind.UnionType, uniqueTypes); + } + }; + }; +} diff --git a/src/transforms/react-hoist-generics-transform.ts b/src/transforms/react-hoist-generics-transform.ts deleted file mode 100644 index b2aa4c3..0000000 --- a/src/transforms/react-hoist-generics-transform.ts +++ /dev/null @@ -1,121 +0,0 @@ -import * as ts from 'typescript'; - -import * as helpers from '../helpers'; - -/** - * Hoist generics to top of a class declarations in a React component - * - * @example - * Before: - * class SomeComponent extends React.Component<{foo: number;}, {bar: string;}> {} - * - * After - * type SomeComponentProps = {foo: number;}; - * type SomeComponentState = {bar: string;}; - * class SomeComponent extends React.Component {} - */ -export function reactHoistGenericsTransformFactoryFactory(typeChecker: ts.TypeChecker): ts.TransformerFactory { - return function reactHoistGenericsTransformFactory(context: ts.TransformationContext) { - return function reactHoistGenericsTransform(node: ts.SourceFile) { - return visitSourceFile(node); - }; - }; - - function visitSourceFile(sourceFile: ts.SourceFile) { - - for (const statement of sourceFile.statements) { - if (helpers.isClassDeclaration(statement) && helpers.isReactComponent(statement, typeChecker)) { - return hoist(statement, sourceFile); - } - } - - return sourceFile; - } -} - -/** - * Hoist props and state generic types - * @param reactClass - * @param sourceFile - */ -function hoist(reactClass: ts.ClassDeclaration, sourceFile: ts.SourceFile) { - if (!reactClass.heritageClauses) { - return reactClass; - } - const className = reactClass && reactClass.name && reactClass.name.getText(sourceFile); - const reactHeritageClauses = helpers.find(reactClass.heritageClauses, helpers.isReactHeritageClause); - - if (reactHeritageClauses === undefined || !reactHeritageClauses.types == undefined) { - return reactClass; - } - const [reactType] = reactHeritageClauses.types; - if (reactType.typeArguments === undefined || reactType.typeArguments.length < 2) { - return reactClass; - } - - const [propType, stateType] = reactType.typeArguments; - const propTypeName = `${className}Props`; - const stateTypeName = `${className}State`; - const propTypeDeclaration = ts.createTypeAliasDeclaration([], [], propTypeName, [], propType); - const stateTypeDeclaration = ts.createTypeAliasDeclaration([], [], stateTypeName, [], stateType); - const propTypeRef = ts.createTypeReferenceNode(propTypeName, []); - const stateTypeRef = ts.createTypeReferenceNode(stateTypeName, []); - const newClassStatement = insertTypeRefs(reactClass, propTypeRef, stateTypeRef); - - let statements = helpers.insertBefore(sourceFile.statements, reactClass, propTypeDeclaration) - statements = helpers.insertAfter(statements, propTypeDeclaration, stateTypeDeclaration); - statements = helpers.replaceItem(statements, reactClass, newClassStatement); - - return ts.updateSourceFileNode(sourceFile, statements); -} - -/** - * Replace props and state types in a React component with type references - * - * @example - * input - * ``` - * class MyComp extends React.Component<{}, {}> {} - * ``` - * - * output - * ``` - * class MyComp extends React.Component {} - * ``` - * - * @param reactClassDeclaration A React class declaration - * @param propTypeRef React Props type reference - * @param stateTypeRef React State type reference - */ -function insertTypeRefs( - reactClassDeclaration: ts.ClassDeclaration, - propTypeRef: ts.TypeReferenceNode, - stateTypeRef: ts.TypeReferenceNode, -) { - if (reactClassDeclaration.heritageClauses === undefined) { - return reactClassDeclaration; - } - const reactHeritageClause = helpers.find(reactClassDeclaration.heritageClauses, helpers.isReactHeritageClause); - - if (reactHeritageClause === undefined) { - return reactClassDeclaration; - } - - const [reactExpression] = reactHeritageClause.types; - const newReactExpression = ts.updateExpressionWithTypeArguments( - reactExpression, - [propTypeRef, stateTypeRef], - reactExpression.expression, - ); - const newHeritageClauses = ts.updateHeritageClause(reactHeritageClause, [newReactExpression]); - - return ts.updateClassDeclaration( - reactClassDeclaration, - reactClassDeclaration.decorators, - reactClassDeclaration.modifiers, - reactClassDeclaration.name, - reactClassDeclaration.typeParameters, - helpers.replaceItem(reactClassDeclaration.heritageClauses, reactHeritageClause, newHeritageClauses), - reactClassDeclaration.members, - ); -} diff --git a/src/transforms/react-js-make-props-and-state-transform.ts b/src/transforms/react-js-make-props-and-state-transform.ts index 6aa9d1d..c61d193 100644 --- a/src/transforms/react-js-make-props-and-state-transform.ts +++ b/src/transforms/react-js-make-props-and-state-transform.ts @@ -1,8 +1,8 @@ import * as ts from 'typescript'; - +import * as _ from 'lodash'; import * as helpers from '../helpers'; -export type Factory = ts.TransformerFactory; +export type Factory = ts.TransformerFactory; /** * Get transform for transforming React code originally written in JS which does not have @@ -12,288 +12,250 @@ export type Factory = ts.TransformerFactory; export function reactJSMakePropsAndStateInterfaceTransformFactoryFactory(typeChecker: ts.TypeChecker): Factory { return function reactJSMakePropsAndStateInterfaceTransformFactory(context: ts.TransformationContext) { return function reactJSMakePropsAndStateInterfaceTransform(sourceFile: ts.SourceFile) { - const visited = ts.visitEachChild(sourceFile, visitor, context); + const visited = visitSourceFile(sourceFile, typeChecker); ts.addEmitHelpers(visited, context.readEmitHelpers()); return visited; + }; + }; +} + +function visitSourceFile(sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker) { + let newSourceFile = sourceFile; + for (const statement of sourceFile.statements) { + if (ts.isClassDeclaration(statement) && helpers.isReactComponent(statement, typeChecker)) { + newSourceFile = visitReactClassDeclaration(statement, newSourceFile, typeChecker); + } + } + + return newSourceFile; +} + +function visitReactClassDeclaration( + classDeclaration: ts.ClassDeclaration, + sourceFile: ts.SourceFile, + typeChecker: ts.TypeChecker, +) { + if (!classDeclaration.heritageClauses || !classDeclaration.heritageClauses.length) { + return sourceFile; + } + const className = classDeclaration && classDeclaration.name && classDeclaration.name.getText(sourceFile); + const propType = getPropsTypeOfReactComponentClass(classDeclaration, sourceFile); + const stateType = getStateTypeOfReactComponentClass(classDeclaration, typeChecker); + const shouldMakePropTypeDeclaration = propType.members.length > 0; + const shouldMakeStateTypeDeclaration = !isStateTypeMemberEmpty(stateType); + const propTypeName = `${className}Props`; + const stateTypeName = `${className}State`; + const propTypeDeclaration = ts.createTypeAliasDeclaration([], [], propTypeName, [], propType); + const stateTypeDeclaration = ts.createTypeAliasDeclaration([], [], stateTypeName, [], stateType); + const propTypeRef = ts.createTypeReferenceNode(propTypeName, []); + const stateTypeRef = ts.createTypeReferenceNode(stateTypeName, []); + + const newClassDeclaration = getNewReactClassDeclaration( + classDeclaration, + shouldMakePropTypeDeclaration ? propTypeRef : propType, + shouldMakeStateTypeDeclaration ? stateTypeRef : stateType, + ); + + const allTypeDeclarations = []; + if (shouldMakePropTypeDeclaration) allTypeDeclarations.push(propTypeDeclaration); + if (shouldMakeStateTypeDeclaration) allTypeDeclarations.push(stateTypeDeclaration); + + let statements = helpers.insertBefore(sourceFile.statements, classDeclaration, allTypeDeclarations); + statements = helpers.replaceItem(statements, classDeclaration, newClassDeclaration); + return ts.updateSourceFileNode(sourceFile, statements); +} + +function getNewReactClassDeclaration( + classDeclaration: ts.ClassDeclaration, + propTypeRef: ts.TypeNode, + stateTypeRef: ts.TypeNode, +) { + if (!classDeclaration.heritageClauses || !classDeclaration.heritageClauses.length) { + return classDeclaration; + } + + const firstHeritageClause = classDeclaration.heritageClauses[0]; + + const newFirstHeritageClauseTypes = helpers.replaceItem( + firstHeritageClause.types, + firstHeritageClause.types[0], + ts.updateExpressionWithTypeArguments( + firstHeritageClause.types[0], + [propTypeRef, stateTypeRef], + firstHeritageClause.types[0].expression, + ), + ); + + const newHeritageClauses = helpers.replaceItem( + classDeclaration.heritageClauses, + firstHeritageClause, + ts.updateHeritageClause(firstHeritageClause, newFirstHeritageClauseTypes), + ); + + return ts.updateClassDeclaration( + classDeclaration, + classDeclaration.decorators, + classDeclaration.modifiers, + classDeclaration.name, + classDeclaration.typeParameters, + newHeritageClauses, + classDeclaration.members, + ); +} + +function getPropsTypeOfReactComponentClass( + classDeclaration: ts.ClassDeclaration, + sourceFile: ts.SourceFile, +): ts.TypeLiteralNode { + const staticPropTypesMember = _.find(classDeclaration.members, member => { + return ( + ts.isPropertyDeclaration(member) && + helpers.hasStaticModifier(member) && + helpers.isPropTypesMember(member, sourceFile) + ); + }); + + if ( + staticPropTypesMember !== undefined && + ts.isPropertyDeclaration(staticPropTypesMember) && // check to satisfy type checker + staticPropTypesMember.initializer && + ts.isObjectLiteralExpression(staticPropTypesMember.initializer) + ) { + return helpers.buildInterfaceFromPropTypeObjectLiteral(staticPropTypesMember.initializer); + } + + const staticPropTypesGetterMember = _.find(classDeclaration.members, member => { + return ( + ts.isGetAccessorDeclaration(member) && + helpers.hasStaticModifier(member) && + helpers.isPropTypesMember(member, sourceFile) + ); + }); + + if ( + staticPropTypesGetterMember !== undefined && + ts.isGetAccessorDeclaration(staticPropTypesGetterMember) // check to satisfy typechecker + ) { + const returnStatement = _.find(staticPropTypesGetterMember.body!.statements, statement => + ts.isReturnStatement(statement), + ); + if ( + returnStatement !== undefined && + ts.isReturnStatement(returnStatement) && // check to satisfy typechecker + returnStatement.expression && + ts.isObjectLiteralExpression(returnStatement.expression) + ) { + return helpers.buildInterfaceFromPropTypeObjectLiteral(returnStatement.expression); + } + } + + return ts.createTypeLiteralNode([]); +} + +function getStateTypeOfReactComponentClass( + classDeclaration: ts.ClassDeclaration, + typeChecker: ts.TypeChecker, +): ts.TypeNode { + const initialState = getInitialStateFromClassDeclaration(classDeclaration, typeChecker); + const initialStateIsVoid = initialState.kind === ts.SyntaxKind.VoidKeyword; + const collectedStateTypes = getStateLookingForSetStateCalls(classDeclaration, typeChecker); + if (!collectedStateTypes.length && initialStateIsVoid) { + return ts.createTypeLiteralNode([]); + } + if (!initialStateIsVoid) { + collectedStateTypes.push(initialState); + } + + return ts.createUnionOrIntersectionTypeNode(ts.SyntaxKind.IntersectionType, collectedStateTypes); +} - function visitor(node: ts.Node) { - if (helpers.isClassDeclaration(node)) { - return visitClassDeclaration(node); - } - - return node; - } - - function visitClassDeclaration(classDeclaration: ts.ClassDeclaration) { - if (!helpers.isReactComponent(classDeclaration, typeChecker)) { - return classDeclaration; - } - - if (!classDeclaration.heritageClauses || !classDeclaration.heritageClauses.length) { - return classDeclaration; - } - - const firstHeritageClauses = classDeclaration.heritageClauses[0]; - const expressionWithTypeArguments = firstHeritageClauses.types[0]; - - firstHeritageClauses.types[0] = ts.updateExpressionWithTypeArguments( - expressionWithTypeArguments, - [ - getPropsTypeOfReactComponentClass(classDeclaration), - getStateTypeOfReactComponentClass(classDeclaration), - ], - expressionWithTypeArguments.expression, - ) - - return ts.updateClassDeclaration( - classDeclaration, - classDeclaration.decorators, - classDeclaration.modifiers, - classDeclaration.name, - classDeclaration.typeParameters, - classDeclaration.heritageClauses, - classDeclaration.members, - ); - - function getPropsTypeOfReactComponentClass(classDeclaration: ts.ClassDeclaration): ts.TypeNode { - const staticPropTypesMember = helpers.find(classDeclaration.members, (member) => { - return helpers.isPropertyDeclaration(member) && - helpers.hasStaticModifier(member) && - helpers.isPropTypesMember(member, sourceFile); - }); - - if ( - staticPropTypesMember !== undefined - && helpers.isPropertyDeclaration(staticPropTypesMember) // check to satisfy type checker - && staticPropTypesMember.initializer - && helpers.isObjectLiteralExpression(staticPropTypesMember.initializer) - ) { - return buildInterfaceFromPropTypeObjectLiteral(staticPropTypesMember.initializer) - } - - const staticPropTypesGetterMember = helpers.find(classDeclaration.members, (member) => { - return helpers.isGetAccessorDeclaration(member) && - helpers.hasStaticModifier(member) && - helpers.isPropTypesMember(member, sourceFile); - }); - - if ( - staticPropTypesGetterMember !== undefined - && helpers.isGetAccessorDeclaration(staticPropTypesGetterMember) // check to satisfy typechecker - ) { - const returnStatement = helpers.find( - staticPropTypesGetterMember.body.statements, - (statement) => helpers.isReturnStatement(statement), - ); - if ( - returnStatement !== undefined - && helpers.isReturnStatement(returnStatement) // check to satisfy typechecker - && returnStatement.expression - && helpers.isObjectLiteralExpression(returnStatement.expression) - ) { - return buildInterfaceFromPropTypeObjectLiteral( - returnStatement.expression - ) - } - } - - return ts.createTypeLiteralNode([]); - } - - function getStateTypeOfReactComponentClass(classDeclaration: ts.ClassDeclaration): ts.TypeNode { - const initialState = getInitialStateFromClassDeclaration(classDeclaration, typeChecker); - const initialStateIsVoid = initialState.kind === ts.SyntaxKind.VoidKeyword; - const collectedStateTypes = getStateLookingForSetStateCalls(classDeclaration, typeChecker); - if (!collectedStateTypes.length && initialStateIsVoid) { - return initialState; - } - if (!initialStateIsVoid) { - collectedStateTypes.push(initialState) - } - - return ts.createUnionOrIntersectionTypeNode(ts.SyntaxKind.IntersectionType, collectedStateTypes); - } - } - - /** - * Get initial state of a React component looking for state value initially set - * @param classDeclaration - * @param typeChecker - */ - function getInitialStateFromClassDeclaration( - classDeclaration: ts.ClassDeclaration, - typeChecker: ts.TypeChecker, - ): ts.TypeNode { - // initial state class member - - const initialStateMember = helpers.find(classDeclaration.members, (member) => { - try { - return helpers.isPropertyDeclaration(member) && - member.name && - member.name.getText() === 'state'; - } catch(e) { - return false; - } - }); - - if (initialStateMember - && helpers.isPropertyDeclaration(initialStateMember) - && initialStateMember.initializer - ) { - const type = typeChecker.getTypeAtLocation(initialStateMember.initializer)! - - return typeChecker.typeToTypeNode(type); - } - - // Initial state in constructor - const constructor = helpers.find( - classDeclaration.members, - (member) => member.kind === ts.SyntaxKind.Constructor, - ) as ts.ConstructorDeclaration | undefined; - - if (constructor && constructor.body) { - for (const statement of constructor.body.statements) { - if ( - helpers.isExpressionStatement(statement) && - helpers.isBinaryExpression(statement.expression) && - statement.expression.left.getText() === 'this.state' - ) { - return typeChecker.typeToTypeNode( - typeChecker.getTypeAtLocation(statement.expression.right) - ); - } - } - } - - // No initial state, fall back to void - return ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword); - } - - /** - * Look for setState() function calls to collect the state interface in a React class component - * @param classDeclaration - * @param typeChecker - */ - function getStateLookingForSetStateCalls( - classDeclaration: ts.ClassDeclaration, - typeChecker: ts.TypeChecker, - ): ts.TypeNode[] { - const typeNodes: ts.TypeNode[] = []; - for (const member of classDeclaration.members) { - if (member && helpers.isMethodDeclaration(member) && member.body) { - lookForSetState(member.body) - } - } - - return typeNodes; - - function lookForSetState(node: ts.Node) { - ts.forEachChild(node, lookForSetState) - if ( - helpers.isExpressionStatement(node) && - helpers.isCallExpression(node.expression) && - node.expression.expression.getText().match(/setState/) - ) { - const type = typeChecker.getTypeAtLocation(node.expression.arguments[0]) - typeNodes.push(typeChecker.typeToTypeNode(type)); - } - } +/** + * Get initial state of a React component looking for state value initially set + * @param classDeclaration + * @param typeChecker + */ +function getInitialStateFromClassDeclaration( + classDeclaration: ts.ClassDeclaration, + typeChecker: ts.TypeChecker, +): ts.TypeNode { + // initial state class member + + const initialStateMember = _.find(classDeclaration.members, member => { + try { + return ts.isPropertyDeclaration(member) && member.name && member.name.getText() === 'state'; + } catch (e) { + return false; + } + }); + + if (initialStateMember && ts.isPropertyDeclaration(initialStateMember) && initialStateMember.initializer) { + const type = typeChecker.getTypeAtLocation(initialStateMember.initializer)!; + + return typeChecker.typeToTypeNode(type); + } + + // Initial state in constructor + const constructor = _.find(classDeclaration.members, member => member.kind === ts.SyntaxKind.Constructor) as + | ts.ConstructorDeclaration + | undefined; + + if (constructor && constructor.body) { + for (const statement of constructor.body.statements) { + if ( + ts.isExpressionStatement(statement) && + ts.isBinaryExpression(statement.expression) && + statement.expression.left.getText() === 'this.state' + ) { + return typeChecker.typeToTypeNode(typeChecker.getTypeAtLocation(statement.expression.right)); } + } + } - /** - * Build props interface from propTypes object - * @example - * { - * foo: React.PropTypes.string.isRequired - * } - * - * becomes - * { - * foo: string; - * } - * @param objectLiteral - */ - function buildInterfaceFromPropTypeObjectLiteral(objectLiteral: ts.ObjectLiteralExpression) { - const resultObjectLiteral = objectLiteral.properties.reduce( - (result, propertyAssignment: ts.PropertyAssignment) => { - const name = propertyAssignment.name.getText(); - if (!helpers.isPropertyAccessExpression(propertyAssignment.initializer)) { - console.warn('Bad value for propType', name, 'at', propertyAssignment.getStart()); - return result; - } - const typeValue = getTypeFromReactPropTypeExpression(propertyAssignment.initializer); - const propertySignature = ts.createPropertySignature([], name, undefined, typeValue, undefined); - result.members.push(propertySignature) - return result; - }, ts.createTypeLiteralNode([])); - - - return resultObjectLiteral; - } + // No initial state, fall back to void + return ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword); +} - /** - * Turns React.PropTypes.* into TypeScript type value - * - * @param node React propTypes value - */ - function getTypeFromReactPropTypeExpression(node: ts.PropertyAccessExpression) { - const text = node.getText().replace(/React\.PropTypes\./, ''); - let result = null; - if (/string/.test(text)) { - result = ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword); - } else if (/any/.test(text)) { - result = ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); - } else if (/array/.test(text)) { - result = ts.createArrayTypeNode(ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)); - } else if (/bool/.test(text)) { - result = ts.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword); - } else if (/number/.test(text)) { - result = ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword); - } else if (/object/.test(text)) { - result = ts.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword); - } else if (/node/.test(text)) { - result = ts.createUnionOrIntersectionTypeNode( - ts.SyntaxKind.UnionType, [ - ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword), - ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), - ts.createTypeReferenceNode('JSX.Element', []) - ] - ) - } else if (/element/.test(text)) { - result = ts.createTypeReferenceNode('JSX.Element', []); - } else if (/func/.test(text)) { - const arrayOfAny = ts.createParameter( - [], - [], - ts.createToken(ts.SyntaxKind.DotDotDotToken), - 'args', - undefined, - ts.createArrayTypeNode(ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)), - undefined, - ); - result = ts.createFunctionTypeNode( - [], - [arrayOfAny], - ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), - ); - } else { - result = ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); - } - - if (!/\.isRequired/.test(text)) { - return makeTypeNodeOptional(result); - } else { - return result; - } - - function makeTypeNodeOptional(node: ts.TypeNode) { - return ts.createUnionOrIntersectionTypeNode(ts.SyntaxKind.UnionType, [ - node, - ts.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword) - ]); - } - } - }; - }; -}; +/** + * Look for setState() function calls to collect the state interface in a React class component + * @param classDeclaration + * @param typeChecker + */ +function getStateLookingForSetStateCalls( + classDeclaration: ts.ClassDeclaration, + typeChecker: ts.TypeChecker, +): ts.TypeNode[] { + const typeNodes: ts.TypeNode[] = []; + for (const member of classDeclaration.members) { + if (member && ts.isMethodDeclaration(member) && member.body) { + lookForSetState(member.body); + } + } + + return typeNodes; + + function lookForSetState(node: ts.Node) { + ts.forEachChild(node, lookForSetState); + if ( + ts.isExpressionStatement(node) && + ts.isCallExpression(node.expression) && + node.expression.expression.getText().match(/setState/) + ) { + const type = typeChecker.getTypeAtLocation(node.expression.arguments[0]); + typeNodes.push(typeChecker.typeToTypeNode(type)); + } + } +} + +function isStateTypeMemberEmpty(stateType: ts.TypeNode): boolean { + // Only need to handle TypeLiteralNode & IntersectionTypeNode + if (ts.isTypeLiteralNode(stateType)) { + return stateType.members.length === 0; + } + + if (!ts.isIntersectionTypeNode(stateType)) { + return true; + } + + return stateType.types.every(isStateTypeMemberEmpty); +} diff --git a/src/transforms/react-move-prop-types-to-class-transform.ts b/src/transforms/react-move-prop-types-to-class-transform.ts index 327e011..d954a11 100644 --- a/src/transforms/react-move-prop-types-to-class-transform.ts +++ b/src/transforms/react-move-prop-types-to-class-transform.ts @@ -1,8 +1,9 @@ import * as ts from 'typescript'; +import * as _ from 'lodash'; import * as helpers from '../helpers'; -export type Factory = ts.TransformerFactory; +export type Factory = ts.TransformerFactory; /** * Move Component.propTypes statements into class as a static member of the class @@ -32,7 +33,9 @@ export type Factory = ts.TransformerFactory; export function reactMovePropTypesToClassTransformFactoryFactory(typeChecker: ts.TypeChecker): Factory { return function reactMovePropTypesToClassTransformFactory(context: ts.TransformationContext) { return function reactMovePropTypesToClassTransform(sourceFile: ts.SourceFile) { - return visitSourceFile(sourceFile, typeChecker); + const visited = visitSourceFile(sourceFile, typeChecker); + ts.addEmitHelpers(visited, context.readEmitHelpers()); + return visited; }; }; } @@ -46,50 +49,36 @@ function visitSourceFile(sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker) let statements = sourceFile.statements; // Look for propType assignment statements - const propTypeAssignments = statements.filter( - (statement) => helpers.isReactPropTypeAssignmentStatement(statement) + const propTypeAssignments = statements.filter(statement => + helpers.isReactPropTypeAssignmentStatement(statement), ) as ts.ExpressionStatement[]; - for (const propTypeAssignment of propTypeAssignments) { - // Look for the class declarations with the same name - const componentName = getComponentName(propTypeAssignment, sourceFile); + const componentName = helpers.getComponentName(propTypeAssignment, sourceFile); - const classStatement = helpers.find( + const classStatement = (_.find( statements, - (statement) => helpers.isClassDeclaration(statement) && + statement => + ts.isClassDeclaration(statement) && statement.name !== undefined && statement.name.getText(sourceFile) === componentName, - ) as {} as ts.ClassDeclaration; // Type weirdness + ) as {}) as ts.ClassDeclaration; // Type weirdness // && helpers.isBinaryExpression(propTypeAssignment.expression) is redundant to satisfy the type checker - if (classStatement && helpers.isBinaryExpression(propTypeAssignment.expression)) { + if (classStatement && ts.isBinaryExpression(propTypeAssignment.expression)) { const newClassStatement = addStaticMemberToClass( classStatement, 'propTypes', propTypeAssignment.expression.right, ); - statements = ts.createNodeArray( - helpers.replaceItem(sourceFile.statements, classStatement, newClassStatement), - ); + statements = ts.createNodeArray(helpers.replaceItem(statements, classStatement, newClassStatement)); } } return ts.updateSourceFileNode(sourceFile, statements); } - -/** - * Get component name off of a propType assignment statement - * @param propTypeAssignment - * @param sourceFile - */ -function getComponentName(propTypeAssignment: ts.Statement, sourceFile: ts.SourceFile) { - const text = propTypeAssignment.getText(sourceFile); - return text.substr(0, text.indexOf('.')); -} - /** * Insert a new static member into a class * @param classDeclaration @@ -97,7 +86,7 @@ function getComponentName(propTypeAssignment: ts.Statement, sourceFile: ts.Sourc * @param value */ function addStaticMemberToClass(classDeclaration: ts.ClassDeclaration, name: string, value: ts.Expression) { - const staticModifier = ts.createToken(ts.SyntaxKind.StaticKeyword) + const staticModifier = ts.createToken(ts.SyntaxKind.StaticKeyword); const propertyDeclaration = ts.createProperty([], [staticModifier], name, undefined, undefined, value); return ts.updateClassDeclaration( classDeclaration, @@ -106,6 +95,6 @@ function addStaticMemberToClass(classDeclaration: ts.ClassDeclaration, name: str classDeclaration.name, classDeclaration.typeParameters, ts.createNodeArray(classDeclaration.heritageClauses), - ts.createNodeArray([propertyDeclaration, ...classDeclaration.members]) - ) + ts.createNodeArray([propertyDeclaration, ...classDeclaration.members]), + ); } diff --git a/src/transforms/react-remove-prop-types-assignment-transform.ts b/src/transforms/react-remove-prop-types-assignment-transform.ts index f87b671..4b57fa9 100644 --- a/src/transforms/react-remove-prop-types-assignment-transform.ts +++ b/src/transforms/react-remove-prop-types-assignment-transform.ts @@ -2,7 +2,7 @@ import * as ts from 'typescript'; import * as helpers from '../helpers'; -export type Factory = ts.TransformerFactory; +export type Factory = ts.TransformerFactory; /** * Remove Component.propTypes statements @@ -15,13 +15,15 @@ export type Factory = ts.TransformerFactory; * After * class SomeComponent extends React.Component<{foo: number;}, {bar: string;}> {} */ -export function reactRemovePropTypesAssignmentTransformFactoryFactory(typeChecker: ts.TypeChecker): Factory{ +export function reactRemovePropTypesAssignmentTransformFactoryFactory(typeChecker: ts.TypeChecker): Factory { return function reactRemovePropTypesAssignmentTransformFactory(context: ts.TransformationContext) { return function reactRemovePropTypesAssignmentTransform(sourceFile: ts.SourceFile) { - return ts.updateSourceFileNode( + const visited = ts.updateSourceFileNode( sourceFile, sourceFile.statements.filter(s => !helpers.isReactPropTypeAssignmentStatement(s)), ); - } - } + ts.addEmitHelpers(visited, context.readEmitHelpers()); + return visited; + }; + }; } diff --git a/src/transforms/react-remove-prop-types-import.ts b/src/transforms/react-remove-prop-types-import.ts new file mode 100644 index 0000000..8644b8c --- /dev/null +++ b/src/transforms/react-remove-prop-types-import.ts @@ -0,0 +1,76 @@ +import * as ts from 'typescript'; +import * as _ from 'lodash'; + +import * as helpers from '../helpers'; + +export type Factory = ts.TransformerFactory; + +/** + * Remove `import PropTypes from 'prop-types'` or + * `import { PropTypes } from 'react'` + * + * @example + * Before: + * import PropTypes from 'prop-types' + * import React, { PropTypes } from 'react' + * + * After: + * import React from 'react' + */ +export function reactRemovePropTypesImportTransformFactoryFactory(typeChecker: ts.TypeChecker): Factory { + return function reactRemovePropTypesImportTransformFactory(context: ts.TransformationContext) { + return function reactRemovePropTypesImportTransform(sourceFile: ts.SourceFile) { + const visited = ts.updateSourceFileNode( + sourceFile, + sourceFile.statements + .filter(s => { + return !( + ts.isImportDeclaration(s) && + ts.isStringLiteral(s.moduleSpecifier) && + s.moduleSpecifier.text === 'prop-types' + ); + }) + .map(updateReactImportIfNeeded), + ); + ts.addEmitHelpers(visited, context.readEmitHelpers()); + return visited; + }; + }; +} + +function updateReactImportIfNeeded(statement: ts.Statement) { + if ( + !ts.isImportDeclaration(statement) || + !ts.isStringLiteral(statement.moduleSpecifier) || + statement.moduleSpecifier.text !== 'react' || + !statement.importClause || + !statement.importClause.namedBindings || + !ts.isNamedImports(statement.importClause.namedBindings) + ) { + return statement; + } + + const namedBindings = statement.importClause.namedBindings; + const newNamedBindingElements = namedBindings.elements.filter(elm => elm.name.text !== 'PropTypes'); + + if (newNamedBindingElements.length === namedBindings.elements.length) { + // Means it has no 'PropTypes' named import + return statement; + } + + const newImportClause = ts.updateImportClause( + statement.importClause, + statement.importClause.name, + newNamedBindingElements.length === 0 + ? undefined + : ts.updateNamedImports(namedBindings, newNamedBindingElements), + ); + + return ts.updateImportDeclaration( + statement, + statement.decorators, + statement.modifiers, + newImportClause, + statement.moduleSpecifier, + ); +} diff --git a/src/transforms/react-remove-static-prop-types-member-transform.ts b/src/transforms/react-remove-static-prop-types-member-transform.ts index 6aa579d..7054884 100644 --- a/src/transforms/react-remove-static-prop-types-member-transform.ts +++ b/src/transforms/react-remove-static-prop-types-member-transform.ts @@ -2,7 +2,7 @@ import * as ts from 'typescript'; import * as helpers from '../helpers'; -export type Factory = ts.TransformerFactory; +export type Factory = ts.TransformerFactory; /** * Remove static propTypes @@ -21,10 +21,12 @@ export type Factory = ts.TransformerFactory; export function reactRemoveStaticPropTypesMemberTransformFactoryFactory(typeChecker: ts.TypeChecker): Factory { return function reactRemoveStaticPropTypesMemberTransformFactory(context: ts.TransformationContext) { return function reactRemoveStaticPropTypesMemberTransform(sourceFile: ts.SourceFile) { - return ts.visitEachChild(sourceFile, visitor, context); + const visited = ts.visitEachChild(sourceFile, visitor, context); + ts.addEmitHelpers(visited, context.readEmitHelpers()); + return visited; function visitor(node: ts.Node) { - if (helpers.isClassDeclaration(node) && helpers.isReactComponent(node, typeChecker)) { + if (ts.isClassDeclaration(node) && helpers.isReactComponent(node, typeChecker)) { return ts.updateClassDeclaration( node, node.decorators, @@ -32,29 +34,29 @@ export function reactRemoveStaticPropTypesMemberTransformFactoryFactory(typeChec node.name, node.typeParameters, ts.createNodeArray(node.heritageClauses), - node.members.filter((member) => { + node.members.filter(member => { if ( - helpers.isPropertyDeclaration(member) - && helpers.hasStaticModifier(member) - && helpers.isPropTypesMember(member, sourceFile) + ts.isPropertyDeclaration(member) && + helpers.hasStaticModifier(member) && + helpers.isPropTypesMember(member, sourceFile) ) { return false; } // propTypes getter if ( - helpers.isGetAccessorDeclaration(member) - && helpers.hasStaticModifier(member) - && helpers.isPropTypesMember(member, sourceFile) + ts.isGetAccessorDeclaration(member) && + helpers.hasStaticModifier(member) && + helpers.isPropTypesMember(member, sourceFile) ) { return false; } return true; }), - ) + ); } return node; } - } - } + }; + }; } diff --git a/src/transforms/react-stateless-function-make-props-transform.ts b/src/transforms/react-stateless-function-make-props-transform.ts new file mode 100644 index 0000000..0222cec --- /dev/null +++ b/src/transforms/react-stateless-function-make-props-transform.ts @@ -0,0 +1,117 @@ +import * as ts from 'typescript'; +import * as _ from 'lodash'; + +import * as helpers from '../helpers'; + +export type Factory = ts.TransformerFactory; + +/** + * Transform react stateless components + * + * @example + * Before: + * const Hello = ({ message }) => { + * return
hello {message}
+ * } + * // Or: + * // const Hello = ({ message }) =>
hello {message}
+ * + * Hello.propTypes = { + * message: React.PropTypes.string, + * } + * + * After: + * Type HelloProps = { + * message: string; + * } + * + * const Hello: React.SFC = ({ message }) => { + * return
hello {message}
+ * } + * + * Hello.propTypes = { + * message: React.PropTypes.string, + * } + */ +export function reactStatelessFunctionMakePropsTransformFactoryFactory(typeChecker: ts.TypeChecker): Factory { + return function reactStatelessFunctionMakePropsTransformFactory(context: ts.TransformationContext) { + return function reactStatelessFunctionMakePropsTransform(sourceFile: ts.SourceFile) { + const visited = visitSourceFile(sourceFile, typeChecker); + ts.addEmitHelpers(visited, context.readEmitHelpers()); + return visited; + }; + }; +} + +function visitSourceFile(sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker) { + // Look for propType assignment statements + const propTypeAssignments = sourceFile.statements.filter(statement => + helpers.isReactPropTypeAssignmentStatement(statement), + ) as ts.ExpressionStatement[]; + + let newSourceFile = sourceFile; + for (const propTypeAssignment of propTypeAssignments) { + const componentName = helpers.getComponentName(propTypeAssignment, newSourceFile); + + const funcComponent = (_.find(newSourceFile.statements, s => { + return ( + (ts.isFunctionDeclaration(s) && s.name !== undefined && s.name.getText() === componentName) || + (ts.isVariableStatement(s) && s.declarationList.declarations[0].name.getText() === componentName) + ); + }) as {}) as ts.FunctionDeclaration | ts.VariableStatement; // Type weirdness + + if (funcComponent) { + newSourceFile = visitReactStatelessComponent(funcComponent, propTypeAssignment, newSourceFile); + } + } + + return newSourceFile; +} + +function visitReactStatelessComponent( + component: ts.FunctionDeclaration | ts.VariableStatement, + propTypesExpressionStatement: ts.ExpressionStatement, + sourceFile: ts.SourceFile, +) { + let arrowFuncComponent = helpers.convertReactStatelessFunctionToArrowFunction(component); + let componentName = arrowFuncComponent.declarationList.declarations[0].name.getText(); + let componentInitializer = arrowFuncComponent.declarationList.declarations[0].initializer; + + const propType = getPropTypesFromTypeAssignment(propTypesExpressionStatement); + const shouldMakePropTypeDeclaration = propType.members.length > 0; + const propTypeName = `${componentName}Props`; + const propTypeDeclaration = ts.createTypeAliasDeclaration([], [], propTypeName, [], propType); + const propTypeRef = ts.createTypeReferenceNode(propTypeName, []); + + let componentType = ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier('React'), 'SFC'), [ + shouldMakePropTypeDeclaration ? propTypeRef : propType, + ]); + + // replace component with ts stateless component + const typedComponent = ts.createVariableStatement( + arrowFuncComponent.modifiers, + ts.createVariableDeclarationList( + [ts.createVariableDeclaration(componentName, componentType, componentInitializer)], + arrowFuncComponent.declarationList.flags, + ), + ); + + let statements = shouldMakePropTypeDeclaration + ? helpers.insertBefore(sourceFile.statements, component, [propTypeDeclaration]) + : sourceFile.statements; + + statements = helpers.replaceItem(statements, component, typedComponent); + return ts.updateSourceFileNode(sourceFile, statements); +} + +function getPropTypesFromTypeAssignment(propTypesExpressionStatement: ts.ExpressionStatement) { + if ( + propTypesExpressionStatement !== undefined && + ts.isBinaryExpression(propTypesExpressionStatement.expression) && + ts.isObjectLiteralExpression(propTypesExpressionStatement.expression.right) + ) { + return helpers.buildInterfaceFromPropTypeObjectLiteral(propTypesExpressionStatement.expression.right); + } + + return ts.createTypeLiteralNode([]); +} diff --git a/src/untyped-modules.d.ts b/src/untyped-modules.d.ts new file mode 100644 index 0000000..f187021 --- /dev/null +++ b/src/untyped-modules.d.ts @@ -0,0 +1 @@ +declare module 'dedent'; diff --git a/test/collapse-intersection-interfaces-transform/advanced/output.tsx b/test/collapse-intersection-interfaces-transform/advanced/output.tsx index a272d5a..a579223 100644 --- a/test/collapse-intersection-interfaces-transform/advanced/output.tsx +++ b/test/collapse-intersection-interfaces-transform/advanced/output.tsx @@ -1,7 +1,7 @@ type Foo = { - foo: string; - stuff: boolean; - other: () => void; - bar: number; - [key: string]: number; + foo: string, + stuff: boolean, + other: () => void, + bar: number, + [key: string]: number, }; diff --git a/test/collapse-intersection-interfaces-transform/multiple/input.tsx b/test/collapse-intersection-interfaces-transform/multiple/input.tsx new file mode 100644 index 0000000..1854a03 --- /dev/null +++ b/test/collapse-intersection-interfaces-transform/multiple/input.tsx @@ -0,0 +1,3 @@ +type Foo = {foo: string} & {bar: number}; + +type Bar = {foo: number} & {bar: string}; diff --git a/test/collapse-intersection-interfaces-transform/multiple/output.tsx b/test/collapse-intersection-interfaces-transform/multiple/output.tsx new file mode 100644 index 0000000..f6d0c1d --- /dev/null +++ b/test/collapse-intersection-interfaces-transform/multiple/output.tsx @@ -0,0 +1,8 @@ +type Foo = { + foo: string, + bar: number, +}; +type Bar = { + foo: number, + bar: string, +}; diff --git a/test/collapse-intersection-interfaces-transform/repeated/input.tsx b/test/collapse-intersection-interfaces-transform/repeated/input.tsx new file mode 100644 index 0000000..2bfb4e6 --- /dev/null +++ b/test/collapse-intersection-interfaces-transform/repeated/input.tsx @@ -0,0 +1,5 @@ +type A = { foo: string; } & { foo: string; }; + +type B = { foo: string; bar: number; } & { foo: number; bar: number; } + +type C = { foo: string; bar: number; } & { foo: number; bar: number; } & { foo: string; } diff --git a/test/collapse-intersection-interfaces-transform/repeated/output.tsx b/test/collapse-intersection-interfaces-transform/repeated/output.tsx new file mode 100644 index 0000000..5dc0eec --- /dev/null +++ b/test/collapse-intersection-interfaces-transform/repeated/output.tsx @@ -0,0 +1,13 @@ +type A = { + foo: string, +}; + +type B = { + foo: string | number, + bar: number, +}; + +type C = { + foo: string | number, + bar: number, +}; diff --git a/test/collapse-intersection-interfaces-transform/simple/output.tsx b/test/collapse-intersection-interfaces-transform/simple/output.tsx index b845331..d4374e3 100644 --- a/test/collapse-intersection-interfaces-transform/simple/output.tsx +++ b/test/collapse-intersection-interfaces-transform/simple/output.tsx @@ -1,4 +1,4 @@ type Foo = { - foo: string; - bar: number; -}; \ No newline at end of file + foo: string, + bar: number, +}; diff --git a/test/end-to-end/basic/input.tsx b/test/end-to-end/basic/input.tsx index 881a680..53750ee 100644 --- a/test/end-to-end/basic/input.tsx +++ b/test/end-to-end/basic/input.tsx @@ -1,7 +1,8 @@ +import PropTypes from 'prop-types'; import * as React from 'react'; export default class MyComponent extends React.Component { render() { return
; } -} \ No newline at end of file +} diff --git a/test/end-to-end/basic/output.tsx b/test/end-to-end/basic/output.tsx index f0b58a2..c98cfbd 100644 --- a/test/end-to-end/basic/output.tsx +++ b/test/end-to-end/basic/output.tsx @@ -1,9 +1,5 @@ import * as React from 'react'; - -type MyComponentProps = {}; -type MyComponentState = void; - -export default class MyComponent extends React.Component { +export default class MyComponent extends React.Component<{}, {}> { render() { return
; } diff --git a/test/end-to-end/initial-state-and-proprypes-and-set-state/output.tsx b/test/end-to-end/initial-state-and-proprypes-and-set-state/output.tsx index 64338c6..95fae0f 100644 --- a/test/end-to-end/initial-state-and-proprypes-and-set-state/output.tsx +++ b/test/end-to-end/initial-state-and-proprypes-and-set-state/output.tsx @@ -1,12 +1,11 @@ import * as React from 'react'; - type MyComponentProps = { - baz: string; + baz: string, }; type MyComponentState = { - dynamicState: number; - foo: number; - bar: string; + dynamicState: number, + foo: number, + bar: string, }; export default class MyComponent extends React.Component { state = { foo: 1, bar: 'str' }; diff --git a/test/end-to-end/initial-state-and-proprypes/output.tsx b/test/end-to-end/initial-state-and-proprypes/output.tsx index 4a358b2..912e3b8 100644 --- a/test/end-to-end/initial-state-and-proprypes/output.tsx +++ b/test/end-to-end/initial-state-and-proprypes/output.tsx @@ -1,11 +1,10 @@ import * as React from 'react'; - type MyComponentProps = { - baz: string; + baz: string, }; type MyComponentState = { - foo: number; - bar: string; + foo: number, + bar: string, }; export default class MyComponent extends React.Component { state = { foo: 1, bar: 'str' }; diff --git a/test/end-to-end/multiple-components/input.tsx b/test/end-to-end/multiple-components/input.tsx new file mode 100644 index 0000000..9cb8345 --- /dev/null +++ b/test/end-to-end/multiple-components/input.tsx @@ -0,0 +1,34 @@ +const Hello = ({ message }) => { + return
hello {message}
+}; + +const Hey = ({ name }) => { + return
hey, {name}
+} + +Hey.propTypes = { + message: React.PropTypes.string, +} + +Hello.propTypes = { + message: React.PropTypes.string, +} + +export default class MyComponent extends React.Component { + render() { + return