4 * Copyright 2013 Palantir Technologies, Inc.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 Object.defineProperty(exports, "__esModule", { value: true });
19 var tslib_1 = require("tslib");
20 var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
21 var tsutils = require("tsutils");
22 var ts = require("typescript");
23 var Lint = require("../index");
24 var exclusions_1 = require("./completed-docs/exclusions");
26 exports.ARGUMENT_CLASSES = "classes";
27 exports.ARGUMENT_ENUMS = "enums";
28 exports.ARGUMENT_ENUM_MEMBERS = "enum-members";
29 exports.ARGUMENT_FUNCTIONS = "functions";
30 exports.ARGUMENT_INTERFACES = "interfaces";
31 exports.ARGUMENT_METHODS = "methods";
32 exports.ARGUMENT_NAMESPACES = "namespaces";
33 exports.ARGUMENT_PROPERTIES = "properties";
34 exports.ARGUMENT_TYPES = "types";
35 exports.ARGUMENT_VARIABLES = "variables";
36 exports.DESCRIPTOR_TAGS = "tags";
37 exports.DESCRIPTOR_LOCATIONS = "locations";
38 exports.DESCRIPTOR_OVERLOADS = "overloads";
39 exports.DESCRIPTOR_PRIVACIES = "privacies";
40 exports.DESCRIPTOR_VISIBILITIES = "visibilities";
41 exports.LOCATION_INSTANCE = "instance";
42 exports.LOCATION_STATIC = "static";
43 exports.PRIVACY_PRIVATE = "private";
44 exports.PRIVACY_PROTECTED = "protected";
45 exports.PRIVACY_PUBLIC = "public";
46 exports.TAGS_FOR_CONTENT = "content";
47 exports.TAGS_FOR_EXISTENCE = "existence";
48 exports.VISIBILITY_EXPORTED = "exported";
49 exports.VISIBILITY_INTERNAL = "internal";
50 var Rule = /** @class */ (function (_super) {
51 tslib_1.__extends(Rule, _super);
53 return _super !== null && _super.apply(this, arguments) || this;
55 /* tslint:enable:object-literal-sort-keys */
56 Rule.prototype.apply = function (sourceFile) {
57 var options = this.getOptions();
58 var exclusionsMap = this.getExclusionsMap(options.ruleArguments);
59 return this.applyWithFunction(sourceFile, walk, exclusionsMap);
61 Rule.prototype.getExclusionsMap = function (ruleArguments) {
62 if (ruleArguments.length === 0) {
63 ruleArguments = [Rule.defaultArguments];
65 return exclusions_1.constructExclusionsMap(ruleArguments);
67 Rule.FAILURE_STRING_EXIST = "Documentation must exist for ";
68 Rule.defaultArguments = (_a = {},
69 _a[exports.ARGUMENT_CLASSES] = true,
70 _a[exports.ARGUMENT_FUNCTIONS] = true,
71 _a[exports.ARGUMENT_METHODS] = (_b = {},
72 _b[exports.DESCRIPTOR_TAGS] = (_c = {},
73 _c[exports.TAGS_FOR_CONTENT] = {
76 _c[exports.TAGS_FOR_EXISTENCE] = ["deprecated", "inheritdoc"],
79 _a[exports.ARGUMENT_PROPERTIES] = (_d = {},
80 _d[exports.DESCRIPTOR_TAGS] = (_e = {},
81 _e[exports.TAGS_FOR_CONTENT] = {
84 _e[exports.TAGS_FOR_EXISTENCE] = ["deprecated", "inheritdoc"],
88 Rule.ARGUMENT_DESCRIPTOR_BLOCK = {
90 _f[exports.DESCRIPTOR_TAGS] = {
92 _g[exports.TAGS_FOR_CONTENT] = {
98 _g[exports.TAGS_FOR_EXISTENCE] = {
106 _f[exports.DESCRIPTOR_VISIBILITIES] = {
107 enum: [exports.ALL, exports.VISIBILITY_EXPORTED, exports.VISIBILITY_INTERNAL],
113 Rule.ARGUMENT_DESCRIPTOR_CLASS = {
114 properties: (_h = {},
115 _h[exports.DESCRIPTOR_TAGS] = {
116 properties: (_j = {},
117 _j[exports.TAGS_FOR_CONTENT] = {
123 _j[exports.TAGS_FOR_EXISTENCE] = {
131 _h[exports.DESCRIPTOR_LOCATIONS] = {
132 enum: [exports.ALL, exports.LOCATION_INSTANCE, exports.LOCATION_STATIC],
135 _h[exports.DESCRIPTOR_PRIVACIES] = {
136 enum: [exports.ALL, exports.PRIVACY_PRIVATE, exports.PRIVACY_PROTECTED, exports.PRIVACY_PUBLIC],
142 Rule.ARGUMENT_DESCRIPTOR_FUNCTION = {
143 properties: tslib_1.__assign({}, Rule.ARGUMENT_DESCRIPTOR_BLOCK.properties, (_k = {}, _k[exports.DESCRIPTOR_OVERLOADS] = {
148 Rule.ARGUMENT_DESCRIPTOR_METHOD = {
149 properties: tslib_1.__assign({}, Rule.ARGUMENT_DESCRIPTOR_CLASS.properties, (_l = {}, _l[exports.DESCRIPTOR_OVERLOADS] = {
154 /* tslint:disable:object-literal-sort-keys */
156 ruleName: "completed-docs",
157 description: "Enforces JSDoc comments for important items be filled out.",
158 optionsDescription: Lint.Utils.dedent(templateObject_1 || (templateObject_1 = tslib_1.__makeTemplateObject(["\n `true` to enable for `[", "]`,\n or an array with each item in one of two formats:\n\n * `string` to enable for that type\n * `object` keying types to when their documentation is required:\n * `\"", "\"` and `\"", "\"` may specify:\n * `\"", "\"`:\n * `\"", "\"`\n * `\"", "\"`\n * `\"", "\"`\n * `\"", "\"`\n * `\"", "\"`:\n * `\"", "\"`\n * `\"", "\"`\n * `\"", "\"`\n * Other types may specify `\"", "\"`:\n * `\"", "\"`\n * `\"", "\"`\n * `\"", "\"`\n * `\"", "\"` `\"", "\"` may also specify `\"", "\"`\n to indicate that each overload should have its own documentation, which is `false` by default.\n * All types may also provide `\"", "\"`\n with members specifying tags that allow the docs to not have a body.\n * `\"", "\"`: Object mapping tags to `RegExp` bodies content allowed to count as complete docs.\n * `\"", "\"`: Array of tags that must only exist to count as complete docs.\n\n Types that may be enabled are:\n\n * `\"", "\"`\n * `\"", "\"`\n * `\"", "\"`\n * `\"", "\"`\n * `\"", "\"`\n * `\"", "\"`\n * `\"", "\"`\n * `\"", "\"`\n * `\"", "\"`\n * `\"", "\"`"], ["\n \\`true\\` to enable for \\`[", "]\\`,\n or an array with each item in one of two formats:\n\n * \\`string\\` to enable for that type\n * \\`object\\` keying types to when their documentation is required:\n * \\`\"", "\"\\` and \\`\"", "\"\\` may specify:\n * \\`\"", "\"\\`:\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`:\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`\n * Other types may specify \\`\"", "\"\\`:\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`\n * \\`\"", "\"\\` \\`\"", "\"\\` may also specify \\`\"", "\"\\`\n to indicate that each overload should have its own documentation, which is \\`false\\` by default.\n * All types may also provide \\`\"", "\"\\`\n with members specifying tags that allow the docs to not have a body.\n * \\`\"", "\"\\`: Object mapping tags to \\`RegExp\\` bodies content allowed to count as complete docs.\n * \\`\"", "\"\\`: Array of tags that must only exist to count as complete docs.\n\n Types that may be enabled are:\n\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`\n * \\`\"", "\"\\`"])), Object.keys(Rule.defaultArguments).join(", "), exports.ARGUMENT_METHODS, exports.ARGUMENT_PROPERTIES, exports.DESCRIPTOR_PRIVACIES, exports.ALL, exports.PRIVACY_PRIVATE, exports.PRIVACY_PROTECTED, exports.PRIVACY_PUBLIC, exports.DESCRIPTOR_LOCATIONS, exports.ALL, exports.LOCATION_INSTANCE, exports.LOCATION_STATIC, exports.DESCRIPTOR_VISIBILITIES, exports.ALL, exports.VISIBILITY_EXPORTED, exports.VISIBILITY_INTERNAL, exports.ARGUMENT_FUNCTIONS, exports.ARGUMENT_METHODS, exports.DESCRIPTOR_OVERLOADS, exports.DESCRIPTOR_TAGS, exports.TAGS_FOR_CONTENT, exports.TAGS_FOR_EXISTENCE, exports.ARGUMENT_CLASSES, exports.ARGUMENT_ENUMS, exports.ARGUMENT_ENUM_MEMBERS, exports.ARGUMENT_FUNCTIONS, exports.ARGUMENT_INTERFACES, exports.ARGUMENT_METHODS, exports.ARGUMENT_NAMESPACES, exports.ARGUMENT_PROPERTIES, exports.ARGUMENT_TYPES, exports.ARGUMENT_VARIABLES),
165 exports.ARGUMENT_CLASSES,
166 exports.ARGUMENT_ENUMS,
167 exports.ARGUMENT_FUNCTIONS,
168 exports.ARGUMENT_INTERFACES,
169 exports.ARGUMENT_METHODS,
170 exports.ARGUMENT_NAMESPACES,
171 exports.ARGUMENT_PROPERTIES,
172 exports.ARGUMENT_TYPES,
173 exports.ARGUMENT_VARIABLES,
179 properties: (_m = {},
180 _m[exports.ARGUMENT_CLASSES] = Rule.ARGUMENT_DESCRIPTOR_BLOCK,
181 _m[exports.ARGUMENT_ENUMS] = Rule.ARGUMENT_DESCRIPTOR_BLOCK,
182 _m[exports.ARGUMENT_ENUM_MEMBERS] = Rule.ARGUMENT_DESCRIPTOR_BLOCK,
183 _m[exports.ARGUMENT_FUNCTIONS] = Rule.ARGUMENT_DESCRIPTOR_FUNCTION,
184 _m[exports.ARGUMENT_INTERFACES] = Rule.ARGUMENT_DESCRIPTOR_BLOCK,
185 _m[exports.ARGUMENT_METHODS] = Rule.ARGUMENT_DESCRIPTOR_METHOD,
186 _m[exports.ARGUMENT_NAMESPACES] = Rule.ARGUMENT_DESCRIPTOR_BLOCK,
187 _m[exports.ARGUMENT_PROPERTIES] = Rule.ARGUMENT_DESCRIPTOR_CLASS,
188 _m[exports.ARGUMENT_TYPES] = Rule.ARGUMENT_DESCRIPTOR_BLOCK,
189 _m[exports.ARGUMENT_VARIABLES] = Rule.ARGUMENT_DESCRIPTOR_BLOCK,
197 [true, exports.ARGUMENT_ENUMS, exports.ARGUMENT_FUNCTIONS, exports.ARGUMENT_METHODS],
201 _o[exports.ARGUMENT_ENUMS] = true,
202 _o[exports.ARGUMENT_FUNCTIONS] = (_p = {},
203 _p[exports.DESCRIPTOR_VISIBILITIES] = [exports.VISIBILITY_EXPORTED],
205 _o[exports.ARGUMENT_METHODS] = (_q = {},
206 _q[exports.DESCRIPTOR_LOCATIONS] = exports.LOCATION_INSTANCE,
207 _q[exports.DESCRIPTOR_PRIVACIES] = [exports.PRIVACY_PUBLIC, exports.PRIVACY_PROTECTED],
209 _o[exports.ARGUMENT_PROPERTIES] = (_r = {},
210 _r[exports.DESCRIPTOR_TAGS] = (_s = {},
211 _s[exports.TAGS_FOR_CONTENT] = {
214 _s[exports.TAGS_FOR_EXISTENCE] = ["inheritdoc"],
220 rationale: Lint.Utils.dedent(templateObject_2 || (templateObject_2 = tslib_1.__makeTemplateObject(["\n Helps ensure important components are documented.\n\n Note: use this rule sparingly. It's better to have self-documenting names on components with single, concise responsibilities.\n Comments that only restate the names of variables add nothing to code, and can easily become outdated.\n "], ["\n Helps ensure important components are documented.\n\n Note: use this rule sparingly. It's better to have self-documenting names on components with single, concise responsibilities.\n Comments that only restate the names of variables add nothing to code, and can easily become outdated.\n "]))),
222 typescriptOnly: false,
225 }(Lint.Rules.AbstractRule));
227 var modifierAliases = {
230 function walk(context) {
231 return ts.forEachChild(context.sourceFile, cb);
234 case ts.SyntaxKind.ClassDeclaration:
235 checkNode(node, exports.ARGUMENT_CLASSES);
237 case ts.SyntaxKind.EnumDeclaration:
238 checkNode(node, exports.ARGUMENT_ENUMS);
239 for (var _i = 0, _a = node.members; _i < _a.length; _i++) {
241 // Enum members don't have modifiers, so use the parent
242 // enum declaration when checking the requirements.
243 checkNode(member, exports.ARGUMENT_ENUM_MEMBERS, node);
246 case ts.SyntaxKind.FunctionDeclaration:
247 checkNode(node, exports.ARGUMENT_FUNCTIONS);
249 case ts.SyntaxKind.InterfaceDeclaration:
250 checkNode(node, exports.ARGUMENT_INTERFACES);
252 case ts.SyntaxKind.MethodSignature:
253 checkNode(node, exports.ARGUMENT_METHODS);
255 case ts.SyntaxKind.MethodDeclaration:
256 if (node.parent.kind !== ts.SyntaxKind.ObjectLiteralExpression) {
257 checkNode(node, exports.ARGUMENT_METHODS);
260 case ts.SyntaxKind.ModuleDeclaration:
261 checkNode(node, exports.ARGUMENT_NAMESPACES);
263 case ts.SyntaxKind.PropertySignature:
264 checkNode(node, exports.ARGUMENT_PROPERTIES);
266 case ts.SyntaxKind.PropertyDeclaration:
267 checkNode(node, exports.ARGUMENT_PROPERTIES);
269 case ts.SyntaxKind.TypeAliasDeclaration:
270 checkNode(node, exports.ARGUMENT_TYPES);
272 case ts.SyntaxKind.VariableStatement:
273 // Only check variables at the namespace/module-level or file-level
274 // and not variables declared inside functions and other things.
275 switch (node.parent.kind) {
276 case ts.SyntaxKind.SourceFile:
277 case ts.SyntaxKind.ModuleBlock:
278 for (var _b = 0, _c = node.declarationList
279 .declarations; _b < _c.length; _b++) {
280 var declaration = _c[_b];
281 checkNode(declaration, exports.ARGUMENT_VARIABLES, node);
285 case ts.SyntaxKind.GetAccessor:
286 case ts.SyntaxKind.SetAccessor:
287 if (node.parent.kind !== ts.SyntaxKind.ObjectLiteralExpression) {
288 checkAccessorNode(node, exports.ARGUMENT_PROPERTIES);
291 return ts.forEachChild(node, cb);
293 function checkNode(node, docType, requirementNode) {
294 if (requirementNode === void 0) { requirementNode = node; }
295 if (!nodeIsExcluded(node, docType, requirementNode) && !nodeHasDocs(node, docType)) {
296 addDocumentationFailure(node, describeDocType(docType), requirementNode);
299 function checkAccessorNode(node, docType) {
300 if (nodeIsExcluded(node, exports.ARGUMENT_PROPERTIES, node) || nodeHasDocs(node, docType)) {
303 var correspondingAccessor = getCorrespondingAccessor(node);
304 if (correspondingAccessor === undefined || !nodeHasDocs(correspondingAccessor, docType)) {
305 addDocumentationFailure(node, exports.ARGUMENT_PROPERTIES, node);
308 function nodeIsExcluded(node, docType, requirementNode) {
309 var name = node.name;
310 if (name === undefined) {
313 var exclusions = context.options.get(docType);
314 if (exclusions === undefined) {
317 for (var _i = 0, _a = exclusions.requirements; _i < _a.length; _i++) {
318 var requirement = _a[_i];
319 if (requirement.excludes(requirementNode)) {
325 function nodeHasDocs(node, docType) {
326 var docs = getApparentJsDoc(node, docType, context.sourceFile);
327 if (docs === undefined) {
331 .map(function (doc) { return doc.comment; })
332 .filter(function (comment) { return comment !== undefined && comment.trim() !== ""; });
333 return comments.length !== 0;
336 * @see https://github.com/ajafff/tsutils/issues/16
338 function getApparentJsDoc(node, docType, sourceFile) {
339 if (ts.isVariableDeclaration(node)) {
340 if (variableIsAfterFirstInDeclarationList(node)) {
345 if (ts.isVariableDeclarationList(node)) {
348 var equivalentNodesForDocs = getEquivalentNodesForDocs(node, docType);
349 return equivalentNodesForDocs
350 .map(function (docsNode) { return tsutils.getJsDoc(docsNode, sourceFile); })
351 .filter(function (nodeDocs) { return nodeDocs !== undefined; })
352 .reduce(function (docs, moreDocs) { return docs.concat(moreDocs); }, []);
355 * @see https://github.com/palantir/tslint/issues/4416
357 function getEquivalentNodesForDocs(node, docType) {
358 var exclusions = context.options.get(docType);
359 if (exclusions === undefined || exclusions.overloadsSeparateDocs) {
362 if (tsutils.isFunctionDeclaration(node) && node.name !== undefined) {
363 var functionName_1 = node.name.text;
364 return getSiblings(node).filter(function (child) {
365 return tsutils.isFunctionDeclaration(child) &&
366 child.name !== undefined &&
367 child.name.text === functionName_1;
370 if (tsutils.isMethodDeclaration(node) &&
371 tsutils.isIdentifier(node.name) &&
372 tsutils.isClassDeclaration(node.parent)) {
373 var methodName_1 = node.name.text;
374 return node.parent.members.filter(function (member) {
375 return tsutils.isMethodDeclaration(member) &&
376 tsutils.isIdentifier(member.name) &&
377 member.name.text === methodName_1;
382 function addDocumentationFailure(node, docType, requirementNode) {
383 var start = node.getStart();
384 var width = node.getText().split(/\r|\n/g)[0].length;
385 var description = describeDocumentationFailure(requirementNode, docType);
386 context.addFailureAt(start, width, description);
389 function getCorrespondingAccessor(node) {
390 var propertyName = tsutils.getPropertyName(node.name);
391 if (propertyName === undefined) {
394 var parent = node.parent;
395 var correspondingKindCheck = node.kind === ts.SyntaxKind.GetAccessor ? isSetAccessor : isGetAccessor;
396 for (var _i = 0, _a = parent.members; _i < _a.length; _i++) {
398 if (!correspondingKindCheck(member)) {
401 if (tsutils.getPropertyName(member.name) === propertyName) {
407 function variableIsAfterFirstInDeclarationList(node) {
408 var parent = node.parent;
409 if (parent === undefined) {
412 return ts.isVariableDeclarationList(parent) && node !== parent.declarations[0];
414 function describeDocumentationFailure(node, docType) {
415 var description = Rule.FAILURE_STRING_EXIST;
416 if (node.modifiers !== undefined) {
417 description += node.modifiers
418 .map(function (modifier) { return describeModifier(modifier.kind); })
421 return "" + description + docType + ".";
423 function describeModifier(kind) {
424 var description = ts.SyntaxKind[kind].toLowerCase().split("keyword")[0];
425 var alias = modifierAliases[description];
426 return alias !== undefined ? alias : description;
428 function describeDocType(docType) {
429 return docType.replace("-", " ");
431 function getSiblings(node) {
432 var parent = node.parent;
433 // Source files nest their statements within a node for getChildren()
434 if (ts.isSourceFile(parent)) {
435 return parent.statements.slice();
437 return parent.getChildren();
439 function isGetAccessor(node) {
440 return node.kind === ts.SyntaxKind.GetAccessor;
442 function isSetAccessor(node) {
443 return node.kind === ts.SyntaxKind.SetAccessor;
445 var templateObject_1, templateObject_2;