Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for typescript with jsdoc #568

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions cmds/ts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use strict'

const EPILOG = `
Presets:
\`check\` Checks src and test folders for Typescript errors
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this mean we have to include TS comments on all of our tests?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it works as is but it may output errors for some inferred types or defined types in the source code.
Checking the tests enables us to be the users of our own types and be able to identify issues in the types early.

\`types\` Generates types declarations inline with the source files
\`types-clean\` Deletes all the *.d.ts files
\`docs\` Generates documentation based on the type declarations

Supports options forwarding with '--' for more info check https://www.typescriptlang.org/v2/docs/handbook/compiler-options.html
`
module.exports = {
command: 'ts',
desc: 'Typescript command with presets for specific tasks.',
builder: (yargs) => {
yargs
.epilog(EPILOG)
.options({
preset: {
type: 'string',
choices: ['check', 'types', 'types-clean', 'docs'],
describe: 'Preset to run'
}
})
},
handler (argv) {
const ts = require('../src/ts')
return ts(argv)
}
}
9 changes: 9 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
"@commitlint/travis-cli": "^8.3.5",
"@electron/get": "^1.10.0",
"@polka/send-type": "^0.5.2",
"@types/mocha": "^7.0.2",
"@types/node": "^14.0.9",
"aegir-typedoc-theme": "~0.0.1",
"babel-loader": "^8.0.5",
"babel-plugin-transform-flow-comments": "^6.22.0",
"buffer": "^5.6.0",
Expand All @@ -76,6 +79,7 @@
"eslint": "^6.3.0",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-jsdoc": "^25.4.2",
Gozala marked this conversation as resolved.
Show resolved Hide resolved
"eslint-plugin-no-only-tests": "^2.4.0",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-promise": "^4.2.1",
Expand Down Expand Up @@ -120,6 +124,8 @@
"stream-array": "^1.1.2",
"terser-webpack-plugin": "^2.3.6",
"transform-loader": "~0.2.4",
"typedoc": "^0.17.7",
"typescript": "^3.9.3",
"update-notifier": "^4.0.0",
"vinyl-fs": "^3.0.3",
"webpack": "^4.43.0",
Expand All @@ -139,6 +145,9 @@
"node": ">=10.0.0",
"npm": ">=6.0.0"
},
"eslintConfig": {
"extends": "./src/config/eslintrc.js"
},
"browserslist": [
">1%",
"last 2 versions",
Expand Down
26 changes: 26 additions & 0 deletions src/config/aegir-tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"target": "es2018",
"module": "CommonJS",
"moduleResolution": "node",
"resolveJsonModule": true,
"stripInternal": true,
"strict": false,
"alwaysStrict": true,
"strictNullChecks": true,
"noFallthroughCasesInSwitch": true,
"preserveConstEnums": true,
"noEmitOnError": true,
Gozala marked this conversation as resolved.
Show resolved Hide resolved
"noEmit": true,
"esModuleInterop": true,
"declaration": true,
"removeComments": false,
"skipLibCheck": true,
"lib": [
"ES2018",
"DOM"
]
}
}
42 changes: 35 additions & 7 deletions src/config/eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@ module.exports = {
parserOptions: {
sourceType: 'script'
},
settings: {
jsdoc: { mode: 'typescript' }
},
env: {
browser: true
},
globals: {
self: true
},
plugins: [
'no-only-tests'
'no-only-tests',
'jsdoc'
],
rules: {
strict: [2, 'safe'],
Expand Down Expand Up @@ -40,11 +47,32 @@ module.exports = {
'require-yield': 2,
'max-nested-callbacks': [2, 4],
'max-depth': [2, 4],
'valid-jsdoc': [2, {
requireReturn: false,
requireParamDescription: false,
requireReturnDescription: false
}],
'require-await': 2
'require-await': 2,
'jsdoc/check-alignment': 2,
'jsdoc/check-examples': 0,
'jsdoc/check-indentation': 2,
'jsdoc/check-param-names': 2,
'jsdoc/check-syntax': 2,
'jsdoc/check-tag-names': [2, { definedTags: ['internal', 'packageDocumentation'] }],
'jsdoc/check-types': 2,
'jsdoc/implements-on-classes': 2,
'jsdoc/match-description': 0,
'jsdoc/newline-after-description': 2,
'jsdoc/no-types': 0,
'jsdoc/no-undefined-types': 2,
Gozala marked this conversation as resolved.
Show resolved Hide resolved
'jsdoc/require-description': 0,
'jsdoc/require-description-complete-sentence': 0,
'jsdoc/require-example': 0,
'jsdoc/require-hyphen-before-param-description': 2,
Gozala marked this conversation as resolved.
Show resolved Hide resolved
'jsdoc/require-jsdoc': 0,
'jsdoc/require-param': 2,
'jsdoc/require-param-description': 1,
'jsdoc/require-param-name': 2,
'jsdoc/require-param-type': 2,
'jsdoc/require-returns': 2,
'jsdoc/require-returns-check': 2,
'jsdoc/require-returns-description': 1,
'jsdoc/require-returns-type': 2,
'jsdoc/valid-types': 2
}
}
4 changes: 2 additions & 2 deletions src/dependency-check.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const defaultInput = [
/**
* Check dependencies
*
* @param {Object} argv
* @param {ExecaOptions} execaOptions
* @param {object} argv
* @param {ExecaOptions} execaOptions - execa options
* @returns {ExecaChildProcess}
*/
const check = (argv = { _: [] }, execaOptions) => {
Expand Down
153 changes: 153 additions & 0 deletions src/ts/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
'use strict'

const path = require('path')
const execa = require('execa')
const fs = require('fs-extra')
const rimraf = require('rimraf')
const merge = require('merge-options')
const { fromRoot, fromAegir, repoDirectory, hasFile } = require('../utils')
let baseTsConfig = require('./../config/aegir-tsconfig.json')

if (hasFile('tsconfig.json')) {
baseTsConfig = require(fromRoot('tsconfig.json'))
}

module.exports = (argv) => {
const forwardOptions = argv['--'] ? argv['--'] : []

if (argv.preset === 'check') {
return check(forwardOptions)
}

if (argv.preset === 'types') {
return types(forwardOptions)
}

if (argv.preset === 'types-clean') {
return typesClean()
}

if (argv.preset === 'docs') {
return docs(forwardOptions)
}

if (!argv.preset) {
return execa('tsc', [
...forwardOptions
], {
localDir: path.join(__dirname, '../..'),
preferLocal: true,
stdio: 'inherit'
})
}
}

const check = async (forwardOptions) => {
const configPath = fromRoot('tsconfig-check.json')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to fail if there is no tsconfig.json (which can include single extends directive e.g. { "extends": "aegir/src/config/aegir-tsconfig.json"}) than do this. Otherwise we introduce another instance of other tools like vscode making different assumptions than tools than aegir.

try {
fs.writeJsonSync(
configPath,
merge(baseTsConfig, {
include: ['src/**/*', 'test/**/*']
})
)
await execa('tsc', [
'-p', configPath,
...forwardOptions
], {
localDir: path.join(__dirname, '../..'),
preferLocal: true,
stdio: 'inherit'
})
} finally {
fs.removeSync(configPath)
}
}

const typesClean = () => {
rimraf.sync(path.join(repoDirectory, 'src/**/*.d.ts'))
}

const types = async (forwardOptions) => {
const configPath = fromRoot('tsconfig-types.json')
typesClean()
try {
fs.writeJsonSync(
configPath,
merge(baseTsConfig, {
compilerOptions: {
noEmit: false,
emitDeclarationOnly: true
// outDir: 'types'
},
include: [
'src/**/*'
]
})
)
await execa('tsc', [
'-p', configPath,
...forwardOptions
], {
localDir: path.join(__dirname, '../..'),
preferLocal: true,
stdio: 'inherit'
})
} finally {
fs.removeSync(configPath)
}
}

const docs = async (forwardOptions) => {
const configPath = fromRoot('tsconfig-docs.json')
try {
fs.writeJsonSync(
configPath,
merge(baseTsConfig, {
compilerOptions: {
noEmit: false,
emitDeclarationOnly: true,
outDir: 'types'
},
include: ['src/**/*']
})
)

// run tsc
await execa('tsc', [
'-p', configPath,
...forwardOptions
], {
localDir: path.join(__dirname, '../..'),
preferLocal: true,
stdio: 'inherit'
})

// run typedoc
await execa('typedoc', [
'--inputfiles', fromRoot('types'),
'--mode', 'modules',
'--out', 'docs',
'--excludeExternals',
'--excludeNotDocumented',
// '--excludeNotExported',
'--excludePrivate',
'--excludeProtected',
'--includeDeclarations',
'--hideGenerator',
'--includeVersion',
'--gitRevision', 'master',
'--disableSources',
'--tsconfig', configPath,
'--plugin', fromAegir('src/ts/typedoc-plugin.js'),
'--theme', fromAegir('./../../node_modules/aegir-typedoc-theme/bin/default')
], {
localDir: path.join(__dirname, '..'),
preferLocal: true,
stdio: 'inherit'
})
} finally {
fs.removeSync(configPath)
fs.removeSync(fromRoot('types'))
}
}
35 changes: 35 additions & 0 deletions src/ts/typedoc-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict'
const { Converter } = require('typedoc/dist/lib/converter')
const path = require('path')
const fs = require('fs')

module.exports = function (PluginHost) {
const app = PluginHost.owner
const pkg = path.join(process.cwd(), 'package.json')
let pkgJson
let main
try {
pkgJson = JSON.parse(fs.readFileSync(pkg).toString())
main = path.join(process.cwd(), pkgJson.main)
} catch (err) {
throw new Error('cant find package.json')
}

app.converter.on(Converter.EVENT_CREATE_DECLARATION, (context, reflection, node) => {
if (reflection.kind === 1 && node) {
// entry point
if (pkgJson && reflection.name === main) {
reflection.name = '\u0000' + pkgJson.name.charAt(0).toUpperCase() + pkgJson.name.slice(1)
// reflection.kind = 2
}

if (pkgJson && reflection.name.includes('types/index.d.ts')) {
reflection.name = '\u0000' + pkgJson.name.charAt(0) + pkgJson.name.slice(1)
}

if (pkgJson && reflection.name.includes('.d.ts')) {
reflection.name = reflection.name.replace('.d.ts', '.js')
}
}
})
}
Loading