diff --git a/__tests__/__snapshots__/bin.js.snap b/__tests__/__snapshots__/bin.js.snap index e89925da4..70fda8853 100644 --- a/__tests__/__snapshots__/bin.js.snap +++ b/__tests__/__snapshots__/bin.js.snap @@ -2024,6 +2024,277 @@ f5 comment exports[`lint command generates lint output 1`] = `""`; +exports[`load a plugin 1`] = ` +Array [ + Object { + "augments": Array [], + "context": Object { + "file": "[path]", + "loc": Object { + "end": Object { + "column": 2, + "line": 8, + }, + "start": Object { + "column": 0, + "line": 5, + }, + }, + }, + "description": Object { + "children": Array [ + Object { + "children": Array [ + Object { + "type": "text", + "value": "This function returns the number one.", + }, + ], + "type": "paragraph", + }, + ], + "type": "root", + }, + "examples": Array [], + "implements": Array [], + "kind": "function", + "loc": Object { + "end": Object { + "column": 3, + "line": 4, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "members": Object { + "events": Array [], + "global": Array [], + "inner": Array [], + "instance": Array [], + "static": Array [], + }, + "name": "simple.input", + "namespace": "simple.input", + "params": Array [], + "path": Array [ + Object { + "kind": "function", + "name": "simple.input", + }, + ], + "properties": Array [], + "returns": Array [ + Object { + "description": Object { + "children": Array [ + Object { + "children": Array [ + Object { + "type": "text", + "value": "numberone", + }, + ], + "type": "paragraph", + }, + ], + "type": "root", + }, + "title": "returns", + "type": Object { + "name": "number", + "type": "NameExpression", + }, + }, + ], + "sees": Array [], + "tags": Array [ + Object { + "description": "numberone", + "lineNumber": 2, + "title": "returns", + "type": Object { + "name": "number", + "type": "NameExpression", + }, + }, + ], + "throws": Array [], + "todos": Array [], + "yields": Array [], + }, + Object { + "augments": Array [], + "context": Object { + "file": "[path]", + "loc": Object { + "end": Object { + "column": 4, + "line": 5, + }, + "start": Object { + "column": 1, + "line": 5, + }, + }, + }, + "description": "", + "examples": Array [], + "implements": Array [], + "kind": "function", + "members": Object { + "events": Array [], + "global": Array [], + "inner": Array [], + "instance": Array [], + "static": Array [], + }, + "name": "dummy", + "namespace": "dummy", + "params": Array [], + "path": Array [ + Object { + "kind": "function", + "name": "dummy", + }, + ], + "properties": Array [], + "returns": Array [], + "sees": Array [], + "tags": Array [ + Object { + "description": null, + "lineNumber": 1, + "name": "dummy", + "title": "method", + }, + ], + "throws": Array [], + "todos": Array [], + "yields": Array [], + }, + Object { + "augments": Array [], + "context": Object { + "file": "[path]", + "kind": "method", + "loc": Object { + "end": Object { + "column": 4, + "line": 5, + }, + "start": Object { + "column": 1, + "line": 5, + }, + }, + "name": "dummy_method", + }, + "description": "", + "examples": Array [], + "implements": Array [], + "kind": "method", + "members": Object { + "events": Array [], + "global": Array [], + "inner": Array [], + "instance": Array [], + "static": Array [], + }, + "name": "dummy_method", + "namespace": "dummy_method", + "params": Array [ + Object { + "lineNumber": 1, + "name": "dummy_param", + "title": "param", + "type": Object { + "name": "number", + "type": "NameExpression", + }, + }, + ], + "path": Array [ + Object { + "kind": "method", + "name": "dummy_method", + }, + ], + "properties": Array [], + "returns": Array [], + "sees": Array [], + "tags": Array [ + Object { + "description": null, + "lineNumber": 1, + "name": "dummy_param", + "title": "param", + "type": Object { + "name": "number", + "type": "NameExpression", + }, + }, + ], + "throws": Array [], + "todos": Array [], + "yields": Array [], + }, + Object { + "augments": Array [], + "context": Object { + "file": "[path]", + "kind": "SHOULD_NOT_APPEAR_IN_THE_RESULT", + "loc": Object { + "end": Object { + "column": 4, + "line": 5, + }, + "start": Object { + "column": 1, + "line": 5, + }, + }, + "name": "SHOULD_NOT_APPEAR_IN_THE_RESULT", + }, + "description": "", + "examples": Array [], + "implements": Array [], + "kind": "function", + "members": Object { + "events": Array [], + "global": Array [], + "inner": Array [], + "instance": Array [], + "static": Array [], + }, + "name": "not_so_dummy", + "namespace": "not_so_dummy", + "params": Array [], + "path": Array [ + Object { + "kind": "function", + "name": "not_so_dummy", + }, + ], + "properties": Array [], + "returns": Array [], + "sees": Array [], + "tags": Array [ + Object { + "description": null, + "lineNumber": 1, + "name": "not_so_dummy", + "title": "method", + }, + ], + "throws": Array [], + "todos": Array [], + "yields": Array [], + }, +] +`; + exports[`should use browser resolve 1`] = ` Array [ Object { diff --git a/__tests__/__snapshots__/test.js.snap b/__tests__/__snapshots__/test.js.snap index e8a0a2294..ff595d1ed 100644 --- a/__tests__/__snapshots__/test.js.snap +++ b/__tests__/__snapshots__/test.js.snap @@ -287,6 +287,325 @@ Array [ ] `; +exports[`Check that plugins are loaded and used 1`] = ` +Array [ + Object { + "augments": Array [], + "context": Object { + "loc": SourceLocation { + "end": Position { + "column": 1, + "line": 13, + }, + "filename": undefined, + "identifierName": undefined, + "start": Position { + "column": 0, + "line": 10, + }, + }, + }, + "description": Object { + "children": Array [ + Object { + "children": Array [ + Object { + "type": "text", + "value": "This function returns the number plus two.", + }, + ], + "type": "paragraph", + }, + ], + "type": "root", + }, + "errors": Array [], + "examples": Array [ + Object { + "description": "var result = returnTwo(4); +// result is 6", + }, + ], + "implements": Array [], + "kind": "function", + "loc": SourceLocation { + "end": Position { + "column": 3, + "line": 9, + }, + "filename": undefined, + "identifierName": undefined, + "start": Position { + "column": 0, + "line": 1, + }, + }, + "members": Object { + "events": Array [], + "global": Array [], + "inner": Array [], + "instance": Array [], + "static": Array [], + }, + "name": "returnTwo", + "namespace": "returnTwo", + "params": Array [ + Object { + "description": Object { + "children": Array [ + Object { + "children": Array [ + Object { + "type": "text", + "value": "the number", + }, + ], + "type": "paragraph", + }, + ], + "type": "root", + }, + "lineNumber": 3, + "name": "a", + "title": "param", + "type": Object { + "name": "Number", + "type": "NameExpression", + }, + }, + ], + "path": Array [ + Object { + "kind": "function", + "name": "returnTwo", + }, + ], + "properties": Array [], + "returns": Array [ + Object { + "description": Object { + "children": Array [ + Object { + "children": Array [ + Object { + "type": "text", + "value": "numbertwo", + }, + ], + "type": "paragraph", + }, + ], + "type": "root", + }, + "title": "returns", + "type": Object { + "name": "Number", + "type": "NameExpression", + }, + }, + ], + "sees": Array [], + "tags": Array [ + Object { + "description": "the number", + "lineNumber": 3, + "name": "a", + "title": "param", + "type": Object { + "name": "Number", + "type": "NameExpression", + }, + }, + Object { + "description": "numbertwo", + "lineNumber": 4, + "title": "returns", + "type": Object { + "name": "Number", + "type": "NameExpression", + }, + }, + Object { + "description": "var result = returnTwo(4); +// result is 6", + "lineNumber": 5, + "title": "example", + }, + ], + "throws": Array [], + "todos": Array [], + "yields": Array [], + }, + Object { + "augments": Array [], + "context": Object { + "loc": Object { + "end": Object { + "column": 4, + "line": 5, + }, + "start": Object { + "column": 1, + "line": 5, + }, + }, + }, + "description": "", + "errors": Array [], + "examples": Array [], + "implements": Array [], + "kind": "function", + "loc": undefined, + "members": Object { + "events": Array [], + "global": Array [], + "inner": Array [], + "instance": Array [], + "static": Array [], + }, + "name": "dummy", + "namespace": "dummy", + "params": Array [], + "path": Array [ + Object { + "kind": "function", + "name": "dummy", + }, + ], + "properties": Array [], + "returns": Array [], + "sees": Array [], + "tags": Array [ + Object { + "description": null, + "lineNumber": 1, + "name": "dummy", + "title": "method", + }, + ], + "throws": Array [], + "todos": Array [], + "yields": Array [], + }, + Object { + "augments": Array [], + "context": Object { + "loc": Object { + "end": Object { + "column": 4, + "line": 5, + }, + "start": Object { + "column": 1, + "line": 5, + }, + }, + }, + "description": "", + "errors": Array [], + "examples": Array [], + "implements": Array [], + "kind": "method", + "loc": undefined, + "members": Object { + "events": Array [], + "global": Array [], + "inner": Array [], + "instance": Array [], + "static": Array [], + }, + "name": "dummy_method", + "namespace": "dummy_method", + "params": Array [ + Object { + "lineNumber": 1, + "name": "dummy_param", + "title": "param", + "type": Object { + "name": "number", + "type": "NameExpression", + }, + }, + ], + "path": Array [ + Object { + "kind": "method", + "name": "dummy_method", + }, + ], + "properties": Array [], + "returns": Array [], + "sees": Array [], + "tags": Array [ + Object { + "description": null, + "lineNumber": 1, + "name": "dummy_param", + "title": "param", + "type": Object { + "name": "number", + "type": "NameExpression", + }, + }, + ], + "throws": Array [], + "todos": Array [], + "yields": Array [], + }, + Object { + "augments": Array [], + "context": Object { + "loc": Object { + "end": Object { + "column": 4, + "line": 5, + }, + "start": Object { + "column": 1, + "line": 5, + }, + }, + }, + "description": "", + "errors": Array [], + "examples": Array [], + "implements": Array [], + "kind": "function", + "loc": undefined, + "members": Object { + "events": Array [], + "global": Array [], + "inner": Array [], + "instance": Array [], + "static": Array [], + }, + "name": "not_so_dummy", + "namespace": "not_so_dummy", + "params": Array [], + "path": Array [ + Object { + "kind": "function", + "name": "not_so_dummy", + }, + ], + "properties": Array [], + "returns": Array [], + "sees": Array [], + "tags": Array [ + Object { + "description": null, + "lineNumber": 1, + "name": "not_so_dummy", + "title": "method", + }, + ], + "throws": Array [], + "todos": Array [], + "yields": Array [], + }, +] +`; + exports[`Use Source attribute only 1`] = ` Array [ Object { diff --git a/__tests__/bin.js b/__tests__/bin.js index 7f46bca1e..e668ad26f 100644 --- a/__tests__/bin.js +++ b/__tests__/bin.js @@ -3,7 +3,6 @@ import path from 'path'; import os from 'os'; import { exec } from 'child_process'; -import tmp from 'tmp'; import fs from 'fs-extra'; import { fileURLToPath } from 'url'; @@ -60,6 +59,13 @@ test.skip('defaults to parsing package.json main', async function () { expect(data.length).toBeTruthy(); }); +test('load a plugin', async function () { + const data = await documentation([ + 'build fixture/simple.input.js fixture/plugin.txt --plugin=../src/mock_plugin.js' + ]); + expect(normalize(data)).toMatchSnapshot(); +}); + test('accepts config file', async function () { const data = await documentation([ 'build fixture/sorting/input.js -c fixture/config.json' diff --git a/__tests__/fixture/plugin.txt b/__tests__/fixture/plugin.txt new file mode 100644 index 000000000..2244c29af --- /dev/null +++ b/__tests__/fixture/plugin.txt @@ -0,0 +1,6 @@ +/** + * @method test + */ + +test + diff --git a/__tests__/lib/__snapshots__/sort.js.snap b/__tests__/lib/__snapshots__/sort.js.snap index dee9fc0b0..6a9936c66 100644 --- a/__tests__/lib/__snapshots__/sort.js.snap +++ b/__tests__/lib/__snapshots__/sort.js.snap @@ -1,5 +1,33 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`sort by custom order 1`] = ` +Array [ + Object { + "context": Object { + "sortKey": "b", + }, + "memberof": "classB", + "name": "carrot", + }, + Object { + "context": Object { + "sortKey": "c", + }, + "kind": "typedef", + "memberof": "classA", + "name": "bananas", + }, + Object { + "context": Object { + "sortKey": "a", + }, + "kind": "method", + "memberof": "classB", + "name": "apples", + }, +] +`; + exports[`sort toc with files 1`] = ` Array [ Object { @@ -139,7 +167,7 @@ Array [ "sortKey": "c", }, "kind": "function", - "memberof": "classB", + "memberof": "classA", "name": "bananas", }, Object { @@ -154,6 +182,13 @@ Array [ exports[`sort toc with files absolute path 3`] = ` Array [ + Object { + "context": Object { + "sortKey": "b", + }, + "memberof": "classB", + "name": "carrot", + }, Object { "context": Object { "sortKey": "a", @@ -167,7 +202,20 @@ Array [ "sortKey": "c", }, "kind": "function", - "memberof": "classB", + "memberof": "classA", + "name": "bananas", + }, +] +`; + +exports[`sort toc with files absolute path 4`] = ` +Array [ + Object { + "context": Object { + "sortKey": "c", + }, + "kind": "function", + "memberof": "classA", "name": "bananas", }, Object { @@ -177,5 +225,13 @@ Array [ "memberof": "classB", "name": "carrot", }, + Object { + "context": Object { + "sortKey": "a", + }, + "kind": "function", + "memberof": "classB", + "name": "apples", + }, ] `; diff --git a/__tests__/lib/sort.js b/__tests__/lib/sort.js index 76ce57f3c..4e0063b72 100644 --- a/__tests__/lib/sort.js +++ b/__tests__/lib/sort.js @@ -141,7 +141,7 @@ test('sort toc with files absolute path', function () { context: { sortKey: 'c' }, name: 'bananas', kind: 'function', - memberof: 'classB' + memberof: 'classA' }; const snowflake = { @@ -159,4 +159,36 @@ test('sort toc with files absolute path', function () { sortOrder: ['kind', 'alpha'] }) ).toMatchSnapshot(); + + expect( + sort([carrot, apples, bananas], { + sortOrder: ['memberof', 'kind', 'alpha'] + }) + ).toMatchSnapshot(); +}); + +test('sort by custom order', function () { + const apples = { + context: { sortKey: 'a' }, + name: 'apples', + kind: 'method', + memberof: 'classB' + }; + const carrot = { + context: { sortKey: 'b' }, + name: 'carrot', + memberof: 'classB' + }; + const bananas = { + context: { sortKey: 'c' }, + name: 'bananas', + kind: 'typedef', + memberof: 'classA' + }; + + expect( + sort([carrot, apples, bananas], { + sortOrder: [{ kind: ['typedef', 'method'] }, 'alpha'] + }) + ).toMatchSnapshot(); }); diff --git a/__tests__/test.js b/__tests__/test.js index aa9037c0c..76538e1f2 100644 --- a/__tests__/test.js +++ b/__tests__/test.js @@ -13,6 +13,7 @@ import _ from 'lodash'; import chdir from 'chdir'; import config from '../src/config'; import { fileURLToPath } from 'url'; +import { jest } from '@jest/globals'; const UPDATE = !!process.env.UPDATE; const __filename = fileURLToPath(import.meta.url); @@ -71,6 +72,41 @@ test('Check that external modules could parse as input', async function () { expect(result).toMatchSnapshot(); }); +test('Check that plugins are loaded and used', async function () { + const initCb = jest.fn(); + const parseCb = jest.fn(); + const mockPlugin = await import('../src/mock_plugin.js'); + mockPlugin.mockInit(initCb, parseCb); + + const dir = path.join(__dirname, 'fixture'); + const result = await documentation.build( + [path.join(dir, 'simple-two.input.js'), path.join(dir, 'plugin.txt')], + { plugin: ['./mock_plugin.js'], order: 'test' } + ); + normalize(result); + expect(result).toMatchSnapshot(); + + // name from JSDoc tag + expect(result[1].name).toBe('dummy'); + + // name parsed by the plugin + expect(result[2].name).toBe('dummy_method'); + + // name from plugin parsing overridden by JSDoc tag + expect(result[3].name).toBe('not_so_dummy'); + + expect(initCb.mock.calls.length).toBe(1); + expect(initCb.mock.calls[0][0].order).toBe('test'); + + expect(parseCb.mock.calls.length).toBe(2); + expect( + parseCb.mock.calls[0][0].file.includes('fixture/simple-two.input.js') + ).toBeTruthy(); + expect( + parseCb.mock.calls[1][0].file.includes('fixture/plugin.txt') + ).toBeTruthy(); +}); + test('bad input', function () { glob .sync(path.join(__dirname, 'fixture/bad', '*.input.js')) diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 6dab84d58..1c6da18f2 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -72,3 +72,31 @@ toc: - shortestPath - salesman ``` + +## Sorting + +Sorting options can be specified in the configuration file. Example: + +```yml +sortOrder: + - memberof + - alpha +``` + +Additionally, a custom sort order can be given, which is not possible when using the CLI option. Example: + +```yml +sortOrder: + - kind: + - namespace + - class + - interface + - typedef + - enum + - constant + - function + - property + - member + - memberof + - alpha +``` diff --git a/docs/POLYGLOT.md b/docs/POLYGLOT.md deleted file mode 100644 index f0005ae0b..000000000 --- a/docs/POLYGLOT.md +++ /dev/null @@ -1,2 +0,0 @@ -🚨 Polyglot mode is now deprecated. It will be replaced by a pluggable -input system in future versions. 🚨 diff --git a/docs/USAGE.md b/docs/USAGE.md index 5fdc6bf03..f801e2568 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -58,7 +58,7 @@ Options: [boolean] [default: false] --sort-order The order to sort the documentation, may be specified multiple times - [choices: "source", "alpha", "kind"] + [choices: "source", "alpha", "kind", "memberof"] [default: "source"] --output, -o output location. omit for stdout, otherwise is a filename for single-file outputs and a directory diff --git a/src/commands/shared_options.js b/src/commands/shared_options.js index 0379debb8..c826ae519 100644 --- a/src/commands/shared_options.js +++ b/src/commands/shared_options.js @@ -45,6 +45,10 @@ export const sharedInputOptions = { type: 'array', alias: 'pe' }, + plugin: { + type: 'array', + describe: 'load a plugin' + }, access: { describe: 'Include only comments with a given access level, out of private, ' + @@ -75,7 +79,7 @@ export const sharedInputOptions = { 'sort-order': { describe: 'The order to sort the documentation', array: true, - choices: ['source', 'alpha', 'kind', 'access'], + choices: ['source', 'alpha', 'kind', 'access', 'memberof'], default: ['source'] }, resolve: { diff --git a/src/config.js b/src/config.js index 17d49d3d4..260c67e03 100644 --- a/src/config.js +++ b/src/config.js @@ -1,11 +1,11 @@ const defaultConfig = { - // package.json ignored and don't get project infromation + // package.json ignored and don't get project information 'no-package': false, - // Extenstions which by dafault are parse + // Extensions which by default are parsed parseExtension: ['.mjs', '.js', '.jsx', '.es5', '.es6', '.vue', '.ts', '.tsx'] }; -function normalaze(config, global) { +function normalize(config, global) { if (config.parseExtension) { config.parseExtension = Array.from( new Set([...config.parseExtension, ...global.parseExtension]) @@ -24,6 +24,6 @@ export default { this.globalConfig.parseExtension = [...defaultConfig.parseExtension]; }, add(parameters) { - Object.assign(this.globalConfig, normalaze(parameters, this.globalConfig)); + Object.assign(this.globalConfig, normalize(parameters, this.globalConfig)); } }; diff --git a/src/index.js b/src/index.js index abe624c7c..dd0b21394 100644 --- a/src/index.js +++ b/src/index.js @@ -26,6 +26,7 @@ import md from './output/markdown.js'; import json from './output/json.js'; import createFormatters from './output/util/formatters.js'; import LinkerStack from './output/util/linker_stack.js'; +import pluginAPI from './plugin_api.js'; /** * Build a pipeline of comment handlers. @@ -104,6 +105,14 @@ function buildInternal(inputsAndConfig) { ]); const extractedComments = _.flatMap(inputs, function (sourceFile) { + if (config.plugin) { + for (const plugin of config.plugin) { + if (config._module[plugin].parse) { + const r = config._module[plugin].parse(sourceFile, config, pluginAPI); + if (r) return r.map(buildPipeline); + } + } + } return parseJavaScript(sourceFile, config).map(buildPipeline); }).filter(Boolean); @@ -132,6 +141,14 @@ function lintInternal(inputsAndConfig) { ]); const extractedComments = _.flatMap(inputs, sourceFile => { + if (config.plugin) { + for (const plugin of config.plugin) { + if (config._module[plugin].parse) { + const r = config._module[plugin].parse(sourceFile, config, pluginAPI); + if (r) return r.map(lintPipeline); + } + } + } return parseJavaScript(sourceFile, config).map(lintPipeline); }).filter(Boolean); @@ -180,6 +197,7 @@ export const lint = (indexes, args) => * @param {Array} args.external a string regex / glob match pattern * that defines what external modules will be whitelisted and included in the * generated documentation. + * @param {Array} [args.plugin=[]] load plugins * @param {boolean} [args.shallow=false] whether to avoid dependency parsing * even in JavaScript code. * @param {Array} [args.order=[]] optional array that diff --git a/src/merge_config.js b/src/merge_config.js index cea2591c6..6ad688d8c 100644 --- a/src/merge_config.js +++ b/src/merge_config.js @@ -79,6 +79,36 @@ export default async function mergeConfig(config = {}) { conf.add(config); conf.add(await readConfigFile(conf.globalConfig.config)); conf.add(await readPackage(conf.globalConfig['no-package'])); + if (conf.globalConfig.plugin) { + await loadPlugins(conf.globalConfig); + } return conf.globalConfig; } + +/** + * Load the external plugins + * + * @param {Object} configuration plugins section of the configuration + * @returns {void} + */ +async function loadPlugins(config) { + if (!config._module) + Object.defineProperty(config, '_module', { + enumerable: false, + writable: false, + configurable: false, + value: {} + }); + for (const plugin of config.plugin) { + try { + config._module[plugin] = await import(plugin); + if (config._module[plugin].init) { + await config._module[plugin].init(config); + } + } catch (e) { + console.error(`Failed loading ${plugin}`); + throw e; + } + } +} diff --git a/src/mock_plugin.js b/src/mock_plugin.js new file mode 100644 index 000000000..2e4d25018 --- /dev/null +++ b/src/mock_plugin.js @@ -0,0 +1,50 @@ +let initCb, parseCb, dummy; + +export function mockInit(init, parse) { + initCb = init; + parseCb = parse; +} + +export async function init() { + if (initCb) initCb(...arguments); + dummy = [ + { + value: '*\n * @method dummy\n ', + context: { + file: 'plugin.txt', + loc: { start: { line: 5, column: 1 }, end: { line: 5, column: 4 } }, + sortKey: 'a' + }, + loc: { start: { line: 0, column: 1 }, end: { line: 2, column: 1 } } + }, + { + value: '*\n * @param {number} dummy_param\n ', + context: { + file: 'plugin.txt', + loc: { start: { line: 5, column: 1 }, end: { line: 5, column: 4 } }, + sortKey: 'b', + kind: 'method', + name: 'dummy_method' + }, + loc: { start: { line: 0, column: 1 }, end: { line: 2, column: 1 } } + }, + { + value: '*\n * @method not_so_dummy\n ', + context: { + file: 'plugin.txt', + loc: { start: { line: 5, column: 1 }, end: { line: 5, column: 4 } }, + sortKey: 'c', + kind: 'SHOULD_NOT_APPEAR_IN_THE_RESULT', + name: 'SHOULD_NOT_APPEAR_IN_THE_RESULT' + }, + loc: { start: { line: 0, column: 1 }, end: { line: 2, column: 1 } } + } + ]; +} + +export function parse(file, _config, api) { + if (parseCb) parseCb(...arguments); + if (file.file.includes('plugin.txt')) + return dummy.map(c => api.parseJSDoc(c.value, c.log, c.context)); + return false; +} diff --git a/src/parse.js b/src/parse.js index a10c7b2b8..d756440a5 100644 --- a/src/parse.js +++ b/src/parse.js @@ -636,6 +636,22 @@ export default function parseJSDoc(comment, loc, context) { result.todos = []; result.yields = []; + if (context) { + for (const tag of [ + 'kind', + 'name', + 'returns', + 'params', + 'properties', + 'errors', + 'augments', + 'throws', + 'yields', + 'implements' + ]) + if (context[tag]) result[tag] = context[tag]; + } + if (result.description) { result.description = parseMarkdown(result.description); } @@ -669,7 +685,7 @@ export default function parseJSDoc(comment, loc, context) { // Using the @name tag, or any other tag that sets the name of a comment, // disconnects the comment from its surrounding code. if (context && result.name) { - delete context.ast; + if (context.ast) delete context.ast; } return result; diff --git a/src/plugin_api.js b/src/plugin_api.js new file mode 100644 index 000000000..6cfdda6d7 --- /dev/null +++ b/src/plugin_api.js @@ -0,0 +1,8 @@ +import parseJSDoc from './parse.js'; +import isJSDocComment from './is_jsdoc_comment.js'; +const pluginAPI = { + parseJSDoc, + isJSDocComment +}; + +export default pluginAPI; diff --git a/src/sort.js b/src/sort.js index a5bde334f..76551823e 100644 --- a/src/sort.js +++ b/src/sort.js @@ -103,13 +103,20 @@ export default function (comments, options) { return fixed.concat(unfixed); } -function compareCommentsByField(field, a, b) { +function compareCommentsByField(field, a, b, customOrder) { const akey = a[field]; const bkey = b[field]; if (akey && bkey) { + if (customOrder) { + const aIdx = customOrder.findIndex(o => o == akey); + const bIdx = customOrder.findIndex(o => o == bkey); + return aIdx - bIdx; + } return akey.localeCompare(bkey, undefined, { caseFirst: 'upper' }); } + if (akey) return 1; + if (bkey) return -1; return 0; } @@ -121,13 +128,19 @@ const sortFns = { alpha: compareCommentsByField.bind(null, 'name'), source: compareCommentsBySourceLocation, kind: compareCommentsByField.bind(null, 'kind'), - access: compareCommentsByField.bind(null, 'access') + access: compareCommentsByField.bind(null, 'access'), + memberof: compareCommentsByField.bind(null, 'memberof') }; function sortComments(comments, sortOrder) { return comments.sort((a, b) => { for (const sortMethod of sortOrder || ['source']) { - const r = sortFns[sortMethod](a, b); + const sortMethodName = + typeof sortMethod === 'object' + ? Object.keys(sortMethod)[0] + : sortMethod; + const customOrder = sortMethod[sortMethodName]; + const r = sortFns[sortMethodName](a, b, customOrder); if (r !== 0) return r; } return 0; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy