mirror of
https://github.com/jsdoc/jsdoc.git
synced 2025-12-08 19:46:11 +00:00
chore: use Prettier to format source files
This commit is contained in:
parent
3025520e15
commit
1305499207
@ -9,4 +9,4 @@ indent_size = 2
|
|||||||
|
|
||||||
[{**/*.js,**/*.css,**/*.json}]
|
[{**/*.js,**/*.css,**/*.json}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 2
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
extends: '@jsdoc'
|
extends: ['@jsdoc', 'plugin:prettier/recommended'],
|
||||||
};
|
};
|
||||||
|
|||||||
4
.github/ISSUE_TEMPLATE.md
vendored
4
.github/ISSUE_TEMPLATE.md
vendored
@ -40,8 +40,8 @@ Your debug output here
|
|||||||
|
|
||||||
### Your environment
|
### Your environment
|
||||||
|
|
||||||
| Software | Version
|
| Software | Version |
|
||||||
| ---------------- | -------
|
| ---------------- | ------- |
|
||||||
| JSDoc |
|
| JSDoc |
|
||||||
| Node.js |
|
| Node.js |
|
||||||
| npm |
|
| npm |
|
||||||
|
|||||||
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -5,14 +5,14 @@ https://github.com/jsdoc3/jsdoc/blob/master/CONTRIBUTING.md
|
|||||||
https://github.com/jsdoc3/jsdoc/blob/master/CODE_OF_CONDUCT.md
|
https://github.com/jsdoc3/jsdoc/blob/master/CODE_OF_CONDUCT.md
|
||||||
-->
|
-->
|
||||||
|
|
||||||
| Q | A
|
| Q | A |
|
||||||
| ---------------- | ---
|
| ---------------- | ---------------------------------------------------------------- |
|
||||||
| Bug fix? | yes/no
|
| Bug fix? | yes/no |
|
||||||
| New feature? | yes/no
|
| New feature? | yes/no |
|
||||||
| Breaking change? | yes/no
|
| Breaking change? | yes/no |
|
||||||
| Deprecations? | yes/no
|
| Deprecations? | yes/no |
|
||||||
| Tests added? | yes/no
|
| Tests added? | yes/no |
|
||||||
| Fixed issues | comma-separated list of issues fixed by the pull request, if any
|
| Fixed issues | comma-separated list of issues fixed by the pull request, if any |
|
||||||
| License | Apache-2.0
|
| License | Apache-2.0 |
|
||||||
|
|
||||||
<!-- Describe your changes below in as much detail as possible. -->
|
<!-- Describe your changes below in as much detail as possible. -->
|
||||||
|
|||||||
5
.prettierignore
Normal file
5
.prettierignore
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Ignore test fixtures.
|
||||||
|
**/test/fixtures/**
|
||||||
|
|
||||||
|
# Ignore code coverage reports.
|
||||||
|
.nyc_output/
|
||||||
3
.prettierrc.js
Normal file
3
.prettierrc.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
...require('./packages/jsdoc-prettier-config'),
|
||||||
|
};
|
||||||
@ -1,12 +1,8 @@
|
|||||||
{
|
{
|
||||||
"extends": [
|
"extends": ["config:base"],
|
||||||
"config:base"
|
"statusCheckVerify": true,
|
||||||
],
|
"ignoreDeps": ["taffydb"],
|
||||||
"statusCheckVerify": true,
|
"automerge": true,
|
||||||
"ignoreDeps": [
|
"automergeType": "branch",
|
||||||
"taffydb"
|
"rangeStrategy": "bump"
|
||||||
],
|
|
||||||
"automerge": true,
|
|
||||||
"automergeType": "branch",
|
|
||||||
"rangeStrategy": "bump"
|
|
||||||
}
|
}
|
||||||
|
|||||||
1170
CHANGES.md
1170
CHANGES.md
File diff suppressed because it is too large
Load Diff
@ -11,20 +11,20 @@ of experience, nationality, personal appearance, race, religion, or sexual ident
|
|||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment include:
|
Examples of behavior that contributes to creating a positive environment include:
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
- Using welcoming and inclusive language
|
||||||
* Being respectful of differing viewpoints and experiences
|
- Being respectful of differing viewpoints and experiences
|
||||||
* Gracefully accepting constructive criticism
|
- Gracefully accepting constructive criticism
|
||||||
* Focusing on what is best for the community
|
- Focusing on what is best for the community
|
||||||
* Showing empathy towards other community members
|
- Showing empathy towards other community members
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
* Public or private harassment
|
- Public or private harassment
|
||||||
* Publishing others' private information, such as a physical or electronic address, without explicit
|
- Publishing others' private information, such as a physical or electronic address, without explicit
|
||||||
permission
|
permission
|
||||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
|
|
||||||
## Our Responsibilities
|
## Our Responsibilities
|
||||||
|
|
||||||
|
|||||||
@ -1,69 +1,68 @@
|
|||||||
Pull Requests
|
## Pull Requests
|
||||||
-------------
|
|
||||||
|
|
||||||
If you're thinking about making some changes, maybe fixing a bug, or adding a
|
If you're thinking about making some changes, maybe fixing a bug, or adding a
|
||||||
snazzy new feature, first, thank you. Contributions are very welcome. Things
|
snazzy new feature, first, thank you. Contributions are very welcome. Things
|
||||||
need to be manageable for the maintainers, however. So below you'll find **The
|
need to be manageable for the maintainers, however. So below you'll find **The
|
||||||
fastest way to get your pull request merged in.** Some things, particularly how
|
fastest way to get your pull request merged in.** Some things, particularly how
|
||||||
you set up your branches and work with git, are just suggestions, but pretty good
|
you set up your branches and work with git, are just suggestions, but pretty good
|
||||||
ones.
|
ones.
|
||||||
|
|
||||||
1. **Create a remote to track the base jsdoc/jsdoc repository**
|
1. **Create a remote to track the base jsdoc/jsdoc repository**
|
||||||
This is just a convenience to make it easier to update your ```<tracking branch>```
|
This is just a convenience to make it easier to update your `<tracking branch>`
|
||||||
(more on that shortly). You would execute something like:
|
(more on that shortly). You would execute something like:
|
||||||
|
|
||||||
git remote add base git://github.com/jsdoc/jsdoc.git
|
git remote add base git://github.com/jsdoc/jsdoc.git
|
||||||
|
|
||||||
Here 'base' is the name of the remote. Feel free to use whatever you want.
|
Here 'base' is the name of the remote. Feel free to use whatever you want.
|
||||||
|
|
||||||
2. **Set up a tracking branch for the base repository**
|
2. **Set up a tracking branch for the base repository**
|
||||||
We're gonna call this your ```<tracking branch>```. You will only ever update
|
We're gonna call this your `<tracking branch>`. You will only ever update
|
||||||
this branch by pulling from the 'base' remote. (as opposed to 'origin')
|
this branch by pulling from the 'base' remote. (as opposed to 'origin')
|
||||||
|
|
||||||
git branch --track pullpost base/master
|
git branch --track pullpost base/master
|
||||||
git checkout pullpost
|
git checkout pullpost
|
||||||
|
|
||||||
Here 'pullpost' is the name of the branch. Fell free to use whatever you want.
|
Here 'pullpost' is the name of the branch. Fell free to use whatever you want.
|
||||||
|
|
||||||
3. **Create your change branch**
|
3. **Create your change branch**
|
||||||
Once you are in ```<tracking branch>```, make sure it's up to date, then create
|
Once you are in `<tracking branch>`, make sure it's up to date, then create
|
||||||
a branch for your changes off of that one.
|
a branch for your changes off of that one.
|
||||||
|
|
||||||
git branch fix-for-issue-395
|
git branch fix-for-issue-395
|
||||||
git checkout fix-for-issue-395
|
git checkout fix-for-issue-395
|
||||||
|
|
||||||
Here 'fix-for-issue-395' is the name of the branch. Feel free to use whatever
|
Here 'fix-for-issue-395' is the name of the branch. Feel free to use whatever
|
||||||
you want. We'll call this the ```<change branch>```. This is the branch that
|
you want. We'll call this the `<change branch>`. This is the branch that
|
||||||
you will eventually issue your pull request from.
|
you will eventually issue your pull request from.
|
||||||
|
|
||||||
The purpose of these first three steps is to make sure that your merge request
|
The purpose of these first three steps is to make sure that your merge request
|
||||||
has a nice clean diff that only involves the changes related to your fix/feature.
|
has a nice clean diff that only involves the changes related to your fix/feature.
|
||||||
|
|
||||||
4. **Make your changes**
|
4. **Make your changes**
|
||||||
On your ```<change branch>``` make any changes relevant to your fix/feature. Don't
|
On your `<change branch>` make any changes relevant to your fix/feature. Don't
|
||||||
group fixes for multiple unrelated issues or multiple unrelated features together.
|
group fixes for multiple unrelated issues or multiple unrelated features together.
|
||||||
Create a separate branch for each unrelated changeset. For instance, if you're
|
Create a separate branch for each unrelated changeset. For instance, if you're
|
||||||
fixing a bug in the parser and adding some new UI to the default template, those
|
fixing a bug in the parser and adding some new UI to the default template, those
|
||||||
should be separate branches and merge requests.
|
should be separate branches and merge requests.
|
||||||
|
|
||||||
5. **Add tests**
|
5. **Add tests**
|
||||||
Add tests for your change. If you are submitting a bugfix, include a test that
|
Add tests for your change. If you are submitting a bugfix, include a test that
|
||||||
verifies the existence of the bug along with your fix. If you are submitting
|
verifies the existence of the bug along with your fix. If you are submitting
|
||||||
a new feature, include tests that verify proper feature function, if applicable.
|
a new feature, include tests that verify proper feature function, if applicable.
|
||||||
See the readme in the 'test' directory for more information
|
See the readme in the 'test' directory for more information
|
||||||
|
|
||||||
6. **Commit and publish**
|
6. **Commit and publish**
|
||||||
Commit your changes and publish your branch (or push it if it's already published)
|
Commit your changes and publish your branch (or push it if it's already published)
|
||||||
|
|
||||||
7. **Issue your pull request**
|
7. **Issue your pull request**
|
||||||
On github.com, switch to your ```<change branch>``` and click the 'Pull Request'
|
On github.com, switch to your `<change branch>` and click the 'Pull Request'
|
||||||
button. Enter some meaningful information about the pull request. If it's a bugfix,
|
button. Enter some meaningful information about the pull request. If it's a bugfix,
|
||||||
that doesn't already have an issue associated with it, provide some info on what
|
that doesn't already have an issue associated with it, provide some info on what
|
||||||
situations that bug occurs in and a sense of it's severity. If it does already have
|
situations that bug occurs in and a sense of it's severity. If it does already have
|
||||||
an issue, make sure the include the hash and issue number (e.g. '#100') so github
|
an issue, make sure the include the hash and issue number (e.g. '#100') so github
|
||||||
links it.
|
links it.
|
||||||
|
|
||||||
If it's a feature, provide some context about the motivations behind the feature,
|
If it's a feature, provide some context about the motivations behind the feature,
|
||||||
why it's important/useful/cool/necessary and what it does/how it works. Don't
|
why it's important/useful/cool/necessary and what it does/how it works. Don't
|
||||||
worry about being too verbose. Folks will be much more amenable to reading through
|
worry about being too verbose. Folks will be much more amenable to reading through
|
||||||
your code if they know what its supposed to be about.
|
your code if they know what its supposed to be about.
|
||||||
|
|||||||
53
README.md
53
README.md
@ -6,8 +6,7 @@ An API documentation generator for JavaScript.
|
|||||||
|
|
||||||
Want to contribute to JSDoc? Please read [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
Want to contribute to JSDoc? Please read [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
||||||
|
|
||||||
Installation and Usage
|
## Installation and Usage
|
||||||
----------------------
|
|
||||||
|
|
||||||
JSDoc supports stable versions of Node.js 8.15.0 and later. You can install
|
JSDoc supports stable versions of Node.js 8.15.0 and later. You can install
|
||||||
JSDoc globally or in your project's `node_modules` folder.
|
JSDoc globally or in your project's `node_modules` folder.
|
||||||
@ -51,41 +50,41 @@ and customize your documentation. Here are a few of them:
|
|||||||
|
|
||||||
### Templates
|
### Templates
|
||||||
|
|
||||||
+ [jaguarjs-jsdoc](https://github.com/davidshimjs/jaguarjs-jsdoc)
|
- [jaguarjs-jsdoc](https://github.com/davidshimjs/jaguarjs-jsdoc)
|
||||||
+ [DocStrap](https://github.com/docstrap/docstrap)
|
- [DocStrap](https://github.com/docstrap/docstrap)
|
||||||
([example](https://docstrap.github.io/docstrap))
|
([example](https://docstrap.github.io/docstrap))
|
||||||
+ [jsdoc3Template](https://github.com/DBCDK/jsdoc3Template)
|
- [jsdoc3Template](https://github.com/DBCDK/jsdoc3Template)
|
||||||
([example](https://github.com/danyg/jsdoc3Template/wiki#wiki-screenshots))
|
([example](https://github.com/danyg/jsdoc3Template/wiki#wiki-screenshots))
|
||||||
+ [minami](https://github.com/Nijikokun/minami)
|
- [minami](https://github.com/Nijikokun/minami)
|
||||||
+ [docdash](https://github.com/clenemt/docdash)
|
- [docdash](https://github.com/clenemt/docdash)
|
||||||
([example](http://clenemt.github.io/docdash/))
|
([example](http://clenemt.github.io/docdash/))
|
||||||
+ [tui-jsdoc-template](https://github.com/nhnent/tui.jsdoc-template)
|
- [tui-jsdoc-template](https://github.com/nhnent/tui.jsdoc-template)
|
||||||
([example](https://nhnent.github.io/tui.jsdoc-template/latest/))
|
([example](https://nhnent.github.io/tui.jsdoc-template/latest/))
|
||||||
+ [better-docs](https://github.com/SoftwareBrothers/better-docs)
|
- [better-docs](https://github.com/SoftwareBrothers/better-docs)
|
||||||
([example](https://softwarebrothers.github.io/admin-bro-dev/index.html))
|
([example](https://softwarebrothers.github.io/admin-bro-dev/index.html))
|
||||||
|
|
||||||
### Build tools
|
### Build tools
|
||||||
|
|
||||||
+ [JSDoc Grunt plugin](https://github.com/krampstudio/grunt-jsdoc)
|
- [JSDoc Grunt plugin](https://github.com/krampstudio/grunt-jsdoc)
|
||||||
+ [JSDoc Gulp plugin](https://github.com/mlucool/gulp-jsdoc3)
|
- [JSDoc Gulp plugin](https://github.com/mlucool/gulp-jsdoc3)
|
||||||
+ [JSDoc GitHub Action](https://github.com/andstor/jsdoc-action)
|
- [JSDoc GitHub Action](https://github.com/andstor/jsdoc-action)
|
||||||
|
|
||||||
### Other tools
|
### Other tools
|
||||||
|
|
||||||
+ [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown)
|
- [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown)
|
||||||
+ [Integrating GitBook with
|
- [Integrating GitBook with
|
||||||
JSDoc](https://medium.com/@kevinast/integrate-gitbook-jsdoc-974be8df6fb3)
|
JSDoc](https://medium.com/@kevinast/integrate-gitbook-jsdoc-974be8df6fb3)
|
||||||
|
|
||||||
## For more information
|
## For more information
|
||||||
|
|
||||||
+ Documentation is available at [jsdoc.app](https://jsdoc.app/).
|
- Documentation is available at [jsdoc.app](https://jsdoc.app/).
|
||||||
+ Contribute to the docs at
|
- Contribute to the docs at
|
||||||
[jsdoc/jsdoc.github.io](https://github.com/jsdoc/jsdoc.github.io).
|
[jsdoc/jsdoc.github.io](https://github.com/jsdoc/jsdoc.github.io).
|
||||||
+ [Join JSDoc's Slack channel](https://jsdoc-slack.appspot.com/).
|
- [Join JSDoc's Slack channel](https://jsdoc-slack.appspot.com/).
|
||||||
+ Ask for help on the
|
- Ask for help on the
|
||||||
[JSDoc Users mailing list](http://groups.google.com/group/jsdoc-users).
|
[JSDoc Users mailing list](http://groups.google.com/group/jsdoc-users).
|
||||||
+ Post questions tagged `jsdoc` to
|
- Post questions tagged `jsdoc` to
|
||||||
[Stack Overflow](http://stackoverflow.com/questions/tagged/jsdoc).
|
[Stack Overflow](http://stackoverflow.com/questions/tagged/jsdoc).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
53
gulpfile.js
53
gulpfile.js
@ -2,50 +2,53 @@ const eslint = require('gulp-eslint');
|
|||||||
const { exec } = require('child_process');
|
const { exec } = require('child_process');
|
||||||
const gulp = require('gulp');
|
const gulp = require('gulp');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const prettier = require('gulp-prettier');
|
||||||
|
|
||||||
function execCb(cb, err, stdout, stderr) {
|
function execCb(cb, err, stdout, stderr) {
|
||||||
console.log(stdout);
|
console.log(stdout);
|
||||||
console.error(stderr);
|
console.error(stderr);
|
||||||
cb(err);
|
cb(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
coveragePaths: [
|
coveragePaths: [
|
||||||
'*.js',
|
'*.js',
|
||||||
'packages/**/*.js',
|
'packages/**/*.js',
|
||||||
'!packages/**/test/*.js',
|
'!packages/**/test/*.js',
|
||||||
'!packages/**/test/**/*.js',
|
'!packages/**/test/**/*.js',
|
||||||
'!packages/**/static/*.js'
|
'!packages/**/static/*.js',
|
||||||
],
|
],
|
||||||
lintPaths: [
|
lintPaths: ['*.js', 'packages/**/*.js', '!packages/**/static/*.js'],
|
||||||
'*.js',
|
nodeBin: path.resolve(__dirname, './packages/jsdoc/jsdoc.js'),
|
||||||
'packages/**/*.js',
|
nodePath: process.execPath,
|
||||||
'!packages/**/static/*.js'
|
|
||||||
],
|
|
||||||
nodeBin: path.resolve(__dirname, './packages/jsdoc/jsdoc.js'),
|
|
||||||
nodePath: process.execPath
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function coverage(cb) {
|
function coverage(cb) {
|
||||||
const cmd = `./node_modules/.bin/nyc --reporter=html ${options.nodeBin} -T`;
|
const cmd = `./node_modules/.bin/nyc --reporter=html ${options.nodeBin} -T`;
|
||||||
|
|
||||||
return exec(cmd, execCb.bind(null, cb));
|
return exec(cmd, execCb.bind(null, cb));
|
||||||
|
}
|
||||||
|
|
||||||
|
function format() {
|
||||||
|
return gulp.src(options.lintPaths).pipe(prettier());
|
||||||
}
|
}
|
||||||
|
|
||||||
function lint() {
|
function lint() {
|
||||||
return gulp.src(options.lintPaths)
|
return gulp
|
||||||
.pipe(eslint())
|
.src(options.lintPaths)
|
||||||
.pipe(eslint.formatEach())
|
.pipe(eslint())
|
||||||
.pipe(eslint.failAfterError());
|
.pipe(eslint.formatEach())
|
||||||
|
.pipe(eslint.failAfterError());
|
||||||
}
|
}
|
||||||
|
|
||||||
function test(cb) {
|
function test(cb) {
|
||||||
const cmd = `${options.nodePath} "${options.nodeBin}" -T`;
|
const cmd = `${options.nodePath} "${options.nodeBin}" -T`;
|
||||||
|
|
||||||
return exec(cmd, execCb.bind(null, cb));
|
return exec(cmd, execCb.bind(null, cb));
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.coverage = coverage;
|
exports.coverage = coverage;
|
||||||
exports.default = gulp.series(lint, test);
|
exports.default = gulp.series(lint, test);
|
||||||
|
exports.format = format;
|
||||||
exports.lint = lint;
|
exports.lint = lint;
|
||||||
exports.test = test;
|
exports.test = test;
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
{
|
{
|
||||||
"packages": [
|
"packages": ["packages/*"],
|
||||||
"packages/*"
|
|
||||||
],
|
|
||||||
"version": "independent"
|
"version": "independent"
|
||||||
}
|
}
|
||||||
|
|||||||
16671
package-lock.json
generated
16671
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -7,14 +7,18 @@
|
|||||||
"@jsdoc/test-matchers": "^0.1.6",
|
"@jsdoc/test-matchers": "^0.1.6",
|
||||||
"ajv": "^8.6.3",
|
"ajv": "^8.6.3",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
|
"eslint-config-prettier": "^8.3.0",
|
||||||
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"gulp": "^4.0.2",
|
"gulp": "^4.0.2",
|
||||||
"gulp-eslint": "^6.0.0",
|
"gulp-eslint": "^6.0.0",
|
||||||
|
"gulp-prettier": "^4.0.0",
|
||||||
"jasmine": "^3.9.0",
|
"jasmine": "^3.9.0",
|
||||||
"jasmine-console-reporter": "^3.1.0",
|
"jasmine-console-reporter": "^3.1.0",
|
||||||
"klaw-sync": "^6.0.0",
|
"klaw-sync": "^6.0.0",
|
||||||
"lerna": "^4.0.0",
|
"lerna": "^4.0.0",
|
||||||
"mock-fs": "^5.1.0",
|
"mock-fs": "^5.1.0",
|
||||||
"nyc": "^15.1.0"
|
"nyc": "^15.1.0",
|
||||||
|
"prettier": "^2.4.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=v14.17.6"
|
"node": ">=v14.17.6"
|
||||||
|
|||||||
@ -3,21 +3,19 @@ const { EventBus } = require('@jsdoc/util');
|
|||||||
const flags = require('./flags');
|
const flags = require('./flags');
|
||||||
const help = require('./help');
|
const help = require('./help');
|
||||||
const { LEVELS, Logger } = require('./logger');
|
const { LEVELS, Logger } = require('./logger');
|
||||||
const {default: ow} = require('ow');
|
const { default: ow } = require('ow');
|
||||||
const yargs = require('yargs-parser');
|
const yargs = require('yargs-parser');
|
||||||
|
|
||||||
function validateChoice(flagInfo, choices, values) {
|
function validateChoice(flagInfo, choices, values) {
|
||||||
let flagNames = flagInfo.alias ? `-${flagInfo.alias}/` : '';
|
let flagNames = flagInfo.alias ? `-${flagInfo.alias}/` : '';
|
||||||
|
|
||||||
flagNames += `--${flagInfo.name}`;
|
flagNames += `--${flagInfo.name}`;
|
||||||
|
|
||||||
for (let value of values) {
|
for (let value of values) {
|
||||||
if (!choices.includes(value)) {
|
if (!choices.includes(value)) {
|
||||||
throw new TypeError(
|
throw new TypeError(`The flag ${flagNames} accepts only these values: ${choices.join(', ')}`);
|
||||||
`The flag ${flagNames} accepts only these values: ${choices.join(', ')}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,54 +28,54 @@ function validateChoice(flagInfo, choices, values) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
const { KNOWN_FLAGS, YARGS_FLAGS } = (() => {
|
const { KNOWN_FLAGS, YARGS_FLAGS } = (() => {
|
||||||
const names = new Set();
|
const names = new Set();
|
||||||
const opts = {
|
const opts = {
|
||||||
alias: {},
|
alias: {},
|
||||||
array: [],
|
array: [],
|
||||||
boolean: [],
|
boolean: [],
|
||||||
coerce: {},
|
coerce: {},
|
||||||
narg: {},
|
narg: {},
|
||||||
normalize: []
|
normalize: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
// `_` contains unparsed arguments.
|
// `_` contains unparsed arguments.
|
||||||
names.add('_');
|
names.add('_');
|
||||||
|
|
||||||
Object.keys(flags).forEach(flag => {
|
Object.keys(flags).forEach((flag) => {
|
||||||
const value = flags[flag];
|
const value = flags[flag];
|
||||||
|
|
||||||
names.add(flag);
|
names.add(flag);
|
||||||
|
|
||||||
if (value.alias) {
|
if (value.alias) {
|
||||||
names.add(value.alias);
|
names.add(value.alias);
|
||||||
opts.alias[flag] = [value.alias];
|
opts.alias[flag] = [value.alias];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.array) {
|
if (value.array) {
|
||||||
opts.array.push(flag);
|
opts.array.push(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.boolean) {
|
if (value.boolean) {
|
||||||
opts.boolean.push(flag);
|
opts.boolean.push(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.coerce) {
|
if (value.coerce) {
|
||||||
opts.coerce[flag] = value.coerce;
|
opts.coerce[flag] = value.coerce;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.normalize) {
|
if (value.normalize) {
|
||||||
opts.normalize.push(flag);
|
opts.normalize.push(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.requiresArg) {
|
if (value.requiresArg) {
|
||||||
opts.narg[flag] = 1;
|
opts.narg[flag] = 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
KNOWN_FLAGS: names,
|
KNOWN_FLAGS: names,
|
||||||
YARGS_FLAGS: opts
|
YARGS_FLAGS: opts,
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -86,148 +84,146 @@ const { KNOWN_FLAGS, YARGS_FLAGS } = (() => {
|
|||||||
* @alias module:@jsdoc/cli
|
* @alias module:@jsdoc/cli
|
||||||
*/
|
*/
|
||||||
class Engine {
|
class Engine {
|
||||||
/**
|
/**
|
||||||
* Create an instance of the CLI engine.
|
* Create an instance of the CLI engine.
|
||||||
*
|
*
|
||||||
* @param {Object} opts - Options for the CLI engine.
|
* @param {Object} opts - Options for the CLI engine.
|
||||||
* @param {number} [opts.logLevel] - The maximum logging level to print to the console. Must be
|
* @param {number} [opts.logLevel] - The maximum logging level to print to the console. Must be
|
||||||
* an enumerated value of `module:@jsdoc/cli.LOG_LEVELS`. The default value is
|
* an enumerated value of `module:@jsdoc/cli.LOG_LEVELS`. The default value is
|
||||||
* `module:@jsdoc/cli.LOG_LEVELS.WARN`.
|
* `module:@jsdoc/cli.LOG_LEVELS.WARN`.
|
||||||
* @param {string} [opts.version] - The version of JSDoc that is running.
|
* @param {string} [opts.version] - The version of JSDoc that is running.
|
||||||
* @param {Date} [opts.revision] - A timestamp for the version of JSDoc that is running.
|
* @param {Date} [opts.revision] - A timestamp for the version of JSDoc that is running.
|
||||||
*/
|
*/
|
||||||
constructor(opts = {}) {
|
constructor(opts = {}) {
|
||||||
ow(opts, ow.object);
|
ow(opts, ow.object);
|
||||||
// The `Logger` class validates `opts.level`, so no need to validate it here.
|
// The `Logger` class validates `opts.level`, so no need to validate it here.
|
||||||
ow(opts.revision, ow.optional.date);
|
ow(opts.revision, ow.optional.date);
|
||||||
ow(opts.version, ow.optional.string);
|
ow(opts.version, ow.optional.string);
|
||||||
|
|
||||||
this._bus = new EventBus('jsdoc', {
|
this._bus = new EventBus('jsdoc', {
|
||||||
cache: _.isBoolean(opts._cacheEventBus) ? opts._cacheEventBus : true
|
cache: _.isBoolean(opts._cacheEventBus) ? opts._cacheEventBus : true,
|
||||||
});
|
});
|
||||||
this._logger = new Logger({
|
this._logger = new Logger({
|
||||||
emitter: this._bus,
|
emitter: this._bus,
|
||||||
level: opts.logLevel
|
level: opts.logLevel,
|
||||||
});
|
});
|
||||||
this.flags = [];
|
this.flags = [];
|
||||||
this.revision = opts.revision;
|
this.revision = opts.revision;
|
||||||
this.version = opts.version;
|
this.version = opts.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The log level to use. Messages are logged only if they are at or above this level.
|
||||||
|
* Must be an enumerated value of {@link module:@jsdoc/cli.LOG_LEVELS}.
|
||||||
|
*
|
||||||
|
* The default value is `module:@jsdoc/cli.LOG_LEVELS.WARN`.
|
||||||
|
*/
|
||||||
|
get logLevel() {
|
||||||
|
return this._logger.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
set logLevel(level) {
|
||||||
|
this._logger.level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get help text for JSDoc.
|
||||||
|
*
|
||||||
|
* You can specify the maximum line length for the help text. This method attempts to fit each
|
||||||
|
* line within the maximum length, but it only splits on word boundaries. If you specify a small
|
||||||
|
* length, such as `10`, some lines will exceed that length.
|
||||||
|
*
|
||||||
|
* @param {Object} [opts] - Options for formatting the help text.
|
||||||
|
* @param {number} [opts.maxLength=Infinity] - The desired maximum length of each line in the
|
||||||
|
* formatted text.
|
||||||
|
* @return {string} The formatted help text.
|
||||||
|
*/
|
||||||
|
help(opts = {}) {
|
||||||
|
ow(opts, ow.object);
|
||||||
|
ow(opts.maxLength, ow.optional.number);
|
||||||
|
|
||||||
|
const maxLength = opts.maxLength || Infinity;
|
||||||
|
|
||||||
|
return (
|
||||||
|
`Options:\n${help({ maxLength })}\n\n` + 'Visit https://jsdoc.app/ for more information.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Details about the command-line flags that JSDoc recognizes.
|
||||||
|
*/
|
||||||
|
get knownFlags() {
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an array of command-line flags (also known as "options").
|
||||||
|
*
|
||||||
|
* Use the instance's `flags` property to retrieve the parsed flags later.
|
||||||
|
*
|
||||||
|
* @param {Array<string>} cliFlags - The command-line flags to parse.
|
||||||
|
* @returns {Object} The name and value for each flag. The `_` property contains all arguments
|
||||||
|
* other than flags and their values.
|
||||||
|
*/
|
||||||
|
parseFlags(cliFlags) {
|
||||||
|
ow(cliFlags, ow.array);
|
||||||
|
|
||||||
|
let normalizedFlags;
|
||||||
|
let parsed;
|
||||||
|
let parsedFlags;
|
||||||
|
let parsedFlagNames;
|
||||||
|
|
||||||
|
normalizedFlags = Object.keys(flags);
|
||||||
|
parsed = yargs.detailed(cliFlags, YARGS_FLAGS);
|
||||||
|
if (parsed.error) {
|
||||||
|
throw parsed.error;
|
||||||
}
|
}
|
||||||
|
parsedFlags = parsed.argv;
|
||||||
|
parsedFlagNames = new Set(Object.keys(parsedFlags));
|
||||||
|
|
||||||
/**
|
// Check all parsed flags for unknown flag names.
|
||||||
* The log level to use. Messages are logged only if they are at or above this level.
|
for (let flag of parsedFlagNames) {
|
||||||
* Must be an enumerated value of {@link module:@jsdoc/cli.LOG_LEVELS}.
|
if (!KNOWN_FLAGS.has(flag)) {
|
||||||
*
|
throw new TypeError(
|
||||||
* The default value is `module:@jsdoc/cli.LOG_LEVELS.WARN`.
|
'Unknown command-line option: ' + (flag.length === 1 ? `-${flag}` : `--${flag}`)
|
||||||
*/
|
|
||||||
get logLevel() {
|
|
||||||
return this._logger.level;
|
|
||||||
}
|
|
||||||
|
|
||||||
set logLevel(level) {
|
|
||||||
this._logger.level = level;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get help text for JSDoc.
|
|
||||||
*
|
|
||||||
* You can specify the maximum line length for the help text. This method attempts to fit each
|
|
||||||
* line within the maximum length, but it only splits on word boundaries. If you specify a small
|
|
||||||
* length, such as `10`, some lines will exceed that length.
|
|
||||||
*
|
|
||||||
* @param {Object} [opts] - Options for formatting the help text.
|
|
||||||
* @param {number} [opts.maxLength=Infinity] - The desired maximum length of each line in the
|
|
||||||
* formatted text.
|
|
||||||
* @return {string} The formatted help text.
|
|
||||||
*/
|
|
||||||
help(opts = {}) {
|
|
||||||
ow(opts, ow.object);
|
|
||||||
ow(opts.maxLength, ow.optional.number);
|
|
||||||
|
|
||||||
const maxLength = opts.maxLength || Infinity;
|
|
||||||
|
|
||||||
return (
|
|
||||||
`Options:\n${help({ maxLength })}\n\n` +
|
|
||||||
'Visit https://jsdoc.app/ for more information.'
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Validate the values of known flags.
|
||||||
* Details about the command-line flags that JSDoc recognizes.
|
for (let flag of normalizedFlags) {
|
||||||
*/
|
if (parsedFlags[flag] && flags[flag].choices) {
|
||||||
get knownFlags() {
|
let flagInfo = {
|
||||||
return flags;
|
name: flag,
|
||||||
|
alias: flags[flag].alias,
|
||||||
|
};
|
||||||
|
|
||||||
|
validateChoice(flagInfo, flags[flag].choices, parsedFlags[flag]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Only keep the long name of each flag.
|
||||||
* Parse an array of command-line flags (also known as "options").
|
this.flags = _.pick(parsedFlags, normalizedFlags.concat(['_']));
|
||||||
*
|
|
||||||
* Use the instance's `flags` property to retrieve the parsed flags later.
|
|
||||||
*
|
|
||||||
* @param {Array<string>} cliFlags - The command-line flags to parse.
|
|
||||||
* @returns {Object} The name and value for each flag. The `_` property contains all arguments
|
|
||||||
* other than flags and their values.
|
|
||||||
*/
|
|
||||||
parseFlags(cliFlags) {
|
|
||||||
ow(cliFlags, ow.array);
|
|
||||||
|
|
||||||
let normalizedFlags;
|
return this.flags;
|
||||||
let parsed;
|
}
|
||||||
let parsedFlags;
|
|
||||||
let parsedFlagNames;
|
|
||||||
|
|
||||||
normalizedFlags = Object.keys(flags);
|
/**
|
||||||
parsed = yargs.detailed(cliFlags, YARGS_FLAGS);
|
* A string that describes the current JSDoc version.
|
||||||
if (parsed.error) {
|
*/
|
||||||
throw parsed.error;
|
get versionDetails() {
|
||||||
}
|
let revision = '';
|
||||||
parsedFlags = parsed.argv;
|
|
||||||
parsedFlagNames = new Set(Object.keys(parsedFlags));
|
|
||||||
|
|
||||||
// Check all parsed flags for unknown flag names.
|
if (!this.version) {
|
||||||
for (let flag of parsedFlagNames) {
|
return '';
|
||||||
if (!KNOWN_FLAGS.has(flag)) {
|
|
||||||
throw new TypeError(
|
|
||||||
'Unknown command-line option: ' +
|
|
||||||
(flag.length === 1 ? `-${flag}` : `--${flag}`)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the values of known flags.
|
|
||||||
for (let flag of normalizedFlags) {
|
|
||||||
if (parsedFlags[flag] && flags[flag].choices) {
|
|
||||||
let flagInfo = {
|
|
||||||
name: flag,
|
|
||||||
alias: flags[flag].alias
|
|
||||||
};
|
|
||||||
|
|
||||||
validateChoice(flagInfo, flags[flag].choices, parsedFlags[flag]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only keep the long name of each flag.
|
|
||||||
this.flags = _.pick(parsedFlags, normalizedFlags.concat(['_']));
|
|
||||||
|
|
||||||
return this.flags;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (this.revision) {
|
||||||
* A string that describes the current JSDoc version.
|
revision = `(${this.revision.toUTCString()})`;
|
||||||
*/
|
|
||||||
get versionDetails() {
|
|
||||||
let revision = '';
|
|
||||||
|
|
||||||
if (!this.version) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.revision) {
|
|
||||||
revision = `(${this.revision.toUTCString()})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `JSDoc ${this.version} ${revision}`.trim();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return `JSDoc ${this.version} ${revision}`.trim();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Engine.LOG_LEVELS = LEVELS;
|
Engine.LOG_LEVELS = LEVELS;
|
||||||
|
|||||||
@ -8,100 +8,100 @@ const querystring = require('querystring');
|
|||||||
* @alias module:@jsdoc/cli/lib/flags
|
* @alias module:@jsdoc/cli/lib/flags
|
||||||
*/
|
*/
|
||||||
module.exports = {
|
module.exports = {
|
||||||
access: {
|
access: {
|
||||||
alias: 'a',
|
alias: 'a',
|
||||||
array: true,
|
array: true,
|
||||||
choices: ['all', 'package', 'private', 'protected', 'public', 'undefined'],
|
choices: ['all', 'package', 'private', 'protected', 'public', 'undefined'],
|
||||||
defaultDescription: 'All except `private`',
|
defaultDescription: 'All except `private`',
|
||||||
description: 'Document only symbols with the specified access level.',
|
description: 'Document only symbols with the specified access level.',
|
||||||
requiresArg: true
|
requiresArg: true,
|
||||||
},
|
},
|
||||||
configure: {
|
configure: {
|
||||||
alias: 'c',
|
alias: 'c',
|
||||||
description: 'The configuration file to use.',
|
description: 'The configuration file to use.',
|
||||||
normalize: true,
|
normalize: true,
|
||||||
requiresArg: true
|
requiresArg: true,
|
||||||
},
|
},
|
||||||
debug: {
|
debug: {
|
||||||
boolean: true,
|
boolean: true,
|
||||||
description: 'Log information to help with debugging.'
|
description: 'Log information to help with debugging.',
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
alias: 'd',
|
alias: 'd',
|
||||||
default: './out',
|
default: './out',
|
||||||
description: 'The output directory.',
|
description: 'The output directory.',
|
||||||
normalize: true,
|
normalize: true,
|
||||||
requiresArg: true
|
requiresArg: true,
|
||||||
},
|
},
|
||||||
encoding: {
|
encoding: {
|
||||||
alias: 'e',
|
alias: 'e',
|
||||||
default: 'utf8',
|
default: 'utf8',
|
||||||
description: 'The encoding to assume when reading source files.',
|
description: 'The encoding to assume when reading source files.',
|
||||||
requiresArg: true
|
requiresArg: true,
|
||||||
},
|
},
|
||||||
explain: {
|
explain: {
|
||||||
alias: 'X',
|
alias: 'X',
|
||||||
boolean: true,
|
boolean: true,
|
||||||
description: 'Print the parse results to the console and exit.'
|
description: 'Print the parse results to the console and exit.',
|
||||||
},
|
},
|
||||||
help: {
|
help: {
|
||||||
alias: 'h',
|
alias: 'h',
|
||||||
boolean: true,
|
boolean: true,
|
||||||
description: 'Print help information and exit.'
|
description: 'Print help information and exit.',
|
||||||
},
|
},
|
||||||
match: {
|
match: {
|
||||||
description: 'Run only tests whose names contain this value.',
|
description: 'Run only tests whose names contain this value.',
|
||||||
requiresArg: true
|
requiresArg: true,
|
||||||
},
|
},
|
||||||
package: {
|
package: {
|
||||||
alias: 'P',
|
alias: 'P',
|
||||||
description: 'The path to the `package.json` file to use.',
|
description: 'The path to the `package.json` file to use.',
|
||||||
normalize: true,
|
normalize: true,
|
||||||
requiresArg: true
|
requiresArg: true,
|
||||||
},
|
},
|
||||||
pedantic: {
|
pedantic: {
|
||||||
boolean: true,
|
boolean: true,
|
||||||
description: 'Treat errors as fatal errors, and treat warnings as errors.'
|
description: 'Treat errors as fatal errors, and treat warnings as errors.',
|
||||||
},
|
},
|
||||||
private: {
|
private: {
|
||||||
alias: 'p',
|
alias: 'p',
|
||||||
boolean: true,
|
boolean: true,
|
||||||
description: 'Document private symbols (equivalent to `--access all`).'
|
description: 'Document private symbols (equivalent to `--access all`).',
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
alias: 'q',
|
alias: 'q',
|
||||||
coerce: ((str) => cast(querystring.parse(str))),
|
coerce: (str) => cast(querystring.parse(str)),
|
||||||
description: 'A query string to parse and store (for example, `foo=bar&baz=true`).',
|
description: 'A query string to parse and store (for example, `foo=bar&baz=true`).',
|
||||||
requiresArg: true
|
requiresArg: true,
|
||||||
},
|
},
|
||||||
readme: {
|
readme: {
|
||||||
alias: 'R',
|
alias: 'R',
|
||||||
description: 'The `README` file to include in the documentation.',
|
description: 'The `README` file to include in the documentation.',
|
||||||
normalize: true,
|
normalize: true,
|
||||||
requiresArg: true
|
requiresArg: true,
|
||||||
},
|
},
|
||||||
recurse: {
|
recurse: {
|
||||||
alias: 'r',
|
alias: 'r',
|
||||||
boolean: true,
|
boolean: true,
|
||||||
description: 'Recurse into subdirectories to find source files.'
|
description: 'Recurse into subdirectories to find source files.',
|
||||||
},
|
},
|
||||||
template: {
|
template: {
|
||||||
alias: 't',
|
alias: 't',
|
||||||
description: 'The template package to use.',
|
description: 'The template package to use.',
|
||||||
requiresArg: true
|
requiresArg: true,
|
||||||
},
|
},
|
||||||
test: {
|
test: {
|
||||||
alias: 'T',
|
alias: 'T',
|
||||||
boolean: true,
|
boolean: true,
|
||||||
description: 'Run all tests and exit.'
|
description: 'Run all tests and exit.',
|
||||||
},
|
},
|
||||||
verbose: {
|
verbose: {
|
||||||
boolean: true,
|
boolean: true,
|
||||||
description: 'Log detailed information to the console.'
|
description: 'Log detailed information to the console.',
|
||||||
},
|
},
|
||||||
version: {
|
version: {
|
||||||
alias: 'v',
|
alias: 'v',
|
||||||
boolean: true,
|
boolean: true,
|
||||||
description: 'Display the version number and exit.'
|
description: 'Display the version number and exit.',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,34 +1,34 @@
|
|||||||
const flags = require('./flags');
|
const flags = require('./flags');
|
||||||
|
|
||||||
function padLeft(str, length) {
|
function padLeft(str, length) {
|
||||||
return str.padStart(str.length + length);
|
return str.padStart(str.length + length);
|
||||||
}
|
}
|
||||||
|
|
||||||
function padRight(str, length) {
|
function padRight(str, length) {
|
||||||
return str.padEnd(str.length + length);
|
return str.padEnd(str.length + length);
|
||||||
}
|
}
|
||||||
|
|
||||||
function findMaxLength(arr) {
|
function findMaxLength(arr) {
|
||||||
let max = 0;
|
let max = 0;
|
||||||
|
|
||||||
arr.forEach(({length}) => {
|
arr.forEach(({ length }) => {
|
||||||
max = Math.max(max, length);
|
max = Math.max(max, length);
|
||||||
});
|
});
|
||||||
|
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
function concatWithMaxLength(items, maxLength) {
|
function concatWithMaxLength(items, maxLength) {
|
||||||
let result = '';
|
let result = '';
|
||||||
|
|
||||||
// to prevent endless loops, always use the first item, regardless of length
|
// to prevent endless loops, always use the first item, regardless of length
|
||||||
result += items.shift();
|
result += items.shift();
|
||||||
|
|
||||||
while (items.length && (result.length + items[0].length < maxLength)) {
|
while (items.length && result.length + items[0].length < maxLength) {
|
||||||
result += ` ${items.shift()}`;
|
result += ` ${items.shift()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,41 +48,41 @@ function concatWithMaxLength(items, maxLength) {
|
|||||||
* @param {Object} opts - Options for formatting the text.
|
* @param {Object} opts - Options for formatting the text.
|
||||||
* @param {number} opts.maxLength - The maximum length of each line.
|
* @param {number} opts.maxLength - The maximum length of each line.
|
||||||
*/
|
*/
|
||||||
function formatHelpInfo({names, descriptions}, {maxLength}) {
|
function formatHelpInfo({ names, descriptions }, { maxLength }) {
|
||||||
const MARGIN_SIZE = 4;
|
const MARGIN_SIZE = 4;
|
||||||
const GUTTER_SIZE = MARGIN_SIZE;
|
const GUTTER_SIZE = MARGIN_SIZE;
|
||||||
const results = [];
|
const results = [];
|
||||||
|
|
||||||
const maxNameLength = findMaxLength(names);
|
const maxNameLength = findMaxLength(names);
|
||||||
const wrapDescriptionAt = maxLength - (MARGIN_SIZE * 2) - GUTTER_SIZE - maxNameLength;
|
const wrapDescriptionAt = maxLength - MARGIN_SIZE * 2 - GUTTER_SIZE - maxNameLength;
|
||||||
|
|
||||||
// Build the string for each flag.
|
// Build the string for each flag.
|
||||||
names.forEach((name, i) => {
|
names.forEach((name, i) => {
|
||||||
let result;
|
let result;
|
||||||
let partialDescription;
|
let partialDescription;
|
||||||
let words;
|
let words;
|
||||||
|
|
||||||
// Add some whitespace before the name.
|
// Add some whitespace before the name.
|
||||||
result = padLeft(name, MARGIN_SIZE);
|
result = padLeft(name, MARGIN_SIZE);
|
||||||
// Make the descriptions left-justified, with a gutter between the names and descriptions.
|
// Make the descriptions left-justified, with a gutter between the names and descriptions.
|
||||||
result = padRight(result, maxNameLength - name.length + GUTTER_SIZE);
|
result = padRight(result, maxNameLength - name.length + GUTTER_SIZE);
|
||||||
|
|
||||||
// Split the description on spaces.
|
// Split the description on spaces.
|
||||||
words = descriptions[i].split(' ');
|
words = descriptions[i].split(' ');
|
||||||
// Add as much of the description as we can fit on the first line.
|
// Add as much of the description as we can fit on the first line.
|
||||||
result += concatWithMaxLength(words, wrapDescriptionAt);
|
result += concatWithMaxLength(words, wrapDescriptionAt);
|
||||||
// If there's anything left, keep going until we've consumed the entire description.
|
// If there's anything left, keep going until we've consumed the entire description.
|
||||||
while (words.length) {
|
while (words.length) {
|
||||||
// Add whitespace for the name column and the gutter.
|
// Add whitespace for the name column and the gutter.
|
||||||
partialDescription = padLeft('', MARGIN_SIZE + maxNameLength + GUTTER_SIZE);
|
partialDescription = padLeft('', MARGIN_SIZE + maxNameLength + GUTTER_SIZE);
|
||||||
partialDescription += concatWithMaxLength(words, wrapDescriptionAt);
|
partialDescription += concatWithMaxLength(words, wrapDescriptionAt);
|
||||||
result += `\n${partialDescription}`;
|
result += `\n${partialDescription}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
results.push(result);
|
results.push(result);
|
||||||
});
|
});
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,41 +95,41 @@ function formatHelpInfo({names, descriptions}, {maxLength}) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
module.exports = ({ maxLength }) => {
|
module.exports = ({ maxLength }) => {
|
||||||
const flagInfo = {
|
const flagInfo = {
|
||||||
names: [],
|
names: [],
|
||||||
descriptions: []
|
descriptions: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.keys(flags)
|
Object.keys(flags)
|
||||||
.sort()
|
.sort()
|
||||||
.forEach(flagName => {
|
.forEach((flagName) => {
|
||||||
const flagDetail = flags[flagName];
|
const flagDetail = flags[flagName];
|
||||||
let description = '';
|
let description = '';
|
||||||
let name = '';
|
let name = '';
|
||||||
|
|
||||||
if (flagDetail.alias) {
|
if (flagDetail.alias) {
|
||||||
name += `-${flagDetail.alias}, `;
|
name += `-${flagDetail.alias}, `;
|
||||||
}
|
}
|
||||||
|
|
||||||
name += `--${flagName}`;
|
name += `--${flagName}`;
|
||||||
|
|
||||||
if (flagDetail.requiresArg) {
|
if (flagDetail.requiresArg) {
|
||||||
name += ' <value>';
|
name += ' <value>';
|
||||||
}
|
}
|
||||||
|
|
||||||
description += flagDetail.description;
|
description += flagDetail.description;
|
||||||
|
|
||||||
if (flagDetail.array) {
|
if (flagDetail.array) {
|
||||||
description += ' Can be specified more than once.';
|
description += ' Can be specified more than once.';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flagDetail.choices) {
|
if (flagDetail.choices) {
|
||||||
description += ` Accepts these values: ${flagDetail.choices.join(', ')}`;
|
description += ` Accepts these values: ${flagDetail.choices.join(', ')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
flagInfo.names.push(name);
|
flagInfo.names.push(name);
|
||||||
flagInfo.descriptions.push(description);
|
flagInfo.descriptions.push(description);
|
||||||
});
|
});
|
||||||
|
|
||||||
return `${formatHelpInfo(flagInfo, {maxLength}).join('\n')}`;
|
return `${formatHelpInfo(flagInfo, { maxLength }).join('\n')}`;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const {default: ow} = require('ow');
|
const { default: ow } = require('ow');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logging levels for the JSDoc logger. The default logging level is
|
* Logging levels for the JSDoc logger. The default logging level is
|
||||||
@ -10,150 +10,152 @@ const {default: ow} = require('ow');
|
|||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
const LEVELS = {
|
const LEVELS = {
|
||||||
/**
|
/**
|
||||||
* Do not log any messages.
|
* Do not log any messages.
|
||||||
*
|
*
|
||||||
* @alias module:@jsdoc/cli.LOG_LEVELS.SILENT
|
* @alias module:@jsdoc/cli.LOG_LEVELS.SILENT
|
||||||
*/
|
*/
|
||||||
SILENT: 0,
|
SILENT: 0,
|
||||||
/**
|
/**
|
||||||
* Log fatal errors that prevent JSDoc from running.
|
* Log fatal errors that prevent JSDoc from running.
|
||||||
*
|
*
|
||||||
* @alias module:@jsdoc/cli.LOG_LEVELS.FATAL
|
* @alias module:@jsdoc/cli.LOG_LEVELS.FATAL
|
||||||
*/
|
*/
|
||||||
FATAL: 10,
|
FATAL: 10,
|
||||||
/**
|
/**
|
||||||
* Log all errors, including errors from which JSDoc can recover.
|
* Log all errors, including errors from which JSDoc can recover.
|
||||||
*
|
*
|
||||||
* @alias module:@jsdoc/cli.LOG_LEVELS.ERROR
|
* @alias module:@jsdoc/cli.LOG_LEVELS.ERROR
|
||||||
*/
|
*/
|
||||||
ERROR: 20,
|
ERROR: 20,
|
||||||
/**
|
/**
|
||||||
* Log the following messages:
|
* Log the following messages:
|
||||||
*
|
*
|
||||||
* + Warnings
|
* + Warnings
|
||||||
* + Errors
|
* + Errors
|
||||||
*
|
*
|
||||||
* @alias module:@jsdoc/cli.LOG_LEVELS.WARN
|
* @alias module:@jsdoc/cli.LOG_LEVELS.WARN
|
||||||
*/
|
*/
|
||||||
WARN: 30,
|
WARN: 30,
|
||||||
/**
|
/**
|
||||||
* Log the following messages:
|
* Log the following messages:
|
||||||
*
|
*
|
||||||
* + Informational messages
|
* + Informational messages
|
||||||
* + Warnings
|
* + Warnings
|
||||||
* + Errors
|
* + Errors
|
||||||
*
|
*
|
||||||
* @alias module:@jsdoc/cli.LOG_LEVELS.INFO
|
* @alias module:@jsdoc/cli.LOG_LEVELS.INFO
|
||||||
*/
|
*/
|
||||||
INFO: 40,
|
INFO: 40,
|
||||||
/**
|
/**
|
||||||
* Log the following messages:
|
* Log the following messages:
|
||||||
*
|
*
|
||||||
* + Debugging messages
|
* + Debugging messages
|
||||||
* + Informational messages
|
* + Informational messages
|
||||||
* + Warnings
|
* + Warnings
|
||||||
* + Errors
|
* + Errors
|
||||||
*
|
*
|
||||||
* @alias module:@jsdoc/cli.LOG_LEVELS.DEBUG
|
* @alias module:@jsdoc/cli.LOG_LEVELS.DEBUG
|
||||||
*/
|
*/
|
||||||
DEBUG: 50,
|
DEBUG: 50,
|
||||||
/**
|
/**
|
||||||
* Log all messages.
|
* Log all messages.
|
||||||
*
|
*
|
||||||
* @alias module:@jsdoc/cli.LOG_LEVELS.VERBOSE
|
* @alias module:@jsdoc/cli.LOG_LEVELS.VERBOSE
|
||||||
*/
|
*/
|
||||||
VERBOSE: 1000
|
VERBOSE: 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_LEVEL = LEVELS.WARN;
|
const DEFAULT_LEVEL = LEVELS.WARN;
|
||||||
const FUNCS = {
|
const FUNCS = {
|
||||||
[LEVELS.DEBUG]: 'debug',
|
[LEVELS.DEBUG]: 'debug',
|
||||||
[LEVELS.ERROR]: 'error',
|
[LEVELS.ERROR]: 'error',
|
||||||
[LEVELS.INFO]: 'info',
|
[LEVELS.INFO]: 'info',
|
||||||
[LEVELS.FATAL]: 'error',
|
[LEVELS.FATAL]: 'error',
|
||||||
[LEVELS.VERBOSE]: 'debug',
|
[LEVELS.VERBOSE]: 'debug',
|
||||||
[LEVELS.WARN]: 'warn'
|
[LEVELS.WARN]: 'warn',
|
||||||
};
|
};
|
||||||
const LEVELS_BY_NUMBER = _.invert(LEVELS);
|
const LEVELS_BY_NUMBER = _.invert(LEVELS);
|
||||||
const PREFIXES = {
|
const PREFIXES = {
|
||||||
[LEVELS.DEBUG]: 'DEBUG: ',
|
[LEVELS.DEBUG]: 'DEBUG: ',
|
||||||
[LEVELS.ERROR]: 'ERROR: ',
|
[LEVELS.ERROR]: 'ERROR: ',
|
||||||
[LEVELS.FATAL]: 'FATAL: ',
|
[LEVELS.FATAL]: 'FATAL: ',
|
||||||
[LEVELS.WARN]: 'WARNING: '
|
[LEVELS.WARN]: 'WARNING: ',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add a prefix to a log message if necessary.
|
// Add a prefix to a log message if necessary.
|
||||||
function addPrefix(level, args) {
|
function addPrefix(level, args) {
|
||||||
const prefix = PREFIXES[level];
|
const prefix = PREFIXES[level];
|
||||||
|
|
||||||
if (prefix && _.isString(args[0])) {
|
if (prefix && _.isString(args[0])) {
|
||||||
args[0] = prefix + args[0];
|
args[0] = prefix + args[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Logger {
|
class Logger {
|
||||||
constructor(opts) {
|
constructor(opts) {
|
||||||
ow(opts, ow.object);
|
ow(opts, ow.object);
|
||||||
// We validate `opts.level` in the setter, so no need to validate it here.
|
// We validate `opts.level` in the setter, so no need to validate it here.
|
||||||
ow(opts.emitter, ow.object.partialShape({
|
ow(
|
||||||
off: ow.function,
|
opts.emitter,
|
||||||
on: ow.function,
|
ow.object.partialShape({
|
||||||
once: ow.function
|
off: ow.function,
|
||||||
}));
|
on: ow.function,
|
||||||
|
once: ow.function,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
this._console = opts._console || console;
|
this._console = opts._console || console;
|
||||||
const emitter = this._emitter = opts.emitter;
|
const emitter = (this._emitter = opts.emitter);
|
||||||
|
|
||||||
this.level = opts.level || DEFAULT_LEVEL;
|
this.level = opts.level || DEFAULT_LEVEL;
|
||||||
|
|
||||||
for (const levelName of Object.keys(LEVELS)) {
|
for (const levelName of Object.keys(LEVELS)) {
|
||||||
let levelNameLower;
|
let levelNameLower;
|
||||||
let levelNumber;
|
let levelNumber;
|
||||||
|
|
||||||
// `logger:silent` events are not a thing.
|
// `logger:silent` events are not a thing.
|
||||||
if (levelName === 'SILENT') {
|
if (levelName === 'SILENT') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
levelNameLower = levelName.toLowerCase();
|
levelNameLower = levelName.toLowerCase();
|
||||||
levelNumber = LEVELS[levelName];
|
levelNumber = LEVELS[levelName];
|
||||||
|
|
||||||
emitter.on(
|
emitter.on(`logger:${levelNameLower}`, (...args) => this._maybeLog(levelNumber, args));
|
||||||
`logger:${levelNameLower}`,
|
}
|
||||||
(...args) => this._maybeLog(levelNumber, args)
|
}
|
||||||
);
|
|
||||||
}
|
_maybeLog(level, args) {
|
||||||
|
if (this._level >= level) {
|
||||||
|
args = addPrefix(level, args);
|
||||||
|
this._console[FUNCS[level]](...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get level() {
|
||||||
|
return this._level;
|
||||||
|
}
|
||||||
|
|
||||||
|
set level(level) {
|
||||||
|
let errorMsg;
|
||||||
|
|
||||||
|
if (_.isUndefined(LEVELS_BY_NUMBER[level])) {
|
||||||
|
errorMsg = `Unrecognized logging level ${level}. Known levels are: `;
|
||||||
|
errorMsg += Object.keys(LEVELS)
|
||||||
|
.map((k) => `${k}: ${LEVELS[k]}`)
|
||||||
|
.join(', ');
|
||||||
|
|
||||||
|
throw new TypeError(errorMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
_maybeLog(level, args) {
|
this._level = level;
|
||||||
if (this._level >= level) {
|
}
|
||||||
args = addPrefix(level, args);
|
|
||||||
this._console[FUNCS[level]](...args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get level() {
|
|
||||||
return this._level;
|
|
||||||
}
|
|
||||||
|
|
||||||
set level(level) {
|
|
||||||
let errorMsg;
|
|
||||||
|
|
||||||
if (_.isUndefined(LEVELS_BY_NUMBER[level])) {
|
|
||||||
errorMsg = `Unrecognized logging level ${level}. Known levels are: `;
|
|
||||||
errorMsg += Object.keys(LEVELS).map(k => `${k}: ${LEVELS[k]}`).join(', ');
|
|
||||||
|
|
||||||
throw new TypeError(errorMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._level = level;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
LEVELS,
|
LEVELS,
|
||||||
Logger
|
Logger,
|
||||||
};
|
};
|
||||||
|
|||||||
123
packages/jsdoc-cli/package-lock.json
generated
123
packages/jsdoc-cli/package-lock.json
generated
@ -1,8 +1,129 @@
|
|||||||
{
|
{
|
||||||
"name": "@jsdoc/cli",
|
"name": "@jsdoc/cli",
|
||||||
"version": "0.2.5",
|
"version": "0.2.5",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "@jsdoc/cli",
|
||||||
|
"version": "0.2.5",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"ow": "^0.27.0",
|
||||||
|
"strip-bom": "^4.0.0",
|
||||||
|
"yargs-parser": "^20.2.9"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=v14.17.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sindresorhus/is": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/callsites": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dot-prop": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-obj": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-obj": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
|
},
|
||||||
|
"node_modules/lodash.isequal": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||||
|
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
|
||||||
|
},
|
||||||
|
"node_modules/ow": {
|
||||||
|
"version": "0.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ow/-/ow-0.27.0.tgz",
|
||||||
|
"integrity": "sha512-SGnrGUbhn4VaUGdU0EJLMwZWSupPmF46hnTRII7aCLCrqixTAC5eKo8kI4/XXf1eaaI8YEVT+3FeGNJI9himAQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sindresorhus/is": "^4.0.1",
|
||||||
|
"callsites": "^3.1.0",
|
||||||
|
"dot-prop": "^6.0.1",
|
||||||
|
"lodash.isequal": "^4.5.0",
|
||||||
|
"type-fest": "^1.2.1",
|
||||||
|
"vali-date": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-bom": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/type-fest": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-pfkPYCcuV0TJoo/jlsUeWNV8rk7uMU6ocnYNvca1Vu+pyKi8Rl8Zo2scPt9O72gCsXIm+dMxOOWuA3VFDSdzWA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vali-date": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs-parser": {
|
||||||
|
"version": "20.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
|
||||||
|
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sindresorhus/is": {
|
"@sindresorhus/is": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
const Engine = require('../../index');
|
const Engine = require('../../index');
|
||||||
|
|
||||||
describe('@jsdoc/cli', () => {
|
describe('@jsdoc/cli', () => {
|
||||||
it('is lib/engine', () => {
|
it('is lib/engine', () => {
|
||||||
const engine = require('../../lib/engine');
|
const engine = require('../../lib/engine');
|
||||||
|
|
||||||
expect(Engine).toBe(engine);
|
expect(Engine).toBe(engine);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -6,221 +6,221 @@ const TYPE_ERROR = 'TypeError';
|
|||||||
|
|
||||||
// Wrapper to prevent reuse of the event bus, which leads to `MaxListenersExceededWarning` messages.
|
// Wrapper to prevent reuse of the event bus, which leads to `MaxListenersExceededWarning` messages.
|
||||||
class Engine extends RealEngine {
|
class Engine extends RealEngine {
|
||||||
constructor(opts) {
|
constructor(opts) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
opts._cacheEventBus = false;
|
opts._cacheEventBus = false;
|
||||||
|
|
||||||
super(opts);
|
super(opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('@jsdoc/cli/lib/engine', () => {
|
describe('@jsdoc/cli/lib/engine', () => {
|
||||||
it('exists', () => {
|
it('exists', () => {
|
||||||
expect(Engine).toBeFunction();
|
expect(Engine).toBeFunction();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('works with no input', () => {
|
||||||
|
expect(() => new Engine()).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a static LOG_LEVELS property', () => {
|
||||||
|
expect(Engine.LOG_LEVELS).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has an empty array of flags by default', () => {
|
||||||
|
expect(new Engine().flags).toBeEmptyArray();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a property that contains the known flags', () => {
|
||||||
|
expect(new Engine().knownFlags).toBe(flags);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a logLevel property that defaults to LEVELS.WARN', () => {
|
||||||
|
expect(new Engine().logLevel).toBe(LEVELS.WARN);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has an undefined revision property by default', () => {
|
||||||
|
expect(new Engine().revision).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has an undefined version property by default', () => {
|
||||||
|
expect(new Engine().version).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a versionDetails property that is an empty string by default', () => {
|
||||||
|
expect(new Engine().versionDetails).toBeEmptyString();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if the input is not an object', () => {
|
||||||
|
expect(() => new Engine('hi')).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the logLevel if provided', () => {
|
||||||
|
const logLevel = LEVELS.VERBOSE;
|
||||||
|
const instance = new Engine({ logLevel });
|
||||||
|
|
||||||
|
expect(instance.logLevel).toBe(logLevel);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if the logLevel is invalid', () => {
|
||||||
|
const logLevel = LEVELS.VERBOSE + 1;
|
||||||
|
|
||||||
|
expect(() => new Engine({ logLevel })).toThrowErrorOfType(TYPE_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the revision if provided', () => {
|
||||||
|
const revision = new Date();
|
||||||
|
const instance = new Engine({ revision });
|
||||||
|
|
||||||
|
expect(instance.revision).toBe(revision);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if the revision is not a date', () => {
|
||||||
|
expect(() => new Engine({ revision: '1' })).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the version if provided', () => {
|
||||||
|
expect(new Engine({ version: '1.2.3' }).version).toBe('1.2.3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if the version is not a string', () => {
|
||||||
|
expect(() => new Engine({ version: 1 })).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('help', () => {
|
||||||
|
const instance = new Engine();
|
||||||
|
|
||||||
it('works with no input', () => {
|
it('works with no input', () => {
|
||||||
expect(() => new Engine()).not.toThrow();
|
expect(() => instance.help()).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a static LOG_LEVELS property', () => {
|
it('throws on bad input', () => {
|
||||||
expect(Engine.LOG_LEVELS).toBeObject();
|
expect(() => instance.help('hi')).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has an empty array of flags by default', () => {
|
it('returns a string', () => {
|
||||||
expect(new Engine().flags).toBeEmptyArray();
|
expect(instance.help()).toBeNonEmptyString();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a property that contains the known flags', () => {
|
it('honors a reasonable maxLength option', () => {
|
||||||
expect(new Engine().knownFlags).toBe(flags);
|
const max = 70;
|
||||||
|
const help = instance.help({ maxLength: max }).split('\n');
|
||||||
|
|
||||||
|
for (let line of help) {
|
||||||
|
expect(line.length).toBeLessThanOrEqualTo(max);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a logLevel property that defaults to LEVELS.WARN', () => {
|
it('throws on a bad maxLength option', () => {
|
||||||
expect(new Engine().logLevel).toBe(LEVELS.WARN);
|
expect(() => instance.help({ maxLength: 'long' })).toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('LOG_LEVELS', () => {
|
||||||
|
it('is lib/logger.LEVELS', () => {
|
||||||
|
expect(Engine.LOG_LEVELS).toBe(LEVELS);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parseFlags', () => {
|
||||||
|
it('throws with no input', () => {
|
||||||
|
expect(() => new Engine().parseFlags()).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has an undefined revision property by default', () => {
|
it('throws if the input is not an array', () => {
|
||||||
expect(new Engine().revision).toBeUndefined();
|
expect(() => new Engine().parseFlags({ foo: 'bar' })).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has an undefined version property by default', () => {
|
it('parses flags with no values', () => {
|
||||||
expect(new Engine().version).toBeUndefined();
|
expect(new Engine().parseFlags(['--help']).help).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a versionDetails property that is an empty string by default', () => {
|
it('parses flags with values', () => {
|
||||||
expect(new Engine().versionDetails).toBeEmptyString();
|
const parsed = new Engine().parseFlags(['--configure', 'conf.json']);
|
||||||
|
|
||||||
|
expect(parsed.configure).toBe('conf.json');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if the input is not an object', () => {
|
it('stores the flags in the `flags` property', () => {
|
||||||
expect(() => new Engine('hi')).toThrow();
|
const instance = new Engine();
|
||||||
|
|
||||||
|
instance.parseFlags(['--help']);
|
||||||
|
|
||||||
|
expect(instance.flags.help).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets the logLevel if provided', () => {
|
it('throws on unrecognized flags', () => {
|
||||||
const logLevel = LEVELS.VERBOSE;
|
expect(() => new Engine().parseFlags(['--notarealflag'])).toThrow();
|
||||||
const instance = new Engine({ logLevel });
|
|
||||||
|
|
||||||
expect(instance.logLevel).toBe(logLevel);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if the logLevel is invalid', () => {
|
it('throws on invalid flag values', () => {
|
||||||
const logLevel = LEVELS.VERBOSE + 1;
|
expect(() => new Engine().parseFlags(['--access', 'maybe'])).toThrow();
|
||||||
|
|
||||||
expect(() => new Engine({ logLevel })).toThrowErrorOfType(TYPE_ERROR);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets the revision if provided', () => {
|
it('includes the long and short name in the error if a value is invalid', () => {
|
||||||
const revision = new Date();
|
let error;
|
||||||
const instance = new Engine({ revision });
|
|
||||||
|
|
||||||
expect(instance.revision).toBe(revision);
|
try {
|
||||||
|
new Engine().parseFlags(['--access', 'just-this-once']);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toContain('-a/--access');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if the revision is not a date', () => {
|
it('includes the allowed values in the error if a value is invalid', () => {
|
||||||
expect(() => new Engine({ revision: '1' })).toThrow();
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
new Engine().parseFlags(['--access', 'maybe-later']);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toContain(flags.access.choices.join(', '));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets the version if provided', () => {
|
it('throws if a required value is missing', () => {
|
||||||
expect(new Engine({ version: '1.2.3' }).version).toBe('1.2.3');
|
expect(() => new Engine().parseFlags(['--template'])).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if the version is not a string', () => {
|
it('always uses the long flag name in the parsed flags', () => {
|
||||||
expect(() => new Engine({ version: 1 })).toThrow();
|
expect(new Engine().parseFlags(['-h']).help).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('help', () => {
|
it('coerces values to other types when appropriate', () => {
|
||||||
const instance = new Engine();
|
const parsed = new Engine().parseFlags(['--query', 'foo=bar&baz=true']);
|
||||||
|
|
||||||
it('works with no input', () => {
|
expect(parsed.query).toEqual({
|
||||||
expect(() => instance.help()).not.toThrow();
|
foo: 'bar',
|
||||||
});
|
baz: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('throws on bad input', () => {
|
describe('versionDetails', () => {
|
||||||
expect(() => instance.help('hi')).toThrow();
|
it('works with a version but no revision', () => {
|
||||||
});
|
const instance = new Engine({ version: '1.2.3' });
|
||||||
|
|
||||||
it('returns a string', () => {
|
expect(instance.versionDetails).toBe('JSDoc 1.2.3');
|
||||||
expect(instance.help()).toBeNonEmptyString();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('honors a reasonable maxLength option', () => {
|
|
||||||
const max = 70;
|
|
||||||
const help = instance.help({ maxLength: max }).split('\n');
|
|
||||||
|
|
||||||
for (let line of help) {
|
|
||||||
expect(line.length).toBeLessThanOrEqualTo(max);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws on a bad maxLength option', () => {
|
|
||||||
expect(() => instance.help({ maxLength: 'long' })).toThrow();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('LOG_LEVELS', () => {
|
it('contains an empty string with a revision but no version', () => {
|
||||||
it('is lib/logger.LEVELS', () => {
|
const revision = new Date();
|
||||||
expect(Engine.LOG_LEVELS).toBe(LEVELS);
|
const instance = new Engine({ revision });
|
||||||
});
|
|
||||||
|
expect(instance.versionDetails).toBeEmptyString();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('parseFlags', () => {
|
it('works with a version and a revision', () => {
|
||||||
it('throws with no input', () => {
|
const revision = new Date();
|
||||||
expect(() => new Engine().parseFlags()).toThrow();
|
const instance = new Engine({
|
||||||
});
|
version: '1.2.3',
|
||||||
|
revision,
|
||||||
|
});
|
||||||
|
|
||||||
it('throws if the input is not an array', () => {
|
expect(instance.versionDetails).toBe(`JSDoc 1.2.3 (${revision.toUTCString()})`);
|
||||||
expect(() => new Engine().parseFlags({ foo: 'bar' })).toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses flags with no values', () => {
|
|
||||||
expect(new Engine().parseFlags(['--help']).help).toBeTrue();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses flags with values', () => {
|
|
||||||
const parsed = new Engine().parseFlags(['--configure', 'conf.json']);
|
|
||||||
|
|
||||||
expect(parsed.configure).toBe('conf.json');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('stores the flags in the `flags` property', () => {
|
|
||||||
const instance = new Engine();
|
|
||||||
|
|
||||||
instance.parseFlags(['--help']);
|
|
||||||
|
|
||||||
expect(instance.flags.help).toBeTrue();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws on unrecognized flags', () => {
|
|
||||||
expect(() => new Engine().parseFlags(['--notarealflag'])).toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws on invalid flag values', () => {
|
|
||||||
expect(() => new Engine().parseFlags(['--access', 'maybe'])).toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('includes the long and short name in the error if a value is invalid', () => {
|
|
||||||
let error;
|
|
||||||
|
|
||||||
try {
|
|
||||||
new Engine().parseFlags(['--access', 'just-this-once']);
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error.message).toContain('-a/--access');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('includes the allowed values in the error if a value is invalid', () => {
|
|
||||||
let error;
|
|
||||||
|
|
||||||
try {
|
|
||||||
new Engine().parseFlags(['--access', 'maybe-later']);
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error.message).toContain(flags.access.choices.join(', '));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws if a required value is missing', () => {
|
|
||||||
expect(() => new Engine().parseFlags(['--template'])).toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('always uses the long flag name in the parsed flags', () => {
|
|
||||||
expect(new Engine().parseFlags(['-h']).help).toBeTrue();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('coerces values to other types when appropriate', () => {
|
|
||||||
const parsed = new Engine().parseFlags(['--query', 'foo=bar&baz=true']);
|
|
||||||
|
|
||||||
expect(parsed.query).toEqual({
|
|
||||||
foo: 'bar',
|
|
||||||
baz: true
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('versionDetails', () => {
|
|
||||||
it('works with a version but no revision', () => {
|
|
||||||
const instance = new Engine({ version: '1.2.3' });
|
|
||||||
|
|
||||||
expect(instance.versionDetails).toBe('JSDoc 1.2.3');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('contains an empty string with a revision but no version', () => {
|
|
||||||
const revision = new Date();
|
|
||||||
const instance = new Engine({ revision });
|
|
||||||
|
|
||||||
expect(instance.versionDetails).toBeEmptyString();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('works with a version and a revision', () => {
|
|
||||||
const revision = new Date();
|
|
||||||
const instance = new Engine({
|
|
||||||
version: '1.2.3',
|
|
||||||
revision
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(instance.versionDetails).toBe(`JSDoc 1.2.3 (${revision.toUTCString()})`);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,40 +1,40 @@
|
|||||||
const flags = require('../../../lib/flags');
|
const flags = require('../../../lib/flags');
|
||||||
const {default: ow} = require('ow');
|
const { default: ow } = require('ow');
|
||||||
|
|
||||||
function validate(name, opts) {
|
function validate(name, opts) {
|
||||||
name = `--${name}`;
|
name = `--${name}`;
|
||||||
|
|
||||||
if (!opts.description) {
|
if (!opts.description) {
|
||||||
throw new TypeError(`${name} is missing its description`);
|
throw new TypeError(`${name} is missing its description`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.array && opts.boolean) {
|
if (opts.array && opts.boolean) {
|
||||||
throw new TypeError(`${name} can be an array or a boolean, but not both`);
|
throw new TypeError(`${name} can be an array or a boolean, but not both`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.requiresArg && opts.boolean) {
|
if (opts.requiresArg && opts.boolean) {
|
||||||
throw new TypeError(`${name} can require an argument or be a boolean, but not both`);
|
throw new TypeError(`${name} can require an argument or be a boolean, but not both`);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ow(opts.coerce, ow.optional.function);
|
ow(opts.coerce, ow.optional.function);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new TypeError(`The coerce value for ${name} is not a function`);
|
throw new TypeError(`The coerce value for ${name} is not a function`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.choices && !opts.requiresArg) {
|
if (opts.choices && !opts.requiresArg) {
|
||||||
throw new TypeError(`${name} specifies choices, but not requiresArg`);
|
throw new TypeError(`${name} specifies choices, but not requiresArg`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('@jsdoc/cli/lib/flags', () => {
|
describe('@jsdoc/cli/lib/flags', () => {
|
||||||
it('is an object', () => {
|
it('is an object', () => {
|
||||||
expect(flags).toBeObject();
|
expect(flags).toBeObject();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has reasonable settings for each flag', () => {
|
it('has reasonable settings for each flag', () => {
|
||||||
for (let flag of Object.keys(flags)) {
|
for (let flag of Object.keys(flags)) {
|
||||||
expect(() => validate(flag, flags[flag])).not.toThrow();
|
expect(() => validate(flag, flags[flag])).not.toThrow();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
describe('@jsdoc/cli/lib/help', () => {
|
describe('@jsdoc/cli/lib/help', () => {
|
||||||
// Tested indirectly by the tests for `@jsdoc/cli/lib/engine`.
|
// Tested indirectly by the tests for `@jsdoc/cli/lib/engine`.
|
||||||
});
|
});
|
||||||
|
|||||||
@ -5,203 +5,204 @@ const ARGUMENT_ERROR = 'ArgumentError';
|
|||||||
const TYPE_ERROR = 'TypeError';
|
const TYPE_ERROR = 'TypeError';
|
||||||
|
|
||||||
describe('@jsdoc/cli/lib/logger', () => {
|
describe('@jsdoc/cli/lib/logger', () => {
|
||||||
describe('Logger', () => {
|
describe('Logger', () => {
|
||||||
let bus;
|
let bus;
|
||||||
let logger;
|
let logger;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
bus = new EventBus('loggerTest', {
|
bus = new EventBus('loggerTest', {
|
||||||
_console: console,
|
_console: console,
|
||||||
cache: false
|
cache: false,
|
||||||
});
|
});
|
||||||
logger = new Logger({ emitter: bus });
|
logger = new Logger({ emitter: bus });
|
||||||
|
|
||||||
['debug', 'error', 'info', 'warn'].forEach(func => spyOn(console, func));
|
['debug', 'error', 'info', 'warn'].forEach((func) => spyOn(console, func));
|
||||||
});
|
|
||||||
|
|
||||||
it('exports a Logger constructor', () => {
|
|
||||||
expect(() => new Logger({ emitter: bus })).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('exports a LEVELS enum', () => {
|
|
||||||
expect(LEVELS).toBeNonEmptyObject();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('constructor', () => {
|
|
||||||
it('throws on invalid input', () => {
|
|
||||||
expect(() => new Logger()).toThrowErrorOfType(ARGUMENT_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('accepts a valid emitter', () => {
|
|
||||||
expect(() => new Logger({ emitter: bus })).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws on an invalid emitter', () => {
|
|
||||||
expect(() => new Logger({ emitter: {} })).toThrowErrorOfType(ARGUMENT_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('accepts a valid level', () => {
|
|
||||||
expect(() => new Logger({
|
|
||||||
emitter: bus,
|
|
||||||
level: LEVELS.VERBOSE
|
|
||||||
})).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws on an invalid level', () => {
|
|
||||||
expect(() => new Logger({
|
|
||||||
emitter: bus,
|
|
||||||
level: LEVELS.VERBOSE + 1
|
|
||||||
})).toThrowErrorOfType(TYPE_ERROR);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('events', () => {
|
|
||||||
it('passes all event arguments through', () => {
|
|
||||||
const args = [
|
|
||||||
'My name is %s %s %s',
|
|
||||||
'foo',
|
|
||||||
'bar',
|
|
||||||
'baz'
|
|
||||||
];
|
|
||||||
const eventType = 'logger:info';
|
|
||||||
|
|
||||||
logger.level = LEVELS.VERBOSE;
|
|
||||||
bus.emit(eventType, ...args);
|
|
||||||
|
|
||||||
expect(console.info).toHaveBeenCalledWith(...args);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs logger:fatal events by default', () => {
|
|
||||||
bus.emit('logger:fatal');
|
|
||||||
|
|
||||||
expect(console.error).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not log logger:fatal events when level is SILENT', () => {
|
|
||||||
logger.level = LEVELS.SILENT;
|
|
||||||
bus.emit('logger:fatal');
|
|
||||||
|
|
||||||
expect(console.error).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs logger:error events by default', () => {
|
|
||||||
bus.emit('logger:error');
|
|
||||||
|
|
||||||
expect(console.error).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not log logger:error events when level is FATAL', () => {
|
|
||||||
logger.level = LEVELS.FATAL;
|
|
||||||
bus.emit('logger:error');
|
|
||||||
|
|
||||||
expect(console.error).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs logger:warn events by default', () => {
|
|
||||||
bus.emit('logger:warn');
|
|
||||||
|
|
||||||
expect(console.warn).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not log logger:warn events when level is ERROR', () => {
|
|
||||||
logger.level = LEVELS.ERROR;
|
|
||||||
bus.emit('logger:warn');
|
|
||||||
|
|
||||||
expect(console.warn).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not log logger:info events by default', () => {
|
|
||||||
bus.emit('logger:info');
|
|
||||||
|
|
||||||
expect(console.info).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs logger:info events when level is INFO', () => {
|
|
||||||
logger.level = LEVELS.INFO;
|
|
||||||
bus.emit('logger:info');
|
|
||||||
|
|
||||||
expect(console.info).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not log logger:debug events by default', () => {
|
|
||||||
bus.emit('logger:debug');
|
|
||||||
|
|
||||||
expect(console.debug).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs logger:debug events when level is DEBUG', () => {
|
|
||||||
logger.level = LEVELS.DEBUG;
|
|
||||||
bus.emit('logger:debug');
|
|
||||||
|
|
||||||
expect(console.debug).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not log logger:verbose events by default', () => {
|
|
||||||
bus.emit('logger:verbose');
|
|
||||||
|
|
||||||
expect(console.debug).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs logger:verbose events when level is VERBOSE', () => {
|
|
||||||
logger.level = LEVELS.VERBOSE;
|
|
||||||
bus.emit('logger:verbose');
|
|
||||||
|
|
||||||
expect(console.debug).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('level', () => {
|
|
||||||
it('contains the current log level', () => {
|
|
||||||
expect(logger.level).toBe(LEVELS.WARN);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws when set to an invalid value', () => {
|
|
||||||
expect(() => {
|
|
||||||
logger.level = LEVELS.VERBOSE + 1;
|
|
||||||
}).toThrowErrorOfType(TYPE_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
// The `events` tests set this property to valid values, so no need to test that
|
|
||||||
// behavior again here.
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('LEVELS', () => {
|
it('exports a Logger constructor', () => {
|
||||||
it('has a numeric SILENT property', () => {
|
expect(() => new Logger({ emitter: bus })).not.toThrow();
|
||||||
expect(LEVELS.SILENT).toBeWholeNumber();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a numeric FATAL property', () => {
|
|
||||||
expect(LEVELS.FATAL).toBeWholeNumber();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a numeric ERROR property', () => {
|
|
||||||
expect(LEVELS.ERROR).toBeWholeNumber();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a numeric WARN property', () => {
|
|
||||||
expect(LEVELS.WARN).toBeWholeNumber();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a numeric INFO property', () => {
|
|
||||||
expect(LEVELS.INFO).toBeWholeNumber();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a numeric DEBUG property', () => {
|
|
||||||
expect(LEVELS.DEBUG).toBeWholeNumber();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a numeric VERBOSE property', () => {
|
|
||||||
expect(LEVELS.VERBOSE).toBeWholeNumber();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('orders the log levels correctly', () => {
|
|
||||||
expect(LEVELS.SILENT).toBeLessThan(LEVELS.FATAL);
|
|
||||||
expect(LEVELS.FATAL).toBeLessThan(LEVELS.ERROR);
|
|
||||||
expect(LEVELS.ERROR).toBeLessThan(LEVELS.WARN);
|
|
||||||
expect(LEVELS.WARN).toBeLessThan(LEVELS.INFO);
|
|
||||||
expect(LEVELS.INFO).toBeLessThan(LEVELS.DEBUG);
|
|
||||||
expect(LEVELS.DEBUG).toBeLessThan(LEVELS.VERBOSE);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('exports a LEVELS enum', () => {
|
||||||
|
expect(LEVELS).toBeNonEmptyObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('constructor', () => {
|
||||||
|
it('throws on invalid input', () => {
|
||||||
|
expect(() => new Logger()).toThrowErrorOfType(ARGUMENT_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts a valid emitter', () => {
|
||||||
|
expect(() => new Logger({ emitter: bus })).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws on an invalid emitter', () => {
|
||||||
|
expect(() => new Logger({ emitter: {} })).toThrowErrorOfType(ARGUMENT_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts a valid level', () => {
|
||||||
|
expect(
|
||||||
|
() =>
|
||||||
|
new Logger({
|
||||||
|
emitter: bus,
|
||||||
|
level: LEVELS.VERBOSE,
|
||||||
|
})
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws on an invalid level', () => {
|
||||||
|
expect(
|
||||||
|
() =>
|
||||||
|
new Logger({
|
||||||
|
emitter: bus,
|
||||||
|
level: LEVELS.VERBOSE + 1,
|
||||||
|
})
|
||||||
|
).toThrowErrorOfType(TYPE_ERROR);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('events', () => {
|
||||||
|
it('passes all event arguments through', () => {
|
||||||
|
const args = ['My name is %s %s %s', 'foo', 'bar', 'baz'];
|
||||||
|
const eventType = 'logger:info';
|
||||||
|
|
||||||
|
logger.level = LEVELS.VERBOSE;
|
||||||
|
bus.emit(eventType, ...args);
|
||||||
|
|
||||||
|
expect(console.info).toHaveBeenCalledWith(...args);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs logger:fatal events by default', () => {
|
||||||
|
bus.emit('logger:fatal');
|
||||||
|
|
||||||
|
expect(console.error).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not log logger:fatal events when level is SILENT', () => {
|
||||||
|
logger.level = LEVELS.SILENT;
|
||||||
|
bus.emit('logger:fatal');
|
||||||
|
|
||||||
|
expect(console.error).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs logger:error events by default', () => {
|
||||||
|
bus.emit('logger:error');
|
||||||
|
|
||||||
|
expect(console.error).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not log logger:error events when level is FATAL', () => {
|
||||||
|
logger.level = LEVELS.FATAL;
|
||||||
|
bus.emit('logger:error');
|
||||||
|
|
||||||
|
expect(console.error).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs logger:warn events by default', () => {
|
||||||
|
bus.emit('logger:warn');
|
||||||
|
|
||||||
|
expect(console.warn).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not log logger:warn events when level is ERROR', () => {
|
||||||
|
logger.level = LEVELS.ERROR;
|
||||||
|
bus.emit('logger:warn');
|
||||||
|
|
||||||
|
expect(console.warn).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not log logger:info events by default', () => {
|
||||||
|
bus.emit('logger:info');
|
||||||
|
|
||||||
|
expect(console.info).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs logger:info events when level is INFO', () => {
|
||||||
|
logger.level = LEVELS.INFO;
|
||||||
|
bus.emit('logger:info');
|
||||||
|
|
||||||
|
expect(console.info).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not log logger:debug events by default', () => {
|
||||||
|
bus.emit('logger:debug');
|
||||||
|
|
||||||
|
expect(console.debug).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs logger:debug events when level is DEBUG', () => {
|
||||||
|
logger.level = LEVELS.DEBUG;
|
||||||
|
bus.emit('logger:debug');
|
||||||
|
|
||||||
|
expect(console.debug).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not log logger:verbose events by default', () => {
|
||||||
|
bus.emit('logger:verbose');
|
||||||
|
|
||||||
|
expect(console.debug).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs logger:verbose events when level is VERBOSE', () => {
|
||||||
|
logger.level = LEVELS.VERBOSE;
|
||||||
|
bus.emit('logger:verbose');
|
||||||
|
|
||||||
|
expect(console.debug).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('level', () => {
|
||||||
|
it('contains the current log level', () => {
|
||||||
|
expect(logger.level).toBe(LEVELS.WARN);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws when set to an invalid value', () => {
|
||||||
|
expect(() => {
|
||||||
|
logger.level = LEVELS.VERBOSE + 1;
|
||||||
|
}).toThrowErrorOfType(TYPE_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
// The `events` tests set this property to valid values, so no need to test that
|
||||||
|
// behavior again here.
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('LEVELS', () => {
|
||||||
|
it('has a numeric SILENT property', () => {
|
||||||
|
expect(LEVELS.SILENT).toBeWholeNumber();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a numeric FATAL property', () => {
|
||||||
|
expect(LEVELS.FATAL).toBeWholeNumber();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a numeric ERROR property', () => {
|
||||||
|
expect(LEVELS.ERROR).toBeWholeNumber();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a numeric WARN property', () => {
|
||||||
|
expect(LEVELS.WARN).toBeWholeNumber();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a numeric INFO property', () => {
|
||||||
|
expect(LEVELS.INFO).toBeWholeNumber();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a numeric DEBUG property', () => {
|
||||||
|
expect(LEVELS.DEBUG).toBeWholeNumber();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a numeric VERBOSE property', () => {
|
||||||
|
expect(LEVELS.VERBOSE).toBeWholeNumber();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('orders the log levels correctly', () => {
|
||||||
|
expect(LEVELS.SILENT).toBeLessThan(LEVELS.FATAL);
|
||||||
|
expect(LEVELS.FATAL).toBeLessThan(LEVELS.ERROR);
|
||||||
|
expect(LEVELS.ERROR).toBeLessThan(LEVELS.WARN);
|
||||||
|
expect(LEVELS.WARN).toBeLessThan(LEVELS.INFO);
|
||||||
|
expect(LEVELS.INFO).toBeLessThan(LEVELS.DEBUG);
|
||||||
|
expect(LEVELS.DEBUG).toBeLessThan(LEVELS.VERBOSE);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -8,6 +8,6 @@ const config = require('./lib/config');
|
|||||||
const name = require('./lib/name');
|
const name = require('./lib/name');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
config,
|
config,
|
||||||
name
|
name,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,125 +11,119 @@ const stripJsonComments = require('strip-json-comments');
|
|||||||
|
|
||||||
const MODULE_NAME = 'jsdoc';
|
const MODULE_NAME = 'jsdoc';
|
||||||
|
|
||||||
const defaults = exports.defaults = {
|
const defaults = (exports.defaults = {
|
||||||
// TODO(hegemonic): Integrate CLI options with other options.
|
// TODO(hegemonic): Integrate CLI options with other options.
|
||||||
opts: {
|
opts: {
|
||||||
destination: './out',
|
destination: './out',
|
||||||
encoding: 'utf8'
|
encoding: 'utf8',
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* The JSDoc plugins to load.
|
||||||
|
*/
|
||||||
|
plugins: [],
|
||||||
|
// TODO(hegemonic): Move to `source` or remove.
|
||||||
|
recurseDepth: 10,
|
||||||
|
/**
|
||||||
|
* Settings for loading and parsing source files.
|
||||||
|
*/
|
||||||
|
source: {
|
||||||
/**
|
/**
|
||||||
* The JSDoc plugins to load.
|
* A regular expression that matches source files to exclude from processing.
|
||||||
|
*
|
||||||
|
* To exclude files if any portion of their path begins with an underscore, use the value
|
||||||
|
* `(^|\\/|\\\\)_`.
|
||||||
*/
|
*/
|
||||||
plugins: [],
|
excludePattern: '',
|
||||||
// TODO(hegemonic): Move to `source` or remove.
|
|
||||||
recurseDepth: 10,
|
|
||||||
/**
|
/**
|
||||||
* Settings for loading and parsing source files.
|
* A regular expression that matches source files that JSDoc should process.
|
||||||
|
*
|
||||||
|
* By default, all source files with the extensions `.js`, `.jsdoc`, and `.jsx` are
|
||||||
|
* processed.
|
||||||
*/
|
*/
|
||||||
source: {
|
includePattern: '.+\\.js(doc|x)?$',
|
||||||
/**
|
|
||||||
* A regular expression that matches source files to exclude from processing.
|
|
||||||
*
|
|
||||||
* To exclude files if any portion of their path begins with an underscore, use the value
|
|
||||||
* `(^|\\/|\\\\)_`.
|
|
||||||
*/
|
|
||||||
excludePattern: '',
|
|
||||||
/**
|
|
||||||
* A regular expression that matches source files that JSDoc should process.
|
|
||||||
*
|
|
||||||
* By default, all source files with the extensions `.js`, `.jsdoc`, and `.jsx` are
|
|
||||||
* processed.
|
|
||||||
*/
|
|
||||||
includePattern: '.+\\.js(doc|x)?$',
|
|
||||||
/**
|
|
||||||
* The type of source file. In general, you should use the value `module`. If none of your
|
|
||||||
* source files use ECMAScript >=2015 syntax, you can use the value `script`.
|
|
||||||
*/
|
|
||||||
type: 'module'
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* Settings for interpreting JSDoc tags.
|
* The type of source file. In general, you should use the value `module`. If none of your
|
||||||
|
* source files use ECMAScript >=2015 syntax, you can use the value `script`.
|
||||||
*/
|
*/
|
||||||
tags: {
|
type: 'module',
|
||||||
/**
|
},
|
||||||
* Set to `true` to allow tags that JSDoc does not recognize.
|
/**
|
||||||
*/
|
* Settings for interpreting JSDoc tags.
|
||||||
allowUnknownTags: true,
|
*/
|
||||||
// TODO(hegemonic): Use module paths, not magic strings.
|
tags: {
|
||||||
/**
|
|
||||||
* The JSDoc tag dictionaries to load.
|
|
||||||
*
|
|
||||||
* If you specify two or more tag dictionaries, and a tag is defined in multiple
|
|
||||||
* dictionaries, JSDoc uses the definition from the first dictionary that includes that tag.
|
|
||||||
*/
|
|
||||||
dictionaries: [
|
|
||||||
'jsdoc',
|
|
||||||
'closure'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* Settings for generating output with JSDoc templates. Some JSDoc templates might ignore these
|
* Set to `true` to allow tags that JSDoc does not recognize.
|
||||||
* settings.
|
|
||||||
*/
|
*/
|
||||||
templates: {
|
allowUnknownTags: true,
|
||||||
/**
|
// TODO(hegemonic): Use module paths, not magic strings.
|
||||||
* Set to `true` to use a monospaced font for links to other code symbols, but not links to
|
/**
|
||||||
* websites.
|
* The JSDoc tag dictionaries to load.
|
||||||
*/
|
*
|
||||||
cleverLinks: false,
|
* If you specify two or more tag dictionaries, and a tag is defined in multiple
|
||||||
/**
|
* dictionaries, JSDoc uses the definition from the first dictionary that includes that tag.
|
||||||
* Set to `true` to use a monospaced font for all links.
|
*/
|
||||||
*/
|
dictionaries: ['jsdoc', 'closure'],
|
||||||
monospaceLinks: false
|
},
|
||||||
}
|
/**
|
||||||
};
|
* Settings for generating output with JSDoc templates. Some JSDoc templates might ignore these
|
||||||
|
* settings.
|
||||||
|
*/
|
||||||
|
templates: {
|
||||||
|
/**
|
||||||
|
* Set to `true` to use a monospaced font for links to other code symbols, but not links to
|
||||||
|
* websites.
|
||||||
|
*/
|
||||||
|
cleverLinks: false,
|
||||||
|
/**
|
||||||
|
* Set to `true` to use a monospaced font for all links.
|
||||||
|
*/
|
||||||
|
monospaceLinks: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: Consider exporting this class.
|
// TODO: Consider exporting this class.
|
||||||
class Config {
|
class Config {
|
||||||
constructor(filepath, config) {
|
constructor(filepath, config) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.filepath = filepath;
|
this.filepath = filepath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadJson(filepath, content) {
|
function loadJson(filepath, content) {
|
||||||
return defaultLoaders['.json'](filepath, stripBom(stripJsonComments(content)));
|
return defaultLoaders['.json'](filepath, stripBom(stripJsonComments(content)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadYaml(filepath, content) {
|
function loadYaml(filepath, content) {
|
||||||
return defaultLoaders['.yaml'](filepath, stripBom(content));
|
return defaultLoaders['.yaml'](filepath, stripBom(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
const explorerSync = cosmiconfigSync(MODULE_NAME, {
|
const explorerSync = cosmiconfigSync(MODULE_NAME, {
|
||||||
cache: false,
|
cache: false,
|
||||||
loaders: {
|
loaders: {
|
||||||
'.json': loadJson,
|
'.json': loadJson,
|
||||||
'.yaml': loadYaml,
|
'.yaml': loadYaml,
|
||||||
'.yml': loadYaml,
|
'.yml': loadYaml,
|
||||||
noExt: loadYaml
|
noExt: loadYaml,
|
||||||
},
|
},
|
||||||
searchPlaces: [
|
searchPlaces: [
|
||||||
'package.json',
|
'package.json',
|
||||||
`.${MODULE_NAME}rc`,
|
`.${MODULE_NAME}rc`,
|
||||||
`.${MODULE_NAME}rc.json`,
|
`.${MODULE_NAME}rc.json`,
|
||||||
`.${MODULE_NAME}rc.yaml`,
|
`.${MODULE_NAME}rc.yaml`,
|
||||||
`.${MODULE_NAME}rc.yml`,
|
`.${MODULE_NAME}rc.yml`,
|
||||||
`.${MODULE_NAME}rc.js`,
|
`.${MODULE_NAME}rc.js`,
|
||||||
`${MODULE_NAME}.config.js`
|
`${MODULE_NAME}.config.js`,
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.loadSync = (filepath) => {
|
exports.loadSync = (filepath) => {
|
||||||
let loaded;
|
let loaded;
|
||||||
|
|
||||||
if (filepath) {
|
if (filepath) {
|
||||||
loaded = explorerSync.load(filepath);
|
loaded = explorerSync.load(filepath);
|
||||||
} else {
|
} else {
|
||||||
loaded = explorerSync.search() || {};
|
loaded = explorerSync.search() || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Config(
|
return new Config(loaded.filepath, _.defaultsDeep({}, loaded.config, defaults));
|
||||||
loaded.filepath,
|
|
||||||
_.defaultsDeep({}, loaded.config, defaults)
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -16,10 +16,10 @@ const hasOwnProp = Object.prototype.hasOwnProperty;
|
|||||||
* @memberof module:jsdoc/name
|
* @memberof module:jsdoc/name
|
||||||
*/
|
*/
|
||||||
exports.LONGNAMES = {
|
exports.LONGNAMES = {
|
||||||
/** Longname used for doclets that do not have a longname, such as anonymous functions. */
|
/** Longname used for doclets that do not have a longname, such as anonymous functions. */
|
||||||
ANONYMOUS: '<anonymous>',
|
ANONYMOUS: '<anonymous>',
|
||||||
/** Longname that represents global scope. */
|
/** Longname that represents global scope. */
|
||||||
GLOBAL: '<global>'
|
GLOBAL: '<global>',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Module namespace prefix.
|
// Module namespace prefix.
|
||||||
@ -32,26 +32,26 @@ exports.MODULE_NAMESPACE = 'module:';
|
|||||||
* @static
|
* @static
|
||||||
* @memberof module:jsdoc/name
|
* @memberof module:jsdoc/name
|
||||||
*/
|
*/
|
||||||
const SCOPE = exports.SCOPE = {
|
const SCOPE = (exports.SCOPE = {
|
||||||
NAMES: {
|
NAMES: {
|
||||||
GLOBAL: 'global',
|
GLOBAL: 'global',
|
||||||
INNER: 'inner',
|
INNER: 'inner',
|
||||||
INSTANCE: 'instance',
|
INSTANCE: 'instance',
|
||||||
STATIC: 'static'
|
STATIC: 'static',
|
||||||
},
|
},
|
||||||
PUNC: {
|
PUNC: {
|
||||||
INNER: '~',
|
INNER: '~',
|
||||||
INSTANCE: '#',
|
INSTANCE: '#',
|
||||||
STATIC: '.'
|
STATIC: '.',
|
||||||
}
|
},
|
||||||
};
|
});
|
||||||
|
|
||||||
// Keys must be lowercase.
|
// Keys must be lowercase.
|
||||||
const SCOPE_TO_PUNC = exports.SCOPE_TO_PUNC = {
|
const SCOPE_TO_PUNC = (exports.SCOPE_TO_PUNC = {
|
||||||
inner: SCOPE.PUNC.INNER,
|
inner: SCOPE.PUNC.INNER,
|
||||||
instance: SCOPE.PUNC.INSTANCE,
|
instance: SCOPE.PUNC.INSTANCE,
|
||||||
static: SCOPE.PUNC.STATIC
|
static: SCOPE.PUNC.STATIC,
|
||||||
};
|
});
|
||||||
|
|
||||||
exports.PUNC_TO_SCOPE = _.invert(SCOPE_TO_PUNC);
|
exports.PUNC_TO_SCOPE = _.invert(SCOPE_TO_PUNC);
|
||||||
|
|
||||||
@ -78,9 +78,9 @@ const REGEXP_NAME_DESCRIPTION = new RegExp(`^(\\[[^\\]]+\\]|\\S+)${DESCRIPTION}`
|
|||||||
* parent; otherwise, `false`.
|
* parent; otherwise, `false`.
|
||||||
*/
|
*/
|
||||||
exports.nameIsLongname = (name, memberof) => {
|
exports.nameIsLongname = (name, memberof) => {
|
||||||
const regexp = new RegExp(`^${escape(memberof)}${SCOPE_PUNC_STRING}`);
|
const regexp = new RegExp(`^${escape(memberof)}${SCOPE_PUNC_STRING}`);
|
||||||
|
|
||||||
return regexp.test(name);
|
return regexp.test(name);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,14 +91,14 @@ exports.nameIsLongname = (name, memberof) => {
|
|||||||
* @param {string} name - The name in which to change `prototype` to `#`.
|
* @param {string} name - The name in which to change `prototype` to `#`.
|
||||||
* @returns {string} The updated name.
|
* @returns {string} The updated name.
|
||||||
*/
|
*/
|
||||||
const prototypeToPunc = exports.prototypeToPunc = name => {
|
const prototypeToPunc = (exports.prototypeToPunc = (name) => {
|
||||||
// Don't mangle symbols named `prototype`.
|
// Don't mangle symbols named `prototype`.
|
||||||
if (name === 'prototype') {
|
if (name === 'prototype') {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return name.replace(/(?:^|\.)prototype\.?/g, SCOPE.PUNC.INSTANCE);
|
return name.replace(/(?:^|\.)prototype\.?/g, SCOPE.PUNC.INSTANCE);
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a name begins with a character that identifies a scope.
|
* Check whether a name begins with a character that identifies a scope.
|
||||||
@ -106,7 +106,7 @@ const prototypeToPunc = exports.prototypeToPunc = name => {
|
|||||||
* @param {string} name - The name to check.
|
* @param {string} name - The name to check.
|
||||||
* @returns {boolean} `true` if the name begins with a scope character; otherwise, `false`.
|
* @returns {boolean} `true` if the name begins with a scope character; otherwise, `false`.
|
||||||
*/
|
*/
|
||||||
exports.hasLeadingScope = name => REGEXP_LEADING_SCOPE.test(name);
|
exports.hasLeadingScope = (name) => REGEXP_LEADING_SCOPE.test(name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a name ends with a character that identifies a scope.
|
* Check whether a name ends with a character that identifies a scope.
|
||||||
@ -114,7 +114,7 @@ exports.hasLeadingScope = name => REGEXP_LEADING_SCOPE.test(name);
|
|||||||
* @param {string} name - The name to check.
|
* @param {string} name - The name to check.
|
||||||
* @returns {boolean} `true` if the name ends with a scope character; otherwise, `false`.
|
* @returns {boolean} `true` if the name ends with a scope character; otherwise, `false`.
|
||||||
*/
|
*/
|
||||||
exports.hasTrailingScope = name => REGEXP_TRAILING_SCOPE.test(name);
|
exports.hasTrailingScope = (name) => REGEXP_TRAILING_SCOPE.test(name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a symbol's basename, which is the first part of its full name before any punctuation (other
|
* Get a symbol's basename, which is the first part of its full name before any punctuation (other
|
||||||
@ -128,94 +128,92 @@ exports.hasTrailingScope = name => REGEXP_TRAILING_SCOPE.test(name);
|
|||||||
* @param {?string} [name] - The symbol's full name.
|
* @param {?string} [name] - The symbol's full name.
|
||||||
* @returns {?string} The symbol's basename.
|
* @returns {?string} The symbol's basename.
|
||||||
*/
|
*/
|
||||||
exports.getBasename = name => {
|
exports.getBasename = (name) => {
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return name.replace(/^([$a-z_][$a-z_0-9]*).*?$/i, '$1');
|
return name.replace(/^([$a-z_][$a-z_0-9]*).*?$/i, '$1');
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: docs
|
// TODO: docs
|
||||||
exports.stripNamespace = longname => longname.replace(/^[a-zA-Z]+:/, '');
|
exports.stripNamespace = (longname) => longname.replace(/^[a-zA-Z]+:/, '');
|
||||||
|
|
||||||
// TODO: docs
|
// TODO: docs
|
||||||
function slice(longname, sliceChars, forcedMemberof) {
|
function slice(longname, sliceChars, forcedMemberof) {
|
||||||
let i;
|
let i;
|
||||||
let memberof = '';
|
let memberof = '';
|
||||||
let name = '';
|
let name = '';
|
||||||
let parts;
|
let parts;
|
||||||
let partsRegExp;
|
let partsRegExp;
|
||||||
let scopePunc = '';
|
let scopePunc = '';
|
||||||
let token;
|
let token;
|
||||||
const tokens = [];
|
const tokens = [];
|
||||||
let variation;
|
let variation;
|
||||||
|
|
||||||
sliceChars = sliceChars || SCOPE_PUNC;
|
sliceChars = sliceChars || SCOPE_PUNC;
|
||||||
|
|
||||||
// Quoted strings in a longname are atomic, so we convert them to tokens:
|
// Quoted strings in a longname are atomic, so we convert them to tokens:
|
||||||
// foo["bar"] => foo.@{1}@
|
// foo["bar"] => foo.@{1}@
|
||||||
// Foo.prototype["bar"] => Foo#@{1}
|
// Foo.prototype["bar"] => Foo#@{1}
|
||||||
longname = longname.replace(/(prototype|#)?(\[?["'].+?["']\]?)/g, ($, p1, p2) => {
|
longname = longname.replace(/(prototype|#)?(\[?["'].+?["']\]?)/g, ($, p1, p2) => {
|
||||||
let punc = '';
|
let punc = '';
|
||||||
|
|
||||||
// Is there a leading bracket?
|
// Is there a leading bracket?
|
||||||
if ( /^\[/.test(p2) ) {
|
if (/^\[/.test(p2)) {
|
||||||
// Is it a static or instance member?
|
// Is it a static or instance member?
|
||||||
punc = p1 ? SCOPE.PUNC.INSTANCE : SCOPE.PUNC.STATIC;
|
punc = p1 ? SCOPE.PUNC.INSTANCE : SCOPE.PUNC.STATIC;
|
||||||
p2 = p2.replace(/^\[/g, '')
|
p2 = p2.replace(/^\[/g, '').replace(/\]$/g, '');
|
||||||
.replace(/\]$/g, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
token = `@{${tokens.length}}@`;
|
|
||||||
tokens.push(p2);
|
|
||||||
|
|
||||||
return punc + token;
|
|
||||||
});
|
|
||||||
|
|
||||||
longname = prototypeToPunc(longname);
|
|
||||||
|
|
||||||
if (typeof forcedMemberof !== 'undefined') {
|
|
||||||
partsRegExp = new RegExp(`^(.*?)([${sliceChars.join()}]?)$`);
|
|
||||||
name = longname.substr(forcedMemberof.length);
|
|
||||||
parts = forcedMemberof.match(partsRegExp);
|
|
||||||
|
|
||||||
if (parts[1]) {
|
|
||||||
memberof = parts[1] || forcedMemberof;
|
|
||||||
}
|
|
||||||
if (parts[2]) {
|
|
||||||
scopePunc = parts[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (longname) {
|
|
||||||
parts = longname.match(new RegExp(`^(:?(.+)([${sliceChars.join()}]))?(.+?)$`)) || [];
|
|
||||||
name = parts.pop() || '';
|
|
||||||
scopePunc = parts.pop() || '';
|
|
||||||
memberof = parts.pop() || '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Like `@name foo.bar(2)`.
|
token = `@{${tokens.length}}@`;
|
||||||
if (/(.+)\(([^)]+)\)$/.test(name)) {
|
tokens.push(p2);
|
||||||
name = RegExp.$1;
|
|
||||||
variation = RegExp.$2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore quoted strings.
|
return punc + token;
|
||||||
i = tokens.length;
|
});
|
||||||
while (i--) {
|
|
||||||
longname = longname.replace(`@{${i}}@`, tokens[i]);
|
|
||||||
memberof = memberof.replace(`@{${i}}@`, tokens[i]);
|
|
||||||
scopePunc = scopePunc.replace(`@{${i}}@`, tokens[i]);
|
|
||||||
name = name.replace(`@{${i}}@`, tokens[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
longname = prototypeToPunc(longname);
|
||||||
longname: longname,
|
|
||||||
memberof: memberof,
|
if (typeof forcedMemberof !== 'undefined') {
|
||||||
scope: scopePunc,
|
partsRegExp = new RegExp(`^(.*?)([${sliceChars.join()}]?)$`);
|
||||||
name: name,
|
name = longname.substr(forcedMemberof.length);
|
||||||
variation: variation
|
parts = forcedMemberof.match(partsRegExp);
|
||||||
};
|
|
||||||
|
if (parts[1]) {
|
||||||
|
memberof = parts[1] || forcedMemberof;
|
||||||
|
}
|
||||||
|
if (parts[2]) {
|
||||||
|
scopePunc = parts[2];
|
||||||
|
}
|
||||||
|
} else if (longname) {
|
||||||
|
parts = longname.match(new RegExp(`^(:?(.+)([${sliceChars.join()}]))?(.+?)$`)) || [];
|
||||||
|
name = parts.pop() || '';
|
||||||
|
scopePunc = parts.pop() || '';
|
||||||
|
memberof = parts.pop() || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like `@name foo.bar(2)`.
|
||||||
|
if (/(.+)\(([^)]+)\)$/.test(name)) {
|
||||||
|
name = RegExp.$1;
|
||||||
|
variation = RegExp.$2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore quoted strings.
|
||||||
|
i = tokens.length;
|
||||||
|
while (i--) {
|
||||||
|
longname = longname.replace(`@{${i}}@`, tokens[i]);
|
||||||
|
memberof = memberof.replace(`@{${i}}@`, tokens[i]);
|
||||||
|
scopePunc = scopePunc.replace(`@{${i}}@`, tokens[i]);
|
||||||
|
name = name.replace(`@{${i}}@`, tokens[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
longname: longname,
|
||||||
|
memberof: memberof,
|
||||||
|
scope: scopePunc,
|
||||||
|
name: name,
|
||||||
|
variation: variation,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -231,9 +229,7 @@ function slice(longname, sliceChars, forcedMemberof) {
|
|||||||
* @param {string} forcedMemberof
|
* @param {string} forcedMemberof
|
||||||
* @returns {object} Representing the properties of the given name.
|
* @returns {object} Representing the properties of the given name.
|
||||||
*/
|
*/
|
||||||
exports.toParts = (longname, forcedMemberof) => slice(
|
exports.toParts = (longname, forcedMemberof) => slice(longname, null, forcedMemberof);
|
||||||
longname, null, forcedMemberof
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO: docs
|
// TODO: docs
|
||||||
/**
|
/**
|
||||||
@ -242,16 +238,16 @@ exports.toParts = (longname, forcedMemberof) => slice(
|
|||||||
* @returns {string} The longname with the namespace applied.
|
* @returns {string} The longname with the namespace applied.
|
||||||
*/
|
*/
|
||||||
exports.applyNamespace = (longname, ns) => {
|
exports.applyNamespace = (longname, ns) => {
|
||||||
const nameParts = slice(longname);
|
const nameParts = slice(longname);
|
||||||
const name = nameParts.name;
|
const name = nameParts.name;
|
||||||
|
|
||||||
longname = nameParts.longname;
|
longname = nameParts.longname;
|
||||||
|
|
||||||
if (!/^[a-zA-Z]+?:.+$/i.test(name)) {
|
if (!/^[a-zA-Z]+?:.+$/i.test(name)) {
|
||||||
longname = longname.replace(new RegExp(`${escape(name)}$`), `${ns}:${name}`);
|
longname = longname.replace(new RegExp(`${escape(name)}$`), `${ns}:${name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return longname;
|
return longname;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -262,70 +258,66 @@ exports.applyNamespace = (longname, ns) => {
|
|||||||
* @return {boolean} `true` if the parent is an ancestor of the child; otherwise, `false`.
|
* @return {boolean} `true` if the parent is an ancestor of the child; otherwise, `false`.
|
||||||
*/
|
*/
|
||||||
exports.hasAncestor = (parent, child) => {
|
exports.hasAncestor = (parent, child) => {
|
||||||
let hasAncestor = false;
|
let hasAncestor = false;
|
||||||
let memberof = child;
|
let memberof = child;
|
||||||
|
|
||||||
if (!parent || !child) {
|
|
||||||
return hasAncestor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fast path for obvious non-ancestors.
|
|
||||||
if (child.indexOf(parent) !== 0) {
|
|
||||||
return hasAncestor;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
memberof = slice(memberof).memberof;
|
|
||||||
|
|
||||||
if (memberof === parent) {
|
|
||||||
hasAncestor = true;
|
|
||||||
}
|
|
||||||
} while (!hasAncestor && memberof);
|
|
||||||
|
|
||||||
|
if (!parent || !child) {
|
||||||
return hasAncestor;
|
return hasAncestor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast path for obvious non-ancestors.
|
||||||
|
if (child.indexOf(parent) !== 0) {
|
||||||
|
return hasAncestor;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
memberof = slice(memberof).memberof;
|
||||||
|
|
||||||
|
if (memberof === parent) {
|
||||||
|
hasAncestor = true;
|
||||||
|
}
|
||||||
|
} while (!hasAncestor && memberof);
|
||||||
|
|
||||||
|
return hasAncestor;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: docs
|
// TODO: docs
|
||||||
const fromParts = exports.fromParts = ({memberof, scope, name, variation}) => [
|
const fromParts = (exports.fromParts = ({ memberof, scope, name, variation }) =>
|
||||||
(memberof || ''),
|
[memberof || '', scope || '', name || '', variation ? `(${variation})` : ''].join(''));
|
||||||
(scope || ''),
|
|
||||||
(name || ''),
|
|
||||||
(variation ? `(${variation})` : '')
|
|
||||||
].join('');
|
|
||||||
|
|
||||||
// TODO: docs
|
// TODO: docs
|
||||||
exports.stripVariation = name => {
|
exports.stripVariation = (name) => {
|
||||||
const parts = slice(name);
|
const parts = slice(name);
|
||||||
|
|
||||||
parts.variation = '';
|
parts.variation = '';
|
||||||
|
|
||||||
return fromParts(parts);
|
return fromParts(parts);
|
||||||
};
|
};
|
||||||
|
|
||||||
function splitLongname(longname, options) {
|
function splitLongname(longname, options) {
|
||||||
const chunks = [];
|
const chunks = [];
|
||||||
let currentNameInfo;
|
let currentNameInfo;
|
||||||
const nameInfo = {};
|
const nameInfo = {};
|
||||||
let previousName = longname;
|
let previousName = longname;
|
||||||
const splitters = SCOPE_PUNC.concat('/');
|
const splitters = SCOPE_PUNC.concat('/');
|
||||||
|
|
||||||
options = _.defaults(options || {}, {
|
options = _.defaults(options || {}, {
|
||||||
includeVariation: true
|
includeVariation: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (!options.includeVariation) {
|
if (!options.includeVariation) {
|
||||||
previousName = exports.stripVariation(previousName);
|
previousName = exports.stripVariation(previousName);
|
||||||
}
|
}
|
||||||
currentNameInfo = nameInfo[previousName] = slice(previousName, splitters);
|
currentNameInfo = nameInfo[previousName] = slice(previousName, splitters);
|
||||||
previousName = currentNameInfo.memberof;
|
previousName = currentNameInfo.memberof;
|
||||||
chunks.push(currentNameInfo.scope + currentNameInfo.name);
|
chunks.push(currentNameInfo.scope + currentNameInfo.name);
|
||||||
} while (previousName);
|
} while (previousName);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
chunks: chunks.reverse(),
|
chunks: chunks.reverse(),
|
||||||
nameInfo: nameInfo
|
nameInfo: nameInfo,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -411,43 +403,43 @@ function splitLongname(longname, options) {
|
|||||||
* @return {Object} A tree with information about each longname in the format shown above.
|
* @return {Object} A tree with information about each longname in the format shown above.
|
||||||
*/
|
*/
|
||||||
exports.longnamesToTree = (longnames, doclets) => {
|
exports.longnamesToTree = (longnames, doclets) => {
|
||||||
const splitOptions = { includeVariation: false };
|
const splitOptions = { includeVariation: false };
|
||||||
const tree = {};
|
const tree = {};
|
||||||
|
|
||||||
longnames.forEach(longname => {
|
longnames.forEach((longname) => {
|
||||||
let currentLongname = '';
|
let currentLongname = '';
|
||||||
let currentParent = tree;
|
let currentParent = tree;
|
||||||
let nameInfo;
|
let nameInfo;
|
||||||
let processed;
|
let processed;
|
||||||
|
|
||||||
// Don't try to add empty longnames to the tree.
|
// Don't try to add empty longnames to the tree.
|
||||||
if (!longname) {
|
if (!longname) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
processed = splitLongname(longname, splitOptions);
|
processed = splitLongname(longname, splitOptions);
|
||||||
nameInfo = processed.nameInfo;
|
nameInfo = processed.nameInfo;
|
||||||
|
|
||||||
processed.chunks.forEach(chunk => {
|
processed.chunks.forEach((chunk) => {
|
||||||
currentLongname += chunk;
|
currentLongname += chunk;
|
||||||
|
|
||||||
if (currentParent !== tree) {
|
if (currentParent !== tree) {
|
||||||
currentParent.children = currentParent.children || {};
|
currentParent.children = currentParent.children || {};
|
||||||
currentParent = currentParent.children;
|
currentParent = currentParent.children;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasOwnProp.call(currentParent, chunk)) {
|
if (!hasOwnProp.call(currentParent, chunk)) {
|
||||||
currentParent[chunk] = nameInfo[currentLongname];
|
currentParent[chunk] = nameInfo[currentLongname];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentParent[chunk]) {
|
if (currentParent[chunk]) {
|
||||||
currentParent[chunk].doclet = doclets ? doclets[currentLongname] : null;
|
currentParent[chunk].doclet = doclets ? doclets[currentLongname] : null;
|
||||||
currentParent = currentParent[chunk];
|
currentParent = currentParent[chunk];
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -459,69 +451,68 @@ exports.longnamesToTree = (longnames, doclets) => {
|
|||||||
* @returns {?Object} Hash with "name" and "description" properties.
|
* @returns {?Object} Hash with "name" and "description" properties.
|
||||||
*/
|
*/
|
||||||
function splitNameMatchingBrackets(nameDesc) {
|
function splitNameMatchingBrackets(nameDesc) {
|
||||||
const buffer = [];
|
const buffer = [];
|
||||||
let c;
|
let c;
|
||||||
let stack = 0;
|
let stack = 0;
|
||||||
let stringEnd = null;
|
let stringEnd = null;
|
||||||
|
|
||||||
for (var i = 0; i < nameDesc.length; ++i) {
|
for (var i = 0; i < nameDesc.length; ++i) {
|
||||||
c = nameDesc[i];
|
c = nameDesc[i];
|
||||||
buffer.push(c);
|
buffer.push(c);
|
||||||
|
|
||||||
if (stringEnd) {
|
if (stringEnd) {
|
||||||
if (c === '\\' && i + 1 < nameDesc.length) {
|
if (c === '\\' && i + 1 < nameDesc.length) {
|
||||||
buffer.push(nameDesc[++i]);
|
buffer.push(nameDesc[++i]);
|
||||||
} else if (c === stringEnd) {
|
} else if (c === stringEnd) {
|
||||||
stringEnd = null;
|
stringEnd = null;
|
||||||
}
|
}
|
||||||
} else if (c === '"' || c === "'") {
|
} else if (c === '"' || c === "'") {
|
||||||
stringEnd = c;
|
stringEnd = c;
|
||||||
} else if (c === '[') {
|
} else if (c === '[') {
|
||||||
++stack;
|
++stack;
|
||||||
} else if (c === ']') {
|
} else if (c === ']') {
|
||||||
if (--stack === 0) {
|
if (--stack === 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (stack || stringEnd) {
|
if (stack || stringEnd) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
nameDesc.substr(i).match(REGEXP_DESCRIPTION);
|
nameDesc.substr(i).match(REGEXP_DESCRIPTION);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: buffer.join(''),
|
name: buffer.join(''),
|
||||||
description: RegExp.$1
|
description: RegExp.$1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split a string that starts with a name and ends with a description into separate parts.
|
* Split a string that starts with a name and ends with a description into separate parts.
|
||||||
* @param {string} str - The string that contains the name and description.
|
* @param {string} str - The string that contains the name and description.
|
||||||
* @returns {object} An object with `name` and `description` properties.
|
* @returns {object} An object with `name` and `description` properties.
|
||||||
*/
|
*/
|
||||||
exports.splitNameAndDescription = str => {
|
exports.splitNameAndDescription = (str) => {
|
||||||
// Like: `name`, `[name]`, `name text`, `[name] text`, `name - text`, or `[name] - text`.
|
// Like: `name`, `[name]`, `name text`, `[name] text`, `name - text`, or `[name] - text`.
|
||||||
// To ensure that we don't get confused by leading dashes in Markdown list items, the hyphen
|
// To ensure that we don't get confused by leading dashes in Markdown list items, the hyphen
|
||||||
// must be on the same line as the name.
|
// must be on the same line as the name.
|
||||||
|
|
||||||
// Optional values get special treatment,
|
// Optional values get special treatment,
|
||||||
let result = null;
|
let result = null;
|
||||||
|
|
||||||
if (str[0] === '[') {
|
if (str[0] === '[') {
|
||||||
result = splitNameMatchingBrackets(str);
|
result = splitNameMatchingBrackets(str);
|
||||||
if (result !== null) {
|
if (result !== null) {
|
||||||
return result;
|
return result;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
str.match(REGEXP_NAME_DESCRIPTION);
|
str.match(REGEXP_NAME_DESCRIPTION);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: RegExp.$1,
|
name: RegExp.$1,
|
||||||
description: RegExp.$2
|
description: RegExp.$2,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
273
packages/jsdoc-core/package-lock.json
generated
273
packages/jsdoc-core/package-lock.json
generated
@ -1,8 +1,279 @@
|
|||||||
{
|
{
|
||||||
"name": "@jsdoc/core",
|
"name": "@jsdoc/core",
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "@jsdoc/core",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"cosmiconfig": "^7.0.1",
|
||||||
|
"escape-string-regexp": "^4.0.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"strip-bom": "^4.0.0",
|
||||||
|
"strip-json-comments": "^3.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=v14.17.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame": {
|
||||||
|
"version": "7.14.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz",
|
||||||
|
"integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/highlight": "^7.14.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-validator-identifier": {
|
||||||
|
"version": "7.14.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz",
|
||||||
|
"integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight": {
|
||||||
|
"version": "7.14.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz",
|
||||||
|
"integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-validator-identifier": "^7.14.5",
|
||||||
|
"chalk": "^2.0.0",
|
||||||
|
"js-tokens": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/parse-json": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
|
||||||
|
},
|
||||||
|
"node_modules/ansi-styles": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^1.9.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/callsites": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chalk": {
|
||||||
|
"version": "2.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||||
|
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^3.2.1",
|
||||||
|
"escape-string-regexp": "^1.0.5",
|
||||||
|
"supports-color": "^5.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chalk/node_modules/escape-string-regexp": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
|
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||||
|
},
|
||||||
|
"node_modules/cosmiconfig": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/parse-json": "^4.0.0",
|
||||||
|
"import-fresh": "^3.2.1",
|
||||||
|
"parse-json": "^5.0.0",
|
||||||
|
"path-type": "^4.0.0",
|
||||||
|
"yaml": "^1.10.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/error-ex": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-arrayish": "^0.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/escape-string-regexp": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-flag": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/import-fresh": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
|
||||||
|
"dependencies": {
|
||||||
|
"parent-module": "^1.0.0",
|
||||||
|
"resolve-from": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-arrayish": {
|
||||||
|
"version": "0.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||||
|
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
|
||||||
|
},
|
||||||
|
"node_modules/js-tokens": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||||
|
},
|
||||||
|
"node_modules/json-parse-even-better-errors": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
|
||||||
|
},
|
||||||
|
"node_modules/lines-and-columns": {
|
||||||
|
"version": "1.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
|
||||||
|
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA="
|
||||||
|
},
|
||||||
|
"node_modules/lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
|
},
|
||||||
|
"node_modules/parent-module": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
||||||
|
"dependencies": {
|
||||||
|
"callsites": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/parse-json": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/code-frame": "^7.0.0",
|
||||||
|
"error-ex": "^1.3.1",
|
||||||
|
"json-parse-even-better-errors": "^2.3.0",
|
||||||
|
"lines-and-columns": "^1.1.6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-type": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/resolve-from": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-bom": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-json-comments": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/supports-color": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
|
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||||
|
"dependencies": {
|
||||||
|
"has-flag": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yaml": {
|
||||||
|
"version": "1.10.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||||
|
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": {
|
"@babel/code-frame": {
|
||||||
"version": "7.14.5",
|
"version": "7.14.5",
|
||||||
|
|||||||
@ -1,23 +1,23 @@
|
|||||||
const core = require('../../index');
|
const core = require('../../index');
|
||||||
|
|
||||||
describe('@jsdoc/core', () => {
|
describe('@jsdoc/core', () => {
|
||||||
it('is an object', () => {
|
it('is an object', () => {
|
||||||
expect(core).toBeObject();
|
expect(core).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('config', () => {
|
||||||
|
it('is lib/config', () => {
|
||||||
|
const config = require('../../lib/config');
|
||||||
|
|
||||||
|
expect(core.config).toBe(config);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('config', () => {
|
describe('name', () => {
|
||||||
it('is lib/config', () => {
|
it('is lib/name', () => {
|
||||||
const config = require('../../lib/config');
|
const name = require('../../lib/name');
|
||||||
|
|
||||||
expect(core.config).toBe(config);
|
expect(core.name).toBe(name);
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('name', () => {
|
|
||||||
it('is lib/name', () => {
|
|
||||||
const name = require('../../lib/name');
|
|
||||||
|
|
||||||
expect(core.name).toBe(name);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,189 +2,189 @@ const mockFs = require('mock-fs');
|
|||||||
const config = require('../../../lib/config');
|
const config = require('../../../lib/config');
|
||||||
|
|
||||||
describe('@jsdoc/core/lib/config', () => {
|
describe('@jsdoc/core/lib/config', () => {
|
||||||
// Explicitly require `yaml` before we run any tests. `cosmiconfig` tries to load `yaml` lazily,
|
// Explicitly require `yaml` before we run any tests. `cosmiconfig` tries to load `yaml` lazily,
|
||||||
// but that doesn't work when the file system is mocked.
|
// but that doesn't work when the file system is mocked.
|
||||||
beforeAll(() => require('yaml'));
|
beforeAll(() => require('yaml'));
|
||||||
|
|
||||||
afterEach(() => mockFs.restore());
|
afterEach(() => mockFs.restore());
|
||||||
|
|
||||||
|
it('is an object', () => {
|
||||||
|
expect(config).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('loadSync', () => {
|
||||||
|
it('is a function', () => {
|
||||||
|
expect(config.loadSync).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an object with `config` and `filepath` properties', () => {
|
||||||
|
mockFs({
|
||||||
|
'conf.json': '{}',
|
||||||
|
});
|
||||||
|
|
||||||
|
const conf = config.loadSync('conf.json');
|
||||||
|
|
||||||
|
expect(conf.config).toBeObject();
|
||||||
|
expect(conf.filepath).toEndWith('conf.json');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads settings from the specified filepath if there is one', () => {
|
||||||
|
mockFs({
|
||||||
|
'conf.json': '{"foo":"bar"}',
|
||||||
|
});
|
||||||
|
|
||||||
|
const conf = config.loadSync('conf.json');
|
||||||
|
|
||||||
|
expect(conf.config.foo).toBe('bar');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('finds the config file when no filepath is specified', () => {
|
||||||
|
mockFs({
|
||||||
|
'package.json': '{"jsdoc":{"foo":"bar"}}',
|
||||||
|
});
|
||||||
|
|
||||||
|
const conf = config.loadSync();
|
||||||
|
|
||||||
|
expect(conf.config.foo).toBe('bar');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses JSON config files that have an extension and contain comments', () => {
|
||||||
|
mockFs({
|
||||||
|
'.jsdocrc.json': '// comment\n{"foo":"bar"}',
|
||||||
|
});
|
||||||
|
|
||||||
|
const conf = config.loadSync();
|
||||||
|
|
||||||
|
expect(conf.config.foo).toBe('bar');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses JSON files that start with a BOM', () => {
|
||||||
|
mockFs({
|
||||||
|
'.jsdocrc.json': '\uFEFF{"foo":"bar"}',
|
||||||
|
});
|
||||||
|
|
||||||
|
const conf = config.loadSync();
|
||||||
|
|
||||||
|
expect(conf.config.foo).toBe('bar');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses YAML files that start with a BOM', () => {
|
||||||
|
mockFs({
|
||||||
|
'.jsdocrc.yaml': '\uFEFF{"foo":"bar"}',
|
||||||
|
});
|
||||||
|
|
||||||
|
const conf = config.loadSync();
|
||||||
|
|
||||||
|
expect(conf.config.foo).toBe('bar');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('provides the default config if the user config is an empty object', () => {
|
||||||
|
mockFs({
|
||||||
|
'.jsdocrc.json': '{}',
|
||||||
|
});
|
||||||
|
|
||||||
|
const conf = config.loadSync();
|
||||||
|
|
||||||
|
expect(conf.config).toEqual(config.defaults);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('provides the default config if there is no user config', () => {
|
||||||
|
const conf = config.loadSync();
|
||||||
|
|
||||||
|
expect(conf.config).toEqual(config.defaults);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('merges nested defaults with nested user settings as expected', () => {
|
||||||
|
mockFs({
|
||||||
|
'.jsdocrc.json': '{"tags":{"foo":"bar"}}',
|
||||||
|
});
|
||||||
|
|
||||||
|
const conf = config.loadSync();
|
||||||
|
|
||||||
|
expect(conf.config.tags.allowUnknownTags).toBe(config.defaults.tags.allowUnknownTags);
|
||||||
|
expect(conf.config.tags.foo).toBe('bar');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('defaults', () => {
|
||||||
|
const { defaults } = config;
|
||||||
|
|
||||||
it('is an object', () => {
|
it('is an object', () => {
|
||||||
expect(config).toBeObject();
|
expect(defaults).toBeObject();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('loadSync', () => {
|
describe('plugins', () => {
|
||||||
it('is a function', () => {
|
it('is an array', () => {
|
||||||
expect(config.loadSync).toBeFunction();
|
expect(defaults.plugins).toBeArray();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an object with `config` and `filepath` properties', () => {
|
|
||||||
mockFs({
|
|
||||||
'conf.json': '{}'
|
|
||||||
});
|
|
||||||
|
|
||||||
const conf = config.loadSync('conf.json');
|
|
||||||
|
|
||||||
expect(conf.config).toBeObject();
|
|
||||||
expect(conf.filepath).toEndWith('conf.json');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads settings from the specified filepath if there is one', () => {
|
|
||||||
mockFs({
|
|
||||||
'conf.json': '{"foo":"bar"}'
|
|
||||||
});
|
|
||||||
|
|
||||||
const conf = config.loadSync('conf.json');
|
|
||||||
|
|
||||||
expect(conf.config.foo).toBe('bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('finds the config file when no filepath is specified', () => {
|
|
||||||
mockFs({
|
|
||||||
'package.json': '{"jsdoc":{"foo":"bar"}}'
|
|
||||||
});
|
|
||||||
|
|
||||||
const conf = config.loadSync();
|
|
||||||
|
|
||||||
expect(conf.config.foo).toBe('bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses JSON config files that have an extension and contain comments', () => {
|
|
||||||
mockFs({
|
|
||||||
'.jsdocrc.json': '// comment\n{"foo":"bar"}'
|
|
||||||
});
|
|
||||||
|
|
||||||
const conf = config.loadSync();
|
|
||||||
|
|
||||||
expect(conf.config.foo).toBe('bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses JSON files that start with a BOM', () => {
|
|
||||||
mockFs({
|
|
||||||
'.jsdocrc.json': '\uFEFF{"foo":"bar"}'
|
|
||||||
});
|
|
||||||
|
|
||||||
const conf = config.loadSync();
|
|
||||||
|
|
||||||
expect(conf.config.foo).toBe('bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parses YAML files that start with a BOM', () => {
|
|
||||||
mockFs({
|
|
||||||
'.jsdocrc.yaml': '\uFEFF{"foo":"bar"}'
|
|
||||||
});
|
|
||||||
|
|
||||||
const conf = config.loadSync();
|
|
||||||
|
|
||||||
expect(conf.config.foo).toBe('bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('provides the default config if the user config is an empty object', () => {
|
|
||||||
mockFs({
|
|
||||||
'.jsdocrc.json': '{}'
|
|
||||||
});
|
|
||||||
|
|
||||||
const conf = config.loadSync();
|
|
||||||
|
|
||||||
expect(conf.config).toEqual(config.defaults);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('provides the default config if there is no user config', () => {
|
|
||||||
const conf = config.loadSync();
|
|
||||||
|
|
||||||
expect(conf.config).toEqual(config.defaults);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('merges nested defaults with nested user settings as expected', () => {
|
|
||||||
mockFs({
|
|
||||||
'.jsdocrc.json': '{"tags":{"foo":"bar"}}'
|
|
||||||
});
|
|
||||||
|
|
||||||
const conf = config.loadSync();
|
|
||||||
|
|
||||||
expect(conf.config.tags.allowUnknownTags).toBe(config.defaults.tags.allowUnknownTags);
|
|
||||||
expect(conf.config.tags.foo).toBe('bar');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('defaults', () => {
|
describe('source', () => {
|
||||||
const { defaults } = config;
|
it('is an object', () => {
|
||||||
|
expect(defaults.source).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('excludePattern', () => {
|
||||||
|
it('is a string', () => {
|
||||||
|
expect(defaults.source.excludePattern).toBeString();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('represents a valid regexp', () => {
|
||||||
|
expect(() => new RegExp(defaults.source.excludePattern)).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('includePattern', () => {
|
||||||
|
it('is a string', () => {
|
||||||
|
expect(defaults.source.includePattern).toBeString();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('represents a valid regexp', () => {
|
||||||
|
expect(() => new RegExp(defaults.source.includePattern)).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('type', () => {
|
||||||
|
it('is a string', () => {
|
||||||
|
expect(defaults.source.type).toBeString();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('tags', () => {
|
||||||
it('is an object', () => {
|
it('is an object', () => {
|
||||||
expect(defaults).toBeObject();
|
expect(defaults.tags).toBeObject();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('plugins', () => {
|
describe('allowUnknownTags', () => {
|
||||||
it('is an array', () => {
|
it('is a boolean', () => {
|
||||||
expect(defaults.plugins).toBeArray();
|
expect(defaults.tags.allowUnknownTags).toBeBoolean();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('source', () => {
|
describe('dictionaries', () => {
|
||||||
it('is an object', () => {
|
it('is an array of strings', () => {
|
||||||
expect(defaults.source).toBeObject();
|
expect(defaults.tags.dictionaries).toBeArrayOfStrings();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('excludePattern', () => {
|
|
||||||
it('is a string', () => {
|
|
||||||
expect(defaults.source.excludePattern).toBeString();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('represents a valid regexp', () => {
|
|
||||||
expect(() => new RegExp(defaults.source.excludePattern)).not.toThrow();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('includePattern', () => {
|
|
||||||
it('is a string', () => {
|
|
||||||
expect(defaults.source.includePattern).toBeString();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('represents a valid regexp', () => {
|
|
||||||
expect(() => new RegExp(defaults.source.includePattern)).not.toThrow();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('type', () => {
|
|
||||||
it('is a string', () => {
|
|
||||||
expect(defaults.source.type).toBeString();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('tags', () => {
|
|
||||||
it('is an object', () => {
|
|
||||||
expect(defaults.tags).toBeObject();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('allowUnknownTags', () => {
|
|
||||||
it('is a boolean', () => {
|
|
||||||
expect(defaults.tags.allowUnknownTags).toBeBoolean();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('dictionaries', () => {
|
|
||||||
it('is an array of strings', () => {
|
|
||||||
expect(defaults.tags.dictionaries).toBeArrayOfStrings();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('templates', () => {
|
|
||||||
it('is an object', () => {
|
|
||||||
expect(defaults.templates).toBeObject();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('cleverLinks', () => {
|
|
||||||
it('is a boolean', () => {
|
|
||||||
expect(defaults.templates.cleverLinks).toBeBoolean();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('monospaceLinks', () => {
|
|
||||||
it('is a boolean', () => {
|
|
||||||
expect(defaults.templates.monospaceLinks).toBeBoolean();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('templates', () => {
|
||||||
|
it('is an object', () => {
|
||||||
|
expect(defaults.templates).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('cleverLinks', () => {
|
||||||
|
it('is a boolean', () => {
|
||||||
|
expect(defaults.templates.cleverLinks).toBeBoolean();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('monospaceLinks', () => {
|
||||||
|
it('is a boolean', () => {
|
||||||
|
expect(defaults.templates.monospaceLinks).toBeBoolean();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,439 +1,434 @@
|
|||||||
describe('@jsdoc/core.name', () => {
|
describe('@jsdoc/core.name', () => {
|
||||||
const { name } = require('@jsdoc/core');
|
const { name } = require('@jsdoc/core');
|
||||||
|
|
||||||
it('exists', () => {
|
it('exists', () => {
|
||||||
expect(name).toBeObject();
|
expect(name).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has an applyNamespace method', () => {
|
||||||
|
expect(name.applyNamespace).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a fromParts method', () => {
|
||||||
|
expect(name.fromParts).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a getBasename method', () => {
|
||||||
|
expect(name.getBasename).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a hasAncestor method', () => {
|
||||||
|
expect(name.hasAncestor).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a hasLeadingScope method', () => {
|
||||||
|
expect(name.hasLeadingScope).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a hasTrailingScope method', () => {
|
||||||
|
expect(name.hasTrailingScope).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a LONGNAMES enum', () => {
|
||||||
|
expect(name.LONGNAMES).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a longnamesToTree method', () => {
|
||||||
|
expect(name.longnamesToTree).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a MODULE_NAMESPACE property', () => {
|
||||||
|
expect(name.MODULE_NAMESPACE).toBeString();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a nameIsLongname method', () => {
|
||||||
|
expect(name.nameIsLongname).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a prototypeToPunc method', () => {
|
||||||
|
expect(name.prototypeToPunc).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a PUNC_TO_SCOPE enum', () => {
|
||||||
|
expect(name.PUNC_TO_SCOPE).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a SCOPE enum', () => {
|
||||||
|
expect(name.SCOPE).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a SCOPE_TO_PUNC enum', () => {
|
||||||
|
expect(name.SCOPE_TO_PUNC).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a splitNameAndDescription method', () => {
|
||||||
|
expect(name.splitNameAndDescription).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a stripNamespace method', () => {
|
||||||
|
expect(name.stripNamespace).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a stripVariation method', () => {
|
||||||
|
expect(name.stripVariation).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a toParts method', () => {
|
||||||
|
expect(name.toParts).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('applyNamespace', () => {
|
||||||
|
it('applies the namespace to the name part of the longname', () => {
|
||||||
|
expect(name.applyNamespace('lib.Panel#open', 'event')).toBe('lib.Panel#event:open');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has an applyNamespace method', () => {
|
it('applies the namespace to the start of a top-level longname', () => {
|
||||||
expect(name.applyNamespace).toBeFunction();
|
expect(name.applyNamespace('math/bigint', 'module')).toBe('module:math/bigint');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a fromParts method', () => {
|
// TODO(hegemonic): This has never worked
|
||||||
expect(name.fromParts).toBeFunction();
|
xit('handles longnames with quoted portions', () => {
|
||||||
|
expect(name.applyNamespace('foo."*don\'t.look~in#here!"', 'event')).toBe(
|
||||||
|
'foo.event:"*don\'t.look~in#here!"'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a getBasename method', () => {
|
it('handles longnames that already have namespaces', () => {
|
||||||
expect(name.getBasename).toBeFunction();
|
expect(name.applyNamespace('lib.Panel#event:open', 'event')).toBe('lib.Panel#event:open');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
xdescribe('fromParts', () => {
|
||||||
|
// TODO: tests
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getBasename', () => {
|
||||||
|
it('returns null on empty input', () => {
|
||||||
|
expect(name.getBasename()).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a hasAncestor method', () => {
|
it('returns the original value if it has no punctuation except underscores', () => {
|
||||||
expect(name.hasAncestor).toBeFunction();
|
expect(name.getBasename('foo_bar')).toBe('foo_bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a hasLeadingScope method', () => {
|
it('returns the basename if the original value has punctuation', () => {
|
||||||
expect(name.hasLeadingScope).toBeFunction();
|
expect(name.getBasename('foo.Bar#baz')).toBe('foo');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hasAncestor', () => {
|
||||||
|
it('returns false if no parent is specified', () => {
|
||||||
|
expect(name.hasAncestor(null, 'foo')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a hasTrailingScope method', () => {
|
it('returns false if no child is specified', () => {
|
||||||
expect(name.hasTrailingScope).toBeFunction();
|
expect(name.hasAncestor('foo')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a LONGNAMES enum', () => {
|
it('returns true when the ancestor is the immediate parent', () => {
|
||||||
expect(name.LONGNAMES).toBeObject();
|
expect(name.hasAncestor('module:foo', 'module:foo~bar')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a longnamesToTree method', () => {
|
it('returns true when the ancestor is not the immediate parent', () => {
|
||||||
expect(name.longnamesToTree).toBeFunction();
|
expect(name.hasAncestor('module:foo', 'module:foo~bar.Baz#qux')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a MODULE_NAMESPACE property', () => {
|
it('returns false when a non-ancestor is passed in', () => {
|
||||||
expect(name.MODULE_NAMESPACE).toBeString();
|
expect(name.hasAncestor('module:foo', 'foo')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a nameIsLongname method', () => {
|
it('returns false if the parent and child are the same', () => {
|
||||||
expect(name.nameIsLongname).toBeFunction();
|
expect(name.hasAncestor('module:foo', 'module:foo')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hasLeadingScope', () => {
|
||||||
|
it('returns true if the string starts with a scope character', () => {
|
||||||
|
expect(name.hasLeadingScope('#foo')).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a prototypeToPunc method', () => {
|
it('returns false if the string does not start with a scope character', () => {
|
||||||
expect(name.prototypeToPunc).toBeFunction();
|
expect(name.hasLeadingScope('!foo')).toBeFalse();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hasTrailingScope', () => {
|
||||||
|
it('returns true if the string ends with a scope character', () => {
|
||||||
|
expect(name.hasTrailingScope('Foo#')).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a PUNC_TO_SCOPE enum', () => {
|
it('returns false if the string does not end with a scope character', () => {
|
||||||
expect(name.PUNC_TO_SCOPE).toBeObject();
|
expect(name.hasTrailingScope('Foo!')).toBeFalse();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('LONGNAMES', () => {
|
||||||
|
it('has an ANONYMOUS property', () => {
|
||||||
|
expect(name.LONGNAMES.ANONYMOUS).toBeString();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a SCOPE enum', () => {
|
it('has a GLOBAL property', () => {
|
||||||
expect(name.SCOPE).toBeObject();
|
expect(name.LONGNAMES.GLOBAL).toBeString();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
xdescribe('longnamesToTree', () => {
|
||||||
|
// TODO: tests
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('MODULE_NAMESPACE', () => {
|
||||||
|
// This is just a string, so nothing to test.
|
||||||
|
});
|
||||||
|
|
||||||
|
xdescribe('nameIsLongname', () => {
|
||||||
|
// TODO(hegemonic)
|
||||||
|
});
|
||||||
|
|
||||||
|
xdescribe('prototypeToPunc', () => {
|
||||||
|
// TODO(hegemonic)
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('PUNC_TO_SCOPE', () => {
|
||||||
|
it('has the same number of properties as SCOPE_TO_PUNC', () => {
|
||||||
|
expect(Object.keys(name.PUNC_TO_SCOPE).length).toBe(Object.keys(name.SCOPE_TO_PUNC).length);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('SCOPE', () => {
|
||||||
|
const SCOPE = name.SCOPE;
|
||||||
|
|
||||||
|
it('has a NAMES enum', () => {
|
||||||
|
expect(SCOPE.NAMES).toBeObject();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a SCOPE_TO_PUNC enum', () => {
|
it('has a PUNC enum', () => {
|
||||||
expect(name.SCOPE_TO_PUNC).toBeObject();
|
expect(SCOPE.PUNC).toBeObject();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a splitNameAndDescription method', () => {
|
describe('NAMES', () => {
|
||||||
expect(name.splitNameAndDescription).toBeFunction();
|
it('has a GLOBAL property', () => {
|
||||||
|
expect(SCOPE.NAMES.GLOBAL).toBeString();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has an INNER property', () => {
|
||||||
|
expect(SCOPE.NAMES.INNER).toBeString();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has an INSTANCE property', () => {
|
||||||
|
expect(SCOPE.NAMES.INSTANCE).toBeString();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a STATIC property', () => {
|
||||||
|
expect(SCOPE.NAMES.STATIC).toBeString();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a stripNamespace method', () => {
|
describe('PUNC', () => {
|
||||||
expect(name.stripNamespace).toBeFunction();
|
it('has an INNER property', () => {
|
||||||
|
expect(SCOPE.PUNC.INNER).toBeString();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has an INSTANCE property', () => {
|
||||||
|
expect(SCOPE.PUNC.INSTANCE).toBeString();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a STATIC property', () => {
|
||||||
|
expect(SCOPE.PUNC.STATIC).toBeString();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('SCOPE_TO_PUNC', () => {
|
||||||
|
it('has an inner property', () => {
|
||||||
|
expect(name.SCOPE_TO_PUNC.inner).toBeString();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a stripVariation method', () => {
|
it('has an instance property', () => {
|
||||||
expect(name.stripVariation).toBeFunction();
|
expect(name.SCOPE_TO_PUNC.instance).toBeString();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a toParts method', () => {
|
it('has a static property', () => {
|
||||||
expect(name.toParts).toBeFunction();
|
expect(name.SCOPE_TO_PUNC.static).toBeString();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('splitNameAndDescription', () => {
|
||||||
|
// TODO(hegemonic): This has never worked
|
||||||
|
xit('separates the name from the description', () => {
|
||||||
|
const parts = name.splitNameAndDescription(
|
||||||
|
'ns.Page#"last \\"sentence\\"".words~sort(2) - This is a description. '
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(parts.name).toBe('ns.Page#"last \\"sentence\\"".words~sort(2)');
|
||||||
|
expect(parts.description).toBe('This is a description.');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('applyNamespace', () => {
|
it('strips a separator when it starts on the same line as the name', () => {
|
||||||
it('applies the namespace to the name part of the longname', () => {
|
const parts = name.splitNameAndDescription('socket - The networking kind, not the wrench.');
|
||||||
expect(name.applyNamespace('lib.Panel#open', 'event')).toBe('lib.Panel#event:open');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('applies the namespace to the start of a top-level longname', () => {
|
expect(parts.name).toBe('socket');
|
||||||
expect(name.applyNamespace('math/bigint', 'module')).toBe('module:math/bigint');
|
expect(parts.description).toBe('The networking kind, not the wrench.');
|
||||||
});
|
|
||||||
|
|
||||||
// TODO(hegemonic): This has never worked
|
|
||||||
xit('handles longnames with quoted portions', () => {
|
|
||||||
expect(name.applyNamespace('foo."*don\'t.look~in#here!"', 'event'))
|
|
||||||
.toBe('foo.event:"*don\'t.look~in#here!"');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles longnames that already have namespaces', () => {
|
|
||||||
expect(name.applyNamespace('lib.Panel#event:open', 'event'))
|
|
||||||
.toBe('lib.Panel#event:open');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
xdescribe('fromParts', () => {
|
it('does not strip a separator that is preceded by a line break', () => {
|
||||||
// TODO: tests
|
const parts = name.splitNameAndDescription('socket\n - The networking kind, not the wrench.');
|
||||||
|
|
||||||
|
expect(parts.name).toBe('socket');
|
||||||
|
expect(parts.description).toBe('- The networking kind, not the wrench.');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getBasename', () => {
|
it('allows default values to contain square brackets', () => {
|
||||||
it('returns null on empty input', () => {
|
const parts = name.splitNameAndDescription(
|
||||||
expect(name.getBasename()).toBeNull();
|
'[path=["home", "user"]] - Path split into components'
|
||||||
});
|
);
|
||||||
|
|
||||||
it('returns the original value if it has no punctuation except underscores', () => {
|
expect(parts.name).toBe('[path=["home", "user"]]');
|
||||||
expect(name.getBasename('foo_bar')).toBe('foo_bar');
|
expect(parts.description).toBe('Path split into components');
|
||||||
});
|
|
||||||
|
|
||||||
it('returns the basename if the original value has punctuation', () => {
|
|
||||||
expect(name.getBasename('foo.Bar#baz')).toBe('foo');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hasAncestor', () => {
|
it('allows default values to contain unmatched square brackets inside strings', () => {
|
||||||
it('returns false if no parent is specified', () => {
|
const parts = name.splitNameAndDescription(
|
||||||
expect(name.hasAncestor(null, 'foo')).toBe(false);
|
'[path=["Unmatched begin: ["]] - Path split into components'
|
||||||
});
|
);
|
||||||
|
|
||||||
it('returns false if no child is specified', () => {
|
expect(parts.name).toBe('[path=["Unmatched begin: ["]]');
|
||||||
expect(name.hasAncestor('foo')).toBe(false);
|
expect(parts.description).toBe('Path split into components');
|
||||||
});
|
|
||||||
|
|
||||||
it('returns true when the ancestor is the immediate parent', () => {
|
|
||||||
expect(name.hasAncestor('module:foo', 'module:foo~bar')).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns true when the ancestor is not the immediate parent', () => {
|
|
||||||
expect(name.hasAncestor('module:foo', 'module:foo~bar.Baz#qux')).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns false when a non-ancestor is passed in', () => {
|
|
||||||
expect(name.hasAncestor('module:foo', 'foo')).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns false if the parent and child are the same', () => {
|
|
||||||
expect(name.hasAncestor('module:foo', 'module:foo')).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hasLeadingScope', () => {
|
it('fails gracefully when the default value has an unmatched square bracket', () => {
|
||||||
it('returns true if the string starts with a scope character', () => {
|
const parts = name.splitNameAndDescription(
|
||||||
expect(name.hasLeadingScope('#foo')).toBeTrue();
|
'[path=["home", "user"] - Path split into components'
|
||||||
});
|
);
|
||||||
|
|
||||||
it('returns false if the string does not start with a scope character', () => {
|
expect(parts).toBeObject();
|
||||||
expect(name.hasLeadingScope('!foo')).toBeFalse();
|
expect(parts.name).toBe('[path=["home", "user"]');
|
||||||
});
|
expect(parts.description).toBe('Path split into components');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hasTrailingScope', () => {
|
it('fails gracefully when the default value has an unmatched quote', () => {
|
||||||
it('returns true if the string ends with a scope character', () => {
|
const parts = name.splitNameAndDescription(
|
||||||
expect(name.hasTrailingScope('Foo#')).toBeTrue();
|
'[path=["home", "user] - Path split into components'
|
||||||
});
|
);
|
||||||
|
|
||||||
it('returns false if the string does not end with a scope character', () => {
|
expect(parts).toBeObject();
|
||||||
expect(name.hasTrailingScope('Foo!')).toBeFalse();
|
expect(parts.name).toBe('[path=["home", "user]');
|
||||||
});
|
expect(parts.description).toBe('Path split into components');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('stripNamespace', () => {
|
||||||
|
it('removes the namespace from the longname', () => {
|
||||||
|
expect(name.stripNamespace('module:foo/bar/baz')).toBe('foo/bar/baz');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('LONGNAMES', () => {
|
it('does not remove the namespace from a child member', () => {
|
||||||
it('has an ANONYMOUS property', () => {
|
expect(name.stripNamespace('foo/bar.baz~event:qux')).toBe('foo/bar.baz~event:qux');
|
||||||
expect(name.LONGNAMES.ANONYMOUS).toBeString();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a GLOBAL property', () => {
|
|
||||||
expect(name.LONGNAMES.GLOBAL).toBeString();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
xdescribe('longnamesToTree', () => {
|
it('does not change longnames that do not have a namespace', () => {
|
||||||
// TODO: tests
|
expect(name.stripNamespace('Foo#bar')).toBe('Foo#bar');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('stripVariation', () => {
|
||||||
|
it('does not change longnames with no variation', () => {
|
||||||
|
expect(name.stripVariation('Foo#bar')).toBe('Foo#bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('MODULE_NAMESPACE', () => {
|
it('removes the variation if present', () => {
|
||||||
// This is just a string, so nothing to test.
|
expect(name.stripVariation('Foo#bar(qux)')).toBe('Foo#bar');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('toParts', () => {
|
||||||
|
it('breaks up a longname into the correct parts', () => {
|
||||||
|
const parts = name.toParts('lib.Panel#open');
|
||||||
|
|
||||||
|
expect(parts.name).toBe('open');
|
||||||
|
expect(parts.memberof).toBe('lib.Panel');
|
||||||
|
expect(parts.scope).toBe('#');
|
||||||
});
|
});
|
||||||
|
|
||||||
xdescribe('nameIsLongname', () => {
|
it('handles static names', () => {
|
||||||
// TODO(hegemonic)
|
const parts = name.toParts('elements.selected.getVisible');
|
||||||
|
|
||||||
|
expect(parts.name).toBe('getVisible');
|
||||||
|
expect(parts.memberof).toBe('elements.selected');
|
||||||
|
expect(parts.scope).toBe('.');
|
||||||
});
|
});
|
||||||
|
|
||||||
xdescribe('prototypeToPunc', () => {
|
it('handles members of a prototype', () => {
|
||||||
// TODO(hegemonic)
|
const parts = name.toParts('Validator.prototype.$element');
|
||||||
|
|
||||||
|
expect(parts.name).toEqual('$element');
|
||||||
|
expect(parts.memberof).toEqual('Validator');
|
||||||
|
expect(parts.scope).toEqual('#');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PUNC_TO_SCOPE', () => {
|
it('handles inner names', () => {
|
||||||
it('has the same number of properties as SCOPE_TO_PUNC', () => {
|
const parts = name.toParts('Button~_onclick');
|
||||||
expect(Object.keys(name.PUNC_TO_SCOPE).length)
|
|
||||||
.toBe(Object.keys(name.SCOPE_TO_PUNC).length);
|
expect(parts.name).toEqual('_onclick');
|
||||||
});
|
expect(parts.memberof).toEqual('Button');
|
||||||
|
expect(parts.scope).toEqual('~');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('SCOPE', () => {
|
it('handles global names', () => {
|
||||||
const SCOPE = name.SCOPE;
|
const parts = name.toParts('close');
|
||||||
|
|
||||||
it('has a NAMES enum', () => {
|
expect(parts.name).toEqual('close');
|
||||||
expect(SCOPE.NAMES).toBeObject();
|
expect(parts.memberof).toEqual('');
|
||||||
});
|
expect(parts.scope).toEqual('');
|
||||||
|
|
||||||
it('has a PUNC enum', () => {
|
|
||||||
expect(SCOPE.PUNC).toBeObject();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('NAMES', () => {
|
|
||||||
it('has a GLOBAL property', () => {
|
|
||||||
expect(SCOPE.NAMES.GLOBAL).toBeString();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has an INNER property', () => {
|
|
||||||
expect(SCOPE.NAMES.INNER).toBeString();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has an INSTANCE property', () => {
|
|
||||||
expect(SCOPE.NAMES.INSTANCE).toBeString();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a STATIC property', () => {
|
|
||||||
expect(SCOPE.NAMES.STATIC).toBeString();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('PUNC', () => {
|
|
||||||
it('has an INNER property', () => {
|
|
||||||
expect(SCOPE.PUNC.INNER).toBeString();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has an INSTANCE property', () => {
|
|
||||||
expect(SCOPE.PUNC.INSTANCE).toBeString();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a STATIC property', () => {
|
|
||||||
expect(SCOPE.PUNC.STATIC).toBeString();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('SCOPE_TO_PUNC', () => {
|
it('handles a single property that uses bracket notation', () => {
|
||||||
it('has an inner property', () => {
|
const parts = name.toParts('channels["#ops"]#open');
|
||||||
expect(name.SCOPE_TO_PUNC.inner).toBeString();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has an instance property', () => {
|
expect(parts.name).toEqual('open');
|
||||||
expect(name.SCOPE_TO_PUNC.instance).toBeString();
|
expect(parts.memberof).toEqual('channels."#ops"');
|
||||||
});
|
expect(parts.scope).toEqual('#');
|
||||||
|
|
||||||
it('has a static property', () => {
|
|
||||||
expect(name.SCOPE_TO_PUNC.static).toBeString();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('splitNameAndDescription', () => {
|
it('handles consecutive properties that use bracket notation', () => {
|
||||||
// TODO(hegemonic): This has never worked
|
const parts = name.toParts('channels["#bots"]["log.max"]');
|
||||||
xit('separates the name from the description', () => {
|
|
||||||
const parts = name.splitNameAndDescription(
|
|
||||||
'ns.Page#"last \\"sentence\\"".words~sort(2) - This is a description. '
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(parts.name).toBe('ns.Page#"last \\"sentence\\"".words~sort(2)');
|
expect(parts.name).toEqual('"log.max"');
|
||||||
expect(parts.description).toBe('This is a description.');
|
expect(parts.memberof).toEqual('channels."#bots"');
|
||||||
});
|
expect(parts.scope).toEqual('.');
|
||||||
|
|
||||||
it('strips a separator when it starts on the same line as the name', () => {
|
|
||||||
const parts = name.splitNameAndDescription(
|
|
||||||
'socket - The networking kind, not the wrench.'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(parts.name).toBe('socket');
|
|
||||||
expect(parts.description).toBe('The networking kind, not the wrench.');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not strip a separator that is preceded by a line break', () => {
|
|
||||||
const parts = name.splitNameAndDescription(
|
|
||||||
'socket\n - The networking kind, not the wrench.'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(parts.name).toBe('socket');
|
|
||||||
expect(parts.description).toBe('- The networking kind, not the wrench.');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('allows default values to contain square brackets', () => {
|
|
||||||
const parts = name.splitNameAndDescription(
|
|
||||||
'[path=["home", "user"]] - Path split into components'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(parts.name).toBe('[path=["home", "user"]]');
|
|
||||||
expect(parts.description).toBe('Path split into components');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('allows default values to contain unmatched square brackets inside strings', () => {
|
|
||||||
const parts = name.splitNameAndDescription(
|
|
||||||
'[path=["Unmatched begin: ["]] - Path split into components'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(parts.name).toBe('[path=["Unmatched begin: ["]]');
|
|
||||||
expect(parts.description).toBe('Path split into components');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails gracefully when the default value has an unmatched square bracket', () => {
|
|
||||||
const parts = name.splitNameAndDescription(
|
|
||||||
'[path=["home", "user"] - Path split into components'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(parts).toBeObject();
|
|
||||||
expect(parts.name).toBe('[path=["home", "user"]');
|
|
||||||
expect(parts.description).toBe('Path split into components');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails gracefully when the default value has an unmatched quote', () => {
|
|
||||||
const parts = name.splitNameAndDescription(
|
|
||||||
'[path=["home", "user] - Path split into components'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(parts).toBeObject();
|
|
||||||
expect(parts.name).toBe('[path=["home", "user]');
|
|
||||||
expect(parts.description).toBe('Path split into components');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('stripNamespace', () => {
|
it('handles a property that uses single-quoted bracket notation', () => {
|
||||||
it('removes the namespace from the longname', () => {
|
const parts = name.toParts("channels['#ops']");
|
||||||
expect(name.stripNamespace('module:foo/bar/baz')).toBe('foo/bar/baz');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not remove the namespace from a child member', () => {
|
expect(parts.name).toBe("'#ops'");
|
||||||
expect(name.stripNamespace('foo/bar.baz~event:qux')).toBe('foo/bar.baz~event:qux');
|
expect(parts.memberof).toBe('channels');
|
||||||
});
|
expect(parts.scope).toBe('.');
|
||||||
|
|
||||||
it('does not change longnames that do not have a namespace', () => {
|
|
||||||
expect(name.stripNamespace('Foo#bar')).toBe('Foo#bar');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('stripVariation', () => {
|
it('handles double-quoted strings', () => {
|
||||||
it('does not change longnames with no variation', () => {
|
const parts = name.toParts('"foo.bar"');
|
||||||
expect(name.stripVariation('Foo#bar')).toBe('Foo#bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('removes the variation if present', () => {
|
expect(parts.name).toEqual('"foo.bar"');
|
||||||
expect(name.stripVariation('Foo#bar(qux)')).toBe('Foo#bar');
|
expect(parts.longname).toEqual('"foo.bar"');
|
||||||
});
|
expect(parts.memberof).toEqual('');
|
||||||
|
expect(parts.scope).toEqual('');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('toParts', () => {
|
it('handles single-quoted strings', () => {
|
||||||
it('breaks up a longname into the correct parts', () => {
|
const parts = name.toParts("'foo.bar'");
|
||||||
const parts = name.toParts('lib.Panel#open');
|
|
||||||
|
|
||||||
expect(parts.name).toBe('open');
|
expect(parts.name).toBe("'foo.bar'");
|
||||||
expect(parts.memberof).toBe('lib.Panel');
|
expect(parts.longname).toBe("'foo.bar'");
|
||||||
expect(parts.scope).toBe('#');
|
expect(parts.memberof).toBe('');
|
||||||
});
|
expect(parts.scope).toBe('');
|
||||||
|
|
||||||
it('handles static names', () => {
|
|
||||||
const parts = name.toParts('elements.selected.getVisible');
|
|
||||||
|
|
||||||
expect(parts.name).toBe('getVisible');
|
|
||||||
expect(parts.memberof).toBe('elements.selected');
|
|
||||||
expect(parts.scope).toBe('.');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles members of a prototype', () => {
|
|
||||||
const parts = name.toParts('Validator.prototype.$element');
|
|
||||||
|
|
||||||
expect(parts.name).toEqual('$element');
|
|
||||||
expect(parts.memberof).toEqual('Validator');
|
|
||||||
expect(parts.scope).toEqual('#');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles inner names', () => {
|
|
||||||
const parts = name.toParts('Button~_onclick');
|
|
||||||
|
|
||||||
expect(parts.name).toEqual('_onclick');
|
|
||||||
expect(parts.memberof).toEqual('Button');
|
|
||||||
expect(parts.scope).toEqual('~');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles global names', () => {
|
|
||||||
const parts = name.toParts('close');
|
|
||||||
|
|
||||||
expect(parts.name).toEqual('close');
|
|
||||||
expect(parts.memberof).toEqual('');
|
|
||||||
expect(parts.scope).toEqual('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles a single property that uses bracket notation', () => {
|
|
||||||
const parts = name.toParts('channels["#ops"]#open');
|
|
||||||
|
|
||||||
expect(parts.name).toEqual('open');
|
|
||||||
expect(parts.memberof).toEqual('channels."#ops"');
|
|
||||||
expect(parts.scope).toEqual('#');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles consecutive properties that use bracket notation', () => {
|
|
||||||
const parts = name.toParts('channels["#bots"]["log.max"]');
|
|
||||||
|
|
||||||
expect(parts.name).toEqual('"log.max"');
|
|
||||||
expect(parts.memberof).toEqual('channels."#bots"');
|
|
||||||
expect(parts.scope).toEqual('.');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles a property that uses single-quoted bracket notation', () => {
|
|
||||||
const parts = name.toParts("channels['#ops']");
|
|
||||||
|
|
||||||
expect(parts.name).toBe("'#ops'");
|
|
||||||
expect(parts.memberof).toBe('channels');
|
|
||||||
expect(parts.scope).toBe('.');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles double-quoted strings', () => {
|
|
||||||
const parts = name.toParts('"foo.bar"');
|
|
||||||
|
|
||||||
expect(parts.name).toEqual('"foo.bar"');
|
|
||||||
expect(parts.longname).toEqual('"foo.bar"');
|
|
||||||
expect(parts.memberof).toEqual('');
|
|
||||||
expect(parts.scope).toEqual('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles single-quoted strings', () => {
|
|
||||||
const parts = name.toParts("'foo.bar'");
|
|
||||||
|
|
||||||
expect(parts.name).toBe("'foo.bar'");
|
|
||||||
expect(parts.longname).toBe("'foo.bar'");
|
|
||||||
expect(parts.memberof).toBe('');
|
|
||||||
expect(parts.scope).toBe('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles variations', () => {
|
|
||||||
const parts = name.toParts('anim.fadein(2)');
|
|
||||||
|
|
||||||
expect(parts.variation).toEqual('2');
|
|
||||||
expect(parts.name).toEqual('fadein');
|
|
||||||
expect(parts.longname).toEqual('anim.fadein(2)');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handles variations', () => {
|
||||||
|
const parts = name.toParts('anim.fadein(2)');
|
||||||
|
|
||||||
|
expect(parts.variation).toEqual('2');
|
||||||
|
expect(parts.name).toEqual('fadein');
|
||||||
|
expect(parts.longname).toEqual('anim.fadein(2)');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,359 +1,362 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
env: {
|
env: {
|
||||||
es6: true,
|
es6: true,
|
||||||
jasmine: true,
|
jasmine: true,
|
||||||
node: true
|
node: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: 2018,
|
ecmaVersion: 2018,
|
||||||
sourceType: 'module'
|
sourceType: 'module',
|
||||||
},
|
},
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
// Possible errors
|
// Possible errors
|
||||||
'for-direction': 'error',
|
'for-direction': 'error',
|
||||||
'getter-return': 'error',
|
'getter-return': 'error',
|
||||||
'no-async-promise-executor': 'error',
|
'no-async-promise-executor': 'error',
|
||||||
'no-await-in-loop': 'error',
|
'no-await-in-loop': 'error',
|
||||||
'no-compare-neg-zero': 'error',
|
'no-compare-neg-zero': 'error',
|
||||||
'no-cond-assign': 'error',
|
'no-cond-assign': 'error',
|
||||||
'no-console': 'off',
|
'no-console': 'off',
|
||||||
'no-constant-condition': 'off',
|
'no-constant-condition': 'off',
|
||||||
'no-control-regex': 'error',
|
'no-control-regex': 'error',
|
||||||
'no-debugger': 'error',
|
'no-debugger': 'error',
|
||||||
'no-dupe-args': 'error',
|
'no-dupe-args': 'error',
|
||||||
'no-dupe-else-if': 'error',
|
'no-dupe-else-if': 'error',
|
||||||
'no-dupe-keys': 'error',
|
'no-dupe-keys': 'error',
|
||||||
'no-duplicate-case': 'error',
|
'no-duplicate-case': 'error',
|
||||||
'no-empty': 'error',
|
'no-empty': 'error',
|
||||||
'no-empty-character-class': 'error',
|
'no-empty-character-class': 'error',
|
||||||
'no-ex-assign': 'error',
|
'no-ex-assign': 'error',
|
||||||
'no-extra-boolean-cast': 'error',
|
'no-extra-boolean-cast': 'error',
|
||||||
'no-extra-parens': 'off',
|
'no-extra-parens': 'off',
|
||||||
'no-extra-semi': 'error',
|
'no-extra-semi': 'error',
|
||||||
'no-func-assign': 'error',
|
'no-func-assign': 'error',
|
||||||
'no-import-assign': 'error',
|
'no-import-assign': 'error',
|
||||||
'no-inner-declarations': ['error', 'functions'],
|
'no-inner-declarations': ['error', 'functions'],
|
||||||
'no-invalid-regexp': 'error',
|
'no-invalid-regexp': 'error',
|
||||||
'no-irregular-whitespace': 'error',
|
'no-irregular-whitespace': 'error',
|
||||||
'no-loss-of-precision': 'error',
|
'no-loss-of-precision': 'error',
|
||||||
'no-misleading-character-class': 'error',
|
'no-misleading-character-class': 'error',
|
||||||
'no-obj-calls': 'error',
|
'no-obj-calls': 'error',
|
||||||
'no-prototype-builtins': 'error',
|
'no-prototype-builtins': 'error',
|
||||||
'no-regex-spaces': 'error',
|
'no-regex-spaces': 'error',
|
||||||
'no-setter-return': 'error',
|
'no-setter-return': 'error',
|
||||||
'no-sparse-arrays': 'error',
|
'no-sparse-arrays': 'error',
|
||||||
'no-template-curly-in-string': 'error',
|
'no-template-curly-in-string': 'error',
|
||||||
'no-unexpected-multiline': 'error',
|
'no-unexpected-multiline': 'error',
|
||||||
'no-unreachable': 'error',
|
'no-unreachable': 'error',
|
||||||
'no-unreachable-loop': 'error',
|
'no-unreachable-loop': 'error',
|
||||||
'no-unsafe-finally': 'error',
|
'no-unsafe-finally': 'error',
|
||||||
'no-unsafe-negation': 'error',
|
'no-unsafe-negation': 'error',
|
||||||
'no-unsafe-optional-chaining': 'error',
|
'no-unsafe-optional-chaining': 'error',
|
||||||
'no-useless-backreference': 'error',
|
'no-useless-backreference': 'error',
|
||||||
'require-atomic-updates': 'error',
|
'require-atomic-updates': 'error',
|
||||||
'use-isnan': 'error',
|
'use-isnan': 'error',
|
||||||
'valid-typeof': 'error',
|
'valid-typeof': 'error',
|
||||||
|
|
||||||
// Best practices
|
// Best practices
|
||||||
'accessor-pairs': 'error',
|
'accessor-pairs': 'error',
|
||||||
'array-callback-return': 'error',
|
'array-callback-return': 'error',
|
||||||
'block-scoped-var': 'off',
|
'block-scoped-var': 'off',
|
||||||
'class-methods-use-this': 'off',
|
'class-methods-use-this': 'off',
|
||||||
complexity: 'off', // TODO: enable
|
complexity: 'off', // TODO: enable
|
||||||
'consistent-return': 'error',
|
'consistent-return': 'error',
|
||||||
curly: ['error', 'all'],
|
curly: ['error', 'all'],
|
||||||
'default-case': 'error',
|
'default-case': 'error',
|
||||||
'default-case-last': 'error',
|
'default-case-last': 'error',
|
||||||
'default-param-last': 'error',
|
'default-param-last': 'error',
|
||||||
'dot-location': ['error', 'property'],
|
'dot-location': ['error', 'property'],
|
||||||
'dot-notation': 'error',
|
'dot-notation': 'error',
|
||||||
eqeqeq: ['error', 'smart'],
|
eqeqeq: ['error', 'smart'],
|
||||||
'grouped-accessor-pairs': 'error',
|
'grouped-accessor-pairs': 'error',
|
||||||
'guard-for-in': 'error',
|
'guard-for-in': 'error',
|
||||||
'max-classes-per-file': 'off',
|
'max-classes-per-file': 'off',
|
||||||
'no-alert': 'error',
|
'no-alert': 'error',
|
||||||
'no-caller': 'error',
|
'no-caller': 'error',
|
||||||
'no-case-declarations': 'error',
|
'no-case-declarations': 'error',
|
||||||
'no-constructor-return': 'off',
|
'no-constructor-return': 'off',
|
||||||
'no-div-regex': 'error',
|
'no-div-regex': 'error',
|
||||||
'no-else-return': 'off',
|
'no-else-return': 'off',
|
||||||
'no-empty-function': 'error',
|
'no-empty-function': 'error',
|
||||||
'no-empty-pattern': 'error',
|
'no-empty-pattern': 'error',
|
||||||
'no-eq-null': 'error',
|
'no-eq-null': 'error',
|
||||||
'no-eval': 'error',
|
'no-eval': 'error',
|
||||||
'no-extend-native': 'error',
|
'no-extend-native': 'error',
|
||||||
'no-extra-bind': 'error',
|
'no-extra-bind': 'error',
|
||||||
'no-extra-label': 'error',
|
'no-extra-label': 'error',
|
||||||
'no-fallthrough': 'off', // disabled due to bug in ESLint
|
'no-fallthrough': 'off', // disabled due to bug in ESLint
|
||||||
'no-floating-decimal': 'error',
|
'no-floating-decimal': 'error',
|
||||||
'no-global-assign': 'error',
|
'no-global-assign': 'error',
|
||||||
'no-implicit-coercion': 'error',
|
'no-implicit-coercion': 'error',
|
||||||
'no-implicit-globals': 'error',
|
'no-implicit-globals': 'error',
|
||||||
'no-implied-eval': 'error',
|
'no-implied-eval': 'error',
|
||||||
'no-invalid-this': 'error',
|
'no-invalid-this': 'error',
|
||||||
'no-iterator': 'error',
|
'no-iterator': 'error',
|
||||||
'no-labels': 'error',
|
'no-labels': 'error',
|
||||||
'no-lone-blocks': 'error',
|
'no-lone-blocks': 'error',
|
||||||
'no-loop-func': 'error',
|
'no-loop-func': 'error',
|
||||||
'no-magic-numbers': 'off', // TODO: enable?
|
'no-magic-numbers': 'off', // TODO: enable?
|
||||||
'no-multi-spaces': 'error',
|
'no-multi-spaces': 'error',
|
||||||
'no-multi-str': 'error',
|
'no-multi-str': 'error',
|
||||||
'no-new': 'error',
|
'no-new': 'error',
|
||||||
'no-new-func': 'error',
|
'no-new-func': 'error',
|
||||||
'no-new-wrappers': 'error',
|
'no-new-wrappers': 'error',
|
||||||
'no-nonoctal-decimal-escape': 'error',
|
'no-nonoctal-decimal-escape': 'error',
|
||||||
'no-octal': 'error',
|
'no-octal': 'error',
|
||||||
'no-octal-escape': 'error',
|
'no-octal-escape': 'error',
|
||||||
'no-param-reassign': 'off',
|
'no-param-reassign': 'off',
|
||||||
'no-proto': 'error',
|
'no-proto': 'error',
|
||||||
'no-redeclare': 'error',
|
'no-redeclare': 'error',
|
||||||
'no-restricted-properties': 'off',
|
'no-restricted-properties': 'off',
|
||||||
'no-return-assign': 'error',
|
'no-return-assign': 'error',
|
||||||
'no-return-await': 'error',
|
'no-return-await': 'error',
|
||||||
'no-script-url': 'error',
|
'no-script-url': 'error',
|
||||||
'no-self-assign': 'error',
|
'no-self-assign': 'error',
|
||||||
'no-self-compare': 'error',
|
'no-self-compare': 'error',
|
||||||
'no-sequences': 'error',
|
'no-sequences': 'error',
|
||||||
'no-throw-literal': 'error',
|
'no-throw-literal': 'error',
|
||||||
'no-unmodified-loop-condition': 'error',
|
'no-unmodified-loop-condition': 'error',
|
||||||
'no-unused-expressions': 'error',
|
'no-unused-expressions': 'error',
|
||||||
'no-unused-labels': 'error',
|
'no-unused-labels': 'error',
|
||||||
'no-useless-call': 'error',
|
'no-useless-call': 'error',
|
||||||
'no-useless-catch': 'error',
|
'no-useless-catch': 'error',
|
||||||
'no-useless-concat': 'error',
|
'no-useless-concat': 'error',
|
||||||
'no-useless-escape': 'error',
|
'no-useless-escape': 'error',
|
||||||
'no-useless-return': 'error',
|
'no-useless-return': 'error',
|
||||||
'no-void': 'error',
|
'no-void': 'error',
|
||||||
'no-warning-comments': 'off',
|
'no-warning-comments': 'off',
|
||||||
'no-with': 'error',
|
'no-with': 'error',
|
||||||
'prefer-named-capture-group': 'off', // TODO: enable
|
'prefer-named-capture-group': 'off', // TODO: enable
|
||||||
'prefer-promise-reject-errors': 'error',
|
'prefer-promise-reject-errors': 'error',
|
||||||
'prefer-regex-literals': 'error',
|
'prefer-regex-literals': 'error',
|
||||||
radix: 'error',
|
radix: 'error',
|
||||||
'require-await': 'error',
|
'require-await': 'error',
|
||||||
'require-unicode-regexp': 'off',
|
'require-unicode-regexp': 'off',
|
||||||
'vars-on-top': 'off', // TODO: enable
|
'vars-on-top': 'off', // TODO: enable
|
||||||
'wrap-iife': ['error', 'inside'],
|
'wrap-iife': ['error', 'inside'],
|
||||||
yoda: 'error',
|
yoda: 'error',
|
||||||
|
|
||||||
// Strict mode
|
// Strict mode
|
||||||
strict: ['error', 'global'],
|
strict: ['error', 'global'],
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
'init-declarations': 'off',
|
'init-declarations': 'off',
|
||||||
'no-delete-var': 'error',
|
'no-delete-var': 'error',
|
||||||
'no-label-var': 'error',
|
'no-label-var': 'error',
|
||||||
'no-restricted-globals': ['error', 'app', 'env'],
|
'no-restricted-globals': ['error', 'app', 'env'],
|
||||||
'no-shadow': 'error',
|
'no-shadow': 'error',
|
||||||
'no-shadow-restricted-names': 'error',
|
'no-shadow-restricted-names': 'error',
|
||||||
'no-undef': 'error',
|
'no-undef': 'error',
|
||||||
'no-undef-init': 'error',
|
'no-undef-init': 'error',
|
||||||
'no-undefined': 'off',
|
'no-undefined': 'off',
|
||||||
'no-unused-vars': 'error',
|
'no-unused-vars': 'error',
|
||||||
'no-use-before-define': 'error',
|
'no-use-before-define': 'error',
|
||||||
|
|
||||||
// Stylistic issues
|
// Stylistic issues
|
||||||
'array-bracket-newline': 'off',
|
'array-bracket-newline': 'off',
|
||||||
'array-bracket-spacing': ['error', 'never'],
|
'array-bracket-spacing': ['error', 'never'],
|
||||||
'array-element-newline': 'off',
|
'array-element-newline': 'off',
|
||||||
'block-spacing': ['error', 'always'],
|
'block-spacing': ['error', 'always'],
|
||||||
'brace-style': 'off', // TODO: enable with "stroustrup" (or "1tbsp" + lots of cleanup)
|
'brace-style': 'off', // TODO: enable with "stroustrup" (or "1tbsp" + lots of cleanup)
|
||||||
camelcase: 'error',
|
camelcase: 'error',
|
||||||
'capitalized-comments': 'off',
|
'capitalized-comments': 'off',
|
||||||
'comma-dangle': 'error',
|
'comma-dangle': 'error',
|
||||||
'comma-spacing': [
|
'comma-spacing': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
before: false,
|
before: false,
|
||||||
after: true
|
after: true,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'comma-style': ['error', 'last'],
|
'comma-style': ['error', 'last'],
|
||||||
'computed-property-spacing': ['error', 'never'],
|
'computed-property-spacing': ['error', 'never'],
|
||||||
'consistent-this': ['error', 'self'],
|
'consistent-this': ['error', 'self'],
|
||||||
'eol-last': 'error',
|
'eol-last': 'error',
|
||||||
'func-call-spacing': ['error', 'never'],
|
'func-call-spacing': ['error', 'never'],
|
||||||
'func-name-matching': ['error', 'always'],
|
'func-name-matching': ['error', 'always'],
|
||||||
'func-names': 'off',
|
'func-names': 'off',
|
||||||
'func-style': 'off',
|
'func-style': 'off',
|
||||||
'function-call-argument-newline:': 'off',
|
'function-call-argument-newline:': 'off',
|
||||||
'function-paren-newline': 'off',
|
'function-paren-newline': 'off',
|
||||||
'id-denylist': 'off',
|
'id-denylist': 'off',
|
||||||
'id-length': 'off',
|
'id-length': 'off',
|
||||||
'id-match': 'off',
|
'id-match': 'off',
|
||||||
'implicit-arrow-linebreak': 'off',
|
'implicit-arrow-linebreak': 'off',
|
||||||
indent: [
|
indent: [
|
||||||
'error',
|
'error',
|
||||||
4,
|
2,
|
||||||
{
|
{
|
||||||
SwitchCase: 1
|
SwitchCase: 1,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'jsx-quotes': ['error', 'prefer-double'],
|
'jsx-quotes': ['error', 'prefer-double'],
|
||||||
'key-spacing': [
|
'key-spacing': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
beforeColon: false,
|
beforeColon: false,
|
||||||
afterColon: true
|
afterColon: true,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'keyword-spacing': [
|
'keyword-spacing': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
before: true,
|
before: true,
|
||||||
after: true
|
after: true,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'line-comment-position': 'off',
|
'line-comment-position': 'off',
|
||||||
'linebreak-style': 'off',
|
'linebreak-style': 'off',
|
||||||
'lines-around-comment': 'off',
|
'lines-around-comment': 'off',
|
||||||
'lines-between-class-members': 'off',
|
'lines-between-class-members': 'off',
|
||||||
'max-depth': 'off', // TODO: enable
|
'max-depth': 'off', // TODO: enable
|
||||||
'max-len': 'off', // TODO: enable
|
'max-len': 'off', // TODO: enable
|
||||||
'max-lines': 'off',
|
'max-lines': 'off',
|
||||||
'max-lines-per-function': 'off',
|
'max-lines-per-function': 'off',
|
||||||
'max-nested-callbacks': 'off',
|
'max-nested-callbacks': 'off',
|
||||||
'max-params': 'off', // TODO: enable
|
'max-params': 'off', // TODO: enable
|
||||||
'max-statements': 'off',
|
'max-statements': 'off',
|
||||||
'max-statements-per-line': 'off',
|
'max-statements-per-line': 'off',
|
||||||
'multiline-comment-style': 'off',
|
'multiline-comment-style': 'off',
|
||||||
'multiline-ternary': 'off',
|
'multiline-ternary': 'off',
|
||||||
'new-cap': 'error',
|
'new-cap': 'error',
|
||||||
'new-parens': 'error',
|
'new-parens': 'error',
|
||||||
'newline-per-chained-call': 'off', // TODO: enable
|
'newline-per-chained-call': 'off', // TODO: enable
|
||||||
'no-array-constructor': 'error',
|
'no-array-constructor': 'error',
|
||||||
'no-bitwise': 'error',
|
'no-bitwise': 'error',
|
||||||
'no-continue': 'off',
|
'no-continue': 'off',
|
||||||
'no-inline-comments': 'off',
|
'no-inline-comments': 'off',
|
||||||
'no-lonely-if': 'error',
|
'no-lonely-if': 'error',
|
||||||
'no-mixed-operators': 'error',
|
'no-mixed-operators': 'error',
|
||||||
'no-mixed-spaces-and-tabs': 'error',
|
'no-mixed-spaces-and-tabs': 'error',
|
||||||
'no-multi-assign': 'off',
|
'no-multi-assign': 'off',
|
||||||
'no-multiple-empty-lines': [
|
'no-multiple-empty-lines': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
max: 2
|
max: 2,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'no-negated-condition': 'off',
|
'no-negated-condition': 'off',
|
||||||
'no-nested-ternary': 'error',
|
'no-nested-ternary': 'error',
|
||||||
'no-new-object': 'error',
|
'no-new-object': 'error',
|
||||||
'no-plusplus': 'off',
|
'no-plusplus': 'off',
|
||||||
'no-restricted-syntax': 'off',
|
'no-restricted-syntax': 'off',
|
||||||
'no-tabs': 'error',
|
'no-tabs': 'error',
|
||||||
'no-ternary': 'off',
|
'no-ternary': 'off',
|
||||||
'no-trailing-spaces': 'error',
|
'no-trailing-spaces': 'error',
|
||||||
'no-underscore-dangle': 'off',
|
'no-underscore-dangle': 'off',
|
||||||
'no-unneeded-ternary': 'error',
|
'no-unneeded-ternary': 'error',
|
||||||
'no-whitespace-before-property': 'error',
|
'no-whitespace-before-property': 'error',
|
||||||
'nonblock-statement-body-position': 'off',
|
'nonblock-statement-body-position': 'off',
|
||||||
'object-curly-newline': 'off',
|
'object-curly-newline': 'off',
|
||||||
'object-curly-spacing': 'off',
|
'object-curly-spacing': 'off',
|
||||||
'object-property-newline': 'error',
|
'object-property-newline': 'error',
|
||||||
'one-var': 'off',
|
'one-var': 'off',
|
||||||
'one-var-declaration-per-line': 'error',
|
'one-var-declaration-per-line': 'error',
|
||||||
'operator-assignment': 'off',
|
'operator-assignment': 'off',
|
||||||
'operator-linebreak': ['error', 'after'],
|
'operator-linebreak': ['error', 'after'],
|
||||||
'padded-blocks': ['error', 'never'],
|
'padded-blocks': ['error', 'never'],
|
||||||
'padding-line-between-statements': [
|
'padding-line-between-statements': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
blankLine: 'always',
|
blankLine: 'always',
|
||||||
prev: '*',
|
prev: '*',
|
||||||
next: 'return'
|
next: 'return',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
blankLine: 'always',
|
blankLine: 'always',
|
||||||
prev: ['const', 'let', 'var'],
|
prev: ['const', 'let', 'var'],
|
||||||
next: '*'
|
next: '*',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
blankLine: 'any',
|
blankLine: 'any',
|
||||||
prev: ['const', 'let', 'var'],
|
prev: ['const', 'let', 'var'],
|
||||||
next: ['const', 'let', 'var']
|
next: ['const', 'let', 'var'],
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'prefer-exponentiation-operator': 'error',
|
'prefer-exponentiation-operator': 'error',
|
||||||
'prefer-object-spread': 'off',
|
'prefer-object-spread': 'off',
|
||||||
'quote-props': 'off',
|
'quote-props': 'off',
|
||||||
quotes: ['error', 'single', 'avoid-escape'],
|
quotes: ['error', 'single', 'avoid-escape'],
|
||||||
semi: ['error', 'always'],
|
semi: ['error', 'always'],
|
||||||
'semi-spacing': 'error',
|
'semi-spacing': 'error',
|
||||||
'semi-style': ['error', 'last'],
|
'semi-style': ['error', 'last'],
|
||||||
'sort-keys': 'off',
|
'sort-keys': 'off',
|
||||||
'sort-vars': 'off', // TODO: enable?
|
'sort-vars': 'off', // TODO: enable?
|
||||||
'space-before-blocks': ['error', 'always'],
|
'space-before-blocks': ['error', 'always'],
|
||||||
'space-before-function-paren': ['error', {
|
'space-before-function-paren': [
|
||||||
anonymous: 'never',
|
'error',
|
||||||
named: 'never',
|
{
|
||||||
asyncArrow: 'always'
|
anonymous: 'never',
|
||||||
}],
|
named: 'never',
|
||||||
'space-in-parens': 'off', // TODO: enable?
|
asyncArrow: 'always',
|
||||||
'space-infix-ops': 'error',
|
},
|
||||||
'space-unary-ops': 'error',
|
],
|
||||||
'spaced-comment': ['error', 'always'],
|
'space-in-parens': 'off', // TODO: enable?
|
||||||
'switch-colon-spacing': [
|
'space-infix-ops': 'error',
|
||||||
'error',
|
'space-unary-ops': 'error',
|
||||||
{
|
'spaced-comment': ['error', 'always'],
|
||||||
after: true,
|
'switch-colon-spacing': [
|
||||||
before: false
|
'error',
|
||||||
}
|
{
|
||||||
],
|
after: true,
|
||||||
'template-tag-spacing': ['error', 'never'],
|
before: false,
|
||||||
'unicode-bom': ['error', 'never'],
|
},
|
||||||
'wrap-regex': 'off',
|
],
|
||||||
|
'template-tag-spacing': ['error', 'never'],
|
||||||
|
'unicode-bom': ['error', 'never'],
|
||||||
|
'wrap-regex': 'off',
|
||||||
|
|
||||||
// ECMAScript 2015
|
// ECMAScript 2015
|
||||||
'arrow-body-style': ['error', 'as-needed'],
|
'arrow-body-style': ['error', 'as-needed'],
|
||||||
'arrow-parens': 'off',
|
'arrow-parens': 'off',
|
||||||
'arrow-spacing': [
|
'arrow-spacing': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
before: true,
|
before: true,
|
||||||
after: true
|
after: true,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'constructor-super': 'error',
|
'constructor-super': 'error',
|
||||||
'generator-star-spacing': [
|
'generator-star-spacing': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
before: true,
|
before: true,
|
||||||
after: false
|
after: false,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'no-class-assign': 'error',
|
'no-class-assign': 'error',
|
||||||
'no-confusing-arrow': 'error',
|
'no-confusing-arrow': 'error',
|
||||||
'no-const-assign': 'error',
|
'no-const-assign': 'error',
|
||||||
'no-dupe-class-members': 'error',
|
'no-dupe-class-members': 'error',
|
||||||
'no-duplicate-imports': [
|
'no-duplicate-imports': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
includeExports: true
|
includeExports: true,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'no-new-symbol': 'error',
|
'no-new-symbol': 'error',
|
||||||
'no-restricted-exports': 'off',
|
'no-restricted-exports': 'off',
|
||||||
'no-restricted-imports': 'off',
|
'no-restricted-imports': 'off',
|
||||||
'no-this-before-super': 'error',
|
'no-this-before-super': 'error',
|
||||||
'no-useless-computed-key': 'error',
|
'no-useless-computed-key': 'error',
|
||||||
'no-useless-constructor': 'off',
|
'no-useless-constructor': 'off',
|
||||||
'no-useless-rename': 'error',
|
'no-useless-rename': 'error',
|
||||||
'no-var': 'off', // TODO: enable
|
'no-var': 'off', // TODO: enable
|
||||||
'object-shorthand': 'off',
|
'object-shorthand': 'off',
|
||||||
'prefer-arrow-callback': 'off',
|
'prefer-arrow-callback': 'off',
|
||||||
'prefer-const': 'off',
|
'prefer-const': 'off',
|
||||||
'prefer-destructuring': 'off',
|
'prefer-destructuring': 'off',
|
||||||
'prefer-numeric-literals': 'off',
|
'prefer-numeric-literals': 'off',
|
||||||
'prefer-rest-params': 'off',
|
'prefer-rest-params': 'off',
|
||||||
'prefer-spread': 'off',
|
'prefer-spread': 'off',
|
||||||
'prefer-template': 'off',
|
'prefer-template': 'off',
|
||||||
'require-yield': 'error',
|
'require-yield': 'error',
|
||||||
'rest-spread-spacing': ['error', 'never'],
|
'rest-spread-spacing': ['error', 'never'],
|
||||||
'sort-imports': 'error',
|
'sort-imports': 'error',
|
||||||
'symbol-description': 'error',
|
'symbol-description': 'error',
|
||||||
'template-curly-spacing': ['error', 'never'],
|
'template-curly-spacing': ['error', 'never'],
|
||||||
'yield-star-spacing': ['error', 'before']
|
'yield-star-spacing': ['error', 'before'],
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,7 +3,7 @@ const astNode = require('./lib/ast-node');
|
|||||||
const { Syntax } = require('./lib/syntax');
|
const { Syntax } = require('./lib/syntax');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
AstBuilder,
|
AstBuilder,
|
||||||
astNode,
|
astNode,
|
||||||
Syntax
|
Syntax,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,63 +3,68 @@ const babelParser = require('@babel/parser');
|
|||||||
const { log } = require('@jsdoc/util');
|
const { log } = require('@jsdoc/util');
|
||||||
|
|
||||||
// Exported so we can use them in tests.
|
// Exported so we can use them in tests.
|
||||||
const parserOptions = exports.parserOptions = {
|
const parserOptions = (exports.parserOptions = {
|
||||||
allowAwaitOutsideFunction: true,
|
allowAwaitOutsideFunction: true,
|
||||||
allowImportExportEverywhere: true,
|
allowImportExportEverywhere: true,
|
||||||
allowReturnOutsideFunction: true,
|
allowReturnOutsideFunction: true,
|
||||||
allowSuperOutsideMethod: true,
|
allowSuperOutsideMethod: true,
|
||||||
allowUndeclaredExports: true,
|
allowUndeclaredExports: true,
|
||||||
plugins: [
|
plugins: [
|
||||||
'asyncGenerators',
|
'asyncGenerators',
|
||||||
'bigInt',
|
'bigInt',
|
||||||
'classPrivateMethods',
|
'classPrivateMethods',
|
||||||
'classPrivateProperties',
|
'classPrivateProperties',
|
||||||
'classProperties',
|
'classProperties',
|
||||||
['decorators', {
|
[
|
||||||
decoratorsBeforeExport: true
|
'decorators',
|
||||||
}],
|
{
|
||||||
'doExpressions',
|
decoratorsBeforeExport: true,
|
||||||
'dynamicImport',
|
},
|
||||||
'estree',
|
|
||||||
'exportDefaultFrom',
|
|
||||||
'exportNamespaceFrom',
|
|
||||||
'functionBind',
|
|
||||||
'functionSent',
|
|
||||||
'importMeta',
|
|
||||||
'jsx',
|
|
||||||
'logicalAssignment',
|
|
||||||
'nullishCoalescingOperator',
|
|
||||||
'numericSeparator',
|
|
||||||
'objectRestSpread',
|
|
||||||
'optionalCatchBinding',
|
|
||||||
'optionalChaining',
|
|
||||||
['pipelineOperator', {
|
|
||||||
proposal: 'minimal'
|
|
||||||
}],
|
|
||||||
'throwExpressions'
|
|
||||||
],
|
],
|
||||||
ranges: true
|
'doExpressions',
|
||||||
};
|
'dynamicImport',
|
||||||
|
'estree',
|
||||||
|
'exportDefaultFrom',
|
||||||
|
'exportNamespaceFrom',
|
||||||
|
'functionBind',
|
||||||
|
'functionSent',
|
||||||
|
'importMeta',
|
||||||
|
'jsx',
|
||||||
|
'logicalAssignment',
|
||||||
|
'nullishCoalescingOperator',
|
||||||
|
'numericSeparator',
|
||||||
|
'objectRestSpread',
|
||||||
|
'optionalCatchBinding',
|
||||||
|
'optionalChaining',
|
||||||
|
[
|
||||||
|
'pipelineOperator',
|
||||||
|
{
|
||||||
|
proposal: 'minimal',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'throwExpressions',
|
||||||
|
],
|
||||||
|
ranges: true,
|
||||||
|
});
|
||||||
|
|
||||||
function parse(source, filename, sourceType) {
|
function parse(source, filename, sourceType) {
|
||||||
let ast;
|
let ast;
|
||||||
const options = _.defaults({}, parserOptions, {sourceType});
|
const options = _.defaults({}, parserOptions, { sourceType });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ast = babelParser.parse(source, options);
|
ast = babelParser.parse(source, options);
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
log.error(`Unable to parse ${filename}: ${e.message}`);
|
||||||
log.error(`Unable to parse ${filename}: ${e.message}`);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return ast;
|
return ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: docs
|
// TODO: docs
|
||||||
class AstBuilder {
|
class AstBuilder {
|
||||||
// TODO: docs
|
// TODO: docs
|
||||||
static build(source, filename, sourceType) {
|
static build(source, filename, sourceType) {
|
||||||
return parse(source, filename, sourceType);
|
return parse(source, filename, sourceType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.AstBuilder = AstBuilder;
|
exports.AstBuilder = AstBuilder;
|
||||||
|
|||||||
@ -15,23 +15,26 @@ let uid = 100000000;
|
|||||||
* @param {(Object|string)} node - The AST node to check, or the `type` property of a node.
|
* @param {(Object|string)} node - The AST node to check, or the `type` property of a node.
|
||||||
* @return {boolean} Set to `true` if the node is a function or `false` in all other cases.
|
* @return {boolean} Set to `true` if the node is a function or `false` in all other cases.
|
||||||
*/
|
*/
|
||||||
const isFunction = exports.isFunction = node => {
|
const isFunction = (exports.isFunction = (node) => {
|
||||||
let type;
|
let type;
|
||||||
|
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof node === 'string') {
|
if (typeof node === 'string') {
|
||||||
type = node;
|
type = node;
|
||||||
}
|
} else {
|
||||||
else {
|
type = node.type;
|
||||||
type = node.type;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return type === Syntax.FunctionDeclaration || type === Syntax.FunctionExpression ||
|
return (
|
||||||
type === Syntax.MethodDefinition || type === Syntax.ArrowFunctionExpression;
|
type === Syntax.FunctionDeclaration ||
|
||||||
};
|
type === Syntax.FunctionExpression ||
|
||||||
|
type === Syntax.MethodDefinition ||
|
||||||
|
type === Syntax.ArrowFunctionExpression
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether an AST node creates a new scope.
|
* Check whether an AST node creates a new scope.
|
||||||
@ -40,513 +43,521 @@ const isFunction = exports.isFunction = node => {
|
|||||||
* @param {Object} node - The AST node to check.
|
* @param {Object} node - The AST node to check.
|
||||||
* @return {Boolean} Set to `true` if the node creates a new scope, or `false` in all other cases.
|
* @return {Boolean} Set to `true` if the node creates a new scope, or `false` in all other cases.
|
||||||
*/
|
*/
|
||||||
exports.isScope = node => // TODO: handle blocks with "let" declarations
|
exports.isScope = (
|
||||||
Boolean(node) && typeof node === 'object' && (node.type === Syntax.CatchClause ||
|
node // TODO: handle blocks with "let" declarations
|
||||||
node.type === Syntax.ClassDeclaration || node.type === Syntax.ClassExpression || isFunction(node));
|
) =>
|
||||||
|
Boolean(node) &&
|
||||||
|
typeof node === 'object' &&
|
||||||
|
(node.type === Syntax.CatchClause ||
|
||||||
|
node.type === Syntax.ClassDeclaration ||
|
||||||
|
node.type === Syntax.ClassExpression ||
|
||||||
|
isFunction(node));
|
||||||
|
|
||||||
// TODO: docs
|
// TODO: docs
|
||||||
exports.addNodeProperties = node => {
|
exports.addNodeProperties = (node) => {
|
||||||
const newProperties = {};
|
const newProperties = {};
|
||||||
|
|
||||||
if (!node || typeof node !== 'object') {
|
if (!node || typeof node !== 'object') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!node.nodeId) {
|
if (!node.nodeId) {
|
||||||
newProperties.nodeId = {
|
newProperties.nodeId = {
|
||||||
value: `astnode${uid++}`,
|
value: `astnode${uid++}`,
|
||||||
enumerable: true
|
enumerable: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_.isUndefined(node.parent)) {
|
if (_.isUndefined(node.parent)) {
|
||||||
newProperties.parent = {
|
newProperties.parent = {
|
||||||
// `null` means 'no parent', so use `undefined` for now
|
// `null` means 'no parent', so use `undefined` for now
|
||||||
value: undefined,
|
value: undefined,
|
||||||
writable: true
|
writable: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_.isUndefined(node.enclosingScope)) {
|
if (_.isUndefined(node.enclosingScope)) {
|
||||||
newProperties.enclosingScope = {
|
newProperties.enclosingScope = {
|
||||||
// `null` means 'no enclosing scope', so use `undefined` for now
|
// `null` means 'no enclosing scope', so use `undefined` for now
|
||||||
value: undefined,
|
value: undefined,
|
||||||
writable: true
|
writable: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_.isUndefined(node.parentId)) {
|
if (_.isUndefined(node.parentId)) {
|
||||||
newProperties.parentId = {
|
newProperties.parentId = {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
get() {
|
get() {
|
||||||
return this.parent ? this.parent.nodeId : null;
|
return this.parent ? this.parent.nodeId : null;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_.isUndefined(node.enclosingScopeId)) {
|
if (_.isUndefined(node.enclosingScopeId)) {
|
||||||
newProperties.enclosingScopeId = {
|
newProperties.enclosingScopeId = {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
get() {
|
get() {
|
||||||
return this.enclosingScope ? this.enclosingScope.nodeId : null;
|
return this.enclosingScope ? this.enclosingScope.nodeId : null;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.defineProperties(node, newProperties);
|
Object.defineProperties(node, newProperties);
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: docs
|
// TODO: docs
|
||||||
const nodeToValue = exports.nodeToValue = node => {
|
const nodeToValue = (exports.nodeToValue = (node) => {
|
||||||
let key;
|
let key;
|
||||||
let parent;
|
let parent;
|
||||||
let str;
|
let str;
|
||||||
let tempObject;
|
let tempObject;
|
||||||
|
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
case Syntax.ArrayExpression:
|
case Syntax.ArrayExpression:
|
||||||
tempObject = [];
|
tempObject = [];
|
||||||
node.elements.forEach((el, i) => {
|
node.elements.forEach((el, i) => {
|
||||||
// handle sparse arrays. use `null` to represent missing values, consistent with
|
// handle sparse arrays. use `null` to represent missing values, consistent with
|
||||||
// JSON.stringify([,]).
|
// JSON.stringify([,]).
|
||||||
if (!el) {
|
if (!el) {
|
||||||
tempObject[i] = null;
|
tempObject[i] = null;
|
||||||
}
|
} else {
|
||||||
else {
|
tempObject[i] = nodeToValue(el);
|
||||||
tempObject[i] = nodeToValue(el);
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
str = JSON.stringify(tempObject);
|
str = JSON.stringify(tempObject);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.AssignmentExpression:
|
case Syntax.AssignmentExpression:
|
||||||
// falls through
|
// falls through
|
||||||
|
|
||||||
case Syntax.AssignmentPattern:
|
case Syntax.AssignmentPattern:
|
||||||
str = nodeToValue(node.left);
|
str = nodeToValue(node.left);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.BigIntLiteral:
|
case Syntax.BigIntLiteral:
|
||||||
str = node.value;
|
str = node.value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.ClassDeclaration:
|
case Syntax.ClassDeclaration:
|
||||||
str = nodeToValue(node.id);
|
str = nodeToValue(node.id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.ClassPrivateProperty:
|
case Syntax.ClassPrivateProperty:
|
||||||
// TODO: Strictly speaking, the name should be '#' plus node.key, but because we
|
// TODO: Strictly speaking, the name should be '#' plus node.key, but because we
|
||||||
// already use '#' as scope punctuation, that causes JSDoc to get extremely confused.
|
// already use '#' as scope punctuation, that causes JSDoc to get extremely confused.
|
||||||
// The solution probably involves quoting part or all of the name, but JSDoc doesn't
|
// The solution probably involves quoting part or all of the name, but JSDoc doesn't
|
||||||
// deal with quoted names very nicely right now, and most people probably won't want to
|
// deal with quoted names very nicely right now, and most people probably won't want to
|
||||||
// document class private properties anyhow. So for now, we'll just cheat and omit the
|
// document class private properties anyhow. So for now, we'll just cheat and omit the
|
||||||
// leading '#'.
|
// leading '#'.
|
||||||
str = nodeToValue(node.key.id);
|
str = nodeToValue(node.key.id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.ClassProperty:
|
case Syntax.ClassProperty:
|
||||||
str = nodeToValue(node.key);
|
str = nodeToValue(node.key);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.ExportAllDeclaration:
|
case Syntax.ExportAllDeclaration:
|
||||||
// falls through
|
// falls through
|
||||||
|
|
||||||
case Syntax.ExportDefaultDeclaration:
|
case Syntax.ExportDefaultDeclaration:
|
||||||
str = 'module.exports';
|
str = 'module.exports';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.ExportNamedDeclaration:
|
case Syntax.ExportNamedDeclaration:
|
||||||
if (node.declaration) {
|
if (node.declaration) {
|
||||||
// like `var` in: export var foo = 'bar';
|
// like `var` in: export var foo = 'bar';
|
||||||
// we need a single value, so we use the first variable name
|
// we need a single value, so we use the first variable name
|
||||||
if (node.declaration.declarations) {
|
if (node.declaration.declarations) {
|
||||||
str = `exports.${nodeToValue(node.declaration.declarations[0])}`;
|
str = `exports.${nodeToValue(node.declaration.declarations[0])}`;
|
||||||
}
|
} else {
|
||||||
else {
|
str = `exports.${nodeToValue(node.declaration)}`;
|
||||||
str = `exports.${nodeToValue(node.declaration)}`;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise we'll use the ExportSpecifier nodes
|
// otherwise we'll use the ExportSpecifier nodes
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.ExportSpecifier:
|
case Syntax.ExportSpecifier:
|
||||||
str = `exports.${nodeToValue(node.exported)}`;
|
str = `exports.${nodeToValue(node.exported)}`;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.ArrowFunctionExpression:
|
case Syntax.ArrowFunctionExpression:
|
||||||
// falls through
|
// falls through
|
||||||
|
|
||||||
case Syntax.FunctionDeclaration:
|
case Syntax.FunctionDeclaration:
|
||||||
// falls through
|
// falls through
|
||||||
|
|
||||||
case Syntax.FunctionExpression:
|
case Syntax.FunctionExpression:
|
||||||
if (node.id && node.id.name) {
|
if (node.id && node.id.name) {
|
||||||
str = node.id.name;
|
str = node.id.name;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.Identifier:
|
case Syntax.Identifier:
|
||||||
str = node.name;
|
str = node.name;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.Literal:
|
case Syntax.Literal:
|
||||||
str = node.value;
|
str = node.value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.MemberExpression:
|
case Syntax.MemberExpression:
|
||||||
// could be computed (like foo['bar']) or not (like foo.bar)
|
// could be computed (like foo['bar']) or not (like foo.bar)
|
||||||
str = nodeToValue(node.object);
|
str = nodeToValue(node.object);
|
||||||
if (node.computed) {
|
if (node.computed) {
|
||||||
str += `[${node.property.raw}]`;
|
str += `[${node.property.raw}]`;
|
||||||
}
|
} else {
|
||||||
else {
|
str += `.${nodeToValue(node.property)}`;
|
||||||
str += `.${nodeToValue(node.property)}`;
|
}
|
||||||
}
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case Syntax.MethodDefinition:
|
case Syntax.MethodDefinition:
|
||||||
parent = node.parent.parent;
|
parent = node.parent.parent;
|
||||||
// for class expressions, we want the name of the variable the class is assigned to
|
// for class expressions, we want the name of the variable the class is assigned to
|
||||||
// (but there won't be a name if the class is returned by an arrow function expression)
|
// (but there won't be a name if the class is returned by an arrow function expression)
|
||||||
// TODO: we should use `LONGNAMES.ANONYMOUS` instead of an empty string, but that
|
// TODO: we should use `LONGNAMES.ANONYMOUS` instead of an empty string, but that
|
||||||
// causes problems downstream if the parent class has an `@alias` tag
|
// causes problems downstream if the parent class has an `@alias` tag
|
||||||
if (parent.type === Syntax.ClassExpression) {
|
if (parent.type === Syntax.ClassExpression) {
|
||||||
str = nodeToValue(parent.parent) || '';
|
str = nodeToValue(parent.parent) || '';
|
||||||
}
|
}
|
||||||
// for the constructor of a module's default export, use a special name
|
// for the constructor of a module's default export, use a special name
|
||||||
else if (node.kind === 'constructor' && parent.parent &&
|
else if (
|
||||||
parent.parent.type === Syntax.ExportDefaultDeclaration) {
|
node.kind === 'constructor' &&
|
||||||
str = 'module.exports';
|
parent.parent &&
|
||||||
}
|
parent.parent.type === Syntax.ExportDefaultDeclaration
|
||||||
// for the constructor of a module's named export, use the name of the export
|
) {
|
||||||
// declaration
|
str = 'module.exports';
|
||||||
else if (node.kind === 'constructor' && parent.parent &&
|
}
|
||||||
parent.parent.type === Syntax.ExportNamedDeclaration) {
|
// for the constructor of a module's named export, use the name of the export
|
||||||
str = nodeToValue(parent.parent);
|
// declaration
|
||||||
}
|
else if (
|
||||||
// for other constructors, use the name of the parent class
|
node.kind === 'constructor' &&
|
||||||
else if (node.kind === 'constructor') {
|
parent.parent &&
|
||||||
str = nodeToValue(parent);
|
parent.parent.type === Syntax.ExportNamedDeclaration
|
||||||
}
|
) {
|
||||||
// if the method is a member of a module's default export, ignore the name, because it's
|
str = nodeToValue(parent.parent);
|
||||||
// irrelevant
|
}
|
||||||
else if (parent.parent && parent.parent.type === Syntax.ExportDefaultDeclaration) {
|
// for other constructors, use the name of the parent class
|
||||||
str = '';
|
else if (node.kind === 'constructor') {
|
||||||
}
|
str = nodeToValue(parent);
|
||||||
// otherwise, use the class's name
|
}
|
||||||
else {
|
// if the method is a member of a module's default export, ignore the name, because it's
|
||||||
str = parent.id ? nodeToValue(parent.id) : '';
|
// irrelevant
|
||||||
}
|
else if (parent.parent && parent.parent.type === Syntax.ExportDefaultDeclaration) {
|
||||||
|
str = '';
|
||||||
|
}
|
||||||
|
// otherwise, use the class's name
|
||||||
|
else {
|
||||||
|
str = parent.id ? nodeToValue(parent.id) : '';
|
||||||
|
}
|
||||||
|
|
||||||
if (node.kind !== 'constructor') {
|
if (node.kind !== 'constructor') {
|
||||||
if (str) {
|
if (str) {
|
||||||
str += node.static ? SCOPE.PUNC.STATIC : SCOPE.PUNC.INSTANCE;
|
str += node.static ? SCOPE.PUNC.STATIC : SCOPE.PUNC.INSTANCE;
|
||||||
}
|
}
|
||||||
str += nodeToValue(node.key);
|
str += nodeToValue(node.key);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.ObjectExpression:
|
case Syntax.ObjectExpression:
|
||||||
tempObject = {};
|
tempObject = {};
|
||||||
node.properties.forEach(prop => {
|
node.properties.forEach((prop) => {
|
||||||
// ExperimentalSpreadProperty have no key
|
// ExperimentalSpreadProperty have no key
|
||||||
// like var hello = {...hi};
|
// like var hello = {...hi};
|
||||||
if (!prop.key) {
|
if (!prop.key) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
key = prop.key.name;
|
key = prop.key.name;
|
||||||
|
|
||||||
// preserve literal values so that the JSON form shows the correct type
|
// preserve literal values so that the JSON form shows the correct type
|
||||||
if (prop.value.type === Syntax.Literal) {
|
if (prop.value.type === Syntax.Literal) {
|
||||||
tempObject[key] = prop.value.value;
|
tempObject[key] = prop.value.value;
|
||||||
}
|
} else {
|
||||||
else {
|
tempObject[key] = nodeToValue(prop);
|
||||||
tempObject[key] = nodeToValue(prop);
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
str = JSON.stringify(tempObject);
|
str = JSON.stringify(tempObject);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.RestElement:
|
case Syntax.RestElement:
|
||||||
str = nodeToValue(node.argument);
|
str = nodeToValue(node.argument);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.ThisExpression:
|
case Syntax.ThisExpression:
|
||||||
str = 'this';
|
str = 'this';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Syntax.UnaryExpression:
|
case Syntax.UnaryExpression:
|
||||||
// like -1. in theory, operator can be prefix or postfix. in practice, any value with a
|
// like -1. in theory, operator can be prefix or postfix. in practice, any value with a
|
||||||
// valid postfix operator (such as -- or ++) is not a UnaryExpression.
|
// valid postfix operator (such as -- or ++) is not a UnaryExpression.
|
||||||
str = nodeToValue(node.argument);
|
str = nodeToValue(node.argument);
|
||||||
|
|
||||||
if (node.prefix === true) {
|
if (node.prefix === true) {
|
||||||
str = cast(node.operator + str);
|
str = cast(node.operator + str);
|
||||||
}
|
} else {
|
||||||
else {
|
// this shouldn't happen
|
||||||
// this shouldn't happen
|
throw new Error(`Found a UnaryExpression with a postfix operator: ${node}`);
|
||||||
throw new Error(`Found a UnaryExpression with a postfix operator: ${node}`);
|
}
|
||||||
}
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case Syntax.VariableDeclarator:
|
case Syntax.VariableDeclarator:
|
||||||
str = nodeToValue(node.id);
|
str = nodeToValue(node.id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
str = '';
|
str = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
};
|
});
|
||||||
|
|
||||||
// backwards compatibility
|
// backwards compatibility
|
||||||
exports.nodeToString = nodeToValue;
|
exports.nodeToString = nodeToValue;
|
||||||
|
|
||||||
// TODO: docs
|
// TODO: docs
|
||||||
const getParamNames = exports.getParamNames = node => {
|
const getParamNames = (exports.getParamNames = (node) => {
|
||||||
let params;
|
let params;
|
||||||
|
|
||||||
if (!node || !node.params) {
|
if (!node || !node.params) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
params = node.params.slice(0);
|
params = node.params.slice(0);
|
||||||
|
|
||||||
return params.map(param => nodeToValue(param));
|
return params.map((param) => nodeToValue(param));
|
||||||
};
|
});
|
||||||
|
|
||||||
// TODO: docs
|
// TODO: docs
|
||||||
const isAccessor = exports.isAccessor = node => Boolean(node) && typeof node === 'object' &&
|
const isAccessor = (exports.isAccessor = (node) =>
|
||||||
(node.type === Syntax.Property || node.type === Syntax.MethodDefinition) &&
|
Boolean(node) &&
|
||||||
(node.kind === 'get' || node.kind === 'set');
|
typeof node === 'object' &&
|
||||||
|
(node.type === Syntax.Property || node.type === Syntax.MethodDefinition) &&
|
||||||
|
(node.kind === 'get' || node.kind === 'set'));
|
||||||
|
|
||||||
// TODO: docs
|
// TODO: docs
|
||||||
exports.isAssignment = node => Boolean(node) && typeof node === 'object' &&
|
exports.isAssignment = (node) =>
|
||||||
(node.type === Syntax.AssignmentExpression || node.type === Syntax.VariableDeclarator);
|
Boolean(node) &&
|
||||||
|
typeof node === 'object' &&
|
||||||
|
(node.type === Syntax.AssignmentExpression || node.type === Syntax.VariableDeclarator);
|
||||||
|
|
||||||
// TODO: docs
|
// TODO: docs
|
||||||
/**
|
/**
|
||||||
* Retrieve information about the node, including its name and type.
|
* Retrieve information about the node, including its name and type.
|
||||||
*/
|
*/
|
||||||
exports.getInfo = node => {
|
exports.getInfo = (node) => {
|
||||||
const info = {};
|
const info = {};
|
||||||
|
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
// like the function in: "var foo = () => {}"
|
// like the function in: "var foo = () => {}"
|
||||||
case Syntax.ArrowFunctionExpression:
|
case Syntax.ArrowFunctionExpression:
|
||||||
info.node = node;
|
info.node = node;
|
||||||
info.name = '';
|
info.name = '';
|
||||||
info.type = info.node.type;
|
info.type = info.node.type;
|
||||||
info.paramnames = getParamNames(node);
|
info.paramnames = getParamNames(node);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like: "foo = 'bar'" (after declaring foo)
|
// like: "foo = 'bar'" (after declaring foo)
|
||||||
// like: "MyClass.prototype.myMethod = function() {}" (after declaring MyClass)
|
// like: "MyClass.prototype.myMethod = function() {}" (after declaring MyClass)
|
||||||
case Syntax.AssignmentExpression:
|
case Syntax.AssignmentExpression:
|
||||||
info.node = node.right;
|
info.node = node.right;
|
||||||
info.name = nodeToValue(node.left);
|
info.name = nodeToValue(node.left);
|
||||||
info.type = info.node.type;
|
info.type = info.node.type;
|
||||||
info.value = nodeToValue(info.node);
|
info.value = nodeToValue(info.node);
|
||||||
// if the assigned value is a function, we need to capture the parameter names here
|
// if the assigned value is a function, we need to capture the parameter names here
|
||||||
info.paramnames = getParamNames(node.right);
|
info.paramnames = getParamNames(node.right);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like "bar='baz'" in: function foo(bar='baz') {}
|
// like "bar='baz'" in: function foo(bar='baz') {}
|
||||||
case Syntax.AssignmentPattern:
|
case Syntax.AssignmentPattern:
|
||||||
info.node = node;
|
info.node = node;
|
||||||
info.name = nodeToValue(node.left);
|
info.name = nodeToValue(node.left);
|
||||||
info.type = info.node.type;
|
info.type = info.node.type;
|
||||||
info.value = nodeToValue(info.node);
|
info.value = nodeToValue(info.node);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like: "class Foo {}"
|
// like: "class Foo {}"
|
||||||
// or "class" in: "export default class {}"
|
// or "class" in: "export default class {}"
|
||||||
case Syntax.ClassDeclaration:
|
case Syntax.ClassDeclaration:
|
||||||
info.node = node;
|
info.node = node;
|
||||||
// if this class is the default export, we need to use a special name
|
// if this class is the default export, we need to use a special name
|
||||||
if (node.parent && node.parent.type === Syntax.ExportDefaultDeclaration) {
|
if (node.parent && node.parent.type === Syntax.ExportDefaultDeclaration) {
|
||||||
info.name = 'module.exports';
|
info.name = 'module.exports';
|
||||||
}
|
} else {
|
||||||
else {
|
info.name = node.id ? nodeToValue(node.id) : '';
|
||||||
info.name = node.id ? nodeToValue(node.id) : '';
|
}
|
||||||
}
|
info.type = info.node.type;
|
||||||
info.type = info.node.type;
|
info.paramnames = [];
|
||||||
info.paramnames = [];
|
|
||||||
|
|
||||||
node.body.body.some(({kind, value}) => {
|
node.body.body.some(({ kind, value }) => {
|
||||||
if (kind === 'constructor') {
|
if (kind === 'constructor') {
|
||||||
info.paramnames = getParamNames(value);
|
info.paramnames = getParamNames(value);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like "#b = 1;" in: "class A { #b = 1; }"
|
// like "#b = 1;" in: "class A { #b = 1; }"
|
||||||
case Syntax.ClassPrivateProperty:
|
case Syntax.ClassPrivateProperty:
|
||||||
info.node = node;
|
info.node = node;
|
||||||
info.name = nodeToValue(info.node);
|
info.name = nodeToValue(info.node);
|
||||||
info.type = info.node.type;
|
info.type = info.node.type;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like "b = 1;" in: "class A { b = 1; }"
|
// like "b = 1;" in: "class A { b = 1; }"
|
||||||
case Syntax.ClassProperty:
|
case Syntax.ClassProperty:
|
||||||
info.node = node;
|
info.node = node;
|
||||||
info.name = nodeToValue(info.node);
|
info.name = nodeToValue(info.node);
|
||||||
info.type = info.node.type;
|
info.type = info.node.type;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like: "export * from 'foo'"
|
// like: "export * from 'foo'"
|
||||||
case Syntax.ExportAllDeclaration:
|
case Syntax.ExportAllDeclaration:
|
||||||
info.node = node;
|
info.node = node;
|
||||||
info.name = nodeToValue(info.node);
|
info.name = nodeToValue(info.node);
|
||||||
info.type = info.node.type;
|
info.type = info.node.type;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like: "export default 'foo'"
|
// like: "export default 'foo'"
|
||||||
case Syntax.ExportDefaultDeclaration:
|
case Syntax.ExportDefaultDeclaration:
|
||||||
info.node = node.declaration;
|
info.node = node.declaration;
|
||||||
info.name = nodeToValue(node);
|
info.name = nodeToValue(node);
|
||||||
info.type = info.node.type;
|
info.type = info.node.type;
|
||||||
|
|
||||||
if ( isFunction(info.node) ) {
|
if (isFunction(info.node)) {
|
||||||
info.paramnames = getParamNames(info.node);
|
info.paramnames = getParamNames(info.node);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like: "export var foo;" (has declaration)
|
// like: "export var foo;" (has declaration)
|
||||||
// or: "export {foo}" (no declaration)
|
// or: "export {foo}" (no declaration)
|
||||||
case Syntax.ExportNamedDeclaration:
|
case Syntax.ExportNamedDeclaration:
|
||||||
info.node = node;
|
info.node = node;
|
||||||
info.name = nodeToValue(info.node);
|
info.name = nodeToValue(info.node);
|
||||||
info.type = info.node.declaration ? info.node.declaration.type :
|
info.type = info.node.declaration ? info.node.declaration.type : Syntax.ObjectExpression;
|
||||||
Syntax.ObjectExpression;
|
|
||||||
|
|
||||||
if (info.node.declaration) {
|
if (info.node.declaration) {
|
||||||
if ( isFunction(info.node.declaration) ) {
|
if (isFunction(info.node.declaration)) {
|
||||||
info.paramnames = getParamNames(info.node.declaration);
|
info.paramnames = getParamNames(info.node.declaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This duplicates logic for another node type in `jsdoc/src/visitor` in
|
// TODO: This duplicates logic for another node type in `jsdoc/src/visitor` in
|
||||||
// `makeSymbolFoundEvent()`. Is there a way to combine the logic for both node types
|
// `makeSymbolFoundEvent()`. Is there a way to combine the logic for both node types
|
||||||
// into a single module?
|
// into a single module?
|
||||||
if (info.node.declaration.kind === 'const') {
|
if (info.node.declaration.kind === 'const') {
|
||||||
info.kind = 'constant';
|
info.kind = 'constant';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like "foo as bar" in: "export {foo as bar}"
|
// like "foo as bar" in: "export {foo as bar}"
|
||||||
case Syntax.ExportSpecifier:
|
case Syntax.ExportSpecifier:
|
||||||
info.node = node;
|
info.node = node;
|
||||||
info.name = nodeToValue(info.node);
|
info.name = nodeToValue(info.node);
|
||||||
info.type = info.node.local.type;
|
info.type = info.node.local.type;
|
||||||
|
|
||||||
if ( isFunction(info.node.local) ) {
|
if (isFunction(info.node.local)) {
|
||||||
info.paramnames = getParamNames(info.node.local);
|
info.paramnames = getParamNames(info.node.local);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like: "function foo() {}"
|
// like: "function foo() {}"
|
||||||
// or the function in: "export default function() {}"
|
// or the function in: "export default function() {}"
|
||||||
case Syntax.FunctionDeclaration:
|
case Syntax.FunctionDeclaration:
|
||||||
info.node = node;
|
info.node = node;
|
||||||
info.name = node.id ? nodeToValue(node.id) : '';
|
info.name = node.id ? nodeToValue(node.id) : '';
|
||||||
info.type = info.node.type;
|
info.type = info.node.type;
|
||||||
info.paramnames = getParamNames(node);
|
info.paramnames = getParamNames(node);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like the function in: "var foo = function() {}"
|
// like the function in: "var foo = function() {}"
|
||||||
case Syntax.FunctionExpression:
|
case Syntax.FunctionExpression:
|
||||||
info.node = node;
|
info.node = node;
|
||||||
// TODO: should we add a name for, e.g., "var foo = function bar() {}"?
|
// TODO: should we add a name for, e.g., "var foo = function bar() {}"?
|
||||||
info.name = '';
|
info.name = '';
|
||||||
info.type = info.node.type;
|
info.type = info.node.type;
|
||||||
info.paramnames = getParamNames(node);
|
info.paramnames = getParamNames(node);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like the param "bar" in: "function foo(bar) {}"
|
// like the param "bar" in: "function foo(bar) {}"
|
||||||
case Syntax.Identifier:
|
case Syntax.Identifier:
|
||||||
info.node = node;
|
info.node = node;
|
||||||
info.name = nodeToValue(info.node);
|
info.name = nodeToValue(info.node);
|
||||||
info.type = info.node.type;
|
info.type = info.node.type;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like "a.b.c"
|
// like "a.b.c"
|
||||||
case Syntax.MemberExpression:
|
case Syntax.MemberExpression:
|
||||||
info.node = node;
|
info.node = node;
|
||||||
info.name = nodeToValue(info.node);
|
info.name = nodeToValue(info.node);
|
||||||
info.type = info.node.type;
|
info.type = info.node.type;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like: "foo() {}"
|
// like: "foo() {}"
|
||||||
case Syntax.MethodDefinition:
|
case Syntax.MethodDefinition:
|
||||||
info.node = node;
|
info.node = node;
|
||||||
info.name = nodeToValue(info.node);
|
info.name = nodeToValue(info.node);
|
||||||
info.type = info.node.type;
|
info.type = info.node.type;
|
||||||
info.paramnames = getParamNames(node.value);
|
info.paramnames = getParamNames(node.value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like "a: 0" in "var foo = {a: 0}"
|
// like "a: 0" in "var foo = {a: 0}"
|
||||||
case Syntax.Property:
|
case Syntax.Property:
|
||||||
info.node = node.value;
|
info.node = node.value;
|
||||||
info.name = nodeToValue(node.key);
|
info.name = nodeToValue(node.key);
|
||||||
info.value = nodeToValue(info.node);
|
info.value = nodeToValue(info.node);
|
||||||
|
|
||||||
// property names with unsafe characters must be quoted
|
// property names with unsafe characters must be quoted
|
||||||
if ( !/^[$_a-zA-Z0-9]*$/.test(info.name) ) {
|
if (!/^[$_a-zA-Z0-9]*$/.test(info.name)) {
|
||||||
info.name = `"${String(info.name).replace(/"/g, '\\"')}"`;
|
info.name = `"${String(info.name).replace(/"/g, '\\"')}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isAccessor(node) ) {
|
if (isAccessor(node)) {
|
||||||
info.type = nodeToValue(info.node);
|
info.type = nodeToValue(info.node);
|
||||||
info.paramnames = getParamNames(info.node);
|
info.paramnames = getParamNames(info.node);
|
||||||
}
|
} else {
|
||||||
else {
|
info.type = info.node.type;
|
||||||
info.type = info.node.type;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like "...bar" in: function foo(...bar) {}
|
// like "...bar" in: function foo(...bar) {}
|
||||||
case Syntax.RestElement:
|
case Syntax.RestElement:
|
||||||
info.node = node;
|
info.node = node;
|
||||||
info.name = nodeToValue(info.node.argument);
|
info.name = nodeToValue(info.node.argument);
|
||||||
info.type = info.node.type;
|
info.type = info.node.type;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// like: "var i = 0" (has init property)
|
// like: "var i = 0" (has init property)
|
||||||
// like: "var i" (no init property)
|
// like: "var i" (no init property)
|
||||||
case Syntax.VariableDeclarator:
|
case Syntax.VariableDeclarator:
|
||||||
info.node = node.init || node.id;
|
info.node = node.init || node.id;
|
||||||
info.name = node.id.name;
|
info.name = node.id.name;
|
||||||
|
|
||||||
if (node.init) {
|
if (node.init) {
|
||||||
info.type = info.node.type;
|
info.type = info.node.type;
|
||||||
info.value = nodeToValue(info.node);
|
info.value = nodeToValue(info.node);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
info.node = node;
|
info.node = node;
|
||||||
info.type = info.node.type;
|
info.type = info.node.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,96 +1,96 @@
|
|||||||
// TODO: docs
|
// TODO: docs
|
||||||
exports.Syntax = {
|
exports.Syntax = {
|
||||||
ArrayExpression: 'ArrayExpression',
|
ArrayExpression: 'ArrayExpression',
|
||||||
ArrayPattern: 'ArrayPattern',
|
ArrayPattern: 'ArrayPattern',
|
||||||
ArrowFunctionExpression: 'ArrowFunctionExpression',
|
ArrowFunctionExpression: 'ArrowFunctionExpression',
|
||||||
AssignmentExpression: 'AssignmentExpression',
|
AssignmentExpression: 'AssignmentExpression',
|
||||||
AssignmentPattern: 'AssignmentPattern',
|
AssignmentPattern: 'AssignmentPattern',
|
||||||
AwaitExpression: 'AwaitExpression',
|
AwaitExpression: 'AwaitExpression',
|
||||||
BigIntLiteral: 'BigIntLiteral',
|
BigIntLiteral: 'BigIntLiteral',
|
||||||
BinaryExpression: 'BinaryExpression',
|
BinaryExpression: 'BinaryExpression',
|
||||||
BindExpression: 'BindExpression',
|
BindExpression: 'BindExpression',
|
||||||
BlockStatement: 'BlockStatement',
|
BlockStatement: 'BlockStatement',
|
||||||
BreakStatement: 'BreakStatement',
|
BreakStatement: 'BreakStatement',
|
||||||
CallExpression: 'CallExpression',
|
CallExpression: 'CallExpression',
|
||||||
CatchClause: 'CatchClause',
|
CatchClause: 'CatchClause',
|
||||||
ClassBody: 'ClassBody',
|
ClassBody: 'ClassBody',
|
||||||
ClassDeclaration: 'ClassDeclaration',
|
ClassDeclaration: 'ClassDeclaration',
|
||||||
ClassExpression: 'ClassExpression',
|
ClassExpression: 'ClassExpression',
|
||||||
ClassPrivateProperty: 'ClassPrivateProperty',
|
ClassPrivateProperty: 'ClassPrivateProperty',
|
||||||
ClassProperty: 'ClassProperty',
|
ClassProperty: 'ClassProperty',
|
||||||
ComprehensionBlock: 'ComprehensionBlock',
|
ComprehensionBlock: 'ComprehensionBlock',
|
||||||
ComprehensionExpression: 'ComprehensionExpression',
|
ComprehensionExpression: 'ComprehensionExpression',
|
||||||
ConditionalExpression: 'ConditionalExpression',
|
ConditionalExpression: 'ConditionalExpression',
|
||||||
ContinueStatement: 'ContinueStatement',
|
ContinueStatement: 'ContinueStatement',
|
||||||
DebuggerStatement: 'DebuggerStatement',
|
DebuggerStatement: 'DebuggerStatement',
|
||||||
Decorator: 'Decorator',
|
Decorator: 'Decorator',
|
||||||
DoExpression: 'DoExpression',
|
DoExpression: 'DoExpression',
|
||||||
DoWhileStatement: 'DoWhileStatement',
|
DoWhileStatement: 'DoWhileStatement',
|
||||||
EmptyStatement: 'EmptyStatement',
|
EmptyStatement: 'EmptyStatement',
|
||||||
ExperimentalRestProperty: 'ExperimentalRestProperty',
|
ExperimentalRestProperty: 'ExperimentalRestProperty',
|
||||||
ExperimentalSpreadProperty: 'ExperimentalSpreadProperty',
|
ExperimentalSpreadProperty: 'ExperimentalSpreadProperty',
|
||||||
ExportAllDeclaration: 'ExportAllDeclaration',
|
ExportAllDeclaration: 'ExportAllDeclaration',
|
||||||
ExportDefaultDeclaration: 'ExportDefaultDeclaration',
|
ExportDefaultDeclaration: 'ExportDefaultDeclaration',
|
||||||
ExportDefaultSpecifier: 'ExportDefaultSpecifier',
|
ExportDefaultSpecifier: 'ExportDefaultSpecifier',
|
||||||
ExportNamedDeclaration: 'ExportNamedDeclaration',
|
ExportNamedDeclaration: 'ExportNamedDeclaration',
|
||||||
ExportNamespaceSpecifier: 'ExportNamespaceSpecifier',
|
ExportNamespaceSpecifier: 'ExportNamespaceSpecifier',
|
||||||
ExportSpecifier: 'ExportSpecifier',
|
ExportSpecifier: 'ExportSpecifier',
|
||||||
ExpressionStatement: 'ExpressionStatement',
|
ExpressionStatement: 'ExpressionStatement',
|
||||||
File: 'File',
|
File: 'File',
|
||||||
ForInStatement: 'ForInStatement',
|
ForInStatement: 'ForInStatement',
|
||||||
ForOfStatement: 'ForOfStatement',
|
ForOfStatement: 'ForOfStatement',
|
||||||
ForStatement: 'ForStatement',
|
ForStatement: 'ForStatement',
|
||||||
FunctionDeclaration: 'FunctionDeclaration',
|
FunctionDeclaration: 'FunctionDeclaration',
|
||||||
FunctionExpression: 'FunctionExpression',
|
FunctionExpression: 'FunctionExpression',
|
||||||
Identifier: 'Identifier',
|
Identifier: 'Identifier',
|
||||||
IfStatement: 'IfStatement',
|
IfStatement: 'IfStatement',
|
||||||
Import: 'Import',
|
Import: 'Import',
|
||||||
ImportDeclaration: 'ImportDeclaration',
|
ImportDeclaration: 'ImportDeclaration',
|
||||||
ImportDefaultSpecifier: 'ImportDefaultSpecifier',
|
ImportDefaultSpecifier: 'ImportDefaultSpecifier',
|
||||||
ImportNamespaceSpecifier: 'ImportNamespaceSpecifier',
|
ImportNamespaceSpecifier: 'ImportNamespaceSpecifier',
|
||||||
ImportSpecifier: 'ImportSpecifier',
|
ImportSpecifier: 'ImportSpecifier',
|
||||||
JSXAttribute: 'JSXAttribute',
|
JSXAttribute: 'JSXAttribute',
|
||||||
JSXClosingElement: 'JSXClosingElement',
|
JSXClosingElement: 'JSXClosingElement',
|
||||||
JSXElement: 'JSXElement',
|
JSXElement: 'JSXElement',
|
||||||
JSXEmptyExpression: 'JSXEmptyExpression',
|
JSXEmptyExpression: 'JSXEmptyExpression',
|
||||||
JSXExpressionContainer: 'JSXExpressionContainer',
|
JSXExpressionContainer: 'JSXExpressionContainer',
|
||||||
JSXIdentifier: 'JSXIdentifier',
|
JSXIdentifier: 'JSXIdentifier',
|
||||||
JSXMemberExpression: 'JSXMemberExpression',
|
JSXMemberExpression: 'JSXMemberExpression',
|
||||||
JSXNamespacedName: 'JSXNamespacedName',
|
JSXNamespacedName: 'JSXNamespacedName',
|
||||||
JSXOpeningElement: 'JSXOpeningElement',
|
JSXOpeningElement: 'JSXOpeningElement',
|
||||||
JSXSpreadAttribute: 'JSXSpreadAttribute',
|
JSXSpreadAttribute: 'JSXSpreadAttribute',
|
||||||
JSXText: 'JSXText',
|
JSXText: 'JSXText',
|
||||||
LabeledStatement: 'LabeledStatement',
|
LabeledStatement: 'LabeledStatement',
|
||||||
LetStatement: 'LetStatement',
|
LetStatement: 'LetStatement',
|
||||||
Literal: 'Literal',
|
Literal: 'Literal',
|
||||||
LogicalExpression: 'LogicalExpression',
|
LogicalExpression: 'LogicalExpression',
|
||||||
MemberExpression: 'MemberExpression',
|
MemberExpression: 'MemberExpression',
|
||||||
MetaProperty: 'MetaProperty',
|
MetaProperty: 'MetaProperty',
|
||||||
MethodDefinition: 'MethodDefinition',
|
MethodDefinition: 'MethodDefinition',
|
||||||
NewExpression: 'NewExpression',
|
NewExpression: 'NewExpression',
|
||||||
ObjectExpression: 'ObjectExpression',
|
ObjectExpression: 'ObjectExpression',
|
||||||
ObjectPattern: 'ObjectPattern',
|
ObjectPattern: 'ObjectPattern',
|
||||||
PrivateName: 'PrivateName',
|
PrivateName: 'PrivateName',
|
||||||
Program: 'Program',
|
Program: 'Program',
|
||||||
Property: 'Property',
|
Property: 'Property',
|
||||||
RestElement: 'RestElement',
|
RestElement: 'RestElement',
|
||||||
ReturnStatement: 'ReturnStatement',
|
ReturnStatement: 'ReturnStatement',
|
||||||
SequenceExpression: 'SequenceExpression',
|
SequenceExpression: 'SequenceExpression',
|
||||||
SpreadElement: 'SpreadElement',
|
SpreadElement: 'SpreadElement',
|
||||||
Super: 'Super',
|
Super: 'Super',
|
||||||
SwitchCase: 'SwitchCase',
|
SwitchCase: 'SwitchCase',
|
||||||
SwitchStatement: 'SwitchStatement',
|
SwitchStatement: 'SwitchStatement',
|
||||||
TaggedTemplateExpression: 'TaggedTemplateExpression',
|
TaggedTemplateExpression: 'TaggedTemplateExpression',
|
||||||
TemplateElement: 'TemplateElement',
|
TemplateElement: 'TemplateElement',
|
||||||
TemplateLiteral: 'TemplateLiteral',
|
TemplateLiteral: 'TemplateLiteral',
|
||||||
ThisExpression: 'ThisExpression',
|
ThisExpression: 'ThisExpression',
|
||||||
ThrowStatement: 'ThrowStatement',
|
ThrowStatement: 'ThrowStatement',
|
||||||
TryStatement: 'TryStatement',
|
TryStatement: 'TryStatement',
|
||||||
UnaryExpression: 'UnaryExpression',
|
UnaryExpression: 'UnaryExpression',
|
||||||
UpdateExpression: 'UpdateExpression',
|
UpdateExpression: 'UpdateExpression',
|
||||||
VariableDeclaration: 'VariableDeclaration',
|
VariableDeclaration: 'VariableDeclaration',
|
||||||
VariableDeclarator: 'VariableDeclarator',
|
VariableDeclarator: 'VariableDeclarator',
|
||||||
WhileStatement: 'WhileStatement',
|
WhileStatement: 'WhileStatement',
|
||||||
WithStatement: 'WithStatement',
|
WithStatement: 'WithStatement',
|
||||||
YieldExpression: 'YieldExpression'
|
YieldExpression: 'YieldExpression',
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,31 +1,31 @@
|
|||||||
const parse = require('../../index');
|
const parse = require('../../index');
|
||||||
|
|
||||||
describe('@jsdoc/parse', () => {
|
describe('@jsdoc/parse', () => {
|
||||||
it('is an object', () => {
|
it('is an object', () => {
|
||||||
expect(parse).toBeObject();
|
expect(parse).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('AstBuilder', () => {
|
||||||
|
it('is lib/ast-builder.AstBuilder', () => {
|
||||||
|
const { AstBuilder } = require('../../lib/ast-builder');
|
||||||
|
|
||||||
|
expect(parse.AstBuilder).toBe(AstBuilder);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('AstBuilder', () => {
|
describe('astNode', () => {
|
||||||
it('is lib/ast-builder.AstBuilder', () => {
|
it('is lib/ast-node', () => {
|
||||||
const { AstBuilder } = require('../../lib/ast-builder');
|
const astNode = require('../../lib/ast-node');
|
||||||
|
|
||||||
expect(parse.AstBuilder).toBe(AstBuilder);
|
expect(parse.astNode).toBe(astNode);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('astNode', () => {
|
describe('Syntax', () => {
|
||||||
it('is lib/ast-node', () => {
|
it('is lib/syntax.Syntax', () => {
|
||||||
const astNode = require('../../lib/ast-node');
|
const { Syntax } = require('../../lib/syntax');
|
||||||
|
|
||||||
expect(parse.astNode).toBe(astNode);
|
expect(parse.Syntax).toBe(Syntax);
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Syntax', () => {
|
|
||||||
it('is lib/syntax.Syntax', () => {
|
|
||||||
const { Syntax } = require('../../lib/syntax');
|
|
||||||
|
|
||||||
expect(parse.Syntax).toBe(Syntax);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,41 +1,41 @@
|
|||||||
/* global jsdoc */
|
/* global jsdoc */
|
||||||
describe('@jsdoc/parse/lib/ast-builder', () => {
|
describe('@jsdoc/parse/lib/ast-builder', () => {
|
||||||
const astBuilder = require('../../../lib/ast-builder');
|
const astBuilder = require('../../../lib/ast-builder');
|
||||||
|
|
||||||
it('is an object', () => {
|
it('is an object', () => {
|
||||||
expect(astBuilder).toBeObject();
|
expect(astBuilder).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exports an AstBuilder class', () => {
|
||||||
|
expect(astBuilder.AstBuilder).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exports a parserOptions object', () => {
|
||||||
|
expect(astBuilder.parserOptions).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('AstBuilder', () => {
|
||||||
|
const { AstBuilder } = astBuilder;
|
||||||
|
|
||||||
|
// TODO: more tests
|
||||||
|
it('has a "build" static method', () => {
|
||||||
|
expect(AstBuilder.build).toBeFunction();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('exports an AstBuilder class', () => {
|
describe('build', () => {
|
||||||
expect(astBuilder.AstBuilder).toBeFunction();
|
// TODO: more tests
|
||||||
|
it('logs (not throws) an error when a file cannot be parsed', () => {
|
||||||
|
function parse() {
|
||||||
|
AstBuilder.build('qwerty!!!!!', 'bad.js');
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(parse).not.toThrow();
|
||||||
|
expect(jsdoc.didLog(parse, 'error')).toBeTrue();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('exports a parserOptions object', () => {
|
describe('parserOptions', () => {
|
||||||
expect(astBuilder.parserOptions).toBeObject();
|
// TODO: tests
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('AstBuilder', () => {
|
|
||||||
const { AstBuilder } = astBuilder;
|
|
||||||
|
|
||||||
// TODO: more tests
|
|
||||||
it('has a "build" static method', () => {
|
|
||||||
expect(AstBuilder.build).toBeFunction();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('build', () => {
|
|
||||||
// TODO: more tests
|
|
||||||
it('logs (not throws) an error when a file cannot be parsed', () => {
|
|
||||||
function parse() {
|
|
||||||
AstBuilder.build('qwerty!!!!!', 'bad.js');
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(parse).not.toThrow();
|
|
||||||
expect(jsdoc.didLog(parse, 'error')).toBeTrue();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('parserOptions', () => {
|
|
||||||
// TODO: tests
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,13 @@
|
|||||||
describe('@jsdoc/parse.Syntax', () => {
|
describe('@jsdoc/parse.Syntax', () => {
|
||||||
const { Syntax } = require('../../../index');
|
const { Syntax } = require('../../../index');
|
||||||
|
|
||||||
it('is an object', () => {
|
it('is an object', () => {
|
||||||
expect(Syntax).toBeObject();
|
expect(Syntax).toBeObject();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has values identical to their keys', () => {
|
it('has values identical to their keys', () => {
|
||||||
for (const key of Object.keys(Syntax)) {
|
for (const key of Object.keys(Syntax)) {
|
||||||
expect(key).toBe(Syntax[key]);
|
expect(key).toBe(Syntax[key]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
14
packages/jsdoc-prettier-config/.npmignore
Normal file
14
packages/jsdoc-prettier-config/.npmignore
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
.editorconfig
|
||||||
|
.eslintignore
|
||||||
|
.eslintrc.js
|
||||||
|
.gitignore
|
||||||
|
.github/
|
||||||
|
.renovaterc.json
|
||||||
|
.travis.yml
|
||||||
|
CHANGES.md
|
||||||
|
CODE_OF_CONDUCT.md
|
||||||
|
CONTRIBUTING.md
|
||||||
|
gulpfile.js
|
||||||
|
lerna.json
|
||||||
|
packages/
|
||||||
|
test/
|
||||||
202
packages/jsdoc-prettier-config/LICENSE
Normal file
202
packages/jsdoc-prettier-config/LICENSE
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
3
packages/jsdoc-prettier-config/README.md
Normal file
3
packages/jsdoc-prettier-config/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# `@jsdoc/prettier-config`
|
||||||
|
|
||||||
|
A Prettier (https://prettier.io/) configuration for JSDoc.
|
||||||
5
packages/jsdoc-prettier-config/index.js
Normal file
5
packages/jsdoc-prettier-config/index.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// https://prettier.io/docs/en/options.html
|
||||||
|
module.exports = {
|
||||||
|
printWidth: 100,
|
||||||
|
singleQuote: true,
|
||||||
|
};
|
||||||
31
packages/jsdoc-prettier-config/package.json
Normal file
31
packages/jsdoc-prettier-config/package.json
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "@jsdoc/prettier-config",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "A Prettier (https://prettier.io/) configuration for JSDoc.",
|
||||||
|
"keywords": [
|
||||||
|
"prettier",
|
||||||
|
"jsdoc"
|
||||||
|
],
|
||||||
|
"author": "Jeff Williams <jeffrey.l.williams@gmail.com>",
|
||||||
|
"homepage": "https://github.com/jsdoc/jsdoc",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint-config-prettier": "^8.3.0",
|
||||||
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
|
"prettier": "^2.4.1"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/jsdoc/jsdoc.git"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/jsdoc/jsdoc/issues"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,6 +2,6 @@ const inline = require('./lib/inline');
|
|||||||
const type = require('./lib/type');
|
const type = require('./lib/type');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
inline,
|
inline,
|
||||||
type
|
type,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -45,7 +45,7 @@
|
|||||||
* @returns {RegExp} A regular expression that matches the requested inline tag.
|
* @returns {RegExp} A regular expression that matches the requested inline tag.
|
||||||
*/
|
*/
|
||||||
function regExpFactory(tagName = '\\S+', prefix = '', suffix = '') {
|
function regExpFactory(tagName = '\\S+', prefix = '', suffix = '') {
|
||||||
return new RegExp(`${prefix}\\{@${tagName}\\s+((?:.|\n)+?)\\}${suffix}`, 'i');
|
return new RegExp(`${prefix}\\{@${tagName}\\s+((?:.|\n)+?)\\}${suffix}`, 'i');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,43 +70,43 @@ exports.isInlineTag = (string, tagName) => regExpFactory(tagName, '^', '$').test
|
|||||||
* @return {module:@jsdoc/tag.inline.InlineTagResult} The updated string, as well as information
|
* @return {module:@jsdoc/tag.inline.InlineTagResult} The updated string, as well as information
|
||||||
* about the inline tags that were found.
|
* about the inline tags that were found.
|
||||||
*/
|
*/
|
||||||
const replaceInlineTags = exports.replaceInlineTags = (string, replacers) => {
|
const replaceInlineTags = (exports.replaceInlineTags = (string, replacers) => {
|
||||||
const tagInfo = [];
|
const tagInfo = [];
|
||||||
|
|
||||||
function replaceMatch(replacer, tag, match, text) {
|
function replaceMatch(replacer, tag, match, text) {
|
||||||
const matchedTag = {
|
const matchedTag = {
|
||||||
completeTag: match,
|
completeTag: match,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
text: text
|
text: text,
|
||||||
};
|
|
||||||
|
|
||||||
tagInfo.push(matchedTag);
|
|
||||||
|
|
||||||
return replacer(string, matchedTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
string = string || '';
|
|
||||||
|
|
||||||
Object.keys(replacers).forEach(replacer => {
|
|
||||||
const tagRegExp = regExpFactory(replacer);
|
|
||||||
let matches;
|
|
||||||
let previousString;
|
|
||||||
|
|
||||||
// call the replacer once for each match
|
|
||||||
do {
|
|
||||||
matches = tagRegExp.exec(string);
|
|
||||||
if (matches) {
|
|
||||||
previousString = string;
|
|
||||||
string = replaceMatch(replacers[replacer], replacer, matches[0], matches[1]);
|
|
||||||
}
|
|
||||||
} while (matches && previousString !== string);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
tags: tagInfo,
|
|
||||||
newString: string.trim()
|
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
tagInfo.push(matchedTag);
|
||||||
|
|
||||||
|
return replacer(string, matchedTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
string = string || '';
|
||||||
|
|
||||||
|
Object.keys(replacers).forEach((replacer) => {
|
||||||
|
const tagRegExp = regExpFactory(replacer);
|
||||||
|
let matches;
|
||||||
|
let previousString;
|
||||||
|
|
||||||
|
// call the replacer once for each match
|
||||||
|
do {
|
||||||
|
matches = tagRegExp.exec(string);
|
||||||
|
if (matches) {
|
||||||
|
previousString = string;
|
||||||
|
string = replaceMatch(replacers[replacer], replacer, matches[0], matches[1]);
|
||||||
|
}
|
||||||
|
} while (matches && previousString !== string);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
tags: tagInfo,
|
||||||
|
newString: string.trim(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace all instances of an inline tag with other text.
|
* Replace all instances of an inline tag with other text.
|
||||||
@ -118,13 +118,13 @@ const replaceInlineTags = exports.replaceInlineTags = (string, replacers) => {
|
|||||||
* @return {module:@jsdoc/tag.inline.InlineTagResult} The updated string, as well as information
|
* @return {module:@jsdoc/tag.inline.InlineTagResult} The updated string, as well as information
|
||||||
* about the inline tags that were found.
|
* about the inline tags that were found.
|
||||||
*/
|
*/
|
||||||
const replaceInlineTag = exports.replaceInlineTag = (string, tag, replacer) => {
|
const replaceInlineTag = (exports.replaceInlineTag = (string, tag, replacer) => {
|
||||||
const replacers = {};
|
const replacers = {};
|
||||||
|
|
||||||
replacers[tag] = replacer;
|
replacers[tag] = replacer;
|
||||||
|
|
||||||
return replaceInlineTags(string, replacers);
|
return replaceInlineTags(string, replacers);
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract inline tags from a string, replacing them with an empty string.
|
* Extract inline tags from a string, replacing them with an empty string.
|
||||||
@ -135,5 +135,4 @@ const replaceInlineTag = exports.replaceInlineTag = (string, tag, replacer) => {
|
|||||||
* about the inline tags that were found.
|
* about the inline tags that were found.
|
||||||
*/
|
*/
|
||||||
exports.extractInlineTag = (string, tag) =>
|
exports.extractInlineTag = (string, tag) =>
|
||||||
replaceInlineTag(string, tag, (str, {completeTag}) =>
|
replaceInlineTag(string, tag, (str, { completeTag }) => str.replace(completeTag, ''));
|
||||||
str.replace(completeTag, ''));
|
|
||||||
|
|||||||
@ -18,8 +18,7 @@ const { splitNameAndDescription } = require('@jsdoc/core').name;
|
|||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
function unescapeBraces(text) {
|
function unescapeBraces(text) {
|
||||||
return text.replace(/\\\{/g, '{')
|
return text.replace(/\\\{/g, '{').replace(/\\\}/g, '}');
|
||||||
.replace(/\\\}/g, '}');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,87 +29,87 @@ function unescapeBraces(text) {
|
|||||||
* @return {module:@jsdoc/tag.type.TypeExpressionInfo} The type expression and updated tag text.
|
* @return {module:@jsdoc/tag.type.TypeExpressionInfo} The type expression and updated tag text.
|
||||||
*/
|
*/
|
||||||
function extractTypeExpression(string) {
|
function extractTypeExpression(string) {
|
||||||
let completeExpression;
|
let completeExpression;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
let position = 0;
|
let position = 0;
|
||||||
let expression = '';
|
let expression = '';
|
||||||
const startIndex = string.search(/\{[^@]/);
|
const startIndex = string.search(/\{[^@]/);
|
||||||
let textStartIndex;
|
let textStartIndex;
|
||||||
|
|
||||||
if (startIndex !== -1) {
|
if (startIndex !== -1) {
|
||||||
// advance to the first character in the type expression
|
// advance to the first character in the type expression
|
||||||
position = textStartIndex = startIndex + 1;
|
position = textStartIndex = startIndex + 1;
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
while (position < string.length) {
|
while (position < string.length) {
|
||||||
switch (string[position]) {
|
switch (string[position]) {
|
||||||
case '\\':
|
case '\\':
|
||||||
// backslash is an escape character, so skip the next character
|
// backslash is an escape character, so skip the next character
|
||||||
position++;
|
position++;
|
||||||
break;
|
break;
|
||||||
case '{':
|
case '{':
|
||||||
count++;
|
count++;
|
||||||
break;
|
break;
|
||||||
case '}':
|
case '}':
|
||||||
count--;
|
count--;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
completeExpression = string.slice(startIndex, position + 1);
|
completeExpression = string.slice(startIndex, position + 1);
|
||||||
expression = string.slice(textStartIndex, position).trim();
|
expression = string.slice(textStartIndex, position).trim();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
position++;
|
position++;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string = completeExpression ? string.replace(completeExpression, '') : string;
|
string = completeExpression ? string.replace(completeExpression, '') : string;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
expression: unescapeBraces(expression),
|
expression: unescapeBraces(expression),
|
||||||
newString: string.trim()
|
newString: string.trim(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
function getTagInfo(tagValue, canHaveName, canHaveType) {
|
function getTagInfo(tagValue, canHaveName, canHaveType) {
|
||||||
let name = '';
|
let name = '';
|
||||||
let typeExpression = '';
|
let typeExpression = '';
|
||||||
let text = tagValue;
|
let text = tagValue;
|
||||||
let expressionAndText;
|
let expressionAndText;
|
||||||
let nameAndDescription;
|
let nameAndDescription;
|
||||||
let typeOverride;
|
let typeOverride;
|
||||||
|
|
||||||
if (canHaveType) {
|
if (canHaveType) {
|
||||||
expressionAndText = extractTypeExpression(text);
|
expressionAndText = extractTypeExpression(text);
|
||||||
typeExpression = expressionAndText.expression;
|
typeExpression = expressionAndText.expression;
|
||||||
text = expressionAndText.newString;
|
text = expressionAndText.newString;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canHaveName) {
|
||||||
|
nameAndDescription = splitNameAndDescription(text);
|
||||||
|
name = nameAndDescription.name;
|
||||||
|
text = nameAndDescription.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
// an inline @type tag, like {@type Foo}, overrides the type expression
|
||||||
|
if (canHaveType) {
|
||||||
|
typeOverride = extractInlineTag(text, 'type');
|
||||||
|
if (typeOverride.tags && typeOverride.tags[0]) {
|
||||||
|
typeExpression = typeOverride.tags[0].text;
|
||||||
}
|
}
|
||||||
|
text = typeOverride.newString;
|
||||||
|
}
|
||||||
|
|
||||||
if (canHaveName) {
|
return {
|
||||||
nameAndDescription = splitNameAndDescription(text);
|
name: name,
|
||||||
name = nameAndDescription.name;
|
typeExpression: typeExpression,
|
||||||
text = nameAndDescription.description;
|
text: text,
|
||||||
}
|
};
|
||||||
|
|
||||||
// an inline @type tag, like {@type Foo}, overrides the type expression
|
|
||||||
if (canHaveType) {
|
|
||||||
typeOverride = extractInlineTag(text, 'type');
|
|
||||||
if (typeOverride.tags && typeOverride.tags[0]) {
|
|
||||||
typeExpression = typeOverride.tags[0].text;
|
|
||||||
}
|
|
||||||
text = typeOverride.newString;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: name,
|
|
||||||
typeExpression: typeExpression,
|
|
||||||
text: text
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,80 +141,80 @@ function getTagInfo(tagValue, canHaveName, canHaveType) {
|
|||||||
* @return {module:@jsdoc/tag.type.TagInfo} Updated information from the tag.
|
* @return {module:@jsdoc/tag.type.TagInfo} Updated information from the tag.
|
||||||
*/
|
*/
|
||||||
function parseName(tagInfo) {
|
function parseName(tagInfo) {
|
||||||
// like '[foo]' or '[ foo ]' or '[foo=bar]' or '[ foo=bar ]' or '[ foo = bar ]'
|
// like '[foo]' or '[ foo ]' or '[foo=bar]' or '[ foo=bar ]' or '[ foo = bar ]'
|
||||||
// or 'foo=bar' or 'foo = bar'
|
// or 'foo=bar' or 'foo = bar'
|
||||||
if ( /^(\[)?\s*(.+?)\s*(\])?$/.test(tagInfo.name) ) {
|
if (/^(\[)?\s*(.+?)\s*(\])?$/.test(tagInfo.name)) {
|
||||||
tagInfo.name = RegExp.$2;
|
tagInfo.name = RegExp.$2;
|
||||||
// were the "optional" brackets present?
|
// were the "optional" brackets present?
|
||||||
if (RegExp.$1 && RegExp.$3) {
|
if (RegExp.$1 && RegExp.$3) {
|
||||||
tagInfo.optional = true;
|
tagInfo.optional = true;
|
||||||
}
|
|
||||||
|
|
||||||
// like 'foo=bar' or 'foo = bar'
|
|
||||||
if ( /^(.+?)\s*=\s*(.+)$/.test(tagInfo.name) ) {
|
|
||||||
tagInfo.name = RegExp.$1;
|
|
||||||
tagInfo.defaultvalue = cast(RegExp.$2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tagInfo;
|
// like 'foo=bar' or 'foo = bar'
|
||||||
|
if (/^(.+?)\s*=\s*(.+)$/.test(tagInfo.name)) {
|
||||||
|
tagInfo.name = RegExp.$1;
|
||||||
|
tagInfo.defaultvalue = cast(RegExp.$2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tagInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
function getTypeStrings(parsedType, isOutermostType) {
|
function getTypeStrings(parsedType, isOutermostType) {
|
||||||
let applications;
|
let applications;
|
||||||
let typeString;
|
let typeString;
|
||||||
|
|
||||||
let types = [];
|
let types = [];
|
||||||
|
|
||||||
const TYPES = catharsis.Types;
|
const TYPES = catharsis.Types;
|
||||||
|
|
||||||
switch (parsedType.type) {
|
switch (parsedType.type) {
|
||||||
case TYPES.AllLiteral:
|
case TYPES.AllLiteral:
|
||||||
types.push('*');
|
types.push('*');
|
||||||
break;
|
break;
|
||||||
case TYPES.FunctionType:
|
case TYPES.FunctionType:
|
||||||
types.push('function');
|
types.push('function');
|
||||||
break;
|
break;
|
||||||
case TYPES.NameExpression:
|
case TYPES.NameExpression:
|
||||||
types.push(parsedType.name);
|
types.push(parsedType.name);
|
||||||
break;
|
break;
|
||||||
case TYPES.NullLiteral:
|
case TYPES.NullLiteral:
|
||||||
types.push('null');
|
types.push('null');
|
||||||
break;
|
break;
|
||||||
case TYPES.RecordType:
|
case TYPES.RecordType:
|
||||||
types.push('Object');
|
types.push('Object');
|
||||||
break;
|
break;
|
||||||
case TYPES.TypeApplication:
|
case TYPES.TypeApplication:
|
||||||
// if this is the outermost type, we strip the modifiers; otherwise, we keep them
|
// if this is the outermost type, we strip the modifiers; otherwise, we keep them
|
||||||
if (isOutermostType) {
|
if (isOutermostType) {
|
||||||
applications = parsedType.applications.map(application =>
|
applications = parsedType.applications
|
||||||
catharsis.stringify(application)).join(', ');
|
.map((application) => catharsis.stringify(application))
|
||||||
typeString = `${getTypeStrings(parsedType.expression)[0]}.<${applications}>`;
|
.join(', ');
|
||||||
|
typeString = `${getTypeStrings(parsedType.expression)[0]}.<${applications}>`;
|
||||||
|
|
||||||
types.push(typeString);
|
types.push(typeString);
|
||||||
}
|
} else {
|
||||||
else {
|
types.push(catharsis.stringify(parsedType));
|
||||||
types.push( catharsis.stringify(parsedType) );
|
}
|
||||||
}
|
break;
|
||||||
break;
|
case TYPES.TypeUnion:
|
||||||
case TYPES.TypeUnion:
|
parsedType.elements.forEach((element) => {
|
||||||
parsedType.elements.forEach(element => {
|
types = types.concat(getTypeStrings(element));
|
||||||
types = types.concat( getTypeStrings(element) );
|
});
|
||||||
});
|
break;
|
||||||
break;
|
case TYPES.UndefinedLiteral:
|
||||||
case TYPES.UndefinedLiteral:
|
types.push('undefined');
|
||||||
types.push('undefined');
|
break;
|
||||||
break;
|
case TYPES.UnknownLiteral:
|
||||||
case TYPES.UnknownLiteral:
|
types.push('?');
|
||||||
types.push('?');
|
break;
|
||||||
break;
|
default:
|
||||||
default:
|
// this shouldn't happen
|
||||||
// this shouldn't happen
|
throw new Error(`unrecognized type ${parsedType.type} in parsed type: ${parsedType}`);
|
||||||
throw new Error(`unrecognized type ${parsedType.type} in parsed type: ${parsedType}`);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -227,40 +226,39 @@ function getTypeStrings(parsedType, isOutermostType) {
|
|||||||
* @return {module:@jsdoc/tag.type.TagInfo} Updated information from the tag.
|
* @return {module:@jsdoc/tag.type.TagInfo} Updated information from the tag.
|
||||||
*/
|
*/
|
||||||
function parseTypeExpression(tagInfo) {
|
function parseTypeExpression(tagInfo) {
|
||||||
let parsedType;
|
let parsedType;
|
||||||
|
|
||||||
// don't try to parse empty type expressions
|
|
||||||
if (!tagInfo.typeExpression) {
|
|
||||||
return tagInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
parsedType = catharsis.parse(tagInfo.typeExpression, {
|
|
||||||
jsdoc: true,
|
|
||||||
useCache: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
// always re-throw so the caller has a chance to report which file was bad
|
|
||||||
throw new Error(`Invalid type expression "${tagInfo.typeExpression}": ${e.message}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
tagInfo.type = tagInfo.type.concat( getTypeStrings(parsedType, true) );
|
|
||||||
tagInfo.parsedType = parsedType;
|
|
||||||
|
|
||||||
// Catharsis and JSDoc use the same names for 'optional' and 'nullable'...
|
|
||||||
['optional', 'nullable'].forEach(key => {
|
|
||||||
if (parsedType[key] !== null && parsedType[key] !== undefined) {
|
|
||||||
tagInfo[key] = parsedType[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ...but not 'variable'.
|
|
||||||
if (parsedType.repeatable !== null && parsedType.repeatable !== undefined) {
|
|
||||||
tagInfo.variable = parsedType.repeatable;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// don't try to parse empty type expressions
|
||||||
|
if (!tagInfo.typeExpression) {
|
||||||
return tagInfo;
|
return tagInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
parsedType = catharsis.parse(tagInfo.typeExpression, {
|
||||||
|
jsdoc: true,
|
||||||
|
useCache: false,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// always re-throw so the caller has a chance to report which file was bad
|
||||||
|
throw new Error(`Invalid type expression "${tagInfo.typeExpression}": ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
tagInfo.type = tagInfo.type.concat(getTypeStrings(parsedType, true));
|
||||||
|
tagInfo.parsedType = parsedType;
|
||||||
|
|
||||||
|
// Catharsis and JSDoc use the same names for 'optional' and 'nullable'...
|
||||||
|
['optional', 'nullable'].forEach((key) => {
|
||||||
|
if (parsedType[key] !== null && parsedType[key] !== undefined) {
|
||||||
|
tagInfo[key] = parsedType[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ...but not 'variable'.
|
||||||
|
if (parsedType.repeatable !== null && parsedType.repeatable !== undefined) {
|
||||||
|
tagInfo.variable = parsedType.repeatable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tagInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: allow users to add/remove type parsers (perhaps via plugins)
|
// TODO: allow users to add/remove type parsers (perhaps via plugins)
|
||||||
@ -278,23 +276,23 @@ const typeParsers = [parseName, parseTypeExpression];
|
|||||||
* @throws {Error} Thrown if a type expression cannot be parsed.
|
* @throws {Error} Thrown if a type expression cannot be parsed.
|
||||||
*/
|
*/
|
||||||
exports.parse = (tagValue, canHaveName, canHaveType) => {
|
exports.parse = (tagValue, canHaveName, canHaveType) => {
|
||||||
let tagInfo;
|
let tagInfo;
|
||||||
|
|
||||||
if (typeof tagValue !== 'string') {
|
if (typeof tagValue !== 'string') {
|
||||||
tagValue = '';
|
tagValue = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
tagInfo = getTagInfo(tagValue, canHaveName, canHaveType);
|
tagInfo = getTagInfo(tagValue, canHaveName, canHaveType);
|
||||||
tagInfo.type = tagInfo.type || [];
|
tagInfo.type = tagInfo.type || [];
|
||||||
|
|
||||||
typeParsers.forEach(parser => {
|
typeParsers.forEach((parser) => {
|
||||||
tagInfo = parser(tagInfo);
|
tagInfo = parser(tagInfo);
|
||||||
});
|
});
|
||||||
|
|
||||||
// if we wanted a type, but the parsers didn't add any type names, use the type expression
|
// if we wanted a type, but the parsers didn't add any type names, use the type expression
|
||||||
if (canHaveType && !tagInfo.type.length && tagInfo.typeExpression) {
|
if (canHaveType && !tagInfo.type.length && tagInfo.typeExpression) {
|
||||||
tagInfo.type = [tagInfo.typeExpression];
|
tagInfo.type = [tagInfo.typeExpression];
|
||||||
}
|
}
|
||||||
|
|
||||||
return tagInfo;
|
return tagInfo;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,23 +1,23 @@
|
|||||||
const tag = require('../../index');
|
const tag = require('../../index');
|
||||||
|
|
||||||
describe('@jsdoc/tag', () => {
|
describe('@jsdoc/tag', () => {
|
||||||
it('is an object', () => {
|
it('is an object', () => {
|
||||||
expect(tag).toBeObject();
|
expect(tag).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('inline', () => {
|
||||||
|
it('is lib/inline', () => {
|
||||||
|
const inline = require('../../lib/inline');
|
||||||
|
|
||||||
|
expect(tag.inline).toBe(inline);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('inline', () => {
|
describe('type', () => {
|
||||||
it('is lib/inline', () => {
|
it('is lib/type', () => {
|
||||||
const inline = require('../../lib/inline');
|
const type = require('../../lib/type');
|
||||||
|
|
||||||
expect(tag.inline).toBe(inline);
|
expect(tag.type).toBe(type);
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('type', () => {
|
|
||||||
it('is lib/type', () => {
|
|
||||||
const type = require('../../lib/type');
|
|
||||||
|
|
||||||
expect(tag.type).toBe(type);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,256 +1,256 @@
|
|||||||
describe('@jsdoc/tag/lib/inline', () => {
|
describe('@jsdoc/tag/lib/inline', () => {
|
||||||
const inline = require('../../../lib/inline');
|
const inline = require('../../../lib/inline');
|
||||||
|
|
||||||
it('is an object', () => {
|
it('is an object', () => {
|
||||||
expect(inline).toBeObject();
|
expect(inline).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exports an isInlineTag function', () => {
|
||||||
|
expect(inline.isInlineTag).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exports a replaceInlineTag function', () => {
|
||||||
|
expect(inline.replaceInlineTag).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exports an extractInlineTag function', () => {
|
||||||
|
expect(inline.extractInlineTag).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isInlineTag', () => {
|
||||||
|
const isInlineTag = inline.isInlineTag;
|
||||||
|
|
||||||
|
it('identifies an inline tag', () => {
|
||||||
|
expect(isInlineTag('{@mytag hooray}', 'mytag')).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('exports an isInlineTag function', () => {
|
it('identifies when something is not an inline tag', () => {
|
||||||
expect(inline.isInlineTag).toBeFunction();
|
expect(isInlineTag('mytag hooray', 'mytag')).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('exports a replaceInlineTag function', () => {
|
it('reports that a string containing an inline tag is not an inline tag', () => {
|
||||||
expect(inline.replaceInlineTag).toBeFunction();
|
expect(isInlineTag('this is {@mytag hooray}', 'mytag')).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('exports an extractInlineTag function', () => {
|
it('allows any inline tag by default', () => {
|
||||||
expect(inline.extractInlineTag).toBeFunction();
|
expect(isInlineTag('{@anyoldtag will do}')).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isInlineTag', () => {
|
it('identifies things that are not inline tags when a tag name is not provided', () => {
|
||||||
const isInlineTag = inline.isInlineTag;
|
expect(isInlineTag('mytag hooray')).toBeFalse();
|
||||||
|
|
||||||
it('identifies an inline tag', () => {
|
|
||||||
expect(isInlineTag('{@mytag hooray}', 'mytag')).toBeTrue();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('identifies when something is not an inline tag', () => {
|
|
||||||
expect(isInlineTag('mytag hooray', 'mytag')).toBeFalse();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('reports that a string containing an inline tag is not an inline tag', () => {
|
|
||||||
expect(isInlineTag('this is {@mytag hooray}', 'mytag')).toBeFalse();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('allows any inline tag by default', () => {
|
|
||||||
expect(isInlineTag('{@anyoldtag will do}')).toBeTrue();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('identifies things that are not inline tags when a tag name is not provided', () => {
|
|
||||||
expect(isInlineTag('mytag hooray')).toBeFalse();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('allows regexp characters in the tag name', () => {
|
|
||||||
expect( isInlineTag('{@mytags hooray}', 'mytag\\S') ).toBeTrue();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns false (rather than throwing) with invalid input', () => {
|
|
||||||
function badInput() {
|
|
||||||
return isInlineTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(badInput).not.toThrow();
|
|
||||||
expect(badInput()).toBeFalse();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('replaceInlineTag', () => {
|
it('allows regexp characters in the tag name', () => {
|
||||||
it('throws if the tag is matched and the replacer is invalid', () => {
|
expect(isInlineTag('{@mytags hooray}', 'mytag\\S')).toBeTrue();
|
||||||
function badReplacerUndefined() {
|
|
||||||
inline.replaceInlineTag('{@foo tag}', 'foo');
|
|
||||||
}
|
|
||||||
|
|
||||||
function badReplacerString() {
|
|
||||||
inline.replaceInlineTag('{@foo tag}', 'foo', 'hello');
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(badReplacerUndefined).toThrow();
|
|
||||||
expect(badReplacerString).toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not find anything if there is no text in braces', () => {
|
|
||||||
const replacer = jasmine.createSpy('replacer');
|
|
||||||
|
|
||||||
inline.replaceInlineTag('braceless text', 'foo', replacer);
|
|
||||||
|
|
||||||
expect(replacer).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('copes with bad escapement at the end of the string', () => {
|
|
||||||
const replacer = jasmine.createSpy('replacer');
|
|
||||||
|
|
||||||
inline.replaceInlineTag('bad {@foo escapement \\', 'foo', replacer);
|
|
||||||
|
|
||||||
expect(replacer).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('works if the tag is the entire string', () => {
|
|
||||||
function replacer(string, {completeTag, text}) {
|
|
||||||
expect(string).toBe('{@foo text in braces}');
|
|
||||||
expect(completeTag).toBe('{@foo text in braces}');
|
|
||||||
expect(text).toBe('text in braces');
|
|
||||||
|
|
||||||
return completeTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = inline.replaceInlineTag('{@foo text in braces}', 'foo',
|
|
||||||
replacer);
|
|
||||||
|
|
||||||
expect(result.tags[0]).toBeObject();
|
|
||||||
expect(result.tags[0].tag).toBe('foo');
|
|
||||||
expect(result.tags[0].text).toBe('text in braces');
|
|
||||||
expect(result.newString).toBe('{@foo text in braces}');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('works if the tag is at the beginning of the string', () => {
|
|
||||||
function replacer(string, {completeTag, text}) {
|
|
||||||
expect(string).toBe('{@foo test string} ahoy');
|
|
||||||
expect(completeTag).toBe('{@foo test string}');
|
|
||||||
expect(text).toBe('test string');
|
|
||||||
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = inline.replaceInlineTag('{@foo test string} ahoy', 'foo',
|
|
||||||
replacer);
|
|
||||||
|
|
||||||
expect(result.tags[0]).toBeObject();
|
|
||||||
expect(result.tags[0].tag).toBe('foo');
|
|
||||||
expect(result.tags[0].text).toBe('test string');
|
|
||||||
expect(result.newString).toBe('{@foo test string} ahoy');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('works if the tag is in the middle of the string', () => {
|
|
||||||
function replacer(string, {completeTag, text}) {
|
|
||||||
expect(string).toBe('a {@foo test string} yay');
|
|
||||||
expect(completeTag).toBe('{@foo test string}');
|
|
||||||
expect(text).toBe('test string');
|
|
||||||
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = inline.replaceInlineTag('a {@foo test string} yay', 'foo',
|
|
||||||
replacer);
|
|
||||||
|
|
||||||
expect(result.tags[0]).toBeObject();
|
|
||||||
expect(result.tags[0].tag).toBe('foo');
|
|
||||||
expect(result.tags[0].text).toBe('test string');
|
|
||||||
expect(result.newString).toBe('a {@foo test string} yay');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('works if the tag is at the end of the string', () => {
|
|
||||||
function replacer(string, {completeTag, text}) {
|
|
||||||
expect(string).toBe('a {@foo test string}');
|
|
||||||
expect(completeTag).toBe('{@foo test string}');
|
|
||||||
expect(text).toBe('test string');
|
|
||||||
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = inline.replaceInlineTag('a {@foo test string}', 'foo', replacer);
|
|
||||||
|
|
||||||
expect(result.tags[0]).toBeObject();
|
|
||||||
expect(result.tags[0].tag).toBe('foo');
|
|
||||||
expect(result.tags[0].text).toBe('test string');
|
|
||||||
expect(result.newString).toBe('a {@foo test string}');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('replaces the string with the specified value', () => {
|
|
||||||
function replacer() {
|
|
||||||
return 'REPLACED!';
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = inline.replaceInlineTag('a {@foo test string}', 'foo', replacer);
|
|
||||||
|
|
||||||
expect(result.newString).toBe('REPLACED!');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('processes all occurrences of a tag', () => {
|
|
||||||
function replacer(string, {completeTag}) {
|
|
||||||
return string.replace(completeTag, 'stuff');
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = inline.replaceInlineTag('some {@foo text} with multiple ' +
|
|
||||||
'{@foo tags}, {@foo like} {@foo this}', 'foo', replacer);
|
|
||||||
|
|
||||||
expect(result.tags.length).toBe(4);
|
|
||||||
|
|
||||||
expect(result.tags[0]).toBeObject();
|
|
||||||
expect(result.tags[0].tag).toBe('foo');
|
|
||||||
expect(result.tags[0].text).toBe('text');
|
|
||||||
|
|
||||||
expect(result.tags[1]).toBeObject();
|
|
||||||
expect(result.tags[1].tag).toBe('foo');
|
|
||||||
expect(result.tags[1].text).toBe('tags');
|
|
||||||
|
|
||||||
expect(result.tags[2]).toBeObject();
|
|
||||||
expect(result.tags[2].tag).toBe('foo');
|
|
||||||
expect(result.tags[2].text).toBe('like');
|
|
||||||
|
|
||||||
expect(result.tags[3]).toBeObject();
|
|
||||||
expect(result.tags[3].tag).toBe('foo');
|
|
||||||
expect(result.tags[3].text).toBe('this');
|
|
||||||
|
|
||||||
expect(result.newString).toBe('some stuff with multiple stuff, stuff stuff');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Largely covered by the `replaceInlineTag()` tests.
|
it('returns false (rather than throwing) with invalid input', () => {
|
||||||
describe('replaceInlineTags', () => {
|
function badInput() {
|
||||||
it('works with an empty replacer object', () => {
|
return isInlineTag();
|
||||||
const replacers = {};
|
}
|
||||||
const text = 'some {@foo text} to parse';
|
|
||||||
const result = inline.replaceInlineTags(text, replacers);
|
|
||||||
|
|
||||||
expect(result.newString).toBe(text);
|
expect(badInput).not.toThrow();
|
||||||
});
|
expect(badInput()).toBeFalse();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('works with one replacer', () => {
|
describe('replaceInlineTag', () => {
|
||||||
const text = 'some {@foo text} with {@bar multiple} tags';
|
it('throws if the tag is matched and the replacer is invalid', () => {
|
||||||
const replacers = {
|
function badReplacerUndefined() {
|
||||||
foo(string, tagInfo) {
|
inline.replaceInlineTag('{@foo tag}', 'foo');
|
||||||
expect(tagInfo.completeTag).toBe('{@foo text}');
|
}
|
||||||
expect(tagInfo.text).toBe('text');
|
|
||||||
|
|
||||||
return string.replace(tagInfo.completeTag, 'stuff');
|
function badReplacerString() {
|
||||||
}
|
inline.replaceInlineTag('{@foo tag}', 'foo', 'hello');
|
||||||
};
|
}
|
||||||
const result = inline.replaceInlineTags(text, replacers);
|
|
||||||
|
|
||||||
expect(result.newString).toBe('some stuff with {@bar multiple} tags');
|
expect(badReplacerUndefined).toThrow();
|
||||||
});
|
expect(badReplacerString).toThrow();
|
||||||
|
|
||||||
it('works with multiple replacers', () => {
|
|
||||||
const text = 'some {@foo text} with {@bar multiple} tags';
|
|
||||||
const replacers = {
|
|
||||||
foo(string, tagInfo) {
|
|
||||||
expect(tagInfo.completeTag).toBe('{@foo text}');
|
|
||||||
expect(tagInfo.text).toBe('text');
|
|
||||||
|
|
||||||
return string.replace(tagInfo.completeTag, 'stuff');
|
|
||||||
},
|
|
||||||
bar(string, tagInfo) {
|
|
||||||
expect(tagInfo.completeTag).toBe('{@bar multiple}');
|
|
||||||
expect(tagInfo.text).toBe('multiple');
|
|
||||||
|
|
||||||
return string.replace(tagInfo.completeTag, 'awesome');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const result = inline.replaceInlineTags(text, replacers);
|
|
||||||
|
|
||||||
expect(result.newString).toBe('some stuff with awesome tags');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Largely covered by the `replaceInlineTag()` tests.
|
it('does not find anything if there is no text in braces', () => {
|
||||||
describe('extractInlineTag', () => {
|
const replacer = jasmine.createSpy('replacer');
|
||||||
it('works when a tag is specified', () => {
|
|
||||||
const result = inline.extractInlineTag('some {@tagged text}', 'tagged');
|
|
||||||
|
|
||||||
expect(result.tags[0]).toBeObject();
|
inline.replaceInlineTag('braceless text', 'foo', replacer);
|
||||||
expect(result.tags[0].tag).toBe('tagged');
|
|
||||||
expect(result.tags[0].text).toBe('text');
|
expect(replacer).not.toHaveBeenCalled();
|
||||||
expect(result.newString).toBe('some');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('copes with bad escapement at the end of the string', () => {
|
||||||
|
const replacer = jasmine.createSpy('replacer');
|
||||||
|
|
||||||
|
inline.replaceInlineTag('bad {@foo escapement \\', 'foo', replacer);
|
||||||
|
|
||||||
|
expect(replacer).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works if the tag is the entire string', () => {
|
||||||
|
function replacer(string, { completeTag, text }) {
|
||||||
|
expect(string).toBe('{@foo text in braces}');
|
||||||
|
expect(completeTag).toBe('{@foo text in braces}');
|
||||||
|
expect(text).toBe('text in braces');
|
||||||
|
|
||||||
|
return completeTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = inline.replaceInlineTag('{@foo text in braces}', 'foo', replacer);
|
||||||
|
|
||||||
|
expect(result.tags[0]).toBeObject();
|
||||||
|
expect(result.tags[0].tag).toBe('foo');
|
||||||
|
expect(result.tags[0].text).toBe('text in braces');
|
||||||
|
expect(result.newString).toBe('{@foo text in braces}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works if the tag is at the beginning of the string', () => {
|
||||||
|
function replacer(string, { completeTag, text }) {
|
||||||
|
expect(string).toBe('{@foo test string} ahoy');
|
||||||
|
expect(completeTag).toBe('{@foo test string}');
|
||||||
|
expect(text).toBe('test string');
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = inline.replaceInlineTag('{@foo test string} ahoy', 'foo', replacer);
|
||||||
|
|
||||||
|
expect(result.tags[0]).toBeObject();
|
||||||
|
expect(result.tags[0].tag).toBe('foo');
|
||||||
|
expect(result.tags[0].text).toBe('test string');
|
||||||
|
expect(result.newString).toBe('{@foo test string} ahoy');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works if the tag is in the middle of the string', () => {
|
||||||
|
function replacer(string, { completeTag, text }) {
|
||||||
|
expect(string).toBe('a {@foo test string} yay');
|
||||||
|
expect(completeTag).toBe('{@foo test string}');
|
||||||
|
expect(text).toBe('test string');
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = inline.replaceInlineTag('a {@foo test string} yay', 'foo', replacer);
|
||||||
|
|
||||||
|
expect(result.tags[0]).toBeObject();
|
||||||
|
expect(result.tags[0].tag).toBe('foo');
|
||||||
|
expect(result.tags[0].text).toBe('test string');
|
||||||
|
expect(result.newString).toBe('a {@foo test string} yay');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works if the tag is at the end of the string', () => {
|
||||||
|
function replacer(string, { completeTag, text }) {
|
||||||
|
expect(string).toBe('a {@foo test string}');
|
||||||
|
expect(completeTag).toBe('{@foo test string}');
|
||||||
|
expect(text).toBe('test string');
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = inline.replaceInlineTag('a {@foo test string}', 'foo', replacer);
|
||||||
|
|
||||||
|
expect(result.tags[0]).toBeObject();
|
||||||
|
expect(result.tags[0].tag).toBe('foo');
|
||||||
|
expect(result.tags[0].text).toBe('test string');
|
||||||
|
expect(result.newString).toBe('a {@foo test string}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('replaces the string with the specified value', () => {
|
||||||
|
function replacer() {
|
||||||
|
return 'REPLACED!';
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = inline.replaceInlineTag('a {@foo test string}', 'foo', replacer);
|
||||||
|
|
||||||
|
expect(result.newString).toBe('REPLACED!');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('processes all occurrences of a tag', () => {
|
||||||
|
function replacer(string, { completeTag }) {
|
||||||
|
return string.replace(completeTag, 'stuff');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = inline.replaceInlineTag(
|
||||||
|
'some {@foo text} with multiple ' + '{@foo tags}, {@foo like} {@foo this}',
|
||||||
|
'foo',
|
||||||
|
replacer
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.tags.length).toBe(4);
|
||||||
|
|
||||||
|
expect(result.tags[0]).toBeObject();
|
||||||
|
expect(result.tags[0].tag).toBe('foo');
|
||||||
|
expect(result.tags[0].text).toBe('text');
|
||||||
|
|
||||||
|
expect(result.tags[1]).toBeObject();
|
||||||
|
expect(result.tags[1].tag).toBe('foo');
|
||||||
|
expect(result.tags[1].text).toBe('tags');
|
||||||
|
|
||||||
|
expect(result.tags[2]).toBeObject();
|
||||||
|
expect(result.tags[2].tag).toBe('foo');
|
||||||
|
expect(result.tags[2].text).toBe('like');
|
||||||
|
|
||||||
|
expect(result.tags[3]).toBeObject();
|
||||||
|
expect(result.tags[3].tag).toBe('foo');
|
||||||
|
expect(result.tags[3].text).toBe('this');
|
||||||
|
|
||||||
|
expect(result.newString).toBe('some stuff with multiple stuff, stuff stuff');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Largely covered by the `replaceInlineTag()` tests.
|
||||||
|
describe('replaceInlineTags', () => {
|
||||||
|
it('works with an empty replacer object', () => {
|
||||||
|
const replacers = {};
|
||||||
|
const text = 'some {@foo text} to parse';
|
||||||
|
const result = inline.replaceInlineTags(text, replacers);
|
||||||
|
|
||||||
|
expect(result.newString).toBe(text);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works with one replacer', () => {
|
||||||
|
const text = 'some {@foo text} with {@bar multiple} tags';
|
||||||
|
const replacers = {
|
||||||
|
foo(string, tagInfo) {
|
||||||
|
expect(tagInfo.completeTag).toBe('{@foo text}');
|
||||||
|
expect(tagInfo.text).toBe('text');
|
||||||
|
|
||||||
|
return string.replace(tagInfo.completeTag, 'stuff');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const result = inline.replaceInlineTags(text, replacers);
|
||||||
|
|
||||||
|
expect(result.newString).toBe('some stuff with {@bar multiple} tags');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works with multiple replacers', () => {
|
||||||
|
const text = 'some {@foo text} with {@bar multiple} tags';
|
||||||
|
const replacers = {
|
||||||
|
foo(string, tagInfo) {
|
||||||
|
expect(tagInfo.completeTag).toBe('{@foo text}');
|
||||||
|
expect(tagInfo.text).toBe('text');
|
||||||
|
|
||||||
|
return string.replace(tagInfo.completeTag, 'stuff');
|
||||||
|
},
|
||||||
|
bar(string, tagInfo) {
|
||||||
|
expect(tagInfo.completeTag).toBe('{@bar multiple}');
|
||||||
|
expect(tagInfo.text).toBe('multiple');
|
||||||
|
|
||||||
|
return string.replace(tagInfo.completeTag, 'awesome');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const result = inline.replaceInlineTags(text, replacers);
|
||||||
|
|
||||||
|
expect(result.newString).toBe('some stuff with awesome tags');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Largely covered by the `replaceInlineTag()` tests.
|
||||||
|
describe('extractInlineTag', () => {
|
||||||
|
it('works when a tag is specified', () => {
|
||||||
|
const result = inline.extractInlineTag('some {@tagged text}', 'tagged');
|
||||||
|
|
||||||
|
expect(result.tags[0]).toBeObject();
|
||||||
|
expect(result.tags[0].tag).toBe('tagged');
|
||||||
|
expect(result.tags[0].text).toBe('text');
|
||||||
|
expect(result.newString).toBe('some');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,266 +1,266 @@
|
|||||||
function buildText(type, name, desc) {
|
function buildText(type, name, desc) {
|
||||||
let text = '';
|
let text = '';
|
||||||
|
|
||||||
if (type) {
|
if (type) {
|
||||||
text += `{${type}}`;
|
text += `{${type}}`;
|
||||||
if (name || desc) {
|
if (name || desc) {
|
||||||
text += ' ';
|
text += ' ';
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name) {
|
|
||||||
text += name;
|
|
||||||
if (desc) {
|
|
||||||
text += ' ';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
text += name;
|
||||||
if (desc) {
|
if (desc) {
|
||||||
text += desc;
|
text += ' ';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return text;
|
if (desc) {
|
||||||
|
text += desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('@jsdoc/tag/lib/type', () => {
|
describe('@jsdoc/tag/lib/type', () => {
|
||||||
const type = require('../../../lib/type');
|
const type = require('../../../lib/type');
|
||||||
|
|
||||||
it('is an object', () => {
|
it('is an object', () => {
|
||||||
expect(type).toBeObject();
|
expect(type).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exports a parse function', () => {
|
||||||
|
expect(type.parse).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parse', () => {
|
||||||
|
it('returns an object with name, type, and text properties', () => {
|
||||||
|
const info = type.parse('');
|
||||||
|
|
||||||
|
expect(info.name).toBeString();
|
||||||
|
expect(info.type).toBeArray();
|
||||||
|
expect(info.text).toBeString();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('exports a parse function', () => {
|
it('does not extract a name or type if canHaveName and canHaveType are not set', () => {
|
||||||
expect(type.parse).toBeFunction();
|
const desc = '{number} foo The foo parameter.';
|
||||||
|
const info = type.parse(desc);
|
||||||
|
|
||||||
|
expect(info.type).toBeEmptyArray();
|
||||||
|
expect(info.name).toBe('');
|
||||||
|
expect(info.text).toBe(desc);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('parse', () => {
|
it('extracts a name, but not a type, if canHaveName is true and canHaveType is false', () => {
|
||||||
it('returns an object with name, type, and text properties', () => {
|
const name = 'bar';
|
||||||
const info = type.parse('');
|
const desc = 'The bar parameter.';
|
||||||
|
const info = type.parse(buildText(null, name, desc), true, false);
|
||||||
|
|
||||||
expect(info.name).toBeString();
|
expect(info.type).toBeEmptyArray();
|
||||||
expect(info.type).toBeArray();
|
expect(info.name).toBe(name);
|
||||||
expect(info.text).toBeString();
|
expect(info.text).toBe(desc);
|
||||||
});
|
|
||||||
|
|
||||||
it('does not extract a name or type if canHaveName and canHaveType are not set', () => {
|
|
||||||
const desc = '{number} foo The foo parameter.';
|
|
||||||
const info = type.parse(desc);
|
|
||||||
|
|
||||||
expect(info.type).toBeEmptyArray();
|
|
||||||
expect(info.name).toBe('');
|
|
||||||
expect(info.text).toBe(desc);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('extracts a name, but not a type, if canHaveName is true and canHaveType is false', () => {
|
|
||||||
const name = 'bar';
|
|
||||||
const desc = 'The bar parameter.';
|
|
||||||
const info = type.parse( buildText(null, name, desc), true, false );
|
|
||||||
|
|
||||||
expect(info.type).toBeEmptyArray();
|
|
||||||
expect(info.name).toBe(name);
|
|
||||||
expect(info.text).toBe(desc);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('extracts a type, but not a name, if canHaveName is false and canHaveType is true', () => {
|
|
||||||
const typeString = 'boolean';
|
|
||||||
const desc = 'Set to true on alternate Thursdays.';
|
|
||||||
const info = type.parse(buildText(typeString, null, desc), false, true);
|
|
||||||
|
|
||||||
expect(info.type).toEqual([typeString]);
|
|
||||||
expect(info.name).toBe('');
|
|
||||||
expect(info.text).toBe(desc);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('extracts a name and type if canHaveName and canHaveType are true', () => {
|
|
||||||
const typeString = 'string';
|
|
||||||
const name = 'baz';
|
|
||||||
const desc = 'The baz parameter.';
|
|
||||||
const info = type.parse(buildText(typeString, name, desc), true, true);
|
|
||||||
|
|
||||||
expect(info.type).toEqual([typeString]);
|
|
||||||
expect(info.name).toBe(name);
|
|
||||||
expect(info.text).toBe(desc);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('reports optional types correctly for both JSDoc and Closure syntax', () => {
|
|
||||||
let desc = '{string} [foo]';
|
|
||||||
let info = type.parse(desc, true, true);
|
|
||||||
|
|
||||||
expect(info.optional).toBeTrue();
|
|
||||||
|
|
||||||
desc = '{string=} [foo]';
|
|
||||||
info = type.parse(desc, true, true);
|
|
||||||
expect(info.optional).toBeTrue();
|
|
||||||
|
|
||||||
desc = '[foo]';
|
|
||||||
info = type.parse(desc, true, true);
|
|
||||||
expect(info.optional).toBeTrue();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returnsthe types as an array', () => {
|
|
||||||
const desc = '{string} foo';
|
|
||||||
const info = type.parse(desc, true, true);
|
|
||||||
|
|
||||||
expect(info.type).toEqual( ['string'] );
|
|
||||||
});
|
|
||||||
|
|
||||||
it('recognizes the entire list of possible types', () => {
|
|
||||||
let desc = '{(string|number)} foo';
|
|
||||||
let info = type.parse(desc, true, true);
|
|
||||||
|
|
||||||
expect(info.type).toEqual( ['string', 'number'] );
|
|
||||||
|
|
||||||
desc = '{ ( string | number ) } foo';
|
|
||||||
info = type.parse(desc, true, true);
|
|
||||||
expect(info.type).toEqual( ['string', 'number'] );
|
|
||||||
|
|
||||||
desc = '{ ( string | number)} foo';
|
|
||||||
info = type.parse(desc, true, true);
|
|
||||||
expect(info.type).toEqual( ['string', 'number'] );
|
|
||||||
|
|
||||||
desc = '{(string|number|boolean|function)} foo';
|
|
||||||
info = type.parse(desc, true, true);
|
|
||||||
expect(info.type).toEqual( ['string', 'number', 'boolean', 'function'] );
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not find any type if there is no text in braces', () => {
|
|
||||||
const desc = 'braceless text';
|
|
||||||
const info = type.parse(desc, false, true);
|
|
||||||
|
|
||||||
expect(info.type).toBeEmptyArray();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('copes with bad escapement at the end of the string', () => {
|
|
||||||
const desc = 'bad {escapement \\';
|
|
||||||
const info = type.parse(desc, false, true);
|
|
||||||
|
|
||||||
expect(info.type).toBeEmptyArray();
|
|
||||||
expect(info.text).toBe(desc);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles escaped braces correctly', () => {
|
|
||||||
const desc = '{weirdObject."with\\}AnnoyingProperty"}';
|
|
||||||
const info = type.parse(desc, false, true);
|
|
||||||
|
|
||||||
expect(info.type[0]).toBe('weirdObject."with}AnnoyingProperty"');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('works if the type expression is the entire string', () => {
|
|
||||||
const desc = '{textInBraces}';
|
|
||||||
const info = type.parse(desc, false, true);
|
|
||||||
|
|
||||||
expect(info.type[0]).toBe('textInBraces');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('works if the type expression is at the beginning of the string', () => {
|
|
||||||
const desc = '{testString} ahoy';
|
|
||||||
const info = type.parse(desc, false, true);
|
|
||||||
|
|
||||||
expect(info.type[0]).toBe('testString');
|
|
||||||
expect(info.text).toBe('ahoy');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('works if the type expression is in the middle of the string', () => {
|
|
||||||
const desc = 'a {testString} yay';
|
|
||||||
const info = type.parse(desc, false, true);
|
|
||||||
|
|
||||||
expect(info.type[0]).toBe('testString');
|
|
||||||
expect(info.text).toBe('a yay');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('works if the tag is at the end of the string', () => {
|
|
||||||
const desc = 'a {testString}';
|
|
||||||
const info = type.parse(desc, false, true);
|
|
||||||
|
|
||||||
expect(info.type[0]).toBe('testString');
|
|
||||||
expect(info.text).toBe('a');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('works when there are nested braces', () => {
|
|
||||||
const desc = 'some {{double}} braces';
|
|
||||||
const info = type.parse(desc, false, true);
|
|
||||||
|
|
||||||
// we currently stringify all record types as 'Object'
|
|
||||||
expect(info.type[0]).toBe('Object');
|
|
||||||
expect(info.text).toBe('some braces');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('overrides the type expression if an inline @type tag is specified', () => {
|
|
||||||
let desc = '{Object} cookie {@type Monster}';
|
|
||||||
let info = type.parse(desc, true, true);
|
|
||||||
|
|
||||||
expect(info.type).toEqual( ['Monster'] );
|
|
||||||
expect(info.text).toBe('');
|
|
||||||
|
|
||||||
desc = '{Object} cookie - {@type Monster}';
|
|
||||||
info = type.parse(desc, true, true);
|
|
||||||
expect(info.type).toEqual( ['Monster'] );
|
|
||||||
expect(info.text).toBe('');
|
|
||||||
|
|
||||||
desc = '{Object} cookie - The cookie parameter. {@type Monster}';
|
|
||||||
info = type.parse(desc, true, true);
|
|
||||||
expect(info.type).toEqual( ['Monster'] );
|
|
||||||
expect(info.text).toBe('The cookie parameter.');
|
|
||||||
|
|
||||||
desc = '{Object} cookie - The cookie parameter. {@type (Monster|Jar)}';
|
|
||||||
info = type.parse(desc, true, true);
|
|
||||||
expect(info.type).toEqual( ['Monster', 'Jar'] );
|
|
||||||
expect(info.text).toBe('The cookie parameter.');
|
|
||||||
|
|
||||||
desc = '{Object} cookie - The cookie parameter. {@type (Monster|Jar)} Mmm, cookie.';
|
|
||||||
info = type.parse(desc, true, true);
|
|
||||||
expect(info.type).toEqual( ['Monster', 'Jar'] );
|
|
||||||
expect(info.text).toBe('The cookie parameter. Mmm, cookie.');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('JSDoc-style type info', () => {
|
|
||||||
it('parses JSDoc-style optional parameters', () => {
|
|
||||||
let name = '[qux]';
|
|
||||||
const desc = 'The qux parameter.';
|
|
||||||
let info = type.parse( buildText(null, name, desc), true, false );
|
|
||||||
|
|
||||||
expect(info.name).toBe('qux');
|
|
||||||
expect(info.text).toBe(desc);
|
|
||||||
expect(info.optional).toBeTrue();
|
|
||||||
|
|
||||||
name = '[ qux ]';
|
|
||||||
info = type.parse( buildText(null, name, desc), true, false );
|
|
||||||
expect(info.name).toBe('qux');
|
|
||||||
expect(info.text).toBe(desc);
|
|
||||||
expect(info.optional).toBeTrue();
|
|
||||||
|
|
||||||
name = '[qux=hooray]';
|
|
||||||
info = type.parse( buildText(null, name, desc), true, false );
|
|
||||||
expect(info.name).toBe('qux');
|
|
||||||
expect(info.text).toBe(desc);
|
|
||||||
expect(info.optional).toBeTrue();
|
|
||||||
expect(info.defaultvalue).toBe('hooray');
|
|
||||||
|
|
||||||
name = '[ qux = hooray ]';
|
|
||||||
info = type.parse( buildText(null, name, desc), true, false );
|
|
||||||
expect(info.name).toBe('qux');
|
|
||||||
expect(info.text).toBe(desc);
|
|
||||||
expect(info.optional).toBeTrue();
|
|
||||||
expect(info.defaultvalue).toBe('hooray');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: Add more tests related to how JSDoc mangles the Catharsis parse results.
|
|
||||||
describe('Closure Compiler-style type info', () => {
|
|
||||||
it('recognizes variable (repeatable) parameters', () => {
|
|
||||||
const desc = '{...string} foo - Foo.';
|
|
||||||
const info = type.parse(desc, true, true);
|
|
||||||
|
|
||||||
expect(info.type).toEqual( ['string'] );
|
|
||||||
expect(info.variable).toBeTrue();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets the type correctly for type applications that contain type unions', () => {
|
|
||||||
const desc = '{Array.<(string|number)>} foo - Foo.';
|
|
||||||
const info = type.parse(desc, true, true);
|
|
||||||
|
|
||||||
expect(info.type).toEqual(['Array.<(string|number)>']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('extracts a type, but not a name, if canHaveName is false and canHaveType is true', () => {
|
||||||
|
const typeString = 'boolean';
|
||||||
|
const desc = 'Set to true on alternate Thursdays.';
|
||||||
|
const info = type.parse(buildText(typeString, null, desc), false, true);
|
||||||
|
|
||||||
|
expect(info.type).toEqual([typeString]);
|
||||||
|
expect(info.name).toBe('');
|
||||||
|
expect(info.text).toBe(desc);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('extracts a name and type if canHaveName and canHaveType are true', () => {
|
||||||
|
const typeString = 'string';
|
||||||
|
const name = 'baz';
|
||||||
|
const desc = 'The baz parameter.';
|
||||||
|
const info = type.parse(buildText(typeString, name, desc), true, true);
|
||||||
|
|
||||||
|
expect(info.type).toEqual([typeString]);
|
||||||
|
expect(info.name).toBe(name);
|
||||||
|
expect(info.text).toBe(desc);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reports optional types correctly for both JSDoc and Closure syntax', () => {
|
||||||
|
let desc = '{string} [foo]';
|
||||||
|
let info = type.parse(desc, true, true);
|
||||||
|
|
||||||
|
expect(info.optional).toBeTrue();
|
||||||
|
|
||||||
|
desc = '{string=} [foo]';
|
||||||
|
info = type.parse(desc, true, true);
|
||||||
|
expect(info.optional).toBeTrue();
|
||||||
|
|
||||||
|
desc = '[foo]';
|
||||||
|
info = type.parse(desc, true, true);
|
||||||
|
expect(info.optional).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returnsthe types as an array', () => {
|
||||||
|
const desc = '{string} foo';
|
||||||
|
const info = type.parse(desc, true, true);
|
||||||
|
|
||||||
|
expect(info.type).toEqual(['string']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('recognizes the entire list of possible types', () => {
|
||||||
|
let desc = '{(string|number)} foo';
|
||||||
|
let info = type.parse(desc, true, true);
|
||||||
|
|
||||||
|
expect(info.type).toEqual(['string', 'number']);
|
||||||
|
|
||||||
|
desc = '{ ( string | number ) } foo';
|
||||||
|
info = type.parse(desc, true, true);
|
||||||
|
expect(info.type).toEqual(['string', 'number']);
|
||||||
|
|
||||||
|
desc = '{ ( string | number)} foo';
|
||||||
|
info = type.parse(desc, true, true);
|
||||||
|
expect(info.type).toEqual(['string', 'number']);
|
||||||
|
|
||||||
|
desc = '{(string|number|boolean|function)} foo';
|
||||||
|
info = type.parse(desc, true, true);
|
||||||
|
expect(info.type).toEqual(['string', 'number', 'boolean', 'function']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not find any type if there is no text in braces', () => {
|
||||||
|
const desc = 'braceless text';
|
||||||
|
const info = type.parse(desc, false, true);
|
||||||
|
|
||||||
|
expect(info.type).toBeEmptyArray();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('copes with bad escapement at the end of the string', () => {
|
||||||
|
const desc = 'bad {escapement \\';
|
||||||
|
const info = type.parse(desc, false, true);
|
||||||
|
|
||||||
|
expect(info.type).toBeEmptyArray();
|
||||||
|
expect(info.text).toBe(desc);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles escaped braces correctly', () => {
|
||||||
|
const desc = '{weirdObject."with\\}AnnoyingProperty"}';
|
||||||
|
const info = type.parse(desc, false, true);
|
||||||
|
|
||||||
|
expect(info.type[0]).toBe('weirdObject."with}AnnoyingProperty"');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works if the type expression is the entire string', () => {
|
||||||
|
const desc = '{textInBraces}';
|
||||||
|
const info = type.parse(desc, false, true);
|
||||||
|
|
||||||
|
expect(info.type[0]).toBe('textInBraces');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works if the type expression is at the beginning of the string', () => {
|
||||||
|
const desc = '{testString} ahoy';
|
||||||
|
const info = type.parse(desc, false, true);
|
||||||
|
|
||||||
|
expect(info.type[0]).toBe('testString');
|
||||||
|
expect(info.text).toBe('ahoy');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works if the type expression is in the middle of the string', () => {
|
||||||
|
const desc = 'a {testString} yay';
|
||||||
|
const info = type.parse(desc, false, true);
|
||||||
|
|
||||||
|
expect(info.type[0]).toBe('testString');
|
||||||
|
expect(info.text).toBe('a yay');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works if the tag is at the end of the string', () => {
|
||||||
|
const desc = 'a {testString}';
|
||||||
|
const info = type.parse(desc, false, true);
|
||||||
|
|
||||||
|
expect(info.type[0]).toBe('testString');
|
||||||
|
expect(info.text).toBe('a');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works when there are nested braces', () => {
|
||||||
|
const desc = 'some {{double}} braces';
|
||||||
|
const info = type.parse(desc, false, true);
|
||||||
|
|
||||||
|
// we currently stringify all record types as 'Object'
|
||||||
|
expect(info.type[0]).toBe('Object');
|
||||||
|
expect(info.text).toBe('some braces');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('overrides the type expression if an inline @type tag is specified', () => {
|
||||||
|
let desc = '{Object} cookie {@type Monster}';
|
||||||
|
let info = type.parse(desc, true, true);
|
||||||
|
|
||||||
|
expect(info.type).toEqual(['Monster']);
|
||||||
|
expect(info.text).toBe('');
|
||||||
|
|
||||||
|
desc = '{Object} cookie - {@type Monster}';
|
||||||
|
info = type.parse(desc, true, true);
|
||||||
|
expect(info.type).toEqual(['Monster']);
|
||||||
|
expect(info.text).toBe('');
|
||||||
|
|
||||||
|
desc = '{Object} cookie - The cookie parameter. {@type Monster}';
|
||||||
|
info = type.parse(desc, true, true);
|
||||||
|
expect(info.type).toEqual(['Monster']);
|
||||||
|
expect(info.text).toBe('The cookie parameter.');
|
||||||
|
|
||||||
|
desc = '{Object} cookie - The cookie parameter. {@type (Monster|Jar)}';
|
||||||
|
info = type.parse(desc, true, true);
|
||||||
|
expect(info.type).toEqual(['Monster', 'Jar']);
|
||||||
|
expect(info.text).toBe('The cookie parameter.');
|
||||||
|
|
||||||
|
desc = '{Object} cookie - The cookie parameter. {@type (Monster|Jar)} Mmm, cookie.';
|
||||||
|
info = type.parse(desc, true, true);
|
||||||
|
expect(info.type).toEqual(['Monster', 'Jar']);
|
||||||
|
expect(info.text).toBe('The cookie parameter. Mmm, cookie.');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('JSDoc-style type info', () => {
|
||||||
|
it('parses JSDoc-style optional parameters', () => {
|
||||||
|
let name = '[qux]';
|
||||||
|
const desc = 'The qux parameter.';
|
||||||
|
let info = type.parse(buildText(null, name, desc), true, false);
|
||||||
|
|
||||||
|
expect(info.name).toBe('qux');
|
||||||
|
expect(info.text).toBe(desc);
|
||||||
|
expect(info.optional).toBeTrue();
|
||||||
|
|
||||||
|
name = '[ qux ]';
|
||||||
|
info = type.parse(buildText(null, name, desc), true, false);
|
||||||
|
expect(info.name).toBe('qux');
|
||||||
|
expect(info.text).toBe(desc);
|
||||||
|
expect(info.optional).toBeTrue();
|
||||||
|
|
||||||
|
name = '[qux=hooray]';
|
||||||
|
info = type.parse(buildText(null, name, desc), true, false);
|
||||||
|
expect(info.name).toBe('qux');
|
||||||
|
expect(info.text).toBe(desc);
|
||||||
|
expect(info.optional).toBeTrue();
|
||||||
|
expect(info.defaultvalue).toBe('hooray');
|
||||||
|
|
||||||
|
name = '[ qux = hooray ]';
|
||||||
|
info = type.parse(buildText(null, name, desc), true, false);
|
||||||
|
expect(info.name).toBe('qux');
|
||||||
|
expect(info.text).toBe(desc);
|
||||||
|
expect(info.optional).toBeTrue();
|
||||||
|
expect(info.defaultvalue).toBe('hooray');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Add more tests related to how JSDoc mangles the Catharsis parse results.
|
||||||
|
describe('Closure Compiler-style type info', () => {
|
||||||
|
it('recognizes variable (repeatable) parameters', () => {
|
||||||
|
const desc = '{...string} foo - Foo.';
|
||||||
|
const info = type.parse(desc, true, true);
|
||||||
|
|
||||||
|
expect(info.type).toEqual(['string']);
|
||||||
|
expect(info.variable).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the type correctly for type applications that contain type unions', () => {
|
||||||
|
const desc = '{Array.<(string|number)>} foo - Foo.';
|
||||||
|
const info = type.parse(desc, true, true);
|
||||||
|
|
||||||
|
expect(info.type).toEqual(['Array.<(string|number)>']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,6 +2,6 @@ const Task = require('./lib/task');
|
|||||||
const TaskRunner = require('./lib/task-runner');
|
const TaskRunner = require('./lib/task-runner');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Task,
|
Task,
|
||||||
TaskRunner
|
TaskRunner,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const { DepGraph } = require('dependency-graph');
|
const { DepGraph } = require('dependency-graph');
|
||||||
const Emittery = require('emittery');
|
const Emittery = require('emittery');
|
||||||
const {default: ow} = require('ow');
|
const { default: ow } = require('ow');
|
||||||
const Queue = require('p-queue').default;
|
const Queue = require('p-queue').default;
|
||||||
const v = require('./validators');
|
const v = require('./validators');
|
||||||
|
|
||||||
@ -9,319 +9,320 @@ const v = require('./validators');
|
|||||||
DepGraph.prototype.dependentsOf = DepGraph.prototype.dependantsOf;
|
DepGraph.prototype.dependentsOf = DepGraph.prototype.dependantsOf;
|
||||||
|
|
||||||
module.exports = class TaskRunner extends Emittery {
|
module.exports = class TaskRunner extends Emittery {
|
||||||
constructor(context) {
|
constructor(context) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
ow(context, ow.optional.object);
|
ow(context, ow.optional.object);
|
||||||
|
|
||||||
this._init(context);
|
this._init(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
_addOrRemoveTasks(tasks, func, action) {
|
_addOrRemoveTasks(tasks, func, action) {
|
||||||
func = _.bind(func, this);
|
func = _.bind(func, this);
|
||||||
|
|
||||||
if (Array.isArray(tasks)) {
|
if (Array.isArray(tasks)) {
|
||||||
tasks.forEach((task, i) => {
|
tasks.forEach((task, i) => {
|
||||||
try {
|
|
||||||
func(task);
|
|
||||||
} catch (e) {
|
|
||||||
e.message = `Can't ${action} task ${i}: ${e.message}`;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (tasks !== null && typeof tasks === 'object') {
|
|
||||||
for (const task of Object.keys(tasks)) {
|
|
||||||
try {
|
|
||||||
func(tasks[task]);
|
|
||||||
} catch (e) {
|
|
||||||
e.message = `Can't ${action} task "${task}": ${e.message}`;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_addTaskEmitters(task) {
|
|
||||||
const u = {};
|
|
||||||
|
|
||||||
u.start = task.on('start', t => this.emit('taskStart', t));
|
|
||||||
u.end = task.on('end', t => this.emit('taskEnd', t));
|
|
||||||
u.error = task.on('error', (e => {
|
|
||||||
this.emit('taskError', {
|
|
||||||
task: e.task,
|
|
||||||
error: e.error
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this._error) {
|
|
||||||
this._error = e.error;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._unsubscribers.set(task.name, u);
|
|
||||||
}
|
|
||||||
|
|
||||||
_bindTaskFunc(task) {
|
|
||||||
return _.bind(task.run, task, this._context);
|
|
||||||
}
|
|
||||||
|
|
||||||
_createTaskSequence(tasks) {
|
|
||||||
if (!tasks.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => tasks.reduce((p, taskName) => {
|
|
||||||
const task = this._nameToTask.get(taskName);
|
|
||||||
|
|
||||||
return p.then(
|
|
||||||
this._bindTaskFunc(task),
|
|
||||||
e => Promise.reject(e)
|
|
||||||
);
|
|
||||||
}, Promise.resolve());
|
|
||||||
}
|
|
||||||
|
|
||||||
_init(context) {
|
|
||||||
this._context = context;
|
|
||||||
this._deps = new Map();
|
|
||||||
this._error = null;
|
|
||||||
this._queue = new Queue();
|
|
||||||
this._taskToName = new WeakMap();
|
|
||||||
this._nameToTask = new Map();
|
|
||||||
this._running = false;
|
|
||||||
this._unsubscribers = new Map();
|
|
||||||
|
|
||||||
this._queue.pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
_newDependencyCycleError(cyclePath) {
|
|
||||||
return new v.DependencyCycleError(
|
|
||||||
`Tasks have circular dependencies: ${cyclePath.join(' > ')}`,
|
|
||||||
cyclePath
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_newStateError() {
|
|
||||||
return new v.StateError('The task runner is already running.');
|
|
||||||
}
|
|
||||||
|
|
||||||
_newUnknownDepsError(dependent, unknownDeps) {
|
|
||||||
let errorText;
|
|
||||||
|
|
||||||
if (unknownDeps.length === 1) {
|
|
||||||
errorText = 'an unknown task';
|
|
||||||
} else {
|
|
||||||
errorText = 'unknown tasks';
|
|
||||||
}
|
|
||||||
|
|
||||||
return new v.UnknownDependencyError(`The task ${dependent} depends on ${errorText}: ` +
|
|
||||||
`${unknownDeps.join(', ')}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
_orderTasks() {
|
|
||||||
let error;
|
|
||||||
const graph = new DepGraph();
|
|
||||||
let parallel;
|
|
||||||
let sequential;
|
|
||||||
|
|
||||||
for (const [task] of this._nameToTask) {
|
|
||||||
graph.addNode(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [dependent] of this._deps) {
|
|
||||||
const unknownDeps = [];
|
|
||||||
|
|
||||||
for (const dependency of this._deps.get(dependent)) {
|
|
||||||
if (!this._nameToTask.has(dependency)) {
|
|
||||||
unknownDeps.push(dependency);
|
|
||||||
} else {
|
|
||||||
graph.addDependency(dependent, dependency);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unknownDeps.length) {
|
|
||||||
error = this._newUnknownDepsError(dependency, unknownDeps);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!error) {
|
|
||||||
try {
|
|
||||||
// Get standalone tasks with no dependencies and no dependents.
|
|
||||||
parallel = graph.overallOrder(true)
|
|
||||||
.filter(task => !(graph.dependentsOf(task).length));
|
|
||||||
// Get tasks with dependencies, in a correctly ordered list.
|
|
||||||
sequential = graph.overallOrder().filter(task => !parallel.includes(task));
|
|
||||||
} catch (e) {
|
|
||||||
error = this._newDependencyCycleError(e.cyclePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
error,
|
|
||||||
parallel,
|
|
||||||
sequential
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_rejectIfRunning() {
|
|
||||||
if (this.running) {
|
|
||||||
return Promise.reject(this._newStateError());
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_throwIfRunning() {
|
|
||||||
if (this.running) {
|
|
||||||
throw this._newStateError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_throwIfUnknownDeps(dependent, unknownDeps) {
|
|
||||||
if (!unknownDeps.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw this._newUnknownDepsError(dependent, unknownDeps);
|
|
||||||
}
|
|
||||||
|
|
||||||
addTask(task) {
|
|
||||||
ow(task, v.checkTaskOrString);
|
|
||||||
|
|
||||||
this._throwIfRunning();
|
|
||||||
|
|
||||||
this._nameToTask.set(task.name, task);
|
|
||||||
if (task.dependsOn) {
|
|
||||||
this._deps.set(task.name, task.dependsOn);
|
|
||||||
}
|
|
||||||
this._taskToName.set(task, task.name);
|
|
||||||
this._addTaskEmitters(task);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
addTasks(tasks) {
|
|
||||||
ow(tasks, ow.any(ow.array, ow.object));
|
|
||||||
this._addOrRemoveTasks(tasks, this.addTask, 'add');
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
end() {
|
|
||||||
this.emit('end', {
|
|
||||||
error: this._error
|
|
||||||
});
|
|
||||||
this._queue.clear();
|
|
||||||
this._init();
|
|
||||||
}
|
|
||||||
|
|
||||||
removeTask(task) {
|
|
||||||
let unsubscribers;
|
|
||||||
|
|
||||||
ow(task, v.checkTaskOrString);
|
|
||||||
this._throwIfRunning();
|
|
||||||
|
|
||||||
if (typeof task === 'string') {
|
|
||||||
task = this._nameToTask.get(task);
|
|
||||||
|
|
||||||
if (!task) {
|
|
||||||
throw new v.UnknownTaskError(`Unknown task: ${task}`);
|
|
||||||
}
|
|
||||||
} else if (typeof task === 'object') {
|
|
||||||
if (!this._taskToName.has(task)) {
|
|
||||||
throw new v.UnknownTaskError(`Unknown task: ${task}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._nameToTask.delete(task.name);
|
|
||||||
this._taskToName.delete(task);
|
|
||||||
this._deps.delete(task.name);
|
|
||||||
|
|
||||||
unsubscribers = this._unsubscribers.get(task.name);
|
|
||||||
for (const u of Object.keys(unsubscribers)) {
|
|
||||||
unsubscribers[u]();
|
|
||||||
}
|
|
||||||
this._unsubscribers.delete(task.name);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeTasks(tasks) {
|
|
||||||
ow(tasks, ow.any(ow.array, ow.object));
|
|
||||||
this._addOrRemoveTasks(tasks, this.removeTask, 'remove');
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
run(context) {
|
|
||||||
ow(context, ow.optional.object);
|
|
||||||
|
|
||||||
let endPromise;
|
|
||||||
const { error, parallel, sequential } = this._orderTasks();
|
|
||||||
let runningPromise;
|
|
||||||
let taskFuncs = [];
|
|
||||||
let taskSequence;
|
|
||||||
|
|
||||||
// First, fail if the runner is already running.
|
|
||||||
runningPromise = this._rejectIfRunning();
|
|
||||||
if (runningPromise) {
|
|
||||||
return runningPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then fail if the tasks couldn't be ordered.
|
|
||||||
if (error) {
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._context = context || this._context;
|
|
||||||
|
|
||||||
for (const taskName of parallel) {
|
|
||||||
taskFuncs.push(this._bindTaskFunc(this._nameToTask.get(taskName)));
|
|
||||||
}
|
|
||||||
|
|
||||||
taskSequence = this._createTaskSequence(sequential);
|
|
||||||
if (taskSequence) {
|
|
||||||
taskFuncs.push(taskSequence);
|
|
||||||
}
|
|
||||||
|
|
||||||
endPromise = this._queue.addAll(taskFuncs).then(() => {
|
|
||||||
this.end();
|
|
||||||
|
|
||||||
if (this._error) {
|
|
||||||
return Promise.reject(this._error);
|
|
||||||
} else {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
}, e => {
|
|
||||||
this.end();
|
|
||||||
|
|
||||||
return Promise.reject(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.emit('start');
|
|
||||||
this._running = true;
|
|
||||||
try {
|
try {
|
||||||
this._queue.start();
|
func(task);
|
||||||
|
|
||||||
return endPromise;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._error = e;
|
e.message = `Can't ${action} task ${i}: ${e.message}`;
|
||||||
this.end();
|
throw e;
|
||||||
|
|
||||||
return Promise.reject(e);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
} else if (tasks !== null && typeof tasks === 'object') {
|
||||||
|
for (const task of Object.keys(tasks)) {
|
||||||
|
try {
|
||||||
|
func(tasks[task]);
|
||||||
|
} catch (e) {
|
||||||
|
e.message = `Can't ${action} task "${task}": ${e.message}`;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_addTaskEmitters(task) {
|
||||||
|
const u = {};
|
||||||
|
|
||||||
|
u.start = task.on('start', (t) => this.emit('taskStart', t));
|
||||||
|
u.end = task.on('end', (t) => this.emit('taskEnd', t));
|
||||||
|
u.error = task.on('error', (e) => {
|
||||||
|
this.emit('taskError', {
|
||||||
|
task: e.task,
|
||||||
|
error: e.error,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!this._error) {
|
||||||
|
this._error = e.error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._unsubscribers.set(task.name, u);
|
||||||
|
}
|
||||||
|
|
||||||
|
_bindTaskFunc(task) {
|
||||||
|
return _.bind(task.run, task, this._context);
|
||||||
|
}
|
||||||
|
|
||||||
|
_createTaskSequence(tasks) {
|
||||||
|
if (!tasks.length) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get running() {
|
return () =>
|
||||||
return this._running;
|
tasks.reduce((p, taskName) => {
|
||||||
|
const task = this._nameToTask.get(taskName);
|
||||||
|
|
||||||
|
return p.then(this._bindTaskFunc(task), (e) => Promise.reject(e));
|
||||||
|
}, Promise.resolve());
|
||||||
|
}
|
||||||
|
|
||||||
|
_init(context) {
|
||||||
|
this._context = context;
|
||||||
|
this._deps = new Map();
|
||||||
|
this._error = null;
|
||||||
|
this._queue = new Queue();
|
||||||
|
this._taskToName = new WeakMap();
|
||||||
|
this._nameToTask = new Map();
|
||||||
|
this._running = false;
|
||||||
|
this._unsubscribers = new Map();
|
||||||
|
|
||||||
|
this._queue.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
_newDependencyCycleError(cyclePath) {
|
||||||
|
return new v.DependencyCycleError(
|
||||||
|
`Tasks have circular dependencies: ${cyclePath.join(' > ')}`,
|
||||||
|
cyclePath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_newStateError() {
|
||||||
|
return new v.StateError('The task runner is already running.');
|
||||||
|
}
|
||||||
|
|
||||||
|
_newUnknownDepsError(dependent, unknownDeps) {
|
||||||
|
let errorText;
|
||||||
|
|
||||||
|
if (unknownDeps.length === 1) {
|
||||||
|
errorText = 'an unknown task';
|
||||||
|
} else {
|
||||||
|
errorText = 'unknown tasks';
|
||||||
}
|
}
|
||||||
|
|
||||||
get tasks() {
|
return new v.UnknownDependencyError(
|
||||||
const entries = [];
|
`The task ${dependent} depends on ${errorText}: ` + `${unknownDeps.join(', ')}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
for (const entry of this._nameToTask.entries()) {
|
_orderTasks() {
|
||||||
entries.push(entry);
|
let error;
|
||||||
|
const graph = new DepGraph();
|
||||||
|
let parallel;
|
||||||
|
let sequential;
|
||||||
|
|
||||||
|
for (const [task] of this._nameToTask) {
|
||||||
|
graph.addNode(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [dependent] of this._deps) {
|
||||||
|
const unknownDeps = [];
|
||||||
|
|
||||||
|
for (const dependency of this._deps.get(dependent)) {
|
||||||
|
if (!this._nameToTask.has(dependency)) {
|
||||||
|
unknownDeps.push(dependency);
|
||||||
|
} else {
|
||||||
|
graph.addDependency(dependent, dependency);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _.fromPairs(entries);
|
if (unknownDeps.length) {
|
||||||
|
error = this._newUnknownDepsError(dependency, unknownDeps);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
try {
|
||||||
|
// Get standalone tasks with no dependencies and no dependents.
|
||||||
|
parallel = graph.overallOrder(true).filter((task) => !graph.dependentsOf(task).length);
|
||||||
|
// Get tasks with dependencies, in a correctly ordered list.
|
||||||
|
sequential = graph.overallOrder().filter((task) => !parallel.includes(task));
|
||||||
|
} catch (e) {
|
||||||
|
error = this._newDependencyCycleError(e.cyclePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
error,
|
||||||
|
parallel,
|
||||||
|
sequential,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_rejectIfRunning() {
|
||||||
|
if (this.running) {
|
||||||
|
return Promise.reject(this._newStateError());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_throwIfRunning() {
|
||||||
|
if (this.running) {
|
||||||
|
throw this._newStateError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_throwIfUnknownDeps(dependent, unknownDeps) {
|
||||||
|
if (!unknownDeps.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw this._newUnknownDepsError(dependent, unknownDeps);
|
||||||
|
}
|
||||||
|
|
||||||
|
addTask(task) {
|
||||||
|
ow(task, v.checkTaskOrString);
|
||||||
|
|
||||||
|
this._throwIfRunning();
|
||||||
|
|
||||||
|
this._nameToTask.set(task.name, task);
|
||||||
|
if (task.dependsOn) {
|
||||||
|
this._deps.set(task.name, task.dependsOn);
|
||||||
|
}
|
||||||
|
this._taskToName.set(task, task.name);
|
||||||
|
this._addTaskEmitters(task);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
addTasks(tasks) {
|
||||||
|
ow(tasks, ow.any(ow.array, ow.object));
|
||||||
|
this._addOrRemoveTasks(tasks, this.addTask, 'add');
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
end() {
|
||||||
|
this.emit('end', {
|
||||||
|
error: this._error,
|
||||||
|
});
|
||||||
|
this._queue.clear();
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTask(task) {
|
||||||
|
let unsubscribers;
|
||||||
|
|
||||||
|
ow(task, v.checkTaskOrString);
|
||||||
|
this._throwIfRunning();
|
||||||
|
|
||||||
|
if (typeof task === 'string') {
|
||||||
|
task = this._nameToTask.get(task);
|
||||||
|
|
||||||
|
if (!task) {
|
||||||
|
throw new v.UnknownTaskError(`Unknown task: ${task}`);
|
||||||
|
}
|
||||||
|
} else if (typeof task === 'object') {
|
||||||
|
if (!this._taskToName.has(task)) {
|
||||||
|
throw new v.UnknownTaskError(`Unknown task: ${task}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._nameToTask.delete(task.name);
|
||||||
|
this._taskToName.delete(task);
|
||||||
|
this._deps.delete(task.name);
|
||||||
|
|
||||||
|
unsubscribers = this._unsubscribers.get(task.name);
|
||||||
|
for (const u of Object.keys(unsubscribers)) {
|
||||||
|
unsubscribers[u]();
|
||||||
|
}
|
||||||
|
this._unsubscribers.delete(task.name);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTasks(tasks) {
|
||||||
|
ow(tasks, ow.any(ow.array, ow.object));
|
||||||
|
this._addOrRemoveTasks(tasks, this.removeTask, 'remove');
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
run(context) {
|
||||||
|
ow(context, ow.optional.object);
|
||||||
|
|
||||||
|
let endPromise;
|
||||||
|
const { error, parallel, sequential } = this._orderTasks();
|
||||||
|
let runningPromise;
|
||||||
|
let taskFuncs = [];
|
||||||
|
let taskSequence;
|
||||||
|
|
||||||
|
// First, fail if the runner is already running.
|
||||||
|
runningPromise = this._rejectIfRunning();
|
||||||
|
if (runningPromise) {
|
||||||
|
return runningPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then fail if the tasks couldn't be ordered.
|
||||||
|
if (error) {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._context = context || this._context;
|
||||||
|
|
||||||
|
for (const taskName of parallel) {
|
||||||
|
taskFuncs.push(this._bindTaskFunc(this._nameToTask.get(taskName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
taskSequence = this._createTaskSequence(sequential);
|
||||||
|
if (taskSequence) {
|
||||||
|
taskFuncs.push(taskSequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
endPromise = this._queue.addAll(taskFuncs).then(
|
||||||
|
() => {
|
||||||
|
this.end();
|
||||||
|
|
||||||
|
if (this._error) {
|
||||||
|
return Promise.reject(this._error);
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(e) => {
|
||||||
|
this.end();
|
||||||
|
|
||||||
|
return Promise.reject(e);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.emit('start');
|
||||||
|
this._running = true;
|
||||||
|
try {
|
||||||
|
this._queue.start();
|
||||||
|
|
||||||
|
return endPromise;
|
||||||
|
} catch (e) {
|
||||||
|
this._error = e;
|
||||||
|
this.end();
|
||||||
|
|
||||||
|
return Promise.reject(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get running() {
|
||||||
|
return this._running;
|
||||||
|
}
|
||||||
|
|
||||||
|
get tasks() {
|
||||||
|
const entries = [];
|
||||||
|
|
||||||
|
for (const entry of this._nameToTask.entries()) {
|
||||||
|
entries.push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _.fromPairs(entries);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,52 +1,49 @@
|
|||||||
const Emittery = require('emittery');
|
const Emittery = require('emittery');
|
||||||
const {default: ow} = require('ow');
|
const { default: ow } = require('ow');
|
||||||
|
|
||||||
module.exports = class Task extends Emittery {
|
module.exports = class Task extends Emittery {
|
||||||
constructor(opts = {}) {
|
constructor(opts = {}) {
|
||||||
let deps;
|
let deps;
|
||||||
|
|
||||||
super();
|
super();
|
||||||
|
|
||||||
ow(opts.name, ow.optional.string);
|
ow(opts.name, ow.optional.string);
|
||||||
ow(opts.func, ow.optional.function);
|
ow(opts.func, ow.optional.function);
|
||||||
ow(opts.dependsOn, ow.any(
|
ow(opts.dependsOn, ow.any(ow.optional.string, ow.optional.array.ofType(ow.string)));
|
||||||
ow.optional.string,
|
|
||||||
ow.optional.array.ofType(ow.string)
|
|
||||||
));
|
|
||||||
|
|
||||||
if (typeof opts.dependsOn === 'string') {
|
if (typeof opts.dependsOn === 'string') {
|
||||||
deps = [opts.dependsOn];
|
deps = [opts.dependsOn];
|
||||||
} else if (Array.isArray(opts.dependsOn)) {
|
} else if (Array.isArray(opts.dependsOn)) {
|
||||||
deps = opts.dependsOn.slice(0);
|
deps = opts.dependsOn.slice(0);
|
||||||
}
|
|
||||||
|
|
||||||
this.name = opts.name || null;
|
|
||||||
this.func = opts.func || null;
|
|
||||||
this.dependsOn = deps || [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
run(context) {
|
this.name = opts.name || null;
|
||||||
ow(this.name, ow.string);
|
this.func = opts.func || null;
|
||||||
ow(this.func, ow.function);
|
this.dependsOn = deps || [];
|
||||||
ow(this.dependsOn, ow.array.ofType(ow.string));
|
}
|
||||||
|
|
||||||
this.emit('start', this);
|
run(context) {
|
||||||
|
ow(this.name, ow.string);
|
||||||
|
ow(this.func, ow.function);
|
||||||
|
ow(this.dependsOn, ow.array.ofType(ow.string));
|
||||||
|
|
||||||
return this.func(context).then(
|
this.emit('start', this);
|
||||||
() => {
|
|
||||||
this.emit('end', this);
|
|
||||||
|
|
||||||
return Promise.resolve();
|
return this.func(context).then(
|
||||||
},
|
() => {
|
||||||
error => {
|
this.emit('end', this);
|
||||||
this.emit('error', {
|
|
||||||
task: this,
|
|
||||||
error
|
|
||||||
});
|
|
||||||
this.emit('end', this);
|
|
||||||
|
|
||||||
return Promise.reject(error);
|
return Promise.resolve();
|
||||||
}
|
},
|
||||||
);
|
(error) => {
|
||||||
}
|
this.emit('error', {
|
||||||
|
task: this,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
this.emit('end', this);
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,47 +1,47 @@
|
|||||||
const {default: ow} = require('ow');
|
const { default: ow } = require('ow');
|
||||||
const Task = require('./task');
|
const Task = require('./task');
|
||||||
|
|
||||||
function checkTask(t) {
|
function checkTask(t) {
|
||||||
return {
|
return {
|
||||||
validator: t instanceof Task,
|
validator: t instanceof Task,
|
||||||
message: `Expected ${t} to be a Task object`
|
message: `Expected ${t} to be a Task object`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
checkTaskOrString: ow.any(ow.object.validate(checkTask), ow.string),
|
checkTaskOrString: ow.any(ow.object.validate(checkTask), ow.string),
|
||||||
DependencyCycleError: class DependencyCycleError extends Error {
|
DependencyCycleError: class DependencyCycleError extends Error {
|
||||||
constructor(message, cyclePath) {
|
constructor(message, cyclePath) {
|
||||||
ow(message, ow.string);
|
ow(message, ow.string);
|
||||||
ow(cyclePath, ow.array.ofType(ow.string));
|
ow(cyclePath, ow.array.ofType(ow.string));
|
||||||
super(message);
|
super(message);
|
||||||
|
|
||||||
this.cyclePath = cyclePath;
|
this.cyclePath = cyclePath;
|
||||||
this.name = 'DependencyCycleError';
|
this.name = 'DependencyCycleError';
|
||||||
}
|
|
||||||
},
|
|
||||||
StateError: class StateError extends Error {
|
|
||||||
constructor(message) {
|
|
||||||
ow(message, ow.string);
|
|
||||||
super(message);
|
|
||||||
|
|
||||||
this.name = 'StateError';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
UnknownDependencyError: class UnknownDependencyError extends Error {
|
|
||||||
constructor(message) {
|
|
||||||
ow(message, ow.string);
|
|
||||||
super(message);
|
|
||||||
|
|
||||||
this.name = 'UnknownDependencyError';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
UnknownTaskError: class UnknownTaskError extends Error {
|
|
||||||
constructor(message) {
|
|
||||||
ow(message, ow.string);
|
|
||||||
super(message);
|
|
||||||
|
|
||||||
this.name = 'UnknownTaskError';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
StateError: class StateError extends Error {
|
||||||
|
constructor(message) {
|
||||||
|
ow(message, ow.string);
|
||||||
|
super(message);
|
||||||
|
|
||||||
|
this.name = 'StateError';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UnknownDependencyError: class UnknownDependencyError extends Error {
|
||||||
|
constructor(message) {
|
||||||
|
ow(message, ow.string);
|
||||||
|
super(message);
|
||||||
|
|
||||||
|
this.name = 'UnknownDependencyError';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UnknownTaskError: class UnknownTaskError extends Error {
|
||||||
|
constructor(message) {
|
||||||
|
ow(message, ow.string);
|
||||||
|
super(message);
|
||||||
|
|
||||||
|
this.name = 'UnknownTaskError';
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
160
packages/jsdoc-task-runner/package-lock.json
generated
160
packages/jsdoc-task-runner/package-lock.json
generated
@ -1,8 +1,166 @@
|
|||||||
{
|
{
|
||||||
"name": "@jsdoc/task-runner",
|
"name": "@jsdoc/task-runner",
|
||||||
"version": "0.1.10",
|
"version": "0.1.10",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "@jsdoc/task-runner",
|
||||||
|
"version": "0.1.10",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"dependency-graph": "^0.11.0",
|
||||||
|
"emittery": "^0.10.0",
|
||||||
|
"ow": "^0.27.0",
|
||||||
|
"p-queue": "^6.6.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=v14.17.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sindresorhus/is": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/callsites": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dependency-graph": {
|
||||||
|
"version": "0.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz",
|
||||||
|
"integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dot-prop": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-obj": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/emittery": {
|
||||||
|
"version": "0.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.0.tgz",
|
||||||
|
"integrity": "sha512-AGvFfs+d0JKCJQ4o01ASQLGPmSCxgfU9RFXvzPvZdjKK8oscynksuJhWrSTSw7j7Ep/sZct5b5ZhYCi8S/t0HQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sindresorhus/emittery?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/eventemitter3": {
|
||||||
|
"version": "4.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||||
|
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
|
||||||
|
},
|
||||||
|
"node_modules/is-obj": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lodash.isequal": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||||
|
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
|
||||||
|
},
|
||||||
|
"node_modules/ow": {
|
||||||
|
"version": "0.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ow/-/ow-0.27.0.tgz",
|
||||||
|
"integrity": "sha512-SGnrGUbhn4VaUGdU0EJLMwZWSupPmF46hnTRII7aCLCrqixTAC5eKo8kI4/XXf1eaaI8YEVT+3FeGNJI9himAQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sindresorhus/is": "^4.0.1",
|
||||||
|
"callsites": "^3.1.0",
|
||||||
|
"dot-prop": "^6.0.1",
|
||||||
|
"lodash.isequal": "^4.5.0",
|
||||||
|
"type-fest": "^1.2.1",
|
||||||
|
"vali-date": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-finally": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-queue": {
|
||||||
|
"version": "6.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz",
|
||||||
|
"integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"eventemitter3": "^4.0.4",
|
||||||
|
"p-timeout": "^3.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-timeout": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
|
||||||
|
"dependencies": {
|
||||||
|
"p-finally": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/type-fest": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-pfkPYCcuV0TJoo/jlsUeWNV8rk7uMU6ocnYNvca1Vu+pyKi8Rl8Zo2scPt9O72gCsXIm+dMxOOWuA3VFDSdzWA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vali-date": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sindresorhus/is": {
|
"@sindresorhus/is": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
|
|||||||
@ -1,23 +1,23 @@
|
|||||||
const taskRunner = require('../../index');
|
const taskRunner = require('../../index');
|
||||||
|
|
||||||
describe('@jsdoc/task-runner', () => {
|
describe('@jsdoc/task-runner', () => {
|
||||||
it('is an object', () => {
|
it('is an object', () => {
|
||||||
expect(taskRunner).toBeObject();
|
expect(taskRunner).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Task', () => {
|
||||||
|
it('is lib/task', () => {
|
||||||
|
const Task = require('../../lib/task');
|
||||||
|
|
||||||
|
expect(taskRunner.Task).toBe(Task);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Task', () => {
|
describe('TaskRunner', () => {
|
||||||
it('is lib/task', () => {
|
it('is lib/task-runner', () => {
|
||||||
const Task = require('../../lib/task');
|
const TaskRunner = require('../../lib/task-runner');
|
||||||
|
|
||||||
expect(taskRunner.Task).toBe(Task);
|
expect(taskRunner.TaskRunner).toBe(TaskRunner);
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('TaskRunner', () => {
|
|
||||||
it('is lib/task-runner', () => {
|
|
||||||
const TaskRunner = require('../../lib/task-runner');
|
|
||||||
|
|
||||||
expect(taskRunner.TaskRunner).toBe(TaskRunner);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -4,201 +4,200 @@ const Task = require('../../../lib/task');
|
|||||||
const ARGUMENT_ERROR = 'ArgumentError';
|
const ARGUMENT_ERROR = 'ArgumentError';
|
||||||
|
|
||||||
describe('@jsdoc/task-runner/lib/task', () => {
|
describe('@jsdoc/task-runner/lib/task', () => {
|
||||||
it('is a function', () => {
|
it('is a function', () => {
|
||||||
expect(Task).toBeFunction();
|
expect(Task).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('inherits from emittery', () => {
|
||||||
|
expect(new Task() instanceof Emittery).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be constructed with no arguments', () => {
|
||||||
|
function factory() {
|
||||||
|
return new Task();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(factory).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the provided name', () => {
|
||||||
|
const task = new Task({ name: 'foo' });
|
||||||
|
|
||||||
|
expect(task.name).toBe('foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the provided function', () => {
|
||||||
|
const func = () => Promise.resolve();
|
||||||
|
const task = new Task({ func });
|
||||||
|
|
||||||
|
expect(task.func).toBe(func);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dependsOn', () => {
|
||||||
|
it('accepts an array of task names as dependencies', () => {
|
||||||
|
const dependsOn = ['bar', 'baz'];
|
||||||
|
const task = new Task({
|
||||||
|
name: 'foo',
|
||||||
|
func: () => Promise.resolve(),
|
||||||
|
dependsOn,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(task.dependsOn).toEqual(dependsOn);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('inherits from emittery', () => {
|
it('accepts a single task name as a dependency', () => {
|
||||||
expect(new Task() instanceof Emittery).toBeTrue();
|
const task = new Task({
|
||||||
|
name: 'foo',
|
||||||
|
func: () => Promise.resolve(),
|
||||||
|
dependsOn: 'bar',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(task.dependsOn).toEqual(['bar']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be constructed with no arguments', () => {
|
it('fails with non-string, non-array dependencies', () => {
|
||||||
function factory() {
|
function factory() {
|
||||||
return new Task();
|
return new Task({
|
||||||
|
name: 'foo',
|
||||||
|
func: () => Promise.resolve(),
|
||||||
|
dependsOn: 7,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(factory).toThrowErrorOfType(ARGUMENT_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails with non-string arrays of dependencies', () => {
|
||||||
|
function factory() {
|
||||||
|
return new Task({
|
||||||
|
name: 'foo',
|
||||||
|
func: () => Promise.resolve(),
|
||||||
|
dependsOn: [7],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(factory).toThrowErrorOfType(ARGUMENT_ERROR);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the provided dependencies', () => {
|
||||||
|
const dependsOn = ['foo', 'bar'];
|
||||||
|
const task = new Task({ dependsOn });
|
||||||
|
|
||||||
|
expect(task.dependsOn).toEqual(dependsOn);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('run', () => {
|
||||||
|
it('requires a name', async () => {
|
||||||
|
let error;
|
||||||
|
|
||||||
|
async function start() {
|
||||||
|
const task = new Task({
|
||||||
|
func: () => Promise.resolve(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await task.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await start();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('requires a function', async () => {
|
||||||
|
let error;
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
const task = new Task({
|
||||||
|
name: 'foo',
|
||||||
|
});
|
||||||
|
|
||||||
|
await task.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await run();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts a context object', async () => {
|
||||||
|
const context = {};
|
||||||
|
const task = new Task({
|
||||||
|
name: 'foo',
|
||||||
|
func: (c) => {
|
||||||
|
c.foo = 'bar';
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await task.run(context);
|
||||||
|
|
||||||
|
expect(context.foo).toBe('bar');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('events', () => {
|
||||||
|
it('emits a `start` event', async () => {
|
||||||
|
let event;
|
||||||
|
const task = new Task({
|
||||||
|
name: 'foo',
|
||||||
|
func: () => Promise.resolve(),
|
||||||
|
});
|
||||||
|
|
||||||
|
task.on('start', (e) => {
|
||||||
|
event = e;
|
||||||
|
});
|
||||||
|
|
||||||
|
await task.run();
|
||||||
|
|
||||||
|
expect(event).toBe(task);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits an `end` event', async () => {
|
||||||
|
let event;
|
||||||
|
const task = new Task({
|
||||||
|
name: 'foo',
|
||||||
|
func: () => Promise.resolve(),
|
||||||
|
});
|
||||||
|
|
||||||
|
task.on('end', (e) => {
|
||||||
|
event = e;
|
||||||
|
});
|
||||||
|
|
||||||
|
await task.run();
|
||||||
|
|
||||||
|
expect(event).toBe(task);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits an `error` event if necessary', async () => {
|
||||||
|
let error = new Error('oh no!');
|
||||||
|
let event;
|
||||||
|
const task = new Task({
|
||||||
|
name: 'foo',
|
||||||
|
func: () => Promise.reject(error),
|
||||||
|
});
|
||||||
|
|
||||||
|
task.on('error', (e) => {
|
||||||
|
event = e;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await task.run();
|
||||||
|
} catch (e) {
|
||||||
|
// Expected behavior.
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(factory).not.toThrow();
|
expect(event.error).toBe(error);
|
||||||
});
|
expect(event.task).toBe(task);
|
||||||
|
});
|
||||||
it('uses the provided name', () => {
|
|
||||||
const task = new Task({ name: 'foo' });
|
|
||||||
|
|
||||||
expect(task.name).toBe('foo');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses the provided function', () => {
|
|
||||||
const func = () => Promise.resolve();
|
|
||||||
const task = new Task({ func });
|
|
||||||
|
|
||||||
expect(task.func).toBe(func);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('dependsOn', () => {
|
|
||||||
it('accepts an array of task names as dependencies', () => {
|
|
||||||
const dependsOn = ['bar', 'baz'];
|
|
||||||
const task = new Task({
|
|
||||||
name: 'foo',
|
|
||||||
func: () => Promise.resolve(),
|
|
||||||
dependsOn
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(task.dependsOn).toEqual(dependsOn);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('accepts a single task name as a dependency', () => {
|
|
||||||
const task = new Task({
|
|
||||||
name: 'foo',
|
|
||||||
func: () => Promise.resolve(),
|
|
||||||
dependsOn: 'bar'
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(task.dependsOn).toEqual(['bar']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails with non-string, non-array dependencies', () => {
|
|
||||||
function factory() {
|
|
||||||
return new Task({
|
|
||||||
name: 'foo',
|
|
||||||
func: () => Promise.resolve(),
|
|
||||||
dependsOn: 7
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(factory).toThrowErrorOfType(ARGUMENT_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails with non-string arrays of dependencies', () => {
|
|
||||||
function factory() {
|
|
||||||
return new Task({
|
|
||||||
name: 'foo',
|
|
||||||
func: () => Promise.resolve(),
|
|
||||||
dependsOn: [7]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(factory).toThrowErrorOfType(ARGUMENT_ERROR);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses the provided dependencies', () => {
|
|
||||||
const dependsOn = ['foo', 'bar'];
|
|
||||||
const task = new Task({ dependsOn });
|
|
||||||
|
|
||||||
expect(task.dependsOn).toEqual(dependsOn);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('run', () => {
|
|
||||||
it('requires a name', async () => {
|
|
||||||
let error;
|
|
||||||
|
|
||||||
async function start() {
|
|
||||||
const task = new Task({
|
|
||||||
func: () => Promise.resolve()
|
|
||||||
});
|
|
||||||
|
|
||||||
await task.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await start();
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('requires a function', async () => {
|
|
||||||
let error;
|
|
||||||
|
|
||||||
async function run() {
|
|
||||||
const task = new Task({
|
|
||||||
name: 'foo'
|
|
||||||
});
|
|
||||||
|
|
||||||
await task.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await run();
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('accepts a context object', async () => {
|
|
||||||
const context = {};
|
|
||||||
const task = new Task({
|
|
||||||
name: 'foo',
|
|
||||||
func: c => {
|
|
||||||
c.foo = 'bar';
|
|
||||||
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await task.run(context);
|
|
||||||
|
|
||||||
expect(context.foo).toBe('bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('events', () => {
|
|
||||||
it('emits a `start` event', async () => {
|
|
||||||
let event;
|
|
||||||
const task = new Task({
|
|
||||||
name: 'foo',
|
|
||||||
func: () => Promise.resolve()
|
|
||||||
});
|
|
||||||
|
|
||||||
task.on('start', e => {
|
|
||||||
event = e;
|
|
||||||
});
|
|
||||||
|
|
||||||
await task.run();
|
|
||||||
|
|
||||||
expect(event).toBe(task);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('emits an `end` event', async () => {
|
|
||||||
let event;
|
|
||||||
const task = new Task({
|
|
||||||
name: 'foo',
|
|
||||||
func: () => Promise.resolve()
|
|
||||||
});
|
|
||||||
|
|
||||||
task.on('end', e => {
|
|
||||||
event = e;
|
|
||||||
});
|
|
||||||
|
|
||||||
await task.run();
|
|
||||||
|
|
||||||
expect(event).toBe(task);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('emits an `error` event if necessary', async () => {
|
|
||||||
let error = new Error('oh no!');
|
|
||||||
let event;
|
|
||||||
const task = new Task({
|
|
||||||
name: 'foo',
|
|
||||||
func: () => Promise.reject(error)
|
|
||||||
});
|
|
||||||
|
|
||||||
task.on('error', e => {
|
|
||||||
event = e;
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
await task.run();
|
|
||||||
} catch (e) {
|
|
||||||
// Expected behavior.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
expect(event.error).toBe(error);
|
|
||||||
expect(event.task).toBe(task);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,32 +3,32 @@ const { addMatchers } = require('add-matchers');
|
|||||||
require('jasmine-expect');
|
require('jasmine-expect');
|
||||||
|
|
||||||
addMatchers({
|
addMatchers({
|
||||||
toBeError(value) {
|
toBeError(value) {
|
||||||
return value instanceof Error;
|
return value instanceof Error;
|
||||||
},
|
},
|
||||||
toBeErrorOfType(other, value) {
|
toBeErrorOfType(other, value) {
|
||||||
return value instanceof Error && value.name === other;
|
return value instanceof Error && value.name === other;
|
||||||
},
|
},
|
||||||
toBeInstanceOf(other, value) {
|
toBeInstanceOf(other, value) {
|
||||||
let otherName;
|
let otherName;
|
||||||
let valueName;
|
let valueName;
|
||||||
|
|
||||||
if (typeof value !== 'object') {
|
if (typeof value !== 'object') {
|
||||||
throw new TypeError(`Expected object value, got ${typeof value}`);
|
throw new TypeError(`Expected object value, got ${typeof value}`);
|
||||||
}
|
|
||||||
|
|
||||||
valueName = value.constructor.name;
|
|
||||||
|
|
||||||
// Class name.
|
|
||||||
if (typeof other === 'string') {
|
|
||||||
otherName = other;
|
|
||||||
// Class constructor.
|
|
||||||
} else if (typeof other === 'function') {
|
|
||||||
otherName = other.name;
|
|
||||||
} else {
|
|
||||||
otherName = other.constructor.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueName === otherName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
valueName = value.constructor.name;
|
||||||
|
|
||||||
|
// Class name.
|
||||||
|
if (typeof other === 'string') {
|
||||||
|
otherName = other;
|
||||||
|
// Class constructor.
|
||||||
|
} else if (typeof other === 'function') {
|
||||||
|
otherName = other.name;
|
||||||
|
} else {
|
||||||
|
otherName = other.constructor.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return valueName === otherName;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
29
packages/jsdoc-test-matchers/package-lock.json
generated
29
packages/jsdoc-test-matchers/package-lock.json
generated
@ -1,8 +1,35 @@
|
|||||||
{
|
{
|
||||||
"name": "@jsdoc/test-matchers",
|
"name": "@jsdoc/test-matchers",
|
||||||
"version": "0.1.6",
|
"version": "0.1.6",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "@jsdoc/test-matchers",
|
||||||
|
"version": "0.1.6",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"add-matchers": "^0.6.2",
|
||||||
|
"jasmine-expect": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=v14.17.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/add-matchers": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/add-matchers/-/add-matchers-0.6.2.tgz",
|
||||||
|
"integrity": "sha512-hVO2wodMei9RF00qe+506MoeJ/NEOdCMEkSJ12+fC3hx/5Z4zmhNiP92nJEF6XhmXokeB0hOtuQrjHCx2vmXrQ=="
|
||||||
|
},
|
||||||
|
"node_modules/jasmine-expect": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jasmine-expect/-/jasmine-expect-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-byn1zq0EQBA9UKs5A+H6gk5TRcanV+TqQMRxrjurGuqKkclaqgjw/vV6aT/jtf5tabXGonTH6VDZJ33Z1pxSxw==",
|
||||||
|
"dependencies": {
|
||||||
|
"add-matchers": "0.6.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"add-matchers": {
|
"add-matchers": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
|
|||||||
@ -10,8 +10,8 @@ const fs = require('./lib/fs');
|
|||||||
const log = require('./lib/log');
|
const log = require('./lib/log');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
cast,
|
cast,
|
||||||
EventBus,
|
EventBus,
|
||||||
fs,
|
fs,
|
||||||
log
|
log,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const EventEmitter = require('events').EventEmitter;
|
const EventEmitter = require('events').EventEmitter;
|
||||||
const {default: ow} = require('ow');
|
const { default: ow } = require('ow');
|
||||||
|
|
||||||
let cache = {};
|
let cache = {};
|
||||||
const hasOwnProp = Object.prototype.hasOwnProperty;
|
const hasOwnProp = Object.prototype.hasOwnProperty;
|
||||||
@ -22,31 +22,31 @@ const hasOwnProp = Object.prototype.hasOwnProperty;
|
|||||||
* @extends module:events.EventEmitter
|
* @extends module:events.EventEmitter
|
||||||
*/
|
*/
|
||||||
class EventBus extends EventEmitter {
|
class EventBus extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* Create a new event bus, or retrieve the cached event bus for the ID you specify.
|
* Create a new event bus, or retrieve the cached event bus for the ID you specify.
|
||||||
*
|
*
|
||||||
* @param {(string|Symbol)} id - The ID for the event bus.
|
* @param {(string|Symbol)} id - The ID for the event bus.
|
||||||
* @param {Object} opts - Options for the event bus.
|
* @param {Object} opts - Options for the event bus.
|
||||||
* @param {boolean} [opts.cache=true] - Set to `false` to prevent the event bus from being
|
* @param {boolean} [opts.cache=true] - Set to `false` to prevent the event bus from being
|
||||||
* cached, and to return a new event bus even if there is already an event bus with the same ID.
|
* cached, and to return a new event bus even if there is already an event bus with the same ID.
|
||||||
*/
|
*/
|
||||||
constructor(id, opts = {}) {
|
constructor(id, opts = {}) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
ow(id, ow.any(ow.string, ow.symbol));
|
ow(id, ow.any(ow.string, ow.symbol));
|
||||||
|
|
||||||
const shouldCache = _.isBoolean(opts.cache) ? opts.cache : true;
|
const shouldCache = _.isBoolean(opts.cache) ? opts.cache : true;
|
||||||
|
|
||||||
if (hasOwnProp.call(cache, id) && shouldCache) {
|
if (hasOwnProp.call(cache, id) && shouldCache) {
|
||||||
return cache[id];
|
return cache[id];
|
||||||
}
|
|
||||||
|
|
||||||
this._id = id;
|
|
||||||
|
|
||||||
if (shouldCache) {
|
|
||||||
cache[id] = this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._id = id;
|
||||||
|
|
||||||
|
if (shouldCache) {
|
||||||
|
cache[id] = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = EventBus;
|
module.exports = EventBus;
|
||||||
|
|||||||
@ -13,49 +13,47 @@
|
|||||||
* @return {(string|number|boolean)} The converted value.
|
* @return {(string|number|boolean)} The converted value.
|
||||||
*/
|
*/
|
||||||
function castString(str) {
|
function castString(str) {
|
||||||
let number;
|
let number;
|
||||||
let result;
|
let result;
|
||||||
|
|
||||||
switch (str) {
|
switch (str) {
|
||||||
case 'true':
|
case 'true':
|
||||||
result = true;
|
result = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'false':
|
case 'false':
|
||||||
result = false;
|
result = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'NaN':
|
case 'NaN':
|
||||||
result = NaN;
|
result = NaN;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'null':
|
case 'null':
|
||||||
result = null;
|
result = null;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'undefined':
|
case 'undefined':
|
||||||
result = undefined;
|
result = undefined;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (typeof str === 'string') {
|
if (typeof str === 'string') {
|
||||||
if (str.includes('.')) {
|
if (str.includes('.')) {
|
||||||
number = parseFloat(str);
|
number = parseFloat(str);
|
||||||
}
|
} else {
|
||||||
else {
|
number = parseInt(str, 10);
|
||||||
number = parseInt(str, 10);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (String(number) === str && !isNaN(number)) {
|
if (String(number) === str && !isNaN(number)) {
|
||||||
result = number;
|
result = number;
|
||||||
}
|
} else {
|
||||||
else {
|
result = str;
|
||||||
result = str;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,27 +67,24 @@ function castString(str) {
|
|||||||
* @param {(string|Object|Array)} item - The item whose type or types will be converted.
|
* @param {(string|Object|Array)} item - The item whose type or types will be converted.
|
||||||
* @return {*?} The converted value.
|
* @return {*?} The converted value.
|
||||||
*/
|
*/
|
||||||
const cast = module.exports = item => {
|
const cast = (module.exports = (item) => {
|
||||||
let result;
|
let result;
|
||||||
|
|
||||||
if (Array.isArray(item)) {
|
if (Array.isArray(item)) {
|
||||||
result = [];
|
result = [];
|
||||||
for (let i = 0, l = item.length; i < l; i++) {
|
for (let i = 0, l = item.length; i < l; i++) {
|
||||||
result[i] = cast(item[i]);
|
result[i] = cast(item[i]);
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (typeof item === 'object' && item !== null) {
|
|
||||||
result = {};
|
|
||||||
Object.keys(item).forEach(prop => {
|
|
||||||
result[prop] = cast(item[prop]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (typeof item === 'string') {
|
|
||||||
result = castString(item);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
result = item;
|
|
||||||
}
|
}
|
||||||
|
} else if (typeof item === 'object' && item !== null) {
|
||||||
|
result = {};
|
||||||
|
Object.keys(item).forEach((prop) => {
|
||||||
|
result[prop] = cast(item[prop]);
|
||||||
|
});
|
||||||
|
} else if (typeof item === 'string') {
|
||||||
|
result = castString(item);
|
||||||
|
} else {
|
||||||
|
result = item;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
});
|
||||||
|
|||||||
@ -6,14 +6,14 @@ const _ = require('lodash');
|
|||||||
const klawSync = require('klaw-sync');
|
const klawSync = require('klaw-sync');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
exports.lsSync = ((dir, opts = {}) => {
|
exports.lsSync = (dir, opts = {}) => {
|
||||||
const depth = _.has(opts, 'depth') ? opts.depth : -1;
|
const depth = _.has(opts, 'depth') ? opts.depth : -1;
|
||||||
|
|
||||||
const files = klawSync(dir, {
|
const files = klawSync(dir, {
|
||||||
depthLimit: depth,
|
depthLimit: depth,
|
||||||
filter: (f => !path.basename(f.path).startsWith('.')),
|
filter: (f) => !path.basename(f.path).startsWith('.'),
|
||||||
nodir: true
|
nodir: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
return files.map(f => f.path);
|
return files.map((f) => f.path);
|
||||||
});
|
};
|
||||||
|
|||||||
@ -3,8 +3,8 @@ const EventBus = require('./bus');
|
|||||||
const bus = new EventBus('jsdoc');
|
const bus = new EventBus('jsdoc');
|
||||||
const loggerFuncs = {};
|
const loggerFuncs = {};
|
||||||
|
|
||||||
['debug', 'error', 'info', 'fatal', 'verbose', 'warn'].forEach(fn => {
|
['debug', 'error', 'info', 'fatal', 'verbose', 'warn'].forEach((fn) => {
|
||||||
loggerFuncs[fn] = (...args) => bus.emit(`logger:${fn}`, ...args);
|
loggerFuncs[fn] = (...args) => bus.emit(`logger:${fn}`, ...args);
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = loggerFuncs;
|
module.exports = loggerFuncs;
|
||||||
|
|||||||
119
packages/jsdoc-util/package-lock.json
generated
119
packages/jsdoc-util/package-lock.json
generated
@ -1,8 +1,125 @@
|
|||||||
{
|
{
|
||||||
"name": "@jsdoc/util",
|
"name": "@jsdoc/util",
|
||||||
"version": "0.2.4",
|
"version": "0.2.4",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "@jsdoc/util",
|
||||||
|
"version": "0.2.4",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"klaw-sync": "^6.0.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"ow": "^0.27.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=v14.17.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sindresorhus/is": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/callsites": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dot-prop": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-obj": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/graceful-fs": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
|
||||||
|
},
|
||||||
|
"node_modules/is-obj": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/klaw-sync": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.1.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
|
},
|
||||||
|
"node_modules/lodash.isequal": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||||
|
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
|
||||||
|
},
|
||||||
|
"node_modules/ow": {
|
||||||
|
"version": "0.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ow/-/ow-0.27.0.tgz",
|
||||||
|
"integrity": "sha512-SGnrGUbhn4VaUGdU0EJLMwZWSupPmF46hnTRII7aCLCrqixTAC5eKo8kI4/XXf1eaaI8YEVT+3FeGNJI9himAQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sindresorhus/is": "^4.0.1",
|
||||||
|
"callsites": "^3.1.0",
|
||||||
|
"dot-prop": "^6.0.1",
|
||||||
|
"lodash.isequal": "^4.5.0",
|
||||||
|
"type-fest": "^1.2.1",
|
||||||
|
"vali-date": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/type-fest": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-pfkPYCcuV0TJoo/jlsUeWNV8rk7uMU6ocnYNvca1Vu+pyKi8Rl8Zo2scPt9O72gCsXIm+dMxOOWuA3VFDSdzWA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vali-date": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sindresorhus/is": {
|
"@sindresorhus/is": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
|
|||||||
@ -1,31 +1,31 @@
|
|||||||
const util = require('../../index');
|
const util = require('../../index');
|
||||||
|
|
||||||
describe('@jsdoc/util', () => {
|
describe('@jsdoc/util', () => {
|
||||||
it('is an object', () => {
|
it('is an object', () => {
|
||||||
expect(util).toBeObject();
|
expect(util).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('cast', () => {
|
||||||
|
it('is lib/cast', () => {
|
||||||
|
const cast = require('../../lib/cast');
|
||||||
|
|
||||||
|
expect(util.cast).toBe(cast);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('cast', () => {
|
describe('EventBus', () => {
|
||||||
it('is lib/cast', () => {
|
it('is lib/bus', () => {
|
||||||
const cast = require('../../lib/cast');
|
const bus = require('../../lib/bus');
|
||||||
|
|
||||||
expect(util.cast).toBe(cast);
|
expect(util.EventBus).toBe(bus);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('EventBus', () => {
|
describe('fs', () => {
|
||||||
it('is lib/bus', () => {
|
it('is lib/fs', () => {
|
||||||
const bus = require('../../lib/bus');
|
const fs = require('../../lib/fs');
|
||||||
|
|
||||||
expect(util.EventBus).toBe(bus);
|
expect(util.fs).toBe(fs);
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('fs', () => {
|
|
||||||
it('is lib/fs', () => {
|
|
||||||
const fs = require('../../lib/fs');
|
|
||||||
|
|
||||||
expect(util.fs).toBe(fs);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,66 +1,66 @@
|
|||||||
describe('@jsdoc/util/lib/bus', () => {
|
describe('@jsdoc/util/lib/bus', () => {
|
||||||
const EventBus = require('../../../lib/bus');
|
const EventBus = require('../../../lib/bus');
|
||||||
const EventEmitter = require('events').EventEmitter;
|
const EventEmitter = require('events').EventEmitter;
|
||||||
|
|
||||||
const ignoreCache = { cache: false };
|
const ignoreCache = { cache: false };
|
||||||
|
|
||||||
it('inherits from EventEmitter', () => {
|
it('inherits from EventEmitter', () => {
|
||||||
expect(new EventBus('foo', ignoreCache) instanceof EventEmitter).toBeTrue();
|
expect(new EventBus('foo', ignoreCache) instanceof EventEmitter).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts a string for the ID', () => {
|
||||||
|
function makeBus() {
|
||||||
|
return new EventBus('foo', ignoreCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(makeBus).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts a Symbol for the ID', () => {
|
||||||
|
function makeBus() {
|
||||||
|
return new EventBus(Symbol('foo'), ignoreCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(makeBus).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws on bad IDs', () => {
|
||||||
|
function crashBus() {
|
||||||
|
return new EventBus(true, ignoreCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(crashBus).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses a cache by default', () => {
|
||||||
|
let fired = false;
|
||||||
|
const id = Symbol('cache-test');
|
||||||
|
const bus1 = new EventBus(id);
|
||||||
|
const bus2 = new EventBus(id);
|
||||||
|
|
||||||
|
bus1.once('foo', () => {
|
||||||
|
fired = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('accepts a string for the ID', () => {
|
bus2.emit('foo');
|
||||||
function makeBus() {
|
|
||||||
return new EventBus('foo', ignoreCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(makeBus).not.toThrow();
|
expect(bus1).toBe(bus2);
|
||||||
|
expect(fired).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignores the cache when asked', () => {
|
||||||
|
let fired = false;
|
||||||
|
const id = Symbol('cache-test');
|
||||||
|
const bus1 = new EventBus(id, ignoreCache);
|
||||||
|
const bus2 = new EventBus(id, ignoreCache);
|
||||||
|
|
||||||
|
bus1.once('foo', () => {
|
||||||
|
fired = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('accepts a Symbol for the ID', () => {
|
bus2.emit('foo');
|
||||||
function makeBus() {
|
|
||||||
return new EventBus(Symbol('foo'), ignoreCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(makeBus).not.toThrow();
|
expect(bus1).not.toBe(bus2);
|
||||||
});
|
expect(fired).toBeFalse();
|
||||||
|
});
|
||||||
it('throws on bad IDs', () => {
|
|
||||||
function crashBus() {
|
|
||||||
return new EventBus(true, ignoreCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(crashBus).toThrowError();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses a cache by default', () => {
|
|
||||||
let fired = false;
|
|
||||||
const id = Symbol('cache-test');
|
|
||||||
const bus1 = new EventBus(id);
|
|
||||||
const bus2 = new EventBus(id);
|
|
||||||
|
|
||||||
bus1.once('foo', () => {
|
|
||||||
fired = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
bus2.emit('foo');
|
|
||||||
|
|
||||||
expect(bus1).toBe(bus2);
|
|
||||||
expect(fired).toBeTrue();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('ignores the cache when asked', () => {
|
|
||||||
let fired = false;
|
|
||||||
const id = Symbol('cache-test');
|
|
||||||
const bus1 = new EventBus(id, ignoreCache);
|
|
||||||
const bus2 = new EventBus(id, ignoreCache);
|
|
||||||
|
|
||||||
bus1.once('foo', () => {
|
|
||||||
fired = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
bus2.emit('foo');
|
|
||||||
|
|
||||||
expect(bus1).not.toBe(bus2);
|
|
||||||
expect(fired).toBeFalse();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,66 +1,66 @@
|
|||||||
describe('@jsdoc/util/lib/cast', () => {
|
describe('@jsdoc/util/lib/cast', () => {
|
||||||
const cast = require('../../../lib/cast');
|
const cast = require('../../../lib/cast');
|
||||||
|
|
||||||
it('is a function', () => {
|
it('is a function', () => {
|
||||||
expect(cast).toBeFunction();
|
expect(cast).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not modify values that are not strings, objects, or arrays', () => {
|
||||||
|
expect(cast(8)).toBe(8);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not modify strings that are neither boolean-ish nor number-ish', () => {
|
||||||
|
expect(cast('hello world')).toBe('hello world');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('casts "true" and "false" to booleans', () => {
|
||||||
|
expect(cast('true')).toBeTrue();
|
||||||
|
expect(cast('false')).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('casts "null" to null', () => {
|
||||||
|
expect(cast('null')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('casts "undefined" to undefined', () => {
|
||||||
|
expect(cast('undefined')).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('casts positive number-ish strings to numbers', () => {
|
||||||
|
expect(cast('17.35')).toBe(17.35);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('casts negative number-ish strings to numbers', () => {
|
||||||
|
expect(cast('-17.35')).toBe(-17.35);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('casts "NaN" to NaN', () => {
|
||||||
|
expect(cast('NaN')).toBeNaN();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('casts values of object properties', () => {
|
||||||
|
expect(cast({ foo: 'true' })).toEqual({ foo: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('casts values of properties in nested objects', () => {
|
||||||
|
const result = cast({
|
||||||
|
foo: {
|
||||||
|
bar: 'true',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not modify values that are not strings, objects, or arrays', () => {
|
expect(result).toEqual({
|
||||||
expect(cast(8)).toBe(8);
|
foo: {
|
||||||
|
bar: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('does not modify strings that are neither boolean-ish nor number-ish', () => {
|
it('casts values in an array', () => {
|
||||||
expect(cast('hello world')).toBe('hello world');
|
expect(cast(['true', '17.35'])).toEqual([true, 17.35]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('casts "true" and "false" to booleans', () => {
|
it('casts values in a nested array', () => {
|
||||||
expect(cast('true')).toBeTrue();
|
expect(cast(['true', ['17.35']])).toEqual([true, [17.35]]);
|
||||||
expect(cast('false')).toBeFalse();
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('casts "null" to null', () => {
|
|
||||||
expect(cast('null')).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('casts "undefined" to undefined', () => {
|
|
||||||
expect(cast('undefined')).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('casts positive number-ish strings to numbers', () => {
|
|
||||||
expect(cast('17.35')).toBe(17.35);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('casts negative number-ish strings to numbers', () => {
|
|
||||||
expect(cast('-17.35')).toBe(-17.35);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('casts "NaN" to NaN', () => {
|
|
||||||
expect(cast('NaN')).toBeNaN();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('casts values of object properties', () => {
|
|
||||||
expect(cast({ foo: 'true' })).toEqual({ foo: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('casts values of properties in nested objects', () => {
|
|
||||||
const result = cast({
|
|
||||||
foo: {
|
|
||||||
bar: 'true'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
foo: {
|
|
||||||
bar: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('casts values in an array', () => {
|
|
||||||
expect(cast(['true', '17.35'])).toEqual([true, 17.35]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('casts values in a nested array', () => {
|
|
||||||
expect(cast(['true', ['17.35']])).toEqual([true, [17.35]]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,71 +1,66 @@
|
|||||||
describe('@jsdoc/util/lib/fs', () => {
|
describe('@jsdoc/util/lib/fs', () => {
|
||||||
const mockFs = require('mock-fs');
|
const mockFs = require('mock-fs');
|
||||||
const fsUtil = require('../../../lib/fs');
|
const fsUtil = require('../../../lib/fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
afterEach(() => mockFs.restore());
|
afterEach(() => mockFs.restore());
|
||||||
|
|
||||||
it('has an lsSync method', () => {
|
it('has an lsSync method', () => {
|
||||||
expect(fsUtil.lsSync).toBeFunction();
|
expect(fsUtil.lsSync).toBeFunction();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('lsSync', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mockFs({
|
||||||
|
head: {
|
||||||
|
eyes: '',
|
||||||
|
ears: '',
|
||||||
|
mouth: '',
|
||||||
|
nose: '',
|
||||||
|
shoulders: {
|
||||||
|
knees: {
|
||||||
|
meniscus: '',
|
||||||
|
toes: {
|
||||||
|
phalanx: '',
|
||||||
|
'.big-toe-phalanx': '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('lsSync', () => {
|
const cwd = process.cwd();
|
||||||
beforeEach(() => {
|
|
||||||
mockFs({
|
|
||||||
head: {
|
|
||||||
eyes: '',
|
|
||||||
ears: '',
|
|
||||||
mouth: '',
|
|
||||||
nose: '',
|
|
||||||
shoulders: {
|
|
||||||
knees: {
|
|
||||||
meniscus: '',
|
|
||||||
toes: {
|
|
||||||
phalanx: '',
|
|
||||||
'.big-toe-phalanx': ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const cwd = process.cwd();
|
function resolvePaths(files) {
|
||||||
|
return files.map((f) => path.join(cwd, f)).sort();
|
||||||
|
}
|
||||||
|
|
||||||
function resolvePaths(files) {
|
const allFiles = resolvePaths([
|
||||||
return files.map(f => path.join(cwd, f)).sort();
|
'head/eyes',
|
||||||
}
|
'head/ears',
|
||||||
|
'head/mouth',
|
||||||
|
'head/nose',
|
||||||
|
'head/shoulders/knees/meniscus',
|
||||||
|
'head/shoulders/knees/toes/phalanx',
|
||||||
|
]);
|
||||||
|
|
||||||
const allFiles = resolvePaths([
|
it('gets all non-hidden files from all levels by default', () => {
|
||||||
'head/eyes',
|
const files = fsUtil.lsSync(cwd).sort();
|
||||||
'head/ears',
|
|
||||||
'head/mouth',
|
|
||||||
'head/nose',
|
|
||||||
'head/shoulders/knees/meniscus',
|
|
||||||
'head/shoulders/knees/toes/phalanx'
|
|
||||||
]);
|
|
||||||
|
|
||||||
it('gets all non-hidden files from all levels by default', () => {
|
expect(files).toEqual(allFiles);
|
||||||
const files = fsUtil.lsSync(cwd).sort();
|
|
||||||
|
|
||||||
expect(files).toEqual(allFiles);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('limits recursion depth when asked', () => {
|
|
||||||
const files = fsUtil.lsSync(cwd, { depth: 1 }).sort();
|
|
||||||
|
|
||||||
expect(files).toEqual(resolvePaths([
|
|
||||||
'head/eyes',
|
|
||||||
'head/ears',
|
|
||||||
'head/mouth',
|
|
||||||
'head/nose'
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('treats a depth of -1 as infinite', () => {
|
|
||||||
const files = fsUtil.lsSync('head', { depth: -1 }).sort();
|
|
||||||
|
|
||||||
expect(files).toEqual(allFiles);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('limits recursion depth when asked', () => {
|
||||||
|
const files = fsUtil.lsSync(cwd, { depth: 1 }).sort();
|
||||||
|
|
||||||
|
expect(files).toEqual(resolvePaths(['head/eyes', 'head/ears', 'head/mouth', 'head/nose']));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('treats a depth of -1 as infinite', () => {
|
||||||
|
const files = fsUtil.lsSync('head', { depth: -1 }).sort();
|
||||||
|
|
||||||
|
expect(files).toEqual(allFiles);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,33 +1,33 @@
|
|||||||
describe('@jsdoc/util/lib/log', () => {
|
describe('@jsdoc/util/lib/log', () => {
|
||||||
const EventBus = require('../../../lib/bus');
|
const EventBus = require('../../../lib/bus');
|
||||||
const log = require('../../../lib/log');
|
const log = require('../../../lib/log');
|
||||||
|
|
||||||
const fns = ['debug', 'error', 'info', 'fatal', 'verbose', 'warn'];
|
const fns = ['debug', 'error', 'info', 'fatal', 'verbose', 'warn'];
|
||||||
|
|
||||||
it('is an object', () => {
|
it('is an object', () => {
|
||||||
expect(log).toBeObject();
|
expect(log).toBeObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('provides the expected functions', () => {
|
||||||
|
fns.forEach((fn) => {
|
||||||
|
expect(log[fn]).toBeFunction();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('provides the expected functions', () => {
|
describe('functions', () => {
|
||||||
fns.forEach(fn => {
|
const bus = new EventBus('jsdoc');
|
||||||
expect(log[fn]).toBeFunction();
|
|
||||||
});
|
it('sends events to the event bus', () => {
|
||||||
});
|
fns.forEach((fn) => {
|
||||||
|
let event;
|
||||||
describe('functions', () => {
|
|
||||||
const bus = new EventBus('jsdoc');
|
bus.once(`logger:${fn}`, (e) => {
|
||||||
|
event = e;
|
||||||
it('sends events to the event bus', () => {
|
|
||||||
fns.forEach(fn => {
|
|
||||||
let event;
|
|
||||||
|
|
||||||
bus.once(`logger:${fn}`, e => {
|
|
||||||
event = e;
|
|
||||||
});
|
|
||||||
log[fn]('testing');
|
|
||||||
|
|
||||||
expect(event).toBe('testing');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
log[fn]('testing');
|
||||||
|
|
||||||
|
expect(event).toBe('testing');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -15,398 +15,383 @@ const Promise = require('bluebird');
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
module.exports = (() => {
|
module.exports = (() => {
|
||||||
const props = {
|
const props = {
|
||||||
docs: [],
|
docs: [],
|
||||||
packageJson: null,
|
packageJson: null,
|
||||||
shouldExitWithError: false,
|
shouldExitWithError: false,
|
||||||
shouldPrintHelp: false,
|
shouldPrintHelp: false,
|
||||||
tmpdir: null
|
tmpdir: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const bus = new EventBus('jsdoc');
|
||||||
|
const cli = {};
|
||||||
|
const engine = new Engine();
|
||||||
|
const FATAL_ERROR_MESSAGE =
|
||||||
|
'Exiting JSDoc because an error occurred. See the previous log ' + 'messages for details.';
|
||||||
|
const LOG_LEVELS = Engine.LOG_LEVELS;
|
||||||
|
|
||||||
|
// TODO: docs
|
||||||
|
cli.setVersionInfo = () => {
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// allow this to throw--something is really wrong if we can't read our own package file
|
||||||
|
const info = JSON.parse(
|
||||||
|
stripBom(fs.readFileSync(path.join(env.dirname, 'package.json'), 'utf8'))
|
||||||
|
);
|
||||||
|
const revision = new Date(parseInt(info.revision, 10));
|
||||||
|
|
||||||
|
env.version = {
|
||||||
|
number: info.version,
|
||||||
|
revision: revision.toUTCString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const bus = new EventBus('jsdoc');
|
engine.version = env.version.number;
|
||||||
const cli = {};
|
engine.revision = revision;
|
||||||
const engine = new Engine();
|
|
||||||
const FATAL_ERROR_MESSAGE = 'Exiting JSDoc because an error occurred. See the previous log ' +
|
|
||||||
'messages for details.';
|
|
||||||
const LOG_LEVELS = Engine.LOG_LEVELS;
|
|
||||||
|
|
||||||
// TODO: docs
|
|
||||||
cli.setVersionInfo = () => {
|
|
||||||
const fs = require('fs');
|
|
||||||
|
|
||||||
// allow this to throw--something is really wrong if we can't read our own package file
|
|
||||||
const info = JSON.parse(stripBom(fs.readFileSync(path.join(env.dirname, 'package.json'),
|
|
||||||
'utf8')));
|
|
||||||
const revision = new Date(parseInt(info.revision, 10));
|
|
||||||
|
|
||||||
env.version = {
|
|
||||||
number: info.version,
|
|
||||||
revision: revision.toUTCString()
|
|
||||||
};
|
|
||||||
|
|
||||||
engine.version = env.version.number;
|
|
||||||
engine.revision = revision;
|
|
||||||
|
|
||||||
return cli;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: docs
|
|
||||||
cli.loadConfig = () => {
|
|
||||||
const _ = require('lodash');
|
|
||||||
let conf;
|
|
||||||
|
|
||||||
try {
|
|
||||||
env.opts = engine.parseFlags(env.args);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
props.shouldPrintHelp = true;
|
|
||||||
cli.exit(
|
|
||||||
1,
|
|
||||||
`${e.message}\n`
|
|
||||||
);
|
|
||||||
|
|
||||||
return cli;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
conf = config.loadSync(env.opts.configure);
|
|
||||||
env.conf = conf.config;
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
cli.exit(
|
|
||||||
1,
|
|
||||||
`Cannot parse the config file ${conf.filepath}: ${e}\n${FATAL_ERROR_MESSAGE}`
|
|
||||||
);
|
|
||||||
|
|
||||||
return cli;
|
|
||||||
}
|
|
||||||
|
|
||||||
// look for options on the command line, then in the config
|
|
||||||
env.opts = _.defaults(env.opts, env.conf.opts);
|
|
||||||
|
|
||||||
return cli;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: docs
|
|
||||||
cli.configureLogger = () => {
|
|
||||||
function recoverableError() {
|
|
||||||
props.shouldExitWithError = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fatalError() {
|
|
||||||
cli.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.opts.test) {
|
|
||||||
engine.logLevel = LOG_LEVELS.SILENT;
|
|
||||||
} else {
|
|
||||||
if (env.opts.debug) {
|
|
||||||
engine.logLevel = LOG_LEVELS.DEBUG;
|
|
||||||
}
|
|
||||||
else if (env.opts.verbose) {
|
|
||||||
engine.logLevel = LOG_LEVELS.INFO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.opts.pedantic) {
|
|
||||||
bus.once('logger:warn', recoverableError);
|
|
||||||
bus.once('logger:error', fatalError);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
bus.once('logger:error', recoverableError);
|
|
||||||
}
|
|
||||||
|
|
||||||
bus.once('logger:fatal', fatalError);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cli;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: docs
|
|
||||||
cli.logStart = () => {
|
|
||||||
log.debug(engine.versionDetails);
|
|
||||||
log.debug('Environment info: %j', {
|
|
||||||
env: {
|
|
||||||
conf: env.conf,
|
|
||||||
opts: env.opts
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: docs
|
|
||||||
cli.logFinish = () => {
|
|
||||||
let delta;
|
|
||||||
let deltaSeconds;
|
|
||||||
|
|
||||||
if (env.run.finish && env.run.start) {
|
|
||||||
delta = env.run.finish.getTime() - env.run.start.getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delta !== undefined) {
|
|
||||||
deltaSeconds = (delta / 1000).toFixed(2);
|
|
||||||
log.info(`Finished running in ${deltaSeconds} seconds.`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: docs
|
|
||||||
cli.runCommand = () => {
|
|
||||||
let cmd;
|
|
||||||
const opts = env.opts;
|
|
||||||
|
|
||||||
// If we already need to exit with an error, don't do any more work.
|
|
||||||
if (props.shouldExitWithError) {
|
|
||||||
cmd = () => Promise.resolve(0);
|
|
||||||
}
|
|
||||||
else if (opts.help) {
|
|
||||||
cmd = cli.printHelp;
|
|
||||||
}
|
|
||||||
else if (opts.test) {
|
|
||||||
cmd = cli.runTests;
|
|
||||||
}
|
|
||||||
else if (opts.version) {
|
|
||||||
cmd = cli.printVersion;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cmd = cli.main;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd().then(errorCode => {
|
|
||||||
if (!errorCode && props.shouldExitWithError) {
|
|
||||||
errorCode = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cli.logFinish();
|
|
||||||
cli.exit(errorCode || 0);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: docs
|
|
||||||
cli.printHelp = () => {
|
|
||||||
cli.printVersion();
|
|
||||||
console.log(engine.help({ maxLength: process.stdout.columns }));
|
|
||||||
|
|
||||||
return Promise.resolve(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: docs
|
|
||||||
cli.runTests = () => require('./test')();
|
|
||||||
|
|
||||||
// TODO: docs
|
|
||||||
cli.printVersion = () => {
|
|
||||||
console.log(engine.versionDetails);
|
|
||||||
|
|
||||||
return Promise.resolve(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: docs
|
|
||||||
cli.main = () => {
|
|
||||||
cli.scanFiles();
|
|
||||||
|
|
||||||
if (env.sourceFiles.length === 0) {
|
|
||||||
console.log('There are no input files to process.');
|
|
||||||
|
|
||||||
return Promise.resolve(0);
|
|
||||||
} else {
|
|
||||||
return cli.createParser()
|
|
||||||
.parseFiles()
|
|
||||||
.processParseResults()
|
|
||||||
.then(() => {
|
|
||||||
env.run.finish = new Date();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function readPackageJson(filepath) {
|
|
||||||
const fs = require('fs');
|
|
||||||
|
|
||||||
try {
|
|
||||||
return stripJsonComments( fs.readFileSync(filepath, 'utf8') );
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
log.error(`Unable to read the package file ${filepath}`);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildSourceList() {
|
|
||||||
let packageJson;
|
|
||||||
let sourceFile;
|
|
||||||
let sourceFiles = env.opts._ ? env.opts._.slice(0) : [];
|
|
||||||
|
|
||||||
if (env.conf.source && env.conf.source.include) {
|
|
||||||
sourceFiles = sourceFiles.concat(env.conf.source.include);
|
|
||||||
}
|
|
||||||
|
|
||||||
// load the user-specified package file, if any
|
|
||||||
if (env.opts.package) {
|
|
||||||
packageJson = readPackageJson(env.opts.package);
|
|
||||||
}
|
|
||||||
|
|
||||||
// source files named `package.json` or `README.md` get special treatment, unless the user
|
|
||||||
// explicitly specified a package and/or README file
|
|
||||||
for (let i = 0, l = sourceFiles.length; i < l; i++) {
|
|
||||||
sourceFile = sourceFiles[i];
|
|
||||||
|
|
||||||
if ( !env.opts.package && /\bpackage\.json$/i.test(sourceFile) ) {
|
|
||||||
packageJson = readPackageJson(sourceFile);
|
|
||||||
sourceFiles.splice(i--, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !env.opts.readme && /(\bREADME|\.md)$/i.test(sourceFile) ) {
|
|
||||||
env.opts.readme = sourceFile;
|
|
||||||
sourceFiles.splice(i--, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve the path to the README.
|
|
||||||
if (env.opts.readme) {
|
|
||||||
env.opts.readme = path.resolve(env.opts.readme);
|
|
||||||
}
|
|
||||||
|
|
||||||
props.packageJson = packageJson;
|
|
||||||
|
|
||||||
return sourceFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: docs
|
|
||||||
cli.scanFiles = () => {
|
|
||||||
const { Filter } = require('jsdoc/src/filter');
|
|
||||||
const { Scanner } = require('jsdoc/src/scanner');
|
|
||||||
|
|
||||||
let filter;
|
|
||||||
let scanner;
|
|
||||||
|
|
||||||
env.opts._ = buildSourceList();
|
|
||||||
|
|
||||||
// are there any files to scan and parse?
|
|
||||||
if (env.conf.source && env.opts._.length) {
|
|
||||||
filter = new Filter(env.conf.source);
|
|
||||||
scanner = new Scanner();
|
|
||||||
|
|
||||||
env.sourceFiles = scanner.scan(env.opts._,
|
|
||||||
(env.opts.recurse ? env.conf.recurseDepth : undefined), filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cli;
|
|
||||||
};
|
|
||||||
|
|
||||||
cli.createParser = () => {
|
|
||||||
const handlers = require('jsdoc/src/handlers');
|
|
||||||
const parser = require('jsdoc/src/parser');
|
|
||||||
const plugins = require('jsdoc/plugins');
|
|
||||||
|
|
||||||
props.parser = parser.createParser(env.conf.parser, env.conf);
|
|
||||||
|
|
||||||
if (env.conf.plugins) {
|
|
||||||
plugins.installPlugins(env.conf.plugins, props.parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
handlers.attachTo(props.parser);
|
|
||||||
|
|
||||||
return cli;
|
|
||||||
};
|
|
||||||
|
|
||||||
cli.parseFiles = () => {
|
|
||||||
const augment = require('jsdoc/augment');
|
|
||||||
const borrow = require('jsdoc/borrow');
|
|
||||||
const Package = require('jsdoc/package').Package;
|
|
||||||
|
|
||||||
let docs;
|
|
||||||
let packageDocs;
|
|
||||||
|
|
||||||
props.docs = docs = props.parser.parse(env.sourceFiles, env.opts.encoding);
|
|
||||||
|
|
||||||
// If there is no package.json, just create an empty package
|
|
||||||
packageDocs = new Package(props.packageJson);
|
|
||||||
packageDocs.files = env.sourceFiles || [];
|
|
||||||
docs.push(packageDocs);
|
|
||||||
|
|
||||||
log.debug('Adding inherited symbols, mixins, and interface implementations...');
|
|
||||||
augment.augmentAll(docs);
|
|
||||||
log.debug('Adding borrowed doclets...');
|
|
||||||
borrow.resolveBorrows(docs);
|
|
||||||
log.debug('Post-processing complete.');
|
|
||||||
|
|
||||||
props.parser.fireProcessingComplete(docs);
|
|
||||||
|
|
||||||
return cli;
|
|
||||||
};
|
|
||||||
|
|
||||||
cli.processParseResults = () => {
|
|
||||||
if (env.opts.explain) {
|
|
||||||
cli.dumpParseResults();
|
|
||||||
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return cli.generateDocs();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
cli.dumpParseResults = () => {
|
|
||||||
console.log(JSON.stringify(props.docs, null, 4));
|
|
||||||
|
|
||||||
return cli;
|
|
||||||
};
|
|
||||||
|
|
||||||
cli.generateDocs = () => {
|
|
||||||
let message;
|
|
||||||
const taffy = require('taffydb').taffy;
|
|
||||||
|
|
||||||
let template;
|
|
||||||
|
|
||||||
env.opts.template = env.opts.template || path.join(__dirname, 'templates', 'default');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// TODO: Just look for a `publish` function in the specified module, not a `publish.js`
|
|
||||||
// file _and_ a `publish` function.
|
|
||||||
template = require(`${env.opts.template}/publish`);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
log.fatal(`Unable to load template: ${e.message}` || e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// templates should include a publish.js file that exports a "publish" function
|
|
||||||
if (template.publish && typeof template.publish === 'function') {
|
|
||||||
let publishPromise;
|
|
||||||
|
|
||||||
log.info('Generating output files...');
|
|
||||||
publishPromise = template.publish(
|
|
||||||
taffy(props.docs),
|
|
||||||
env.opts
|
|
||||||
);
|
|
||||||
|
|
||||||
return Promise.resolve(publishPromise);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
message = `${env.opts.template} does not export a "publish" function. ` +
|
|
||||||
'Global "publish" functions are no longer supported.';
|
|
||||||
log.fatal(message);
|
|
||||||
|
|
||||||
return Promise.reject(new Error(message));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: docs
|
|
||||||
cli.exit = (exitCode, message) => {
|
|
||||||
if (exitCode > 0) {
|
|
||||||
props.shouldExitWithError = true;
|
|
||||||
|
|
||||||
if (message) {
|
|
||||||
console.error(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
process.on('exit', () => {
|
|
||||||
if (props.shouldPrintHelp) {
|
|
||||||
cli.printHelp();
|
|
||||||
}
|
|
||||||
|
|
||||||
process.exit(exitCode);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return cli;
|
return cli;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: docs
|
||||||
|
cli.loadConfig = () => {
|
||||||
|
const _ = require('lodash');
|
||||||
|
let conf;
|
||||||
|
|
||||||
|
try {
|
||||||
|
env.opts = engine.parseFlags(env.args);
|
||||||
|
} catch (e) {
|
||||||
|
props.shouldPrintHelp = true;
|
||||||
|
cli.exit(1, `${e.message}\n`);
|
||||||
|
|
||||||
|
return cli;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
conf = config.loadSync(env.opts.configure);
|
||||||
|
env.conf = conf.config;
|
||||||
|
} catch (e) {
|
||||||
|
cli.exit(1, `Cannot parse the config file ${conf.filepath}: ${e}\n${FATAL_ERROR_MESSAGE}`);
|
||||||
|
|
||||||
|
return cli;
|
||||||
|
}
|
||||||
|
|
||||||
|
// look for options on the command line, then in the config
|
||||||
|
env.opts = _.defaults(env.opts, env.conf.opts);
|
||||||
|
|
||||||
|
return cli;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: docs
|
||||||
|
cli.configureLogger = () => {
|
||||||
|
function recoverableError() {
|
||||||
|
props.shouldExitWithError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fatalError() {
|
||||||
|
cli.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (env.opts.test) {
|
||||||
|
engine.logLevel = LOG_LEVELS.SILENT;
|
||||||
|
} else {
|
||||||
|
if (env.opts.debug) {
|
||||||
|
engine.logLevel = LOG_LEVELS.DEBUG;
|
||||||
|
} else if (env.opts.verbose) {
|
||||||
|
engine.logLevel = LOG_LEVELS.INFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (env.opts.pedantic) {
|
||||||
|
bus.once('logger:warn', recoverableError);
|
||||||
|
bus.once('logger:error', fatalError);
|
||||||
|
} else {
|
||||||
|
bus.once('logger:error', recoverableError);
|
||||||
|
}
|
||||||
|
|
||||||
|
bus.once('logger:fatal', fatalError);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cli;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: docs
|
||||||
|
cli.logStart = () => {
|
||||||
|
log.debug(engine.versionDetails);
|
||||||
|
log.debug('Environment info: %j', {
|
||||||
|
env: {
|
||||||
|
conf: env.conf,
|
||||||
|
opts: env.opts,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: docs
|
||||||
|
cli.logFinish = () => {
|
||||||
|
let delta;
|
||||||
|
let deltaSeconds;
|
||||||
|
|
||||||
|
if (env.run.finish && env.run.start) {
|
||||||
|
delta = env.run.finish.getTime() - env.run.start.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delta !== undefined) {
|
||||||
|
deltaSeconds = (delta / 1000).toFixed(2);
|
||||||
|
log.info(`Finished running in ${deltaSeconds} seconds.`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: docs
|
||||||
|
cli.runCommand = () => {
|
||||||
|
let cmd;
|
||||||
|
const opts = env.opts;
|
||||||
|
|
||||||
|
// If we already need to exit with an error, don't do any more work.
|
||||||
|
if (props.shouldExitWithError) {
|
||||||
|
cmd = () => Promise.resolve(0);
|
||||||
|
} else if (opts.help) {
|
||||||
|
cmd = cli.printHelp;
|
||||||
|
} else if (opts.test) {
|
||||||
|
cmd = cli.runTests;
|
||||||
|
} else if (opts.version) {
|
||||||
|
cmd = cli.printVersion;
|
||||||
|
} else {
|
||||||
|
cmd = cli.main;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd().then((errorCode) => {
|
||||||
|
if (!errorCode && props.shouldExitWithError) {
|
||||||
|
errorCode = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cli.logFinish();
|
||||||
|
cli.exit(errorCode || 0);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: docs
|
||||||
|
cli.printHelp = () => {
|
||||||
|
cli.printVersion();
|
||||||
|
console.log(engine.help({ maxLength: process.stdout.columns }));
|
||||||
|
|
||||||
|
return Promise.resolve(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: docs
|
||||||
|
cli.runTests = () => require('./test')();
|
||||||
|
|
||||||
|
// TODO: docs
|
||||||
|
cli.printVersion = () => {
|
||||||
|
console.log(engine.versionDetails);
|
||||||
|
|
||||||
|
return Promise.resolve(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: docs
|
||||||
|
cli.main = () => {
|
||||||
|
cli.scanFiles();
|
||||||
|
|
||||||
|
if (env.sourceFiles.length === 0) {
|
||||||
|
console.log('There are no input files to process.');
|
||||||
|
|
||||||
|
return Promise.resolve(0);
|
||||||
|
} else {
|
||||||
|
return cli
|
||||||
|
.createParser()
|
||||||
|
.parseFiles()
|
||||||
|
.processParseResults()
|
||||||
|
.then(() => {
|
||||||
|
env.run.finish = new Date();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function readPackageJson(filepath) {
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
try {
|
||||||
|
return stripJsonComments(fs.readFileSync(filepath, 'utf8'));
|
||||||
|
} catch (e) {
|
||||||
|
log.error(`Unable to read the package file ${filepath}`);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSourceList() {
|
||||||
|
let packageJson;
|
||||||
|
let sourceFile;
|
||||||
|
let sourceFiles = env.opts._ ? env.opts._.slice(0) : [];
|
||||||
|
|
||||||
|
if (env.conf.source && env.conf.source.include) {
|
||||||
|
sourceFiles = sourceFiles.concat(env.conf.source.include);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the user-specified package file, if any
|
||||||
|
if (env.opts.package) {
|
||||||
|
packageJson = readPackageJson(env.opts.package);
|
||||||
|
}
|
||||||
|
|
||||||
|
// source files named `package.json` or `README.md` get special treatment, unless the user
|
||||||
|
// explicitly specified a package and/or README file
|
||||||
|
for (let i = 0, l = sourceFiles.length; i < l; i++) {
|
||||||
|
sourceFile = sourceFiles[i];
|
||||||
|
|
||||||
|
if (!env.opts.package && /\bpackage\.json$/i.test(sourceFile)) {
|
||||||
|
packageJson = readPackageJson(sourceFile);
|
||||||
|
sourceFiles.splice(i--, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!env.opts.readme && /(\bREADME|\.md)$/i.test(sourceFile)) {
|
||||||
|
env.opts.readme = sourceFile;
|
||||||
|
sourceFiles.splice(i--, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the path to the README.
|
||||||
|
if (env.opts.readme) {
|
||||||
|
env.opts.readme = path.resolve(env.opts.readme);
|
||||||
|
}
|
||||||
|
|
||||||
|
props.packageJson = packageJson;
|
||||||
|
|
||||||
|
return sourceFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: docs
|
||||||
|
cli.scanFiles = () => {
|
||||||
|
const { Filter } = require('jsdoc/src/filter');
|
||||||
|
const { Scanner } = require('jsdoc/src/scanner');
|
||||||
|
|
||||||
|
let filter;
|
||||||
|
let scanner;
|
||||||
|
|
||||||
|
env.opts._ = buildSourceList();
|
||||||
|
|
||||||
|
// are there any files to scan and parse?
|
||||||
|
if (env.conf.source && env.opts._.length) {
|
||||||
|
filter = new Filter(env.conf.source);
|
||||||
|
scanner = new Scanner();
|
||||||
|
|
||||||
|
env.sourceFiles = scanner.scan(
|
||||||
|
env.opts._,
|
||||||
|
env.opts.recurse ? env.conf.recurseDepth : undefined,
|
||||||
|
filter
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cli;
|
||||||
|
};
|
||||||
|
|
||||||
|
cli.createParser = () => {
|
||||||
|
const handlers = require('jsdoc/src/handlers');
|
||||||
|
const parser = require('jsdoc/src/parser');
|
||||||
|
const plugins = require('jsdoc/plugins');
|
||||||
|
|
||||||
|
props.parser = parser.createParser(env.conf.parser, env.conf);
|
||||||
|
|
||||||
|
if (env.conf.plugins) {
|
||||||
|
plugins.installPlugins(env.conf.plugins, props.parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers.attachTo(props.parser);
|
||||||
|
|
||||||
|
return cli;
|
||||||
|
};
|
||||||
|
|
||||||
|
cli.parseFiles = () => {
|
||||||
|
const augment = require('jsdoc/augment');
|
||||||
|
const borrow = require('jsdoc/borrow');
|
||||||
|
const Package = require('jsdoc/package').Package;
|
||||||
|
|
||||||
|
let docs;
|
||||||
|
let packageDocs;
|
||||||
|
|
||||||
|
props.docs = docs = props.parser.parse(env.sourceFiles, env.opts.encoding);
|
||||||
|
|
||||||
|
// If there is no package.json, just create an empty package
|
||||||
|
packageDocs = new Package(props.packageJson);
|
||||||
|
packageDocs.files = env.sourceFiles || [];
|
||||||
|
docs.push(packageDocs);
|
||||||
|
|
||||||
|
log.debug('Adding inherited symbols, mixins, and interface implementations...');
|
||||||
|
augment.augmentAll(docs);
|
||||||
|
log.debug('Adding borrowed doclets...');
|
||||||
|
borrow.resolveBorrows(docs);
|
||||||
|
log.debug('Post-processing complete.');
|
||||||
|
|
||||||
|
props.parser.fireProcessingComplete(docs);
|
||||||
|
|
||||||
|
return cli;
|
||||||
|
};
|
||||||
|
|
||||||
|
cli.processParseResults = () => {
|
||||||
|
if (env.opts.explain) {
|
||||||
|
cli.dumpParseResults();
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
} else {
|
||||||
|
return cli.generateDocs();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
cli.dumpParseResults = () => {
|
||||||
|
console.log(JSON.stringify(props.docs, null, 4));
|
||||||
|
|
||||||
|
return cli;
|
||||||
|
};
|
||||||
|
|
||||||
|
cli.generateDocs = () => {
|
||||||
|
let message;
|
||||||
|
const taffy = require('taffydb').taffy;
|
||||||
|
|
||||||
|
let template;
|
||||||
|
|
||||||
|
env.opts.template = env.opts.template || path.join(__dirname, 'templates', 'default');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO: Just look for a `publish` function in the specified module, not a `publish.js`
|
||||||
|
// file _and_ a `publish` function.
|
||||||
|
template = require(`${env.opts.template}/publish`);
|
||||||
|
} catch (e) {
|
||||||
|
log.fatal(`Unable to load template: ${e.message}` || e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// templates should include a publish.js file that exports a "publish" function
|
||||||
|
if (template.publish && typeof template.publish === 'function') {
|
||||||
|
let publishPromise;
|
||||||
|
|
||||||
|
log.info('Generating output files...');
|
||||||
|
publishPromise = template.publish(taffy(props.docs), env.opts);
|
||||||
|
|
||||||
|
return Promise.resolve(publishPromise);
|
||||||
|
} else {
|
||||||
|
message =
|
||||||
|
`${env.opts.template} does not export a "publish" function. ` +
|
||||||
|
'Global "publish" functions are no longer supported.';
|
||||||
|
log.fatal(message);
|
||||||
|
|
||||||
|
return Promise.reject(new Error(message));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: docs
|
||||||
|
cli.exit = (exitCode, message) => {
|
||||||
|
if (exitCode > 0) {
|
||||||
|
props.shouldExitWithError = true;
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
console.error(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process.on('exit', () => {
|
||||||
|
if (props.shouldPrintHelp) {
|
||||||
|
cli.printHelp();
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(exitCode);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return cli;
|
||||||
})();
|
})();
|
||||||
|
|||||||
@ -2,34 +2,34 @@
|
|||||||
|
|
||||||
// initialize the environment for Node.js
|
// initialize the environment for Node.js
|
||||||
(() => {
|
(() => {
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
let env;
|
let env;
|
||||||
let jsdocPath = __dirname;
|
let jsdocPath = __dirname;
|
||||||
|
|
||||||
// Create a custom require method that adds `lib/jsdoc` and `node_modules` to the module
|
// Create a custom require method that adds `lib/jsdoc` and `node_modules` to the module
|
||||||
// lookup path. This makes it possible to `require('jsdoc/foo')` from external templates and
|
// lookup path. This makes it possible to `require('jsdoc/foo')` from external templates and
|
||||||
// plugins, and within JSDoc itself. It also allows external templates and plugins to
|
// plugins, and within JSDoc itself. It also allows external templates and plugins to
|
||||||
// require JSDoc's module dependencies without installing them locally.
|
// require JSDoc's module dependencies without installing them locally.
|
||||||
/* eslint-disable no-global-assign, no-redeclare */
|
/* eslint-disable no-global-assign, no-redeclare */
|
||||||
require = require('requizzle')({
|
require = require('requizzle')({
|
||||||
requirePaths: {
|
requirePaths: {
|
||||||
before: [path.join(__dirname, 'lib')],
|
before: [path.join(__dirname, 'lib')],
|
||||||
after: [path.join(__dirname, 'node_modules')]
|
after: [path.join(__dirname, 'node_modules')],
|
||||||
},
|
},
|
||||||
infect: true
|
infect: true,
|
||||||
});
|
});
|
||||||
/* eslint-enable no-global-assign, no-redeclare */
|
/* eslint-enable no-global-assign, no-redeclare */
|
||||||
|
|
||||||
// resolve the path if it's a symlink
|
// resolve the path if it's a symlink
|
||||||
if ( fs.statSync(jsdocPath).isSymbolicLink() ) {
|
if (fs.statSync(jsdocPath).isSymbolicLink()) {
|
||||||
jsdocPath = path.resolve( path.dirname(jsdocPath), fs.readlinkSync(jsdocPath) );
|
jsdocPath = path.resolve(path.dirname(jsdocPath), fs.readlinkSync(jsdocPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
env = require('./lib/jsdoc/env');
|
env = require('./lib/jsdoc/env');
|
||||||
env.dirname = jsdocPath;
|
env.dirname = jsdocPath;
|
||||||
env.args = process.argv.slice(2);
|
env.args = process.argv.slice(2);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,12 +44,9 @@
|
|||||||
global.env = (() => require('./lib/jsdoc/env'))();
|
global.env = (() => require('./lib/jsdoc/env'))();
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const cli = require('./cli');
|
const cli = require('./cli');
|
||||||
|
|
||||||
cli.setVersionInfo()
|
cli.setVersionInfo().loadConfig().configureLogger().logStart();
|
||||||
.loadConfig()
|
|
||||||
.configureLogger()
|
|
||||||
.logStart();
|
|
||||||
|
|
||||||
await cli.runCommand();
|
await cli.runCommand();
|
||||||
})();
|
})();
|
||||||
|
|||||||
@ -6,116 +6,116 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const { fromParts, SCOPE, toParts } = require('@jsdoc/core').name;
|
const { fromParts, SCOPE, toParts } = require('@jsdoc/core').name;
|
||||||
const jsdoc = {
|
const jsdoc = {
|
||||||
doclet: require('jsdoc/doclet')
|
doclet: require('jsdoc/doclet'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasOwnProp = Object.prototype.hasOwnProperty;
|
const hasOwnProp = Object.prototype.hasOwnProperty;
|
||||||
|
|
||||||
function mapDependencies(index, propertyName) {
|
function mapDependencies(index, propertyName) {
|
||||||
const dependencies = {};
|
const dependencies = {};
|
||||||
let doc;
|
let doc;
|
||||||
let doclets;
|
let doclets;
|
||||||
const kinds = ['class', 'external', 'interface', 'mixin'];
|
const kinds = ['class', 'external', 'interface', 'mixin'];
|
||||||
let len = 0;
|
let len = 0;
|
||||||
|
|
||||||
Object.keys(index).forEach(indexName => {
|
Object.keys(index).forEach((indexName) => {
|
||||||
doclets = index[indexName];
|
doclets = index[indexName];
|
||||||
for (let i = 0, ii = doclets.length; i < ii; i++) {
|
for (let i = 0, ii = doclets.length; i < ii; i++) {
|
||||||
doc = doclets[i];
|
doc = doclets[i];
|
||||||
if (kinds.includes(doc.kind)) {
|
if (kinds.includes(doc.kind)) {
|
||||||
dependencies[indexName] = {};
|
dependencies[indexName] = {};
|
||||||
if (hasOwnProp.call(doc, propertyName)) {
|
if (hasOwnProp.call(doc, propertyName)) {
|
||||||
len = doc[propertyName].length;
|
len = doc[propertyName].length;
|
||||||
for (let j = 0; j < len; j++) {
|
for (let j = 0; j < len; j++) {
|
||||||
dependencies[indexName][doc[propertyName][j]] = true;
|
dependencies[indexName][doc[propertyName][j]] = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return dependencies;
|
return dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Sorter {
|
class Sorter {
|
||||||
constructor(dependencies) {
|
constructor(dependencies) {
|
||||||
this.dependencies = dependencies;
|
this.dependencies = dependencies;
|
||||||
this.visited = {};
|
this.visited = {};
|
||||||
this.sorted = [];
|
this.sorted = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
visit(key) {
|
visit(key) {
|
||||||
if (!(key in this.visited)) {
|
if (!(key in this.visited)) {
|
||||||
this.visited[key] = true;
|
this.visited[key] = true;
|
||||||
|
|
||||||
if (this.dependencies[key]) {
|
if (this.dependencies[key]) {
|
||||||
Object.keys(this.dependencies[key]).forEach(path => {
|
Object.keys(this.dependencies[key]).forEach((path) => {
|
||||||
this.visit(path);
|
this.visit(path);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sorted.push(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort() {
|
|
||||||
Object.keys(this.dependencies).forEach(key => {
|
|
||||||
this.visit(key);
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return this.sorted;
|
this.sorted.push(key);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort() {
|
||||||
|
Object.keys(this.dependencies).forEach((key) => {
|
||||||
|
this.visit(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.sorted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sort(dependencies) {
|
function sort(dependencies) {
|
||||||
const sorter = new Sorter(dependencies);
|
const sorter = new Sorter(dependencies);
|
||||||
|
|
||||||
return sorter.sort();
|
return sorter.sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMembers(longname, {index}, scopes) {
|
function getMembers(longname, { index }, scopes) {
|
||||||
const memberof = index.memberof[longname] || [];
|
const memberof = index.memberof[longname] || [];
|
||||||
const members = [];
|
const members = [];
|
||||||
|
|
||||||
memberof.forEach(candidate => {
|
memberof.forEach((candidate) => {
|
||||||
if (scopes.includes(candidate.scope)) {
|
if (scopes.includes(candidate.scope)) {
|
||||||
members.push(candidate);
|
members.push(candidate);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return members;
|
return members;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDocumentedLongname(longname, {index}) {
|
function getDocumentedLongname(longname, { index }) {
|
||||||
const doclets = index.documented[longname] || [];
|
const doclets = index.documented[longname] || [];
|
||||||
|
|
||||||
return doclets[doclets.length - 1];
|
return doclets[doclets.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
function addDocletProperty(doclets, propName, value) {
|
function addDocletProperty(doclets, propName, value) {
|
||||||
for (let i = 0, l = doclets.length; i < l; i++) {
|
for (let i = 0, l = doclets.length; i < l; i++) {
|
||||||
doclets[i][propName] = value;
|
doclets[i][propName] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function reparentDoclet({longname}, child) {
|
function reparentDoclet({ longname }, child) {
|
||||||
const parts = toParts(child.longname);
|
const parts = toParts(child.longname);
|
||||||
|
|
||||||
parts.memberof = longname;
|
parts.memberof = longname;
|
||||||
child.memberof = longname;
|
child.memberof = longname;
|
||||||
child.longname = fromParts(parts);
|
child.longname = fromParts(parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parentIsClass({kind}) {
|
function parentIsClass({ kind }) {
|
||||||
return kind === 'class';
|
return kind === 'class';
|
||||||
}
|
}
|
||||||
|
|
||||||
function staticToInstance(doclet) {
|
function staticToInstance(doclet) {
|
||||||
const parts = toParts(doclet.longname);
|
const parts = toParts(doclet.longname);
|
||||||
|
|
||||||
parts.scope = SCOPE.PUNC.INSTANCE;
|
parts.scope = SCOPE.PUNC.INSTANCE;
|
||||||
doclet.longname = fromParts(parts);
|
doclet.longname = fromParts(parts);
|
||||||
doclet.scope = SCOPE.NAMES.INSTANCE;
|
doclet.scope = SCOPE.NAMES.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,15 +137,14 @@ function staticToInstance(doclet) {
|
|||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
function updateAddedDoclets(doclet, additions, indexes) {
|
function updateAddedDoclets(doclet, additions, indexes) {
|
||||||
if (typeof indexes[doclet.longname] !== 'undefined') {
|
if (typeof indexes[doclet.longname] !== 'undefined') {
|
||||||
// replace the existing doclet
|
// replace the existing doclet
|
||||||
additions[indexes[doclet.longname]] = doclet;
|
additions[indexes[doclet.longname]] = doclet;
|
||||||
}
|
} else {
|
||||||
else {
|
// add the doclet to the array, and track its index
|
||||||
// add the doclet to the array, and track its index
|
additions.push(doclet);
|
||||||
additions.push(doclet);
|
indexes[doclet.longname] = additions.length - 1;
|
||||||
indexes[doclet.longname] = additions.length - 1;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -158,11 +157,11 @@ function updateAddedDoclets(doclet, additions, indexes) {
|
|||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
function updateDocumentedDoclets(doclet, documented) {
|
function updateDocumentedDoclets(doclet, documented) {
|
||||||
if ( !hasOwnProp.call(documented, doclet.longname) ) {
|
if (!hasOwnProp.call(documented, doclet.longname)) {
|
||||||
documented[doclet.longname] = [];
|
documented[doclet.longname] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
documented[doclet.longname].push(doclet);
|
documented[doclet.longname].push(doclet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -175,363 +174,360 @@ function updateDocumentedDoclets(doclet, documented) {
|
|||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
function updateMemberofDoclets(doclet, memberof) {
|
function updateMemberofDoclets(doclet, memberof) {
|
||||||
if (doclet.memberof) {
|
if (doclet.memberof) {
|
||||||
if ( !hasOwnProp.call(memberof, doclet.memberof) ) {
|
if (!hasOwnProp.call(memberof, doclet.memberof)) {
|
||||||
memberof[doclet.memberof] = [];
|
memberof[doclet.memberof] = [];
|
||||||
}
|
|
||||||
|
|
||||||
memberof[doclet.memberof].push(doclet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memberof[doclet.memberof].push(doclet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function explicitlyInherits(doclets) {
|
function explicitlyInherits(doclets) {
|
||||||
let doclet;
|
let doclet;
|
||||||
let inherits = false;
|
let inherits = false;
|
||||||
|
|
||||||
for (let i = 0, l = doclets.length; i < l; i++) {
|
for (let i = 0, l = doclets.length; i < l; i++) {
|
||||||
doclet = doclets[i];
|
doclet = doclets[i];
|
||||||
if (typeof doclet.inheritdoc !== 'undefined' || typeof doclet.override !== 'undefined') {
|
if (typeof doclet.inheritdoc !== 'undefined' || typeof doclet.override !== 'undefined') {
|
||||||
inherits = true;
|
inherits = true;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return inherits;
|
return inherits;
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeMemberof(longname, newMemberof) {
|
function changeMemberof(longname, newMemberof) {
|
||||||
const atoms = toParts(longname);
|
const atoms = toParts(longname);
|
||||||
|
|
||||||
atoms.memberof = newMemberof;
|
atoms.memberof = newMemberof;
|
||||||
|
|
||||||
return fromParts(atoms);
|
return fromParts(atoms);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: try to reduce overlap with similar methods
|
// TODO: try to reduce overlap with similar methods
|
||||||
function getInheritedAdditions(doclets, docs, {documented, memberof}) {
|
function getInheritedAdditions(doclets, docs, { documented, memberof }) {
|
||||||
let additionIndexes;
|
let additionIndexes;
|
||||||
const additions = [];
|
const additions = [];
|
||||||
let childDoclet;
|
let childDoclet;
|
||||||
let childLongname;
|
let childLongname;
|
||||||
let doc;
|
let doc;
|
||||||
let parentDoclet;
|
let parentDoclet;
|
||||||
let parentMembers;
|
let parentMembers;
|
||||||
let parents;
|
let parents;
|
||||||
let member;
|
let member;
|
||||||
let parts;
|
let parts;
|
||||||
|
|
||||||
// doclets will be undefined if the inherited symbol isn't documented
|
// doclets will be undefined if the inherited symbol isn't documented
|
||||||
doclets = doclets || [];
|
doclets = doclets || [];
|
||||||
|
|
||||||
for (let i = 0, ii = doclets.length; i < ii; i++) {
|
for (let i = 0, ii = doclets.length; i < ii; i++) {
|
||||||
doc = doclets[i];
|
doc = doclets[i];
|
||||||
parents = doc.augments;
|
parents = doc.augments;
|
||||||
|
|
||||||
if ( parents && (doc.kind === 'class' || doc.kind === 'interface') ) {
|
if (parents && (doc.kind === 'class' || doc.kind === 'interface')) {
|
||||||
// reset the lookup table of added doclet indexes by longname
|
// reset the lookup table of added doclet indexes by longname
|
||||||
additionIndexes = {};
|
additionIndexes = {};
|
||||||
|
|
||||||
for (let j = 0, jj = parents.length; j < jj; j++) {
|
for (let j = 0, jj = parents.length; j < jj; j++) {
|
||||||
parentMembers = getMembers(parents[j], docs, ['instance']);
|
parentMembers = getMembers(parents[j], docs, ['instance']);
|
||||||
|
|
||||||
for (let k = 0, kk = parentMembers.length; k < kk; k++) {
|
for (let k = 0, kk = parentMembers.length; k < kk; k++) {
|
||||||
parentDoclet = parentMembers[k];
|
parentDoclet = parentMembers[k];
|
||||||
|
|
||||||
// We only care about symbols that are documented.
|
// We only care about symbols that are documented.
|
||||||
if (parentDoclet.undocumented) {
|
if (parentDoclet.undocumented) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
childLongname = changeMemberof(parentDoclet.longname, doc.longname);
|
childLongname = changeMemberof(parentDoclet.longname, doc.longname);
|
||||||
childDoclet = getDocumentedLongname(childLongname, docs) || {};
|
childDoclet = getDocumentedLongname(childLongname, docs) || {};
|
||||||
|
|
||||||
// We don't want to fold in properties from the child doclet if it had an
|
// We don't want to fold in properties from the child doclet if it had an
|
||||||
// `@inheritdoc` tag.
|
// `@inheritdoc` tag.
|
||||||
if (hasOwnProp.call(childDoclet, 'inheritdoc')) {
|
if (hasOwnProp.call(childDoclet, 'inheritdoc')) {
|
||||||
childDoclet = {};
|
childDoclet = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
member = jsdoc.doclet.combine(childDoclet, parentDoclet);
|
member = jsdoc.doclet.combine(childDoclet, parentDoclet);
|
||||||
|
|
||||||
if (!member.inherited) {
|
if (!member.inherited) {
|
||||||
member.inherits = member.longname;
|
member.inherits = member.longname;
|
||||||
}
|
}
|
||||||
member.inherited = true;
|
member.inherited = true;
|
||||||
|
|
||||||
member.memberof = doc.longname;
|
member.memberof = doc.longname;
|
||||||
parts = toParts(member.longname);
|
parts = toParts(member.longname);
|
||||||
parts.memberof = doc.longname;
|
parts.memberof = doc.longname;
|
||||||
member.longname = fromParts(parts);
|
member.longname = fromParts(parts);
|
||||||
|
|
||||||
// Indicate what the descendant is overriding. (We only care about the closest
|
// Indicate what the descendant is overriding. (We only care about the closest
|
||||||
// ancestor. For classes A > B > C, if B#a overrides A#a, and C#a inherits B#a,
|
// ancestor. For classes A > B > C, if B#a overrides A#a, and C#a inherits B#a,
|
||||||
// we don't want the doclet for C#a to say that it overrides A#a.)
|
// we don't want the doclet for C#a to say that it overrides A#a.)
|
||||||
if ( hasOwnProp.call(docs.index.longname, member.longname) ) {
|
if (hasOwnProp.call(docs.index.longname, member.longname)) {
|
||||||
member.overrides = parentDoclet.longname;
|
member.overrides = parentDoclet.longname;
|
||||||
}
|
} else {
|
||||||
else {
|
delete member.overrides;
|
||||||
delete member.overrides;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Add the ancestor's docs unless the descendant overrides the ancestor AND
|
// Add the ancestor's docs unless the descendant overrides the ancestor AND
|
||||||
// documents the override.
|
// documents the override.
|
||||||
if ( !hasOwnProp.call(documented, member.longname) ) {
|
if (!hasOwnProp.call(documented, member.longname)) {
|
||||||
updateAddedDoclets(member, additions, additionIndexes);
|
updateAddedDoclets(member, additions, additionIndexes);
|
||||||
updateDocumentedDoclets(member, documented);
|
updateDocumentedDoclets(member, documented);
|
||||||
updateMemberofDoclets(member, memberof);
|
updateMemberofDoclets(member, memberof);
|
||||||
}
|
}
|
||||||
// If the descendant used an @inheritdoc or @override tag, add the ancestor's
|
// If the descendant used an @inheritdoc or @override tag, add the ancestor's
|
||||||
// docs, and ignore the existing doclets.
|
// docs, and ignore the existing doclets.
|
||||||
else if ( explicitlyInherits(documented[member.longname]) ) {
|
else if (explicitlyInherits(documented[member.longname])) {
|
||||||
// Ignore any existing doclets. (This is safe because we only get here if
|
// Ignore any existing doclets. (This is safe because we only get here if
|
||||||
// `member.longname` is an own property of `documented`.)
|
// `member.longname` is an own property of `documented`.)
|
||||||
addDocletProperty(documented[member.longname], 'ignore', true);
|
addDocletProperty(documented[member.longname], 'ignore', true);
|
||||||
|
|
||||||
updateAddedDoclets(member, additions, additionIndexes);
|
updateAddedDoclets(member, additions, additionIndexes);
|
||||||
updateDocumentedDoclets(member, documented);
|
updateDocumentedDoclets(member, documented);
|
||||||
updateMemberofDoclets(member, memberof);
|
updateMemberofDoclets(member, memberof);
|
||||||
|
|
||||||
// Remove property that's no longer accurate.
|
// Remove property that's no longer accurate.
|
||||||
if (member.virtual) {
|
if (member.virtual) {
|
||||||
delete member.virtual;
|
delete member.virtual;
|
||||||
}
|
|
||||||
// Remove properties that we no longer need.
|
|
||||||
if (member.inheritdoc) {
|
|
||||||
delete member.inheritdoc;
|
|
||||||
}
|
|
||||||
if (member.override) {
|
|
||||||
delete member.override;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If the descendant overrides the ancestor and documents the override,
|
|
||||||
// update the doclets to indicate what the descendant is overriding.
|
|
||||||
else {
|
|
||||||
addDocletProperty(documented[member.longname], 'overrides',
|
|
||||||
parentDoclet.longname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// Remove properties that we no longer need.
|
||||||
|
if (member.inheritdoc) {
|
||||||
|
delete member.inheritdoc;
|
||||||
|
}
|
||||||
|
if (member.override) {
|
||||||
|
delete member.override;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the descendant overrides the ancestor and documents the override,
|
||||||
|
// update the doclets to indicate what the descendant is overriding.
|
||||||
|
else {
|
||||||
|
addDocletProperty(documented[member.longname], 'overrides', parentDoclet.longname);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return additions;
|
return additions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateMixes(mixedDoclet, mixedLongname) {
|
function updateMixes(mixedDoclet, mixedLongname) {
|
||||||
let idx;
|
let idx;
|
||||||
let mixedName;
|
let mixedName;
|
||||||
let names;
|
let names;
|
||||||
|
|
||||||
// take the fast path if there's no array of mixed-in longnames
|
// take the fast path if there's no array of mixed-in longnames
|
||||||
if (!mixedDoclet.mixes) {
|
if (!mixedDoclet.mixes) {
|
||||||
mixedDoclet.mixes = [mixedLongname];
|
mixedDoclet.mixes = [mixedLongname];
|
||||||
|
} else {
|
||||||
|
// find the short name of the longname we're mixing in
|
||||||
|
mixedName = toParts(mixedLongname).name;
|
||||||
|
// find the short name of each previously mixed-in symbol
|
||||||
|
// TODO: why do we run a map if we always shorten the same value? this looks like a bug...
|
||||||
|
names = mixedDoclet.mixes.map(() => toParts(mixedDoclet.longname).name);
|
||||||
|
|
||||||
|
// if we're mixing `myMethod` into `MixinC` from `MixinB`, and `MixinB` had the method mixed
|
||||||
|
// in from `MixinA`, don't show `MixinA.myMethod` in the `mixes` list
|
||||||
|
idx = names.indexOf(mixedName);
|
||||||
|
if (idx !== -1) {
|
||||||
|
mixedDoclet.mixes.splice(idx, 1);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// find the short name of the longname we're mixing in
|
|
||||||
mixedName = toParts(mixedLongname).name;
|
|
||||||
// find the short name of each previously mixed-in symbol
|
|
||||||
// TODO: why do we run a map if we always shorten the same value? this looks like a bug...
|
|
||||||
names = mixedDoclet.mixes.map(() => toParts(mixedDoclet.longname).name);
|
|
||||||
|
|
||||||
// if we're mixing `myMethod` into `MixinC` from `MixinB`, and `MixinB` had the method mixed
|
mixedDoclet.mixes.push(mixedLongname);
|
||||||
// in from `MixinA`, don't show `MixinA.myMethod` in the `mixes` list
|
}
|
||||||
idx = names.indexOf(mixedName);
|
|
||||||
if (idx !== -1) {
|
|
||||||
mixedDoclet.mixes.splice(idx, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
mixedDoclet.mixes.push(mixedLongname);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: try to reduce overlap with similar methods
|
// TODO: try to reduce overlap with similar methods
|
||||||
function getMixedInAdditions(mixinDoclets, allDoclets, {documented, memberof}) {
|
function getMixedInAdditions(mixinDoclets, allDoclets, { documented, memberof }) {
|
||||||
let additionIndexes;
|
let additionIndexes;
|
||||||
const additions = [];
|
const additions = [];
|
||||||
const commentedDoclets = documented;
|
const commentedDoclets = documented;
|
||||||
let doclet;
|
let doclet;
|
||||||
let mixedDoclet;
|
let mixedDoclet;
|
||||||
let mixedDoclets;
|
let mixedDoclets;
|
||||||
let mixes;
|
let mixes;
|
||||||
|
|
||||||
// mixinDoclets will be undefined if the mixed-in symbol isn't documented
|
// mixinDoclets will be undefined if the mixed-in symbol isn't documented
|
||||||
mixinDoclets = mixinDoclets || [];
|
mixinDoclets = mixinDoclets || [];
|
||||||
|
|
||||||
for (let i = 0, ii = mixinDoclets.length; i < ii; i++) {
|
for (let i = 0, ii = mixinDoclets.length; i < ii; i++) {
|
||||||
doclet = mixinDoclets[i];
|
doclet = mixinDoclets[i];
|
||||||
mixes = doclet.mixes;
|
mixes = doclet.mixes;
|
||||||
|
|
||||||
if (mixes) {
|
if (mixes) {
|
||||||
// reset the lookup table of added doclet indexes by longname
|
// reset the lookup table of added doclet indexes by longname
|
||||||
additionIndexes = {};
|
additionIndexes = {};
|
||||||
|
|
||||||
for (let j = 0, jj = mixes.length; j < jj; j++) {
|
for (let j = 0, jj = mixes.length; j < jj; j++) {
|
||||||
mixedDoclets = getMembers(mixes[j], allDoclets, ['static']);
|
mixedDoclets = getMembers(mixes[j], allDoclets, ['static']);
|
||||||
|
|
||||||
for (let k = 0, kk = mixedDoclets.length; k < kk; k++) {
|
for (let k = 0, kk = mixedDoclets.length; k < kk; k++) {
|
||||||
// We only care about symbols that are documented.
|
// We only care about symbols that are documented.
|
||||||
if (mixedDoclets[k].undocumented) {
|
if (mixedDoclets[k].undocumented) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
mixedDoclet = _.cloneDeep(mixedDoclets[k]);
|
mixedDoclet = _.cloneDeep(mixedDoclets[k]);
|
||||||
|
|
||||||
updateMixes(mixedDoclet, mixedDoclet.longname);
|
updateMixes(mixedDoclet, mixedDoclet.longname);
|
||||||
mixedDoclet.mixed = true;
|
mixedDoclet.mixed = true;
|
||||||
|
|
||||||
reparentDoclet(doclet, mixedDoclet);
|
reparentDoclet(doclet, mixedDoclet);
|
||||||
|
|
||||||
// if we're mixing into a class, treat the mixed-in symbol as an instance member
|
// if we're mixing into a class, treat the mixed-in symbol as an instance member
|
||||||
if (parentIsClass(doclet)) {
|
if (parentIsClass(doclet)) {
|
||||||
staticToInstance(mixedDoclet);
|
staticToInstance(mixedDoclet);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAddedDoclets(mixedDoclet, additions, additionIndexes);
|
updateAddedDoclets(mixedDoclet, additions, additionIndexes);
|
||||||
updateDocumentedDoclets(mixedDoclet, commentedDoclets);
|
updateDocumentedDoclets(mixedDoclet, commentedDoclets);
|
||||||
updateMemberofDoclets(mixedDoclet, memberof);
|
updateMemberofDoclets(mixedDoclet, memberof);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return additions;
|
return additions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateImplements(implDoclets, implementedLongname) {
|
function updateImplements(implDoclets, implementedLongname) {
|
||||||
if ( !Array.isArray(implDoclets) ) {
|
if (!Array.isArray(implDoclets)) {
|
||||||
implDoclets = [implDoclets];
|
implDoclets = [implDoclets];
|
||||||
|
}
|
||||||
|
|
||||||
|
implDoclets.forEach((implDoclet) => {
|
||||||
|
if (!hasOwnProp.call(implDoclet, 'implements')) {
|
||||||
|
implDoclet.implements = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
implDoclets.forEach(implDoclet => {
|
if (!implDoclet.implements.includes(implementedLongname)) {
|
||||||
if ( !hasOwnProp.call(implDoclet, 'implements') ) {
|
implDoclet.implements.push(implementedLongname);
|
||||||
implDoclet.implements = [];
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
if (!implDoclet.implements.includes(implementedLongname)) {
|
|
||||||
implDoclet.implements.push(implementedLongname);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: try to reduce overlap with similar methods
|
// TODO: try to reduce overlap with similar methods
|
||||||
function getImplementedAdditions(implDoclets, allDoclets, {documented, memberof}) {
|
function getImplementedAdditions(implDoclets, allDoclets, { documented, memberof }) {
|
||||||
let additionIndexes;
|
let additionIndexes;
|
||||||
const additions = [];
|
const additions = [];
|
||||||
let childDoclet;
|
let childDoclet;
|
||||||
let childLongname;
|
let childLongname;
|
||||||
const commentedDoclets = documented;
|
const commentedDoclets = documented;
|
||||||
let doclet;
|
let doclet;
|
||||||
let implementations;
|
let implementations;
|
||||||
let implExists;
|
let implExists;
|
||||||
let implementationDoclet;
|
let implementationDoclet;
|
||||||
let interfaceDoclets;
|
let interfaceDoclets;
|
||||||
let parentDoclet;
|
let parentDoclet;
|
||||||
|
|
||||||
// interfaceDoclets will be undefined if the implemented symbol isn't documented
|
// interfaceDoclets will be undefined if the implemented symbol isn't documented
|
||||||
implDoclets = implDoclets || [];
|
implDoclets = implDoclets || [];
|
||||||
|
|
||||||
for (let i = 0, ii = implDoclets.length; i < ii; i++) {
|
for (let i = 0, ii = implDoclets.length; i < ii; i++) {
|
||||||
doclet = implDoclets[i];
|
doclet = implDoclets[i];
|
||||||
implementations = doclet.implements;
|
implementations = doclet.implements;
|
||||||
|
|
||||||
if (implementations) {
|
if (implementations) {
|
||||||
// reset the lookup table of added doclet indexes by longname
|
// reset the lookup table of added doclet indexes by longname
|
||||||
additionIndexes = {};
|
additionIndexes = {};
|
||||||
|
|
||||||
for (let j = 0, jj = implementations.length; j < jj; j++) {
|
for (let j = 0, jj = implementations.length; j < jj; j++) {
|
||||||
interfaceDoclets = getMembers(implementations[j], allDoclets, ['instance']);
|
interfaceDoclets = getMembers(implementations[j], allDoclets, ['instance']);
|
||||||
|
|
||||||
for (let k = 0, kk = interfaceDoclets.length; k < kk; k++) {
|
for (let k = 0, kk = interfaceDoclets.length; k < kk; k++) {
|
||||||
parentDoclet = interfaceDoclets[k];
|
parentDoclet = interfaceDoclets[k];
|
||||||
|
|
||||||
// We only care about symbols that are documented.
|
// We only care about symbols that are documented.
|
||||||
if (parentDoclet.undocumented) {
|
if (parentDoclet.undocumented) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
childLongname = changeMemberof(parentDoclet.longname, doclet.longname);
|
childLongname = changeMemberof(parentDoclet.longname, doclet.longname);
|
||||||
childDoclet = getDocumentedLongname(childLongname, allDoclets) || {};
|
childDoclet = getDocumentedLongname(childLongname, allDoclets) || {};
|
||||||
|
|
||||||
// We don't want to fold in properties from the child doclet if it had an
|
// We don't want to fold in properties from the child doclet if it had an
|
||||||
// `@inheritdoc` tag.
|
// `@inheritdoc` tag.
|
||||||
if (hasOwnProp.call(childDoclet, 'inheritdoc')) {
|
if (hasOwnProp.call(childDoclet, 'inheritdoc')) {
|
||||||
childDoclet = {};
|
childDoclet = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
implementationDoclet = jsdoc.doclet.combine(childDoclet, parentDoclet);
|
implementationDoclet = jsdoc.doclet.combine(childDoclet, parentDoclet);
|
||||||
|
|
||||||
reparentDoclet(doclet, implementationDoclet);
|
reparentDoclet(doclet, implementationDoclet);
|
||||||
updateImplements(implementationDoclet, parentDoclet.longname);
|
updateImplements(implementationDoclet, parentDoclet.longname);
|
||||||
|
|
||||||
// If there's no implementation, move along.
|
// If there's no implementation, move along.
|
||||||
implExists = hasOwnProp.call(allDoclets.index.longname,
|
implExists = hasOwnProp.call(allDoclets.index.longname, implementationDoclet.longname);
|
||||||
implementationDoclet.longname);
|
if (!implExists) {
|
||||||
if (!implExists) {
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Add the interface's docs unless the implementation is already documented.
|
// Add the interface's docs unless the implementation is already documented.
|
||||||
if ( !hasOwnProp.call(commentedDoclets, implementationDoclet.longname) ) {
|
if (!hasOwnProp.call(commentedDoclets, implementationDoclet.longname)) {
|
||||||
updateAddedDoclets(implementationDoclet, additions, additionIndexes);
|
updateAddedDoclets(implementationDoclet, additions, additionIndexes);
|
||||||
updateDocumentedDoclets(implementationDoclet, commentedDoclets);
|
updateDocumentedDoclets(implementationDoclet, commentedDoclets);
|
||||||
updateMemberofDoclets(implementationDoclet, memberof);
|
updateMemberofDoclets(implementationDoclet, memberof);
|
||||||
}
|
}
|
||||||
// If the implementation used an @inheritdoc or @override tag, add the
|
// If the implementation used an @inheritdoc or @override tag, add the
|
||||||
// interface's docs, and ignore the existing doclets.
|
// interface's docs, and ignore the existing doclets.
|
||||||
else if ( explicitlyInherits(commentedDoclets[implementationDoclet.longname]) ) {
|
else if (explicitlyInherits(commentedDoclets[implementationDoclet.longname])) {
|
||||||
// Ignore any existing doclets. (This is safe because we only get here if
|
// Ignore any existing doclets. (This is safe because we only get here if
|
||||||
// `implementationDoclet.longname` is an own property of
|
// `implementationDoclet.longname` is an own property of
|
||||||
// `commentedDoclets`.)
|
// `commentedDoclets`.)
|
||||||
addDocletProperty(commentedDoclets[implementationDoclet.longname], 'ignore',
|
addDocletProperty(commentedDoclets[implementationDoclet.longname], 'ignore', true);
|
||||||
true);
|
|
||||||
|
|
||||||
updateAddedDoclets(implementationDoclet, additions, additionIndexes);
|
updateAddedDoclets(implementationDoclet, additions, additionIndexes);
|
||||||
updateDocumentedDoclets(implementationDoclet, commentedDoclets);
|
updateDocumentedDoclets(implementationDoclet, commentedDoclets);
|
||||||
updateMemberofDoclets(implementationDoclet, memberof);
|
updateMemberofDoclets(implementationDoclet, memberof);
|
||||||
|
|
||||||
// Remove property that's no longer accurate.
|
// Remove property that's no longer accurate.
|
||||||
if (implementationDoclet.virtual) {
|
if (implementationDoclet.virtual) {
|
||||||
delete implementationDoclet.virtual;
|
delete implementationDoclet.virtual;
|
||||||
}
|
|
||||||
// Remove properties that we no longer need.
|
|
||||||
if (implementationDoclet.inheritdoc) {
|
|
||||||
delete implementationDoclet.inheritdoc;
|
|
||||||
}
|
|
||||||
if (implementationDoclet.override) {
|
|
||||||
delete implementationDoclet.override;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If there's an implementation, and it's documented, update the doclets to
|
|
||||||
// indicate what the implementation is implementing.
|
|
||||||
else {
|
|
||||||
updateImplements(commentedDoclets[implementationDoclet.longname],
|
|
||||||
parentDoclet.longname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// Remove properties that we no longer need.
|
||||||
|
if (implementationDoclet.inheritdoc) {
|
||||||
|
delete implementationDoclet.inheritdoc;
|
||||||
|
}
|
||||||
|
if (implementationDoclet.override) {
|
||||||
|
delete implementationDoclet.override;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If there's an implementation, and it's documented, update the doclets to
|
||||||
|
// indicate what the implementation is implementing.
|
||||||
|
else {
|
||||||
|
updateImplements(
|
||||||
|
commentedDoclets[implementationDoclet.longname],
|
||||||
|
parentDoclet.longname
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return additions;
|
return additions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function augment(doclets, propertyName, docletFinder) {
|
function augment(doclets, propertyName, docletFinder) {
|
||||||
const index = doclets.index.longname;
|
const index = doclets.index.longname;
|
||||||
const dependencies = sort( mapDependencies(index, propertyName) );
|
const dependencies = sort(mapDependencies(index, propertyName));
|
||||||
|
|
||||||
dependencies.forEach(depName => {
|
dependencies.forEach((depName) => {
|
||||||
const additions = docletFinder(index[depName], doclets, doclets.index);
|
const additions = docletFinder(index[depName], doclets, doclets.index);
|
||||||
|
|
||||||
additions.forEach(addition => {
|
additions.forEach((addition) => {
|
||||||
const longname = addition.longname;
|
const longname = addition.longname;
|
||||||
|
|
||||||
if ( !hasOwnProp.call(index, longname) ) {
|
if (!hasOwnProp.call(index, longname)) {
|
||||||
index[longname] = [];
|
index[longname] = [];
|
||||||
}
|
}
|
||||||
index[longname].push(addition);
|
index[longname].push(addition);
|
||||||
doclets.push(addition);
|
doclets.push(addition);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -544,8 +540,8 @@ function augment(doclets, propertyName, docletFinder) {
|
|||||||
* @param {!Object} doclets.index - The doclet index.
|
* @param {!Object} doclets.index - The doclet index.
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
exports.addInherited = doclets => {
|
exports.addInherited = (doclets) => {
|
||||||
augment(doclets, 'augments', getInheritedAdditions);
|
augment(doclets, 'augments', getInheritedAdditions);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -563,8 +559,8 @@ exports.addInherited = doclets => {
|
|||||||
* @param {!Object} doclets.index - The doclet index.
|
* @param {!Object} doclets.index - The doclet index.
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
exports.addMixedIn = doclets => {
|
exports.addMixedIn = (doclets) => {
|
||||||
augment(doclets, 'mixes', getMixedInAdditions);
|
augment(doclets, 'mixes', getMixedInAdditions);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -584,8 +580,8 @@ exports.addMixedIn = doclets => {
|
|||||||
* @param {!Object} doclets.index - The doclet index.
|
* @param {!Object} doclets.index - The doclet index.
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
exports.addImplemented = doclets => {
|
exports.addImplemented = (doclets) => {
|
||||||
augment(doclets, 'implements', getImplementedAdditions);
|
augment(doclets, 'implements', getImplementedAdditions);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -599,10 +595,10 @@ exports.addImplemented = doclets => {
|
|||||||
*
|
*
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
exports.augmentAll = doclets => {
|
exports.augmentAll = (doclets) => {
|
||||||
exports.addMixedIn(doclets);
|
exports.addMixedIn(doclets);
|
||||||
exports.addImplemented(doclets);
|
exports.addImplemented(doclets);
|
||||||
exports.addInherited(doclets);
|
exports.addInherited(doclets);
|
||||||
// look for implemented doclets again, in case we inherited an interface
|
// look for implemented doclets again, in case we inherited an interface
|
||||||
exports.addImplemented(doclets);
|
exports.addImplemented(doclets);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,35 +5,34 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const { SCOPE } = require('@jsdoc/core').name;
|
const { SCOPE } = require('@jsdoc/core').name;
|
||||||
|
|
||||||
function cloneBorrowedDoclets({borrowed, longname}, doclets) {
|
function cloneBorrowedDoclets({ borrowed, longname }, doclets) {
|
||||||
borrowed.forEach(({from, as}) => {
|
borrowed.forEach(({ from, as }) => {
|
||||||
const borrowedDoclets = doclets.index.longname[from];
|
const borrowedDoclets = doclets.index.longname[from];
|
||||||
let borrowedAs = as || from;
|
let borrowedAs = as || from;
|
||||||
let parts;
|
let parts;
|
||||||
let scopePunc;
|
let scopePunc;
|
||||||
|
|
||||||
if (borrowedDoclets) {
|
if (borrowedDoclets) {
|
||||||
borrowedAs = borrowedAs.replace(/^prototype\./, SCOPE.PUNC.INSTANCE);
|
borrowedAs = borrowedAs.replace(/^prototype\./, SCOPE.PUNC.INSTANCE);
|
||||||
_.cloneDeep(borrowedDoclets).forEach(clone => {
|
_.cloneDeep(borrowedDoclets).forEach((clone) => {
|
||||||
// TODO: this will fail on longnames like '"Foo#bar".baz'
|
// TODO: this will fail on longnames like '"Foo#bar".baz'
|
||||||
parts = borrowedAs.split(SCOPE.PUNC.INSTANCE);
|
parts = borrowedAs.split(SCOPE.PUNC.INSTANCE);
|
||||||
|
|
||||||
if (parts.length === 2) {
|
if (parts.length === 2) {
|
||||||
clone.scope = SCOPE.NAMES.INSTANCE;
|
clone.scope = SCOPE.NAMES.INSTANCE;
|
||||||
scopePunc = SCOPE.PUNC.INSTANCE;
|
scopePunc = SCOPE.PUNC.INSTANCE;
|
||||||
}
|
} else {
|
||||||
else {
|
clone.scope = SCOPE.NAMES.STATIC;
|
||||||
clone.scope = SCOPE.NAMES.STATIC;
|
scopePunc = SCOPE.PUNC.STATIC;
|
||||||
scopePunc = SCOPE.PUNC.STATIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
clone.name = parts.pop();
|
|
||||||
clone.memberof = longname;
|
|
||||||
clone.longname = clone.memberof + scopePunc + clone.name;
|
|
||||||
doclets.push(clone);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
clone.name = parts.pop();
|
||||||
|
clone.memberof = longname;
|
||||||
|
clone.longname = clone.memberof + scopePunc + clone.name;
|
||||||
|
doclets.push(clone);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,11 +41,11 @@ function cloneBorrowedDoclets({borrowed, longname}, doclets) {
|
|||||||
moving docs from the "borrowed" array and into the general docs, then
|
moving docs from the "borrowed" array and into the general docs, then
|
||||||
deleting the "borrowed" array.
|
deleting the "borrowed" array.
|
||||||
*/
|
*/
|
||||||
exports.resolveBorrows = doclets => {
|
exports.resolveBorrows = (doclets) => {
|
||||||
for (let doclet of doclets.index.borrowed) {
|
for (let doclet of doclets.index.borrowed) {
|
||||||
cloneBorrowedDoclets(doclet, doclets);
|
cloneBorrowedDoclets(doclet, doclets);
|
||||||
delete doclet.borrowed;
|
delete doclet.borrowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
doclets.index.borrowed = [];
|
doclets.index.borrowed = [];
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -5,73 +5,73 @@
|
|||||||
* @module jsdoc/env
|
* @module jsdoc/env
|
||||||
*/
|
*/
|
||||||
module.exports = {
|
module.exports = {
|
||||||
/**
|
/**
|
||||||
* The times at which JSDoc started and finished.
|
* The times at which JSDoc started and finished.
|
||||||
*
|
*
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
* @property {Date} start - The time at which JSDoc started running.
|
* @property {Date} start - The time at which JSDoc started running.
|
||||||
* @property {Date} finish - The time at which JSDoc finished running.
|
* @property {Date} finish - The time at which JSDoc finished running.
|
||||||
*/
|
*/
|
||||||
run: {
|
run: {
|
||||||
start: new Date(),
|
start: new Date(),
|
||||||
finish: null
|
finish: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The command-line arguments passed to JSDoc.
|
* The command-line arguments passed to JSDoc.
|
||||||
*
|
*
|
||||||
* @type {Array<*>}
|
* @type {Array<*>}
|
||||||
*/
|
*/
|
||||||
args: [],
|
args: [],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data parsed from JSDoc's configuration file.
|
* The data parsed from JSDoc's configuration file.
|
||||||
*
|
*
|
||||||
* @type Object<string, *>
|
* @type Object<string, *>
|
||||||
*/
|
*/
|
||||||
conf: {},
|
conf: {},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The absolute path to the base directory in which JSDoc is located. Set at startup.
|
* The absolute path to the base directory in which JSDoc is located. Set at startup.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
dirname: null,
|
dirname: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The user's working directory at the time when JSDoc started running.
|
* The user's working directory at the time when JSDoc started running.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
pwd: null,
|
pwd: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The command-line arguments, parsed into a key/value hash.
|
* The command-line arguments, parsed into a key/value hash.
|
||||||
*
|
*
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
* @example if (global.env.opts.help) { console.log('Helpful message.'); }
|
* @example if (global.env.opts.help) { console.log('Helpful message.'); }
|
||||||
*/
|
*/
|
||||||
opts: {},
|
opts: {},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The source files that JSDoc will parse.
|
* The source files that JSDoc will parse.
|
||||||
*
|
*
|
||||||
* @type {Array<string>}
|
* @type {Array<string>}
|
||||||
* @memberof env
|
* @memberof env
|
||||||
*/
|
*/
|
||||||
sourceFiles: [],
|
sourceFiles: [],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The JSDoc version number and revision date.
|
* The JSDoc version number and revision date.
|
||||||
*
|
*
|
||||||
* @type {Object<string, string>}
|
* @type {Object<string, string>}
|
||||||
* @property {string} number - The JSDoc version number.
|
* @property {string} number - The JSDoc version number.
|
||||||
* @property {string} revision - The JSDoc revision number, expressed as a UTC date string.
|
* @property {string} revision - The JSDoc revision number, expressed as a UTC date string.
|
||||||
*/
|
*/
|
||||||
version: {
|
version: {
|
||||||
number: null,
|
number: null,
|
||||||
revision: null
|
revision: null,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,13 +10,13 @@ const stripBom = require('strip-bom');
|
|||||||
|
|
||||||
// Collect all of the license information from a `package.json` file.
|
// Collect all of the license information from a `package.json` file.
|
||||||
function getLicenses(packageInfo) {
|
function getLicenses(packageInfo) {
|
||||||
const licenses = packageInfo.licenses ? packageInfo.licenses.slice(0) : [];
|
const licenses = packageInfo.licenses ? packageInfo.licenses.slice(0) : [];
|
||||||
|
|
||||||
if (packageInfo.license) {
|
if (packageInfo.license) {
|
||||||
licenses.push({ type: packageInfo.license });
|
licenses.push({ type: packageInfo.license });
|
||||||
}
|
}
|
||||||
|
|
||||||
return licenses;
|
return licenses;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,195 +63,194 @@ function getLicenses(packageInfo) {
|
|||||||
* object may not use the format documented here.
|
* object may not use the format documented here.
|
||||||
*/
|
*/
|
||||||
class Package {
|
class Package {
|
||||||
|
/**
|
||||||
|
* @param {string} json - The contents of the `package.json` file.
|
||||||
|
*/
|
||||||
|
constructor(json) {
|
||||||
|
let packageInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} json - The contents of the `package.json` file.
|
* The string identifier that is shared by all `Package` objects.
|
||||||
|
*
|
||||||
|
* @readonly
|
||||||
|
* @default
|
||||||
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
constructor(json) {
|
this.kind = 'package';
|
||||||
let packageInfo;
|
|
||||||
|
|
||||||
/**
|
try {
|
||||||
* The string identifier that is shared by all `Package` objects.
|
packageInfo = JSON.parse(json ? stripBom(json) : '{}');
|
||||||
*
|
} catch (e) {
|
||||||
* @readonly
|
log.error(`Unable to parse the package file: ${e.message}`);
|
||||||
* @default
|
packageInfo = {};
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
this.kind = 'package';
|
|
||||||
|
|
||||||
try {
|
|
||||||
packageInfo = JSON.parse(json ? stripBom(json) : '{}');
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
log.error(`Unable to parse the package file: ${e.message}`);
|
|
||||||
packageInfo = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageInfo.name) {
|
|
||||||
/**
|
|
||||||
* The package name.
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
this.name = packageInfo.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The unique longname for this `Package` object.
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
this.longname = `${this.kind}:${this.name}`;
|
|
||||||
|
|
||||||
if (packageInfo.author) {
|
|
||||||
/**
|
|
||||||
* The author of this package. Contains either a
|
|
||||||
* {@link module:jsdoc/package.Package~PersonInfo PersonInfo} object or a string with
|
|
||||||
* information about the author.
|
|
||||||
*
|
|
||||||
* @type {(module:jsdoc/package.Package~PersonInfo|string)}
|
|
||||||
* @since 3.3.0
|
|
||||||
*/
|
|
||||||
this.author = packageInfo.author;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageInfo.bugs) {
|
|
||||||
/**
|
|
||||||
* Information about where to report bugs in the project. May contain a URL, a string, or an
|
|
||||||
* object with more detailed information.
|
|
||||||
*
|
|
||||||
* @type {(string|module:jsdoc/package.Package~BugInfo)}
|
|
||||||
* @since 3.3.0
|
|
||||||
*/
|
|
||||||
this.bugs = packageInfo.bugs;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageInfo.contributors) {
|
|
||||||
/**
|
|
||||||
* The contributors to this package.
|
|
||||||
*
|
|
||||||
* @type {Array.<(module:jsdoc/package.Package~PersonInfo|string)>}
|
|
||||||
* @since 3.3.0
|
|
||||||
*/
|
|
||||||
this.contributors = packageInfo.contributors;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageInfo.dependencies) {
|
|
||||||
/**
|
|
||||||
* The dependencies for this package.
|
|
||||||
*
|
|
||||||
* @type {Object}
|
|
||||||
* @since 3.3.0
|
|
||||||
*/
|
|
||||||
this.dependencies = packageInfo.dependencies;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageInfo.description) {
|
|
||||||
/**
|
|
||||||
* A brief description of the package.
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
this.description = packageInfo.description;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageInfo.devDependencies) {
|
|
||||||
/**
|
|
||||||
* The development dependencies for this package.
|
|
||||||
*
|
|
||||||
* @type {Object}
|
|
||||||
* @since 3.3.0
|
|
||||||
*/
|
|
||||||
this.devDependencies = packageInfo.devDependencies;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageInfo.engines) {
|
|
||||||
/**
|
|
||||||
* The JavaScript engines that this package supports. Each key is a string that identifies
|
|
||||||
* the engine (for example, `node`). Each value is a
|
|
||||||
* [semver](https://www.npmjs.org/doc/misc/semver.html)-compliant version number for the
|
|
||||||
* engine.
|
|
||||||
*
|
|
||||||
* @type {Object}
|
|
||||||
* @since 3.3.0
|
|
||||||
*/
|
|
||||||
this.engines = packageInfo.engines;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The source files associated with the package.
|
|
||||||
*
|
|
||||||
* New `Package` objects always contain an empty array, regardless of whether the `package.json`
|
|
||||||
* file includes a `files` property.
|
|
||||||
*
|
|
||||||
* After JSDoc parses your input files, it sets this property to a list of paths to your input
|
|
||||||
* files.
|
|
||||||
*
|
|
||||||
* @type {Array.<string>}
|
|
||||||
*/
|
|
||||||
this.files = [];
|
|
||||||
|
|
||||||
if (packageInfo.homepage) {
|
|
||||||
/**
|
|
||||||
* The URL for the package's homepage.
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @since 3.3.0
|
|
||||||
*/
|
|
||||||
this.homepage = packageInfo.homepage;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageInfo.keywords) {
|
|
||||||
/**
|
|
||||||
* Keywords to help users find the package.
|
|
||||||
*
|
|
||||||
* @type {Array.<string>}
|
|
||||||
* @since 3.3.0
|
|
||||||
*/
|
|
||||||
this.keywords = packageInfo.keywords;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageInfo.license || packageInfo.licenses) {
|
|
||||||
/**
|
|
||||||
* The licenses used by this package. Combines information from the `package.json` file's
|
|
||||||
* `license` property and the deprecated `licenses` property.
|
|
||||||
*
|
|
||||||
* @type {Array.<module:jsdoc/package.Package~LicenseInfo>}
|
|
||||||
*/
|
|
||||||
this.licenses = getLicenses(packageInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageInfo.main) {
|
|
||||||
/**
|
|
||||||
* The module ID that provides the primary entry point to the package. For example, if your
|
|
||||||
* package is a CommonJS module, and the value of this property is `foo`, users should be
|
|
||||||
* able to load your module with `require('foo')`.
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @since 3.3.0
|
|
||||||
*/
|
|
||||||
this.main = packageInfo.main;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageInfo.repository) {
|
|
||||||
/**
|
|
||||||
* The version-control repository for the package.
|
|
||||||
*
|
|
||||||
* @type {module:jsdoc/package.Package~RepositoryInfo}
|
|
||||||
* @since 3.3.0
|
|
||||||
*/
|
|
||||||
this.repository = packageInfo.repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageInfo.version) {
|
|
||||||
/**
|
|
||||||
* The [semver](https://www.npmjs.org/doc/misc/semver.html)-compliant version number of the
|
|
||||||
* package.
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @since 3.2.0
|
|
||||||
*/
|
|
||||||
this.version = packageInfo.version;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (packageInfo.name) {
|
||||||
|
/**
|
||||||
|
* The package name.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.name = packageInfo.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unique longname for this `Package` object.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.longname = `${this.kind}:${this.name}`;
|
||||||
|
|
||||||
|
if (packageInfo.author) {
|
||||||
|
/**
|
||||||
|
* The author of this package. Contains either a
|
||||||
|
* {@link module:jsdoc/package.Package~PersonInfo PersonInfo} object or a string with
|
||||||
|
* information about the author.
|
||||||
|
*
|
||||||
|
* @type {(module:jsdoc/package.Package~PersonInfo|string)}
|
||||||
|
* @since 3.3.0
|
||||||
|
*/
|
||||||
|
this.author = packageInfo.author;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageInfo.bugs) {
|
||||||
|
/**
|
||||||
|
* Information about where to report bugs in the project. May contain a URL, a string, or an
|
||||||
|
* object with more detailed information.
|
||||||
|
*
|
||||||
|
* @type {(string|module:jsdoc/package.Package~BugInfo)}
|
||||||
|
* @since 3.3.0
|
||||||
|
*/
|
||||||
|
this.bugs = packageInfo.bugs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageInfo.contributors) {
|
||||||
|
/**
|
||||||
|
* The contributors to this package.
|
||||||
|
*
|
||||||
|
* @type {Array.<(module:jsdoc/package.Package~PersonInfo|string)>}
|
||||||
|
* @since 3.3.0
|
||||||
|
*/
|
||||||
|
this.contributors = packageInfo.contributors;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageInfo.dependencies) {
|
||||||
|
/**
|
||||||
|
* The dependencies for this package.
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
* @since 3.3.0
|
||||||
|
*/
|
||||||
|
this.dependencies = packageInfo.dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageInfo.description) {
|
||||||
|
/**
|
||||||
|
* A brief description of the package.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.description = packageInfo.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageInfo.devDependencies) {
|
||||||
|
/**
|
||||||
|
* The development dependencies for this package.
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
* @since 3.3.0
|
||||||
|
*/
|
||||||
|
this.devDependencies = packageInfo.devDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageInfo.engines) {
|
||||||
|
/**
|
||||||
|
* The JavaScript engines that this package supports. Each key is a string that identifies
|
||||||
|
* the engine (for example, `node`). Each value is a
|
||||||
|
* [semver](https://www.npmjs.org/doc/misc/semver.html)-compliant version number for the
|
||||||
|
* engine.
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
* @since 3.3.0
|
||||||
|
*/
|
||||||
|
this.engines = packageInfo.engines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The source files associated with the package.
|
||||||
|
*
|
||||||
|
* New `Package` objects always contain an empty array, regardless of whether the `package.json`
|
||||||
|
* file includes a `files` property.
|
||||||
|
*
|
||||||
|
* After JSDoc parses your input files, it sets this property to a list of paths to your input
|
||||||
|
* files.
|
||||||
|
*
|
||||||
|
* @type {Array.<string>}
|
||||||
|
*/
|
||||||
|
this.files = [];
|
||||||
|
|
||||||
|
if (packageInfo.homepage) {
|
||||||
|
/**
|
||||||
|
* The URL for the package's homepage.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @since 3.3.0
|
||||||
|
*/
|
||||||
|
this.homepage = packageInfo.homepage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageInfo.keywords) {
|
||||||
|
/**
|
||||||
|
* Keywords to help users find the package.
|
||||||
|
*
|
||||||
|
* @type {Array.<string>}
|
||||||
|
* @since 3.3.0
|
||||||
|
*/
|
||||||
|
this.keywords = packageInfo.keywords;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageInfo.license || packageInfo.licenses) {
|
||||||
|
/**
|
||||||
|
* The licenses used by this package. Combines information from the `package.json` file's
|
||||||
|
* `license` property and the deprecated `licenses` property.
|
||||||
|
*
|
||||||
|
* @type {Array.<module:jsdoc/package.Package~LicenseInfo>}
|
||||||
|
*/
|
||||||
|
this.licenses = getLicenses(packageInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageInfo.main) {
|
||||||
|
/**
|
||||||
|
* The module ID that provides the primary entry point to the package. For example, if your
|
||||||
|
* package is a CommonJS module, and the value of this property is `foo`, users should be
|
||||||
|
* able to load your module with `require('foo')`.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @since 3.3.0
|
||||||
|
*/
|
||||||
|
this.main = packageInfo.main;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageInfo.repository) {
|
||||||
|
/**
|
||||||
|
* The version-control repository for the package.
|
||||||
|
*
|
||||||
|
* @type {module:jsdoc/package.Package~RepositoryInfo}
|
||||||
|
* @since 3.3.0
|
||||||
|
*/
|
||||||
|
this.repository = packageInfo.repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageInfo.version) {
|
||||||
|
/**
|
||||||
|
* The [semver](https://www.npmjs.org/doc/misc/semver.html)-compliant version number of the
|
||||||
|
* package.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
this.version = packageInfo.version;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exports.Package = Package;
|
exports.Package = Package;
|
||||||
|
|||||||
@ -5,31 +5,31 @@
|
|||||||
const dictionary = require('jsdoc/tag/dictionary');
|
const dictionary = require('jsdoc/tag/dictionary');
|
||||||
|
|
||||||
function addHandlers(handlers, parser) {
|
function addHandlers(handlers, parser) {
|
||||||
Object.keys(handlers).forEach(eventName => {
|
Object.keys(handlers).forEach((eventName) => {
|
||||||
parser.on(eventName, handlers[eventName]);
|
parser.on(eventName, handlers[eventName]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.installPlugins = (plugins, parser) => {
|
exports.installPlugins = (plugins, parser) => {
|
||||||
let plugin;
|
let plugin;
|
||||||
|
|
||||||
for (let pluginModule of plugins) {
|
for (let pluginModule of plugins) {
|
||||||
plugin = require(pluginModule);
|
plugin = require(pluginModule);
|
||||||
|
|
||||||
// allow user-defined plugins to...
|
// allow user-defined plugins to...
|
||||||
// ...register event handlers
|
// ...register event handlers
|
||||||
if (plugin.handlers) {
|
if (plugin.handlers) {
|
||||||
addHandlers(plugin.handlers, parser);
|
addHandlers(plugin.handlers, parser);
|
||||||
}
|
|
||||||
|
|
||||||
// ...define tags
|
|
||||||
if (plugin.defineTags) {
|
|
||||||
plugin.defineTags(dictionary);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...add an ESTree node visitor
|
|
||||||
if (plugin.astNodeVisitor) {
|
|
||||||
parser.addAstNodeVisitor(plugin.astNodeVisitor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ...define tags
|
||||||
|
if (plugin.defineTags) {
|
||||||
|
plugin.defineTags(dictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...add an ESTree node visitor
|
||||||
|
if (plugin.astNodeVisitor) {
|
||||||
|
parser.addAstNodeVisitor(plugin.astNodeVisitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -4,60 +4,59 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
function makeRegExp(config) {
|
function makeRegExp(config) {
|
||||||
let regExp = null;
|
let regExp = null;
|
||||||
|
|
||||||
if (config) {
|
if (config) {
|
||||||
regExp = (typeof config === 'string') ? new RegExp(config) : config;
|
regExp = typeof config === 'string' ? new RegExp(config) : config;
|
||||||
}
|
}
|
||||||
|
|
||||||
return regExp;
|
return regExp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @alias module:jsdoc/src/filter.Filter
|
* @alias module:jsdoc/src/filter.Filter
|
||||||
*/
|
*/
|
||||||
class Filter {
|
class Filter {
|
||||||
/**
|
/**
|
||||||
* @param {Object} opts
|
* @param {Object} opts
|
||||||
* @param {string[]} opts.exclude - Specific files to exclude.
|
* @param {string[]} opts.exclude - Specific files to exclude.
|
||||||
* @param {(string|RegExp)} opts.includePattern
|
* @param {(string|RegExp)} opts.includePattern
|
||||||
* @param {(string|RegExp)} opts.excludePattern
|
* @param {(string|RegExp)} opts.excludePattern
|
||||||
*/
|
*/
|
||||||
constructor({exclude, includePattern, excludePattern}) {
|
constructor({ exclude, includePattern, excludePattern }) {
|
||||||
this._cwd = process.cwd();
|
this._cwd = process.cwd();
|
||||||
this.exclude = exclude && Array.isArray(exclude) ?
|
this.exclude =
|
||||||
exclude.map($ => path.resolve(this._cwd, $)) :
|
exclude && Array.isArray(exclude) ? exclude.map(($) => path.resolve(this._cwd, $)) : null;
|
||||||
null;
|
this.includePattern = makeRegExp(includePattern);
|
||||||
this.includePattern = makeRegExp(includePattern);
|
this.excludePattern = makeRegExp(excludePattern);
|
||||||
this.excludePattern = makeRegExp(excludePattern);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} filepath - The filepath to check.
|
||||||
|
* @returns {boolean} Should the given file be included?
|
||||||
|
*/
|
||||||
|
isIncluded(filepath) {
|
||||||
|
let included = true;
|
||||||
|
|
||||||
|
filepath = path.resolve(this._cwd, filepath);
|
||||||
|
|
||||||
|
if (this.includePattern && !this.includePattern.test(filepath)) {
|
||||||
|
included = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (this.excludePattern && this.excludePattern.test(filepath)) {
|
||||||
* @param {string} filepath - The filepath to check.
|
included = false;
|
||||||
* @returns {boolean} Should the given file be included?
|
|
||||||
*/
|
|
||||||
isIncluded(filepath) {
|
|
||||||
let included = true;
|
|
||||||
|
|
||||||
filepath = path.resolve(this._cwd, filepath);
|
|
||||||
|
|
||||||
if ( this.includePattern && !this.includePattern.test(filepath) ) {
|
|
||||||
included = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( this.excludePattern && this.excludePattern.test(filepath) ) {
|
|
||||||
included = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.exclude) {
|
|
||||||
this.exclude.forEach(exclude => {
|
|
||||||
if ( filepath.indexOf(exclude) === 0 ) {
|
|
||||||
included = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return included;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.exclude) {
|
||||||
|
this.exclude.forEach((exclude) => {
|
||||||
|
if (filepath.indexOf(exclude) === 0) {
|
||||||
|
included = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return included;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exports.Filter = Filter;
|
exports.Filter = Filter;
|
||||||
|
|||||||
@ -10,37 +10,36 @@ const { Syntax } = require('@jsdoc/parse');
|
|||||||
let currentModule = null;
|
let currentModule = null;
|
||||||
|
|
||||||
class CurrentModule {
|
class CurrentModule {
|
||||||
constructor(doclet) {
|
constructor(doclet) {
|
||||||
this.doclet = doclet;
|
this.doclet = doclet;
|
||||||
this.longname = doclet.longname;
|
this.longname = doclet.longname;
|
||||||
this.originalName = doclet.meta.code.name || '';
|
this.originalName = doclet.meta.code.name || '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function filterByLongname({longname}) {
|
function filterByLongname({ longname }) {
|
||||||
// you can't document prototypes
|
// you can't document prototypes
|
||||||
if ( /#$/.test(longname) ) {
|
if (/#$/.test(longname)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDoclet(comment, e) {
|
function createDoclet(comment, e) {
|
||||||
let doclet;
|
let doclet;
|
||||||
let flatComment;
|
let flatComment;
|
||||||
let msg;
|
let msg;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
doclet = new Doclet(comment, e);
|
doclet = new Doclet(comment, e);
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
flatComment = comment.replace(/[\r\n]/g, '');
|
||||||
flatComment = comment.replace(/[\r\n]/g, '');
|
msg = `cannot create a doclet for the comment "${flatComment}": ${error.message}`;
|
||||||
msg = `cannot create a doclet for the comment "${flatComment}": ${error.message}`;
|
log.error(msg);
|
||||||
log.error(msg);
|
doclet = new Doclet('', e);
|
||||||
doclet = new Doclet('', e);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return doclet;
|
return doclet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,301 +62,301 @@ function createDoclet(comment, e) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function createSymbolDoclet(comment, e) {
|
function createSymbolDoclet(comment, e) {
|
||||||
let doclet = createDoclet(comment, e);
|
let doclet = createDoclet(comment, e);
|
||||||
|
|
||||||
if (doclet.name) {
|
if (doclet.name) {
|
||||||
// try again, without the comment
|
// try again, without the comment
|
||||||
e.comment = '@undocumented';
|
e.comment = '@undocumented';
|
||||||
doclet = createDoclet(e.comment, e);
|
doclet = createDoclet(e.comment, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return doclet;
|
return doclet;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCurrentModule(doclet) {
|
function setCurrentModule(doclet) {
|
||||||
if (doclet.kind === 'module') {
|
if (doclet.kind === 'module') {
|
||||||
currentModule = new CurrentModule(doclet);
|
currentModule = new CurrentModule(doclet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setModuleScopeMemberOf(parser, doclet) {
|
function setModuleScopeMemberOf(parser, doclet) {
|
||||||
let parentDoclet;
|
let parentDoclet;
|
||||||
let skipMemberof;
|
let skipMemberof;
|
||||||
|
|
||||||
// handle module symbols that are _not_ assigned to module.exports
|
// handle module symbols that are _not_ assigned to module.exports
|
||||||
if (currentModule && currentModule.longname !== doclet.name) {
|
if (currentModule && currentModule.longname !== doclet.name) {
|
||||||
if (!doclet.scope) {
|
if (!doclet.scope) {
|
||||||
// is this a method definition? if so, we usually get the scope from the node directly
|
// is this a method definition? if so, we usually get the scope from the node directly
|
||||||
if (doclet.meta && doclet.meta.code && doclet.meta.code.node &&
|
if (
|
||||||
doclet.meta.code.node.type === Syntax.MethodDefinition) {
|
doclet.meta &&
|
||||||
// special case for constructors of classes that have @alias tags
|
doclet.meta.code &&
|
||||||
if (doclet.meta.code.node.kind === 'constructor') {
|
doclet.meta.code.node &&
|
||||||
parentDoclet = parser._getDocletById(
|
doclet.meta.code.node.type === Syntax.MethodDefinition
|
||||||
doclet.meta.code.node.parent.parent.nodeId
|
) {
|
||||||
);
|
// special case for constructors of classes that have @alias tags
|
||||||
|
if (doclet.meta.code.node.kind === 'constructor') {
|
||||||
|
parentDoclet = parser._getDocletById(doclet.meta.code.node.parent.parent.nodeId);
|
||||||
|
|
||||||
if (parentDoclet && parentDoclet.alias) {
|
if (parentDoclet && parentDoclet.alias) {
|
||||||
// the constructor should use the same name as the class
|
// the constructor should use the same name as the class
|
||||||
doclet.addTag('alias', parentDoclet.alias);
|
doclet.addTag('alias', parentDoclet.alias);
|
||||||
doclet.addTag('name', parentDoclet.alias);
|
doclet.addTag('name', parentDoclet.alias);
|
||||||
|
|
||||||
// and we shouldn't try to set a memberof value
|
// and we shouldn't try to set a memberof value
|
||||||
skipMemberof = true;
|
skipMemberof = true;
|
||||||
}
|
}
|
||||||
}
|
} else if (doclet.meta.code.node.static) {
|
||||||
else if (doclet.meta.code.node.static) {
|
doclet.addTag('static');
|
||||||
doclet.addTag('static');
|
} else {
|
||||||
}
|
doclet.addTag('instance');
|
||||||
else {
|
|
||||||
doclet.addTag('instance');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// is this something that the module exports? if so, it's a static member
|
|
||||||
else if (doclet.meta && doclet.meta.code && doclet.meta.code.node &&
|
|
||||||
doclet.meta.code.node.parent &&
|
|
||||||
doclet.meta.code.node.parent.type === Syntax.ExportNamedDeclaration) {
|
|
||||||
doclet.addTag('static');
|
|
||||||
}
|
|
||||||
// otherwise, it must be an inner member
|
|
||||||
else {
|
|
||||||
doclet.addTag('inner');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the doclet isn't a memberof anything yet, and it's not a global, it must be a memberof
|
|
||||||
// the current module (unless we were told to skip adding memberof)
|
|
||||||
if (!doclet.memberof && doclet.scope !== SCOPE.NAMES.GLOBAL && !skipMemberof) {
|
|
||||||
doclet.addTag('memberof', currentModule.longname);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// is this something that the module exports? if so, it's a static member
|
||||||
|
else if (
|
||||||
|
doclet.meta &&
|
||||||
|
doclet.meta.code &&
|
||||||
|
doclet.meta.code.node &&
|
||||||
|
doclet.meta.code.node.parent &&
|
||||||
|
doclet.meta.code.node.parent.type === Syntax.ExportNamedDeclaration
|
||||||
|
) {
|
||||||
|
doclet.addTag('static');
|
||||||
|
}
|
||||||
|
// otherwise, it must be an inner member
|
||||||
|
else {
|
||||||
|
doclet.addTag('inner');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the doclet isn't a memberof anything yet, and it's not a global, it must be a memberof
|
||||||
|
// the current module (unless we were told to skip adding memberof)
|
||||||
|
if (!doclet.memberof && doclet.scope !== SCOPE.NAMES.GLOBAL && !skipMemberof) {
|
||||||
|
doclet.addTag('memberof', currentModule.longname);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDefaultScope(doclet) {
|
function setDefaultScope(doclet) {
|
||||||
// module doclets don't get a default scope
|
// module doclets don't get a default scope
|
||||||
if (!doclet.scope && doclet.kind !== 'module') {
|
if (!doclet.scope && doclet.kind !== 'module') {
|
||||||
doclet.setScope(SCOPE.NAMES.GLOBAL);
|
doclet.setScope(SCOPE.NAMES.GLOBAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addDoclet(parser, newDoclet) {
|
function addDoclet(parser, newDoclet) {
|
||||||
let e;
|
let e;
|
||||||
|
|
||||||
if (newDoclet) {
|
if (newDoclet) {
|
||||||
setCurrentModule(newDoclet);
|
setCurrentModule(newDoclet);
|
||||||
e = { doclet: newDoclet };
|
e = { doclet: newDoclet };
|
||||||
parser.emit('newDoclet', e);
|
parser.emit('newDoclet', e);
|
||||||
|
|
||||||
if ( !e.defaultPrevented && !filterByLongname(e.doclet) ) {
|
if (!e.defaultPrevented && !filterByLongname(e.doclet)) {
|
||||||
parser.addResult(e.doclet);
|
parser.addResult(e.doclet);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processAlias(parser, doclet, astNode) {
|
function processAlias(parser, doclet, astNode) {
|
||||||
let memberofName;
|
let memberofName;
|
||||||
|
|
||||||
if (doclet.alias === '{@thisClass}') {
|
if (doclet.alias === '{@thisClass}') {
|
||||||
memberofName = parser.resolveThis(astNode);
|
memberofName = parser.resolveThis(astNode);
|
||||||
|
|
||||||
// "class" refers to the owner of the prototype, not the prototype itself
|
// "class" refers to the owner of the prototype, not the prototype itself
|
||||||
if ( /^(.+?)(\.prototype|#)$/.test(memberofName) ) {
|
if (/^(.+?)(\.prototype|#)$/.test(memberofName)) {
|
||||||
memberofName = RegExp.$1;
|
memberofName = RegExp.$1;
|
||||||
}
|
|
||||||
doclet.alias = memberofName;
|
|
||||||
}
|
}
|
||||||
|
doclet.alias = memberofName;
|
||||||
|
}
|
||||||
|
|
||||||
doclet.addTag('name', doclet.alias);
|
doclet.addTag('name', doclet.alias);
|
||||||
doclet.postProcess();
|
doclet.postProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: separate code that resolves `this` from code that resolves the module object
|
// TODO: separate code that resolves `this` from code that resolves the module object
|
||||||
function findSymbolMemberof(parser, doclet, astNode, nameStartsWith, trailingPunc) {
|
function findSymbolMemberof(parser, doclet, astNode, nameStartsWith, trailingPunc) {
|
||||||
let memberof = '';
|
let memberof = '';
|
||||||
let nameAndPunc;
|
let nameAndPunc;
|
||||||
let scopePunc = '';
|
let scopePunc = '';
|
||||||
|
|
||||||
// handle computed properties like foo['bar']
|
// handle computed properties like foo['bar']
|
||||||
if (trailingPunc === '[') {
|
if (trailingPunc === '[') {
|
||||||
// we don't know yet whether the symbol is a static or instance member
|
// we don't know yet whether the symbol is a static or instance member
|
||||||
trailingPunc = null;
|
trailingPunc = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
nameAndPunc = nameStartsWith + (trailingPunc || '');
|
||||||
|
|
||||||
|
// remove stuff that indicates module membership (but don't touch the name `module.exports`,
|
||||||
|
// which identifies the module object itself)
|
||||||
|
if (doclet.name !== 'module.exports') {
|
||||||
|
doclet.name = doclet.name.replace(nameAndPunc, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// like `bar` in:
|
||||||
|
// exports.bar = 1;
|
||||||
|
// module.exports.bar = 1;
|
||||||
|
// module.exports = MyModuleObject; MyModuleObject.bar = 1;
|
||||||
|
if (nameStartsWith !== 'this' && currentModule && doclet.name !== 'module.exports') {
|
||||||
|
memberof = currentModule.longname;
|
||||||
|
scopePunc = SCOPE.PUNC.STATIC;
|
||||||
|
}
|
||||||
|
// like: module.exports = 1;
|
||||||
|
else if (doclet.name === 'module.exports' && currentModule) {
|
||||||
|
doclet.addTag('name', currentModule.longname);
|
||||||
|
doclet.postProcess();
|
||||||
|
} else {
|
||||||
|
memberof = parser.resolveThis(astNode);
|
||||||
|
|
||||||
|
// like the following at the top level of a module:
|
||||||
|
// this.foo = 1;
|
||||||
|
if (nameStartsWith === 'this' && currentModule && !memberof) {
|
||||||
|
memberof = currentModule.longname;
|
||||||
|
scopePunc = SCOPE.PUNC.STATIC;
|
||||||
|
} else {
|
||||||
|
scopePunc = SCOPE.PUNC.INSTANCE;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nameAndPunc = nameStartsWith + (trailingPunc || '');
|
return {
|
||||||
|
memberof: memberof,
|
||||||
// remove stuff that indicates module membership (but don't touch the name `module.exports`,
|
scopePunc: scopePunc,
|
||||||
// which identifies the module object itself)
|
};
|
||||||
if (doclet.name !== 'module.exports') {
|
|
||||||
doclet.name = doclet.name.replace(nameAndPunc, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
// like `bar` in:
|
|
||||||
// exports.bar = 1;
|
|
||||||
// module.exports.bar = 1;
|
|
||||||
// module.exports = MyModuleObject; MyModuleObject.bar = 1;
|
|
||||||
if (nameStartsWith !== 'this' && currentModule && doclet.name !== 'module.exports') {
|
|
||||||
memberof = currentModule.longname;
|
|
||||||
scopePunc = SCOPE.PUNC.STATIC;
|
|
||||||
}
|
|
||||||
// like: module.exports = 1;
|
|
||||||
else if (doclet.name === 'module.exports' && currentModule) {
|
|
||||||
doclet.addTag('name', currentModule.longname);
|
|
||||||
doclet.postProcess();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
memberof = parser.resolveThis(astNode);
|
|
||||||
|
|
||||||
// like the following at the top level of a module:
|
|
||||||
// this.foo = 1;
|
|
||||||
if (nameStartsWith === 'this' && currentModule && !memberof) {
|
|
||||||
memberof = currentModule.longname;
|
|
||||||
scopePunc = SCOPE.PUNC.STATIC;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
scopePunc = SCOPE.PUNC.INSTANCE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
memberof: memberof,
|
|
||||||
scopePunc: scopePunc
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function addSymbolMemberof(parser, doclet, astNode) {
|
function addSymbolMemberof(parser, doclet, astNode) {
|
||||||
let basename;
|
let basename;
|
||||||
let memberof;
|
let memberof;
|
||||||
let memberofInfo;
|
let memberofInfo;
|
||||||
let moduleOriginalName = '';
|
let moduleOriginalName = '';
|
||||||
let resolveTargetRegExp;
|
let resolveTargetRegExp;
|
||||||
let scopePunc;
|
let scopePunc;
|
||||||
let unresolved;
|
let unresolved;
|
||||||
|
|
||||||
if (!astNode) {
|
if (!astNode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check to see if the doclet name is an unresolved reference to the module object, or to `this`
|
// check to see if the doclet name is an unresolved reference to the module object, or to `this`
|
||||||
// TODO: handle cases where the module object is shadowed in the current scope
|
// TODO: handle cases where the module object is shadowed in the current scope
|
||||||
if (currentModule) {
|
if (currentModule) {
|
||||||
moduleOriginalName = `|${currentModule.originalName}`;
|
moduleOriginalName = `|${currentModule.originalName}`;
|
||||||
}
|
}
|
||||||
resolveTargetRegExp = new RegExp(`^((?:module.)?exports|this${moduleOriginalName})(\\.|\\[|$)`);
|
resolveTargetRegExp = new RegExp(`^((?:module.)?exports|this${moduleOriginalName})(\\.|\\[|$)`);
|
||||||
unresolved = resolveTargetRegExp.exec(doclet.name);
|
unresolved = resolveTargetRegExp.exec(doclet.name);
|
||||||
|
|
||||||
if (unresolved) {
|
if (unresolved) {
|
||||||
memberofInfo = findSymbolMemberof(parser, doclet, astNode, unresolved[1], unresolved[2]);
|
memberofInfo = findSymbolMemberof(parser, doclet, astNode, unresolved[1], unresolved[2]);
|
||||||
memberof = memberofInfo.memberof;
|
memberof = memberofInfo.memberof;
|
||||||
scopePunc = memberofInfo.scopePunc;
|
scopePunc = memberofInfo.scopePunc;
|
||||||
|
|
||||||
if (memberof) {
|
|
||||||
doclet.name = doclet.name ?
|
|
||||||
memberof + scopePunc + doclet.name :
|
|
||||||
memberof;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
memberofInfo = parser.astnodeToMemberof(astNode);
|
|
||||||
basename = memberofInfo.basename;
|
|
||||||
memberof = memberofInfo.memberof;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we found a memberof name, apply it to the doclet
|
|
||||||
if (memberof) {
|
if (memberof) {
|
||||||
doclet.addTag('memberof', memberof);
|
doclet.name = doclet.name ? memberof + scopePunc + doclet.name : memberof;
|
||||||
if (basename) {
|
|
||||||
doclet.name = (doclet.name || '')
|
|
||||||
.replace(new RegExp(`^${escape(basename)}.`), '');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// otherwise, add the defaults for a module (if we're currently in a module)
|
} else {
|
||||||
else {
|
memberofInfo = parser.astnodeToMemberof(astNode);
|
||||||
setModuleScopeMemberOf(parser, doclet);
|
basename = memberofInfo.basename;
|
||||||
|
memberof = memberofInfo.memberof;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we found a memberof name, apply it to the doclet
|
||||||
|
if (memberof) {
|
||||||
|
doclet.addTag('memberof', memberof);
|
||||||
|
if (basename) {
|
||||||
|
doclet.name = (doclet.name || '').replace(new RegExp(`^${escape(basename)}.`), '');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// otherwise, add the defaults for a module (if we're currently in a module)
|
||||||
|
else {
|
||||||
|
setModuleScopeMemberOf(parser, doclet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function newSymbolDoclet(parser, docletSrc, e) {
|
function newSymbolDoclet(parser, docletSrc, e) {
|
||||||
const newDoclet = createSymbolDoclet(docletSrc, e);
|
const newDoclet = createSymbolDoclet(docletSrc, e);
|
||||||
|
|
||||||
// if there's an alias, use that as the symbol name
|
// if there's an alias, use that as the symbol name
|
||||||
if (newDoclet.alias) {
|
if (newDoclet.alias) {
|
||||||
processAlias(parser, newDoclet, e.astnode);
|
processAlias(parser, newDoclet, e.astnode);
|
||||||
}
|
}
|
||||||
// otherwise, get the symbol name from the code
|
// otherwise, get the symbol name from the code
|
||||||
else if (e.code && typeof e.code.name !== 'undefined' && e.code.name !== '') {
|
else if (e.code && typeof e.code.name !== 'undefined' && e.code.name !== '') {
|
||||||
newDoclet.addTag('name', e.code.name);
|
newDoclet.addTag('name', e.code.name);
|
||||||
if (!newDoclet.memberof) {
|
if (!newDoclet.memberof) {
|
||||||
addSymbolMemberof(parser, newDoclet, e.astnode);
|
addSymbolMemberof(parser, newDoclet, e.astnode);
|
||||||
}
|
|
||||||
|
|
||||||
newDoclet.postProcess();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the scope to global unless any of the following are true:
|
newDoclet.postProcess();
|
||||||
// a) the doclet is a memberof something
|
} else {
|
||||||
// b) the doclet represents a module
|
return false;
|
||||||
// c) we're in a module that exports only this symbol
|
}
|
||||||
if ( !newDoclet.memberof && newDoclet.kind !== 'module' &&
|
|
||||||
(!currentModule || currentModule.longname !== newDoclet.name) ) {
|
|
||||||
newDoclet.scope = SCOPE.NAMES.GLOBAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle cases where the doclet kind is auto-detected from the node type
|
// set the scope to global unless any of the following are true:
|
||||||
if (e.code.kind && newDoclet.kind === 'member') {
|
// a) the doclet is a memberof something
|
||||||
newDoclet.kind = e.code.kind;
|
// b) the doclet represents a module
|
||||||
}
|
// c) we're in a module that exports only this symbol
|
||||||
|
if (
|
||||||
|
!newDoclet.memberof &&
|
||||||
|
newDoclet.kind !== 'module' &&
|
||||||
|
(!currentModule || currentModule.longname !== newDoclet.name)
|
||||||
|
) {
|
||||||
|
newDoclet.scope = SCOPE.NAMES.GLOBAL;
|
||||||
|
}
|
||||||
|
|
||||||
addDoclet(parser, newDoclet);
|
// handle cases where the doclet kind is auto-detected from the node type
|
||||||
e.doclet = newDoclet;
|
if (e.code.kind && newDoclet.kind === 'member') {
|
||||||
|
newDoclet.kind = e.code.kind;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
addDoclet(parser, newDoclet);
|
||||||
|
e.doclet = newDoclet;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attach these event handlers to a particular instance of a parser.
|
* Attach these event handlers to a particular instance of a parser.
|
||||||
* @param parser
|
* @param parser
|
||||||
*/
|
*/
|
||||||
exports.attachTo = parser => {
|
exports.attachTo = (parser) => {
|
||||||
// Handle JSDoc "virtual comments" that include one of the following:
|
// Handle JSDoc "virtual comments" that include one of the following:
|
||||||
// + A `@name` tag
|
// + A `@name` tag
|
||||||
// + Another tag that accepts a name, such as `@function`
|
// + Another tag that accepts a name, such as `@function`
|
||||||
parser.on('jsdocCommentFound', e => {
|
parser.on('jsdocCommentFound', (e) => {
|
||||||
const comments = e.comment.split(/@also\b/g);
|
const comments = e.comment.split(/@also\b/g);
|
||||||
let newDoclet;
|
let newDoclet;
|
||||||
|
|
||||||
for (let i = 0, l = comments.length; i < l; i++) {
|
for (let i = 0, l = comments.length; i < l; i++) {
|
||||||
newDoclet = createDoclet(comments[i], e);
|
newDoclet = createDoclet(comments[i], e);
|
||||||
|
|
||||||
// we're only interested in virtual comments here
|
// we're only interested in virtual comments here
|
||||||
if (!newDoclet.name) {
|
if (!newDoclet.name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the default scope/memberof for a module (if we're in a module)
|
// add the default scope/memberof for a module (if we're in a module)
|
||||||
setModuleScopeMemberOf(parser, newDoclet);
|
setModuleScopeMemberOf(parser, newDoclet);
|
||||||
newDoclet.postProcess();
|
newDoclet.postProcess();
|
||||||
|
|
||||||
// if we _still_ don't have a scope, use the default
|
// if we _still_ don't have a scope, use the default
|
||||||
setDefaultScope(newDoclet);
|
setDefaultScope(newDoclet);
|
||||||
|
|
||||||
addDoclet(parser, newDoclet);
|
addDoclet(parser, newDoclet);
|
||||||
|
|
||||||
e.doclet = newDoclet;
|
e.doclet = newDoclet;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle named symbols in the code. May or may not have a JSDoc comment attached.
|
// Handle named symbols in the code. May or may not have a JSDoc comment attached.
|
||||||
parser.on('symbolFound', e => {
|
parser.on('symbolFound', (e) => {
|
||||||
const comments = e.comment.split(/@also\b/g);
|
const comments = e.comment.split(/@also\b/g);
|
||||||
|
|
||||||
for (let i = 0, l = comments.length; i < l; i++) {
|
for (let i = 0, l = comments.length; i < l; i++) {
|
||||||
newSymbolDoclet(parser, comments[i], e);
|
newSymbolDoclet(parser, comments[i], e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
parser.on('fileComplete', () => {
|
parser.on('fileComplete', () => {
|
||||||
currentModule = null;
|
currentModule = null;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -11,54 +11,52 @@ const { statSync } = require('fs');
|
|||||||
* @extends module:events.EventEmitter
|
* @extends module:events.EventEmitter
|
||||||
*/
|
*/
|
||||||
class Scanner extends EventEmitter {
|
class Scanner extends EventEmitter {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively searches the given searchPaths for js files.
|
* Recursively searches the given searchPaths for js files.
|
||||||
* @param {Array.<string>} searchPaths
|
* @param {Array.<string>} searchPaths
|
||||||
* @param {number} [depth]
|
* @param {number} [depth]
|
||||||
* @fires sourceFileFound
|
* @fires sourceFileFound
|
||||||
*/
|
*/
|
||||||
scan(searchPaths, depth, filter) {
|
scan(searchPaths, depth, filter) {
|
||||||
let currentFile;
|
let currentFile;
|
||||||
let filePaths = [];
|
let filePaths = [];
|
||||||
|
|
||||||
searchPaths = searchPaths || [];
|
searchPaths = searchPaths || [];
|
||||||
depth = depth || 1;
|
depth = depth || 1;
|
||||||
|
|
||||||
searchPaths.forEach($ => {
|
searchPaths.forEach(($) => {
|
||||||
const filepath = path.resolve(process.cwd(), decodeURIComponent($));
|
const filepath = path.resolve(process.cwd(), decodeURIComponent($));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
currentFile = statSync(filepath);
|
currentFile = statSync(filepath);
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
log.error(`Unable to find the source file or directory ${filepath}`);
|
||||||
log.error(`Unable to find the source file or directory ${filepath}`);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( currentFile.isFile() ) {
|
if (currentFile.isFile()) {
|
||||||
filePaths.push(filepath);
|
filePaths.push(filepath);
|
||||||
}
|
} else {
|
||||||
else {
|
filePaths = filePaths.concat(lsSync(filepath, depth));
|
||||||
filePaths = filePaths.concat(lsSync(filepath, depth));
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
filePaths = filePaths.filter($ => filter.isIncluded($));
|
filePaths = filePaths.filter(($) => filter.isIncluded($));
|
||||||
|
|
||||||
filePaths = filePaths.filter($ => {
|
filePaths = filePaths.filter(($) => {
|
||||||
const e = { fileName: $ };
|
const e = { fileName: $ };
|
||||||
|
|
||||||
this.emit('sourceFileFound', e);
|
this.emit('sourceFileFound', e);
|
||||||
|
|
||||||
return !e.defaultPrevented;
|
return !e.defaultPrevented;
|
||||||
});
|
});
|
||||||
|
|
||||||
return filePaths;
|
return filePaths;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.Scanner = Scanner;
|
exports.Scanner = Scanner;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -6,115 +6,115 @@ const env = require('jsdoc/env');
|
|||||||
const { log } = require('@jsdoc/util');
|
const { log } = require('@jsdoc/util');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const tag = {
|
const tag = {
|
||||||
dictionary: require('jsdoc/tag/dictionary'),
|
dictionary: require('jsdoc/tag/dictionary'),
|
||||||
validator: require('jsdoc/tag/validator'),
|
validator: require('jsdoc/tag/validator'),
|
||||||
type: require('@jsdoc/tag').type
|
type: require('@jsdoc/tag').type,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check whether the text is the same as a symbol name with leading or trailing whitespace. If so,
|
// Check whether the text is the same as a symbol name with leading or trailing whitespace. If so,
|
||||||
// the whitespace must be preserved, and the text cannot be trimmed.
|
// the whitespace must be preserved, and the text cannot be trimmed.
|
||||||
function mustPreserveWhitespace(text, meta) {
|
function mustPreserveWhitespace(text, meta) {
|
||||||
return meta && meta.code && meta.code.name === text && text.match(/(?:^\s+)|(?:\s+$)/);
|
return meta && meta.code && meta.code.name === text && text.match(/(?:^\s+)|(?:\s+$)/);
|
||||||
}
|
}
|
||||||
|
|
||||||
function trim(text, opts, meta) {
|
function trim(text, opts, meta) {
|
||||||
let indentMatcher;
|
let indentMatcher;
|
||||||
let match;
|
let match;
|
||||||
|
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
text = String(typeof text === 'undefined' ? '' : text);
|
text = String(typeof text === 'undefined' ? '' : text);
|
||||||
|
|
||||||
if ( mustPreserveWhitespace(text, meta) ) {
|
if (mustPreserveWhitespace(text, meta)) {
|
||||||
text = `"${text}"`;
|
text = `"${text}"`;
|
||||||
}
|
} else if (opts.keepsWhitespace) {
|
||||||
else if (opts.keepsWhitespace) {
|
text = text.replace(/^[\n\r\f]+|[\n\r\f]+$/g, '');
|
||||||
text = text.replace(/^[\n\r\f]+|[\n\r\f]+$/g, '');
|
if (opts.removesIndent) {
|
||||||
if (opts.removesIndent) {
|
match = text.match(/^([ \t]+)/);
|
||||||
match = text.match(/^([ \t]+)/);
|
if (match && match[1]) {
|
||||||
if (match && match[1]) {
|
indentMatcher = new RegExp(`^${match[1]}`, 'gm');
|
||||||
indentMatcher = new RegExp(`^${match[1]}`, 'gm');
|
text = text.replace(indentMatcher, '');
|
||||||
text = text.replace(indentMatcher, '');
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
text = text.replace(/^\s+|\s+$/g, '');
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
text = text.replace(/^\s+|\s+$/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addHiddenProperty(obj, propName, propValue) {
|
function addHiddenProperty(obj, propName, propValue) {
|
||||||
Object.defineProperty(obj, propName, {
|
Object.defineProperty(obj, propName, {
|
||||||
value: propValue,
|
value: propValue,
|
||||||
writable: true,
|
writable: true,
|
||||||
enumerable: Boolean(env.opts.debug),
|
enumerable: Boolean(env.opts.debug),
|
||||||
configurable: true
|
configurable: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseType({text, originalTitle}, {canHaveName, canHaveType}, meta) {
|
function parseType({ text, originalTitle }, { canHaveName, canHaveType }, meta) {
|
||||||
try {
|
try {
|
||||||
return tag.type.parse(text, canHaveName, canHaveType);
|
return tag.type.parse(text, canHaveName, canHaveType);
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
log.error(
|
||||||
log.error(
|
'Unable to parse a tag\'s type expression%s with tag title "%s" and text "%s": %s',
|
||||||
'Unable to parse a tag\'s type expression%s with tag title "%s" and text "%s": %s',
|
meta.filename
|
||||||
meta.filename ? ( ` for source file ${path.join(meta.path, meta.filename)}${meta.lineno ? (` in line ${meta.lineno}`) : ''}` ) : '',
|
? ` for source file ${path.join(meta.path, meta.filename)}${
|
||||||
originalTitle,
|
meta.lineno ? ` in line ${meta.lineno}` : ''
|
||||||
text,
|
}`
|
||||||
e.message
|
: '',
|
||||||
);
|
originalTitle,
|
||||||
|
text,
|
||||||
|
e.message
|
||||||
|
);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processTagText(tagInstance, tagDef, meta) {
|
function processTagText(tagInstance, tagDef, meta) {
|
||||||
let tagType;
|
let tagType;
|
||||||
|
|
||||||
if (tagDef.onTagText) {
|
if (tagDef.onTagText) {
|
||||||
tagInstance.text = tagDef.onTagText(tagInstance.text);
|
tagInstance.text = tagDef.onTagText(tagInstance.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagDef.canHaveType || tagDef.canHaveName) {
|
||||||
|
/** The value property represents the result of parsing the tag text. */
|
||||||
|
tagInstance.value = {};
|
||||||
|
|
||||||
|
tagType = parseType(tagInstance, tagDef, meta);
|
||||||
|
|
||||||
|
// It is possible for a tag to *not* have a type but still have
|
||||||
|
// optional or defaultvalue, e.g. '@param [foo]'.
|
||||||
|
// Although tagType.type.length == 0 we should still copy the other properties.
|
||||||
|
if (tagType.type) {
|
||||||
|
if (tagType.type.length) {
|
||||||
|
tagInstance.value.type = {
|
||||||
|
names: tagType.type,
|
||||||
|
};
|
||||||
|
addHiddenProperty(tagInstance.value.type, 'parsedType', tagType.parsedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
['optional', 'nullable', 'variable', 'defaultvalue'].forEach((prop) => {
|
||||||
|
if (typeof tagType[prop] !== 'undefined') {
|
||||||
|
tagInstance.value[prop] = tagType[prop];
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tagDef.canHaveType || tagDef.canHaveName) {
|
if (tagType.text && tagType.text.length) {
|
||||||
/** The value property represents the result of parsing the tag text. */
|
tagInstance.value.description = tagType.text;
|
||||||
tagInstance.value = {};
|
|
||||||
|
|
||||||
tagType = parseType(tagInstance, tagDef, meta);
|
|
||||||
|
|
||||||
// It is possible for a tag to *not* have a type but still have
|
|
||||||
// optional or defaultvalue, e.g. '@param [foo]'.
|
|
||||||
// Although tagType.type.length == 0 we should still copy the other properties.
|
|
||||||
if (tagType.type) {
|
|
||||||
if (tagType.type.length) {
|
|
||||||
tagInstance.value.type = {
|
|
||||||
names: tagType.type
|
|
||||||
};
|
|
||||||
addHiddenProperty(tagInstance.value.type, 'parsedType', tagType.parsedType);
|
|
||||||
}
|
|
||||||
|
|
||||||
['optional', 'nullable', 'variable', 'defaultvalue'].forEach(prop => {
|
|
||||||
if (typeof tagType[prop] !== 'undefined') {
|
|
||||||
tagInstance.value[prop] = tagType[prop];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tagType.text && tagType.text.length) {
|
|
||||||
tagInstance.value.description = tagType.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tagDef.canHaveName) {
|
|
||||||
// note the dash is a special case: as a param name it means "no name"
|
|
||||||
if (tagType.name && tagType.name !== '-') {
|
|
||||||
tagInstance.value.name = tagType.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
tagInstance.value = tagInstance.text;
|
if (tagDef.canHaveName) {
|
||||||
|
// note the dash is a special case: as a param name it means "no name"
|
||||||
|
if (tagType.name && tagType.name !== '-') {
|
||||||
|
tagInstance.value.name = tagType.name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
tagInstance.value = tagInstance.text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -127,62 +127,62 @@ function processTagText(tagInstance, tagDef, meta) {
|
|||||||
* @param {module:jsdoc/tag/dictionary.Dictionary} dict - The new tag dictionary.
|
* @param {module:jsdoc/tag/dictionary.Dictionary} dict - The new tag dictionary.
|
||||||
*/
|
*/
|
||||||
exports._replaceDictionary = function _replaceDictionary(dict) {
|
exports._replaceDictionary = function _replaceDictionary(dict) {
|
||||||
tag.dictionary = dict;
|
tag.dictionary = dict;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a single doclet tag.
|
* Represents a single doclet tag.
|
||||||
*/
|
*/
|
||||||
class Tag {
|
class Tag {
|
||||||
|
/**
|
||||||
|
* Constructs a new tag object. Calls the tag validator.
|
||||||
|
*
|
||||||
|
* @param {string} tagTitle
|
||||||
|
* @param {string=} tagBody
|
||||||
|
* @param {object=} meta
|
||||||
|
*/
|
||||||
|
constructor(tagTitle, tagBody, meta) {
|
||||||
|
let tagDef;
|
||||||
|
let trimOpts;
|
||||||
|
|
||||||
|
meta = meta || {};
|
||||||
|
|
||||||
|
this.originalTitle = trim(tagTitle);
|
||||||
|
|
||||||
|
/** The title of the tag (for example, `title` in `@title text`). */
|
||||||
|
this.title = tag.dictionary.normalize(this.originalTitle);
|
||||||
|
|
||||||
|
tagDef = tag.dictionary.lookUp(this.title);
|
||||||
|
trimOpts = {
|
||||||
|
keepsWhitespace: tagDef.keepsWhitespace,
|
||||||
|
removesIndent: tagDef.removesIndent,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new tag object. Calls the tag validator.
|
* The text following the tag (for example, `text` in `@title text`).
|
||||||
*
|
*
|
||||||
* @param {string} tagTitle
|
* Whitespace is trimmed from the tag text as follows:
|
||||||
* @param {string=} tagBody
|
*
|
||||||
* @param {object=} meta
|
* + If the tag's `keepsWhitespace` option is falsy, all leading and trailing whitespace are
|
||||||
|
* removed.
|
||||||
|
* + If the tag's `keepsWhitespace` option is set to `true`, leading and trailing whitespace are
|
||||||
|
* not trimmed, unless the `removesIndent` option is also enabled.
|
||||||
|
* + If the tag's `removesIndent` option is set to `true`, any indentation that is shared by
|
||||||
|
* every line in the string is removed. This option is ignored unless `keepsWhitespace` is set
|
||||||
|
* to `true`.
|
||||||
|
*
|
||||||
|
* **Note**: If the tag text is the name of a symbol, and the symbol's name includes leading or
|
||||||
|
* trailing whitespace (for example, the property names in `{ ' ': true, ' foo ': false }`),
|
||||||
|
* the tag text is not trimmed. Instead, the tag text is wrapped in double quotes to prevent the
|
||||||
|
* whitespace from being trimmed.
|
||||||
*/
|
*/
|
||||||
constructor(tagTitle, tagBody, meta) {
|
this.text = trim(tagBody, trimOpts, meta);
|
||||||
let tagDef;
|
|
||||||
let trimOpts;
|
|
||||||
|
|
||||||
meta = meta || {};
|
if (this.text) {
|
||||||
|
processTagText(this, tagDef, meta);
|
||||||
this.originalTitle = trim(tagTitle);
|
|
||||||
|
|
||||||
/** The title of the tag (for example, `title` in `@title text`). */
|
|
||||||
this.title = tag.dictionary.normalize(this.originalTitle);
|
|
||||||
|
|
||||||
tagDef = tag.dictionary.lookUp(this.title);
|
|
||||||
trimOpts = {
|
|
||||||
keepsWhitespace: tagDef.keepsWhitespace,
|
|
||||||
removesIndent: tagDef.removesIndent
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The text following the tag (for example, `text` in `@title text`).
|
|
||||||
*
|
|
||||||
* Whitespace is trimmed from the tag text as follows:
|
|
||||||
*
|
|
||||||
* + If the tag's `keepsWhitespace` option is falsy, all leading and trailing whitespace are
|
|
||||||
* removed.
|
|
||||||
* + If the tag's `keepsWhitespace` option is set to `true`, leading and trailing whitespace are
|
|
||||||
* not trimmed, unless the `removesIndent` option is also enabled.
|
|
||||||
* + If the tag's `removesIndent` option is set to `true`, any indentation that is shared by
|
|
||||||
* every line in the string is removed. This option is ignored unless `keepsWhitespace` is set
|
|
||||||
* to `true`.
|
|
||||||
*
|
|
||||||
* **Note**: If the tag text is the name of a symbol, and the symbol's name includes leading or
|
|
||||||
* trailing whitespace (for example, the property names in `{ ' ': true, ' foo ': false }`),
|
|
||||||
* the tag text is not trimmed. Instead, the tag text is wrapped in double quotes to prevent the
|
|
||||||
* whitespace from being trimmed.
|
|
||||||
*/
|
|
||||||
this.text = trim(tagBody, trimOpts, meta);
|
|
||||||
|
|
||||||
if (this.text) {
|
|
||||||
processTagText(this, tagDef, meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
tag.validator.validate(this, tagDef, meta);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tag.validator.validate(this, tagDef, meta);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exports.Tag = Tag;
|
exports.Tag = Tag;
|
||||||
|
|||||||
@ -6,168 +6,171 @@ const { log } = require('@jsdoc/util');
|
|||||||
const hasOwnProp = Object.prototype.hasOwnProperty;
|
const hasOwnProp = Object.prototype.hasOwnProperty;
|
||||||
|
|
||||||
const DEFINITIONS = {
|
const DEFINITIONS = {
|
||||||
closure: 'closureTags',
|
closure: 'closureTags',
|
||||||
jsdoc: 'jsdocTags'
|
jsdoc: 'jsdocTags',
|
||||||
};
|
};
|
||||||
|
|
||||||
let dictionary;
|
let dictionary;
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
class TagDefinition {
|
class TagDefinition {
|
||||||
constructor(dict, title, etc) {
|
constructor(dict, title, etc) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
etc = etc || {};
|
etc = etc || {};
|
||||||
|
|
||||||
this.title = dict.normalize(title);
|
this.title = dict.normalize(title);
|
||||||
|
|
||||||
Object.defineProperty(this, '_dictionary', {
|
Object.defineProperty(this, '_dictionary', {
|
||||||
value: dict
|
value: dict,
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.keys(etc).forEach(p => {
|
Object.keys(etc).forEach((p) => {
|
||||||
self[p] = etc[p];
|
self[p] = etc[p];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
synonym(synonymName) {
|
synonym(synonymName) {
|
||||||
this._dictionary.defineSynonym(this.title, synonymName);
|
this._dictionary.defineSynonym(this.title, synonymName);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @alias module:jsdoc/tag/dictionary.Dictionary
|
* @alias module:jsdoc/tag/dictionary.Dictionary
|
||||||
*/
|
*/
|
||||||
class Dictionary {
|
class Dictionary {
|
||||||
constructor() {
|
constructor() {
|
||||||
// TODO: Consider adding internal tags in the constructor, ideally as fallbacks that aren't
|
// TODO: Consider adding internal tags in the constructor, ideally as fallbacks that aren't
|
||||||
// used to confirm whether a tag is defined/valid, rather than requiring every set of tag
|
// used to confirm whether a tag is defined/valid, rather than requiring every set of tag
|
||||||
// definitions to contain the internal tags.
|
// definitions to contain the internal tags.
|
||||||
this._tags = {};
|
this._tags = {};
|
||||||
this._tagSynonyms = {};
|
this._tagSynonyms = {};
|
||||||
// The longnames for `Package` objects include a `package` namespace. There's no `package`
|
// The longnames for `Package` objects include a `package` namespace. There's no `package`
|
||||||
// tag, though, so we declare the namespace here.
|
// tag, though, so we declare the namespace here.
|
||||||
// TODO: Consider making this a fallback as suggested above for internal tags.
|
// TODO: Consider making this a fallback as suggested above for internal tags.
|
||||||
this._namespaces = ['package'];
|
this._namespaces = ['package'];
|
||||||
|
}
|
||||||
|
|
||||||
|
_defineNamespace(title) {
|
||||||
|
title = this.normalize(title || '');
|
||||||
|
|
||||||
|
if (title && !this._namespaces.includes(title)) {
|
||||||
|
this._namespaces.push(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
_defineNamespace(title) {
|
return this;
|
||||||
title = this.normalize(title || '');
|
}
|
||||||
|
|
||||||
if (title && !this._namespaces.includes(title)) {
|
defineTag(title, opts) {
|
||||||
this._namespaces.push(title);
|
const tagDef = new TagDefinition(this, title, opts);
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
this._tags[tagDef.title] = tagDef;
|
||||||
|
|
||||||
|
if (tagDef.isNamespace) {
|
||||||
|
this._defineNamespace(tagDef.title);
|
||||||
|
}
|
||||||
|
if (tagDef.synonyms) {
|
||||||
|
tagDef.synonyms.forEach((synonym) => {
|
||||||
|
this.defineSynonym(title, synonym);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
defineTag(title, opts) {
|
return this._tags[tagDef.title];
|
||||||
const tagDef = new TagDefinition(this, title, opts);
|
}
|
||||||
|
|
||||||
this._tags[tagDef.title] = tagDef;
|
defineTags(tagDefs) {
|
||||||
|
const tags = {};
|
||||||
|
|
||||||
if (tagDef.isNamespace) {
|
for (const title of Object.keys(tagDefs)) {
|
||||||
this._defineNamespace(tagDef.title);
|
tags[title] = this.defineTag(title, tagDefs[title]);
|
||||||
}
|
|
||||||
if (tagDef.synonyms) {
|
|
||||||
tagDef.synonyms.forEach(synonym => {
|
|
||||||
this.defineSynonym(title, synonym);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._tags[tagDef.title];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defineTags(tagDefs) {
|
return tags;
|
||||||
const tags = {};
|
}
|
||||||
|
|
||||||
for (const title of Object.keys(tagDefs)) {
|
defineSynonym(title, synonym) {
|
||||||
tags[title] = this.defineTag(title, tagDefs[title]);
|
this._tagSynonyms[synonym.toLowerCase()] = this.normalize(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tags;
|
static fromConfig(env) {
|
||||||
}
|
let dictionaries = env.conf.tags.dictionaries;
|
||||||
|
const dict = new Dictionary();
|
||||||
|
|
||||||
defineSynonym(title, synonym) {
|
if (!dictionaries) {
|
||||||
this._tagSynonyms[synonym.toLowerCase()] = this.normalize(title);
|
log.error(
|
||||||
}
|
'The configuration setting "tags.dictionaries" is undefined. ' +
|
||||||
|
'Unable to load tag definitions.'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
dictionaries
|
||||||
|
.slice()
|
||||||
|
.reverse()
|
||||||
|
.forEach((dictName) => {
|
||||||
|
const tagDefs = definitions[DEFINITIONS[dictName]];
|
||||||
|
|
||||||
static fromConfig(env) {
|
if (!tagDefs) {
|
||||||
let dictionaries = env.conf.tags.dictionaries;
|
|
||||||
const dict = new Dictionary();
|
|
||||||
|
|
||||||
if (!dictionaries) {
|
|
||||||
log.error(
|
log.error(
|
||||||
'The configuration setting "tags.dictionaries" is undefined. ' +
|
'The configuration setting "tags.dictionaries" contains ' +
|
||||||
'Unable to load tag definitions.'
|
`the unknown dictionary name ${dictName}. Ignoring the dictionary.`
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
dictionaries.slice().reverse().forEach(dictName => {
|
|
||||||
const tagDefs = definitions[DEFINITIONS[dictName]];
|
|
||||||
|
|
||||||
if (!tagDefs) {
|
return;
|
||||||
log.error(
|
}
|
||||||
'The configuration setting "tags.dictionaries" contains ' +
|
|
||||||
`the unknown dictionary name ${dictName}. Ignoring the dictionary.`
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
dict.defineTags(tagDefs);
|
||||||
}
|
});
|
||||||
|
|
||||||
dict.defineTags(tagDefs);
|
dict.defineTags(definitions.internalTags);
|
||||||
});
|
|
||||||
|
|
||||||
dict.defineTags(definitions.internalTags);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dict;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getNamespaces() {
|
return dict;
|
||||||
return this._namespaces.slice();
|
}
|
||||||
|
|
||||||
|
getNamespaces() {
|
||||||
|
return this._namespaces.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
isNamespace(kind) {
|
||||||
|
if (kind) {
|
||||||
|
kind = this.normalize(kind);
|
||||||
|
if (this._namespaces.includes(kind)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isNamespace(kind) {
|
return false;
|
||||||
if (kind) {
|
}
|
||||||
kind = this.normalize(kind);
|
|
||||||
if (this._namespaces.includes(kind)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
lookup(title) {
|
||||||
|
title = this.normalize(title);
|
||||||
|
|
||||||
|
if (hasOwnProp.call(this._tags, title)) {
|
||||||
|
return this._tags[title];
|
||||||
}
|
}
|
||||||
|
|
||||||
lookup(title) {
|
return false;
|
||||||
title = this.normalize(title);
|
}
|
||||||
|
|
||||||
if ( hasOwnProp.call(this._tags, title) ) {
|
lookUp(title) {
|
||||||
return this._tags[title];
|
return this.lookup(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
normalise(title) {
|
||||||
|
return this.normalize(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize(title) {
|
||||||
|
const canonicalName = title.toLowerCase();
|
||||||
|
|
||||||
|
if (hasOwnProp.call(this._tagSynonyms, canonicalName)) {
|
||||||
|
return this._tagSynonyms[canonicalName];
|
||||||
}
|
}
|
||||||
|
|
||||||
lookUp(title) {
|
return canonicalName;
|
||||||
return this.lookup(title);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
normalise(title) {
|
|
||||||
return this.normalize(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
normalize(title) {
|
|
||||||
const canonicalName = title.toLowerCase();
|
|
||||||
|
|
||||||
if ( hasOwnProp.call(this._tagSynonyms, canonicalName) ) {
|
|
||||||
return this._tagSynonyms[canonicalName];
|
|
||||||
}
|
|
||||||
|
|
||||||
return canonicalName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize the default dictionary
|
// initialize the default dictionary
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -5,47 +5,47 @@
|
|||||||
const env = require('jsdoc/env');
|
const env = require('jsdoc/env');
|
||||||
const { log } = require('@jsdoc/util');
|
const { log } = require('@jsdoc/util');
|
||||||
|
|
||||||
function buildMessage(tagName, {filename, lineno, comment}, desc) {
|
function buildMessage(tagName, { filename, lineno, comment }, desc) {
|
||||||
let result = `The @${tagName} tag ${desc}. File: ${filename}, line: ${lineno}`;
|
let result = `The @${tagName} tag ${desc}. File: ${filename}, line: ${lineno}`;
|
||||||
|
|
||||||
if (comment) {
|
if (comment) {
|
||||||
result += `\n${comment}`;
|
result += `\n${comment}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate the given tag.
|
* Validate the given tag.
|
||||||
*/
|
*/
|
||||||
exports.validate = ({title, text, value}, tagDef, meta) => {
|
exports.validate = ({ title, text, value }, tagDef, meta) => {
|
||||||
const allowUnknownTags = env.conf.tags.allowUnknownTags;
|
const allowUnknownTags = env.conf.tags.allowUnknownTags;
|
||||||
|
|
||||||
// handle cases where the tag definition does not exist
|
// handle cases where the tag definition does not exist
|
||||||
if (!tagDef) {
|
if (!tagDef) {
|
||||||
// log an error if unknown tags are not allowed
|
// log an error if unknown tags are not allowed
|
||||||
if (!allowUnknownTags ||
|
if (
|
||||||
(Array.isArray(allowUnknownTags) &&
|
!allowUnknownTags ||
|
||||||
!allowUnknownTags.includes(title))) {
|
(Array.isArray(allowUnknownTags) && !allowUnknownTags.includes(title))
|
||||||
log.error(buildMessage(title, meta, 'is not a known tag'));
|
) {
|
||||||
}
|
log.error(buildMessage(title, meta, 'is not a known tag'));
|
||||||
|
|
||||||
// stop validation, since there's nothing to validate against
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for errors that make the tag useless
|
// stop validation, since there's nothing to validate against
|
||||||
if (!text && tagDef.mustHaveValue) {
|
return;
|
||||||
log.error(buildMessage(title, meta, 'requires a value'));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// check for minor issues that are usually harmless
|
// check for errors that make the tag useless
|
||||||
else if (text && tagDef.mustNotHaveValue) {
|
if (!text && tagDef.mustHaveValue) {
|
||||||
log.warn(buildMessage(title, meta,
|
log.error(buildMessage(title, meta, 'requires a value'));
|
||||||
'does not permit a value; the value will be ignored'));
|
}
|
||||||
}
|
|
||||||
else if (value && value.description && tagDef.mustNotHaveDescription) {
|
// check for minor issues that are usually harmless
|
||||||
log.warn(buildMessage(title, meta,
|
else if (text && tagDef.mustNotHaveValue) {
|
||||||
'does not permit a description; the description will be ignored'));
|
log.warn(buildMessage(title, meta, 'does not permit a value; the value will be ignored'));
|
||||||
}
|
} else if (value && value.description && tagDef.mustNotHaveDescription) {
|
||||||
|
log.warn(
|
||||||
|
buildMessage(title, meta, 'does not permit a description; the description will be ignored')
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,71 +10,71 @@ const path = require('path');
|
|||||||
* Template helper.
|
* Template helper.
|
||||||
*/
|
*/
|
||||||
class Template {
|
class Template {
|
||||||
/**
|
/**
|
||||||
* @param {string} filepath - Templates directory.
|
* @param {string} filepath - Templates directory.
|
||||||
*/
|
*/
|
||||||
constructor(filepath) {
|
constructor(filepath) {
|
||||||
this.path = filepath;
|
this.path = filepath;
|
||||||
this.layout = null;
|
this.layout = null;
|
||||||
this.cache = {};
|
this.cache = {};
|
||||||
// override default template tag settings
|
// override default template tag settings
|
||||||
this.settings = {
|
this.settings = {
|
||||||
evaluate: /<\?js([\s\S]+?)\?>/g,
|
evaluate: /<\?js([\s\S]+?)\?>/g,
|
||||||
interpolate: /<\?js=([\s\S]+?)\?>/g,
|
interpolate: /<\?js=([\s\S]+?)\?>/g,
|
||||||
escape: /<\?js~([\s\S]+?)\?>/g
|
escape: /<\?js~([\s\S]+?)\?>/g,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads template from given file.
|
||||||
|
* @param {string} file - Template filename.
|
||||||
|
* @return {function} Returns template closure.
|
||||||
|
*/
|
||||||
|
load(file) {
|
||||||
|
return _.template(fs.readFileSync(file, 'utf8'), this.settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders template using given data.
|
||||||
|
*
|
||||||
|
* This is low-level function, for rendering full templates use {@link Template.render()}.
|
||||||
|
*
|
||||||
|
* @param {string} file - Template filename.
|
||||||
|
* @param {object} data - Template variables (doesn't have to be object, but passing variables dictionary is best way and most common use).
|
||||||
|
* @return {string} Rendered template.
|
||||||
|
*/
|
||||||
|
partial(file, data) {
|
||||||
|
file = path.resolve(this.path, file);
|
||||||
|
|
||||||
|
// load template into cache
|
||||||
|
if (!(file in this.cache)) {
|
||||||
|
this.cache[file] = this.load(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// keep template helper context
|
||||||
* Loads template from given file.
|
return this.cache[file].call(this, data);
|
||||||
* @param {string} file - Template filename.
|
}
|
||||||
* @return {function} Returns template closure.
|
|
||||||
*/
|
/**
|
||||||
load(file) {
|
* Renders template with given data.
|
||||||
return _.template(fs.readFileSync(file, 'utf8'), this.settings);
|
*
|
||||||
|
* This method automaticaly applies layout if set.
|
||||||
|
*
|
||||||
|
* @param {string} file - Template filename.
|
||||||
|
* @param {object} data - Template variables (doesn't have to be object, but passing variables dictionary is best way and most common use).
|
||||||
|
* @return {string} Rendered template.
|
||||||
|
*/
|
||||||
|
render(file, data) {
|
||||||
|
// main content
|
||||||
|
let content = this.partial(file, data);
|
||||||
|
|
||||||
|
// apply layout
|
||||||
|
if (this.layout) {
|
||||||
|
data.content = content;
|
||||||
|
content = this.partial(this.layout, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return content;
|
||||||
* Renders template using given data.
|
}
|
||||||
*
|
|
||||||
* This is low-level function, for rendering full templates use {@link Template.render()}.
|
|
||||||
*
|
|
||||||
* @param {string} file - Template filename.
|
|
||||||
* @param {object} data - Template variables (doesn't have to be object, but passing variables dictionary is best way and most common use).
|
|
||||||
* @return {string} Rendered template.
|
|
||||||
*/
|
|
||||||
partial(file, data) {
|
|
||||||
file = path.resolve(this.path, file);
|
|
||||||
|
|
||||||
// load template into cache
|
|
||||||
if (!(file in this.cache)) {
|
|
||||||
this.cache[file] = this.load(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep template helper context
|
|
||||||
return this.cache[file].call(this, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders template with given data.
|
|
||||||
*
|
|
||||||
* This method automaticaly applies layout if set.
|
|
||||||
*
|
|
||||||
* @param {string} file - Template filename.
|
|
||||||
* @param {object} data - Template variables (doesn't have to be object, but passing variables dictionary is best way and most common use).
|
|
||||||
* @return {string} Rendered template.
|
|
||||||
*/
|
|
||||||
render(file, data) {
|
|
||||||
// main content
|
|
||||||
let content = this.partial(file, data);
|
|
||||||
|
|
||||||
// apply layout
|
|
||||||
if (this.layout) {
|
|
||||||
data.content = content;
|
|
||||||
content = this.partial(this.layout, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
exports.Template = Template;
|
exports.Template = Template;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
128
packages/jsdoc/package-lock.json
generated
128
packages/jsdoc/package-lock.json
generated
@ -1,8 +1,134 @@
|
|||||||
{
|
{
|
||||||
"name": "jsdoc",
|
"name": "jsdoc",
|
||||||
"version": "4.0.0-dev.16",
|
"version": "4.0.0-dev.16",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "jsdoc",
|
||||||
|
"version": "4.0.0-dev.16",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/parser": "^7.15.7",
|
||||||
|
"bluebird": "^3.7.2",
|
||||||
|
"catharsis": "^0.9.0",
|
||||||
|
"code-prettify": "^0.1.0",
|
||||||
|
"color-themes-for-google-code-prettify": "^2.0.4",
|
||||||
|
"common-path-prefix": "^3.0.0",
|
||||||
|
"escape-string-regexp": "^4.0.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"open-sans-fonts": "^1.6.2",
|
||||||
|
"requizzle": "^0.2.3",
|
||||||
|
"strip-bom": "^4.0.0",
|
||||||
|
"strip-json-comments": "^3.1.1",
|
||||||
|
"taffydb": "2.6.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"jsdoc": "jsdoc.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=v14.17.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/parser": {
|
||||||
|
"version": "7.15.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz",
|
||||||
|
"integrity": "sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g==",
|
||||||
|
"bin": {
|
||||||
|
"parser": "bin/babel-parser.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/bluebird": {
|
||||||
|
"version": "3.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||||
|
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
|
||||||
|
},
|
||||||
|
"node_modules/catharsis": {
|
||||||
|
"version": "0.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
|
||||||
|
"integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4.17.15"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/code-prettify": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/code-prettify/-/code-prettify-0.1.0.tgz",
|
||||||
|
"integrity": "sha1-RocMyMGlDQm61TmzOpg9vUqjSx4="
|
||||||
|
},
|
||||||
|
"node_modules/color-themes-for-google-code-prettify": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-themes-for-google-code-prettify/-/color-themes-for-google-code-prettify-2.0.4.tgz",
|
||||||
|
"integrity": "sha1-3urPZX/WhXaGR1TU5IbXjf2x54Q=",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=5.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/common-path-prefix": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w=="
|
||||||
|
},
|
||||||
|
"node_modules/escape-string-regexp": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
|
},
|
||||||
|
"node_modules/open-sans-fonts": {
|
||||||
|
"version": "1.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/open-sans-fonts/-/open-sans-fonts-1.6.2.tgz",
|
||||||
|
"integrity": "sha512-vsJ6/Mm0TdUKQJqxfkXJy+0K2X0QeRuTmxQq9YE1ycziw6CbDPolDsHhQ6+ImoV/7OTh8K8ZTGklY1Z5nUAwug=="
|
||||||
|
},
|
||||||
|
"node_modules/requizzle": {
|
||||||
|
"version": "0.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz",
|
||||||
|
"integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4.17.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-bom": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-json-comments": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/taffydb": {
|
||||||
|
"version": "2.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz",
|
||||||
|
"integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg="
|
||||||
|
}
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": {
|
"@babel/parser": {
|
||||||
"version": "7.15.7",
|
"version": "7.15.7",
|
||||||
|
|||||||
@ -5,17 +5,17 @@
|
|||||||
* @module plugins/commentConvert
|
* @module plugins/commentConvert
|
||||||
*/
|
*/
|
||||||
exports.handlers = {
|
exports.handlers = {
|
||||||
///
|
///
|
||||||
/// Convert ///-style comments into jsdoc comments.
|
/// Convert ///-style comments into jsdoc comments.
|
||||||
/// @param e
|
/// @param e
|
||||||
/// @param e.filename
|
/// @param e.filename
|
||||||
/// @param e.source
|
/// @param e.source
|
||||||
///
|
///
|
||||||
beforeParse(e) {
|
beforeParse(e) {
|
||||||
e.source = e.source.replace(/(\n[ \t]*\/\/\/[^\n]*)+/g, $ => {
|
e.source = e.source.replace(/(\n[ \t]*\/\/\/[^\n]*)+/g, ($) => {
|
||||||
const replacement = `\n/**${$.replace(/^[ \t]*\/\/\//mg, '').replace(/(\n$|$)/, '*/$1')}`;
|
const replacement = `\n/**${$.replace(/^[ \t]*\/\/\//gm, '').replace(/(\n$|$)/, '*/$1')}`;
|
||||||
|
|
||||||
return replacement;
|
return replacement;
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,14 +4,14 @@
|
|||||||
* @module plugins/commentsOnly
|
* @module plugins/commentsOnly
|
||||||
*/
|
*/
|
||||||
exports.handlers = {
|
exports.handlers = {
|
||||||
beforeParse(e) {
|
beforeParse(e) {
|
||||||
// a JSDoc comment looks like: /**[one or more chars]*/
|
// a JSDoc comment looks like: /**[one or more chars]*/
|
||||||
const comments = e.source.match(/\/\*\*[\s\S]+?\*\//g);
|
const comments = e.source.match(/\/\*\*[\s\S]+?\*\//g);
|
||||||
|
|
||||||
if (comments) {
|
if (comments) {
|
||||||
e.source = comments.join('\n\n');
|
e.source = comments.join('\n\n');
|
||||||
} else {
|
} else {
|
||||||
e.source = ''; // If file has no comments, parser should still receive no code
|
e.source = ''; // If file has no comments, parser should still receive no code
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,15 +4,15 @@
|
|||||||
* @module plugins/escapeHtml
|
* @module plugins/escapeHtml
|
||||||
*/
|
*/
|
||||||
exports.handlers = {
|
exports.handlers = {
|
||||||
/**
|
/**
|
||||||
* Translate HTML tags in descriptions into safe entities. Replaces <, & and newlines
|
* Translate HTML tags in descriptions into safe entities. Replaces <, & and newlines
|
||||||
*/
|
*/
|
||||||
newDoclet({doclet}) {
|
newDoclet({ doclet }) {
|
||||||
if (doclet.description) {
|
if (doclet.description) {
|
||||||
doclet.description = doclet.description
|
doclet.description = doclet.description
|
||||||
.replace(/&/g, '&')
|
.replace(/&/g, '&')
|
||||||
.replace(/</g, '<')
|
.replace(/</g, '<')
|
||||||
.replace(/\r\n|\n|\r/g, '<br>');
|
.replace(/\r\n|\n|\r/g, '<br>');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,20 +10,20 @@ const conf = env.conf.eventDumper || {};
|
|||||||
|
|
||||||
// Dump the included parser events (defaults to all events)
|
// Dump the included parser events (defaults to all events)
|
||||||
let events = conf.include || [
|
let events = conf.include || [
|
||||||
'parseBegin',
|
'parseBegin',
|
||||||
'fileBegin',
|
'fileBegin',
|
||||||
'beforeParse',
|
'beforeParse',
|
||||||
'jsdocCommentFound',
|
'jsdocCommentFound',
|
||||||
'symbolFound',
|
'symbolFound',
|
||||||
'newDoclet',
|
'newDoclet',
|
||||||
'fileComplete',
|
'fileComplete',
|
||||||
'parseComplete',
|
'parseComplete',
|
||||||
'processingComplete'
|
'processingComplete',
|
||||||
];
|
];
|
||||||
|
|
||||||
// Don't dump the excluded parser events
|
// Don't dump the excluded parser events
|
||||||
if (conf.exclude) {
|
if (conf.exclude) {
|
||||||
events = _.difference(events, conf.exclude);
|
events = _.difference(events, conf.exclude);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,25 +33,25 @@ if (conf.exclude) {
|
|||||||
* @return {Object} The modified object.
|
* @return {Object} The modified object.
|
||||||
*/
|
*/
|
||||||
function replaceNodeObjects(o) {
|
function replaceNodeObjects(o) {
|
||||||
const OBJECT_PLACEHOLDER = '<Object>';
|
const OBJECT_PLACEHOLDER = '<Object>';
|
||||||
|
|
||||||
if (o.code && o.code.node) {
|
if (o.code && o.code.node) {
|
||||||
// don't break the original object!
|
// don't break the original object!
|
||||||
o.code = _.cloneDeep(o.code);
|
o.code = _.cloneDeep(o.code);
|
||||||
o.code.node = OBJECT_PLACEHOLDER;
|
o.code.node = OBJECT_PLACEHOLDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (o.doclet && o.doclet.meta && o.doclet.meta.code && o.doclet.meta.code.node) {
|
if (o.doclet && o.doclet.meta && o.doclet.meta.code && o.doclet.meta.code.node) {
|
||||||
// don't break the original object!
|
// don't break the original object!
|
||||||
o.doclet.meta.code = _.cloneDeep(o.doclet.meta.code);
|
o.doclet.meta.code = _.cloneDeep(o.doclet.meta.code);
|
||||||
o.doclet.meta.code.node = OBJECT_PLACEHOLDER;
|
o.doclet.meta.code.node = OBJECT_PLACEHOLDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (o.astnode) {
|
if (o.astnode) {
|
||||||
o.astnode = OBJECT_PLACEHOLDER;
|
o.astnode = OBJECT_PLACEHOLDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,35 +61,43 @@ function replaceNodeObjects(o) {
|
|||||||
* @return {object} The fixed-up object.
|
* @return {object} The fixed-up object.
|
||||||
*/
|
*/
|
||||||
function cleanse(e) {
|
function cleanse(e) {
|
||||||
let result = {};
|
let result = {};
|
||||||
|
|
||||||
Object.keys(e).forEach(prop => {
|
Object.keys(e).forEach((prop) => {
|
||||||
// by default, don't stringify properties that contain an array of functions
|
// by default, don't stringify properties that contain an array of functions
|
||||||
if (!conf.includeFunctions && Array.isArray(e[prop]) && e[prop][0] &&
|
if (
|
||||||
String(typeof e[prop][0]) === 'function') {
|
!conf.includeFunctions &&
|
||||||
result[prop] = `function[${e[prop].length}]`;
|
Array.isArray(e[prop]) &&
|
||||||
}
|
e[prop][0] &&
|
||||||
// never include functions that belong to the object
|
String(typeof e[prop][0]) === 'function'
|
||||||
else if (typeof e[prop] !== 'function') {
|
) {
|
||||||
result[prop] = e[prop];
|
result[prop] = `function[${e[prop].length}]`;
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// allow users to omit node objects, which can be enormous
|
|
||||||
if (conf.omitNodes) {
|
|
||||||
result = replaceNodeObjects(result);
|
|
||||||
}
|
}
|
||||||
|
// never include functions that belong to the object
|
||||||
|
else if (typeof e[prop] !== 'function') {
|
||||||
|
result[prop] = e[prop];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return result;
|
// allow users to omit node objects, which can be enormous
|
||||||
|
if (conf.omitNodes) {
|
||||||
|
result = replaceNodeObjects(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.handlers = {};
|
exports.handlers = {};
|
||||||
|
|
||||||
events.forEach(eventType => {
|
events.forEach((eventType) => {
|
||||||
exports.handlers[eventType] = e => {
|
exports.handlers[eventType] = (e) => {
|
||||||
console.log(JSON.stringify({
|
console.log(
|
||||||
type: eventType,
|
JSON.stringify({
|
||||||
content: cleanse(e)
|
type: eventType,
|
||||||
}), null, 4);
|
content: cleanse(e),
|
||||||
};
|
}),
|
||||||
|
null,
|
||||||
|
4
|
||||||
|
);
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@ -38,144 +38,143 @@
|
|||||||
let functionDoclets;
|
let functionDoclets;
|
||||||
|
|
||||||
function hasUniqueValues(obj) {
|
function hasUniqueValues(obj) {
|
||||||
let isUnique = true;
|
let isUnique = true;
|
||||||
const seen = [];
|
const seen = [];
|
||||||
|
|
||||||
Object.keys(obj).forEach(key => {
|
Object.keys(obj).forEach((key) => {
|
||||||
if (seen.includes(obj[key])) {
|
if (seen.includes(obj[key])) {
|
||||||
isUnique = false;
|
isUnique = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
seen.push(obj[key]);
|
seen.push(obj[key]);
|
||||||
});
|
});
|
||||||
|
|
||||||
return isUnique;
|
return isUnique;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getParamNames(params) {
|
function getParamNames(params) {
|
||||||
const names = [];
|
const names = [];
|
||||||
|
|
||||||
params.forEach(param => {
|
params.forEach((param) => {
|
||||||
let name = param.name || '';
|
let name = param.name || '';
|
||||||
|
|
||||||
if (param.variable) {
|
if (param.variable) {
|
||||||
name = `...${name}`;
|
name = `...${name}`;
|
||||||
}
|
}
|
||||||
if (name !== '') {
|
if (name !== '') {
|
||||||
names.push(name);
|
names.push(name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return names.length ? names.join(', ') : '';
|
return names.length ? names.join(', ') : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function getParamVariation({params}) {
|
function getParamVariation({ params }) {
|
||||||
return getParamNames(params || []);
|
return getParamNames(params || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUniqueVariations(doclets) {
|
function getUniqueVariations(doclets) {
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
const variations = {};
|
const variations = {};
|
||||||
const docletKeys = Object.keys(doclets);
|
const docletKeys = Object.keys(doclets);
|
||||||
|
|
||||||
function getUniqueNumbers() {
|
function getUniqueNumbers() {
|
||||||
docletKeys.forEach(doclet => {
|
docletKeys.forEach((doclet) => {
|
||||||
let newLongname;
|
let newLongname;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
counter++;
|
counter++;
|
||||||
variations[doclet] = String(counter);
|
variations[doclet] = String(counter);
|
||||||
|
|
||||||
// is this longname + variation unique?
|
// is this longname + variation unique?
|
||||||
newLongname = `${doclets[doclet].longname}(${variations[doclet]})`;
|
newLongname = `${doclets[doclet].longname}(${variations[doclet]})`;
|
||||||
if ( !functionDoclets[newLongname] ) {
|
if (!functionDoclets[newLongname]) {
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getUniqueNames() {
|
|
||||||
// start by trying to preserve existing variations
|
|
||||||
docletKeys.forEach(doclet => {
|
|
||||||
variations[doclet] = doclets[doclet].variation || getParamVariation(doclets[doclet]);
|
|
||||||
});
|
|
||||||
|
|
||||||
// if they're identical, try again, without preserving existing variations
|
|
||||||
if ( !hasUniqueValues(variations) ) {
|
|
||||||
docletKeys.forEach(doclet => {
|
|
||||||
variations[doclet] = getParamVariation(doclets[doclet]);
|
|
||||||
});
|
|
||||||
|
|
||||||
// if they're STILL identical, switch to numeric variations
|
|
||||||
if ( !hasUniqueValues(variations) ) {
|
|
||||||
getUniqueNumbers();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// are we already using numeric variations? if so, keep doing that
|
function getUniqueNames() {
|
||||||
if (functionDoclets[`${doclets.newDoclet.longname}(1)`]) {
|
// start by trying to preserve existing variations
|
||||||
|
docletKeys.forEach((doclet) => {
|
||||||
|
variations[doclet] = doclets[doclet].variation || getParamVariation(doclets[doclet]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// if they're identical, try again, without preserving existing variations
|
||||||
|
if (!hasUniqueValues(variations)) {
|
||||||
|
docletKeys.forEach((doclet) => {
|
||||||
|
variations[doclet] = getParamVariation(doclets[doclet]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// if they're STILL identical, switch to numeric variations
|
||||||
|
if (!hasUniqueValues(variations)) {
|
||||||
getUniqueNumbers();
|
getUniqueNumbers();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
getUniqueNames();
|
|
||||||
}
|
|
||||||
|
|
||||||
return variations;
|
// are we already using numeric variations? if so, keep doing that
|
||||||
|
if (functionDoclets[`${doclets.newDoclet.longname}(1)`]) {
|
||||||
|
getUniqueNumbers();
|
||||||
|
} else {
|
||||||
|
getUniqueNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
return variations;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureUniqueLongname(newDoclet) {
|
function ensureUniqueLongname(newDoclet) {
|
||||||
const doclets = {
|
const doclets = {
|
||||||
oldDoclet: functionDoclets[newDoclet.longname],
|
oldDoclet: functionDoclets[newDoclet.longname],
|
||||||
newDoclet: newDoclet
|
newDoclet: newDoclet,
|
||||||
};
|
};
|
||||||
const docletKeys = Object.keys(doclets);
|
const docletKeys = Object.keys(doclets);
|
||||||
let oldDocletLongname;
|
let oldDocletLongname;
|
||||||
let variations = {};
|
let variations = {};
|
||||||
|
|
||||||
if (doclets.oldDoclet) {
|
if (doclets.oldDoclet) {
|
||||||
oldDocletLongname = doclets.oldDoclet.longname;
|
oldDocletLongname = doclets.oldDoclet.longname;
|
||||||
// if the shared longname has a variation, like MyClass#myLongname(variation),
|
// if the shared longname has a variation, like MyClass#myLongname(variation),
|
||||||
// remove the variation
|
// remove the variation
|
||||||
if (doclets.oldDoclet.variation || doclets.oldDoclet.variation === '') {
|
if (doclets.oldDoclet.variation || doclets.oldDoclet.variation === '') {
|
||||||
docletKeys.forEach(doclet => {
|
docletKeys.forEach((doclet) => {
|
||||||
doclets[doclet].longname = doclets[doclet].longname.replace(/\([\s\S]*\)$/, '');
|
doclets[doclet].longname = doclets[doclet].longname.replace(/\([\s\S]*\)$/, '');
|
||||||
doclets[doclet].variation = null;
|
doclets[doclet].variation = null;
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
variations = getUniqueVariations(doclets);
|
|
||||||
|
|
||||||
// update the longnames/variations
|
|
||||||
docletKeys.forEach(doclet => {
|
|
||||||
doclets[doclet].longname += `(${variations[doclet]})`;
|
|
||||||
doclets[doclet].variation = variations[doclet];
|
|
||||||
});
|
|
||||||
|
|
||||||
// update the old doclet in the lookup table
|
|
||||||
functionDoclets[oldDocletLongname] = null;
|
|
||||||
functionDoclets[doclets.oldDoclet.longname] = doclets.oldDoclet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// always store the new doclet in the lookup table
|
variations = getUniqueVariations(doclets);
|
||||||
functionDoclets[doclets.newDoclet.longname] = doclets.newDoclet;
|
|
||||||
|
|
||||||
return doclets.newDoclet;
|
// update the longnames/variations
|
||||||
|
docletKeys.forEach((doclet) => {
|
||||||
|
doclets[doclet].longname += `(${variations[doclet]})`;
|
||||||
|
doclets[doclet].variation = variations[doclet];
|
||||||
|
});
|
||||||
|
|
||||||
|
// update the old doclet in the lookup table
|
||||||
|
functionDoclets[oldDocletLongname] = null;
|
||||||
|
functionDoclets[doclets.oldDoclet.longname] = doclets.oldDoclet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// always store the new doclet in the lookup table
|
||||||
|
functionDoclets[doclets.newDoclet.longname] = doclets.newDoclet;
|
||||||
|
|
||||||
|
return doclets.newDoclet;
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.handlers = {
|
exports.handlers = {
|
||||||
parseBegin() {
|
parseBegin() {
|
||||||
functionDoclets = {};
|
functionDoclets = {};
|
||||||
},
|
},
|
||||||
|
|
||||||
newDoclet(e) {
|
newDoclet(e) {
|
||||||
if (e.doclet.kind === 'function') {
|
if (e.doclet.kind === 'function') {
|
||||||
e.doclet = ensureUniqueLongname(e.doclet);
|
e.doclet = ensureUniqueLongname(e.doclet);
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
parseComplete() {
|
|
||||||
functionDoclets = null;
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
parseComplete() {
|
||||||
|
functionDoclets = null;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user