@sister.software/oxlint-config 9.3.0 → 11.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.
Files changed (78) hide show
  1. package/README.md +198 -10
  2. package/out/browser-globals.d.ts +15 -0
  3. package/out/browser-globals.d.ts.map +1 -0
  4. package/out/browser-globals.js +76 -0
  5. package/out/browser-globals.js.map +1 -0
  6. package/out/console-padding-plugin.d.ts +16 -0
  7. package/out/console-padding-plugin.d.ts.map +1 -0
  8. package/out/console-padding-plugin.js +68 -0
  9. package/out/console-padding-plugin.js.map +1 -0
  10. package/out/constant-doc-plugin.d.ts +32 -0
  11. package/out/constant-doc-plugin.d.ts.map +1 -0
  12. package/out/constant-doc-plugin.js +91 -0
  13. package/out/constant-doc-plugin.js.map +1 -0
  14. package/out/headers-plugin.d.ts +6 -2
  15. package/out/headers-plugin.d.ts.map +1 -1
  16. package/out/headers-plugin.js +4 -2
  17. package/out/headers-plugin.js.map +1 -1
  18. package/out/index.d.ts +166 -13
  19. package/out/index.d.ts.map +1 -1
  20. package/out/index.js +332 -6
  21. package/out/index.js.map +1 -1
  22. package/out/jsdoc-plugin.d.ts +16 -0
  23. package/out/jsdoc-plugin.d.ts.map +1 -0
  24. package/out/jsdoc-plugin.js +68 -0
  25. package/out/jsdoc-plugin.js.map +1 -0
  26. package/out/length-truthiness-plugin.d.ts +18 -0
  27. package/out/length-truthiness-plugin.d.ts.map +1 -0
  28. package/out/length-truthiness-plugin.js +123 -0
  29. package/out/length-truthiness-plugin.js.map +1 -0
  30. package/out/multiline-statement-plugin.d.ts +18 -0
  31. package/out/multiline-statement-plugin.d.ts.map +1 -0
  32. package/out/multiline-statement-plugin.js +93 -0
  33. package/out/multiline-statement-plugin.js.map +1 -0
  34. package/out/padding-plugin.d.ts +4 -3
  35. package/out/padding-plugin.d.ts.map +1 -1
  36. package/out/padding-plugin.js +44 -27
  37. package/out/padding-plugin.js.map +1 -1
  38. package/out/padding-utils.d.ts +34 -0
  39. package/out/padding-utils.d.ts.map +1 -0
  40. package/out/padding-utils.js +50 -0
  41. package/out/padding-utils.js.map +1 -0
  42. package/out/plugin-types.d.ts +68 -3
  43. package/out/plugin-types.d.ts.map +1 -1
  44. package/out/plugin.d.ts.map +1 -1
  45. package/out/plugin.js +17 -0
  46. package/out/plugin.js.map +1 -1
  47. package/out/process-globals-plugin.d.ts.map +1 -1
  48. package/out/process-globals-plugin.js +6 -2
  49. package/out/process-globals-plugin.js.map +1 -1
  50. package/out/restrictions.d.ts +9 -3
  51. package/out/restrictions.d.ts.map +1 -1
  52. package/out/restrictions.js +4 -70
  53. package/out/restrictions.js.map +1 -1
  54. package/out/section-marker-plugin.d.ts +25 -0
  55. package/out/section-marker-plugin.d.ts.map +1 -0
  56. package/out/section-marker-plugin.js +234 -0
  57. package/out/section-marker-plugin.js.map +1 -0
  58. package/out/threshold-plugin.d.ts +27 -0
  59. package/out/threshold-plugin.d.ts.map +1 -0
  60. package/out/threshold-plugin.js +78 -0
  61. package/out/threshold-plugin.js.map +1 -0
  62. package/package.json +2 -2
  63. package/src/browser-globals.ts +77 -0
  64. package/src/console-padding-plugin.ts +76 -0
  65. package/src/constant-doc-plugin.ts +124 -0
  66. package/src/headers-plugin.ts +7 -3
  67. package/src/index.ts +490 -17
  68. package/src/jsdoc-plugin.ts +75 -0
  69. package/src/length-truthiness-plugin.ts +142 -0
  70. package/src/multiline-statement-plugin.ts +104 -0
  71. package/src/padding-plugin.ts +44 -29
  72. package/src/padding-utils.ts +70 -0
  73. package/src/plugin-types.ts +68 -3
  74. package/src/plugin.ts +22 -0
  75. package/src/process-globals-plugin.ts +6 -2
  76. package/src/restrictions.ts +16 -77
  77. package/src/section-marker-plugin.ts +306 -0
  78. package/src/threshold-plugin.ts +105 -0
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ * @file The `sister-software/multiline-jsdoc` rule: a JSDoc block always spans multiple lines, even
6
+ * when its content would fit on one. A one-line block reads as an aside; the multi-line form reads
7
+ * as documentation, and it leaves somewhere to put the second sentence when one is needed.
8
+ *
9
+ * Two shapes are left alone. A JSDoc that shares its line with code is a type cast or an inline
10
+ * annotation, and expanding those changes what the line means. A JSDoc on a union or intersection
11
+ * member labels one alternative in what reads as a list — three lines per entry turns a list you
12
+ * can scan into a page you have to read.
13
+ */
14
+ /**
15
+ * The JSDoc opener, so the rule can tell a documentation block from a plain block comment.
16
+ */
17
+ const JSDOC_OPENER = "/**";
18
+ /**
19
+ * A `|` or `&` as the next thing after a comment, meaning the comment labels one member of a union or intersection
20
+ * rather than documenting a declaration.
21
+ */
22
+ const UNION_MEMBER = /^\s*[|&]/;
23
+ export const multilineJSDocRule = {
24
+ meta: {
25
+ name: "multiline-jsdoc",
26
+ type: "layout",
27
+ fixable: "whitespace",
28
+ schema: [{ type: "object", additionalProperties: true }],
29
+ },
30
+ create(context) {
31
+ const sourceCode = context.sourceCode ?? context.getSourceCode();
32
+ const text = sourceCode.getText();
33
+ return {
34
+ Program() {
35
+ for (const comment of sourceCode.getAllComments()) {
36
+ if (comment.type !== "Block")
37
+ continue;
38
+ const raw = text.slice(comment.range[0], comment.range[1]);
39
+ if (!raw.startsWith(JSDOC_OPENER) || raw.includes("\n"))
40
+ continue;
41
+ // `value` excludes the delimiters, so a one-line block arrives as `"* x "`.
42
+ const body = comment.value.replace(/^\*/, "").trim();
43
+ if (!body)
44
+ continue;
45
+ // Everything from the start of the line up to the comment. All-whitespace means the
46
+ // block leads its line and owns the indentation; anything else means it is inline.
47
+ const lineStart = text.lastIndexOf("\n", comment.range[0] - 1) + 1;
48
+ const indent = text.slice(lineStart, comment.range[0]);
49
+ if (indent.trim())
50
+ continue;
51
+ // A block documenting a union or intersection member is a label on one alternative, and
52
+ // the members read as a list. Three lines per entry turns a scannable list into a page,
53
+ // so the requirement does not reach inside one.
54
+ if (UNION_MEMBER.test(text.slice(comment.range[1])))
55
+ continue;
56
+ context.report({
57
+ node: { type: "Block", range: comment.range },
58
+ message: "JSDoc should span multiple lines.",
59
+ fix(fixer) {
60
+ return fixer.replaceTextRange(comment.range, `/**\n${indent} * ${body}\n${indent} */`);
61
+ },
62
+ });
63
+ }
64
+ },
65
+ };
66
+ },
67
+ };
68
+ //# sourceMappingURL=jsdoc-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsdoc-plugin.js","sourceRoot":"","sources":["../src/jsdoc-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH;;GAEG;AACH,MAAM,YAAY,GAAG,KAAK,CAAA;AAE1B;;;GAGG;AACH,MAAM,YAAY,GAAG,UAAU,CAAA;AAE/B,MAAM,CAAC,MAAM,kBAAkB,GAAS;IACvC,IAAI,EAAE;QACL,IAAI,EAAE,iBAAiB;QACvB,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,YAAY;QACrB,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,oBAAoB,EAAE,IAAI,EAAE,CAAC;KACxD;IACD,MAAM,CAAC,OAAO;QACb,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,aAAc,EAAE,CAAA;QACjE,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,EAAE,CAAA;QAEjC,OAAO;YACN,OAAO;gBACN,KAAK,MAAM,OAAO,IAAI,UAAU,CAAC,cAAc,EAAE,EAAE,CAAC;oBACnD,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO;wBAAE,SAAQ;oBACtC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;oBAE1D,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;wBAAE,SAAQ;oBAEjE,4EAA4E;oBAC5E,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;oBAEpD,IAAI,CAAC,IAAI;wBAAE,SAAQ;oBAEnB,oFAAoF;oBACpF,mFAAmF;oBACnF,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;oBAClE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;oBAEtD,IAAI,MAAM,CAAC,IAAI,EAAE;wBAAE,SAAQ;oBAE3B,wFAAwF;oBACxF,wFAAwF;oBACxF,gDAAgD;oBAChD,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;wBAAE,SAAQ;oBAE7D,OAAO,CAAC,MAAM,CAAC;wBACd,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE;wBAC7C,OAAO,EAAE,mCAAmC;wBAC5C,GAAG,CAAC,KAAK;4BACR,OAAO,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,MAAM,MAAM,IAAI,KAAK,MAAM,KAAK,CAAC,CAAA;wBACvF,CAAC;qBACD,CAAC,CAAA;gBACH,CAAC;YACF,CAAC;SACD,CAAA;IACF,CAAC;CACD,CAAA"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ * @file A prefer-length-truthiness rule, authored as an oxlint JS plugin (ESLint v9-compatible API).
6
+ * It is the house counterpart to `unicorn/explicit-length-check`, which enforces the opposite
7
+ * convention and is therefore off: `if (items.length)` reads better here than
8
+ * `if (items.length > 0)`.
9
+ *
10
+ * The rule only fires where the value is ALREADY coerced to a boolean — a condition, a ternary
11
+ * test, or the operand of `!` — including through `&&`/`||` nested inside one. Outside those
12
+ * positions the comparison is the value itself, and rewriting `const hasItems = items.length > 0`
13
+ * would silently change its type from boolean to number.
14
+ */
15
+ import type { Rule } from "./plugin-types.js";
16
+ export declare const preferLengthTruthinessRule: Rule;
17
+ export default preferLengthTruthinessRule;
18
+ //# sourceMappingURL=length-truthiness-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"length-truthiness-plugin.d.ts","sourceRoot":"","sources":["../src/length-truthiness-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAkB,IAAI,EAAe,MAAM,mBAAmB,CAAA;AAmD1E,eAAO,MAAM,0BAA0B,EAAE,IAyExC,CAAA;AAED,eAAe,0BAA0B,CAAA"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ * @file A prefer-length-truthiness rule, authored as an oxlint JS plugin (ESLint v9-compatible API).
6
+ * It is the house counterpart to `unicorn/explicit-length-check`, which enforces the opposite
7
+ * convention and is therefore off: `if (items.length)` reads better here than
8
+ * `if (items.length > 0)`.
9
+ *
10
+ * The rule only fires where the value is ALREADY coerced to a boolean — a condition, a ternary
11
+ * test, or the operand of `!` — including through `&&`/`||` nested inside one. Outside those
12
+ * positions the comparison is the value itself, and rewriting `const hasItems = items.length > 0`
13
+ * would silently change its type from boolean to number.
14
+ */
15
+ /**
16
+ * Comparisons meaning "non-empty", which become the bare length.
17
+ */
18
+ const TRUTHY_FORMS = new Set(["> 0", "!== 0", "!= 0", ">= 1"]);
19
+ /**
20
+ * Comparisons meaning "empty", which become a negated length.
21
+ */
22
+ const FALSY_FORMS = new Set(["=== 0", "== 0", "< 1"]);
23
+ /**
24
+ * Members whose length-ness the rule understands.
25
+ */
26
+ const LENGTH_PROPERTIES = new Set(["length", "size"]);
27
+ /**
28
+ * The comparison rendered as `<operator> <literal>`, or null when it is not a length comparison.
29
+ */
30
+ function classify(node) {
31
+ if (node.type !== "BinaryExpression" || !node.operator || !node.left || !node.right)
32
+ return null;
33
+ // Accept both `x.length > 0` and the flipped `0 < x.length`.
34
+ const flipped = { "<": ">", ">": "<", "<=": ">=", ">=": "<=" };
35
+ let { left, right, operator } = { left: node.left, right: node.right, operator: node.operator };
36
+ if (left.type === "Literal" || left.type === "NumericLiteral") {
37
+ ;
38
+ [left, right] = [right, left];
39
+ operator = flipped[operator] ?? operator;
40
+ }
41
+ if (left.type !== "MemberExpression" && left.type !== "StaticMemberExpression")
42
+ return null;
43
+ const property = left.property;
44
+ if (!property || property.type !== "Identifier" || !LENGTH_PROPERTIES.has(property.name ?? ""))
45
+ return null;
46
+ if (right.type !== "Literal" && right.type !== "NumericLiteral")
47
+ return null;
48
+ if (typeof right.value !== "number")
49
+ return null;
50
+ const form = `${operator} ${right.value}`;
51
+ if (TRUTHY_FORMS.has(form))
52
+ return { member: left, negate: false };
53
+ if (FALSY_FORMS.has(form))
54
+ return { member: left, negate: true };
55
+ return null;
56
+ }
57
+ export const preferLengthTruthinessRule = {
58
+ meta: {
59
+ name: "prefer-length-truthiness",
60
+ type: "suggestion",
61
+ fixable: "code",
62
+ schema: [{ type: "object", additionalProperties: true }],
63
+ },
64
+ create(context) {
65
+ const sourceCode = context.sourceCode ?? context.getSourceCode();
66
+ const text = sourceCode.getText();
67
+ function report(node) {
68
+ const hit = classify(node);
69
+ if (!hit)
70
+ return;
71
+ const member = text.slice(hit.member.range[0], hit.member.range[1]);
72
+ const replacement = hit.negate ? `!${member}` : member;
73
+ context.report({
74
+ node,
75
+ message: `Prefer \`${replacement}\` over an explicit length comparison — the house convention is truthiness.`,
76
+ fix(fixer) {
77
+ return fixer.replaceTextRange(node.range, replacement);
78
+ },
79
+ });
80
+ }
81
+ /**
82
+ * Walk into a boolean context: logical operands and `!` arguments stay boolean.
83
+ */
84
+ function visitCondition(node) {
85
+ if (!node)
86
+ return;
87
+ if (node.type === "LogicalExpression") {
88
+ visitCondition(node.left);
89
+ visitCondition(node.right);
90
+ return;
91
+ }
92
+ if (node.type === "UnaryExpression" && node.operator === "!") {
93
+ visitCondition(node.argument);
94
+ return;
95
+ }
96
+ report(node);
97
+ }
98
+ return {
99
+ IfStatement(node) {
100
+ visitCondition(node.test);
101
+ },
102
+ WhileStatement(node) {
103
+ visitCondition(node.test);
104
+ },
105
+ DoWhileStatement(node) {
106
+ visitCondition(node.test);
107
+ },
108
+ ForStatement(node) {
109
+ visitCondition(node.test);
110
+ },
111
+ ConditionalExpression(node) {
112
+ visitCondition(node.test);
113
+ },
114
+ UnaryExpression(node) {
115
+ if (node.operator === "!") {
116
+ visitCondition(node.argument);
117
+ }
118
+ },
119
+ };
120
+ },
121
+ };
122
+ export default preferLengthTruthinessRule;
123
+ //# sourceMappingURL=length-truthiness-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"length-truthiness-plugin.js","sourceRoot":"","sources":["../src/length-truthiness-plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ * @file The `sister-software/multiline-statement-padding` rule: a statement that spans lines gets a
6
+ * blank line on each side, so a reader can see where it starts and stops without matching brackets.
7
+ *
8
+ * One rule covers what look like two habits — a big object literal jammed against the next
9
+ * declaration, and a multi-line call jammed against the counter after it. Both are the same thing:
10
+ * a statement tall enough to read as a block, sitting flush against its neighbour.
11
+ *
12
+ * Neither edge is required at a block boundary, where the brace already separates. `console.*` calls
13
+ * are left to `console-padding`, which groups consecutive ones — this rule would split a run that
14
+ * happens to contain a tall call, and a run of output is one thing.
15
+ */
16
+ import type { Rule } from "./plugin-types.js";
17
+ export declare const multilineStatementPaddingRule: Rule;
18
+ //# sourceMappingURL=multiline-statement-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multiline-statement-plugin.d.ts","sourceRoot":"","sources":["../src/multiline-statement-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,mBAAmB,CAAA;AAwBtD,eAAO,MAAM,6BAA6B,EAAE,IA8D3C,CAAA"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ * @file The `sister-software/multiline-statement-padding` rule: a statement that spans lines gets a
6
+ * blank line on each side, so a reader can see where it starts and stops without matching brackets.
7
+ *
8
+ * One rule covers what look like two habits — a big object literal jammed against the next
9
+ * declaration, and a multi-line call jammed against the counter after it. Both are the same thing:
10
+ * a statement tall enough to read as a block, sitting flush against its neighbour.
11
+ *
12
+ * Neither edge is required at a block boundary, where the brace already separates. `console.*` calls
13
+ * are left to `console-padding`, which groups consecutive ones — this rule would split a run that
14
+ * happens to contain a tall call, and a run of output is one thing.
15
+ */
16
+ import { createPaddingHelpers } from "./padding-utils.js";
17
+ /**
18
+ * The object whose calls belong to `console-padding` rather than to this rule.
19
+ */
20
+ const CONSOLE_OBJECT = "console";
21
+ /**
22
+ * Does this statement's own source span more than one line?
23
+ */
24
+ function isMultiline(node, text) {
25
+ return text.slice(node.range[0], node.range[1]).includes("\n");
26
+ }
27
+ /**
28
+ * Is this statement a bare `console.<method>(…)` call, which `console-padding` owns?
29
+ */
30
+ function isConsoleStatement(node) {
31
+ if (node?.type !== "ExpressionStatement")
32
+ return false;
33
+ const callee = node.expression?.type === "CallExpression" ? node.expression.callee : undefined;
34
+ return callee?.type === "MemberExpression" && callee.object?.name === CONSOLE_OBJECT;
35
+ }
36
+ export const multilineStatementPaddingRule = {
37
+ meta: {
38
+ name: "multiline-statement-padding",
39
+ type: "layout",
40
+ fixable: "whitespace",
41
+ schema: [{ type: "object", additionalProperties: true }],
42
+ },
43
+ create(context) {
44
+ const sourceCode = context.sourceCode ?? context.getSourceCode();
45
+ const text = sourceCode.getText();
46
+ const { requirePadding } = createPaddingHelpers(context);
47
+ function check(node) {
48
+ const parent = node.parent;
49
+ if (!parent || !Array.isArray(parent.body))
50
+ return;
51
+ if (!isMultiline(node, text) || isConsoleStatement(node))
52
+ return;
53
+ const body = parent.body;
54
+ const index = body.indexOf(node);
55
+ if (index === -1)
56
+ return;
57
+ const previous = body[index - 1];
58
+ const next = body[index + 1];
59
+ if (previous && !isConsoleStatement(previous)) {
60
+ requirePadding(previous, node, node, "Expected a blank line before this multi-line statement.");
61
+ }
62
+ if (next && !isConsoleStatement(next)) {
63
+ requirePadding(node, next, node, "Expected a blank line after this multi-line statement.");
64
+ }
65
+ }
66
+ // Every statement type that can stand in a statement list and span lines. Block-like statements are
67
+ // already padded from the front by `padding-lines`; this adds their trailing edge.
68
+ return Object.fromEntries([
69
+ "VariableDeclaration",
70
+ "ExpressionStatement",
71
+ "IfStatement",
72
+ "ForStatement",
73
+ "ForInStatement",
74
+ "ForOfStatement",
75
+ "WhileStatement",
76
+ "DoWhileStatement",
77
+ "SwitchStatement",
78
+ "TryStatement",
79
+ "FunctionDeclaration",
80
+ "ClassDeclaration",
81
+ "ReturnStatement",
82
+ "TSInterfaceDeclaration",
83
+ "TSTypeAliasDeclaration",
84
+ "TSEnumDeclaration",
85
+ "TSModuleDeclaration",
86
+ // An exported declaration arrives wrapped, and the wrapper spans the same lines as what it
87
+ // wraps — so matching the wrapper is enough. Without these, every `export` was invisible here.
88
+ "ExportNamedDeclaration",
89
+ "ExportDefaultDeclaration",
90
+ ].map((type) => [type, check]));
91
+ },
92
+ };
93
+ //# sourceMappingURL=multiline-statement-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multiline-statement-plugin.js","sourceRoot":"","sources":["../src/multiline-statement-plugin.ts"],"names":[],"mappings":"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"}
@@ -3,9 +3,10 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  * @file A padding-lines rule, authored as an oxlint JS plugin (ESLint v9-compatible API). It
6
- * requires a blank line before `return` and block-like statements (the equivalent of ESLint's
7
- * `padding-line-between-statements` with `{ blankLine: "always", prev: "*", next: "return" | "block-like" }`),
8
- * and autofixes by inserting the missing blank line (before any leading comments).
6
+ * requires a blank line before the statements that END a path or a step — `return`, `continue`,
7
+ * `break`, a bare `x++`/`x--` and before block-like statements. Roughly ESLint's
8
+ * `padding-line-between-statements` with `{ blankLine: "always", prev: "*", next: "return" | "block-like" }`,
9
+ * widened to the rest of that family. Autofixes by inserting the blank line before any leading comments.
9
10
  */
