UNPKG

7.51 kBJavaScriptView Raw
1'use strict'
2const doctrine = require("doctrine");
3const util = require('util')
4
5module.exports = {
6 create (context) {
7
8
9 // rule implementation ...
10 const source = context.getSourceCode();
11
12 // console.log(source)
13 /**
14 * Report the error message
15 * @param {ASTNode} node node to report
16 * @returns {void}
17 */
18 function report(node) {
19 context.report({node, message: "Missing JSDoc comment."});
20 }
21
22 function checkJSDoc(nodes) {
23 nodes.forEach(jsdocNode => {
24 // console.log(jsdocNode.value)
25 const leadingComments = jsdocNode.leadingComments
26 if (!leadingComments) {
27 context.report({node: jsdocNode, message: "Missing JSDoc."});
28 return
29 }
30 const l = leadingComments.length
31 const comment = leadingComments[l - 1].value
32 const params = Object.create(null)
33
34 let jsdoc, hasReturn
35 try {
36 jsdoc = doctrine.parse(comment, {
37 strict: true,
38 unwrap: true,
39 sloppy: true
40 });
41 } catch (ex) {
42
43 if (/braces/i.test(ex.message)) {
44 context.report({node: jsdocNode, message: "JSDoc type missing brace."});
45 } else {
46 context.report({node: jsdocNode, message: "JSDoc syntax error."});
47 }
48
49 return
50 }
51
52 // check for functions missing description
53 if (!jsdoc.description) {
54 context.report({
55 node: jsdocNode,
56 message: "Missing JSDoc description for function."
57 });
58 }
59
60 jsdoc.tags.forEach(tag => {
61 switch (tag.title.toLowerCase()) {
62 case 'param':
63 if (!tag.type) {
64 context.report({
65 node: jsdocNode,
66 message: "Missing JSDoc parameter type for '{{name}}'.",
67 data: {name: tag.name}
68 });
69 }
70
71 if (!tag.description) {
72 context.report({
73 node: jsdocNode,
74 message: "Missing JSDoc parameter description for '{{name}}'.",
75 data: {name: tag.name}
76 });
77 }
78
79 if (params[tag.name]) {
80 context.report({
81 node: jsdocNode,
82 message: "Duplicate JSDoc parameter '{{name}}'.",
83 data: {name: tag.name}
84 });
85 } else if (tag.name.indexOf(".") === -1) {
86 params[tag.name] = 1;
87 }
88 break;
89 // case 'return':
90 // case 'returns': // 如果有@returns,检测@returns 是否正确
91 // hasReturn = true
92 // if(!tag.type){
93 // context.report({ node: jsdocNode, message: "Missing JSDoc return type." });
94 // }
95 // if (!tag.description) {
96 // context.report({ node: jsdocNode, message: "Missing JSDoc return description." });
97 // }
98 // break;
99 }
100 })
101
102
103 // check for functions missing @returns
104 /*if(!hasReturn){
105 context.report({
106 node: jsdocNode,
107 message: "Missing JSDoc @{{returns}} for function."
108 })
109 }*/
110
111
112 // check the parameters
113 const jsdocParams = Object.keys(params);
114
115 if (jsdocNode.value && jsdocNode.value.params) {
116 jsdocNode.value.params.forEach((param, i) => {
117 if (param.type === "AssignmentPattern") {
118 param = param.left;
119 }
120
121 const name = param.name;
122
123 // TODO(nzakas): Figure out logical things to do with destructured, default, rest params
124 if (param.type === "Identifier") {
125 if (jsdocParams[i] && (name !== jsdocParams[i])) {
126 context.report({
127 node: jsdocNode,
128 message: "Expected JSDoc for '{{name}}' but found '{{jsdocName}}'.",
129 data: {
130 name,
131 jsdocName: jsdocParams[i]
132 }
133 });
134 } else if (!params[name]) {
135 context.report({
136 node: jsdocNode, message: "Missing JSDoc for parameter '{{name}}'.", data: {
137 name
138 }
139 });
140 }
141 }
142 });
143 }
144 })
145 }
146
147 const default_opt = ['created', 'data']
148 // console.log(context.options[0]['obj-doc'])
149 let opt = context.options[0] && context.options[0]['ignoreMethods'] || []
150
151 opt = default_opt.concat(opt)
152
153 const blacklist = new Set();
154
155 opt.forEach(item => blacklist.add(item))
156
157 /**
158 * 获取需要检测的method节点
159 * @param node
160 * @returns {*}
161 */
162 function getJSDocNode(node) {
163
164 let methodNodes = []
165 if (util.isArray(node.properties)) {
166 node.properties.forEach(item => {
167 if (item.key && blacklist.has(item.key.name)) return;
168 if (item.method) methodNodes.push(item)
169 })
170 }
171
172 return methodNodes
173 }
174
175
176 return {
177 ReturnStatement: function (node) {
178 // at a ReturnStatement node while going down
179 // console.log(node)
180 },
181 // at a function expression node while going up:
182 // "FunctionExpression:exit": checkLastSegment,
183 // "ArrowFunctionExpression:exit": checkLastSegment,
184 ObjectExpression: function (node) {
185 let methodNodes = getJSDocNode(node)
186 if (methodNodes) {
187 checkJSDoc(methodNodes)
188 }
189 },
190 onCodePathStart: function (codePath, node) {
191 // console.log(node)
192
193 // at the start of analyzing a code path
194 },
195 onCodePathEnd: function (codePath, node) {
196 // at the end of analyzing a code path
197 }
198 }
199 }
200}
\No newline at end of file