|
1 |
| -import { ESLintUtils, TSESTree } from "@typescript-eslint/utils"; |
2 |
| -import { RuleListener, RuleModule } from "@typescript-eslint/utils/ts-eslint"; |
3 |
| - |
4 | 1 | /**
|
5 |
| - * ESlint plugin to enforce people to comply with our code guidelines |
6 |
| - * Reference: https://developer.bmwgroup.net/docs/cdc/cdc-platform-wasm/guidelines/#cdc-specific-rules |
| 2 | + * AssemblyScript ESLint Plugin |
| 3 | + * |
| 4 | + * This plugin provides rules for validating AssemblyScript code. |
| 5 | + * It detects patterns that are either not supported in AssemblyScript |
| 6 | + * or violate best practices specific to the language. |
7 | 7 | */
|
8 |
| - |
9 |
| -const createRule = ESLintUtils.RuleCreator( |
10 |
| - (name) => `https://example.com/rule/${name}` |
11 |
| -); |
12 |
| - |
13 |
| -const dontOmitElse: RuleModule<"omittedElse", [], unknown, RuleListener> = |
14 |
| - createRule({ |
15 |
| - name: "dont-omit-else", |
16 |
| - meta: { |
17 |
| - type: "problem", |
18 |
| - docs: { |
19 |
| - description: |
20 |
| - "Enforce else block unless if branch contains control flow", |
21 |
| - }, |
22 |
| - messages: { |
23 |
| - omittedElse: |
24 |
| - "Omitted else block is not allowed unless if branch contains control flow.", |
25 |
| - }, |
26 |
| - schema: [], |
27 |
| - }, |
28 |
| - defaultOptions: [], |
29 |
| - create(context) { |
30 |
| - // Check if the statement is part of an else-if chain |
31 |
| - function isElseIf(node: TSESTree.Node) { |
32 |
| - // Check parent relationship to determine if this is an else-if |
33 |
| - const ancestors = context.sourceCode.getAncestors(node); |
34 |
| - const parent = ancestors[ancestors.length - 1]; |
35 |
| - return ( |
36 |
| - parent && parent.type === "IfStatement" && parent.alternate === node |
37 |
| - ); |
38 |
| - } |
39 |
| - |
40 |
| - // Helper function to check if a node is a control flow statement |
41 |
| - function isControlFlowStatement( |
42 |
| - node: TSESTree.Node | null | undefined |
43 |
| - ): boolean { |
44 |
| - // Return true if the node is a control flow statement |
45 |
| - return ( |
46 |
| - node?.type === "ReturnStatement" || |
47 |
| - node?.type === "ThrowStatement" || |
48 |
| - node?.type === "BreakStatement" || |
49 |
| - node?.type === "ContinueStatement" |
50 |
| - ); |
51 |
| - } |
52 |
| - |
53 |
| - // Check if statement or block contains control flow |
54 |
| - function checkControlFlow(node: TSESTree.Node) { |
55 |
| - // Handle different statement types |
56 |
| - if (node.type === "BlockStatement") { |
57 |
| - // For block statements, check the last statement in the block |
58 |
| - const lastStatement = |
59 |
| - node.body.length > 0 ? node.body[node.body.length - 1] : null; |
60 |
| - return isControlFlowStatement(lastStatement); |
61 |
| - } else { |
62 |
| - // For single statements (no curly braces), check the statement directly |
63 |
| - return isControlFlowStatement(node); |
64 |
| - } |
65 |
| - } |
66 |
| - |
67 |
| - return { |
68 |
| - IfStatement(node) { |
69 |
| - // Skip if this is already an else-if |
70 |
| - if (isElseIf(node)) { |
71 |
| - return; |
72 |
| - } |
73 |
| - const hasElse = node.alternate !== null; |
74 |
| - const ifBlockEndsWithControlFlow = checkControlFlow(node.consequent); |
75 |
| - // Report error if no else and no control flow |
76 |
| - if (!hasElse && !ifBlockEndsWithControlFlow) { |
77 |
| - context.report({ |
78 |
| - node, |
79 |
| - messageId: "omittedElse", |
80 |
| - }); |
81 |
| - } |
82 |
| - }, |
83 |
| - }; |
84 |
| - }, |
85 |
| - }); |
86 |
| - |
87 |
| -// reject usages of ...var: T on function definition |
88 |
| -const noRestParam: ESLintUtils.RuleModule< |
89 |
| - "noRestMsg", |
90 |
| - [], |
91 |
| - unknown, |
92 |
| - ESLintUtils.RuleListener |
93 |
| -> = createRule({ |
94 |
| - name: "no-rest-params", |
95 |
| - meta: { |
96 |
| - type: "problem", |
97 |
| - docs: { |
98 |
| - description: "Rest params are not supported.", |
99 |
| - }, |
100 |
| - messages: { |
101 |
| - noRestMsg: "Don't use rest params, it's not supported in assemblyscript", |
102 |
| - }, |
103 |
| - schema: [], |
104 |
| - }, |
105 |
| - defaultOptions: [], |
106 |
| - create(context) { |
107 |
| - return { |
108 |
| - RestElement: (node) => { |
109 |
| - context.report({ |
110 |
| - messageId: "noRestMsg", |
111 |
| - node: node, |
112 |
| - }); |
113 |
| - }, |
114 |
| - }; |
115 |
| - }, |
116 |
| -}); |
117 |
| - |
118 |
| -// reject usages of ...var on call expressions |
119 |
| -const noSpread: ESLintUtils.RuleModule< |
120 |
| - "noSpreadMsg", |
121 |
| - [], |
122 |
| - unknown, |
123 |
| - ESLintUtils.RuleListener |
124 |
| -> = createRule({ |
125 |
| - name: "no-spread", |
126 |
| - meta: { |
127 |
| - type: "problem", |
128 |
| - docs: { |
129 |
| - description: "Spreads are not supported.", |
130 |
| - }, |
131 |
| - messages: { |
132 |
| - noSpreadMsg: "Spreads are not supported.", |
133 |
| - }, |
134 |
| - schema: [], |
135 |
| - }, |
136 |
| - defaultOptions: [], |
137 |
| - create(context) { |
138 |
| - return { |
139 |
| - SpreadElement: (node) => { |
140 |
| - context.report({ messageId: "noSpreadMsg", node: node }); |
141 |
| - }, |
142 |
| - }; |
143 |
| - }, |
144 |
| -}); |
145 |
| - |
146 |
| -const noUnsupportedKeyword: ESLintUtils.RuleModule< |
147 |
| - string, |
148 |
| - [], |
149 |
| - unknown, |
150 |
| - ESLintUtils.RuleListener |
151 |
| -> = createRule({ |
152 |
| - name: "no-unsupported-keyword", |
153 |
| - meta: { |
154 |
| - type: "problem", |
155 |
| - docs: { |
156 |
| - description: "Some keywords are not supported in assemblyscript", |
157 |
| - }, |
158 |
| - messages: { |
159 |
| - noNever: "'never' is not supported in AssemblyScript.", |
160 |
| - noAny: "'any' is not supported in AssemblyScript.", |
161 |
| - noUndefined: "'undefined' is not supported in AssemblyScript.", |
162 |
| - }, |
163 |
| - schema: [], |
164 |
| - }, |
165 |
| - defaultOptions: [], |
166 |
| - create(context) { |
167 |
| - return { |
168 |
| - TSNeverKeyword: (node) => { |
169 |
| - context.report({ messageId: "noNever", node: node }); |
170 |
| - }, |
171 |
| - TSAnyKeyword: (node) => { |
172 |
| - context.report({ messageId: "noAny", node: node }); |
173 |
| - }, |
174 |
| - TSUndefinedKeyword: (node) => { |
175 |
| - context.report({ messageId: "noUndefined", node: node }); |
176 |
| - }, |
177 |
| - }; |
178 |
| - }, |
179 |
| -}); |
| 8 | +import dontOmitElse from "./rules/dont-omit-else.js"; |
| 9 | +import noSpread from "./rules/no-spread.js"; |
| 10 | +import noUnsupportedKeyword from "./rules/no-unsupported-keyword.js"; |
180 | 11 |
|
181 | 12 | export default {
|
182 | 13 | rules: {
|
183 | 14 | "dont-omit-else": dontOmitElse,
|
184 |
| - "no-rest-params": noRestParam, |
185 | 15 | "no-spread": noSpread,
|
186 | 16 | "no-unsupported-keyword": noUnsupportedKeyword,
|
187 | 17 | },
|
|
0 commit comments