@so1ve/eslint-plugin 3.20.2 → 3.22.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.d.ts CHANGED
@@ -1,40 +1,54 @@
1
1
  import { ESLintUtils } from "@typescript-eslint/utils";
2
2
 
3
3
  //#region src/rules/function-style.d.ts
4
- type MessageIds$9 = "arrow" | "declaration";
5
- declare const rule: ESLintUtils.RuleModule<MessageIds$9>;
4
+ type MessageIds$10 = "arrow" | "declaration";
5
+ type Options$10 = [];
6
+ declare const rule$10: ESLintUtils.RuleModule<MessageIds$10, Options$10>;
7
+ //#endregion
8
+ //#region src/rules/html-spaced-comment.d.ts
9
+ type MessageIds$9 = "expectedSpaceBefore" | "expectedSpaceAfter";
10
+ type Options$9 = [];
11
+ declare const rule$9: ESLintUtils.RuleModule<MessageIds$9, Options$9>;
6
12
  //#endregion
7
13
  //#region src/rules/import-dedupe.d.ts
8
14
  type MessageIds$8 = "importDedupe";
9
- declare const rule$1: ESLintUtils.RuleModule<MessageIds$8>;
15
+ type Options$8 = [];
16
+ declare const rule$8: ESLintUtils.RuleModule<MessageIds$8, Options$8>;
17
+ //#endregion
18
+ //#region src/rules/import-export-newline.d.ts
19
+ type MessageIds$7 = "newlineAfterLastImport" | "newlineBeforeExport" | "newlineAfterExport";
20
+ type Options$7 = [];
21
+ declare const rule$7: ESLintUtils.RuleModule<MessageIds$7, Options$7>;
10
22
  //#endregion
11
23
  //#region src/rules/no-import-promises-as.d.ts
12
- type MessageIds$7 = "noImportPromisesAs";
13
- declare const rule$2: ESLintUtils.RuleModule<MessageIds$7>;
24
+ type MessageIds$6 = "noImportPromisesAs";
25
+ type Options$6 = [];
26
+ declare const rule$6: ESLintUtils.RuleModule<MessageIds$6, Options$6>;
14
27
  //#endregion
15
28
  //#region src/rules/no-inline-type-import.d.ts
16
- type MessageIds$6 = "noInlineTypeImport";
17
- declare const rule$3: ESLintUtils.RuleModule<MessageIds$6>;
29
+ type MessageIds$5 = "noInlineTypeImport";
30
+ type Options$5 = [];
31
+ declare const rule$5: ESLintUtils.RuleModule<MessageIds$5, Options$5>;
18
32
  //#endregion
19
33
  //#region src/rules/no-negated-comparison.d.ts
20
- type MessageIds$5 = "noNegatedComparison";
21
- declare const rule$4: ESLintUtils.RuleModule<MessageIds$5>;
34
+ type MessageIds$4 = "noNegatedComparison";
35
+ type Options$4 = [];
36
+ declare const rule$4: ESLintUtils.RuleModule<MessageIds$4, Options$4>;
22
37
  //#endregion
23
38
  //#region src/rules/no-useless-template-string.d.ts
24
- type MessageIds$4 = "noUselessTemplateString";
25
- declare const rule$5: ESLintUtils.RuleModule<MessageIds$4>;
26
- //#endregion
27
- //#region src/rules/pad-after-last-import.d.ts
28
- type MessageIds$3 = "padAfterLastImport";
29
- declare const rule$6: ESLintUtils.RuleModule<MessageIds$3>;
39
+ type MessageIds$3 = "noUselessTemplateString";
40
+ type Options$3 = [];
41
+ declare const rule$3: ESLintUtils.RuleModule<MessageIds$3, Options$3>;
30
42
  //#endregion
31
43
  //#region src/rules/prefer-ts-expect-error.d.ts
32
44
  type MessageIds$2 = "preferExpectErrorComment";
33
- declare const rule$7: ESLintUtils.RuleModule<MessageIds$2>;
45
+ type Options$2 = [];
46
+ declare const rule$2: ESLintUtils.RuleModule<MessageIds$2, Options$2>;
34
47
  //#endregion
