├── .gitignore ├── .npmignore ├── .travis.yml ├── .vscode └── settings.json ├── DOCUMENTATION.MD ├── LICENSE ├── README.md ├── demo ├── DOCUMENTATION.md └── terminal.png ├── dist ├── __mocks__ │ ├── ExampleComponent.js │ ├── OtherExampleComponent.js │ └── inside │ │ └── OtherExampleComponent.js ├── __tests__ │ ├── __snapshots__ │ │ ├── generatereactdoc.test.js.snap │ │ ├── processprop.test.js.snap │ │ └── react-doc-generator.test.js.snap │ ├── generatereactdoc.test.js │ ├── processprop.test.js │ ├── react-doc-generator.test.js │ └── utilities.test.js ├── generatereactdoc.js ├── lib │ └── command.js ├── react-doc-generator.js └── template.handlebars ├── package.json ├── src ├── .babelrc ├── __mocks__ │ ├── ExampleComponent.js │ ├── OtherExampleComponent.js │ └── inside │ │ └── OtherExampleComponent.js ├── __tests__ │ ├── __snapshots__ │ │ ├── generatereactdoc.test.js.snap │ │ ├── processprop.test.js.snap │ │ └── react-doc-generator.test.js.snap │ ├── generatereactdoc.test.js │ ├── processprop.test.js │ ├── react-doc-generator.test.js │ └── utilities.test.js ├── generatereactdoc.js ├── lib │ └── command.js ├── react-doc-generator.js └── template.handlebars └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # react-doc-generator 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # nyc test coverage 20 | .nyc_output 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # node-waf configuration 26 | .lock-wscript 27 | 28 | # Compiled binary addons (http://nodejs.org/api/addons.html) 29 | build/Release 30 | 31 | # Dependency directories 32 | node_modules 33 | jspm_packages 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional REPL history 39 | .node_repl_history 40 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # react-doc-generator 2 | # Logs 3 | demo 4 | logs 5 | *.log 6 | npm-debug.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # nyc test coverage 20 | .nyc_output 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # node-waf configuration 26 | .lock-wscript 27 | 28 | # Compiled binary addons (http://nodejs.org/api/addons.html) 29 | build/Release 30 | 31 | # Dependency directories 32 | node_modules 33 | jspm_packages 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional REPL history 39 | .node_repl_history 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | - "node" 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.validate.enable": false, 3 | "javascript.validate.enable": false 4 | } -------------------------------------------------------------------------------- /DOCUMENTATION.MD: -------------------------------------------------------------------------------- 1 | 2 | 3 | This document was generated by the **React DOC Generator v1.2.8**. 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Marcin Borkowski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React DOC Generator 2 | [![npm version](https://img.shields.io/npm/v/react-doc-generator.svg?style=flat-square)](https://www.npmjs.com/package/react-doc-generator) 3 | [![dependency status](https://img.shields.io/david/marborkowski/react-doc-generator.svg?style=flat-square)](https://david-dm.org/marborkowski/react-doc-generator) 4 | [![build status](https://img.shields.io/travis/marborkowski/react-doc-generator.svg?style=flat-square)](https://travis-ci.org/marborkowski/react-doc-generator) 5 | 6 | Generate simple React components documentation in [Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet). 7 | 8 | ## Installation 9 | 10 | `$ npm install -save-dev react-doc-generator` 11 | 12 | ## Usage 13 | 14 | Check every option runnig `react-doc-generator` with `--help` or `-h`: 15 | 16 | ``` 17 | $ react-doc-generator --help 18 | 19 | Usage: react-doc-generator [options] 20 | 21 | Options: 22 | 23 | -h, --help output usage information 24 | -V, --version output the version number 25 | -x, --extensions Include only these file extensions. Default: js,jsx 26 | -i, --ignore Folders to ignore. Default: node_modules,__tests__,__mocks__ 27 | -e, --exclude-patterns Filename patterns to exclude. Default: [] 28 | -t, --title [value]> Document title. Default: 'Components' 29 | -o, --output Markdown file to write. Default: 'README.MD' 30 | ``` 31 | 32 | ### By the command line 33 | 34 | Example: 35 | 36 | `$ react-doc-generator src -o DOCUMENTATION.md` 37 | 38 | ### NPM script 39 | 40 | Example: 41 | 42 | In your `package.json` put: 43 | ``` 44 | { 45 | // ... 46 | "scripts": { 47 | "doc": "react-doc-generator ./app/components/custom -o DOCUMENTATION.md" 48 | } 49 | // ... 50 | } 51 | ``` 52 | 53 | so then you are able to call this script by the command line: 54 | 55 | `$ npm run doc` 56 | 57 | ## API 58 | 59 | ```js 60 | /** 61 | * General component description. 62 | * You can even use the native Markdown here. 63 | * E.g.: 64 | * ```html 65 | * 66 | * ``` 67 | */ 68 | export class MyComponent extends React.Component { 69 | static displayName = 'Official Component Name' 70 | static propTypes = { 71 | /** 72 | * Description of prop "foo". 73 | */ 74 | foo: React.PropTypes.number, 75 | /** 76 | * Description of prop "bar" (a custom validation function). 77 | */ 78 | bar: function(props, propName, componentName) { 79 | // ... 80 | }, 81 | baz: React.PropTypes.oneOfType([ 82 | React.PropTypes.number, 83 | React.PropTypes.string 84 | ]), 85 | } 86 | 87 | static defaultProps = { 88 | foo: 10000099999 89 | } 90 | 91 | render () { 92 | return (
Hello
); 93 | } 94 | } 95 | ``` 96 | 97 | Because [**react-doc-generator**](https://github.com/marborkowski/react-doc-generator) uses [**react-docgen**](https://github.com/reactjs/react-docgen) library, you can [follow other examples here](https://github.com/reactjs/react-docgen). 98 | 99 | ## Demo 100 | 101 | * [Example output](https://github.com/marborkowski/react-doc-generator/blob/master/demo/DOCUMENTATION.md) 102 | 103 | ## Terminal 104 | 105 | This is an example of what you'll see in your terminal. 106 | 107 | ![Terminal](https://raw.githubusercontent.com/marborkowski/react-doc-generator/master/demo/terminal.png) 108 | 109 | ### License 110 | 111 | MIT License 112 | Copyright (c) 2017 Marcin Borkowski () 113 | Permission is hereby granted, free of charge, to any person obtaining a copy 114 | of this software and associated documentation files (the "Software"), to deal 115 | in the Software without restriction, including without limitation the rights 116 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 117 | copies of the Software, and to permit persons to whom the Software is 118 | furnished to do so, subject to the following conditions: 119 | 120 | The above copyright notice and this permission notice shall be included in all 121 | copies or substantial portions of the Software. 122 | 123 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 124 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 125 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 126 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 127 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 128 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 129 | SOFTWARE. 130 | -------------------------------------------------------------------------------- /demo/DOCUMENTATION.md: -------------------------------------------------------------------------------- 1 | Components 2 | ---------- 3 | 4 | **app/components/custom/articles/articles.jsx** 5 | 6 | ### 1. Simple `List Of Articles` component. 7 | 8 | WARNING: This is only the demo version to provide a sample data. 9 | 10 | Example: 11 | ```js 12 | 13 | ``` 14 | 15 | 16 | 17 | 18 | Property | Type | Required | Default value | Description 19 | :--- | :--- | :--- | :--- | :--- 20 | showMore|bool|yes|false| 21 | title|string|no|| 22 | ----- 23 | **app/components/button/button.jsx** 24 | 25 | ### 1. Button 26 | 27 | A button clearly communicates what action will occur when the user touches it. It consists of text, an image, or both, designed in accordance with your app’s color theme. 28 | 29 | 30 | ```js 31 | import {Button, IconButton} from 'react-toolbox/lib/button'; 32 | 33 | const GithubIcon = () => ( 34 | 35 | 36 | 37 | ); 38 | 39 | const TestButtons = () => ( 40 |
41 | 44 |
55 | ); 56 | ``` 57 | 58 | Property | Type | Required | Default value | Description 59 | :--- | :--- | :--- | :--- | :--- 60 | label|string|yes|Submit|Button label 61 | width|number|no|500|Button width 62 | 63 | 64 | ----- 65 | **app/components/dropdown/dropdown.jsx** 66 | 67 | ### 1. Dropdown 68 | 69 | The Dropdown selects an option between multiple selections. The element displays the current state and a down arrow. When it is clicked, it displays the list of available options. 70 | 71 | 72 | ```js 73 | import Dropdown from 'react-toolbox/lib/dropdown'; 74 | 75 | const countries = [ 76 | { value: 'EN-gb', label: 'England' }, 77 | { value: 'ES-es', label: 'Spain'}, 78 | { value: 'TH-th', label: 'Thailand' }, 79 | { value: 'EN-en', label: 'USA'} 80 | ]; 81 | 82 | class DropdownTest extends React.Component { 83 | state = { value: 'ES-es' }; 84 | 85 | handleChange = (value) => { 86 | this.setState({value: value}); 87 | }; 88 | 89 | render () { 90 | return ( 91 | 97 | ); 98 | } 99 | } 100 | ``` 101 | 102 | Property | Type | Required | Default value | Description 103 | :--- | :--- | :--- | :--- | :--- 104 | source|array|yes|[1, 2, 3]|Elements 105 | -------------------------------------------------------------------------------- /demo/terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marborkowski/react-doc-generator/9b103405ce0744a7279a8fc1422406a891ff9a63/demo/terminal.png -------------------------------------------------------------------------------- /dist/__mocks__/ExampleComponent.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var React = require('react'); 4 | 5 | var PropTypes = require('prop-types'); 6 | /** 7 | * General component description. 8 | * fdgdfgdf gfdgfdg fdgfdgdfg 9 | * gdfgfdgdfg dfgdfgfdg dfg df getDefaultPropsg fdgfd 10 | * gfdgfdgdfgfdg. 11 | * 12 | * 13 | * Example: 14 | * ```html 15 | * 16 | * ``` 17 | */ 18 | 19 | 20 | var Component = React.createClass({ 21 | displayName: "Component", 22 | propTypes: { 23 | /** 24 | * Description of prop "toe" has one break line 25 | * here following more comments and has default 26 | * empty string. 27 | */ 28 | toe: PropTypes.string, 29 | 30 | /** 31 | * Description of prop "finger". 32 | */ 33 | finger: PropTypes.string, 34 | 35 | /** 36 | * Description of prop "foo". 37 | */ 38 | foo: PropTypes.number, 39 | 40 | /** 41 | * Description of prop "bar" (a custom validation function). 42 | */ 43 | bar: function bar(props, propName, componentName) {// ... 44 | }, 45 | baz: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) 46 | }, 47 | getDefaultProps: function getDefaultProps() { 48 | return { 49 | finger: 'medium', 50 | toe: '', 51 | foo: 42, 52 | bar: 21 53 | }; 54 | }, 55 | render: function render() { 56 | return ( 57 | /*#__PURE__*/ 58 | React.createElement("div", null, "Hello World!") 59 | ); 60 | } 61 | }); 62 | module.exports = Component; -------------------------------------------------------------------------------- /dist/__mocks__/OtherExampleComponent.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports["default"] = exports.Component1 = void 0; 9 | 10 | var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); 11 | 12 | var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); 13 | 14 | var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); 15 | 16 | var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); 17 | 18 | var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); 19 | 20 | var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); 21 | 22 | var _react = _interopRequireDefault(require("react")); 23 | 24 | function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var result, Super = (0, _getPrototypeOf2["default"])(Derived); if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else result = Super.apply(this, arguments); return (0, _possibleConstructorReturn2["default"])(this, result); }; } 25 | 26 | function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { return Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})), true; } catch (e) { return false; } } 27 | 28 | /** 29 | * General component description. 30 | */ 31 | var Component1 = 32 | /*#__PURE__*/ 33 | function (_React$Component) { 34 | function Component1() { 35 | return (0, _classCallCheck2["default"])(this, Component1), _super.apply(this, arguments); 36 | } 37 | 38 | (0, _inherits2["default"])(Component1, _React$Component); 39 | 40 | var _super = _createSuper(Component1); 41 | 42 | return (0, _createClass2["default"])(Component1, [{ 43 | key: "render", 44 | value: function render() { 45 | return ( 46 | /*#__PURE__*/ 47 | _react["default"].createElement("div", null, "Hello") 48 | ); 49 | } 50 | }]), Component1; 51 | }(_react["default"].Component); 52 | 53 | exports.Component1 = Component1; 54 | (0, _defineProperty2["default"])(Component1, "defaultProps", { 55 | foo: 10000099999, 56 | onExit: function onExit() { 57 | console.debug('onExit'); 58 | } 59 | }); 60 | 61 | /** 62 | * General another component description. 63 | * Blah blah blah... 64 | * fdfdfsdf 65 | * fdsfsd 66 | */ 67 | function Component2(props) { 68 | return ( 69 | /*#__PURE__*/ 70 | _react["default"].createElement("div", null, "Hello") 71 | ); 72 | } 73 | 74 | Component2.displayName = "DUPA"; 75 | var _default = Component2; 76 | exports["default"] = _default; -------------------------------------------------------------------------------- /dist/__mocks__/inside/OtherExampleComponent.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports["default"] = exports.Component1 = void 0; 9 | 10 | var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized")); 11 | 12 | var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); 13 | 14 | var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); 15 | 16 | var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); 17 | 18 | var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); 19 | 20 | var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); 21 | 22 | var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); 23 | 24 | var _react = _interopRequireDefault(require("react")); 25 | 26 | function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var result, Super = (0, _getPrototypeOf2["default"])(Derived); if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else result = Super.apply(this, arguments); return (0, _possibleConstructorReturn2["default"])(this, result); }; } 27 | 28 | function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { return Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})), true; } catch (e) { return false; } } 29 | 30 | /** 31 | * General component description. 32 | */ 33 | var Component1 = 34 | /*#__PURE__*/ 35 | function (_React$Component) { 36 | function Component1() { 37 | return (0, _classCallCheck2["default"])(this, Component1), _super.apply(this, arguments); 38 | } 39 | 40 | (0, _inherits2["default"])(Component1, _React$Component); 41 | 42 | var _super = _createSuper(Component1); 43 | 44 | return (0, _createClass2["default"])(Component1, [{ 45 | key: "render", 46 | value: function render() { 47 | return ( 48 | /*#__PURE__*/ 49 | _react["default"].createElement("div", null, "Hello") 50 | ); 51 | } 52 | }]), Component1; 53 | }(_react["default"].Component); 54 | /** 55 | * General another component description. 56 | * Blah blah blah... 57 | * fdfdfsdf 58 | * fdsfsd 59 | */ 60 | 61 | 62 | exports.Component1 = Component1; 63 | (0, _defineProperty2["default"])(Component1, "propTypes", { 64 | /** 65 | * Description of prop "foo". 66 | */ 67 | foo: _react["default"].PropTypes.number, 68 | 69 | /** 70 | * Description of prop "bar" (a custom validation function). 71 | */ 72 | bar: function bar(props, propName, componentName) {// ... 73 | }, 74 | baz: _react["default"].PropTypes.oneOfType([_react["default"].PropTypes.number, _react["default"].PropTypes.string]), 75 | onExit: _react["default"].PropTypes.func 76 | }); 77 | (0, _defineProperty2["default"])(Component1, "defaultProps", { 78 | foo: 10000099999, 79 | onExit: function onExit() { 80 | console.debug('onExit'); 81 | } 82 | }); 83 | 84 | var Component2 = 85 | /*#__PURE__*/ 86 | function (_React$Component2) { 87 | function Component2() { 88 | var _this; 89 | 90 | (0, _classCallCheck2["default"])(this, Component2); 91 | 92 | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) args[_key] = arguments[_key]; 93 | 94 | return _this = _super2.call.apply(_super2, [this].concat(args)), (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "displayName", "DUPA"), _this = _super2.call.apply(_super2, [this].concat(args)), (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "displayName", "DUPA"), _this; 95 | } 96 | 97 | (0, _inherits2["default"])(Component2, _React$Component2); 98 | 99 | var _super2 = _createSuper(Component2); 100 | 101 | return (0, _createClass2["default"])(Component2, [{ 102 | key: "render", 103 | value: function render() { 104 | return ( 105 | /*#__PURE__*/ 106 | _react["default"].createElement("div", null, "Hello") 107 | ); 108 | } 109 | }]), Component2; 110 | }(_react["default"].Component); 111 | 112 | (0, _defineProperty2["default"])(Component2, "propTypes", { 113 | /** 114 | * Description of prop "foo". 115 | */ 116 | foo: _react["default"].PropTypes.number, 117 | 118 | /** 119 | * Description of prop "bar" (a custom validation function). 120 | */ 121 | bar: function bar(props, propName, componentName) {// ... 122 | }, 123 | baz: _react["default"].PropTypes.oneOfType([_react["default"].PropTypes.number, _react["default"].PropTypes.string]) 124 | }); 125 | var _default = Component2; 126 | exports["default"] = _default; -------------------------------------------------------------------------------- /dist/__tests__/__snapshots__/generatereactdoc.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`GenerateReactDoc Whole thing should work 1`] = ` 4 | Array [ 5 | Object { 6 | "files": Array [ 7 | Object { 8 | "components": Array [ 9 | Object { 10 | "description": "fdgdfgdf gfdgfdg fdgfdgdfg 11 | gdfgfdgdfg dfgdfgfdg dfg df getDefaultPropsg fdgfd 12 | gfdgfdgdfgfdg. 13 | 14 | 15 | Example: 16 | \`\`\`html 17 | 18 | \`\`\` 19 | 20 | ", 21 | "methods": Array [], 22 | "props": Object { 23 | "bar": Object { 24 | "defaultValue": Object { 25 | "computed": false, 26 | "value": "21", 27 | }, 28 | "description": "Description of prop \\"bar\\" (a custom validation function).", 29 | "required": false, 30 | "type": Object { 31 | "name": "custom", 32 | "raw": "function(props, propName, componentName) { 33 | // ... 34 | }", 35 | }, 36 | }, 37 | "baz": Object { 38 | "defaultValue": Object { 39 | "value": undefined, 40 | }, 41 | "description": "", 42 | "required": false, 43 | "type": Object { 44 | "name": "union", 45 | "value": Array [ 46 | Object { 47 | "name": "number", 48 | }, 49 | Object { 50 | "name": "string", 51 | }, 52 | ], 53 | }, 54 | }, 55 | "finger": Object { 56 | "defaultValue": Object { 57 | "computed": false, 58 | "value": "'medium'", 59 | }, 60 | "description": "Description of prop \\"finger\\".", 61 | "required": false, 62 | "type": Object { 63 | "name": "string", 64 | }, 65 | }, 66 | "foo": Object { 67 | "defaultValue": Object { 68 | "computed": false, 69 | "value": "42", 70 | }, 71 | "description": "Description of prop \\"foo\\".", 72 | "required": false, 73 | "type": Object { 74 | "name": "number", 75 | }, 76 | }, 77 | "toe": Object { 78 | "defaultValue": Object { 79 | "computed": false, 80 | "value": "''", 81 | }, 82 | "description": "Description of prop \\"toe\\" has one break line here following more comments and has default empty string.", 83 | "required": false, 84 | "type": Object { 85 | "name": "string", 86 | }, 87 | }, 88 | }, 89 | "title": "General component description.", 90 | }, 91 | ], 92 | "filename": "/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/ExampleComponent.js", 93 | }, 94 | Object { 95 | "components": Array [ 96 | Object { 97 | "description": "null 98 | 99 | ", 100 | "displayName": "Component1", 101 | "methods": Array [], 102 | "props": Object { 103 | "bar": Object { 104 | "defaultValue": Object { 105 | "value": undefined, 106 | }, 107 | "description": "Description of prop \\"bar\\" (a custom validation function).", 108 | "flowType": Object { 109 | "name": "signature", 110 | "raw": "() => void", 111 | "signature": Object { 112 | "arguments": Array [], 113 | "return": Object { 114 | "name": "void", 115 | }, 116 | }, 117 | "type": "function", 118 | }, 119 | "required": true, 120 | "type": Object { 121 | "name": "() => void", 122 | }, 123 | }, 124 | "baz": Object { 125 | "defaultValue": Object { 126 | "value": undefined, 127 | }, 128 | "description": "", 129 | "flowType": Object { 130 | "elements": Array [ 131 | Object { 132 | "name": "number", 133 | }, 134 | Object { 135 | "name": "string", 136 | }, 137 | ], 138 | "name": "union", 139 | "raw": "number|string", 140 | }, 141 | "required": true, 142 | "type": Object { 143 | "name": "number|string", 144 | }, 145 | }, 146 | "foo": Object { 147 | "defaultValue": Object { 148 | "computed": false, 149 | "value": "10000099999", 150 | }, 151 | "description": "", 152 | "flowType": Object { 153 | "name": "number", 154 | }, 155 | "required": false, 156 | "type": Object { 157 | "name": "number", 158 | }, 159 | }, 160 | "onExit": Object { 161 | "defaultValue": Object { 162 | "computed": false, 163 | "value": "() => { 164 | console.debug('onExit'); 165 | }", 166 | }, 167 | "description": "", 168 | "flowType": Object { 169 | "name": "signature", 170 | "raw": "() => void", 171 | "signature": Object { 172 | "arguments": Array [], 173 | "return": Object { 174 | "name": "void", 175 | }, 176 | }, 177 | "type": "function", 178 | }, 179 | "required": false, 180 | "type": Object { 181 | "name": "() => void", 182 | }, 183 | }, 184 | }, 185 | "title": "Component1", 186 | }, 187 | Object { 188 | "description": "Blah blah blah... 189 | fdfdfsdf 190 | fdsfsd 191 | 192 | ", 193 | "displayName": "DUPA", 194 | "methods": Array [], 195 | "props": Object { 196 | "bar": Object { 197 | "defaultValue": Object { 198 | "value": undefined, 199 | }, 200 | "description": "Description of prop \\"bar\\" (a custom validation function).", 201 | "flowType": Object { 202 | "name": "signature", 203 | "raw": "() => mixed", 204 | "signature": Object { 205 | "arguments": Array [], 206 | "return": Object { 207 | "name": "mixed", 208 | }, 209 | }, 210 | "type": "function", 211 | }, 212 | "required": true, 213 | "type": Object { 214 | "name": "() => mixed", 215 | }, 216 | }, 217 | "baz": Object { 218 | "defaultValue": Object { 219 | "value": undefined, 220 | }, 221 | "description": "", 222 | "flowType": Object { 223 | "elements": Array [ 224 | Object { 225 | "name": "number", 226 | }, 227 | Object { 228 | "name": "string", 229 | }, 230 | ], 231 | "name": "union", 232 | "raw": "number | string", 233 | }, 234 | "required": true, 235 | "type": Object { 236 | "name": "number | string", 237 | }, 238 | }, 239 | "foo": Object { 240 | "defaultValue": Object { 241 | "value": undefined, 242 | }, 243 | "description": "Description of prop \\"foo\\".", 244 | "flowType": Object { 245 | "name": "number", 246 | }, 247 | "required": true, 248 | "type": Object { 249 | "name": "number", 250 | }, 251 | }, 252 | }, 253 | "title": "DUPA", 254 | }, 255 | ], 256 | "filename": "/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/OtherExampleComponent.js", 257 | }, 258 | Object { 259 | "components": Array [ 260 | Object { 261 | "description": "null 262 | 263 | ", 264 | "displayName": "Component1", 265 | "methods": Array [], 266 | "props": Object { 267 | "bar": Object { 268 | "defaultValue": Object { 269 | "value": undefined, 270 | }, 271 | "description": "Description of prop \\"bar\\" (a custom validation function).", 272 | "required": false, 273 | "type": Object { 274 | "name": "custom", 275 | "raw": "function(props, propName, componentName) { 276 | // ... 277 | }", 278 | }, 279 | }, 280 | "baz": Object { 281 | "defaultValue": Object { 282 | "value": undefined, 283 | }, 284 | "description": "", 285 | "required": false, 286 | "type": Object { 287 | "name": "union", 288 | "value": Array [ 289 | Object { 290 | "name": "number", 291 | }, 292 | Object { 293 | "name": "string", 294 | }, 295 | ], 296 | }, 297 | }, 298 | "foo": Object { 299 | "defaultValue": Object { 300 | "computed": false, 301 | "value": "10000099999", 302 | }, 303 | "description": "Description of prop \\"foo\\".", 304 | "required": false, 305 | "type": Object { 306 | "name": "number", 307 | }, 308 | }, 309 | "onExit": Object { 310 | "defaultValue": Object { 311 | "computed": false, 312 | "value": "See code", 313 | }, 314 | "description": "", 315 | "required": false, 316 | "type": Object { 317 | "name": "func", 318 | }, 319 | }, 320 | }, 321 | "title": "Component1", 322 | }, 323 | Object { 324 | "description": "Blah blah blah... 325 | fdfdfsdf 326 | fdsfsd 327 | 328 | ", 329 | "displayName": "DUPA", 330 | "methods": Array [], 331 | "props": Object { 332 | "bar": Object { 333 | "defaultValue": Object { 334 | "value": undefined, 335 | }, 336 | "description": "Description of prop \\"bar\\" (a custom validation function).", 337 | "required": false, 338 | "type": Object { 339 | "name": "custom", 340 | "raw": "function(props, propName, componentName) { 341 | // ... 342 | }", 343 | }, 344 | }, 345 | "baz": Object { 346 | "defaultValue": Object { 347 | "value": undefined, 348 | }, 349 | "description": "", 350 | "required": false, 351 | "type": Object { 352 | "name": "union", 353 | "value": Array [ 354 | Object { 355 | "name": "number", 356 | }, 357 | Object { 358 | "name": "string", 359 | }, 360 | ], 361 | }, 362 | }, 363 | "foo": Object { 364 | "defaultValue": Object { 365 | "value": undefined, 366 | }, 367 | "description": "Description of prop \\"foo\\".", 368 | "required": false, 369 | "type": Object { 370 | "name": "number", 371 | }, 372 | }, 373 | }, 374 | "title": "DUPA", 375 | }, 376 | ], 377 | "filename": "/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/inside/OtherExampleComponent.js", 378 | }, 379 | ], 380 | "version": "1.2.7", 381 | }, 382 | Array [ 383 | Array [ 384 | "/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/ExampleComponent.js", 385 | 1, 386 | "OK.", 387 | ], 388 | Array [ 389 | "/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/OtherExampleComponent.js", 390 | 2, 391 | "OK.", 392 | ], 393 | Array [ 394 | "/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/inside/OtherExampleComponent.js", 395 | 2, 396 | "OK.", 397 | ], 398 | ], 399 | ] 400 | `; 401 | -------------------------------------------------------------------------------- /dist/__tests__/__snapshots__/processprop.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`ProcessProp Process prop should not modify the default value if the type is a string 1`] = ` 4 | Object { 5 | "defaultValue": Object { 6 | "value": "somestring", 7 | }, 8 | "description": "", 9 | "type": Object { 10 | "name": "string", 11 | }, 12 | } 13 | `; 14 | 15 | exports[`ProcessProp Process prop should not modify the default value if the type is not string but default value doesnt have special characters 1`] = ` 16 | Object { 17 | "defaultValue": Object { 18 | "value": 22, 19 | }, 20 | "description": "", 21 | "type": Object { 22 | "name": "number", 23 | }, 24 | } 25 | `; 26 | 27 | exports[`ProcessProp Process prop should overwrite the default value when type is not string and there are invalid characters 1`] = ` 28 | Object { 29 | "defaultValue": Object { 30 | "value": "See code", 31 | }, 32 | "description": "", 33 | "type": Object { 34 | "name": "number", 35 | }, 36 | } 37 | `; 38 | -------------------------------------------------------------------------------- /dist/__tests__/__snapshots__/react-doc-generator.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`output file has needed values 1`] = ` 4 | Array [ 5 | "", 6 | "**/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/ExampleComponent.js**", 7 | "", 8 | "### 1. General component description.", 9 | "", 10 | "fdgdfgdf gfdgfdg fdgfdgdfg ", 11 | "gdfgfdgdfg dfgdfgfdg dfg df getDefaultPropsg fdgfd ", 12 | "gfdgfdgdfgfdg. ", 13 | " ", 14 | " ", 15 | "Example: ", 16 | "\`\`\`html ", 17 | " ", 18 | "\`\`\` ", 19 | "", 20 | "", 21 | "", 22 | "", 23 | "Property | Type | Required | Default value | Description", 24 | ":--- | :--- | :--- | :--- | :---", 25 | "toe|string|no|''|Description of prop "toe" has one break line here following more comments and has default empty string.", 26 | "finger|string|no|'medium'|Description of prop "finger".", 27 | "foo|number|no|42|Description of prop "foo".", 28 | "bar|custom|no|21|Description of prop "bar" (a custom validation function).", 29 | "baz|union|no||", 30 | "-----", 31 | "**/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/OtherExampleComponent.js**", 32 | "", 33 | "### 1. Component1", 34 | "", 35 | "null ", 36 | "", 37 | "", 38 | "", 39 | "", 40 | "Property | Type | Required | Default value | Description", 41 | ":--- | :--- | :--- | :--- | :---", 42 | "foo|number|no|10000099999|", 43 | "bar|() => void|yes||Description of prop "bar" (a custom validation function).", 44 | "baz|number|string|yes||", 45 | "onExit|() => void|no|() => {", 46 | " console.debug('onExit');", 47 | "}|", 48 | "### 2. DUPA", 49 | "", 50 | "Blah blah blah... ", 51 | "fdfdfsdf ", 52 | "fdsfsd ", 53 | "", 54 | "", 55 | "", 56 | "", 57 | "Property | Type | Required | Default value | Description", 58 | ":--- | :--- | :--- | :--- | :---", 59 | "foo|number|yes||Description of prop "foo".", 60 | "bar|() => mixed|yes||Description of prop "bar" (a custom validation function).", 61 | "baz|number | string|yes||", 62 | "-----", 63 | "**/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/inside/OtherExampleComponent.js**", 64 | "", 65 | "### 1. Component1", 66 | "", 67 | "null ", 68 | "", 69 | "", 70 | "", 71 | "", 72 | "Property | Type | Required | Default value | Description", 73 | ":--- | :--- | :--- | :--- | :---", 74 | "foo|number|no|10000099999|Description of prop "foo".", 75 | "bar|custom|no||Description of prop "bar" (a custom validation function).", 76 | "baz|union|no||", 77 | "onExit|func|no|See code|", 78 | "### 2. DUPA", 79 | "", 80 | "Blah blah blah... ", 81 | "fdfdfsdf ", 82 | "fdsfsd ", 83 | "", 84 | "", 85 | "", 86 | "", 87 | "Property | Type | Required | Default value | Description", 88 | ":--- | :--- | :--- | :--- | :---", 89 | "foo|number|no||Description of prop "foo".", 90 | "bar|custom|no||Description of prop "bar" (a custom validation function).", 91 | "baz|union|no||", 92 | "-----", 93 | "", 94 | "This document was generated by the **React DOC Generator v1.2.7**.", 95 | "", 96 | ] 97 | `; 98 | 99 | exports[`react-doc-generator contains help section if no argument is available in query 1`] = ` 100 | " 101 | 102 | REACT DOC GENERATOR v1.2.7 103 | by Marcin Borkowski  104 | Please specify as the first argument! 105 | Usage: react-doc-generator [options] 106 | 107 | Options: 108 | -V, --version output the version number 109 | -x, --extensions Include only these file extensions. Default: 110 | js,jsx (default: [\\"js\\",\\"jsx\\"]) 111 | -i, --ignore Folders to ignore. Default: 112 | node_modules,__tests__,__mocks__ (default: 113 | [\\"node_modules\\",\\"__tests__\\",\\"__mocks__\\"]) 114 | -e, --exclude-patterns Filename patterns to exclude. Default: [] 115 | (default: []) 116 | -t, --title [value] Document title. Default: 'Components' 117 | (default: \\"Components\\") 118 | -o, --output Markdown file to write. Default: 119 | 'DOCUMENTATION.MD' (default: 120 | \\"DOCUMENTATION.MD\\") 121 | -h, --help display help for command 122 | " 123 | `; 124 | 125 | exports[`react-doc-generator has the proper console output 1`] = ` 126 | " 127 | 128 | REACT DOC GENERATOR v1.2.7 129 | by Marcin Borkowski  130 | ┌──────────────────────────────────────────────────────────────────────────────────────────────────┬────────────┬────────┐ 131 | │ Path │ Components │ Status │ 132 | ├──────────────────────────────────────────────────────────────────────────────────────────────────┼────────────┼────────┤ 133 | │ /Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/ExampleComponent.js │ 1 │ OK. │ 134 | ├──────────────────────────────────────────────────────────────────────────────────────────────────┼────────────┼────────┤ 135 | │ /Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/OtherExampleComponent.js │ 2 │ OK. │ 136 | ├──────────────────────────────────────────────────────────────────────────────────────────────────┼────────────┼────────┤ 137 | │ /Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/inside/OtherExampleComponent.js │ 2 │ OK. │ 138 | └──────────────────────────────────────────────────────────────────────────────────────────────────┴────────────┴────────┘ 139 | " 140 | `; 141 | 142 | exports[`react-doc-generator return the proper message when given extensions not found 1`] = ` 143 | " 144 | 145 | REACT DOC GENERATOR v1.2.7 146 | by Marcin Borkowski  147 | Warning: Could not find any files matching the file type: \`*.4hs0\` OR \`*.kku4\` 148 | 149 | ┌──────┬────────────┬────────┐ 150 | │ Path │ Components │ Status │ 151 | └──────┴────────────┴────────┘ 152 | " 153 | `; 154 | -------------------------------------------------------------------------------- /dist/__tests__/generatereactdoc.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | 5 | var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); 6 | 7 | var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); 8 | 9 | var _generatereactdoc = _interopRequireDefault(require("../generatereactdoc")); 10 | 11 | var _path = _interopRequireDefault(require("path")); 12 | 13 | describe("GenerateReactDoc", function () { 14 | it("Whole thing should work", 15 | /*#__PURE__*/ 16 | (0, _asyncToGenerator2["default"])( 17 | /*#__PURE__*/ 18 | _regenerator["default"].mark(function _callee() { 19 | var output; 20 | return _regenerator["default"].wrap(function _callee$(_context) { 21 | for (; 1;) switch (_context.prev = _context.next) { 22 | case 0: 23 | return _context.next = 2, (0, _generatereactdoc["default"])({ 24 | sourceDir: _path["default"].resolve(__dirname, "../__mocks__"), 25 | extensions: ["js", "jsx"], 26 | outputDir: _path["default"].resolve(__dirname, "../__mocks__/output.md") 27 | }); 28 | 29 | case 2: 30 | output = _context.sent, expect(output).toMatchSnapshot(); 31 | 32 | case 4: 33 | case "end": 34 | return _context.stop(); 35 | } 36 | }, _callee); 37 | }))); 38 | }); -------------------------------------------------------------------------------- /dist/__tests__/processprop.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _generatereactdoc = require("../generatereactdoc"); 4 | 5 | describe('ProcessProp', function () { 6 | it('Process prop should not modify the default value if the type is a string', function () { 7 | var mockProp = { 8 | type: { 9 | name: 'string' 10 | }, 11 | defaultValue: { 12 | value: 'somestring' 13 | } 14 | }; 15 | var output = (0, _generatereactdoc.processProp)(mockProp); 16 | expect(output).toMatchSnapshot(); 17 | }), it('Process prop should not modify the default value if the type is not string but default value doesnt have special characters', function () { 18 | var mockProp = { 19 | type: { 20 | name: 'number' 21 | }, 22 | defaultValue: { 23 | value: 22 24 | } 25 | }; 26 | var output = (0, _generatereactdoc.processProp)(mockProp); 27 | expect(output).toMatchSnapshot(); 28 | }), it('Process prop should overwrite the default value when type is not string and there are invalid characters', function () { 29 | var mockProp = { 30 | type: { 31 | name: 'number' 32 | }, 33 | defaultValue: { 34 | value: '@#454' 35 | } 36 | }; 37 | var output = (0, _generatereactdoc.processProp)(mockProp); 38 | expect(output).toMatchSnapshot(); 39 | }); 40 | }); -------------------------------------------------------------------------------- /dist/__tests__/react-doc-generator.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | 5 | var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); 6 | 7 | var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); 8 | 9 | // https://travis-ci.org/marborkowski/react-doc-generator 10 | var path = require("path"); 11 | 12 | var fs = require("fs"); 13 | 14 | var spawn = require("child_process").spawn; 15 | 16 | function run(command, args) { 17 | var stdout = []; 18 | var stderr = []; 19 | return new Promise(function (resolve, reject) { 20 | var spawned = spawn(command, args); 21 | spawned.stdout.on("data", function (data) { 22 | stdout.push(data); 23 | }), spawned.stderr.on("data", function (data) { 24 | return stderr.push(data); 25 | }), spawned.on("close", function () { 26 | return resolve([stdout.join(""), stderr.join("")]); 27 | }), spawned.on("error", function (err) { 28 | throw err; 29 | }); 30 | })["catch"](function (error) { 31 | throw console.log(error, "Error"), error; 32 | }); 33 | } 34 | 35 | function loadDoc() { 36 | return new Promise(function (resolve, reject) { 37 | fs.readFile("./dist/DOCUMENTATION.md", "utf8", function (err, data) { 38 | err ? reject(err) : resolve(data); 39 | }); 40 | }); 41 | } 42 | 43 | var binPath = path.join(__dirname, "../../dist/react-doc-generator.js"); 44 | fs.chmodSync(binPath, "0777"), describe("react-doc-generator", function () { 45 | it("has the proper console output", 46 | /*#__PURE__*/ 47 | (0, _asyncToGenerator2["default"])( 48 | /*#__PURE__*/ 49 | _regenerator["default"].mark(function _callee() { 50 | var stdout, output; 51 | return _regenerator["default"].wrap(function _callee$(_context) { 52 | for (; 1;) switch (_context.prev = _context.next) { 53 | case 0: 54 | return _context.prev = 0, _context.next = 3, run("node", [binPath, "src/__mocks__", "-o", "./dist/DOCUMENTATION.md"]); 55 | 56 | case 3: 57 | stdout = _context.sent, output = stdout[0], expect(output).toMatchSnapshot(), _context.next = 11; 58 | break; 59 | 60 | case 8: 61 | throw _context.prev = 8, _context.t0 = _context["catch"](0), (console.error(_context.t0), _context.t0); 62 | 63 | case 11: 64 | case "end": 65 | return _context.stop(); 66 | } 67 | }, _callee, null, [[0, 8]]); 68 | }))), it("return the proper message when given extensions not found", 69 | /*#__PURE__*/ 70 | (0, _asyncToGenerator2["default"])( 71 | /*#__PURE__*/ 72 | _regenerator["default"].mark(function _callee2() { 73 | var stdout, output; 74 | return _regenerator["default"].wrap(function _callee2$(_context2) { 75 | for (; 1;) switch (_context2.prev = _context2.next) { 76 | case 0: 77 | return _context2.prev = 0, _context2.next = 3, run("node", [binPath, "src/__mocks__", "-o", "./dist/DOCUMENTATION.md", "-x", "4hs0,kku4"]); 78 | 79 | case 3: 80 | stdout = _context2.sent, output = stdout[0], expect(output).toMatchSnapshot(), _context2.next = 11; 81 | break; 82 | 83 | case 8: 84 | throw _context2.prev = 8, _context2.t0 = _context2["catch"](0), _context2.t0; 85 | 86 | case 11: 87 | case "end": 88 | return _context2.stop(); 89 | } 90 | }, _callee2, null, [[0, 8]]); 91 | }))), it("contains help section if no argument is available in query", 92 | /*#__PURE__*/ 93 | (0, _asyncToGenerator2["default"])( 94 | /*#__PURE__*/ 95 | _regenerator["default"].mark(function _callee3() { 96 | var stdout, output; 97 | return _regenerator["default"].wrap(function _callee3$(_context3) { 98 | for (; 1;) switch (_context3.prev = _context3.next) { 99 | case 0: 100 | return _context3.prev = 0, _context3.next = 3, run("node", [binPath, "-o", "./dist/DOCUMENTATION.md"]); 101 | 102 | case 3: 103 | stdout = _context3.sent, output = stdout[0], expect(output).toMatchSnapshot(), _context3.next = 11; 104 | break; 105 | 106 | case 8: 107 | throw _context3.prev = 8, _context3.t0 = _context3["catch"](0), _context3.t0; 108 | 109 | case 11: 110 | case "end": 111 | return _context3.stop(); 112 | } 113 | }, _callee3, null, [[0, 8]]); 114 | }))); 115 | }), describe("output file", function () { 116 | it("has needed values", 117 | /*#__PURE__*/ 118 | (0, _asyncToGenerator2["default"])( 119 | /*#__PURE__*/ 120 | _regenerator["default"].mark(function _callee4() { 121 | var result, lines; 122 | return _regenerator["default"].wrap(function _callee4$(_context4) { 123 | for (; 1;) switch (_context4.prev = _context4.next) { 124 | case 0: 125 | return _context4.prev = 0, _context4.next = 3, run("node", [binPath, "src/__mocks__", "-o", "./dist/DOCUMENTATION.md", "-t", "MyTitleXYZ"]); 126 | 127 | case 3: 128 | return _context4.next = 5, loadDoc(); 129 | 130 | case 5: 131 | result = _context4.sent, lines = result.split("\n"), expect(lines).toMatchSnapshot(), _context4.next = 13; 132 | break; 133 | 134 | case 10: 135 | throw _context4.prev = 10, _context4.t0 = _context4["catch"](0), _context4.t0; 136 | 137 | case 13: 138 | case "end": 139 | return _context4.stop(); 140 | } 141 | }, _callee4, null, [[0, 10]]); 142 | }))); 143 | }); -------------------------------------------------------------------------------- /dist/__tests__/utilities.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _generatereactdoc = require("../generatereactdoc"); 4 | 5 | describe('isDefaultValueTypeString', function () { 6 | it('Should return false if type of default value is not string', function () { 7 | var mockInput = { 8 | type: { 9 | name: 'number' 10 | }, 11 | defaultValue: { 12 | value: 23 13 | } 14 | }; 15 | var isString = (0, _generatereactdoc.isDefaultValueTypeString)(mockInput); 16 | expect(isString).toEqual(false); 17 | }), it('Should return false if type of default value is string but prop type is not string', function () { 18 | var mockInput = { 19 | type: { 20 | name: 'number' 21 | }, 22 | defaultValue: { 23 | value: 'sdsd' 24 | } 25 | }; 26 | var isString = (0, _generatereactdoc.isDefaultValueTypeString)(mockInput); 27 | expect(isString).toEqual(false); 28 | }), it('Should return null if prop was undefined', function () { 29 | var isString = (0, _generatereactdoc.isDefaultValueTypeString)(); 30 | expect(isString).toEqual(null); 31 | }), it('Should return true if prop type was string and typeof default value was also string', function () { 32 | var mockInput = { 33 | type: { 34 | name: 'string' 35 | }, 36 | defaultValue: { 37 | value: 'sdsd' 38 | } 39 | }; 40 | var isString = (0, _generatereactdoc.isDefaultValueTypeString)(mockInput); 41 | expect(isString).toEqual(true); 42 | }); 43 | }), describe('isInvalidDefaultValue', function () { 44 | it('Should return true for invalid input', function () { 45 | var isInValid = (0, _generatereactdoc.isInvalidDefaultValue)('3###'); 46 | expect(isInValid).toEqual(true); 47 | }), it('Should return false for valid string input', function () { 48 | var isValid = (0, _generatereactdoc.isInvalidDefaultValue)('3333'); 49 | expect(isValid).toEqual(false); 50 | }), it('Should return false for valid number input', function () { 51 | var isValid = (0, _generatereactdoc.isInvalidDefaultValue)(3333); 52 | expect(isValid).toEqual(false); 53 | }); 54 | }); -------------------------------------------------------------------------------- /dist/generatereactdoc.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports.isInvalidDefaultValue = exports.isDefaultValueTypeString = exports.getTypeOfProp = exports["default"] = void 0; 9 | exports.processProp = processProp; 10 | 11 | var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); 12 | 13 | var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); 14 | 15 | var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); 16 | 17 | var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); 18 | 19 | var _path = _interopRequireDefault(require("path")); 20 | 21 | var _reactDocgen = require("react-docgen"); 22 | 23 | var _util = require("util"); 24 | 25 | var _nodeDir = require("node-dir"); 26 | 27 | var _colors = _interopRequireDefault(require("colors")); 28 | 29 | var _package = _interopRequireDefault(require("../package.json")); 30 | 31 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } 32 | 33 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null == arguments[i] ? {} : arguments[i]; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } 34 | 35 | var readFilesPromise = (0, _util.promisify)(_nodeDir.readFiles); 36 | var templateData = { 37 | files: [], 38 | version: _package["default"].version 39 | }; 40 | 41 | var isDefaultValueTypeString = function isDefaultValueTypeString(prop) { 42 | return prop && prop.type ? prop.type.name === "string" && typeof prop.defaultValue.value === "string" : null; 43 | }; 44 | 45 | exports.isDefaultValueTypeString = isDefaultValueTypeString; 46 | 47 | var isInvalidDefaultValue = function isInvalidDefaultValue(value) { 48 | return /[^\w\s.&:\-+*,!@%$]+/gim.test(value); 49 | }; 50 | 51 | exports.isInvalidDefaultValue = isInvalidDefaultValue; 52 | 53 | var getTypeOfProp = function getTypeOfProp(prop) { 54 | if (!prop) return ""; 55 | if (prop.type) return prop.type; 56 | 57 | if (prop.flowType) { 58 | var typeName = prop.flowType.raw ? prop.flowType.raw : prop.flowType.name; 59 | return { 60 | name: typeName 61 | }; 62 | } 63 | }; 64 | 65 | exports.getTypeOfProp = getTypeOfProp; 66 | 67 | function processProp(prop) { 68 | var _prop$defaultValue = prop.defaultValue, 69 | defaultValue = _prop$defaultValue === void 0 ? {} : _prop$defaultValue; 70 | var isString = isDefaultValueTypeString(prop); 71 | var isInvalidValue = isInvalidDefaultValue(defaultValue.value); 72 | var processedType = getTypeOfProp(prop); 73 | var processedDefaultValue = defaultValue && isInvalidValue && isString === false ? "See code" : defaultValue.value; 74 | var processedDescription = prop.description ? prop.description.split("\n").map(function (text) { 75 | return text.replace(/(^\s+|\s+$)/, ""); 76 | }).map(function (hasValidValue) { 77 | return hasValidValue; 78 | }).join(" ") : ""; 79 | return _objectSpread(_objectSpread({}, prop), {}, { 80 | defaultValue: _objectSpread(_objectSpread({}, prop.defaultValue), {}, { 81 | value: processedDefaultValue 82 | }), 83 | description: processedDescription, 84 | type: processedType 85 | }); 86 | } 87 | 88 | function parseSingleFile(fileContent) { 89 | var components = (0, _reactDocgen.parse)(fileContent, _reactDocgen.resolver.findAllExportedComponentDefinitions); 90 | var componentObjects = components.map(function (component) { 91 | var description = component.description, 92 | displayName = component.displayName; 93 | var modifiedTitle = description && !displayName ? description.match(/^(.*)$/m)[0] : displayName; 94 | var modifiedDescription = null; 95 | description && (description.split("\n").length > 1 && (modifiedDescription = description.replace(/[\w\W]+?\n+?/, ""), modifiedDescription = modifiedDescription.replace(/(\n)/gm, " \n")), modifiedDescription = "".concat(modifiedDescription, " \n\n")); 96 | // validate default values 97 | var propEntries = Object.entries(component.props); 98 | var modifiedPropEntries = propEntries.map(function (_ref) { 99 | var _ref2 = (0, _slicedToArray2["default"])(_ref, 2), 100 | propName = _ref2[0], 101 | propObject = _ref2[1]; 102 | 103 | var modifiedProp = processProp(propObject); 104 | return [propName, modifiedProp]; 105 | }); 106 | var modife = modifiedPropEntries.reduce(function (accum, current) { 107 | var modifiedAccum = _objectSpread(_objectSpread({}, accum), {}, (0, _defineProperty2["default"])({}, current[0], current[1])); 108 | 109 | return modifiedAccum; 110 | }, {}); 111 | return _objectSpread(_objectSpread({}, component), {}, { 112 | title: modifiedTitle, 113 | description: modifiedDescription, 114 | props: modife 115 | }); 116 | }); 117 | return componentObjects; 118 | } 119 | 120 | function generateReactDocs(_x) { 121 | return _generateReactDocs.apply(this, arguments); 122 | } 123 | 124 | function _generateReactDocs() { 125 | return _generateReactDocs = (0, _asyncToGenerator2["default"])( 126 | /*#__PURE__*/ 127 | _regenerator["default"].mark(function _callee(_ref3) { 128 | var sourceDir, _ref3$extensions, extensions, _ref3$excludePatterns, excludePatterns, _ref3$ignoreDirectory, ignoreDirectory, cliOutput, inputPath, allExtensions; 129 | 130 | return _regenerator["default"].wrap(function _callee$(_context) { 131 | for (; 1;) switch (_context.prev = _context.next) { 132 | case 0: 133 | return sourceDir = _ref3.sourceDir, _ref3$extensions = _ref3.extensions, extensions = _ref3$extensions === void 0 ? [] : _ref3$extensions, _ref3$excludePatterns = _ref3.excludePatterns, excludePatterns = _ref3$excludePatterns === void 0 ? [] : _ref3$excludePatterns, _ref3$ignoreDirectory = _ref3.ignoreDirectory, ignoreDirectory = _ref3$ignoreDirectory === void 0 ? [] : _ref3$ignoreDirectory, cliOutput = [], inputPath = _path["default"].resolve(sourceDir), _context.next = 5, readFilesPromise(inputPath, { 134 | match: new RegExp("\\.(?:" + extensions.join("|") + ")$"), 135 | exclude: excludePatterns, 136 | excludeDir: ignoreDirectory 137 | }, function (err, content, filename, next) { 138 | if (err) throw console.log(err, "error"), err; 139 | 140 | try { 141 | var components = parseSingleFile(content); 142 | templateData.files.push({ 143 | filename: filename, 144 | components: components 145 | }), cliOutput.push([filename, components.length, _colors["default"].green("OK.")]); 146 | } catch (e) { 147 | console.error("In error", e), cliOutput.push([filename, 0, _colors["default"].red("You have to export at least one valid React Class!")]); 148 | } 149 | 150 | next(); 151 | }); 152 | 153 | case 5: 154 | if (templateData.files.length !== 0) { 155 | _context.next = 8; 156 | break; 157 | } 158 | 159 | allExtensions = extensions.map(function (ext) { 160 | return "`*.".concat(ext, "`"); 161 | }), console.log("".concat(_colors["default"].bold.yellow("Warning:"), " ").concat(_colors["default"].yellow("Could not find any files matching the file type: ".concat(allExtensions.join(" OR "))), "\n")); 162 | 163 | case 8: 164 | return _context.abrupt("return", [templateData, cliOutput]); 165 | 166 | case 9: 167 | case "end": 168 | return _context.stop(); 169 | } 170 | }, _callee); 171 | })), _generateReactDocs.apply(this, arguments); 172 | } 173 | 174 | var _default = generateReactDocs; 175 | exports["default"] = _default; -------------------------------------------------------------------------------- /dist/lib/command.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _require = require("commander"), 9 | program = _require.program; 10 | 11 | var pkg = require("../../package.json"); 12 | 13 | var _default = function Command() { 14 | var list = function list(val) { 15 | return val = val.replace(/[, ]+/g, ",").trim(), val.split(",").filter(function (value) { 16 | return value.length > 0; 17 | }); 18 | }; 19 | 20 | return program.version(pkg.version).usage(" [options]").option("-x, --extensions ", "Include only these file extensions. Default: js,jsx", list, ["js", "jsx"]).option("-i, --ignore ", "Folders to ignore. Default: node_modules,__tests__,__mocks__", list, ["node_modules", "__tests__", "__mocks__"]).option("-e, --exclude-patterns ", "Filename patterns to exclude. Default: []", list, []).option("-t, --title [value]", "Document title. Default: 'Components'", "Components").option("-o, --output ", "Markdown file to write. Default: 'DOCUMENTATION.MD'", "DOCUMENTATION.MD").parse(process.argv), program; 21 | }(); 22 | 23 | exports["default"] = _default; -------------------------------------------------------------------------------- /dist/react-doc-generator.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | 4 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 5 | 6 | var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); 7 | 8 | var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); 9 | 10 | var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); 11 | 12 | var _fs = _interopRequireDefault(require("fs")); 13 | 14 | var _path = _interopRequireDefault(require("path")); 15 | 16 | var _generatereactdoc = _interopRequireDefault(require("./generatereactdoc")); 17 | 18 | var _command = _interopRequireDefault(require("./lib/command.js")); 19 | 20 | var _handlebars = _interopRequireDefault(require("handlebars")); 21 | 22 | var _colors = _interopRequireDefault(require("colors")); 23 | 24 | var _cliTable = _interopRequireDefault(require("cli-table")); 25 | 26 | _handlebars["default"].registerHelper("inc", function (value, options) { 27 | return parseInt(value, 10) + 1; 28 | }), (0, _asyncToGenerator2["default"])( 29 | /*#__PURE__*/ 30 | _regenerator["default"].mark(function _callee() { 31 | var pkg, template, table, _yield$generateReactD, _yield$generateReactD2, templateData, cliOutput, outputFile; 32 | 33 | return _regenerator["default"].wrap(function _callee$(_context) { 34 | for (; 1;) switch (_context.prev = _context.next) { 35 | case 0: 36 | if (pkg = require("../package.json"), template = _handlebars["default"].compile("".concat(_fs["default"].readFileSync(_path["default"].join(__dirname, "template.handlebars")))), table = new _cliTable["default"]({ 37 | head: [_colors["default"].cyan("Path"), _colors["default"].cyan("Components"), _colors["default"].cyan("Status")] 38 | }), console.log(_colors["default"].white("\n\nREACT DOC GENERATOR v".concat(pkg.version))), console.log(_colors["default"].white("by Marcin Borkowski ")), _context.prev = 4, _command["default"].args.length === 1) { 39 | _context.next = 9; 40 | break; 41 | } 42 | 43 | console.log("".concat(_colors["default"].red("Please specify as the first argument!"))), _command["default"].help(), _context.next = 17; 44 | break; 45 | 46 | case 9: 47 | return _context.next = 11, (0, _generatereactdoc["default"])({ 48 | sourceDir: _command["default"].args[0], 49 | extensions: _command["default"].opts().extensions, 50 | excludePatterns: _command["default"].opts().excludePatterns, 51 | ignoreDirectory: _command["default"].opts().ignore 52 | }); 53 | 54 | case 11: 55 | _yield$generateReactD = _context.sent, _yield$generateReactD2 = (0, _slicedToArray2["default"])(_yield$generateReactD, 2), templateData = _yield$generateReactD2[0], cliOutput = _yield$generateReactD2[1], outputFile = _fs["default"].createWriteStream(_command["default"].opts().output), outputFile.write(template(templateData)), cliOutput.forEach(function (cliRow) { 56 | table.push(cliRow); 57 | }), console.log(table.toString()); 58 | 59 | case 17: 60 | _context.next = 22; 61 | break; 62 | 63 | case 19: 64 | _context.prev = 19, _context.t0 = _context["catch"](4), console.error("Error occurred", _context.t0); 65 | 66 | case 22: 67 | case "end": 68 | return _context.stop(); 69 | } 70 | }, _callee, null, [[4, 19]]); 71 | }))(); -------------------------------------------------------------------------------- /dist/template.handlebars: -------------------------------------------------------------------------------- 1 | {{#if documentTitle}} 2 | {{documentTitle}} 3 | ---------- 4 | {{/if}} 5 | 6 | {{#each files}} 7 | **{{filename}}** 8 | 9 | {{#each components}} 10 | ### {{inc @index}}. {{title}} 11 | 12 | {{{description}}} 13 | 14 | 15 | {{#if props}} 16 | Property | Type | Required | Default value | Description 17 | :--- | :--- | :--- | :--- | :--- 18 | {{#each props}} 19 | {{@key}}|{{type.name}}|{{#if required}}yes{{else}}no{{/if}}|{{defaultValue.value}}|{{description}} 20 | {{/each}} 21 | {{/if}} 22 | {{/each}} 23 | ----- 24 | {{/each}} 25 | 26 | This document was generated by the **React DOC Generator v{{version}}**. 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-doc-generator", 3 | "version": "1.2.8", 4 | "description": "Generate a simple React Component documentation in Markdown.", 5 | "main": "dist/index.js", 6 | "bin": { 7 | "react-doc-generator": "dist/react-doc-generator.js" 8 | }, 9 | "scripts": { 10 | "lint": "eslint src", 11 | "transpile": "babel --plugins minify-simplify src -D -d dist --ignore '__tests__,__mocks__' --presets=@babel/preset-env,@babel/preset-flow,@babel/preset-react", 12 | "watch": "babel src -D -d dist --ignore '__tests__,__mocks__' --watch", 13 | "pretest": "npm run transpile", 14 | "preversion": "npm run lint", 15 | "test": "jest" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/marborkowski/react-doc-generator.git" 20 | }, 21 | "author": "Marcin Borkowski", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/marborkowski/react-doc-generator/issues" 25 | }, 26 | "homepage": "https://github.com/marborkowski/react-doc-generator#readme", 27 | "keywords": [ 28 | "react", 29 | "react-docgen", 30 | "markdown", 31 | "documentation", 32 | "generator", 33 | "tool", 34 | "tools", 35 | "docgen", 36 | "readme file", 37 | "readme", 38 | "instruction", 39 | "guide", 40 | "list of components", 41 | "components", 42 | "spa", 43 | "webpack", 44 | "engine" 45 | ], 46 | "dependencies": { 47 | "cli-table": "^0.3.11", 48 | "colors": "^1.4.0", 49 | "commander": "9.1.0", 50 | "handlebars": "^4.7.7", 51 | "node-dir": "^0.1.17", 52 | "react-docgen": "^5.4.0" 53 | }, 54 | "peerDependencies": { 55 | "prop-types": "16 - 18", 56 | "react": "16 - 18", 57 | "react-dom": "16 - 18" 58 | }, 59 | "devDependencies": { 60 | "@babel/cli": "^7.17.6", 61 | "@babel/core": "^7.17.9", 62 | "@babel/plugin-proposal-class-properties": "^7.5.5", 63 | "@babel/plugin-transform-runtime": "^7.17.0", 64 | "@babel/preset-env": "^7.16.11", 65 | "@babel/preset-flow": "^7.16.7", 66 | "@babel/preset-react": "^7.16.7", 67 | "babel-eslint": "^10.1.0", 68 | "babel-jest": "^27.5.1", 69 | "babel-plugin-minify-simplify": "^0.5.1", 70 | "eslint": "^8.12.0", 71 | "eslint-config-react-app": "^7.0.0", 72 | "eslint-plugin-flowtype": "^8.0.3", 73 | "eslint-plugin-import": "^2.26.0", 74 | "eslint-plugin-jsx-a11y": "^6.5.1", 75 | "eslint-plugin-react": "^7.29.4", 76 | "jest-cli": "^27.5.1" 77 | }, 78 | "babel": { 79 | "presets": [ 80 | "env" 81 | ] 82 | }, 83 | "eslintConfig": { 84 | "extends": "react-app" 85 | }, 86 | "jest": { 87 | "roots": [ 88 | "src" 89 | ], 90 | "testRegex": "/__tests__/(.+?).test.js$" 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": { 7 | "esmodules": true 8 | } 9 | } 10 | ], 11 | "@babel/preset-flow", 12 | "@babel/preset-react" 13 | ], 14 | "plugins": ["minify-simplify", "@babel/plugin-transform-runtime"] 15 | } 16 | -------------------------------------------------------------------------------- /src/__mocks__/ExampleComponent.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var PropTypes = require('prop-types'); 3 | 4 | /** 5 | * General component description. 6 | * fdgdfgdf gfdgfdg fdgfdgdfg 7 | * gdfgfdgdfg dfgdfgfdg dfg df getDefaultPropsg fdgfd 8 | * gfdgfdgdfgfdg. 9 | * 10 | * 11 | * Example: 12 | * ```html 13 | * 14 | * ``` 15 | */ 16 | var Component = React.createClass({ 17 | propTypes: { 18 | /** 19 | * Description of prop "toe" has one break line 20 | * here following more comments and has default 21 | * empty string. 22 | */ 23 | toe: PropTypes.string, 24 | /** 25 | * Description of prop "finger". 26 | */ 27 | finger: PropTypes.string, 28 | /** 29 | * Description of prop "foo". 30 | */ 31 | foo: PropTypes.number, 32 | /** 33 | * Description of prop "bar" (a custom validation function). 34 | */ 35 | bar: function(props, propName, componentName) { 36 | // ... 37 | }, 38 | baz: PropTypes.oneOfType([ 39 | PropTypes.number, 40 | PropTypes.string 41 | ]), 42 | }, 43 | 44 | getDefaultProps: function() { 45 | return { 46 | finger: 'medium', 47 | toe: '', 48 | foo: 42, 49 | bar: 21 50 | }; 51 | }, 52 | 53 | render: function() { 54 | return ( 55 |
56 | Hello World! 57 |
58 | ); 59 | } 60 | }); 61 | 62 | module.exports = Component; 63 | -------------------------------------------------------------------------------- /src/__mocks__/OtherExampleComponent.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React from 'react'; 3 | 4 | type Component1PropType = { 5 | foo: number, 6 | /** 7 | * Description of prop "bar" (a custom validation function). 8 | */ 9 | bar: () => void, 10 | baz: number|string, 11 | onExit: () => void 12 | } 13 | /** 14 | * General component description. 15 | */ 16 | export class Component1 extends React.Component { 17 | 18 | static defaultProps = { 19 | foo: 10000099999, 20 | onExit: () => { 21 | console.debug('onExit'); 22 | } 23 | } 24 | 25 | render () { 26 | return (
Hello
); 27 | } 28 | } 29 | 30 | type Component2PropType = { 31 | /** 32 | * Description of prop "foo". 33 | */ 34 | foo: number, 35 | /** 36 | * Description of prop "bar" (a custom validation function). 37 | */ 38 | bar: () => mixed, 39 | baz: number | string, 40 | } 41 | /** 42 | * General another component description. 43 | * Blah blah blah... 44 | * fdfdfsdf 45 | * fdsfsd 46 | */ 47 | function Component2(props: Component2PropType) { 48 | return (
Hello
); 49 | } 50 | Component2.displayName = "DUPA" 51 | export default Component2; 52 | -------------------------------------------------------------------------------- /src/__mocks__/inside/OtherExampleComponent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | /** 4 | * General component description. 5 | */ 6 | export class Component1 extends React.Component { 7 | static propTypes = { 8 | /** 9 | * Description of prop "foo". 10 | */ 11 | foo: React.PropTypes.number, 12 | /** 13 | * Description of prop "bar" (a custom validation function). 14 | */ 15 | bar: function(props, propName, componentName) { 16 | // ... 17 | }, 18 | baz: React.PropTypes.oneOfType([ 19 | React.PropTypes.number, 20 | React.PropTypes.string 21 | ]), 22 | onExit: React.PropTypes.func 23 | } 24 | 25 | static defaultProps = { 26 | foo: 10000099999, 27 | onExit: () => { 28 | console.debug('onExit'); 29 | } 30 | } 31 | 32 | render () { 33 | return (
Hello
); 34 | } 35 | } 36 | 37 | /** 38 | * General another component description. 39 | * Blah blah blah... 40 | * fdfdfsdf 41 | * fdsfsd 42 | */ 43 | class Component2 extends React.Component { 44 | displayName = "DUPA"; 45 | static propTypes = { 46 | /** 47 | * Description of prop "foo". 48 | */ 49 | foo: React.PropTypes.number, 50 | /** 51 | * Description of prop "bar" (a custom validation function). 52 | */ 53 | bar: function(props, propName, componentName) { 54 | // ... 55 | }, 56 | baz: React.PropTypes.oneOfType([ 57 | React.PropTypes.number, 58 | React.PropTypes.string 59 | ]), 60 | } 61 | 62 | render () { 63 | return (
Hello
); 64 | } 65 | } 66 | 67 | export default Component2; 68 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/generatereactdoc.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`GenerateReactDoc Whole thing should work 1`] = ` 4 | Array [ 5 | Object { 6 | "files": Array [ 7 | Object { 8 | "components": Array [ 9 | Object { 10 | "description": "fdgdfgdf gfdgfdg fdgfdgdfg 11 | gdfgfdgdfg dfgdfgfdg dfg df getDefaultPropsg fdgfd 12 | gfdgfdgdfgfdg. 13 | 14 | 15 | Example: 16 | \`\`\`html 17 | 18 | \`\`\` 19 | 20 | ", 21 | "methods": Array [], 22 | "props": Object { 23 | "bar": Object { 24 | "defaultValue": Object { 25 | "computed": false, 26 | "value": "21", 27 | }, 28 | "description": "Description of prop \\"bar\\" (a custom validation function).", 29 | "required": false, 30 | "type": Object { 31 | "name": "custom", 32 | "raw": "function(props, propName, componentName) { 33 | // ... 34 | }", 35 | }, 36 | }, 37 | "baz": Object { 38 | "defaultValue": Object { 39 | "value": undefined, 40 | }, 41 | "description": "", 42 | "required": false, 43 | "type": Object { 44 | "name": "union", 45 | "value": Array [ 46 | Object { 47 | "name": "number", 48 | }, 49 | Object { 50 | "name": "string", 51 | }, 52 | ], 53 | }, 54 | }, 55 | "finger": Object { 56 | "defaultValue": Object { 57 | "computed": false, 58 | "value": "'medium'", 59 | }, 60 | "description": "Description of prop \\"finger\\".", 61 | "required": false, 62 | "type": Object { 63 | "name": "string", 64 | }, 65 | }, 66 | "foo": Object { 67 | "defaultValue": Object { 68 | "computed": false, 69 | "value": "42", 70 | }, 71 | "description": "Description of prop \\"foo\\".", 72 | "required": false, 73 | "type": Object { 74 | "name": "number", 75 | }, 76 | }, 77 | "toe": Object { 78 | "defaultValue": Object { 79 | "computed": false, 80 | "value": "''", 81 | }, 82 | "description": "Description of prop \\"toe\\" has one break line here following more comments and has default empty string.", 83 | "required": false, 84 | "type": Object { 85 | "name": "string", 86 | }, 87 | }, 88 | }, 89 | "title": "General component description.", 90 | }, 91 | ], 92 | "filename": "/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/ExampleComponent.js", 93 | }, 94 | Object { 95 | "components": Array [ 96 | Object { 97 | "description": "null 98 | 99 | ", 100 | "displayName": "Component1", 101 | "methods": Array [], 102 | "props": Object { 103 | "bar": Object { 104 | "defaultValue": Object { 105 | "value": undefined, 106 | }, 107 | "description": "Description of prop \\"bar\\" (a custom validation function).", 108 | "flowType": Object { 109 | "name": "signature", 110 | "raw": "() => void", 111 | "signature": Object { 112 | "arguments": Array [], 113 | "return": Object { 114 | "name": "void", 115 | }, 116 | }, 117 | "type": "function", 118 | }, 119 | "required": true, 120 | "type": Object { 121 | "name": "() => void", 122 | }, 123 | }, 124 | "baz": Object { 125 | "defaultValue": Object { 126 | "value": undefined, 127 | }, 128 | "description": "", 129 | "flowType": Object { 130 | "elements": Array [ 131 | Object { 132 | "name": "number", 133 | }, 134 | Object { 135 | "name": "string", 136 | }, 137 | ], 138 | "name": "union", 139 | "raw": "number|string", 140 | }, 141 | "required": true, 142 | "type": Object { 143 | "name": "number|string", 144 | }, 145 | }, 146 | "foo": Object { 147 | "defaultValue": Object { 148 | "computed": false, 149 | "value": "10000099999", 150 | }, 151 | "description": "", 152 | "flowType": Object { 153 | "name": "number", 154 | }, 155 | "required": false, 156 | "type": Object { 157 | "name": "number", 158 | }, 159 | }, 160 | "onExit": Object { 161 | "defaultValue": Object { 162 | "computed": false, 163 | "value": "() => { 164 | console.debug('onExit'); 165 | }", 166 | }, 167 | "description": "", 168 | "flowType": Object { 169 | "name": "signature", 170 | "raw": "() => void", 171 | "signature": Object { 172 | "arguments": Array [], 173 | "return": Object { 174 | "name": "void", 175 | }, 176 | }, 177 | "type": "function", 178 | }, 179 | "required": false, 180 | "type": Object { 181 | "name": "() => void", 182 | }, 183 | }, 184 | }, 185 | "title": "Component1", 186 | }, 187 | Object { 188 | "description": "Blah blah blah... 189 | fdfdfsdf 190 | fdsfsd 191 | 192 | ", 193 | "displayName": "DUPA", 194 | "methods": Array [], 195 | "props": Object { 196 | "bar": Object { 197 | "defaultValue": Object { 198 | "value": undefined, 199 | }, 200 | "description": "Description of prop \\"bar\\" (a custom validation function).", 201 | "flowType": Object { 202 | "name": "signature", 203 | "raw": "() => mixed", 204 | "signature": Object { 205 | "arguments": Array [], 206 | "return": Object { 207 | "name": "mixed", 208 | }, 209 | }, 210 | "type": "function", 211 | }, 212 | "required": true, 213 | "type": Object { 214 | "name": "() => mixed", 215 | }, 216 | }, 217 | "baz": Object { 218 | "defaultValue": Object { 219 | "value": undefined, 220 | }, 221 | "description": "", 222 | "flowType": Object { 223 | "elements": Array [ 224 | Object { 225 | "name": "number", 226 | }, 227 | Object { 228 | "name": "string", 229 | }, 230 | ], 231 | "name": "union", 232 | "raw": "number | string", 233 | }, 234 | "required": true, 235 | "type": Object { 236 | "name": "number | string", 237 | }, 238 | }, 239 | "foo": Object { 240 | "defaultValue": Object { 241 | "value": undefined, 242 | }, 243 | "description": "Description of prop \\"foo\\".", 244 | "flowType": Object { 245 | "name": "number", 246 | }, 247 | "required": true, 248 | "type": Object { 249 | "name": "number", 250 | }, 251 | }, 252 | }, 253 | "title": "DUPA", 254 | }, 255 | ], 256 | "filename": "/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/OtherExampleComponent.js", 257 | }, 258 | Object { 259 | "components": Array [ 260 | Object { 261 | "description": "null 262 | 263 | ", 264 | "displayName": "Component1", 265 | "methods": Array [], 266 | "props": Object { 267 | "bar": Object { 268 | "defaultValue": Object { 269 | "value": undefined, 270 | }, 271 | "description": "Description of prop \\"bar\\" (a custom validation function).", 272 | "required": false, 273 | "type": Object { 274 | "name": "custom", 275 | "raw": "function(props, propName, componentName) { 276 | // ... 277 | }", 278 | }, 279 | }, 280 | "baz": Object { 281 | "defaultValue": Object { 282 | "value": undefined, 283 | }, 284 | "description": "", 285 | "required": false, 286 | "type": Object { 287 | "name": "union", 288 | "value": Array [ 289 | Object { 290 | "name": "number", 291 | }, 292 | Object { 293 | "name": "string", 294 | }, 295 | ], 296 | }, 297 | }, 298 | "foo": Object { 299 | "defaultValue": Object { 300 | "computed": false, 301 | "value": "10000099999", 302 | }, 303 | "description": "Description of prop \\"foo\\".", 304 | "required": false, 305 | "type": Object { 306 | "name": "number", 307 | }, 308 | }, 309 | "onExit": Object { 310 | "defaultValue": Object { 311 | "computed": false, 312 | "value": "See code", 313 | }, 314 | "description": "", 315 | "required": false, 316 | "type": Object { 317 | "name": "func", 318 | }, 319 | }, 320 | }, 321 | "title": "Component1", 322 | }, 323 | Object { 324 | "description": "Blah blah blah... 325 | fdfdfsdf 326 | fdsfsd 327 | 328 | ", 329 | "displayName": "DUPA", 330 | "methods": Array [], 331 | "props": Object { 332 | "bar": Object { 333 | "defaultValue": Object { 334 | "value": undefined, 335 | }, 336 | "description": "Description of prop \\"bar\\" (a custom validation function).", 337 | "required": false, 338 | "type": Object { 339 | "name": "custom", 340 | "raw": "function(props, propName, componentName) { 341 | // ... 342 | }", 343 | }, 344 | }, 345 | "baz": Object { 346 | "defaultValue": Object { 347 | "value": undefined, 348 | }, 349 | "description": "", 350 | "required": false, 351 | "type": Object { 352 | "name": "union", 353 | "value": Array [ 354 | Object { 355 | "name": "number", 356 | }, 357 | Object { 358 | "name": "string", 359 | }, 360 | ], 361 | }, 362 | }, 363 | "foo": Object { 364 | "defaultValue": Object { 365 | "value": undefined, 366 | }, 367 | "description": "Description of prop \\"foo\\".", 368 | "required": false, 369 | "type": Object { 370 | "name": "number", 371 | }, 372 | }, 373 | }, 374 | "title": "DUPA", 375 | }, 376 | ], 377 | "filename": "/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/inside/OtherExampleComponent.js", 378 | }, 379 | ], 380 | "version": "1.2.7", 381 | }, 382 | Array [ 383 | Array [ 384 | "/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/ExampleComponent.js", 385 | 1, 386 | "OK.", 387 | ], 388 | Array [ 389 | "/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/OtherExampleComponent.js", 390 | 2, 391 | "OK.", 392 | ], 393 | Array [ 394 | "/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/inside/OtherExampleComponent.js", 395 | 2, 396 | "OK.", 397 | ], 398 | ], 399 | ] 400 | `; 401 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/processprop.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`ProcessProp Process prop should not modify the default value if the type is a string 1`] = ` 4 | Object { 5 | "defaultValue": Object { 6 | "value": "somestring", 7 | }, 8 | "description": "", 9 | "type": Object { 10 | "name": "string", 11 | }, 12 | } 13 | `; 14 | 15 | exports[`ProcessProp Process prop should not modify the default value if the type is not string but default value doesnt have special characters 1`] = ` 16 | Object { 17 | "defaultValue": Object { 18 | "value": 22, 19 | }, 20 | "description": "", 21 | "type": Object { 22 | "name": "number", 23 | }, 24 | } 25 | `; 26 | 27 | exports[`ProcessProp Process prop should overwrite the default value when type is not string and there are invalid characters 1`] = ` 28 | Object { 29 | "defaultValue": Object { 30 | "value": "See code", 31 | }, 32 | "description": "", 33 | "type": Object { 34 | "name": "number", 35 | }, 36 | } 37 | `; 38 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/react-doc-generator.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`output file has needed values 1`] = ` 4 | Array [ 5 | "", 6 | "**/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/ExampleComponent.js**", 7 | "", 8 | "### 1. General component description.", 9 | "", 10 | "fdgdfgdf gfdgfdg fdgfdgdfg ", 11 | "gdfgfdgdfg dfgdfgfdg dfg df getDefaultPropsg fdgfd ", 12 | "gfdgfdgdfgfdg. ", 13 | " ", 14 | " ", 15 | "Example: ", 16 | "\`\`\`html ", 17 | " ", 18 | "\`\`\` ", 19 | "", 20 | "", 21 | "", 22 | "", 23 | "Property | Type | Required | Default value | Description", 24 | ":--- | :--- | :--- | :--- | :---", 25 | "toe|string|no|''|Description of prop "toe" has one break line here following more comments and has default empty string.", 26 | "finger|string|no|'medium'|Description of prop "finger".", 27 | "foo|number|no|42|Description of prop "foo".", 28 | "bar|custom|no|21|Description of prop "bar" (a custom validation function).", 29 | "baz|union|no||", 30 | "-----", 31 | "**/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/OtherExampleComponent.js**", 32 | "", 33 | "### 1. Component1", 34 | "", 35 | "null ", 36 | "", 37 | "", 38 | "", 39 | "", 40 | "Property | Type | Required | Default value | Description", 41 | ":--- | :--- | :--- | :--- | :---", 42 | "foo|number|no|10000099999|", 43 | "bar|() => void|yes||Description of prop "bar" (a custom validation function).", 44 | "baz|number|string|yes||", 45 | "onExit|() => void|no|() => {", 46 | " console.debug('onExit');", 47 | "}|", 48 | "### 2. DUPA", 49 | "", 50 | "Blah blah blah... ", 51 | "fdfdfsdf ", 52 | "fdsfsd ", 53 | "", 54 | "", 55 | "", 56 | "", 57 | "Property | Type | Required | Default value | Description", 58 | ":--- | :--- | :--- | :--- | :---", 59 | "foo|number|yes||Description of prop "foo".", 60 | "bar|() => mixed|yes||Description of prop "bar" (a custom validation function).", 61 | "baz|number | string|yes||", 62 | "-----", 63 | "**/Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/inside/OtherExampleComponent.js**", 64 | "", 65 | "### 1. Component1", 66 | "", 67 | "null ", 68 | "", 69 | "", 70 | "", 71 | "", 72 | "Property | Type | Required | Default value | Description", 73 | ":--- | :--- | :--- | :--- | :---", 74 | "foo|number|no|10000099999|Description of prop "foo".", 75 | "bar|custom|no||Description of prop "bar" (a custom validation function).", 76 | "baz|union|no||", 77 | "onExit|func|no|See code|", 78 | "### 2. DUPA", 79 | "", 80 | "Blah blah blah... ", 81 | "fdfdfsdf ", 82 | "fdsfsd ", 83 | "", 84 | "", 85 | "", 86 | "", 87 | "Property | Type | Required | Default value | Description", 88 | ":--- | :--- | :--- | :--- | :---", 89 | "foo|number|no||Description of prop "foo".", 90 | "bar|custom|no||Description of prop "bar" (a custom validation function).", 91 | "baz|union|no||", 92 | "-----", 93 | "", 94 | "This document was generated by the **React DOC Generator v1.2.7**.", 95 | "", 96 | ] 97 | `; 98 | 99 | exports[`react-doc-generator contains help section if no argument is available in query 1`] = ` 100 | " 101 | 102 | REACT DOC GENERATOR v1.2.7 103 | by Marcin Borkowski  104 | Please specify as the first argument! 105 | Usage: react-doc-generator [options] 106 | 107 | Options: 108 | -V, --version output the version number 109 | -x, --extensions Include only these file extensions. Default: 110 | js,jsx (default: [\\"js\\",\\"jsx\\"]) 111 | -i, --ignore Folders to ignore. Default: 112 | node_modules,__tests__,__mocks__ (default: 113 | [\\"node_modules\\",\\"__tests__\\",\\"__mocks__\\"]) 114 | -e, --exclude-patterns Filename patterns to exclude. Default: [] 115 | (default: []) 116 | -t, --title [value] Document title. Default: 'Components' 117 | (default: \\"Components\\") 118 | -o, --output Markdown file to write. Default: 119 | 'DOCUMENTATION.MD' (default: 120 | \\"DOCUMENTATION.MD\\") 121 | -h, --help display help for command 122 | " 123 | `; 124 | 125 | exports[`react-doc-generator has the proper console output 1`] = ` 126 | " 127 | 128 | REACT DOC GENERATOR v1.2.7 129 | by Marcin Borkowski  130 | ┌──────────────────────────────────────────────────────────────────────────────────────────────────┬────────────┬────────┐ 131 | │ Path │ Components │ Status │ 132 | ├──────────────────────────────────────────────────────────────────────────────────────────────────┼────────────┼────────┤ 133 | │ /Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/ExampleComponent.js │ 1 │ OK. │ 134 | ├──────────────────────────────────────────────────────────────────────────────────────────────────┼────────────┼────────┤ 135 | │ /Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/OtherExampleComponent.js │ 2 │ OK. │ 136 | ├──────────────────────────────────────────────────────────────────────────────────────────────────┼────────────┼────────┤ 137 | │ /Users/marcin/Projects/Private/react-doc-generator/src/__mocks__/inside/OtherExampleComponent.js │ 2 │ OK. │ 138 | └──────────────────────────────────────────────────────────────────────────────────────────────────┴────────────┴────────┘ 139 | " 140 | `; 141 | 142 | exports[`react-doc-generator return the proper message when given extensions not found 1`] = ` 143 | " 144 | 145 | REACT DOC GENERATOR v1.2.7 146 | by Marcin Borkowski  147 | Warning: Could not find any files matching the file type: \`*.4hs0\` OR \`*.kku4\` 148 | 149 | ┌──────┬────────────┬────────┐ 150 | │ Path │ Components │ Status │ 151 | └──────┴────────────┴────────┘ 152 | " 153 | `; 154 | -------------------------------------------------------------------------------- /src/__tests__/generatereactdoc.test.js: -------------------------------------------------------------------------------- 1 | import generateReactDocs from "../generatereactdoc"; 2 | import path from "path"; 3 | 4 | describe("GenerateReactDoc", () => { 5 | it("Whole thing should work", async () => { 6 | const output = await generateReactDocs({ 7 | sourceDir: path.resolve(__dirname, "../__mocks__"), 8 | extensions: ["js", "jsx"], 9 | outputDir: path.resolve(__dirname, "../__mocks__/output.md"), 10 | }); 11 | expect(output).toMatchSnapshot(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/__tests__/processprop.test.js: -------------------------------------------------------------------------------- 1 | import { processProp } from '../generatereactdoc' 2 | 3 | describe('ProcessProp', () => { 4 | it('Process prop should not modify the default value if the type is a string', () => { 5 | const mockProp = { 6 | type: { 7 | name:'string' 8 | }, 9 | defaultValue: { 10 | value:'somestring' 11 | } 12 | } 13 | const output = processProp(mockProp) 14 | expect(output).toMatchSnapshot() 15 | }) 16 | it('Process prop should not modify the default value if the type is not string but default value doesnt have special characters', () => { 17 | const mockProp = { 18 | type: { 19 | name: 'number', 20 | }, 21 | defaultValue: { 22 | value:22 23 | } 24 | } 25 | const output = processProp(mockProp) 26 | expect(output).toMatchSnapshot() 27 | }) 28 | it('Process prop should overwrite the default value when type is not string and there are invalid characters', () => { 29 | const mockProp = { 30 | type: { 31 | name: 'number', 32 | }, 33 | defaultValue: { 34 | value:'@#454' 35 | } 36 | } 37 | const output = processProp(mockProp) 38 | expect(output).toMatchSnapshot() 39 | }) 40 | }) -------------------------------------------------------------------------------- /src/__tests__/react-doc-generator.test.js: -------------------------------------------------------------------------------- 1 | // https://travis-ci.org/marborkowski/react-doc-generator 2 | 3 | const path = require("path"); 4 | const fs = require("fs"); 5 | const spawn = require("child_process").spawn; 6 | 7 | function run(command, args) { 8 | let stdout = []; 9 | let stderr = []; 10 | 11 | return new Promise((resolve, reject) => { 12 | let spawned = spawn(command, args); 13 | spawned.stdout.on("data", (data) => { 14 | stdout.push(data); 15 | }); 16 | spawned.stderr.on("data", (data) => stderr.push(data)); 17 | spawned.on("close", () => resolve([stdout.join(""), stderr.join("")])); 18 | spawned.on("error", (err) => { 19 | throw err; 20 | }); 21 | }).catch((error) => { 22 | console.log(error, "Error"); 23 | throw error; 24 | }); 25 | } 26 | 27 | function loadDoc() { 28 | return new Promise((resolve, reject) => { 29 | fs.readFile("./dist/DOCUMENTATION.md", "utf8", function (err, data) { 30 | if (err) { 31 | reject(err); 32 | } else { 33 | resolve(data); 34 | } 35 | }); 36 | }); 37 | } 38 | let binPath = path.join(__dirname, "../../dist/react-doc-generator.js"); 39 | fs.chmodSync(binPath, "0777"); 40 | describe("react-doc-generator", () => { 41 | it("has the proper console output", async () => { 42 | try { 43 | const stdout = await run("node", [ 44 | binPath, 45 | "src/__mocks__", 46 | "-o", 47 | "./dist/DOCUMENTATION.md", 48 | ]); 49 | const output = stdout[0]; 50 | expect(output).toMatchSnapshot(); 51 | } catch (e) { 52 | console.error(e); 53 | throw e; 54 | } 55 | }); 56 | 57 | it("return the proper message when given extensions not found", async () => { 58 | try { 59 | const stdout = await run("node", [ 60 | binPath, 61 | "src/__mocks__", 62 | "-o", 63 | "./dist/DOCUMENTATION.md", 64 | "-x", 65 | "4hs0,kku4", 66 | ]); 67 | const output = stdout[0]; 68 | expect(output).toMatchSnapshot(); 69 | } catch (e) { 70 | throw e; 71 | } 72 | }); 73 | 74 | it("contains help section if no argument is available in query", async () => { 75 | try { 76 | const stdout = await run("node", [ 77 | binPath, 78 | "-o", 79 | "./dist/DOCUMENTATION.md", 80 | ]); 81 | const output = stdout[0]; 82 | expect(output).toMatchSnapshot(); 83 | } catch (e) { 84 | throw e; 85 | } 86 | }); 87 | }); 88 | describe("output file", () => { 89 | it("has needed values", async () => { 90 | try { 91 | await run("node", [ 92 | binPath, 93 | "src/__mocks__", 94 | "-o", 95 | "./dist/DOCUMENTATION.md", 96 | "-t", 97 | "MyTitleXYZ", 98 | ]); 99 | const result = await loadDoc(); 100 | const lines = result.split("\n"); 101 | expect(lines).toMatchSnapshot(); 102 | } catch (e) { 103 | throw e; 104 | } 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /src/__tests__/utilities.test.js: -------------------------------------------------------------------------------- 1 | import {isDefaultValueTypeString,isInvalidDefaultValue } from '../generatereactdoc' 2 | describe('isDefaultValueTypeString', () =>{ 3 | it('Should return false if type of default value is not string', () => { 4 | const mockInput = { 5 | type: {name: 'number'}, 6 | defaultValue: { 7 | value: 23 8 | } 9 | } 10 | const isString = isDefaultValueTypeString(mockInput) 11 | expect(isString).toEqual(false) 12 | }) 13 | it('Should return false if type of default value is string but prop type is not string', () => { 14 | const mockInput = { 15 | type: {name: 'number'}, 16 | defaultValue: { 17 | value: 'sdsd' 18 | } 19 | } 20 | const isString = isDefaultValueTypeString(mockInput) 21 | expect(isString).toEqual(false) 22 | }) 23 | it('Should return null if prop was undefined', () => { 24 | const isString = isDefaultValueTypeString() 25 | expect(isString).toEqual(null) 26 | }) 27 | it('Should return true if prop type was string and typeof default value was also string', () => { 28 | const mockInput = { 29 | type: {name: 'string'}, 30 | defaultValue: { 31 | value: 'sdsd' 32 | } 33 | } 34 | const isString = isDefaultValueTypeString(mockInput) 35 | expect(isString).toEqual(true) 36 | }) 37 | }) 38 | 39 | describe('isInvalidDefaultValue', () => { 40 | it('Should return true for invalid input', () => { 41 | const isInValid = isInvalidDefaultValue('3###') 42 | expect(isInValid).toEqual(true) 43 | }) 44 | it('Should return false for valid string input', () => { 45 | const isValid = isInvalidDefaultValue('3333') 46 | expect(isValid).toEqual(false) 47 | }) 48 | it('Should return false for valid number input', () => { 49 | const isValid = isInvalidDefaultValue(3333) 50 | expect(isValid).toEqual(false) 51 | }) 52 | }) -------------------------------------------------------------------------------- /src/generatereactdoc.js: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import { parse, resolver } from "react-docgen"; 3 | import { promisify } from "util"; 4 | import { readFiles } from "node-dir"; 5 | 6 | import Colors from "colors"; 7 | import pkg from "../package.json"; 8 | 9 | const readFilesPromise = promisify(readFiles); 10 | 11 | const templateData = { 12 | files: [], 13 | version: pkg.version, 14 | }; 15 | 16 | export const isDefaultValueTypeString = (prop) => { 17 | if (!prop || !prop.type) { 18 | return null; 19 | } 20 | return ( 21 | prop.type.name === "string" && typeof prop.defaultValue.value === "string" 22 | ); 23 | }; 24 | export const isInvalidDefaultValue = (value) => 25 | /[^\w\s.&:\-+*,!@%$]+/gim.test(value); 26 | export const getTypeOfProp = (prop) => { 27 | if (!prop) { 28 | return ""; 29 | } 30 | if (prop.type) { 31 | return prop.type; 32 | } else if (prop.flowType) { 33 | const typeName = prop.flowType.raw ? prop.flowType.raw : prop.flowType.name; 34 | return { 35 | name: typeName, 36 | }; 37 | } 38 | }; 39 | export function processProp(prop) { 40 | const { defaultValue = {} } = prop; 41 | const isString = isDefaultValueTypeString(prop); 42 | const isInvalidValue = isInvalidDefaultValue(defaultValue.value); 43 | const processedType = getTypeOfProp(prop); 44 | const processedDefaultValue = 45 | defaultValue && isInvalidValue && isString === false 46 | ? "See code" 47 | : defaultValue.value; 48 | const processedDescription = prop.description 49 | ? prop.description 50 | .split("\n") 51 | .map((text) => text.replace(/(^\s+|\s+$)/, "")) 52 | .map((hasValidValue) => hasValidValue) 53 | .join(" ") 54 | : ""; 55 | 56 | return { 57 | ...prop, 58 | defaultValue: { ...prop.defaultValue, value: processedDefaultValue }, 59 | description: processedDescription, 60 | type: processedType, 61 | }; 62 | } 63 | function parseSingleFile(fileContent) { 64 | const components = parse( 65 | fileContent, 66 | resolver.findAllExportedComponentDefinitions 67 | ); 68 | const componentObjects = components.map((component) => { 69 | const { description, displayName } = component; 70 | const modifiedTitle = 71 | description && !displayName 72 | ? description.match(/^(.*)$/m)[0] 73 | : displayName; 74 | let modifiedDescription = null; 75 | if (description) { 76 | if (description.split("\n").length > 1) { 77 | modifiedDescription = description.replace(/[\w\W]+?\n+?/, ""); 78 | modifiedDescription = modifiedDescription.replace(/(\n)/gm, " \n"); 79 | } 80 | modifiedDescription = `${modifiedDescription} \n\n`; 81 | } 82 | // validate default values 83 | const propEntries = Object.entries(component.props); 84 | const modifiedPropEntries = propEntries.map(([propName, propObject]) => { 85 | const modifiedProp = processProp(propObject); 86 | return [propName, modifiedProp]; 87 | }); 88 | const modife = modifiedPropEntries.reduce((accum, current) => { 89 | const modifiedAccum = { ...accum, [current[0]]: current[1] }; 90 | return modifiedAccum; 91 | }, {}); 92 | return { 93 | ...component, 94 | title: modifiedTitle, 95 | description: modifiedDescription, 96 | props: modife, 97 | }; 98 | }); 99 | return componentObjects; 100 | } 101 | async function generateReactDocs({ 102 | sourceDir, 103 | extensions = [], 104 | excludePatterns = [], 105 | ignoreDirectory = [], 106 | }) { 107 | const cliOutput = []; 108 | const inputPath = path.resolve(sourceDir); 109 | await readFilesPromise( 110 | inputPath, 111 | { 112 | match: new RegExp("\\.(?:" + extensions.join("|") + ")$"), 113 | exclude: excludePatterns, 114 | excludeDir: ignoreDirectory, 115 | }, 116 | (err, content, filename, next) => { 117 | if (err) { 118 | console.log(err, "error"); 119 | throw err; 120 | } 121 | try { 122 | const components = parseSingleFile(content); 123 | templateData.files.push({ filename, components }); 124 | cliOutput.push([filename, components.length, Colors.green(`OK.`)]); 125 | } catch (e) { 126 | console.error("In error", e); 127 | cliOutput.push([ 128 | filename, 129 | 0, 130 | Colors.red(`You have to export at least one valid React Class!`), 131 | ]); 132 | } 133 | 134 | next(); 135 | } 136 | ); 137 | if (templateData.files.length === 0) { 138 | let allExtensions = extensions.map((ext) => { 139 | return `\`*.${ext}\``; 140 | }); 141 | console.log( 142 | `${Colors.bold.yellow("Warning:")} ${Colors.yellow( 143 | `Could not find any files matching the file type: ${allExtensions.join( 144 | " OR " 145 | )}` 146 | )}\n` 147 | ); 148 | } 149 | 150 | return [templateData, cliOutput]; 151 | } 152 | export default generateReactDocs; 153 | -------------------------------------------------------------------------------- /src/lib/command.js: -------------------------------------------------------------------------------- 1 | const { program } = require("commander"); 2 | 3 | const pkg = require("../../package.json"); 4 | 5 | export default (function Command() { 6 | const list = (val) => { 7 | val = val.replace(/[, ]+/g, ",").trim(); 8 | return val.split(",").filter((value) => value.length > 0); 9 | }; 10 | 11 | program 12 | .version(pkg.version) 13 | .usage(` [options]`) 14 | .option( 15 | "-x, --extensions ", 16 | "Include only these file extensions. Default: js,jsx", 17 | list, 18 | ["js", "jsx"] 19 | ) 20 | .option( 21 | "-i, --ignore ", 22 | "Folders to ignore. Default: node_modules,__tests__,__mocks__", 23 | list, 24 | ["node_modules", "__tests__", "__mocks__"] 25 | ) 26 | .option( 27 | "-e, --exclude-patterns ", 28 | "Filename patterns to exclude. Default: []", 29 | list, 30 | [] 31 | ) 32 | .option( 33 | "-t, --title [value]", 34 | "Document title. Default: 'Components'", 35 | "Components" 36 | ) 37 | .option( 38 | "-o, --output ", 39 | "Markdown file to write. Default: 'DOCUMENTATION.MD'", 40 | "DOCUMENTATION.MD" 41 | ) 42 | .parse(process.argv); 43 | 44 | return program; 45 | })(); 46 | -------------------------------------------------------------------------------- /src/react-doc-generator.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import fs from "fs"; 3 | import path from "path"; 4 | import generateReactDocs from "./generatereactdoc"; 5 | import Command from "./lib/command.js"; 6 | import Handlebars from "handlebars"; 7 | 8 | import Colors from "colors"; 9 | import Table from "cli-table"; 10 | Handlebars.registerHelper("inc", (value, options) => { 11 | return parseInt(value, 10) + 1; 12 | }); 13 | (async () => { 14 | const pkg = require("../package.json"); 15 | const template = Handlebars.compile( 16 | `${fs.readFileSync(path.join(__dirname, "template.handlebars"))}` 17 | ); 18 | const table = new Table({ 19 | head: [ 20 | Colors.cyan("Path"), 21 | Colors.cyan("Components"), 22 | Colors.cyan("Status"), 23 | ], 24 | }); 25 | console.log(Colors.white(`\n\nREACT DOC GENERATOR v${pkg.version}`)); 26 | console.log(Colors.white(`by Marcin Borkowski `)); 27 | 28 | try { 29 | if (Command.args.length !== 1) { 30 | console.log( 31 | `${Colors.red("Please specify as the first argument!")}` 32 | ); 33 | Command.help(); 34 | } else { 35 | const [templateData, cliOutput] = await generateReactDocs({ 36 | sourceDir: Command.args[0], 37 | extensions: Command.opts().extensions, 38 | excludePatterns: Command.opts().excludePatterns, 39 | ignoreDirectory: Command.opts().ignore, 40 | }); 41 | const outputFile = fs.createWriteStream(Command.opts().output); 42 | outputFile.write(template(templateData)); 43 | cliOutput.forEach((cliRow) => { 44 | table.push(cliRow); 45 | }); 46 | console.log(table.toString()); 47 | } 48 | } catch (e) { 49 | console.error("Error occurred", e); 50 | } 51 | })(); 52 | -------------------------------------------------------------------------------- /src/template.handlebars: -------------------------------------------------------------------------------- 1 | {{#if documentTitle}} 2 | {{documentTitle}} 3 | ---------- 4 | {{/if}} 5 | 6 | {{#each files}} 7 | **{{filename}}** 8 | 9 | {{#each components}} 10 | ### {{inc @index}}. {{title}} 11 | 12 | {{{description}}} 13 | 14 | 15 | {{#if props}} 16 | Property | Type | Required | Default value | Description 17 | :--- | :--- | :--- | :--- | :--- 18 | {{#each props}} 19 | {{@key}}|{{type.name}}|{{#if required}}yes{{else}}no{{/if}}|{{defaultValue.value}}|{{description}} 20 | {{/each}} 21 | {{/if}} 22 | {{/each}} 23 | ----- 24 | {{/each}} 25 | 26 | This document was generated by the **React DOC Generator v{{version}}**. 27 | --------------------------------------------------------------------------------