Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

Commit

Permalink
[new-rule-option] allow-single-line-comments for file-header rule (#4560
Browse files Browse the repository at this point in the history
)

fixes #3180
  • Loading branch information
rrogowski authored and adidahiya committed Mar 21, 2019
1 parent d81a317 commit caad563
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 19 deletions.
136 changes: 117 additions & 19 deletions src/rules/fileHeaderRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,17 @@ import * as ts from "typescript";

import * as Lint from "../index";

const ENFORCE_TRAILING_NEWLINE = "enforce-trailing-newline";
const OPTION_MATCH = "match";
const OPTION_ALLOW_SINGLE_LINE_COMMENTS = "allow-single-line-comments";
const OPTION_DEFAULT = "default";
const OPTION_ENFORCE_TRAILING_NEWLINE = "enforce-trailing-newline";

interface FileHeaderRuleOptions {
[OPTION_MATCH]: string;
[OPTION_ALLOW_SINGLE_LINE_COMMENTS]?: boolean;
[OPTION_DEFAULT]?: string;
[OPTION_ENFORCE_TRAILING_NEWLINE]?: boolean;
}

export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
Expand All @@ -28,29 +38,76 @@ export class Rule extends Lint.Rules.AbstractRule {
description:
"Enforces a certain header comment for all files, matched by a regular expression.",
optionsDescription: Lint.Utils.dedent`
A single object may be passed in for configuration that must contain:
* \`${OPTION_MATCH}\`: a regular expression that all headers should match
Any of the following optional fields may also be provided:
* \`${OPTION_ALLOW_SINGLE_LINE_COMMENTS}\`: a boolean for whether \`//\` should be considered file headers in addition to \`/*\` comments
* \`${OPTION_DEFAULT}\`: text to add for file headers when running in \`--fix\` mode
* \`${OPTION_ENFORCE_TRAILING_NEWLINE}\`: a boolean for whether a newline must follow the header
The rule will also accept array of strings as a legacy form of options, though the object form is recommended.
The first option, which is mandatory, is a regular expression that all headers should match.
The second argument, which is optional, is a string that should be inserted as a header comment
if fixing is enabled and no header that matches the first argument is found.
The third argument, which is optional, is a string that denotes whether or not a newline should
exist on the header.`,
options: {
type: "array",
items: [
oneOf: [
{
type: "string",
type: "array",
items: {
type: "object",
properties: {
[OPTION_MATCH]: {
type: "string",
},
[OPTION_ALLOW_SINGLE_LINE_COMMENTS]: {
type: "boolean",
},
[OPTION_DEFAULT]: {
type: "string",
},
[OPTION_ENFORCE_TRAILING_NEWLINE]: {
type: "boolean",
},
},
additionalProperties: false,
},
},
{
type: "string",
type: "array",
items: [
{
type: "string",
},
{
type: "string",
},
{
type: "string",
},
],
additionalItems: false,
minLength: 1,
maxLength: 3,
},
],
},
optionExamples: [
[
true,
{
type: "string",
[OPTION_MATCH]: "Copyright \\d{4}",
[OPTION_ALLOW_SINGLE_LINE_COMMENTS]: true,
[OPTION_DEFAULT]: "Copyright 2018",
[OPTION_ENFORCE_TRAILING_NEWLINE]: true,
},
],
additionalItems: false,
minLength: 1,
maxLength: 3,
},
optionExamples: [[true, "Copyright \\d{4}", "Copyright 2018", ENFORCE_TRAILING_NEWLINE]],
[true, "Copyright \\d{4}", "Copyright 2018", OPTION_ENFORCE_TRAILING_NEWLINE],
],
hasFix: true,
type: "style",
typescriptOnly: false,
Expand All @@ -61,17 +118,18 @@ export class Rule extends Lint.Rules.AbstractRule {
public static MISSING_NEW_LINE_FAILURE_STRING = "missing new line following the file header";

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
const options = this.getRuleOptions();

const { text } = sourceFile;
const headerFormat = new RegExp(this.ruleArguments[0] as string);
const textToInsert = this.ruleArguments[1] as string | undefined;
const enforceExtraTrailingLine =
this.ruleArguments.indexOf(ENFORCE_TRAILING_NEWLINE) !== -1;
const headerFormat = new RegExp(options[OPTION_MATCH]);
const textToInsert = options[OPTION_DEFAULT];

// ignore shebang if it exists
let offset = text.startsWith("#!") ? text.indexOf("\n") : 0;
// returns the text of the first comment or undefined
const commentText = ts.forEachLeadingCommentRange(text, offset, (pos, end, kind) =>
text.substring(pos + 2, kind === ts.SyntaxKind.SingleLineCommentTrivia ? end : end - 2),
const commentText = this.getFileHeaderText(
text,
offset,
!!options[OPTION_ALLOW_SINGLE_LINE_COMMENTS],
);

if (commentText === undefined || !headerFormat.test(commentText)) {
Expand Down Expand Up @@ -107,7 +165,7 @@ export class Rule extends Lint.Rules.AbstractRule {
}

const trailingNewLineViolation =
enforceExtraTrailingLine &&
options[OPTION_ENFORCE_TRAILING_NEWLINE] &&
headerFormat.test(commentText) &&
this.doesNewLineEndingViolationExist(text, offset);

Expand Down Expand Up @@ -135,6 +193,24 @@ export class Rule extends Lint.Rules.AbstractRule {
return [];
}

private getRuleOptions(): FileHeaderRuleOptions {
const options = this.ruleArguments;
if (options.length === 1 && typeof options[0] === "object") {
return options[0] as FileHeaderRuleOptions;
}

// Legacy options
const args = this.ruleArguments as string[];
return {
[OPTION_DEFAULT]: args[1],
[OPTION_ENFORCE_TRAILING_NEWLINE]:
args[2] !== undefined
? args[2].indexOf(OPTION_ENFORCE_TRAILING_NEWLINE) !== -1
: undefined,
[OPTION_MATCH]: args[0],
};
}

private createComment(
sourceFile: ts.SourceFile,
commentText: string,
Expand Down Expand Up @@ -172,4 +248,26 @@ export class Rule extends Lint.Rules.AbstractRule {
entireComment !== undefined && NEW_LINE_FOLLOWING_HEADER.test(entireComment) !== null
);
}

private getFileHeaderText(
text: string,
offset: number,
allowSingleLineComments: boolean,
): string | undefined {
const ranges = ts.getLeadingCommentRanges(text, offset);
if (ranges === undefined || ranges.length === 0) {
return undefined;
}

const fileHeaderRanges = !allowSingleLineComments ? ranges.slice(0, 1) : ranges;
return fileHeaderRanges
.map(range => {
const { pos, kind, end } = range;
return text.substring(
pos + 2,
kind === ts.SyntaxKind.SingleLineCommentTrivia ? end : end - 2,
);
})
.join("\n");
}
}
7 changes: 7 additions & 0 deletions test/rules/file-header/bad/test2.ts.fix
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*!
* Good header 2
*/

// **********************************
// Bad header
// **********************************
4 changes: 4 additions & 0 deletions test/rules/file-header/bad/test2.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// **********************************
~nil [missing file header]
// Bad header
// **********************************
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// **********************************
// Good header
// **********************************

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"rules": {
"file-header": [true, {
"match": "Good header",
"allow-single-line-comments": true
}]
}
}

0 comments on commit caad563

Please sign in to comment.