35
48
  //#region src/rules/require-async-with-await.d.ts
36
49
  type MessageIds$1 = "requireAsyncWithAwait";
37
- declare const rule$8: ESLintUtils.RuleModule<MessageIds$1>;
50
+ type Options$1 = [];
51
+ declare const rule$1: ESLintUtils.RuleModule<MessageIds$1, Options$1>;
38
52
  //#endregion
39
53
  //#region src/rules/vue-root-element-sort-attributes.d.ts
40
54
  type MessageIds = "wrongOrder";
@@ -42,21 +56,22 @@ type Options = [{
42
56
  script?: string[];
43
57
  [otherElement: string]: string[] | undefined;
44
58
  }];
45
- declare const rule$9: ESLintUtils.RuleModule<MessageIds, Options>;
59
+ declare const rule: ESLintUtils.RuleModule<MessageIds, Options>;
46
60
  //#endregion
47
61
  //#region src/index.d.ts
48
62
  declare const _default: {
49
63
  rules: {
50
- "function-style": typeof rule;
51
- "import-dedupe": typeof rule$1;
52
- "no-import-promises-as": typeof rule$2;
53
- "no-inline-type-import": typeof rule$3;
64
+ "function-style": typeof rule$10;
65
+ "html-spaced-comment": typeof rule$9;
66
+ "import-dedupe": typeof rule$8;
67
+ "import-export-newline": typeof rule$7;
68
+ "no-import-promises-as": typeof rule$6;
69
+ "no-inline-type-import": typeof rule$5;
54
70
  "no-negated-comparison": typeof rule$4;
55
- "no-useless-template-string": typeof rule$5;
56
- "pad-after-last-import": typeof rule$6;
57
- "prefer-ts-expect-error": typeof rule$7;
58
- "require-async-with-await": typeof rule$8;
59
- "vue-root-element-sort-attributes": typeof rule$9;
71
+ "no-useless-template-string": typeof rule$3;
72
+ "prefer-ts-expect-error": typeof rule$2;
73
+ "require-async-with-await": typeof rule$1;
74
+ "vue-root-element-sort-attributes": typeof rule;
60
75
  };
61
76
  };
62
77
  //#endregion