10
11
  import type { Rule } from "./plugin-types.js";
11
12
  export declare const paddingRule: Rule;
@@ -1 +1 @@
1
- {"version":3,"file":"padding-plugin.d.ts","sourceRoot":"","sources":["../src/padding-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,mBAAmB,CAAA;AAmBtD,eAAO,MAAM,WAAW,EAAE,IAiDzB,CAAA;AAED,eAAe,WAAW,CAAA"}
1
+ {"version":3,"file":"padding-plugin.d.ts","sourceRoot":"","sources":["../src/padding-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,mBAAmB,CAAA;AAkCtD,eAAO,MAAM,WAAW,EAAE,IA+CzB,CAAA;AAED,eAAe,WAAW,CAAA"}
@@ -3,16 +3,20 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  * @file A padding-lines rule, authored as an oxlint JS plugin (ESLint v9-compatible API). It
6
- * requires a blank line before `return` and block-like statements (the equivalent of ESLint's
7
- * `padding-line-between-statements` with `{ blankLine: "always", prev: "*", next: "return" | "block-like" }`),
8
- * and autofixes by inserting the missing blank line (before any leading comments).
6
+ * requires a blank line before the statements that END a path or a step — `return`, `continue`,
7
+ * `break`, a bare `x++`/`x--` and before block-like statements. Roughly ESLint's
8
+ * `padding-line-between-statements` with `{ blankLine: "always", prev: "*", next: "return" | "block-like" }`,
9
+ * widened to the rest of that family. Autofixes by inserting the blank line before any leading comments.
9
10
  */
11
+ import { createPaddingHelpers } from "./padding-utils.js";
10
12
  /**
11
- * Statement node types that require a preceding blank line: `return` plus all "block-like" statements (matching
13
+ * Statement node types that require a preceding blank line: the path-enders plus all "block-like" statements (matching
12
14
  * ESLint's `block-like` selector).
13
15
  */
14
16
  const PADDED_STATEMENT_TYPES = [
15
17
  "ReturnStatement",
18
+ "ContinueStatement",
19
+ "BreakStatement",
16
20
  "BlockStatement",
17
21
  "IfStatement",
18
22
  "ForStatement",
@@ -22,7 +26,19 @@ const PADDED_STATEMENT_TYPES = [
22
26
  "DoWhileStatement",
23
27
  "SwitchStatement",
24
28
  "TryStatement",
29
+ // An interface or a type alias with a body reads as a block too — same braces, same weight on the
30
+ // page — so it wants the same separation from whatever precedes it.
31
+ "TSInterfaceDeclaration",
32
+ "TSTypeAliasDeclaration",
33
+ "TSEnumDeclaration",
34
+ "TSModuleDeclaration",
25
35
  ];
36
+ /**
37
+ * Does this node type require a preceding blank line?
38
+ */
39
+ function isPaddedType(type) {
40
+ return !!type && PADDED_STATEMENT_TYPES.includes(type);
41
+ }
26
42
  export const paddingRule = {
27
43
  meta: {
28
44
  name: "padding-lines",
@@ -31,9 +47,7 @@ export const paddingRule = {
31
47
  schema: [{ type: "object", additionalProperties: true }],
32
48
  },
33
49
  create(context) {
34
- const sourceCode = context.sourceCode ?? context.getSourceCode();
35
- const text = sourceCode.getText();
36
- const comments = sourceCode.getAllComments();
50
+ const { requirePadding } = createPaddingHelpers(context);
37
51
  function check(node) {
38
52
  const parent = node.parent;
39
53
  // Only statements that are direct members of a statement list (a block/program body).
@@ -43,27 +57,30 @@ export const paddingRule = {
43
57
  const index = parent.body.indexOf(node);
44
58
  if (index <= 0)
45
59
  return; // first statement in the block — nothing to pad against.
46
- const previous = parent.body[index - 1];
47
- // A blank line should sit before the statement's own leading comments, so measure the gap
48
- // up to the earliest comment between the previous statement and this one.
49
- let start = node.range[0];
50
- for (const comment of comments) {
51
- if (comment.range[0] >= previous.range[1] && comment.range[1] <= node.range[0]) {
52
- start = Math.min(start, comment.range[0]);
53
- }
54
- }
55
- const gap = text.slice(previous.range[1], start);
56
- if ((gap.match(/\n/g) ?? []).length >= 2)
57
- return; // already a blank line.
58
- context.report({
59
- node,
60
- message: "Expected a blank line before this statement.",
61
- fix(fixer) {
62
- return fixer.insertTextAfterRange([previous.range[1], previous.range[1]], "\n");
63
- },
64
- });
60
+ requirePadding(parent.body[index - 1], node, node, "Expected a blank line before this statement.");
65
61
  }
66
- return Object.fromEntries(PADDED_STATEMENT_TYPES.map((type) => [type, check]));
62
+ return {
63
+ ...Object.fromEntries(PADDED_STATEMENT_TYPES.map((type) => [type, check])),
64
+ // A bare `x++` / `x--` is a counter step, and reads like one only when it stands apart. It
65
+ // arrives as an ExpressionStatement, so it cannot be matched by node type alone.
66
+ ExpressionStatement(node) {
67
+ if (node.expression?.type === "UpdateExpression") {
68
+ check(node);
69
+ }
70
+ },
71
+ // An exported declaration arrives wrapped. Look through the wrapper so `export interface Foo {}`
72
+ // is padded exactly like the unexported form.
73
+ ExportNamedDeclaration(node) {
74
+ if (isPaddedType(node.declaration?.type)) {
75
+ check(node);
76
+ }
77
+ },
78
+ ExportDefaultDeclaration(node) {
79
+ if (isPaddedType(node.declaration?.type)) {
80
+ check(node);
81
+ }
82
+ },
83
+ };
67
84
  },
68
85
  };
69
86
  export default paddingRule;
@@ -1 +1 @@
1
- {"version":3,"file":"padding-plugin.js","sourceRoot":"","sources":["../src/padding-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;;GAGG;AACH,MAAM,sBAAsB,GAAG;IAC9B,iBAAiB;IACjB,gBAAgB;IAChB,aAAa;IACb,cAAc;IACd,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,kBAAkB;IAClB,iBAAiB;IACjB,cAAc;CACL,CAAA;AAEV,MAAM,CAAC,MAAM,WAAW,GAAS;IAChC,IAAI,EAAE;QACL,IAAI,EAAE,eAAe;QACrB,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,YAAY;QACrB,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,oBAAoB,EAAE,IAAI,EAAE,CAAC;KACxD;IACD,MAAM,CAAC,OAAO;QACb,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,aAAc,EAAE,CAAA;QACjE,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,EAAE,CAAA;QACjC,MAAM,QAAQ,GAAG,UAAU,CAAC,cAAc,EAAE,CAAA;QAE5C,SAAS,KAAK,CAAC,IAAa;YAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;YAE1B,sFAAsF;YACtF,4EAA4E;YAC5E,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;gBAAE,OAAM;YAElD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;YAEvC,IAAI,KAAK,IAAI,CAAC;gBAAE,OAAM,CAAC,yDAAyD;YAChF,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAE,CAAA;YAExC,0FAA0F;YAC1F,0EAA0E;YAC1E,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YAEzB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAChC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;oBAChF,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;gBAC1C,CAAC;YACF,CAAC;YAED,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;YAEhD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC;gBAAE,OAAM,CAAC,wBAAwB;YAEzE,OAAO,CAAC,MAAM,CAAC;gBACd,IAAI;gBACJ,OAAO,EAAE,8CAA8C;gBACvD,GAAG,CAAC,KAAK;oBACR,OAAO,KAAK,CAAC,oBAAoB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;gBAChF,CAAC;aACD,CAAC,CAAA;QACH,CAAC;QAED,OAAO,MAAM,CAAC,WAAW,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;IAC/E,CAAC;CACD,CAAA;AAED,eAAe,WAAW,CAAA"}
1
+ {"version":3,"file":"padding-plugin.js","sourceRoot":"","sources":["../src/padding-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAGzD;;;GAGG;AACH,MAAM,sBAAsB,GAAG;IAC9B,iBAAiB;IACjB,mBAAmB;IACnB,gBAAgB;IAChB,gBAAgB;IAChB,aAAa;IACb,cAAc;IACd,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,kBAAkB;IAClB,iBAAiB;IACjB,cAAc;IACd,kGAAkG;IAClG,oEAAoE;IACpE,wBAAwB;IACxB,wBAAwB;IACxB,mBAAmB;IACnB,qBAAqB;CACZ,CAAA;AAEV;;GAEG;AACH,SAAS,YAAY,CAAC,IAAwB;IAC7C,OAAO,CAAC,CAAC,IAAI,IAAK,sBAA4C,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;AAC9E,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAS;IAChC,IAAI,EAAE;QACL,IAAI,EAAE,eAAe;QACrB,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,YAAY;QACrB,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,oBAAoB,EAAE,IAAI,EAAE,CAAC;KACxD;IACD,MAAM,CAAC,OAAO;QACb,MAAM,EAAE,cAAc,EAAE,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAA;QAExD,SAAS,KAAK,CAAC,IAAa;YAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;YAE1B,sFAAsF;YACtF,4EAA4E;YAC5E,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;gBAAE,OAAM;YAElD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;YAEvC,IAAI,KAAK,IAAI,CAAC;gBAAE,OAAM,CAAC,yDAAyD;YAEhF,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAE,EAAE,IAAI,EAAE,IAAI,EAAE,8CAA8C,CAAC,CAAA;QACpG,CAAC;QAED,OAAO;YACN,GAAG,MAAM,CAAC,WAAW,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;YAC1E,2FAA2F;YAC3F,iFAAiF;YACjF,mBAAmB,CAAC,IAAa;gBAChC,IAAI,IAAI,CAAC,UAAU,EAAE,IAAI,KAAK,kBAAkB,EAAE,CAAC;oBAClD,KAAK,CAAC,IAAI,CAAC,CAAA;gBACZ,CAAC;YACF,CAAC;YACD,iGAAiG;YACjG,8CAA8C;YAC9C,sBAAsB,CAAC,IAAa;gBACnC,IAAI,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC;oBAC1C,KAAK,CAAC,IAAI,CAAC,CAAA;gBACZ,CAAC;YACF,CAAC;YACD,wBAAwB,CAAC,IAAa;gBACrC,IAAI,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC;oBAC1C,KAAK,CAAC,IAAI,CAAC,CAAA;gBACZ,CAAC;YACF,CAAC;SACD,CAAA;IACF,CAAC;CACD,CAAA;AAED,eAAe,WAAW,CAAA"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ * @file The blank-line machinery the three padding rules share: how to tell whether two statements are
6
+ * already separated, and how to ask for the separation when they are not.
7
+ *
8
+ * All three anchor their fix to the END of the earlier statement. That is deliberate: when two rules
9
+ * want the same blank line — a console call before a `return`, say — they emit the identical edit, so
10
+ * one is applied and the other is a no-op, instead of each inserting a newline and producing a gap of
11
+ * two.
12
+ */
13
+ import type { AstNode, RuleContext } from "./plugin-types.js";
14
+ /**
15
+ * What a padding rule needs from its context, resolved once per `create`.
16
+ */
17
+ export interface PaddingHelpers {
18
+ /**
19
+ * Is there already a blank line between these two statements?
20
+ */
21
+ isPadded(from: AstNode, to: AstNode): boolean;
22
+ /**
23
+ * Report and fix a missing blank line between `from` and `to`, unless one is impossible or present.
24
+ */
25
+ requirePadding(from: AstNode, to: AstNode, node: AstNode, message: string): void;
26
+ }
27
+ /**
28
+ * Build the helpers for one rule invocation.
29
+ *
30
+ * A gap is measured to the earliest comment that leads the later statement, so the blank line lands above a statement's
31
+ * own comments rather than between them and their subject.
32
+ */
33
+ export declare function createPaddingHelpers(context: RuleContext): PaddingHelpers;
34
+ //# sourceMappingURL=padding-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"padding-utils.d.ts","sourceRoot":"","sources":["../src/padding-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAS,WAAW,EAAc,MAAM,mBAAmB,CAAA;AAEhF;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,GAAG,OAAO,CAAA;IAC7C;;OAEG;IACH,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAChF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,GAAG,cAAc,CAkCzE"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ * @file The blank-line machinery the three padding rules share: how to tell whether two statements are
6
+ * already separated, and how to ask for the separation when they are not.
7
+ *
8
+ * All three anchor their fix to the END of the earlier statement. That is deliberate: when two rules
9
+ * want the same blank line — a console call before a `return`, say — they emit the identical edit, so
10
+ * one is applied and the other is a no-op, instead of each inserting a newline and producing a gap of
11
+ * two.
12
+ */
13
+ /**
14
+ * Build the helpers for one rule invocation.
15
+ *
16
+ * A gap is measured to the earliest comment that leads the later statement, so the blank line lands above a statement's
17
+ * own comments rather than between them and their subject.
18
+ */
19
+ export function createPaddingHelpers(context) {
20
+ const sourceCode = context.sourceCode ?? context.getSourceCode();
21
+ const text = sourceCode.getText();
22
+ const comments = sourceCode.getAllComments();
23
+ function isPadded(from, to) {
24
+ // Two statements with no newline between them cannot be separated by a blank line. This is the
25
+ // `;(expr)` ASI guard: the semicolon terminates the PREVIOUS statement, so the gap here is zero
26
+ // characters wide. Asking for padding there is asking for the impossible.
27
+ if (!text.slice(from.range[1], to.range[0]).includes("\n"))
28
+ return true;
29
+ let start = to.range[0];
30
+ for (const comment of comments) {
31
+ if (comment.range[0] >= from.range[1] && comment.range[1] <= to.range[0]) {
32
+ start = Math.min(start, comment.range[0]);
33
+ }
34
+ }
35
+ return (text.slice(from.range[1], start).match(/\n/g) ?? []).length >= 2;
36
+ }
37
+ function requirePadding(from, to, node, message) {
38
+ if (isPadded(from, to))
39
+ return;
40
+ context.report({
41
+ node,
42
+ message,
43
+ fix(fixer) {
44
+ return fixer.insertTextAfterRange([from.range[1], from.range[1]], "\n");
45
+ },
46
+ });
47
+ }
48
+ return { isPadded, requirePadding };
49
+ }
50
+ //# sourceMappingURL=padding-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"padding-utils.js","sourceRoot":"","sources":["../src/padding-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAkBH;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAoB;IACxD,MAAM,UAAU,GAAe,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,aAAc,EAAE,CAAA;IAC7E,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,EAAE,CAAA;IACjC,MAAM,QAAQ,GAAG,UAAU,CAAC,cAAc,EAAE,CAAA;IAE5C,SAAS,QAAQ,CAAC,IAAa,EAAE,EAAW;QAC3C,+FAA+F;QAC/F,gGAAgG;QAChG,0EAA0E;QAC1E,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;QACvE,IAAI,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAEvB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAChC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC1E,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;YAC1C,CAAC;QACF,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAA;IACzE,CAAC;IAED,SAAS,cAAc,CAAC,IAAa,EAAE,EAAW,EAAE,IAAa,EAAE,OAAe;QACjF,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;YAAE,OAAM;QAE9B,OAAO,CAAC,MAAM,CAAC;YACd,IAAI;YACJ,OAAO;YACP,GAAG,CAAC,KAAY;gBACf,OAAO,KAAK,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;YACxE,CAAC;SACD,CAAC,CAAA;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAA;AACpC,CAAC"}