From 8a2360f8614bcdc9a03e76b318ae51fbc08dc952 Mon Sep 17 00:00:00 2001 From: Zach Bloomquist Date: Fri, 27 Sep 2019 14:47:20 -0400 Subject: [PATCH] wip: use typedoc to generate type info, add tags to use type info in docs --- .gitignore | 1 + gulpfile.js | 26 ++++++++++- lib/tags/typedoc.js | 108 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 + scripts/tags.js | 52 +++++++++------------ 5 files changed, 158 insertions(+), 31 deletions(-) create mode 100644 lib/tags/typedoc.js diff --git a/.gitignore b/.gitignore index 8ddd744fb7..41339eb81d 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ cypress/videos # Dynamically parsed data source/_data/banners.yml +source/_data/cypress-typedoc.json # Translations # By default we ignore each translation folder like "source/ja", diff --git a/gulpfile.js b/gulpfile.js index 81a9bc4848..5839690d93 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,5 +1,7 @@ +const execa = require('execa') const gulp = require('gulp') const RevAll = require('gulp-rev-all') +const typedoc = require('gulp-typedoc') const clean = require('gulp-clean') const revisionOpts = { @@ -99,6 +101,26 @@ gulp.task('move:robots.txt:to:public', function () { .pipe(gulp.dest('public')) }) +gulp.task('clone:cypress', function () { + // TODO: control branch name? + return execa.command('git clone --depth 1 --single-branch --branch master https://github.com/cypress-io/cypress.git ./tmp/cypress') +}) + +gulp.task('run:typedoc', function () { + return gulp.src(['./tmp/cypress/cli/types/index.d.ts']) + .pipe(typedoc({ + entryPoint: 'Cypress', + // drastically reduces output size - but we don't get docs for included packages + excludeExternals: true, + ignoreCompilerErrors: true, + includeDeclarations: true, + json: './source/_data/cypress-typedoc.json', + logger: 'console', + mode: 'file', + tsconfig: './tmp/cypress/packages/ts/tsconfig.json', + })) +}) + gulp.task('clean:non:application:js', () => { return remove('public/js/!(application).js') }) @@ -127,7 +149,9 @@ gulp.task('clean:public', () => { gulp.task('copy:static:assets', gulp.parallel('move:menu:spy:js', 'move:scrolling:element:js', 'move:doc:search:js', 'move:doc:yall:js', 'move:doc:search:css', 'move:roboto:fonts', 'move:font:awesome:fonts')) -gulp.task('pre:build', gulp.parallel('copy:static:assets')) +gulp.task('generate:typedoc', gulp.series('clone:cypress', 'run:typedoc')) + +gulp.task('pre:build', gulp.parallel('generate:typedoc', 'copy:static:assets')) gulp.task('post:build', gulp.series('clean:js', 'clean:css', 'clean:fonts:folders', 'revision', 'clean:public', 'copy:tmp:to:public', 'move:robots.txt:to:public', 'clean:tmp')) diff --git a/lib/tags/typedoc.js b/lib/tags/typedoc.js new file mode 100644 index 0000000000..fd985d54ee --- /dev/null +++ b/lib/tags/typedoc.js @@ -0,0 +1,108 @@ +const _ = require('lodash') +const assert = require('assert') +const path = require('path') +const fse = require('fs-extra') +const Promise = require('bluebird') +const rawRender = require('../raw_render') + +const pathToTypeDoc = path.join(__dirname, '../../source/_data/cypress-typedoc.json') + +let getTypeDocPromise + +function _getTypeDoc() { + if (!getTypeDocPromise) { + getTypeDocPromise = fse.readJson(pathToTypeDoc) + } + + return getTypeDocPromise +} + +const knownKindStrings = { + 'cy': 'Interface', + 'Cypress': 'Module' +} + +function _getTypeAtPath(path) { + return _getTypeDoc() + .then((type) => { + path.forEach(name => { + type = _.find(type.children, (child) => { + const knownKindString = knownKindStrings[name] + + return child.name === name && (!knownKindString || child.kindString === knownKindString) + }) + }) + + return type + }) +} + +function _renderInterface(path, interface) { + assert.strictEqual(method.kindString, 'Interface') + let out = 'Option | Default | Description\n' + out += '--- | --- | ---\n' + + out += _.chain(interface.children) + .filter({ kindString: 'Property' }) + .sortBy(_.property('name')) + .map(_renderProperty) + .value() + + return out +} + +function _renderMethod(path, method) { + assert.strictEqual(method.kindString, 'Method') + let out = '' + + out += '## Syntax\n\n' + out += '```javascript\n' + + out += method.signatures + .map(_.partial(_renderSignature, path)) + .join('\n') + + out += '\n```\n\n' + + return out +} + +function _renderParameter(parameter) { + assert.strictEqual(parameter.kindString, 'Parameter') + return `${parameter.name}${parameter.flags.isOptional ? '?' : ''}: ${parameter.type.name}` +} + +function _renderProperty(property) { + assert.strictEqual(property.kindString, 'Property') + return `\`${property.name}\` | \`${property.type.name}\` | ${_renderPropertyComment(property.comment)}` +} + +function _renderPropertyComment(comment) { + if (!comment) { + return '' + } + + const default = _.find(comment.tags, { tag: 'default' }) + + return `${(comment.shortText || '')}${default ? ` (default: \`${default.text}\`)` : ''}` +} + +function _renderSignature(path, signature) { + assert.strictEqual(signature.kindString, 'Call signature') + return `${path}(${signature.parameters ? signature.parameters.map(_renderParameter).join(', ') : ''})` +} + +module.exports = function partial (hexo, [pathString]) { + const path = _.toPath(`Cypress.${pathString}`) + + return _getTypeAtPath(path) + .then((type) => { + const out = ({ + Interface: _renderInterface + Method: _renderMethod, + })[type.kindString](pathString, type) + + console.log(out) + return rawRender(hexo, `
${out}
`, { engine: 'markdown' }) + }) +} \ No newline at end of file diff --git a/package.json b/package.json index a794c8a358..afda730796 100644 --- a/package.json +++ b/package.json @@ -106,6 +106,7 @@ "gulp": "4.0.2", "gulp-clean": "0.4.0", "gulp-rev-all": "2.0.2", + "gulp-typedoc": "2.2.2", "husky": "3.0.3", "inquirer": "6.5.0", "jest": "24.8.0", @@ -127,6 +128,7 @@ "textlint-filter-rule-comments": "1.2.2", "textlint-rule-eslint": "3.2.0", "textlint-rule-terminology": "1.1.30", + "typedoc": "0.15.0", "utf8": "3.0.0", "wait-on": "3.3.0", "yamljs": "0.3.0" diff --git a/scripts/tags.js b/scripts/tags.js index 7ac454b07e..55c71e8f8f 100644 --- a/scripts/tags.js +++ b/scripts/tags.js @@ -10,6 +10,7 @@ const yields = require('../lib/tags/yields') const requirements = require('../lib/tags/requirements') const assertions = require('../lib/tags/assertions') const timeouts = require('../lib/tags/timeouts') +const typedoc = require('../lib/tags/typedoc') const usageOptions = require('../lib/tags/usage') const { issue, PR, openAnIssue, user } = require('../lib/tags/github') const { badge } = require('../lib/tags/badge') @@ -22,61 +23,52 @@ const history = require('../lib/tags/history') const aliases = require('../lib/tags/aliases') const tags = { - // aliases - aliases: aliases, + aliases, - // assertions - assertions: assertions, + assertions, - // badge - badge: badge, + badge, - // changelog - changelog: changelog, + changelog, // icons - fa: fa, + fa, helper_icon: helperIcon, - + // images - imgTag: imgTag, + imgTag, // github links - issue: issue, + issue, open_an_issue: openAnIssue, - PR: PR, - user: user, + PR, + user, + + partial, - // partials - partial: partial, + requirements, - // requirements - requirements: requirements, + timeouts, - // timeouts - timeouts: timeouts, + typedoc, // url - url: url, - urlHash: urlHash, + url, + urlHash, - // usage_options usage_options: usageOptions, // video - video: video, + video, - // yields - yields: yields, + yields, } // tags which require ending const endingTags = { - // history - history: history, + history, - // note - note: note, + note, } function promisify (fn) {