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

New rule option: 'check-rest-spread' for 'whitespace' rule #3089

Merged
merged 6 commits into from
Aug 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 37 additions & 6 deletions src/rules/whitespaceRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const OPTION_DECL = "check-decl";
const OPTION_OPERATOR = "check-operator";
const OPTION_MODULE = "check-module";
const OPTION_SEPARATOR = "check-separator";
const OPTION_REST_SPREAD = "check-rest-spread";
const OPTION_TYPE = "check-type";
const OPTION_TYPECAST = "check-typecast";
const OPTION_PREBLOCK = "check-preblock";
Expand All @@ -37,13 +38,14 @@ export class Rule extends Lint.Rules.AbstractRule {
description: "Enforces whitespace style conventions.",
rationale: "Helps maintain a readable, consistent style in your codebase.",
optionsDescription: Lint.Utils.dedent`
Eight arguments may be optionally provided:
Nine arguments may be optionally provided:

* \`"check-branch"\` checks branching statements (\`if\`/\`else\`/\`for\`/\`while\`) are followed by whitespace.
* \`"check-decl"\`checks that variable declarations have whitespace around the equals token.
* \`"check-operator"\` checks for whitespace around operator tokens.
* \`"check-module"\` checks for whitespace in import & export statements.
* \`"check-separator"\` checks for whitespace after separator tokens (\`,\`/\`;\`).
* \`"check-rest-spread"\` checks that there is no whitespace after rest/spread operator (\`...\`).
* \`"check-type"\` checks for whitespace before a variable type specification.
* \`"check-typecast"\` checks for whitespace between a typecast and its target.
* \`"check-preblock"\` checks for whitespace before the opening brace of a block`,
Expand All @@ -52,31 +54,33 @@ export class Rule extends Lint.Rules.AbstractRule {
items: {
type: "string",
enum: ["check-branch", "check-decl", "check-operator", "check-module",
"check-separator", "check-type", "check-typecast", "check-preblock"],
"check-separator", "check-rest-spread", "check-type", "check-typecast", "check-preblock"],
},
minLength: 0,
maxLength: 7,
maxLength: 9,
},
optionExamples: [[true, "check-branch", "check-operator", "check-typecast"]],
type: "style",
typescriptOnly: false,
};

public static FAILURE_STRING = "missing whitespace";
public static FAILURE_STRING_MISSING = "missing whitespace";
public static FAILURE_STRING_INVALID = "invalid whitespace";

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, parseOptions(this.ruleArguments));
}
}

type Options = Record<"branch" | "decl" | "operator" | "module" | "separator" | "type" | "typecast" | "preblock", boolean>;
type Options = Record<"branch" | "decl" | "operator" | "module" | "separator" | "restSpread" | "type" | "typecast" | "preblock", boolean>;
function parseOptions(ruleArguments: string[]): Options {
return {
branch: has(OPTION_BRANCH),
decl: has(OPTION_DECL),
operator: has(OPTION_OPERATOR),
module: has(OPTION_MODULE),
separator: has(OPTION_SEPARATOR),
restSpread: has(OPTION_REST_SPREAD),
type: has(OPTION_TYPE),
typecast: has(OPTION_TYPECAST),
preblock: has(OPTION_PREBLOCK),
Expand Down Expand Up @@ -192,6 +196,22 @@ function walk(ctx: Lint.WalkContext<Options>) {
if (options.decl && initializer !== undefined) {
checkForTrailingWhitespace((type !== undefined ? type : name).getEnd());
}
break;

case ts.SyntaxKind.BindingElement:
case ts.SyntaxKind.Parameter:
const { dotDotDotToken } = node as ts.BindingElement | ts.ParameterDeclaration;
if (options.restSpread && dotDotDotToken !== undefined) {
checkForExcessiveWhitespace(dotDotDotToken.end);
}
break;

case ts.SyntaxKind.SpreadAssignment:
case ts.SyntaxKind.SpreadElement:
if (options.restSpread) {
const position = (node as ts.SpreadAssignment).expression.getFullStart();
checkForExcessiveWhitespace(position);
}
}

ts.forEachChild(node, cb);
Expand Down Expand Up @@ -278,6 +298,17 @@ function walk(ctx: Lint.WalkContext<Options>) {
return;
}
const fix = Lint.Replacement.appendText(position, " ");
ctx.addFailureAt(position, 1, Rule.FAILURE_STRING, fix);
ctx.addFailureAt(position, 1, Rule.FAILURE_STRING_MISSING, fix);
}

function checkForExcessiveWhitespace(position: number): void {
if (position !== sourceFile.end && Lint.isWhiteSpace(sourceFile.text.charCodeAt(position))) {
addInvalidWhitespaceErrorAt(position);
}
}

function addInvalidWhitespaceErrorAt(position: number): void {
const fix = Lint.Replacement.deleteText(position, 1);
ctx.addFailureAt(position, 1, Rule.FAILURE_STRING_INVALID, fix);
}
}
8 changes: 8 additions & 0 deletions test/rules/whitespace/all/test.ts.fix
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,11 @@ else
const foo = 123;
// code that just wants to be encapsulated in a block scope
}

const foo = { ...bar };

const foo = [ ...bar ];

function foo (bar, ...baz) {}

const { foo, ...bar } = baz;
12 changes: 12 additions & 0 deletions test/rules/whitespace/all/test.ts.lint
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,15 @@ else
const foo = 123;
// code that just wants to be encapsulated in a block scope
}

const foo = { ... bar };
~ [invalid whitespace]

const foo = [ ... bar ];
~ [invalid whitespace]

function foo (bar, ... baz) {}
~ [invalid whitespace]

const { foo, ... bar } = baz;
~ [invalid whitespace]
1 change: 1 addition & 0 deletions test/rules/whitespace/all/tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"check-module",
"check-preblock",
"check-separator",
"check-rest-spread",
"check-type",
"check-typecast"
]
Expand Down
8 changes: 8 additions & 0 deletions test/rules/whitespace/none/test.ts.lint
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,11 @@ else
const foo = 123;
// code that just wants to be encapsulated in a block scope
}

const foo = { ... bar };

const foo = [ ... bar ];

function foo (bar, ... baz) {}

const { foo, ... bar } = baz;