Skip to content

Commit 0059763

Browse files
committed
move eslint rules from eslint-plugin-microsoft-typescript to scripts/eslint
1 parent a79f598 commit 0059763

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1254
-38
lines changed

.eslintrc

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"es6": true
1313
},
1414
"plugins": [
15-
"@typescript-eslint", "microsoft-typescript", "jsdoc", "no-null", "import"
15+
"@typescript-eslint", "jsdoc", "no-null", "import"
1616
],
1717
"rules": {
1818
"@typescript-eslint/adjacent-overload-signatures": "error",
@@ -56,19 +56,19 @@
5656
"@typescript-eslint/type-annotation-spacing": "error",
5757
"@typescript-eslint/unified-signatures": "error",
5858

59-
/** eslint-plugin-microsoft-typescript */
60-
"microsoft-typescript/object-literal-surrounding-space": "error",
61-
"microsoft-typescript/no-type-assertion-whitespace": "error",
62-
"microsoft-typescript/type-operator-spacing": "error",
63-
"microsoft-typescript/only-arrow-functions": ["error", {
59+
/** scripts/eslint/rules */
60+
"object-literal-surrounding-space": "error",
61+
"no-type-assertion-whitespace": "error",
62+
"type-operator-spacing": "error",
63+
"only-arrow-functions": ["error", {
6464
"allowNamedFunctions": true ,
6565
"allowDeclarations": true
6666
}],
67-
"microsoft-typescript/no-double-space": "error",
68-
"microsoft-typescript/boolean-trivia": "error",
69-
"microsoft-typescript/no-in-operator": "error",
70-
"microsoft-typescript/debug-assert": "error",
71-
"microsoft-typescript/no-keywords": "error",
67+
"no-double-space": "error",
68+
"boolean-trivia": "error",
69+
"no-in-operator": "error",
70+
"debug-assert": "error",
71+
"no-keywords": "error",
7272

7373
/** eslint-plugin-import */
7474
"import/no-extraneous-dependencies": ["error", { "optionalDependencies": false }],
@@ -151,8 +151,8 @@
151151
"@typescript-eslint/prefer-function-type": "off",
152152
"@typescript-eslint/unified-signatures": "off",
153153

154-
/** eslint-plugin-microsoft-typescript */
155-
"microsoft-typescript/no-keywords": "off",
154+
/** scripts/eslint/rules */
155+
"no-keywords": "off",
156156

157157
/** eslint */
158158
"no-var": "off"

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ src
1111
tests
1212
Jakefile.js
1313
.eslintrc
14+
.eslintignore
1415
.editorconfig
1516
.failed-tests
1617
.git

Gulpfile.js

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,20 +318,39 @@ task("clean-tests").description = "Cleans the outputs for the test infrastructur
318318

319319
const watchTests = () => watchProject("src/testRunner", cmdLineOptions);
320320

321+
const buildRules = () => buildProject("scripts/eslint");
322+
task("build-rules", buildRules);
323+
task("build-rules").description = "Compiles eslint rules to js";
324+
325+
const cleanRules = () => cleanProject("scripts/eslint");
326+
cleanTasks.push(cleanRules);
327+
task("clean-rules", cleanRules);
328+
task("clean-rules").description = "Cleans the outputs for the eslint rules";
329+
330+
const runRulesTests = () => runConsoleTests("built/eslint/tests", "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ false);
331+
task("run-rules-tests", series(buildRules, runRulesTests));
332+
task("run-rules-tests").description = "Runs the eslint rule tests";
333+
321334
const lintFoldStart = async () => { if (fold.isTravis()) console.log(fold.start("lint")); };
322335
const lintFoldEnd = async () => { if (fold.isTravis()) console.log(fold.end("lint")); };
323336
const eslint = async () => {
324337
const args = [
325-
"node_modules/eslint/bin/eslint", "-f", "autolinkable-stylish", "-c", ".eslintrc", "--ext", ".ts", "."
338+
"node_modules/eslint/bin/eslint",
339+
"--format", "autolinkable-stylish",
340+
"--config", ".eslintrc",
341+
"--ext", ".ts", ".",
342+
"--rulesdir", "built/eslint/rules/",
326343
];
327344

328345
if (cmdLineOptions.fix) {
329346
args.push("--fix");
330347
}
348+
331349
log(`Linting: ${args.join(" ")}`);
332350
return exec(process.execPath, args);
333351
}
334-
const lint = series([lintFoldStart, eslint, lintFoldEnd]);
352+
353+
const lint = series([buildRules, lintFoldStart, eslint, lintFoldEnd]);
335354
lint.displayName = "lint";
336355
task("lint", lint);
337356
task("lint").description = "Runs eslint on the compiler sources.";

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"@types/travis-fold": "latest",
5656
"@types/xml2js": "^0.4.0",
5757
"@typescript-eslint/eslint-plugin": "1.13.0",
58+
"@typescript-eslint/experimental-utils": "1.13.0",
5859
"@typescript-eslint/parser": "1.13.0",
5960
"async": "latest",
6061
"azure-devops-node-api": "^8.0.0",
@@ -68,7 +69,6 @@
6869
"eslint-formatter-autolinkable-stylish": "1.0.0",
6970
"eslint-plugin-import": "2.18.2",
7071
"eslint-plugin-jsdoc": "15.6.1",
71-
"eslint-plugin-microsoft-typescript": "0.2.0",
7272
"eslint-plugin-no-null": "1.0.2",
7373
"fancy-log": "latest",
7474
"fs-extra": "^6.0.1",
@@ -112,6 +112,7 @@
112112
"gulp": "gulp",
113113
"jake": "gulp",
114114
"lint": "gulp lint",
115+
"lint:test": "gulp run-rules-tests",
115116
"setup-hooks": "node scripts/link-hooks.js",
116117
"update-costly-tests": "node scripts/costly-tests.js"
117118
},
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { SyntaxKind } from "typescript";
2+
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/experimental-utils";
3+
import { getEsTreeNodeToTSNodeMap, createRule } from "./utils";
4+
5+
export = createRule({
6+
name: "boolean-trivia",
7+
meta: {
8+
docs: {
9+
description: ``,
10+
category: "Best Practices",
11+
recommended: "error",
12+
},
13+
messages: {
14+
booleanTriviaArgumentError: `Tag argument with parameter name`,
15+
booleanTriviaArgumentSpaceError: `There should be 1 space between an argument and its comment`,
16+
},
17+
schema: [],
18+
type: "problem",
19+
},
20+
defaultOptions: [],
21+
22+
create(context) {
23+
const esTreeNodeToTSNodeMap = getEsTreeNodeToTSNodeMap(context.parserServices);
24+
const sourceCode = context.getSourceCode();
25+
const sourceCodeText = sourceCode.getText();
26+
27+
const isSetOrAssert = (name: string): boolean => name.startsWith("set") || name.startsWith("assert");
28+
const isTrivia = (node: TSESTree.Expression): boolean => {
29+
const tsNode = esTreeNodeToTSNodeMap.get(node);
30+
31+
if (tsNode.kind === SyntaxKind.Identifier) {
32+
return tsNode.originalKeywordKind === SyntaxKind.UndefinedKeyword;
33+
}
34+
35+
return [SyntaxKind.TrueKeyword, SyntaxKind.FalseKeyword, SyntaxKind.NullKeyword].indexOf(tsNode.kind) >= 0;
36+
};
37+
38+
const shouldIgnoreCalledExpression = (node: TSESTree.CallExpression): boolean => {
39+
if (node.callee && node.callee.type === AST_NODE_TYPES.MemberExpression) {
40+
const methodName = node.callee.property.type === AST_NODE_TYPES.Identifier
41+
? node.callee.property.name
42+
: "";
43+
44+
if (isSetOrAssert(methodName)) {
45+
return true;
46+
}
47+
48+
return ["apply", "call", "equal", "fail", "isTrue", "output", "stringify", "push"].indexOf(methodName) >= 0;
49+
}
50+
51+
if (node.callee && node.callee.type === AST_NODE_TYPES.Identifier) {
52+
const functionName = node.callee.name;
53+
54+
if (isSetOrAssert(functionName)) {
55+
return true;
56+
}
57+
58+
return [
59+
"createImportSpecifier",
60+
"createAnonymousType",
61+
"createSignature",
62+
"createProperty",
63+
"resolveName",
64+
"contains",
65+
].indexOf(functionName) >= 0;
66+
}
67+
68+
return false;
69+
};
70+
71+
const checkArg = (node: TSESTree.Expression): void => {
72+
if (!isTrivia(node)) {
73+
return;
74+
}
75+
76+
const comments = sourceCode.getCommentsBefore(node);
77+
if (!comments || comments.length !== 1 || comments[0].type !== "Block") {
78+
context.report({ messageId: "booleanTriviaArgumentError", node });
79+
return;
80+
}
81+
82+
const argRangeStart = node.range[0];
83+
const commentRangeEnd = comments[0].range[1];
84+
const hasNewLine = sourceCodeText.slice(commentRangeEnd, argRangeStart).indexOf("\n") >= 0;
85+
86+
if (argRangeStart !== commentRangeEnd + 1 && !hasNewLine) {
87+
context.report({ messageId: "booleanTriviaArgumentSpaceError", node });
88+
}
89+
};
90+
91+
const checkBooleanTrivia = (node: TSESTree.CallExpression) => {
92+
if (shouldIgnoreCalledExpression(node)) {
93+
return;
94+
}
95+
96+
for (const arg of node.arguments) {
97+
checkArg(arg);
98+
}
99+
};
100+
101+
return {
102+
CallExpression: checkBooleanTrivia,
103+
};
104+
},
105+
});
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/experimental-utils";
2+
import { createRule } from "./utils";
3+
4+
export = createRule({
5+
name: "debug-assert",
6+
meta: {
7+
docs: {
8+
description: ``,
9+
category: "Possible Errors",
10+
recommended: "error",
11+
},
12+
messages: {
13+
secondArgumentDebugAssertError: `Second argument to 'Debug.assert' should be a string literal`,
14+
thirdArgumentDebugAssertError: `Third argument to 'Debug.assert' should be a string literal or arrow function`,
15+
},
16+
schema: [],
17+
type: "problem",
18+
},
19+
defaultOptions: [],
20+
21+
create(context) {
22+
const isArrowFunction = (node: TSESTree.Node) => node.type === AST_NODE_TYPES.ArrowFunctionExpression;
23+
const isStringLiteral = (node: TSESTree.Node): boolean => (
24+
(node.type === AST_NODE_TYPES.Literal && typeof node.value === "string") || node.type === AST_NODE_TYPES.TemplateLiteral
25+
);
26+
27+
const isDebugAssert = (node: TSESTree.MemberExpression): boolean => (
28+
node.object.type === AST_NODE_TYPES.Identifier
29+
&& node.object.name === "Debug"
30+
&& node.property.type === AST_NODE_TYPES.Identifier
31+
&& node.property.name === "assert"
32+
);
33+
34+
const checkDebugAssert = (node: TSESTree.CallExpression) => {
35+
const args = node.arguments;
36+
const argsLen = args.length;
37+
if (!(node.callee.type === AST_NODE_TYPES.MemberExpression && isDebugAssert(node.callee)) || argsLen < 2) {
38+
return;
39+
}
40+
41+
const message1Node = args[1];
42+
if (message1Node && !isStringLiteral(message1Node)) {
43+
context.report({ messageId: "secondArgumentDebugAssertError", node: message1Node });
44+
}
45+
46+
if (argsLen < 3) {
47+
return;
48+
}
49+
50+
const message2Node = args[2];
51+
if (message2Node && (!isStringLiteral(message2Node) && !isArrowFunction(message2Node))) {
52+
context.report({ messageId: "thirdArgumentDebugAssertError", node: message2Node });
53+
}
54+
};
55+
56+
return {
57+
CallExpression: checkDebugAssert,
58+
};
59+
},
60+
});
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { TSESTree } from "@typescript-eslint/experimental-utils";
2+
import { SyntaxKind } from "typescript";
3+
import { getEsTreeNodeToTSNodeMap, createRule } from "./utils";
4+
5+
export = createRule({
6+
name: "no-double-space",
7+
meta: {
8+
docs: {
9+
description: ``,
10+
category: "Stylistic Issues",
11+
recommended: "error",
12+
},
13+
messages: {
14+
noDoubleSpaceError: `Use only one space`,
15+
},
16+
schema: [],
17+
type: "problem",
18+
},
19+
defaultOptions: [],
20+
21+
create(context) {
22+
const esTreeNodeToTSNodeMap = getEsTreeNodeToTSNodeMap(context.parserServices);
23+
const sourceCode = context.getSourceCode();
24+
const lines = sourceCode.getLines();
25+
26+
const isLiteral = (node: TSESTree.Node | null) => {
27+
if (!node) {
28+
return false;
29+
}
30+
31+
const tsNode = esTreeNodeToTSNodeMap.get(node);
32+
if (!tsNode) {
33+
return false;
34+
}
35+
36+
return [
37+
SyntaxKind.NoSubstitutionTemplateLiteral,
38+
SyntaxKind.RegularExpressionLiteral,
39+
SyntaxKind.TemplateMiddle,
40+
SyntaxKind.StringLiteral,
41+
SyntaxKind.TemplateHead,
42+
SyntaxKind.TemplateTail,
43+
].indexOf(tsNode.kind) >= 0;
44+
};
45+
46+
const checkDoubleSpace = (node: TSESTree.Node) => {
47+
lines.forEach((line, index) => {
48+
const firstNonSpace = /\S/.exec(line);
49+
if (!firstNonSpace || line.includes("@param")) {
50+
return;
51+
}
52+
53+
// Allow common uses of double spaces
54+
// * To align `=` or `!=` signs
55+
// * To align comments at the end of lines
56+
// * To indent inside a comment
57+
// * To use two spaces after a period
58+
// * To include aligned `->` in a comment
59+
const rgx = /[^/*. ][ ]{2}[^-!/= ]/g;
60+
rgx.lastIndex = firstNonSpace.index;
61+
const doubleSpace = rgx.exec(line);
62+
63+
if (!doubleSpace) {
64+
return;
65+
}
66+
67+
const locIndex = sourceCode.getIndexFromLoc({ column: doubleSpace.index, line: index + 1 });
68+
const sourceNode = sourceCode.getNodeByRangeIndex(locIndex);
69+
if (isLiteral(sourceNode)) {
70+
return;
71+
}
72+
73+
context.report({ messageId: "noDoubleSpaceError", node, loc: { line: index + 1, column: doubleSpace.index + 1 } });
74+
});
75+
};
76+
77+
return {
78+
Program: checkDoubleSpace,
79+
};
80+
},
81+
});

0 commit comments

Comments
 (0)