From c434183c5d708819be523ff7d0beeeaf60eb8661 Mon Sep 17 00:00:00 2001 From: Klaus Meinhardt Date: Mon, 18 Sep 2017 22:20:57 +0200 Subject: [PATCH] jsdoc-format: check first line of multiline comment (#3181) --- src/configs/all.ts | 2 +- src/configs/latest.ts | 5 ++- src/rules/jsdocFormatRule.ts | 37 ++++++++++++++++--- .../check-multiline-start/test.ts.lint | 10 +++++ .../check-multiline-start/tslint.json | 5 +++ .../{ => default}/jsdoc-bom.ts.lint | 0 .../{ => default}/jsdoc-windows.ts.lint | 0 .../jsdoc-format/{ => default}/jsdoc.ts.lint | 3 ++ .../jsdoc-format/{ => default}/tslint.json | 0 9 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 test/rules/jsdoc-format/check-multiline-start/test.ts.lint create mode 100644 test/rules/jsdoc-format/check-multiline-start/tslint.json rename test/rules/jsdoc-format/{ => default}/jsdoc-bom.ts.lint (100%) rename test/rules/jsdoc-format/{ => default}/jsdoc-windows.ts.lint (100%) rename test/rules/jsdoc-format/{ => default}/jsdoc.ts.lint (96%) rename test/rules/jsdoc-format/{ => default}/tslint.json (100%) diff --git a/src/configs/all.ts b/src/configs/all.ts index b7446925ff9..f04e635c3e1 100644 --- a/src/configs/all.ts +++ b/src/configs/all.ts @@ -188,7 +188,7 @@ export const rules = { "import-spacing": true, "interface-name": true, "interface-over-type-literal": true, - "jsdoc-format": true, + "jsdoc-format": [true, "check-multiline-start"], "match-default-export-name": true, "new-parens": true, "newline-before-return": true, diff --git a/src/configs/latest.ts b/src/configs/latest.ts index 2b9d9c1353d..29cc946faca 100644 --- a/src/configs/latest.ts +++ b/src/configs/latest.ts @@ -64,8 +64,11 @@ export const rules = { ], }, - // added in v5.8.0 + // added in v5.8 "no-duplicate-switch-case": true, + "jsdoc-format": { + options: "check-multiline-start", + }, }; // tslint:enable object-literal-sort-keys diff --git a/src/rules/jsdocFormatRule.ts b/src/rules/jsdocFormatRule.ts index 1a6fecd6fa3..1641fe7ff24 100644 --- a/src/rules/jsdocFormatRule.ts +++ b/src/rules/jsdocFormatRule.ts @@ -20,6 +20,8 @@ import * as ts from "typescript"; import * as Lint from "../index"; +const OPTION_CHECK_MULTILINE_START = "check-multiline-start"; + export class Rule extends Lint.Rules.AbstractRule { /* tslint:disable:object-literal-sort-keys */ public static metadata: Lint.IRuleMetadata = { @@ -31,11 +33,24 @@ export class Rule extends Lint.Rules.AbstractRule { * each line contains an asterisk and asterisks must be aligned * each asterisk must be followed by either a space or a newline (except for the first and the last) * the only characters before the asterisk on each line must be whitespace characters - * one line comments must start with \`/** \` and end with \`*/\``, + * one line comments must start with \`/** \` and end with \`*/\` + * multiline comments don't allow text after \`/** \` in the first line (with option \`"${OPTION_CHECK_MULTILINE_START}"\`) + `, rationale: "Helps maintain a consistent, readable style for JSDoc comments.", - optionsDescription: "Not configurable.", - options: null, - optionExamples: [true], + optionsDescription: Lint.Utils.dedent` + You can optionally specify the option \`"${OPTION_CHECK_MULTILINE_START}"\` to enforce the first line of a + multiline JSDoc comment to be empty. + `, + options: { + type: "array", + minItems: 0, + maxItems: 1, + items: { + type: "string", + enum: [OPTION_CHECK_MULTILINE_START], + }, + }, + optionExamples: [true, [true, OPTION_CHECK_MULTILINE_START]], type: "style", typescriptOnly: false, }; @@ -45,11 +60,17 @@ export class Rule extends Lint.Rules.AbstractRule { public static FORMAT_FAILURE_STRING = "jsdoc is not formatted correctly on this line"; public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithFunction(sourceFile, walk); + return this.applyWithFunction(sourceFile, walk, { + firstLineOfMultiline: this.ruleArguments.indexOf(OPTION_CHECK_MULTILINE_START) !== -1, + }); } } -function walk(ctx: Lint.WalkContext) { +interface Options { + firstLineOfMultiline: boolean; +} + +function walk(ctx: Lint.WalkContext) { return utils.forEachComment(ctx.sourceFile, (fullText, {kind, pos, end}) => { if (kind !== ts.SyntaxKind.MultiLineCommentTrivia || fullText[pos + 2] !== "*" || fullText[pos + 3] === "*" || fullText[pos + 3] === "/") { @@ -65,6 +86,10 @@ function walk(ctx: Lint.WalkContext) { } const alignColumn = getAlignColumn(ctx.sourceFile, pos + 1); + if (ctx.options.firstLineOfMultiline && /\S/.test(firstLine)) { + // first line of multiline JSDoc should be empty, i.e. only contain whitespace + ctx.addFailureAt(pos, firstLine.length + 3, Rule.FORMAT_FAILURE_STRING); + } let lineStart = pos + firstLine.length + 4; // +3 for the comment start "/**" and +1 for the newline const endIndex = lines.length - 1; for (let i = 1; i < endIndex; ++i) { diff --git a/test/rules/jsdoc-format/check-multiline-start/test.ts.lint b/test/rules/jsdoc-format/check-multiline-start/test.ts.lint new file mode 100644 index 00000000000..1e78b77b079 --- /dev/null +++ b/test/rules/jsdoc-format/check-multiline-start/test.ts.lint @@ -0,0 +1,10 @@ + /** First line of multiline jsdoc + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [jsdoc is not formatted correctly on this line] + * should be empty. + */ + + /** + * Good multiline. + */ + + /** Good singleline */ diff --git a/test/rules/jsdoc-format/check-multiline-start/tslint.json b/test/rules/jsdoc-format/check-multiline-start/tslint.json new file mode 100644 index 00000000000..40faa974a27 --- /dev/null +++ b/test/rules/jsdoc-format/check-multiline-start/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "jsdoc-format": [true, "check-multiline-start"] + } +} diff --git a/test/rules/jsdoc-format/jsdoc-bom.ts.lint b/test/rules/jsdoc-format/default/jsdoc-bom.ts.lint similarity index 100% rename from test/rules/jsdoc-format/jsdoc-bom.ts.lint rename to test/rules/jsdoc-format/default/jsdoc-bom.ts.lint diff --git a/test/rules/jsdoc-format/jsdoc-windows.ts.lint b/test/rules/jsdoc-format/default/jsdoc-windows.ts.lint similarity index 100% rename from test/rules/jsdoc-format/jsdoc-windows.ts.lint rename to test/rules/jsdoc-format/default/jsdoc-windows.ts.lint diff --git a/test/rules/jsdoc-format/jsdoc.ts.lint b/test/rules/jsdoc-format/default/jsdoc.ts.lint similarity index 96% rename from test/rules/jsdoc-format/jsdoc.ts.lint rename to test/rules/jsdoc-format/default/jsdoc.ts.lint index 448be1be18a..8a66c75a6be 100644 --- a/test/rules/jsdoc-format/jsdoc.ts.lint +++ b/test/rules/jsdoc-format/default/jsdoc.ts.lint @@ -70,6 +70,9 @@ one * /**/ // no jsdoc + /** First line of multiline jsdoc + * should be empty. + */ } // Regression test: jsdoc rule shouldn't look inside template strings (https://github.com/palantir/tslint/issues/332) diff --git a/test/rules/jsdoc-format/tslint.json b/test/rules/jsdoc-format/default/tslint.json similarity index 100% rename from test/rules/jsdoc-format/tslint.json rename to test/rules/jsdoc-format/default/tslint.json