diff --git a/package.json b/package.json index e313159..e896142 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "scripts": { "fetch:ios:sdk": "node ./scripts/download-mapbox-gl-native-ios-if-on-mac.js 3.6.1", - "docs:parse": "react-docgen ./javascript/components --pretty -o ./docs/docs.json", + "docs:parse": "node ./scripts/generateDocJSON.js", "docs:build:md": "node ./scripts/buildMarkdown.js", "docs:generate": "npm run docs:parse && npm run docs:build:md", "preinstall": "npm run fetch:ios:sdk", @@ -37,7 +37,9 @@ "eslint": "^3.19.0", "eslint-config-strict-react": "^8.0.1", "eslint-plugin-import": "^2.7.0", - "eslint-plugin-react": "^5.2.2" + "eslint-plugin-react": "^5.2.2", + "node-dir": "^0.1.17", + "react-docgen": "^2.18.0" }, "dependencies": { "@turf/helpers": "^4.6.0" diff --git a/scripts/buildMarkdown.js b/scripts/buildMarkdown.js index 0acc161..c09cac0 100644 --- a/scripts/buildMarkdown.js +++ b/scripts/buildMarkdown.js @@ -11,9 +11,46 @@ class MarkdownBuilder { return Object.keys(this.json); } - getComponentName (componenPath) { - // Example: javascript/components/MapView.js - return `${componenPath.split('/').pop()}`.replace('.js', ''); + getComponentName (fileName) { + return `${fileName}`.replace('.js', ''); + } + + getMethodSignature (method) { + const params = method.params.map((param, i) => { + const isOptional = param.optional; + + let name = ''; + + if (i !== 0) { + name += ', '; + } + + name += param.name; + return isOptional ? `[${name}]` : name; + }).join(''); + + return `${method.name}(${params})`; + } + + getMethodExamples (method) { + return method.examples.map((example) => { + return ` + +\`\`\`javascript +${example.trim()} +\`\`\` + +`; + }).join(''); + } + + getMethodArguments (method) { +return ` +| Name | Type | Required | Description | +| ---- | :--: | :------: | :----------: | +${method.params.map((param) => { + return `| \`${param.name}\` | \`${param.type.name}\` | \`${param.optional ? 'No' : 'Yes'}\` | ${param.description} |`; +}).join('\n')}`; } generateComponentHeaderMarkdown (componentJSON, componentName) { @@ -37,23 +74,45 @@ class MarkdownBuilder { return ` -#### props +### props | Prop | Type | Default | Required | Description | -| ---- | :--: | :-----: | :------: | ----------: | +| ---- | :--: | :-----: | :------: | :----------: | ${props.map((prop) => { return `| ${prop.name} | \`${prop.type}\` | \`${prop.default}\` | \`${prop.required}\` | ${prop.description} |` }).join('\n')} - ` + `; } - generateComponentFile (componentPath) { + generateComponentMethodsMarkdown (componentJSON) { + const methods = componentJSON.methods; + + if (!Array.isArray(methods) || !methods.length) { + return ''; + } + + return ` +### methods + +${methods.map((method) => { + return ` +#### ${this.getMethodSignature(method)} + +##### arguments +${this.getMethodArguments(method)} + +${this.getMethodExamples(method)}` +}).join('\n')}`; + + } + + generateComponentFile (componentName) { let fileContents = ''; - const componentJSON = this.json[componentPath]; - const componentName = this.getComponentName(componentPath); + const componentJSON = this.json[componentName]; fileContents += this.generateComponentHeaderMarkdown(componentJSON, componentName); fileContents += this.generateComponentPropsMarkdown(componentJSON); + fileContents += this.generateComponentMethodsMarkdown(componentJSON); fs.writeFileSync(path.join(__dirname, '..', 'docs', `${componentName}.md`), fileContents); } diff --git a/scripts/generateDocJSON.js b/scripts/generateDocJSON.js new file mode 100644 index 0000000..6f95fe8 --- /dev/null +++ b/scripts/generateDocJSON.js @@ -0,0 +1,64 @@ +const docgen = require('react-docgen'); +const dir = require('node-dir'); +const fs = require('fs'); +const path = require('path'); + +const INPUT_PATH = path.join(__dirname, '..', 'javascript', 'components'); +const OUTPUT_PATH = path.join(__dirname, '..', 'docs', 'docs.json'); + +class DocJSONBuilder { + constructor (filePath) { + this._filePath = filePath; + } + + get options () { + return { + match: /.js$/, + shortName: true, + }; + } + + isPrivateMethod (methodName = '') { + return !methodName || methodName.charAt(0) === '_'; + } + + postprocess (component) { + // Remove all private methods and parse examples from docblock + + if (!Array.isArray(component.methods)) { + return; + } + + const privateMethods = []; + for (let method of component.methods) { + if (this.isPrivateMethod(method.name)) { + privateMethods.push(method.name); + continue; + } + + const examples = method.docblock.split('@').filter((block) => block.startsWith('example')); + method.examples = examples.map((example) => example.substring('example'.length)); + } + + component.methods = component.methods.filter((method) => !privateMethods.includes(method.name)); + } + + generate () { + let results = {}; + + dir.readFiles(this._filePath, this.options, (err, content, fileName, next) => { + if (err) { + throw err; + } + + fileName = fileName.replace('.js', ''); + results[fileName] = docgen.parse(content); + this.postprocess(results[fileName]); + + next(); + }, () => fs.writeFileSync(OUTPUT_PATH, JSON.stringify(results, null, 2))); + } +} + +const docJSONBuilder = new DocJSONBuilder(INPUT_PATH); +docJSONBuilder.generate();