eslint-plugin-kirklin 1.5.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- const version = "1.5.0";
1
+ const version = "2.0.0";
2
2
 
3
3
  const hasDocs = [
4
4
  "consistent-chaining",
@@ -59,7 +59,7 @@ function unindent(str) {
59
59
  return min;
60
60
  }
61
61
  const indent = line.match(/^\s*/)?.[0].length;
62
- return indent === void 0 ? min : Math.min(min, indent);
62
+ return indent === undefined ? min : Math.min(min, indent);
63
63
  }, Number.POSITIVE_INFINITY);
64
64
  let emptyLinesHead = 0;
65
65
  while (emptyLinesHead < lines.length && whitespaceLines[emptyLinesHead]) {
@@ -135,7 +135,7 @@ const consistentChaining = createEslintRule({
135
135
  break;
136
136
  }
137
137
  default: {
138
- current = void 0;
138
+ current = undefined;
139
139
  break;
140
140
  }
141
141
  }
@@ -228,7 +228,7 @@ const consistentListNewline = createEslintRule({
228
228
  return;
229
229
  }
230
230
  const currentContent = context.sourceCode.text.slice(current.range[0], current.range[1]);
231
- return currentContent.match(/(?:,|;)$/) ? void 0 : ",";
231
+ return currentContent.match(/(?:,|;)$/) ? undefined : ",";
232
232
  }
233
233
  function hasComments(current) {
234
234
  let program = current;
@@ -246,7 +246,7 @@ const consistentListNewline = createEslintRule({
246
246
  if (items.length === 0) {
247
247
  return;
248
248
  }
249
- let startToken = ["CallExpression", "NewExpression"].includes(node.type) ? void 0 : context.sourceCode.getFirstToken(node);
249
+ let startToken = ["CallExpression", "NewExpression"].includes(node.type) ? undefined : context.sourceCode.getFirstToken(node);
250
250
  if (node.type === "CallExpression") {
251
251
  startToken = context.sourceCode.getTokenAfter(
252
252
  node.typeArguments ? node.typeArguments : node.callee.type === "MemberExpression" ? node.callee.property : node.callee
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "eslint-plugin-kirklin",
3
3
  "type": "module",
4
- "version": "1.5.0",
5
- "packageManager": "pnpm@9.12.3",
4
+ "version": "2.0.0",
5
+ "packageManager": "pnpm@10.2.1",
6
6
  "description": "Kirk Lin extended ESLint rules",
7
7
  "author": "Kirk Lin (https://github.com/kirklin)",
8
8
  "license": "MIT",
@@ -13,18 +13,19 @@
13
13
  "url": "git+https://github.com/kirklin/eslint-plugin-kirklin.git"
14
14
  },
15
15
  "bugs": "https://github.com/kirklin/eslint-plugin-kirklin/issues",
16
- "keywords": [],
16
+ "keywords": [
17
+ "eslint-plugin"
18
+ ],
17
19
  "sideEffects": false,
18
20
  "exports": {
19
21
  ".": {
20
- "types": "./dist/index.d.ts",
21
- "import": "./dist/index.mjs",
22
- "require": "./dist/index.cjs"
22
+ "types": "./dist/index.d.mts",
23
+ "default": "./dist/index.mjs"
23
24
  }
24
25
  },
25
26
  "main": "./dist/index.mjs",
26
27
  "module": "./dist/index.mjs",
27
- "types": "./dist/index.d.ts",
28
+ "types": "./dist/index.d.mts",
28
29
  "files": [
29
30
  "dist"
30
31
  ],
@@ -44,31 +45,32 @@
44
45
  "eslint": "*"
45
46
  },
46
47
  "devDependencies": {
47
- "@antfu/ni": "^0.23.0",
48
- "@kirklin/eslint-config": "^2.6.1",
48
+ "@antfu/ni": "^23.3.1",
49
+ "@antfu/utils": "^8.1.0",
50
+ "@kirklin/eslint-config": "^2.8.0",
49
51
  "@types/eslint": "^9.6.1",
50
52
  "@types/lodash.merge": "^4.6.9",
51
- "@types/node": "^22.8.6",
53
+ "@types/node": "^22.13.1",
52
54
  "@types/semver": "^7.5.8",
53
- "@typescript-eslint/typescript-estree": "^8.12.2",
54
- "@typescript-eslint/utils": "^8.12.2",
55
- "bumpp": "^9.8.0",
56
- "eslint": "^9.14.0",
55
+ "@typescript-eslint/typescript-estree": "^8.23.0",
56
+ "@typescript-eslint/utils": "^8.23.0",
57
+ "bumpp": "^10.0.2",
58
+ "eslint": "^9.20.0",
57
59
  "eslint-define-config": "^2.1.0",
58
- "eslint-vitest-rule-tester": "^0.6.1",
60
+ "eslint-vitest-rule-tester": "^1.1.0",
59
61
  "esno": "^4.8.0",
60
62
  "jsonc-eslint-parser": "^2.4.0",
61
- "lint-staged": "^15.2.10",
63
+ "lint-staged": "^15.4.3",
62
64
  "lodash.merge": "4.6.2",
63
- "pnpm": "^9.12.3",
64
- "semver": "^7.6.3",
65
+ "pnpm": "^10.2.1",
66
+ "semver": "^7.7.1",
65
67
  "simple-git-hooks": "^2.11.1",
66
- "tsup": "^8.3.5",
68
+ "tsup": "^8.3.6",
67
69
  "tsx": "^4.19.2",
68
- "typescript": "^5.6.3",
69
- "unbuild": "^2.0.0",
70
- "vite": "^5.4.10",
71
- "vitest": "^2.1.4"
70
+ "typescript": "^5.7.3",
71
+ "unbuild": "^3.3.1",
72
+ "vite": "^6.1.0",
73
+ "vitest": "^3.0.5"
72
74
  },
73
75
  "resolutions": {
74
76
  "eslint-plugin-kirklin": "workspace:*"
package/dist/index.cjs DELETED
@@ -1,837 +0,0 @@
1
- 'use strict';
2
-
3
- const version = "1.5.0";
4
-
5
- const hasDocs = [
6
- "consistent-chaining",
7
- "consistent-list-newline",
8
- "curly",
9
- "if-newline",
10
- "import-dedupe",
11
- "indent-unindent",
12
- "top-level-function"
13
- ];
14
- const blobUrl = "https://github.com/kirklin/eslint-plugin-kirklin/blob/main/src/rules/";
15
- function RuleCreator(urlCreator) {
16
- return function createNamedRule({
17
- name,
18
- meta,
19
- ...rule
20
- }) {
21
- return createRule({
22
- meta: {
23
- ...meta,
24
- docs: {
25
- ...meta.docs,
26
- url: urlCreator(name)
27
- }
28
- },
29
- ...rule
30
- });
31
- };
32
- }
33
- function createRule({
34
- create,
35
- defaultOptions,
36
- meta
37
- }) {
38
- return {
39
- create: (context) => {
40
- const optionsWithDefault = context.options.map((options, index) => {
41
- return {
42
- ...defaultOptions[index] || {},
43
- ...options || {}
44
- };
45
- });
46
- return create(context, optionsWithDefault);
47
- },
48
- defaultOptions,
49
- meta
50
- };
51
- }
52
- const createEslintRule = RuleCreator(
53
- (ruleName) => hasDocs.includes(ruleName) ? `${blobUrl}${ruleName}.md` : `${blobUrl}${ruleName}.test.ts`
54
- );
55
- const _reFullWs = /^\s*$/;
56
- function unindent(str) {
57
- const lines = (typeof str === "string" ? str : str[0]).split("\n");
58
- const whitespaceLines = lines.map((line) => _reFullWs.test(line));
59
- const commonIndent = lines.reduce((min, line, idx) => {
60
- if (whitespaceLines[idx]) {
61
- return min;
62
- }
63
- const indent = line.match(/^\s*/)?.[0].length;
64
- return indent === void 0 ? min : Math.min(min, indent);
65
- }, Number.POSITIVE_INFINITY);
66
- let emptyLinesHead = 0;
67
- while (emptyLinesHead < lines.length && whitespaceLines[emptyLinesHead]) {
68
- emptyLinesHead++;
69
- }
70
- let emptyLinesTail = 0;
71
- while (emptyLinesTail < lines.length && whitespaceLines[lines.length - emptyLinesTail - 1]) {
72
- emptyLinesTail++;
73
- }
74
- return lines.slice(emptyLinesHead, lines.length - emptyLinesTail).map((line) => line.slice(commonIndent)).join("\n");
75
- }
76
-
77
- const RULE_NAME$8 = "consistent-chaining";
78
- const consistentChaining = createEslintRule({
79
- name: RULE_NAME$8,
80
- meta: {
81
- type: "layout",
82
- docs: {
83
- description: "Having line breaks styles to object, array and named imports"
84
- },
85
- fixable: "whitespace",
86
- schema: [
87
- {
88
- type: "object",
89
- properties: {
90
- allowLeadingPropertyAccess: {
91
- type: "boolean",
92
- description: "Allow leading property access to be on the same line",
93
- default: true
94
- }
95
- },
96
- additionalProperties: false
97
- }
98
- ],
99
- messages: {
100
- shouldWrap: "Should have line breaks between items, in node {{name}}",
101
- shouldNotWrap: "Should not have line breaks between items, in node {{name}}"
102
- }
103
- },
104
- defaultOptions: [
105
- {
106
- allowLeadingPropertyAccess: true
107
- }
108
- ],
109
- create: (context) => {
110
- const knownRoot = /* @__PURE__ */ new WeakSet();
111
- const {
112
- allowLeadingPropertyAccess = true
113
- } = context.options[0] || {};
114
- return {
115
- MemberExpression(node) {
116
- let root = node;
117
- while (root.parent && (root.parent.type === "MemberExpression" || root.parent.type === "CallExpression")) {
118
- root = root.parent;
119
- }
120
- if (knownRoot.has(root)) {
121
- return;
122
- }
123
- knownRoot.add(root);
124
- const members = [];
125
- let current = root;
126
- while (current) {
127
- switch (current.type) {
128
- case "MemberExpression": {
129
- if (!current.computed) {
130
- members.unshift(current);
131
- }
132
- current = current.object;
133
- break;
134
- }
135
- case "CallExpression": {
136
- current = current.callee;
137
- break;
138
- }
139
- default: {
140
- current = void 0;
141
- break;
142
- }
143
- }
144
- }
145
- let leadingPropertyAcccess = allowLeadingPropertyAccess;
146
- let mode = null;
147
- members.forEach((m) => {
148
- const token = context.sourceCode.getTokenBefore(m.property);
149
- const tokenBefore = context.sourceCode.getTokenBefore(token);
150
- const currentMode = token.loc.start.line === tokenBefore.loc.end.line ? "single" : "multi";
151
- if (leadingPropertyAcccess && (m.object.type === "ThisExpression" || m.object.type === "Identifier" || m.object.type === "MemberExpression" || m.object.type === "Literal") && currentMode === "single") {
152
- return;
153
- }
154
- leadingPropertyAcccess = false;
155
- if (mode == null) {
156
- mode = currentMode;
157
- return;
158
- }
159
- if (mode !== currentMode) {
160
- context.report({
161
- messageId: mode === "single" ? "shouldNotWrap" : "shouldWrap",
162
- loc: token.loc,
163
- data: {
164
- name: root.type
165
- },
166
- fix(fixer) {
167
- if (mode === "multi") {
168
- return fixer.insertTextAfter(tokenBefore, "\n");
169
- } else {
170
- return fixer.removeRange([tokenBefore.range[1], token.range[0]]);
171
- }
172
- }
173
- });
174
- }
175
- });
176
- }
177
- };
178
- }
179
- });
180
-
181
- const RULE_NAME$7 = "consistent-list-newline";
182
- const consistentListNewline = createEslintRule({
183
- name: RULE_NAME$7,
184
- meta: {
185
- type: "layout",
186
- docs: {
187
- description: "Having line breaks styles to object, array and named imports"
188
- },
189
- fixable: "whitespace",
190
- schema: [{
191
- type: "object",
192
- properties: {
193
- ArrayExpression: { type: "boolean" },
194
- ArrayPattern: { type: "boolean" },
195
- ArrowFunctionExpression: { type: "boolean" },
196
- CallExpression: { type: "boolean" },
197
- ExportNamedDeclaration: { type: "boolean" },
198
- FunctionDeclaration: { type: "boolean" },
199
- FunctionExpression: { type: "boolean" },
200
- ImportDeclaration: { type: "boolean" },
201
- JSONArrayExpression: { type: "boolean" },
202
- JSONObjectExpression: { type: "boolean" },
203
- JSXOpeningElement: { type: "boolean" },
204
- NewExpression: { type: "boolean" },
205
- ObjectExpression: { type: "boolean" },
206
- ObjectPattern: { type: "boolean" },
207
- TSFunctionType: { type: "boolean" },
208
- TSInterfaceDeclaration: { type: "boolean" },
209
- TSTupleType: { type: "boolean" },
210
- TSTypeLiteral: { type: "boolean" },
211
- TSTypeParameterDeclaration: { type: "boolean" },
212
- TSTypeParameterInstantiation: { type: "boolean" }
213
- },
214
- additionalProperties: false
215
- }],
216
- messages: {
217
- shouldWrap: "Should have line breaks between items, in node {{name}}",
218
- shouldNotWrap: "Should not have line breaks between items, in node {{name}}"
219
- }
220
- },
221
- defaultOptions: [{}],
222
- create: (context, [options = {}] = [{}]) => {
223
- function removeLines(fixer, start, end, delimiter) {
224
- const range = [start, end];
225
- const code = context.sourceCode.text.slice(...range);
226
- return fixer.replaceTextRange(range, code.replace(/(\r\n|\n)/g, delimiter ?? ""));
227
- }
228
- function getDelimiter(root, current) {
229
- if (root.type !== "TSInterfaceDeclaration" && root.type !== "TSTypeLiteral") {
230
- return;
231
- }
232
- const currentContent = context.sourceCode.text.slice(current.range[0], current.range[1]);
233
- return currentContent.match(/(?:,|;)$/) ? void 0 : ",";
234
- }
235
- function hasComments(current) {
236
- let program = current;
237
- while (program.type !== "Program") {
238
- program = program.parent;
239
- }
240
- const currentRange = current.range;
241
- return !!program.comments?.some((comment) => {
242
- const commentRange = comment.range;
243
- return commentRange[0] > currentRange[0] && commentRange[1] < currentRange[1];
244
- });
245
- }
246
- function check(node, children, nextNode) {
247
- const items = children.filter(Boolean);
248
- if (items.length === 0) {
249
- return;
250
- }
251
- let startToken = ["CallExpression", "NewExpression"].includes(node.type) ? void 0 : context.sourceCode.getFirstToken(node);
252
- if (node.type === "CallExpression") {
253
- startToken = context.sourceCode.getTokenAfter(
254
- node.typeArguments ? node.typeArguments : node.callee.type === "MemberExpression" ? node.callee.property : node.callee
255
- );
256
- }
257
- if (startToken?.type !== "Punctuator") {
258
- startToken = context.sourceCode.getTokenBefore(items[0]);
259
- }
260
- const endToken = context.sourceCode.getTokenAfter(items[items.length - 1]);
261
- const startLine = startToken.loc.start.line;
262
- if (startToken.loc.start.line === endToken.loc.end.line) {
263
- return;
264
- }
265
- let mode = null;
266
- let lastLine = startLine;
267
- items.forEach((item, idx) => {
268
- if (mode == null) {
269
- mode = item.loc.start.line === lastLine ? "inline" : "newline";
270
- lastLine = item.loc.end.line;
271
- return;
272
- }
273
- const currentStart = item.loc.start.line;
274
- if (mode === "newline" && currentStart === lastLine) {
275
- context.report({
276
- node: item,
277
- messageId: "shouldWrap",
278
- data: {
279
- name: node.type
280
- },
281
- *fix(fixer) {
282
- yield fixer.insertTextBefore(item, "\n");
283
- }
284
- });
285
- } else if (mode === "inline" && currentStart !== lastLine) {
286
- const lastItem2 = items[idx - 1];
287
- if (context.sourceCode.getCommentsBefore(item).length > 0) {
288
- return;
289
- }
290
- const content = context.sourceCode.text.slice(lastItem2.range[1], item.range[0]);
291
- if (content.includes("\n")) {
292
- context.report({
293
- node: item,
294
- messageId: "shouldNotWrap",
295
- data: {
296
- name: node.type
297
- },
298
- *fix(fixer) {
299
- yield removeLines(fixer, lastItem2.range[1], item.range[0], getDelimiter(node, lastItem2));
300
- }
301
- });
302
- }
303
- }
304
- lastLine = item.loc.end.line;
305
- });
306
- const endRange = nextNode ? Math.min(
307
- context.sourceCode.getTokenBefore(nextNode).range[0],
308
- node.range[1]
309
- ) : node.range[1];
310
- const endLoc = context.sourceCode.getLocFromIndex(endRange);
311
- const lastItem = items[items.length - 1];
312
- if (mode === "newline" && endLoc.line === lastLine) {
313
- context.report({
314
- node: lastItem,
315
- messageId: "shouldWrap",
316
- data: {
317
- name: node.type
318
- },
319
- *fix(fixer) {
320
- yield fixer.insertTextAfter(lastItem, "\n");
321
- }
322
- });
323
- } else if (mode === "inline" && endLoc.line !== lastLine) {
324
- if (items.length === 1 && items[0].loc.start.line !== items[1]?.loc.start.line) {
325
- return;
326
- }
327
- if (context.sourceCode.getCommentsAfter(lastItem).length > 0) {
328
- return;
329
- }
330
- const content = context.sourceCode.text.slice(lastItem.range[1], endRange);
331
- if (content.includes("\n")) {
332
- context.report({
333
- node: lastItem,
334
- messageId: "shouldNotWrap",
335
- data: {
336
- name: node.type
337
- },
338
- *fix(fixer) {
339
- yield removeLines(fixer, lastItem.range[1], endRange, getDelimiter(node, lastItem));
340
- }
341
- });
342
- }
343
- }
344
- }
345
- const listenser = {
346
- ObjectExpression: (node) => {
347
- check(node, node.properties);
348
- },
349
- ArrayExpression: (node) => {
350
- check(node, node.elements);
351
- },
352
- ImportDeclaration: (node) => {
353
- check(
354
- node,
355
- node.specifiers[0]?.type === "ImportDefaultSpecifier" ? node.specifiers.slice(1) : node.specifiers
356
- );
357
- },
358
- ExportNamedDeclaration: (node) => {
359
- check(node, node.specifiers);
360
- },
361
- FunctionDeclaration: (node) => {
362
- check(
363
- node,
364
- node.params,
365
- node.returnType || node.body
366
- );
367
- },
368
- FunctionExpression: (node) => {
369
- check(
370
- node,
371
- node.params,
372
- node.returnType || node.body
373
- );
374
- },
375
- ArrowFunctionExpression: (node) => {
376
- if (node.params.length <= 1) {
377
- return;
378
- }
379
- check(
380
- node,
381
- node.params,
382
- node.returnType || node.body
383
- );
384
- },
385
- CallExpression: (node) => {
386
- check(node, node.arguments);
387
- },
388
- TSInterfaceDeclaration: (node) => {
389
- check(node, node.body.body);
390
- },
391
- TSTypeLiteral: (node) => {
392
- check(node, node.members);
393
- },
394
- TSTupleType: (node) => {
395
- check(node, node.elementTypes);
396
- },
397
- TSFunctionType: (node) => {
398
- check(node, node.params);
399
- },
400
- NewExpression: (node) => {
401
- check(node, node.arguments);
402
- },
403
- TSTypeParameterDeclaration(node) {
404
- check(node, node.params);
405
- },
406
- TSTypeParameterInstantiation(node) {
407
- check(node, node.params);
408
- },
409
- ObjectPattern(node) {
410
- check(node, node.properties, node.typeAnnotation);
411
- },
412
- ArrayPattern(node) {
413
- check(node, node.elements);
414
- },
415
- JSXOpeningElement(node) {
416
- if (node.attributes.some((attr) => attr.loc.start.line !== attr.loc.end.line)) {
417
- return;
418
- }
419
- check(node, node.attributes);
420
- },
421
- JSONArrayExpression(node) {
422
- if (hasComments(node)) {
423
- return;
424
- }
425
- check(node, node.elements);
426
- },
427
- JSONObjectExpression(node) {
428
- if (hasComments(node)) {
429
- return;
430
- }
431
- check(node, node.properties);
432
- }
433
- };
434
- Object.keys(options).forEach((key) => {
435
- if (options[key] === false) {
436
- delete listenser[key];
437
- }
438
- });
439
- return listenser;
440
- }
441
- });
442
-
443
- const RULE_NAME$6 = "if-newline";
444
- const ifNewline = createEslintRule({
445
- name: RULE_NAME$6,
446
- meta: {
447
- type: "layout",
448
- docs: {
449
- description: "Newline after if"
450
- },
451
- fixable: "whitespace",
452
- schema: [],
453
- messages: {
454
- missingIfNewline: "Expect newline after if"
455
- }
456
- },
457
- defaultOptions: [],
458
- create: (context) => {
459
- return {
460
- IfStatement(node) {
461
- if (!node.consequent) {
462
- return;
463
- }
464
- if (node.consequent.type === "BlockStatement") {
465
- return;
466
- }
467
- if (node.test.loc.end.line === node.consequent.loc.start.line) {
468
- context.report({
469
- node,
470
- loc: {
471
- start: node.test.loc.end,
472
- end: node.consequent.loc.start
473
- },
474
- messageId: "missingIfNewline",
475
- fix(fixer) {
476
- return fixer.replaceTextRange([node.consequent.range[0], node.consequent.range[0]], "\n");
477
- }
478
- });
479
- }
480
- }
481
- };
482
- }
483
- });
484
-
485
- const RULE_NAME$5 = "import-dedupe";
486
- const importDedupe = createEslintRule({
487
- name: RULE_NAME$5,
488
- meta: {
489
- type: "problem",
490
- docs: {
491
- description: "Fix duplication in imports"
492
- },
493
- fixable: "code",
494
- schema: [],
495
- messages: {
496
- importDedupe: "Expect no duplication in imports"
497
- }
498
- },
499
- defaultOptions: [],
500
- create: (context) => {
501
- return {
502
- ImportDeclaration(node) {
503
- if (node.specifiers.length <= 1) {
504
- return;
505
- }
506
- const names = /* @__PURE__ */ new Set();
507
- node.specifiers.forEach((n) => {
508
- const id = n.local.name;
509
- if (names.has(id)) {
510
- context.report({
511
- node,
512
- loc: {
513
- start: n.loc.end,
514
- end: n.loc.start
515
- },
516
- messageId: "importDedupe",
517
- fix(fixer) {
518
- const s = n.range[0];
519
- let e = n.range[1];
520
- if (context.getSourceCode().text[e] === ",") {
521
- e += 1;
522
- }
523
- return fixer.removeRange([s, e]);
524
- }
525
- });
526
- }
527
- names.add(id);
528
- });
529
- }
530
- };
531
- }
532
- });
533
-
534
- const indentUnindent = createEslintRule({
535
- name: "indent-unindent",
536
- meta: {
537
- type: "layout",
538
- docs: {
539
- description: "Enforce consistent indentation in `unindent` template tag"
540
- },
541
- fixable: "code",
542
- schema: [
543
- {
544
- type: "object",
545
- properties: {
546
- indent: {
547
- type: "number",
548
- minimum: 0,
549
- default: 2
550
- },
551
- tags: {
552
- type: "array",
553
- items: {
554
- type: "string"
555
- }
556
- }
557
- },
558
- additionalProperties: false
559
- }
560
- ],
561
- messages: {
562
- "indent-unindent": "Consistent indentation in unindent tag"
563
- }
564
- },
565
- defaultOptions: [{}],
566
- create(context) {
567
- const {
568
- tags = ["$", "unindent", "unIndent"],
569
- indent = 2
570
- } = context.options?.[0] ?? {};
571
- return {
572
- TaggedTemplateExpression(node) {
573
- const id = node.tag;
574
- if (!id || id.type !== "Identifier") {
575
- return;
576
- }
577
- if (!tags.includes(id.name)) {
578
- return;
579
- }
580
- if (node.quasi.quasis.length !== 1) {
581
- return;
582
- }
583
- const quasi = node.quasi.quasis[0];
584
- const value = quasi.value.raw;
585
- const lineStartIndex = context.sourceCode.getIndexFromLoc({
586
- line: node.loc.start.line,
587
- column: 0
588
- });
589
- const baseIndent = context.sourceCode.text.slice(lineStartIndex).match(/^\s*/)?.[0] ?? "";
590
- const targetIndent = baseIndent + " ".repeat(indent);
591
- const pure = unindent([value]);
592
- let final = pure.split("\n").map((line) => targetIndent + line).join("\n");
593
- final = `
594
- ${final}
595
- ${baseIndent}`;
596
- if (final !== value) {
597
- context.report({
598
- node: quasi,
599
- messageId: "indent-unindent",
600
- fix: (fixer) => fixer.replaceText(quasi, `\`${final}\``)
601
- });
602
- }
603
- }
604
- };
605
- }
606
- });
607
-
608
- const RULE_NAME$4 = "no-import-dist";
609
- const noImportDist = createEslintRule({
610
- name: RULE_NAME$4,
611
- meta: {
612
- type: "problem",
613
- docs: {
614
- description: "Prevent importing modules in `dist` folder"
615
- },
616
- schema: [],
617
- messages: {
618
- noImportDist: "Do not import modules in `dist` folder, got {{path}}"
619
- }
620
- },
621
- defaultOptions: [],
622
- create: (context) => {
623
- function isDist(path) {
624
- return Boolean(path.startsWith(".") && path.match(/\/dist(\/|$)/)) || path === "dist";
625
- }
626
- return {
627
- ImportDeclaration: (node) => {
628
- if (isDist(node.source.value)) {
629
- context.report({
630
- node,
631
- messageId: "noImportDist",
632
- data: {
633
- path: node.source.value
634
- }
635
- });
636
- }
637
- }
638
- };
639
- }
640
- });
641
-
642
- const RULE_NAME$3 = "no-import-node-modules-by-path";
643
- const noImportNodeModulesByPath = createEslintRule({
644
- name: RULE_NAME$3,
645
- meta: {
646
- type: "problem",
647
- docs: {
648
- description: "Prevent importing modules in `node_modules` folder by relative or absolute path"
649
- },
650
- schema: [],
651
- messages: {
652
- noImportNodeModulesByPath: "Do not import modules in `node_modules` folder by path"
653
- }
654
- },
655
- defaultOptions: [],
656
- create: (context) => {
657
- return {
658
- "ImportDeclaration": (node) => {
659
- if (node.source.value.includes("/node_modules/")) {
660
- context.report({
661
- node,
662
- messageId: "noImportNodeModulesByPath"
663
- });
664
- }
665
- },
666
- 'CallExpression[callee.name="require"]': (node) => {
667
- const value = node.arguments[0]?.value;
668
- if (typeof value === "string" && value.includes("/node_modules/")) {
669
- context.report({
670
- node,
671
- messageId: "noImportNodeModulesByPath"
672
- });
673
- }
674
- }
675
- };
676
- }
677
- });
678
-
679
- const RULE_NAME$2 = "no-top-level-await";
680
- const noTopLevelAwait = createEslintRule({
681
- name: RULE_NAME$2,
682
- meta: {
683
- type: "problem",
684
- docs: {
685
- description: "Prevent using top-level await"
686
- },
687
- schema: [],
688
- messages: {
689
- NoTopLevelAwait: "Do not use top-level await"
690
- }
691
- },
692
- defaultOptions: [],
693
- create: (context) => {
694
- return {
695
- AwaitExpression: (node) => {
696
- let parent = node.parent;
697
- while (parent) {
698
- if (parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression" || parent.type === "ArrowFunctionExpression") {
699
- return;
700
- }
701
- parent = parent.parent;
702
- }
703
- context.report({
704
- node,
705
- messageId: "NoTopLevelAwait"
706
- });
707
- }
708
- };
709
- }
710
- });
711
-
712
- const RULE_NAME$1 = "no-ts-export-equal";
713
- const noTsExportEqual = createEslintRule({
714
- name: RULE_NAME$1,
715
- meta: {
716
- type: "problem",
717
- docs: {
718
- description: "Do not use `exports =`"
719
- },
720
- schema: [],
721
- messages: {
722
- noTsExportEqual: "Use ESM `export default` instead"
723
- }
724
- },
725
- defaultOptions: [],
726
- create: (context) => {
727
- const extension = context.getFilename().split(".").pop();
728
- if (!extension) {
729
- return {};
730
- }
731
- if (!["ts", "tsx", "mts", "cts"].includes(extension)) {
732
- return {};
733
- }
734
- return {
735
- TSExportAssignment(node) {
736
- context.report({
737
- node,
738
- messageId: "noTsExportEqual"
739
- });
740
- }
741
- };
742
- }
743
- });
744
-
745
- const RULE_NAME = "top-level-function";
746
- const topLevelFunction = createEslintRule({
747
- name: RULE_NAME,
748
- meta: {
749
- type: "problem",
750
- docs: {
751
- description: "Enforce top-level functions to be declared with function keyword"
752
- },
753
- fixable: "code",
754
- schema: [],
755
- messages: {
756
- topLevelFunctionDeclaration: "Top-level functions should be declared with function keyword"
757
- }
758
- },
759
- defaultOptions: [],
760
- create: (context) => {
761
- return {
762
- VariableDeclaration(node) {
763
- if (node.parent.type !== "Program" && node.parent.type !== "ExportNamedDeclaration") {
764
- return;
765
- }
766
- if (node.declarations.length !== 1) {
767
- return;
768
- }
769
- if (node.kind !== "const") {
770
- return;
771
- }
772
- if (node.declare) {
773
- return;
774
- }
775
- const declaration = node.declarations[0];
776
- if (declaration.init?.type !== "ArrowFunctionExpression") {
777
- return;
778
- }
779
- if (declaration.id?.type !== "Identifier") {
780
- return;
781
- }
782
- if (declaration.id.typeAnnotation) {
783
- return;
784
- }
785
- if (declaration.init.body.type !== "BlockStatement" && declaration.id?.loc.start.line === declaration.init?.body.loc.end.line) {
786
- return;
787
- }
788
- const arrowFn = declaration.init;
789
- const body = declaration.init.body;
790
- const id = declaration.id;
791
- context.report({
792
- node,
793
- loc: {
794
- start: id.loc.start,
795
- end: body.loc.start
796
- },
797
- messageId: "topLevelFunctionDeclaration",
798
- fix(fixer) {
799
- const code = context.getSourceCode().text;
800
- const textName = code.slice(id.range[0], id.range[1]);
801
- const textArgs = arrowFn.params.length ? code.slice(arrowFn.params[0].range[0], arrowFn.params[arrowFn.params.length - 1].range[1]) : "";
802
- const textBody = body.type === "BlockStatement" ? code.slice(body.range[0], body.range[1]) : `{
803
- return ${code.slice(body.range[0], body.range[1])}
804
- }`;
805
- const textGeneric = arrowFn.typeParameters ? code.slice(arrowFn.typeParameters.range[0], arrowFn.typeParameters.range[1]) : "";
806
- const textTypeReturn = arrowFn.returnType ? code.slice(arrowFn.returnType.range[0], arrowFn.returnType.range[1]) : "";
807
- const textAsync = arrowFn.async ? "async " : "";
808
- const final = `${textAsync}function ${textName} ${textGeneric}(${textArgs})${textTypeReturn} ${textBody}`;
809
- return fixer.replaceTextRange([node.range[0], node.range[1]], final);
810
- }
811
- });
812
- }
813
- };
814
- }
815
- });
816
-
817
- const plugin = {
818
- meta: {
819
- name: "kirklin",
820
- version
821
- },
822
- // @keep-sorted
823
- rules: {
824
- "consistent-chaining": consistentChaining,
825
- "consistent-list-newline": consistentListNewline,
826
- "if-newline": ifNewline,
827
- "import-dedupe": importDedupe,
828
- "indent-unindent": indentUnindent,
829
- "no-import-dist": noImportDist,
830
- "no-import-node-modules-by-path": noImportNodeModulesByPath,
831
- "no-top-level-await": noTopLevelAwait,
832
- "no-ts-export-equal": noTsExportEqual,
833
- "top-level-function": topLevelFunction
834
- }
835
- };
836
-
837
- module.exports = plugin;
package/dist/index.d.cts DELETED
@@ -1,72 +0,0 @@
1
- import { Rule, Linter } from 'eslint';
2
-
3
- interface RuleModule<T extends readonly unknown[]> extends Rule.RuleModule {
4
- defaultOptions: T;
5
- }
6
-
7
- type Options$2 = [
8
- {
9
- indent?: number;
10
- tags?: string[];
11
- }
12
- ];
13
-
14
- type Options$1 = [
15
- {
16
- ArrayExpression?: boolean;
17
- ArrayPattern?: boolean;
18
- ArrowFunctionExpression?: boolean;
19
- CallExpression?: boolean;
20
- ExportNamedDeclaration?: boolean;
21
- FunctionDeclaration?: boolean;
22
- FunctionExpression?: boolean;
23
- ImportDeclaration?: boolean;
24
- JSONArrayExpression?: boolean;
25
- JSONObjectExpression?: boolean;
26
- JSXOpeningElement?: boolean;
27
- NewExpression?: boolean;
28
- ObjectExpression?: boolean;
29
- ObjectPattern?: boolean;
30
- TSFunctionType?: boolean;
31
- TSInterfaceDeclaration?: boolean;
32
- TSTupleType?: boolean;
33
- TSTypeLiteral?: boolean;
34
- TSTypeParameterDeclaration?: boolean;
35
- TSTypeParameterInstantiation?: boolean;
36
- }
37
- ];
38
-
39
- type Options = [
40
- {
41
- allowLeadingPropertyAccess?: boolean;
42
- }
43
- ];
44
-
45
- declare const plugin: {
46
- meta: {
47
- name: string;
48
- version: string;
49
- };
50
- rules: {
51
- "consistent-chaining": RuleModule<Options>;
52
- "consistent-list-newline": RuleModule<Options$1>;
53
- "if-newline": RuleModule<[]>;
54
- "import-dedupe": RuleModule<[]>;
55
- "indent-unindent": RuleModule<Options$2>;
56
- "no-import-dist": RuleModule<[]>;
57
- "no-import-node-modules-by-path": RuleModule<[]>;
58
- "no-top-level-await": RuleModule<[]>;
59
- "no-ts-export-equal": RuleModule<[]>;
60
- "top-level-function": RuleModule<[]>;
61
- };
62
- };
63
-
64
- type RuleDefinitions = typeof plugin["rules"];
65
- type RuleOptions = {
66
- [K in keyof RuleDefinitions]: RuleDefinitions[K]["defaultOptions"];
67
- };
68
- type Rules = {
69
- [K in keyof RuleOptions]: Linter.RuleEntry<RuleOptions[K]>;
70
- };
71
-
72
- export { type RuleOptions, type Rules, plugin as default };