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

Commit

Permalink
Support granular configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
pablobirukov committed Aug 28, 2018
1 parent 7975240 commit c16bd79
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 48 deletions.
144 changes: 96 additions & 48 deletions src/rules/objectLiteralShorthandRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,19 @@ import {
import * as ts from "typescript";
import * as Lint from "..";

const OPTION_NEVER = "never";
const OPTION_VALUE_NEVER = "never";
const OPTION_KEY_PROPERTY = "property";
const OPTION_KEY_METHOD = "method";

interface RawOptions {
[OPTION_KEY_PROPERTY]?: "never" | "always";
[OPTION_KEY_METHOD]?: "never" | "always";
}

interface Options {
enforceShorthandMethods: boolean;
enforceShorthandProperties: boolean;
}

export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
Expand All @@ -36,12 +48,37 @@ export class Rule extends Lint.Rules.AbstractRule {
description: "Enforces/disallows use of ES6 object literal shorthand.",
hasFix: true,
optionsDescription: Lint.Utils.dedent`
If the \'never\' option is provided, any shorthand object literal syntax will cause a failure.`,
If the \'never\' option is provided, any shorthand object literal syntax will cause a failure.
With \`{"property": "never"}\` provided, the rule fails on property shothands only,
and respectively with \`{"method": "never"}\`, the rule fails only on method shorthands`,
options: {
type: "string",
enum: [OPTION_NEVER]
oneOf: [
{
type: "string",
enum: [OPTION_VALUE_NEVER]
},
{
type: "object",
properties: {
[OPTION_KEY_PROPERTY]: {
type: "string",
enum: [OPTION_VALUE_NEVER]
},
[OPTION_KEY_METHOD]: {
type: "string",
enum: [OPTION_VALUE_NEVER]
}
},
minProperties: 1,
maxProperties: 2
}
]
},
optionExamples: [true, [true, OPTION_NEVER]],
optionExamples: [
true,
[true, OPTION_VALUE_NEVER],
[true, { [OPTION_KEY_PROPERTY]: OPTION_VALUE_NEVER }]
],
type: "style",
typescriptOnly: false
};
Expand All @@ -52,24 +89,71 @@ export class Rule extends Lint.Rules.AbstractRule {
public static SHORTHAND_ASSIGNMENT = "Shorthand property assignments have been disallowed.";

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(
sourceFile,
this.ruleArguments.indexOf(OPTION_NEVER) === -1
? enforceShorthandWalker
: disallowShorthandWalker
return this.applyWithFunction(sourceFile, walk, this.parseOptions(this.ruleArguments));
}
private parseOptions(options: Array<string | RawOptions>): Options {
if (options.indexOf(OPTION_VALUE_NEVER) !== -1) {
return {
enforceShorthandMethods: false,
enforceShorthandProperties: false
};
}
const optionsObject: RawOptions | undefined = options.find(
(el: string | RawOptions): el is RawOptions =>
typeof el === "object" &&
(el[OPTION_KEY_PROPERTY] === "never" || el[OPTION_KEY_METHOD] === "never")
);
if (optionsObject !== undefined) {
return {
enforceShorthandMethods: !(optionsObject[OPTION_KEY_METHOD] === "never"),
enforceShorthandProperties: !(optionsObject[OPTION_KEY_PROPERTY] === "never")
};
} else {
return {
enforceShorthandMethods: true,
enforceShorthandProperties: true
};
}
}
}

function disallowShorthandWalker(ctx: Lint.WalkContext<void>) {
function walk(ctx: Lint.WalkContext<Options>) {
const { enforceShorthandMethods, enforceShorthandProperties } = ctx.options;
return ts.forEachChild(ctx.sourceFile, function cb(node): void {
if (isShorthandPropertyAssignment(node)) {
if (
enforceShorthandProperties &&
isPropertyAssignment(node) &&
node.name.kind === ts.SyntaxKind.Identifier &&
isIdentifier(node.initializer) &&
node.name.text === node.initializer.text
) {
ctx.addFailureAtNode(
node,
`${Rule.LONGHAND_PROPERTY}('{${node.name.text}}').`,
Lint.Replacement.deleteFromTo(node.name.end, node.end)
);
} else if (
enforceShorthandMethods &&
isPropertyAssignment(node) &&
isFunctionExpression(node.initializer) &&
// allow named function expressions
node.initializer.name === undefined
) {
const [name, fix] = handleLonghandMethod(node.name, node.initializer, ctx.sourceFile);
ctx.addFailure(
node.getStart(ctx.sourceFile),
getChildOfKind(node.initializer, ts.SyntaxKind.OpenParenToken, ctx.sourceFile)!.pos,
`${Rule.LONGHAND_METHOD}('{${name}() {...}}').`,
fix
);
} else if (!enforceShorthandProperties && isShorthandPropertyAssignment(node)) {
ctx.addFailureAtNode(
node.name,
Rule.SHORTHAND_ASSIGNMENT,
Lint.Replacement.appendText(node.getStart(ctx.sourceFile), `${node.name.text}: `)
);
} else if (
!enforceShorthandMethods &&
isMethodDeclaration(node) &&
node.parent!.kind === ts.SyntaxKind.ObjectLiteralExpression
) {
Expand All @@ -83,42 +167,6 @@ function disallowShorthandWalker(ctx: Lint.WalkContext<void>) {
});
}

function enforceShorthandWalker(ctx: Lint.WalkContext<void>) {
return ts.forEachChild(ctx.sourceFile, function cb(node): void {
if (isPropertyAssignment(node)) {
if (
node.name.kind === ts.SyntaxKind.Identifier &&
isIdentifier(node.initializer) &&
node.name.text === node.initializer.text
) {
ctx.addFailureAtNode(
node,
`${Rule.LONGHAND_PROPERTY}('{${node.name.text}}').`,
Lint.Replacement.deleteFromTo(node.name.end, node.end)
);
} else if (
isFunctionExpression(node.initializer) &&
// allow named function expressions
node.initializer.name === undefined
) {
const [name, fix] = handleLonghandMethod(
node.name,
node.initializer,
ctx.sourceFile
);
ctx.addFailure(
node.getStart(ctx.sourceFile),
getChildOfKind(node.initializer, ts.SyntaxKind.OpenParenToken, ctx.sourceFile)!
.pos,
`${Rule.LONGHAND_METHOD}('{${name}() {...}}').`,
fix
);
}
}
return ts.forEachChild(node, cb);
});
}

function fixShorthandMethodDeclaration(node: ts.MethodDeclaration, sourceFile: ts.SourceFile) {
const isGenerator = node.asteriskToken !== undefined;
const isAsync = hasModifier(node.modifiers, ts.SyntaxKind.AsyncKeyword);
Expand Down
45 changes: 45 additions & 0 deletions test/rules/object-literal-shorthand/onlyMethods/test.ts.fix
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const badMethodsGoodProps = {
w() {},
*x() {},
[y]() {},
z: z
};

const goodMethodsBadProps = {
w() {},
*x() {},
[y]() {},
z: z
};

const arrows = {
x: (y) => y // this is OK.
};

const namedFunctions = {
x: function y() {} // named function expressions are also OK.
};

const quotes = {
"foo-bar"() {},
"foo-bar"() {}
};

const extraCases = {
x: x,
a: 123,
b: "hello",
c: 'c',
["a" + "nested"]: {
x: x
}
};

const asyncFn = {
async foo() {},
async *bar() {}
}

({foo: foo} = {foo: foo});
({foo: foo} = {foo: foo});

58 changes: 58 additions & 0 deletions test/rules/object-literal-shorthand/onlyMethods/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const badMethodsGoodProps = {
w: function() {},
~~~~~~~~~~~ [LONGHAND_METHOD % ("('{w() {...}}')")]
x: function *() {},
~~~~~~~~~~~~~ [LONGHAND_METHOD % ("('{*x() {...}}')")]
[y]: function() {},
~~~~~~~~~~~~~ [LONGHAND_METHOD % ("('{[y]() {...}}')")]
z: z
};

const goodMethodsBadProps = {
w() {},
*x() {},
[y]() {},
z
~ [OBJECT_LITERAL_DISALLOWED]
};

const arrows = {
x: (y) => y // this is OK.
};

const namedFunctions = {
x: function y() {} // named function expressions are also OK.
};

const quotes = {
"foo-bar": function() {},
~~~~~~~~~~~~~~~~~~~ [LONGHAND_METHOD % ("('{\"foo-bar\"() {...}}')")]
"foo-bar"() {}
};

const extraCases = {
x,
~ [OBJECT_LITERAL_DISALLOWED]
a: 123,
b: "hello",
c: 'c',
["a" + "nested"]: {
x: x
}
};

const asyncFn = {
foo: async function() {},
~~~~~~~~~~~~~~~~~~~ [LONGHAND_METHOD % ("('{async foo() {...}}')")]
bar: async function*() {}
~~~~~~~~~~~~~~~~~~~~ [LONGHAND_METHOD % ("('{async *bar() {...}}')")]
}

({foo: foo} = {foo: foo});
({foo} = {foo});
~~~ [OBJECT_LITERAL_DISALLOWED]
~~~ [OBJECT_LITERAL_DISALLOWED]

[OBJECT_LITERAL_DISALLOWED]: Shorthand property assignments have been disallowed.
[LONGHAND_METHOD]: Expected method shorthand in object literal %s.
[LONGHAND_PROPERTY]: Expected property shorthand in object literal %s.
10 changes: 10 additions & 0 deletions test/rules/object-literal-shorthand/onlyMethods/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"rules": {
"object-literal-shorthand": [
true,
{
"property": "never"
}
]
}
}

0 comments on commit c16bd79

Please sign in to comment.