package/dist/index.js CHANGED
@@ -16,9 +16,9 @@ function getPreviousNode(node) {
16
16
 
17
17
  //#endregion
18
18
  //#region src/rules/function-style.ts
19
- const RULE_NAME$8 = "function-style";
20
- const rule$9 = createEslintRule({
21
- name: RULE_NAME$8,
19
+ const RULE_NAME$9 = "function-style";
20
+ const rule$10 = createEslintRule({
21
+ name: RULE_NAME$9,
22
22
  meta: {
23
23
  type: "problem",
24
24
  docs: { description: "Enforce function style." },
@@ -153,7 +153,45 @@ const rule$9 = createEslintRule({
153
153
  };
154
154
  }
155
155
  });
156
- var function_style_default = rule$9;
156
+ var function_style_default = rule$10;
157
+
158
+ //#endregion
159
+ //#region src/rules/html-spaced-comment.ts
160
+ const RULE_NAME$8 = "html-spaced-comment";
161
+ const rule$9 = createEslintRule({
162
+ name: RULE_NAME$8,
163
+ meta: {
164
+ type: "layout",
165
+ docs: { description: "Enforce consistent spacing in HTML comments" },
166
+ fixable: "whitespace",
167
+ schema: [],
168
+ messages: {
169
+ expectedSpaceBefore: "Expected space after '<!--'.",
170
+ expectedSpaceAfter: "Expected space before '-->'."
171
+ }
172
+ },
173
+ defaultOptions: [],
174
+ create: (context) => ({ Comment(node) {
175
+ if (node.value?.type !== "CommentContent") return;
176
+ const rawValue = node.value.value;
177
+ if (rawValue.trim().length === 0) return;
178
+ if (!rawValue.startsWith(" ")) context.report({
179
+ node: node.value,
180
+ messageId: "expectedSpaceBefore",
181
+ fix(fixer) {
182
+ return fixer.insertTextBefore(node.value, " ");
183
+ }
184
+ });
185
+ if (!rawValue.endsWith(" ")) context.report({
186
+ node: node.value,
187
+ messageId: "expectedSpaceAfter",
188
+ fix(fixer) {
189
+ return fixer.insertTextAfter(node.value, " ");
190
+ }
191
+ });
192
+ } })
193
+ });
194
+ var html_spaced_comment_default = rule$9;
157
195
 
158
196
  //#endregion
159
197
  //#region src/rules/import-dedupe.ts
@@ -194,17 +232,101 @@ const rule$8 = createEslintRule({
194
232
  });
195
233
  var import_dedupe_default = rule$8;
196
234
 
235
+ //#endregion
236
+ //#region src/rules/import-export-newline.ts
237
+ const RULE_NAME$6 = "import-export-newline";
238
+ const isExportDeclaration = (node) => node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration" || node.type === "ExportAllDeclaration";
239
+ function getSiblingNode(node, direction) {
240
+ const parent = node.parent;
241
+ if (!parent || !("body" in parent) || !Array.isArray(parent.body)) return null;
242
+ const body = parent.body;
243
+ const index = body.indexOf(node);
244
+ if (index === -1) return null;
245
+ return direction === "prev" ? index > 0 ? body[index - 1] : null : index < body.length - 1 ? body[index + 1] : null;
246
+ }
247
+ const rule$7 = createEslintRule({
248
+ name: RULE_NAME$6,
249
+ meta: {
250
+ type: "problem",
251
+ docs: { description: "Enforce spacing between imports and exports." },
252
+ fixable: "code",
253
+ schema: [],
254
+ messages: {
255
+ newlineAfterLastImport: "Expected a blank line after the last import.",
256
+ newlineBeforeExport: "Expected a blank line before the export.",
257
+ newlineAfterExport: "Expected a blank line after the export."
258
+ }
259
+ },
260
+ defaultOptions: [],
261
+ create: (context) => {
262
+ const sourceCode = context.sourceCode;
263
+ let lastImportNode = null;
264
+ const exportNodes = [];
265
+ function checkExport(node) {
266
+ exportNodes.push(node);
267
+ }
268
+ function shouldHaveNewline(node, direction) {
269
+ let token;
270
+ let commentLine;
271
+ let expectedLine;
272
+ let tokenLine;
273
+ if (direction === "after") {
274
+ token = sourceCode.getTokenAfter(node);
275
+ commentLine = sourceCode.getCommentsAfter(node)[0]?.loc.start.line;
276
+ expectedLine = node.loc.end.line + 1;
277
+ tokenLine = token?.loc.start.line;
278
+ } else {
279
+ token = sourceCode.getTokenBefore(node);
280
+ commentLine = sourceCode.getCommentsBefore(node)[0]?.loc.end.line;
281
+ expectedLine = node.loc.start.line - 1;
282
+ tokenLine = token?.loc.end.line;
283
+ }
284
+ return token && (expectedLine === tokenLine || expectedLine === commentLine) && (direction === "after" ? token.value !== "}" && token.value !== "<\/script>" : true);
285
+ }
286
+ return {
287
+ ImportDeclaration(node) {
288
+ lastImportNode = node;
289
+ },
290
+ ExportNamedDeclaration: checkExport,
291
+ ExportDefaultDeclaration: checkExport,
292
+ ExportAllDeclaration: checkExport,
293
+ "Program:exit"() {
294
+ if (lastImportNode && shouldHaveNewline(lastImportNode, "after")) context.report({
295
+ node: lastImportNode,
296
+ messageId: "newlineAfterLastImport",
297
+ fix: (fixer) => fixer.insertTextAfter(lastImportNode, "\n")
298
+ });
299
+ for (const node of exportNodes) {
300
+ const prevNode = getSiblingNode(node, "prev");
301
+ if ((!prevNode || !isExportDeclaration(prevNode)) && !(lastImportNode && prevNode === lastImportNode) && shouldHaveNewline(node, "before")) context.report({
302
+ node,
303
+ messageId: "newlineBeforeExport",
304
+ fix: (fixer) => fixer.insertTextBefore(node, "\n")
305
+ });
306
+ const nextNode = getSiblingNode(node, "next");
307
+ if (nextNode && !isExportDeclaration(nextNode) && shouldHaveNewline(node, "after")) context.report({
308
+ node,
309
+ messageId: "newlineAfterExport",
310
+ fix: (fixer) => fixer.insertTextAfter(node, "\n")
311
+ });
312
+ }
313
+ }
314
+ };
315
+ }
316
+ });
317
+ var import_export_newline_default = rule$7;
318
+
197
319
  //#endregion
198
320
  //#region src/rules/no-import-promises-as.ts
199
- const RULE_NAME$6 = "no-import-promises-as";
321
+ const RULE_NAME$5 = "no-import-promises-as";
200
322
  const POSSIBLE_IMPORT_SOURCES = [
201
323
  "dns",
202
324
  "fs",
203
325
  "readline",
204
326
  "stream"
205
327
  ].flatMap((s) => [s, `node:${s}`]);
206
- const rule$7 = createEslintRule({
207
- name: RULE_NAME$6,
328
+ const rule$6 = createEslintRule({
329
+ name: RULE_NAME$5,
208
330
  meta: {
209
331
  type: "problem",
210
332
  docs: { description: "Disallow import promises as." },
@@ -234,13 +356,39 @@ const rule$7 = createEslintRule({
234
356
  } };
235
357
  }
236
358
  });
237
- var no_import_promises_as_default = rule$7;
359
+ var no_import_promises_as_default = rule$6;
238
360
 
239
361
  //#endregion
240
362
  //#region src/rules/no-inline-type-import.ts
241
- const RULE_NAME$5 = "no-inline-type-import";
242
- const rule$6 = createEslintRule({
243
- name: RULE_NAME$5,
363
+ const RULE_NAME$4 = "no-inline-type-import";
364
+ function generateImportsText(specifiers) {
365
+ let text = "{ ";
366
+ const texts = [];
367
+ for (const s of specifiers) {
368
+ const importedName = s.imported.type === AST_NODE_TYPES.Identifier ? s.imported.name : s.imported.raw;
369
+ texts.push(importedName === s.local.name ? s.local.name : `${importedName} as ${s.local.name}`);
370
+ }
371
+ text += texts.join(", ");
372
+ text += " }";
373
+ return text;
374
+ }
375
+ function generateTypeImportText(typeSpecifiers) {
376
+ let text = "type ";
377
+ text += generateImportsText(typeSpecifiers);
378
+ return text;
379
+ }
380
+ function generateValueImportText(defaultImportSpecifier, valueSpecifiers) {
381
+ const hasValueImport = valueSpecifiers.length > 0;
382
+ let text = "";
383
+ if (defaultImportSpecifier) {
384
+ text += defaultImportSpecifier.local.name;
385
+ if (hasValueImport) text += ", ";
386
+ }
387
+ if (hasValueImport) text += generateImportsText(valueSpecifiers);
388
+ return text;
389
+ }
390
+ const rule$5 = createEslintRule({
391
+ name: RULE_NAME$4,
244
392
  meta: {
245
393
  type: "layout",
246
394
  docs: { description: "Disallow inline type import." },
@@ -254,51 +402,28 @@ const rule$6 = createEslintRule({
254
402
  const typeSpecifiers = specifiers.filter((s) => s.type === AST_NODE_TYPES.ImportSpecifier && s.importKind === "type");
255
403
  const valueSpecifiers = specifiers.filter((s) => s.type === AST_NODE_TYPES.ImportSpecifier && s.importKind === "value");
256
404
  const defaultImportSpecifier = specifiers.find((s) => s.type === AST_NODE_TYPES.ImportDefaultSpecifier);
257
- if (typeSpecifiers.length > 0) if (valueSpecifiers.length > 0) context.report({
405
+ const hasDefaultImport = !!defaultImportSpecifier;
406
+ const hasTypeImport = typeSpecifiers.length > 0;
407
+ const hasValueImport = valueSpecifiers.length > 0;
408
+ if (!hasTypeImport) return;
409
+ const texts = [];
410
+ texts.push(generateTypeImportText(typeSpecifiers));
411
+ if (hasDefaultImport || hasValueImport) texts.push(generateValueImportText(defaultImportSpecifier, valueSpecifiers));
412
+ const textToReport = texts.map((text) => `import ${text} from "${node.source.value}";`).join("\n");
413
+ context.report({
258
414
  node,
259
415
  messageId: "noInlineTypeImport",
260
416
  fix(fixer) {
261
- const typeSpecifiersText = typeSpecifiers.map((s) => {
262
- if (s.type === AST_NODE_TYPES.ImportSpecifier) {
263
- const importedName = s.imported.type === AST_NODE_TYPES.Identifier ? s.imported.name : s.imported.value;
264
- return importedName === s.local.name ? s.local.name : `${importedName} as ${s.local.name}`;
265
- }
266
- return s.local.name;
267
- }).join(", ");
268
- const valueSpecifiersText = valueSpecifiers.map((s) => {
269
- if (s.type === AST_NODE_TYPES.ImportSpecifier) {
270
- const importedName = s.imported.type === AST_NODE_TYPES.Identifier ? s.imported.name : s.imported.value;
271
- return importedName === s.local.name ? s.local.name : `${importedName} as ${s.local.name}`;
272
- }
273
- return s.local.name;
274
- }).join(", ");
275
- const defaultImportSpecifierText = defaultImportSpecifier?.local.name;
276
- const defaultAndValueSpecifiersText = defaultImportSpecifier ? `import ${defaultImportSpecifierText}, { ${valueSpecifiersText} } from "${node.source.value}";` : `import { ${valueSpecifiersText} } from "${node.source.value}";`;
277
- const texts = [`import type { ${typeSpecifiersText} } from "${node.source.value}";`, defaultAndValueSpecifiersText];
278
- return fixer.replaceText(node, texts.join("\n"));
279
- }
280
- });
281
- else context.report({
282
- node,
283
- messageId: "noInlineTypeImport",
284
- fix(fixer) {
285
- const typeSpecifiersText = typeSpecifiers.map((s) => {
286
- if (s.type === AST_NODE_TYPES.ImportSpecifier) {
287
- const importedName = s.imported.type === AST_NODE_TYPES.Identifier ? s.imported.name : s.imported.value;
288
- return importedName === s.local.name ? s.local.name : `${importedName} as ${s.local.name}`;
289
- }
290
- return s.local.name;
291
- }).join(", ");
292
- return fixer.replaceText(node, `import type { ${typeSpecifiersText} } from "${node.source.value}";`);
417
+ return fixer.replaceText(node, textToReport);
293
418
  }
294
419
  });
295
420
  } })
296
421
  });
297
- var no_inline_type_import_default = rule$6;
422
+ var no_inline_type_import_default = rule$5;
298
423
 
299
424
  //#endregion
300
425
  //#region src/rules/no-negated-comparison.ts
301
- const RULE_NAME$4 = "no-negated-comparison";
426
+ const RULE_NAME$3 = "no-negated-comparison";
302
427
  const negatedToPositive = {
303
428
  "==": "!=",
304
429
  "===": "!==",
@@ -310,8 +435,8 @@ const negatedToPositive = {
310
435
  ">=": "<"
311
436
  };
312
437
  const negatives = Object.keys(negatedToPositive);
313
- const rule$5 = createEslintRule({
314
- name: RULE_NAME$4,
438
+ const rule$4 = createEslintRule({
439
+ name: RULE_NAME$3,
315
440
  meta: {
316
441
  type: "problem",
317
442
  docs: { description: "Disallow negated comparison." },
@@ -335,13 +460,13 @@ const rule$5 = createEslintRule({
335
460
  });
336
461
  } })
337
462
  });
338
- var no_negated_comparison_default = rule$5;
463
+ var no_negated_comparison_default = rule$4;
339
464
 
340
465
  //#endregion
341
466
  //#region src/rules/no-useless-template-string.ts
342
- const RULE_NAME$3 = "no-useless-template-string";
343
- const rule$4 = createEslintRule({
344
- name: RULE_NAME$3,
467
+ const RULE_NAME$2 = "no-useless-template-string";
468
+ const rule$3 = createEslintRule({
469
+ name: RULE_NAME$2,
345
470
  meta: {
346
471
  type: "problem",
347
472
  docs: { description: "No useless template string." },
@@ -362,45 +487,7 @@ const rule$4 = createEslintRule({
362
487
  });
363
488
  } })
364
489
  });
365
- var no_useless_template_string_default = rule$4;
366
-
367
- //#endregion
368
- //#region src/rules/pad-after-last-import.ts
369
- const RULE_NAME$2 = "pad-after-last-import";
370
- const rule$3 = createEslintRule({
371
- name: RULE_NAME$2,
372
- meta: {
373
- type: "problem",
374
- docs: { description: "Pad after the last import." },
375
- fixable: "code",
376
- schema: [],
377
- messages: { padAfterLastImport: "Expected a blank line after the last import." }
378
- },
379
- defaultOptions: [],
380
- create: (context) => {
381
- const sourceCode = context.sourceCode;
382
- let lastImportNode = null;
383
- return {
384
- ImportDeclaration(node) {
385
- lastImportNode = node;
386
- },
387
- "Program:exit"() {
388
- if (lastImportNode) {
389
- const nextToken = sourceCode.getTokenAfter(lastImportNode);
390
- const firstCommentAfterTokenStartLine = sourceCode.getCommentsAfter(lastImportNode)[0]?.loc.start.line;
391
- const expectedLine = lastImportNode.loc.end.line + 1;
392
- const nextTokenStartLine = nextToken?.loc.start.line;
393
- if (nextToken && nextToken.value !== "<\/script>" && (expectedLine === nextTokenStartLine || expectedLine === firstCommentAfterTokenStartLine)) context.report({
394
- node: lastImportNode,
395
- messageId: "padAfterLastImport",
396
- fix: (fixer) => fixer.insertTextAfter(lastImportNode, "\n")
397
- });
398
- }
399
- }
400
- };
401
- }
402
- });
403
- var pad_after_last_import_default = rule$3;
490
+ var no_useless_template_string_default = rule$3;
404
491
 
405
492
  //#endregion
406
493
  //#region src/rules/prefer-ts-expect-error.ts
@@ -558,12 +645,13 @@ var vue_root_element_sort_attributes_default = rule;
558
645
  //#region src/index.ts
559
646
  var src_default = { rules: {
560
647
  "function-style": function_style_default,
648
+ "html-spaced-comment": html_spaced_comment_default,
561
649
  "import-dedupe": import_dedupe_default,
650
+ "import-export-newline": import_export_newline_default,
562
651
  "no-import-promises-as": no_import_promises_as_default,
563
652
  "no-inline-type-import": no_inline_type_import_default,
564
653
  "no-negated-comparison": no_negated_comparison_default,
565
654
  "no-useless-template-string": no_useless_template_string_default,
566
- "pad-after-last-import": pad_after_last_import_default,
567
655
  "prefer-ts-expect-error": prefer_ts_expect_error_default,
568
656
  "require-async-with-await": require_async_with_await_default,
569
657
  "vue-root-element-sort-attributes": vue_root_element_sort_attributes_default
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@so1ve/eslint-plugin",
3
- "version": "3.20.2",
3
+ "version": "3.22.0",
4
4
  "author": "Ray <i@mk1.io> (https://github.com/so1ve/)",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -33,10 +33,11 @@
33
33
  "access": "public"
34
34
  },
35
35
  "dependencies": {
36
- "@typescript-eslint/types": "^8.47.0",
37
- "@typescript-eslint/utils": "^8.47.0"
36
+ "@typescript-eslint/types": "^8.50.1",
37
+ "@typescript-eslint/utils": "^8.50.1"
38
38
  },
39
39
  "devDependencies": {
40
+ "@html-eslint/parser": "^0.52.0",
40
41
  "vue-eslint-parser": "^10.2.0"
41
42
  }
42
43
  }