-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
150 lines (138 loc) · 4.07 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import { parse as babylonParse } from '@babel/parser';
import doctrine from 'doctrine';
import descriptionPlugin from './plugins/description';
import examplesPlugin from './plugins/examples';
import normalizerPlugin from './plugins/normalizer';
import paramsPlugin from './plugins/params';
import renderPlugin from './plugins/render';
import sourcePlugin from './plugins/source';
import statesPlugin from './plugins/states';
import tagsPlugin from './plugins/tags';
const plugins = {
description: descriptionPlugin,
examples: examplesPlugin,
normalizer: normalizerPlugin,
params: paramsPlugin,
render: renderPlugin,
source: sourcePlugin,
states: statesPlugin,
tags: tagsPlugin,
};
const DEFAULT_OPTIONS = {
ranges: true,
tokens: true,
sourceType: 'module',
plugins: [
'jsx',
'flow',
'importMeta',
'dynamicImport',
'classProperties',
'classPrivateMethods',
'classPrivateProperties',
'objectRestSpread',
],
};
/**
* Constructor that gives you methods.
*
* @name docks
* @returns {Object} instance of `Docks`
* @public
*/
export default function docks() {
const docksPlugins = [];
const app = {
/**
* A plugin is a function that may extend the core functionality,
* or if it returns another function it is called for each block comment.
*
* Look at [src/plugins/](/src/plugins) folder to see the built-in ones.
*
* @example
* import docks from 'docks';
*
* const app = docks();
*
* // extending the core
* app.use((self) => {
* self.foobar = 123
* });
*
* console.log(app.foobar); // => 123
*
* // Or plugin that will be called on each block comment
* app.use(() => (comment) => {
* comment.hoho = 'okey'
* });
*
* @name .use
* @param {Function} plugin with signature like `(docks) => (comment) => {}`
* @returns {Object} instance of `Docks`
* @public
*/
use(plugin) {
const res = plugin(app);
if (typeof res === 'function') {
docksPlugins.push(res);
}
Object.assign(app, res);
return app;
},
/**
* Parses given `input` using `@babel/parser` and passes
* all block comments to `doctrine` which is JSDoc parser.
* It also applies all the "Smart Plugins". Smart plugin is the function
* that is returned from each function passed to the `app.use` method.
*
* @example
* const app = docks();
*
* const smartPlugin = (comment) => {
* // do some stuff witht he Comment object.
* };
*
* app.use((self) => smartPlugin);
*
* const cmts = app.parse('some cool stuff with block comments');
* console.log(cmts);
*
* @name .parse
* @param {string} input file content which contains document block comments
* @returns {Array<Comment>} an array with `Comment` objects.
* @public
*/
parse(input) {
if (input && input.length === 0 && typeof input !== 'string') {
throw new TypeError('docks: expect an `input` string');
}
const codeAST = babylonParse(input, DEFAULT_OPTIONS);
const comments = getComments(codeAST);
const docs = comments.map((comment) =>
docksPlugins.reduce(
(acc, plugin) => Object.assign({}, acc, plugin(acc, input)),
comment,
),
);
return docs;
},
};
// default, built-in plugins
Object.keys(plugins).forEach((name) => {
app.use(plugins[name]);
});
return app;
}
function getComments(codeAST) {
const isApi = (tag) => tag.title === 'api' && tag.description === 'public';
const isPublic = (tag) => tag.title === 'public' || isApi(tag);
return codeAST.comments
.filter((comment) => comment.type === 'CommentBlock')
.map(({ value, start, end, loc }) => {
const pos = { start, end };
const { tags } = doctrine.parse(value, { unwrap: true, sloppy: true });
return { tags, pos, value, loc: { start: loc.start, end: loc.end } };
})
.filter((comment) => comment.tags.length)
.filter((comment) => comment.tags.filter(isPublic).length > 0);
}