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
Browse files Browse the repository at this point in the history
  • Loading branch information
jamescdavis committed Aug 15, 2018
1 parent 9b7574b commit 2b4306e
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 4 deletions.
23 changes: 19 additions & 4 deletions src/rules/objectLiteralSortKeysRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ import { codeExamples } from "./code-examples/objectLiteralSortKeys.examples";

const OPTION_IGNORE_CASE = "ignore-case";
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;
matchDeclarationOrder: boolean;
matchDeclarationOrderOnly: boolean;
shorthandFirst: boolean;
}

Expand All @@ -53,17 +55,24 @@ 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.
* "${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:
const obj = { a, c, b: true };
`,
options: {
type: "string",
enum: [OPTION_IGNORE_CASE, OPTION_MATCH_DECLARATION_ORDER, OPTION_SHORTHAND_FIRST],
enum: [OPTION_IGNORE_CASE, OPTION_MATCH_DECLARATION_ORDER, OPTION_MATCH_DECLARATION_ORDER_ONLY, OPTION_SHORTHAND_FIRST],
},
optionExamples: [
true,
[true, OPTION_IGNORE_CASE, OPTION_MATCH_DECLARATION_ORDER, OPTION_SHORTHAND_FIRST],
[true, OPTION_IGNORE_CASE, OPTION_MATCH_DECLARATION_ORDER_ONLY, OPTION_SHORTHAND_FIRST],
],
type: "maintainability",
typescriptOnly: false,
Expand All @@ -89,6 +98,9 @@ export class Rule extends Lint.Rules.OptionallyTypedRule {
if (options.matchDeclarationOrder) {
throw new Error(`${this.ruleName} needs type info to use "${OPTION_MATCH_DECLARATION_ORDER}".`);
}
if (options.matchDeclarationOrderOnly) {
throw new Error(`${this.ruleName} needs type info to use "${OPTION_MATCH_DECLARATION_ORDER_ONLY}".`);
}
return this.applyWithFunction(sourceFile, walk, options);
}

Expand All @@ -106,6 +118,7 @@ function parseOptions(ruleArguments: any[]): Options {
return {
ignoreCase: has(OPTION_IGNORE_CASE),
matchDeclarationOrder: has(OPTION_MATCH_DECLARATION_ORDER),
matchDeclarationOrderOnly: has(OPTION_MATCH_DECLARATION_ORDER_ONLY),
shorthandFirst: has(OPTION_SHORTHAND_FIRST),
};

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

ts.forEachChild(sourceFile, function cb(node): void {
Expand All @@ -128,7 +141,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 @@ -138,7 +151,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 2b4306e

Please sign in to comment.