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

Commit

Permalink
object-literal-sort-keys: Add match-declaration-order-only option (#3748
Browse files Browse the repository at this point in the history
)
  • Loading branch information
jamescdavis authored and adidahiya committed Dec 23, 2018
1 parent 941fa88 commit e1ddce0
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 7 deletions.
43 changes: 36 additions & 7 deletions src/rules/objectLiteralSortKeysRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,21 @@ import {
} from "tsutils";
import * as ts from "typescript";

import { showWarningOnce } from "../error";
import * as Lint from "../index";
import { codeExamples } from "./code-examples/objectLiteralSortKeys.examples";

const OPTION_IGNORE_CASE = "ignore-case";
const OPTION_LOCALE_COMPARE = "locale-compare";
const OPTION_MATCH_DECLARATION_ORDER = "match-declaration-order";
const OPTION_MATCH_DECLARATION_ORDER_ONLY = "match-declaration-order-only";
const OPTION_SHORTHAND_FIRST = "shorthand-first";

interface Options {
ignoreCase: boolean;
localeCompare: boolean;
matchDeclarationOrder: boolean;
matchDeclarationOrderOnly: boolean;
shorthandFirst: boolean;
}

Expand Down Expand Up @@ -63,7 +66,13 @@ export class Rule extends Lint.Rules.OptionallyTypedRule {
const obj: I = { foo: 1, bar: 2 };
\`\`\`
If a contextual type is not found, alphabetical ordering will be used instead.
If a contextual type is not found, alphabetical ordering will be used instead.
* "${OPTION_MATCH_DECLARATION_ORDER_ONLY}" exactly like "${OPTION_MATCH_DECLARATION_ORDER}",
but don't fall back to alphabetical if a contextual type is not found.
Note: If both ${OPTION_MATCH_DECLARATION_ORDER_ONLY} and ${OPTION_MATCH_DECLARATION_ORDER} options are present,
${OPTION_MATCH_DECLARATION_ORDER_ONLY} will take precedence and alphabetical fallback will not occur.
* \`${OPTION_SHORTHAND_FIRST}\` will enforce shorthand properties to appear first, as in:
\`\`\`
Expand All @@ -76,6 +85,7 @@ export class Rule extends Lint.Rules.OptionallyTypedRule {
OPTION_IGNORE_CASE,
OPTION_LOCALE_COMPARE,
OPTION_MATCH_DECLARATION_ORDER,
OPTION_MATCH_DECLARATION_ORDER_ONLY,
OPTION_SHORTHAND_FIRST,
],
},
Expand Down Expand Up @@ -113,15 +123,31 @@ export class Rule extends Lint.Rules.OptionallyTypedRule {

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
const options = parseOptions(this.ruleArguments);
if (options.matchDeclarationOrder) {
throw new Error(
`${this.ruleName} needs type info to use "${OPTION_MATCH_DECLARATION_ORDER}".`,
if (options.matchDeclarationOrder || options.matchDeclarationOrderOnly) {
showWarningOnce(
Lint.Utils.dedent`
${this.ruleName} needs type info to use "${OPTION_MATCH_DECLARATION_ORDER}" or
"${OPTION_MATCH_DECLARATION_ORDER_ONLY}".
See https://palantir.github.io/tslint/usage/type-checking/ for documentation on
how to enable this feature.
`,
);
return [];
}

return this.applyWithFunction(sourceFile, walk, options);
}

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
const options = parseOptions(this.ruleArguments);
if (options.matchDeclarationOrder && options.matchDeclarationOrderOnly) {
showWarningOnce(
`"${OPTION_MATCH_DECLARATION_ORDER}" will be ignored since ` +
`"${OPTION_MATCH_DECLARATION_ORDER_ONLY}" has been enabled for ${this.ruleName}.`
);
return [];
}

return this.applyWithFunction(
sourceFile,
walk,
Expand All @@ -136,6 +162,7 @@ function parseOptions(ruleArguments: any[]): Options {
ignoreCase: has(OPTION_IGNORE_CASE),
localeCompare: has(OPTION_LOCALE_COMPARE),
matchDeclarationOrder: has(OPTION_MATCH_DECLARATION_ORDER),
matchDeclarationOrderOnly: has(OPTION_MATCH_DECLARATION_ORDER_ONLY),
shorthandFirst: has(OPTION_SHORTHAND_FIRST),
};

Expand All @@ -147,7 +174,7 @@ function parseOptions(ruleArguments: any[]): Options {
function walk(ctx: Lint.WalkContext<Options>, checker?: ts.TypeChecker): void {
const {
sourceFile,
options: { ignoreCase, localeCompare, matchDeclarationOrder, shorthandFirst },
options: { ignoreCase, localeCompare, matchDeclarationOrder, matchDeclarationOrderOnly, shorthandFirst },
} = ctx;

ts.forEachChild(sourceFile, function cb(node): void {
Expand All @@ -158,7 +185,7 @@ function walk(ctx: Lint.WalkContext<Options>, checker?: ts.TypeChecker): void {
});

function check(node: ts.ObjectLiteralExpression): void {
if (matchDeclarationOrder) {
if (matchDeclarationOrder || matchDeclarationOrderOnly) {
const type = getContextualType(node, checker!);
// If type has an index signature, we can't check ordering.
// If type has call/construct signatures, it can't be satisfied by an object literal anyway.
Expand All @@ -176,7 +203,9 @@ function walk(ctx: Lint.WalkContext<Options>, checker?: ts.TypeChecker): void {
return;
}
}
checkAlphabetical(node);
if (!matchDeclarationOrderOnly) {
checkAlphabetical(node);
}
}

function checkAlphabetical(node: ts.ObjectLiteralExpression): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
interface I {
b;
a;
}

declare function f(i: I): void;

const a = 0, b = 0;

f({ b, a });

f({ a, b });
~ [0 % ('b', 'I')]

// Resets ordering after spread operator.

f({ a, ...x, b });

f({ a, ...x, a, b });
~ [0 % ('b', 'I')]


// Methods and getters/setters work like any other key.

f({ b() {}, a() {} });

f({ a() {}, b() {} });
~ [0 % ('b', 'I')]

f({
get b() {},
a,
set b(v) {},
~ [0 % ('b', 'I')]
});

f({
get b() {},
set b() {},
a,
});

// Ignores computed properties. Does not ignore string / number keys.

interface J {
"foo";
2;
[Symol.iterator];
}
declare function j(j: J): void;
j({ [Symbol.iterator]: 1, "foo": 1, 2: 1 });
j({ [Symbol.iterator]: 1, 2: 1, "foo": 1 });
~~~~~ [0 % ('foo', 'J')]

// Works with anonymous type too.
type T = { b, a };
const o: T = { a, b };
~ [0 % ('b', 'T')]

const o: { b, a } = { a, b };
~ [1 % ('b')]

// Non-alphabetical ordering is fine, even if it can't find a type.

const o = {
b,
a,
};

[0]: The key '%s' is not in the same order as it is in '%s'.
[1]: The key '%s' is not in the same order as it is in its type declaration.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"object-literal-sort-keys": [true, "ignore-case", "match-declaration-order-only"]
}
}

0 comments on commit e1ddce0

Please sign in to comment.