diff --git a/.babelrc b/.babelrc
new file mode 100644
index 0000000..4ff711a
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,5 @@
+{
+ stage: 0,
+ loose: true,
+ optional: ["runtime"]
+}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c2402ef..0c8c248 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,12 @@
**Note**: Gaps between patch versions are faulty/broken releases.
+## v0.8.12
+
+- **New Feature**
+ - Documentation tool: `parse` module, fix #24
+ - Documentation tool: `toMarkdown` module, fix #25
+
## v0.8.11
- **New Feature**
diff --git a/README.md b/README.md
index cc20c15..fca9ddb 100644
--- a/README.md
+++ b/README.md
@@ -4,25 +4,46 @@
# Features
-- builds on [tcomb](https://github.com/gcanti/tcomb) library
-- additional fine grained type checks, nestable at arbitrary level
- **by default props are required**, a saner default since it's quite easy to forget `.isRequired`
- **checks for unwanted additional props**
+- **documentation** (types and comments) can be automatically extracted
+- additional fine grained type checks, nestable at arbitrary level
+- builds on [tcomb](https://github.com/gcanti/tcomb), [tcomb-validation](https://github.com/gcanti/tcomb-validation), [tcomb-doc](https://github.com/gcanti/tcomb-doc) libraries
+
+# Prop types
+
+## The `@props` decorator (ES7)
+
+For an equivalent implementation in ES5 see the `propTypes` function below.
+
+**Signature**
+
+```js
+type Props = {[key: string]: TcombType};
+
+@props(type: Props | TcombType, options?: Object)
+```
+
+where
+
+- `type` can be a map `string -> TcombType`, a `tcomb` struct or a refinement of a struct
+- `options`:
+ - `strict: boolean` (default `true`) if `true` checks for unwanted additional props
-# ES7 decorator
+**Example** (ES7)
```js
import { props, t } from 'tcomb-react';
const Gender = t.enums.of(['Male', 'Female'], 'Gender');
-const URL = t.subtype(t.String, s => s.startsWith('http'), 'URL');
+const URL = t.refinement(t.String, (s) => s.startsWith('http'), 'URL');
@props({
- name: t.String,
- surname: t.maybe(t.String),
- age: t.Number,
- gender: Gender,
- avatar: URL
+ name: t.String, // a required string
+ surname: t.maybe(t.String), // an optional string
+ age: t.Number, // a required number
+ gender: Gender, // an enum
+ avatar: URL // a refinement
})
class Card extends React.Component {
@@ -38,18 +59,9 @@ class Card extends React.Component {
}
```
-**Note**. `@props` can accepts a subtype of a struct (see [The subtype combinator](https://github.com/gcanti/tcomb/blob/master/GUIDE.md#the-subtype-combinator)).
+**Unwanted additional props**
-```js
-@props(t.subtype(t.struct({
- name: t.String,
- ...
-}), () => { ... }))
-```
-
-## Unwanted additional props
-
-By default tcomb-react checks unwanted additional props:
+By default `tcomb-react` checks for unwanted additional props:
```js
@props({
@@ -72,7 +84,7 @@ class Person extends React.Component {
```
-ouput to console:
+**Output**
```
Warning: Failed propType: [tcomb] Invalid additional prop(s):
@@ -84,42 +96,31 @@ Warning: Failed propType: [tcomb] Invalid additional prop(s):
supplied to Person.
```
-You can **opt-out** passing an additional argument `{ strict: false }`:
+**Note**. You can **opt-out** passing the `option` argument `{ strict: false }`.
-```js
-@props({
- name: t.String
-}, { strict: false })
-class Person extends React.Component {
+## The `propTypes` function (ES5)
- render() {
- return (
-
- );
- }
+**Signature**
-}
-```
+Same as `@props`.
-# ES5
+**Example** (ES5)
```js
var t = require('tcomb-react').t;
var propTypes = require('tcomb-react').propTypes;
var Gender = t.enums.of(['Male', 'Female'], 'Gender');
-var URL = t.subtype(t.String, function (s) { return s.startsWith('http'); }, 'URL');
+var URL = t.refinement(t.String, function (s) { return s.startsWith('http'); }, 'URL');
var Card = React.createClass({
propTypes: propTypes({
- name: t.String,
- surname: t.maybe(t.String),
- age: t.Number,
- gender: Gender,
- avatar: URL
+ name: t.String, // a required string
+ surname: t.maybe(t.String), // an optional string
+ age: t.Number, // a required number
+ gender: Gender, // an enum
+ avatar: URL // a refinement
}),
render: function () {
@@ -134,18 +135,151 @@ var Card = React.createClass({
});
```
+# Extract documentation from your components
+
+## The `parse` function
+
+Given a path to a component file returns a JSON / JavaScript blob containing **props types, default values and comments**.
+
+**Signature**
+
+```js
+(path: string | Array) => Object
+```
+
+**Example**
+
+Source
+
+```js
+import { t, props } from 'tcomb-react'
+
+/**
+ * Component description here
+ * @param name - name description here
+ * @param surname - surname description here
+ */
+
+@props({
+ name: t.String, // a required string
+ surname: t.maybe(t.String) // an optional string
+})
+export default class Card extends React.Component {
+
+ static defaultProps = {
+ surname: 'Canti' // default value for surname prop
+ }
+
+ render() {
+ return (
+
+
{this.props.name}
+
{this.props.surname}
+
+ );
+ }
+}
+```
+
+Usage
+
+```js
+import parse from 'tcomb-react/lib/parse'
+const json = parse('./components/Card.js')
+console.log(JSON.stringify(json, null, 2))
+```
+
+Output
+
+```json
+{
+ "name": "Card",
+ "description": "Component description here",
+ "props": {
+ "name": {
+ "kind": "irreducible",
+ "name": "String",
+ "required": true,
+ "description": "name description here"
+ },
+ "surname": {
+ "kind": "irreducible",
+ "name": "String",
+ "required": false,
+ "defaultValue": "Canti",
+ "description": "surname description here"
+ }
+ }
+}
+```
+
+**Note**. Since `parse` uses runtime type introspection, your components should be `require`able from your script (you may be required to shim the browser environment).
+
+**Parsing multiple components**
+
+```js
+import parse from 'tcomb-react/lib/parse'
+import path import 'path'
+import glob import 'glob'
+
+function getPath(file) {
+ return path.resolve(process.cwd(), file);
+}
+
+parse(glob.sync('./components/*.js').map(getPath));
+```
+
+## The `toMarkdown` function
+
+Given a JSON / JavaScript blob returned by `parse` returns a markdown containing the components documentation.
+
+**Signature**
+
+```js
+(json: Object) => string
+```
+
+**Example**
+
+Usage
+
+```js
+import parse from 'tcomb-react/lib/parse'
+import toMarkdown from 'tcomb-react/lib/toMarkdown'
+const json = parse('./components/Card.js')
+console.log(toMarkdown(json));
+```
+
+Output
+
+```markdown
+## Card
+
+Component description here
+
+**Props**
+
+- `name: String` name description here
+- `surname: String` (optional, default: `"Canti"`) surname description here
+
+```
+
# Augmented pre-defined types
+`tcomb-react` exports some useful pre-defined types:
+
- `t.ReactElement`
- `t.ReactNode`
- `t.ReactChild`
- `t.ReactChildren`
+**Example**
+
```js
import { props, t } from 'tcomb-react';
@props({
- children: t.ReactChild // allow only one child
+ children: t.ReactChild // only one child is allowed
})
class MyComponent extends React.Component {
diff --git a/lib/parse.js b/lib/parse.js
new file mode 100644
index 0000000..a957829
--- /dev/null
+++ b/lib/parse.js
@@ -0,0 +1,95 @@
+var fs = require('fs');
+var t = require('../').t;
+var toObject = require('tcomb-doc').toObject;
+var parseComments = require('get-comments');
+var parseJSDocs = require('doctrine').parse;
+
+// (path: t.String) => { description: t.String, tags: Array }
+function getComments(path) {
+ var source = fs.readFileSync(path, 'utf8');
+ var comments = parseComments(source, true);
+ var values = comments.map(function (comment) {
+ return comment.value;
+ });
+ return parseJSDocs(values.join('\n'), { unwrap: true });
+}
+
+// (comments: t.Object) => { component, props: {} }
+function getDescriptions(comments) {
+ var ret = {
+ component: comments.description || null,
+ props: {}
+ };
+ comments.tags.forEach(function (tag) {
+ if (tag.name) {
+ ret.props[tag.name] = tag.description || null;
+ }
+ });
+ return ret;
+}
+
+// (exports: t.Object) => ReactComponent
+function getComponent(defaultExport) {
+ if (defaultExport['default']) { // eslint-disable-line dot-notation
+ defaultExport = defaultExport['default']; // eslint-disable-line dot-notation
+ }
+ return defaultExport.propTypes ? defaultExport : null;
+}
+
+// (component: ReactComponent) => t.String
+function getComponentName(component) {
+ return component.name;
+}
+
+// (component: ReactComponent) => TcombType
+function getPropsType(component) {
+ var propTypes = component.propTypes;
+ var props = {};
+ Object.keys(propTypes).forEach(function (k) {
+ if (k !== '__strict__' && k !== '__subtype__') {
+ props[k] = propTypes[k].tcomb;
+ }
+ });
+ if (propTypes.hasOwnProperty('__subtype__')) {
+ return t.refinement(t.struct(props), propTypes.__subtype__.predicate);
+ }
+ return t.struct(props);
+}
+
+// (component: ReactComponent) => t.Object
+function getDefaultProps(component) {
+ return component.defaultProps || {};
+}
+
+// (path: t.String) => { name, description, props }
+function parse(path) {
+ if (t.Array.is(path)) {
+ return path.map(parse).filter(Boolean);
+ }
+ var component = getComponent(require(path));
+ if (component) {
+ var comments = getComments(path);
+ var descriptions = getDescriptions(comments);
+ var type = toObject(getPropsType(component));
+ var props = type.kind === 'refinement' ? type.type.props : type.props;
+ var defaultProps = getDefaultProps(component);
+ var name = getComponentName(component);
+ for (var prop in props) {
+ if (props.hasOwnProperty(prop)) {
+ if (defaultProps.hasOwnProperty(prop)) {
+ props[prop].defaultValue = defaultProps[prop];
+ }
+ if (descriptions.props.hasOwnProperty(prop)) {
+ props[prop].description = descriptions.props[prop];
+ }
+ }
+ }
+ return {
+ name: name,
+ description: descriptions.component,
+ props: props
+ };
+ }
+}
+
+module.exports = parse;
diff --git a/lib/toMarkdown.js b/lib/toMarkdown.js
new file mode 100644
index 0000000..e352d72
--- /dev/null
+++ b/lib/toMarkdown.js
@@ -0,0 +1,17 @@
+var t = require('../').t;
+
+function getProps(props) {
+ return Object.keys(props).map(function (k) {
+ var prop = props[k];
+ return '- `' + k + ': ' + prop.name + '` ' + (prop.required ? '' : '(optional' + (t.Nil.is(prop.defaultValue) ? '' : ', default: `' + JSON.stringify(prop.defaultValue) + '`') + ') ') + ( prop.description || '');
+ }).join('\n');
+}
+
+function toMarkdown(json) {
+ if (t.Array.is(json)) {
+ return json.map(toMarkdown).join('\n');
+ }
+ return '## ' + json.name + '\n' + (json.description ? '\n' + json.description + '\n' : '') + '\n**Props**\n\n' + getProps(json.props) + '\n';
+}
+
+module.exports = toMarkdown;
diff --git a/package.json b/package.json
index 484db7e..7e2871e 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,15 @@
{
"name": "tcomb-react",
- "version": "0.8.11",
+ "version": "0.8.12",
"description": "Type checking for React components",
"main": "index.js",
+ "files": [
+ "index.js",
+ "lib"
+ ],
"scripts": {
- "lint": "eslint index.js test",
- "test": "npm run lint && npm run mocha",
- "mocha": "mocha"
+ "lint": "eslint index.js lib test",
+ "test": "mocha --compilers js:babel/register"
},
"repository": {
"type": "git",
@@ -19,14 +22,20 @@
},
"homepage": "https://github.com/gcanti/tcomb-react",
"dependencies": {
+ "doctrine": "0.7.2",
+ "get-comments": "1.0.1",
+ "tcomb-doc": "^0.4.0",
"tcomb-validation": "^2.2.0",
"react": ">=0.13.0"
},
"devDependencies": {
- "babel-eslint": "^3.1.23",
- "eslint": "^0.22.1",
- "eslint-plugin-react": "^2.7.1",
- "mocha": "^2.2.5"
+ "babel": "5.8.34",
+ "babel-core": "5.8.34",
+ "babel-runtime": "5.8.34",
+ "babel-eslint": "3.1.30",
+ "eslint": "0.22.1",
+ "eslint-plugin-react": "2.7.1",
+ "mocha": "2.3.2"
},
"tags": [
"tcomb",
diff --git a/test/fixtures/parse/default/Actual.js b/test/fixtures/parse/default/Actual.js
new file mode 100644
index 0000000..6230b97
--- /dev/null
+++ b/test/fixtures/parse/default/Actual.js
@@ -0,0 +1,20 @@
+import React from 'react';
+import { props, t } from '../../../../.';
+
+@props({
+ name: t.maybe(t.String)
+})
+export default class Component extends React.Component {
+
+ static defaultProps = {
+ name: 'Giulio'
+ }
+
+ render() {
+ return (
+
+ );
+ }
+}
diff --git a/test/fixtures/parse/default/expected.json b/test/fixtures/parse/default/expected.json
new file mode 100644
index 0000000..e79ffcc
--- /dev/null
+++ b/test/fixtures/parse/default/expected.json
@@ -0,0 +1,12 @@
+{
+ "name": "Component",
+ "description": null,
+ "props": {
+ "name": {
+ "kind": "irreducible",
+ "name": "String",
+ "required": false,
+ "defaultValue": "Giulio"
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/fixtures/parse/description/Actual.js b/test/fixtures/parse/description/Actual.js
new file mode 100644
index 0000000..9fbf42a
--- /dev/null
+++ b/test/fixtures/parse/description/Actual.js
@@ -0,0 +1,20 @@
+import React from 'react';
+import { props, t } from '../../../../.';
+
+/**
+ * component description
+ */
+
+@props({
+ name: t.String
+})
+export default class Component extends React.Component {
+
+ render() {
+ return (
+
+ );
+ }
+}
diff --git a/test/fixtures/parse/description/expected.json b/test/fixtures/parse/description/expected.json
new file mode 100644
index 0000000..43c13b9
--- /dev/null
+++ b/test/fixtures/parse/description/expected.json
@@ -0,0 +1,11 @@
+{
+ "name": "Component",
+ "description": "component description",
+ "props": {
+ "name": {
+ "kind": "irreducible",
+ "name": "String",
+ "required": true
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/fixtures/parse/example/Actual.js b/test/fixtures/parse/example/Actual.js
new file mode 100644
index 0000000..fa635a0
--- /dev/null
+++ b/test/fixtures/parse/example/Actual.js
@@ -0,0 +1,28 @@
+import React from 'react';
+import { props, t } from '../../../../.';
+
+/**
+ * Component description here
+ * @param name - name description here
+ * @param surname - surname description here
+ */
+
+@props({
+ name: t.String, // a required string
+ surname: t.maybe(t.String) // an optional string
+})
+export default class Card extends React.Component {
+
+ static defaultProps = {
+ surname: 'Canti'
+ }
+
+ render() {
+ return (
+
+
{this.props.name}
+
{this.props.surname}
+
+ );
+ }
+}
diff --git a/test/fixtures/parse/example/expected.json b/test/fixtures/parse/example/expected.json
new file mode 100644
index 0000000..b9d9e7c
--- /dev/null
+++ b/test/fixtures/parse/example/expected.json
@@ -0,0 +1,19 @@
+{
+ "name": "Card",
+ "description": "Component description here",
+ "props": {
+ "name": {
+ "kind": "irreducible",
+ "name": "String",
+ "required": true,
+ "description": "name description here"
+ },
+ "surname": {
+ "kind": "irreducible",
+ "name": "String",
+ "required": false,
+ "defaultValue": "Canti",
+ "description": "surname description here"
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/fixtures/parse/optional/Actual.js b/test/fixtures/parse/optional/Actual.js
new file mode 100644
index 0000000..636bdbc
--- /dev/null
+++ b/test/fixtures/parse/optional/Actual.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { props, t } from '../../../../.';
+
+@props({
+ name: t.maybe(t.String)
+})
+export default class Component extends React.Component {
+
+ render() {
+ return (
+
+ );
+ }
+}
diff --git a/test/fixtures/parse/optional/expected.json b/test/fixtures/parse/optional/expected.json
new file mode 100644
index 0000000..3bf4a09
--- /dev/null
+++ b/test/fixtures/parse/optional/expected.json
@@ -0,0 +1,11 @@
+{
+ "name": "Component",
+ "description": null,
+ "props": {
+ "name": {
+ "kind": "irreducible",
+ "name": "String",
+ "required": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/fixtures/parse/prop-description/Actual.js b/test/fixtures/parse/prop-description/Actual.js
new file mode 100644
index 0000000..d07f541
--- /dev/null
+++ b/test/fixtures/parse/prop-description/Actual.js
@@ -0,0 +1,20 @@
+import React from 'react';
+import { props, t } from '../../../../.';
+
+/**
+ * @param name - prop description
+ */
+
+@props({
+ name: t.String
+})
+export default class Component extends React.Component {
+
+ render() {
+ return (
+
+ );
+ }
+}
diff --git a/test/fixtures/parse/prop-description/expected.json b/test/fixtures/parse/prop-description/expected.json
new file mode 100644
index 0000000..612affa
--- /dev/null
+++ b/test/fixtures/parse/prop-description/expected.json
@@ -0,0 +1,12 @@
+{
+ "name": "Component",
+ "description": null,
+ "props": {
+ "name": {
+ "kind": "irreducible",
+ "name": "String",
+ "required": true,
+ "description": "prop description"
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/fixtures/parse/require/Actual.js b/test/fixtures/parse/require/Actual.js
new file mode 100644
index 0000000..4d10800
--- /dev/null
+++ b/test/fixtures/parse/require/Actual.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import { props, t } from '../../../../.';
+import User from './User';
+
+/**
+ * Component description here
+ * name and surname must be both nil or both specified
+ * @param name - name description
+ * @param surname - surname description
+ */
+
+const Props = t.refinement(t.struct({
+ name: t.maybe(User.meta.props.name),
+ surname: t.maybe(User.meta.props.surname)
+}), (x) => t.Nil.is(x.name) === t.Nil.is(x.surname));
+
+@props(Props)
+export default class Component extends React.Component {
+
+ static defaultProps = {
+ name: 'Giulio',
+ surname: 'Canti'
+ }
+
+ render() {
+ return (
+
+
{this.props.name}
+
{this.props.surname}
+
+ );
+ }
+}
diff --git a/test/fixtures/parse/require/User.js b/test/fixtures/parse/require/User.js
new file mode 100644
index 0000000..ede118a
--- /dev/null
+++ b/test/fixtures/parse/require/User.js
@@ -0,0 +1,6 @@
+import { t } from '../../../../.';
+
+export default t.struct({
+ name: t.String,
+ surname: t.String
+}, 'User');
diff --git a/test/fixtures/parse/require/expected.json b/test/fixtures/parse/require/expected.json
new file mode 100644
index 0000000..7ac6311
--- /dev/null
+++ b/test/fixtures/parse/require/expected.json
@@ -0,0 +1,20 @@
+{
+ "name": "Component",
+ "description": "Component description here\nname and surname must be both nil or both specified",
+ "props": {
+ "name": {
+ "kind": "irreducible",
+ "name": "String",
+ "required": false,
+ "defaultValue": "Giulio",
+ "description": "name description"
+ },
+ "surname": {
+ "kind": "irreducible",
+ "name": "String",
+ "required": false,
+ "defaultValue": "Canti",
+ "description": "surname description"
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/fixtures/parse/required/Actual.js b/test/fixtures/parse/required/Actual.js
new file mode 100644
index 0000000..0b597f0
--- /dev/null
+++ b/test/fixtures/parse/required/Actual.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { props, t } from '../../../../.';
+
+@props({
+ name: t.String
+})
+export default class Component extends React.Component {
+
+ render() {
+ return (
+
+ );
+ }
+}
diff --git a/test/fixtures/parse/required/expected.json b/test/fixtures/parse/required/expected.json
new file mode 100644
index 0000000..9bb67c3
--- /dev/null
+++ b/test/fixtures/parse/required/expected.json
@@ -0,0 +1,11 @@
+{
+ "name": "Component",
+ "description": null,
+ "props": {
+ "name": {
+ "kind": "irreducible",
+ "name": "String",
+ "required": true
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/fixtures/toMarkdown/default/expected.md b/test/fixtures/toMarkdown/default/expected.md
new file mode 100644
index 0000000..a1fc0cc
--- /dev/null
+++ b/test/fixtures/toMarkdown/default/expected.md
@@ -0,0 +1,5 @@
+## Component
+
+**Props**
+
+- `name: String` (optional, default: `"Giulio"`)
diff --git a/test/fixtures/toMarkdown/description/expected.md b/test/fixtures/toMarkdown/description/expected.md
new file mode 100644
index 0000000..a16e27f
--- /dev/null
+++ b/test/fixtures/toMarkdown/description/expected.md
@@ -0,0 +1,7 @@
+## Component
+
+component description
+
+**Props**
+
+- `name: String`
diff --git a/test/fixtures/toMarkdown/example/expected.md b/test/fixtures/toMarkdown/example/expected.md
new file mode 100644
index 0000000..ec6e79c
--- /dev/null
+++ b/test/fixtures/toMarkdown/example/expected.md
@@ -0,0 +1,8 @@
+## Card
+
+Component description here
+
+**Props**
+
+- `name: String` name description here
+- `surname: String` (optional, default: `"Canti"`) surname description here
\ No newline at end of file
diff --git a/test/fixtures/toMarkdown/optional/expected.md b/test/fixtures/toMarkdown/optional/expected.md
new file mode 100644
index 0000000..86f0041
--- /dev/null
+++ b/test/fixtures/toMarkdown/optional/expected.md
@@ -0,0 +1,5 @@
+## Component
+
+**Props**
+
+- `name: String` (optional)
diff --git a/test/fixtures/toMarkdown/prop-description/expected.md b/test/fixtures/toMarkdown/prop-description/expected.md
new file mode 100644
index 0000000..0730594
--- /dev/null
+++ b/test/fixtures/toMarkdown/prop-description/expected.md
@@ -0,0 +1,5 @@
+## Component
+
+**Props**
+
+- `name: String` prop description
diff --git a/test/fixtures/toMarkdown/require/expected.md b/test/fixtures/toMarkdown/require/expected.md
new file mode 100644
index 0000000..10a4a9e
--- /dev/null
+++ b/test/fixtures/toMarkdown/require/expected.md
@@ -0,0 +1,9 @@
+## Component
+
+Component description here
+name and surname must be both nil or both specified
+
+**Props**
+
+- `name: String` (optional, default: `"Giulio"`) name description
+- `surname: String` (optional, default: `"Canti"`) surname description
\ No newline at end of file
diff --git a/test/fixtures/toMarkdown/required/expected.md b/test/fixtures/toMarkdown/required/expected.md
new file mode 100644
index 0000000..b326a3c
--- /dev/null
+++ b/test/fixtures/toMarkdown/required/expected.md
@@ -0,0 +1,5 @@
+## Component
+
+**Props**
+
+- `name: String`
diff --git a/test/test.js b/test/test.js
index 156818a..3a315ab 100644
--- a/test/test.js
+++ b/test/test.js
@@ -8,6 +8,10 @@ var library = require('../index');
var getPropTypes = library.propTypes;
var ReactElement = library.ReactElement;
var ReactNode = library.ReactNode;
+var path = require('path');
+var fs = require('fs');
+var parse = require('../lib/parse');
+var toMarkdown = require('../lib/toMarkdown');
function throwsWithMessage(f, message) {
assert.throws(f, function (err) {
@@ -219,3 +223,43 @@ describe('pre-defined types', function () {
});
});
+
+var skipDirectories = {
+ '.DS_Store': 1
+};
+
+describe('parse', function () {
+ var fixturesDir = path.join(__dirname, 'fixtures/parse');
+ fs.readdirSync(fixturesDir).map(function (caseName) {
+ if ((caseName in skipDirectories)) {
+ return;
+ }
+ it(caseName, function () {
+ var fixtureDir = path.join(fixturesDir, caseName);
+ var filepath = path.join(fixtureDir, 'Actual.js');
+ var expected = require(path.join(fixtureDir, 'expected.json'));
+ assert.deepEqual(JSON.stringify(parse(filepath)), JSON.stringify(expected));
+ });
+ });
+});
+
+function trim(str) {
+ return str.replace(/^\s+|\s+$/, '');
+}
+
+describe('toMarkdown', function () {
+ var fixturesDir = path.join(__dirname, 'fixtures/toMarkdown');
+ fs.readdirSync(fixturesDir).map(function (caseName) {
+ if ((caseName in skipDirectories)) {
+ return;
+ }
+ it(caseName, function () {
+ var actualFixtureDir = path.join(__dirname, 'fixtures/parse', caseName);
+ var filepath = path.join(actualFixtureDir, 'Actual.js');
+ var fixtureDir = path.join(fixturesDir, caseName);
+ const expected = fs.readFileSync(path.join(fixtureDir, 'expected.md')).toString();
+ assert.equal(trim(toMarkdown(parse(filepath))), trim(expected));
+ });
+ });
+});
+