@sister.software/oxlint-config 9.1.0 → 9.2.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/out/braces-plugin.d.ts +14 -0
- package/out/braces-plugin.d.ts.map +1 -0
- package/out/braces-plugin.js +80 -0
- package/out/braces-plugin.js.map +1 -0
- package/out/index.d.ts +2 -0
- package/out/index.d.ts.map +1 -1
- package/out/index.js +6 -2
- package/out/index.js.map +1 -1
- package/out/plugin-types.d.ts +5 -1
- package/out/plugin-types.d.ts.map +1 -1
- package/out/plugin.d.ts.map +1 -1
- package/out/plugin.js +2 -0
- package/out/plugin.js.map +1 -1
- package/package.json +1 -1
- package/src/braces-plugin.ts +89 -0
- package/src/index.ts +9 -1
- package/src/plugin-types.ts +5 -1
- package/src/plugin.ts +2 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
* @file A require-braces rule, authored as an oxlint JS plugin (ESLint v9-compatible API). It
|
|
6
|
+
* requires braces around single-statement `if`/`else`/`for`/`while`/`do` bodies that do work
|
|
7
|
+
* (assignments, calls, …) and autofixes by wrapping them — but leaves a bare control-flow terminal
|
|
8
|
+
* (`return`/`throw`/`break`/`continue`) alone (braces optional). Unlike the built-in `curly` rule,
|
|
9
|
+
* which can't exempt those.
|
|
10
|
+
*/
|
|
11
|
+
import type { Rule } from "./plugin-types.js";
|
|
12
|
+
export declare const bracesRule: Rule;
|
|
13
|
+
export default bracesRule;
|
|
14
|
+
//# sourceMappingURL=braces-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"braces-plugin.d.ts","sourceRoot":"","sources":["../src/braces-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,mBAAmB,CAAA;AAgBtD,eAAO,MAAM,UAAU,EAAE,IA2DxB,CAAA;AAED,eAAe,UAAU,CAAA"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
* @file A require-braces rule, authored as an oxlint JS plugin (ESLint v9-compatible API). It
|
|
6
|
+
* requires braces around single-statement `if`/`else`/`for`/`while`/`do` bodies that do work
|
|
7
|
+
* (assignments, calls, …) and autofixes by wrapping them — but leaves a bare control-flow terminal
|
|
8
|
+
* (`return`/`throw`/`break`/`continue`) alone (braces optional). Unlike the built-in `curly` rule,
|
|
9
|
+
* which can't exempt those.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Body node types that may stay brace-less: an existing block; a bare control-flow terminal
|
|
13
|
+
* (`return`/`throw`/`break`/`continue`, exempt by house style); or an empty statement (a different concern —
|
|
14
|
+
* `no-empty`).
|
|
15
|
+
*/
|
|
16
|
+
const EXEMPT_BODY_TYPES = new Set([
|
|
17
|
+
"BlockStatement",
|
|
18
|
+
"ReturnStatement",
|
|
19
|
+
"ThrowStatement",
|
|
20
|
+
"BreakStatement",
|
|
21
|
+
"ContinueStatement",
|
|
22
|
+
"EmptyStatement",
|
|
23
|
+
]);
|
|
24
|
+
export const bracesRule = {
|
|
25
|
+
meta: {
|
|
26
|
+
name: "require-braces",
|
|
27
|
+
type: "layout",
|
|
28
|
+
fixable: "code",
|
|
29
|
+
schema: [{ type: "object", additionalProperties: true }],
|
|
30
|
+
},
|
|
31
|
+
create(context) {
|
|
32
|
+
const sourceCode = context.sourceCode ?? context.getSourceCode();
|
|
33
|
+
function check(body) {
|
|
34
|
+
if (!body || EXEMPT_BODY_TYPES.has(body.type))
|
|
35
|
+
return;
|
|
36
|
+
// `else if (…)` — the alternate is itself an `if`; its own body is visited separately.
|
|
37
|
+
if (body.type === "IfStatement")
|
|
38
|
+
return;
|
|
39
|
+
const inner = sourceCode.getText().slice(body.range[0], body.range[1]);
|
|
40
|
+
context.report({
|
|
41
|
+
node: body,
|
|
42
|
+
message: "Expected braces around this single-statement body.",
|
|
43
|
+
fix(fixer) {
|
|
44
|
+
// Wrap inline; oxfmt expands it to a formatted multi-line block afterward.
|
|
45
|
+
return fixer.replaceTextRange(body.range, `{ ${inner} }`);
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
IfStatement(node) {
|
|
51
|
+
check(node.consequent);
|
|
52
|
+
check(node.alternate);
|
|
53
|
+
},
|
|
54
|
+
ForStatement(node) {
|
|
55
|
+
checkLoopBody(node);
|
|
56
|
+
},
|
|
57
|
+
ForInStatement(node) {
|
|
58
|
+
checkLoopBody(node);
|
|
59
|
+
},
|
|
60
|
+
ForOfStatement(node) {
|
|
61
|
+
checkLoopBody(node);
|
|
62
|
+
},
|
|
63
|
+
WhileStatement(node) {
|
|
64
|
+
checkLoopBody(node);
|
|
65
|
+
},
|
|
66
|
+
DoWhileStatement(node) {
|
|
67
|
+
checkLoopBody(node);
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
function checkLoopBody(node) {
|
|
71
|
+
const body = node.body;
|
|
72
|
+
// A loop body is a single statement (never a statement-list array).
|
|
73
|
+
if (body && !Array.isArray(body)) {
|
|
74
|
+
check(body);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
export default bracesRule;
|
|
80
|
+
//# sourceMappingURL=braces-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"braces-plugin.js","sourceRoot":"","sources":["../src/braces-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC;IACjC,gBAAgB;IAChB,iBAAiB;IACjB,gBAAgB;IAChB,gBAAgB;IAChB,mBAAmB;IACnB,gBAAgB;CAChB,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,UAAU,GAAS;IAC/B,IAAI,EAAE;QACL,IAAI,EAAE,gBAAgB;QACtB,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,MAAM;QACf,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;QAEjE,SAAS,KAAK,CAAC,IAAgC;YAC9C,IAAI,CAAC,IAAI,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAM;YAErD,uFAAuF;YACvF,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa;gBAAE,OAAM;YAEvC,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;YAEtE,OAAO,CAAC,MAAM,CAAC;gBACd,IAAI,EAAE,IAAI;gBACV,OAAO,EAAE,oDAAoD;gBAC7D,GAAG,CAAC,KAAK;oBACR,2EAA2E;oBAC3E,OAAO,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,CAAA;gBAC1D,CAAC;aACD,CAAC,CAAA;QACH,CAAC;QAED,OAAO;YACN,WAAW,CAAC,IAAI;gBACf,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;gBACtB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;YACtB,CAAC;YACD,YAAY,CAAC,IAAI;gBAChB,aAAa,CAAC,IAAI,CAAC,CAAA;YACpB,CAAC;YACD,cAAc,CAAC,IAAI;gBAClB,aAAa,CAAC,IAAI,CAAC,CAAA;YACpB,CAAC;YACD,cAAc,CAAC,IAAI;gBAClB,aAAa,CAAC,IAAI,CAAC,CAAA;YACpB,CAAC;YACD,cAAc,CAAC,IAAI;gBAClB,aAAa,CAAC,IAAI,CAAC,CAAA;YACpB,CAAC;YACD,gBAAgB,CAAC,IAAI;gBACpB,aAAa,CAAC,IAAI,CAAC,CAAA;YACpB,CAAC;SACD,CAAA;QAED,SAAS,aAAa,CAAC,IAAa;YACnC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;YAEtB,oEAAoE;YACpE,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClC,KAAK,CAAC,IAAI,CAAC,CAAA;YACZ,CAAC;QACF,CAAC;IACF,CAAC;CACD,CAAA;AAED,eAAe,UAAU,CAAA"}
|
package/out/index.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export interface OxlintConfigOptions {
|
|
|
23
23
|
headers?: boolean;
|
|
24
24
|
/** Require a blank line before `return`/block-like statements (on by default). */
|
|
25
25
|
padding?: boolean;
|
|
26
|
+
/** Require braces around single-statement work bodies (bare `return` exempt; on by default). */
|
|
27
|
+
braces?: boolean;
|
|
26
28
|
/** Override the default ignore patterns. */
|
|
27
29
|
ignorePatterns?: string[];
|
|
28
30
|
/** Extra config deep-merged last; an escape hatch for per-repo tweaks. */
|
package/out/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,cAAc,mBAAmB,CAAA;AAEjC,4FAA4F;AAC5F,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAElD,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IACnC,4FAA4F;IAC5F,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,sDAAsD;IACtD,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,6DAA6D;IAC7D,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,qDAAqD;IACrD,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,4CAA4C;IAC5C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,YAAY,CAAA;CACxB;AAED,0DAA0D;AAC1D,eAAO,MAAM,qBAAqB,UAOjC,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,cAAc,mBAAmB,CAAA;AAEjC,4FAA4F;AAC5F,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAElD,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IACnC,4FAA4F;IAC5F,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,sDAAsD;IACtD,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,6DAA6D;IAC7D,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,qDAAqD;IACrD,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gGAAgG;IAChG,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,4CAA4C;IAC5C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,YAAY,CAAA;CACxB;AAED,0DAA0D;AAC1D,eAAO,MAAM,qBAAqB,UAOjC,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,YAAY,CA0ElF;AAED,eAAe,kBAAkB,CAAA"}
|
package/out/index.js
CHANGED
|
@@ -30,7 +30,7 @@ export const DefaultIgnorePatterns = [
|
|
|
30
30
|
* @returns A complete oxlint config object (no `extends` required).
|
|
31
31
|
*/
|
|
32
32
|
export function createOxlintConfig(options = {}) {
|
|
33
|
-
const { packageNamespace = "@sister.software", copyrightHolder = "Sister Software", spdxLicenseIdentifier = "UNLICENSED", author = "Teffen Ellis, et al.", react = false, headers = true, padding = true, ignorePatterns = DefaultIgnorePatterns, overrides = {}, } = options;
|
|
33
|
+
const { packageNamespace = "@sister.software", copyrightHolder = "Sister Software", spdxLicenseIdentifier = "UNLICENSED", author = "Teffen Ellis, et al.", react = false, headers = true, padding = true, braces = true, ignorePatterns = DefaultIgnorePatterns, overrides = {}, } = options;
|
|
34
34
|
const plugins = ["typescript", "unicorn", "oxc", ...(react ? ["react"] : [])];
|
|
35
35
|
const rules = {
|
|
36
36
|
// JavaScript
|
|
@@ -72,9 +72,13 @@ export function createOxlintConfig(options = {}) {
|
|
|
72
72
|
// Warning severity: a stylistic nudge (auto-fixable), not a hard CI gate.
|
|
73
73
|
rules["sister-software/padding-lines"] = "warn";
|
|
74
74
|
}
|
|
75
|
+
if (braces) {
|
|
76
|
+
// Warning severity: a stylistic nudge (auto-fixable), not a hard CI gate.
|
|
77
|
+
rules["sister-software/require-braces"] = "warn";
|
|
78
|
+
}
|
|
75
79
|
return {
|
|
76
80
|
plugins,
|
|
77
|
-
...(headers || padding ? { jsPlugins: ["@sister.software/oxlint-config/plugin"] } : {}),
|
|
81
|
+
...(headers || padding || braces ? { jsPlugins: ["@sister.software/oxlint-config/plugin"] } : {}),
|
|
78
82
|
categories: { correctness: "error" },
|
|
79
83
|
ignorePatterns,
|
|
80
84
|
rules,
|
package/out/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAE1D,cAAc,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAE1D,cAAc,mBAAmB,CAAA;AA6BjC,0DAA0D;AAC1D,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACpC,QAAQ;IACR,SAAS;IACT,mBAAmB;IACnB,iBAAiB;IACjB,aAAa;IACb,qBAAqB;CACrB,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAA+B,EAAE;IACnE,MAAM,EACL,gBAAgB,GAAG,kBAAkB,EACrC,eAAe,GAAG,iBAAiB,EACnC,qBAAqB,GAAG,YAAY,EACpC,MAAM,GAAG,sBAAsB,EAC/B,KAAK,GAAG,KAAK,EACb,OAAO,GAAG,IAAI,EACd,OAAO,GAAG,IAAI,EACd,MAAM,GAAG,IAAI,EACb,cAAc,GAAG,qBAAqB,EACtC,SAAS,GAAG,EAAE,GACd,GAAG,OAAO,CAAA;IAEX,MAAM,OAAO,GAAG,CAAC,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAE7E,MAAM,KAAK,GAA4B;QACtC,aAAa;QACb,MAAM,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC/C,cAAc,EAAE,MAAM;QACtB,kBAAkB,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC;QACtC,WAAW,EAAE,KAAK;QAClB,UAAU,EAAE,KAAK;QACjB,gBAAgB,EAAE;YACjB,MAAM;YACN;gBACC,IAAI,EAAE,KAAK;gBACX,iBAAiB,EAAE,IAAI;gBACvB,YAAY,EAAE,KAAK;gBACnB,yBAAyB,EAAE,IAAI;gBAC/B,8BAA8B,EAAE,IAAI;gBACpC,uFAAuF;gBACvF,gFAAgF;gBAChF,iBAAiB,EAAE,MAAM;gBACzB,kBAAkB,EAAE,IAAI;aACxB;SACD;QAED,2EAA2E;QAC3E,2BAA2B,EAAE,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,wBAAwB,EAAE,CAAC;QAChF,sBAAsB,EAAE,KAAK;QAC7B,+BAA+B,EAAE,KAAK;QACtC,4BAA4B,EAAE,KAAK;QACnC,2BAA2B,EAAE,KAAK;QAClC,kCAAkC,EAAE,KAAK;QACzC,4BAA4B,EAAE,KAAK;QACnC,+BAA+B,EAAE,KAAK;KACtC,CAAA;IAED,IAAI,OAAO,EAAE,CAAC;QACb,0FAA0F;QAC1F,oFAAoF;QACpF,KAAK,CAAC,qCAAqC,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,EAAE,CAAC,CAAA;IAC7G,CAAC;IAED,IAAI,OAAO,EAAE,CAAC;QACb,0EAA0E;QAC1E,KAAK,CAAC,+BAA+B,CAAC,GAAG,MAAM,CAAA;IAChD,CAAC;IAED,IAAI,MAAM,EAAE,CAAC;QACZ,0EAA0E;QAC1E,KAAK,CAAC,gCAAgC,CAAC,GAAG,MAAM,CAAA;IACjD,CAAC;IAED,OAAO;QACN,OAAO;QACP,GAAG,CAAC,OAAO,IAAI,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,uCAAuC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjG,UAAU,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE;QACpC,cAAc;QACd,KAAK;QACL,SAAS,EAAE,sBAAsB,CAAC,gBAAgB,CAAC;QACnD,GAAG,SAAS;KACZ,CAAA;AACF,CAAC;AAED,eAAe,kBAAkB,CAAA"}
|
package/out/plugin-types.d.ts
CHANGED
|
@@ -15,7 +15,11 @@ export interface AstNode {
|
|
|
15
15
|
type: string;
|
|
16
16
|
range: [number, number];
|
|
17
17
|
parent?: AstNode;
|
|
18
|
-
body
|
|
18
|
+
/** A statement-list body (block/program) is an array; a loop body is a single statement. */
|
|
19
|
+
body?: AstNode[] | AstNode;
|
|
20
|
+
/** `if`/`else` branches, for the require-braces rule. */
|
|
21
|
+
consequent?: AstNode;
|
|
22
|
+
alternate?: AstNode | null;
|
|
19
23
|
}
|
|
20
24
|
export interface SourceCode {
|
|
21
25
|
getText(): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-types.d.ts","sourceRoot":"","sources":["../src/plugin-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,OAAO;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACvB;AAED,yEAAyE;AACzE,MAAM,WAAW,OAAO;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"plugin-types.d.ts","sourceRoot":"","sources":["../src/plugin-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,OAAO;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACvB;AAED,yEAAyE;AACzE,MAAM,WAAW,OAAO;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,4FAA4F;IAC5F,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IAC1B,yDAAyD;IACzD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;CAC1B;AAED,MAAM,WAAW,UAAU;IAC1B,OAAO,IAAI,MAAM,CAAA;IACjB,cAAc,IAAI,OAAO,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,KAAK;IACrB,qBAAqB,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IACrE,oBAAoB,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IACpE,gBAAgB,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;CAChE;AAED,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,OAAO,EAAE,CAAA;IAClB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,aAAa,CAAC,IAAI,UAAU,CAAA;IAC5B,MAAM,CAAC,UAAU,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAA;KAAE,GAAG,IAAI,CAAA;CACzF;AAED,MAAM,WAAW,IAAI;IACpB,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;QAAC,MAAM,EAAE,OAAO,EAAE,CAAA;KAAE,CAAA;IACxF,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,CAAA;CACrE;AAED,MAAM,WAAW,MAAM;IACtB,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;CAC3B"}
|
package/out/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAE/C,QAAA,MAAM,oBAAoB,EAAE,MAO3B,CAAA;AAED,eAAe,oBAAoB,CAAA"}
|
package/out/plugin.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* @file The bundled Sister Software oxlint JS plugin. A single plugin (oxlint requires unique plugin
|
|
6
6
|
* names) exposing all of Sister Software's custom rules under the `sister-software/` namespace.
|
|
7
7
|
*/
|
|
8
|
+
import { bracesRule } from "./braces-plugin.js";
|
|
8
9
|
import { headerRule } from "./headers-plugin.js";
|
|
9
10
|
import { paddingRule } from "./padding-plugin.js";
|
|
10
11
|
const sisterSoftwarePlugin = {
|
|
@@ -12,6 +13,7 @@ const sisterSoftwarePlugin = {
|
|
|
12
13
|
rules: {
|
|
13
14
|
"require-file-header": headerRule,
|
|
14
15
|
"padding-lines": paddingRule,
|
|
16
|
+
"require-braces": bracesRule,
|
|
15
17
|
},
|
|
16
18
|
};
|
|
17
19
|
export default sisterSoftwarePlugin;
|
package/out/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAGjD,MAAM,oBAAoB,GAAW;IACpC,IAAI,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;IACjC,KAAK,EAAE;QACN,qBAAqB,EAAE,UAAU;QACjC,eAAe,EAAE,WAAW;KAC5B;CACD,CAAA;AAED,eAAe,oBAAoB,CAAA"}
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAGjD,MAAM,oBAAoB,GAAW;IACpC,IAAI,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;IACjC,KAAK,EAAE;QACN,qBAAqB,EAAE,UAAU;QACjC,eAAe,EAAE,WAAW;QAC5B,gBAAgB,EAAE,UAAU;KAC5B;CACD,CAAA;AAED,eAAe,oBAAoB,CAAA"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
* @file A require-braces rule, authored as an oxlint JS plugin (ESLint v9-compatible API). It
|
|
6
|
+
* requires braces around single-statement `if`/`else`/`for`/`while`/`do` bodies that do work
|
|
7
|
+
* (assignments, calls, …) and autofixes by wrapping them — but leaves a bare control-flow terminal
|
|
8
|
+
* (`return`/`throw`/`break`/`continue`) alone (braces optional). Unlike the built-in `curly` rule,
|
|
9
|
+
* which can't exempt those.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { AstNode, Rule } from "./plugin-types.js"
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Body node types that may stay brace-less: an existing block; a bare control-flow terminal
|
|
16
|
+
* (`return`/`throw`/`break`/`continue`, exempt by house style); or an empty statement (a different concern —
|
|
17
|
+
* `no-empty`).
|
|
18
|
+
*/
|
|
19
|
+
const EXEMPT_BODY_TYPES = new Set([
|
|
20
|
+
"BlockStatement",
|
|
21
|
+
"ReturnStatement",
|
|
22
|
+
"ThrowStatement",
|
|
23
|
+
"BreakStatement",
|
|
24
|
+
"ContinueStatement",
|
|
25
|
+
"EmptyStatement",
|
|
26
|
+
])
|
|
27
|
+
|
|
28
|
+
export const bracesRule: Rule = {
|
|
29
|
+
meta: {
|
|
30
|
+
name: "require-braces",
|
|
31
|
+
type: "layout",
|
|
32
|
+
fixable: "code",
|
|
33
|
+
schema: [{ type: "object", additionalProperties: true }],
|
|
34
|
+
},
|
|
35
|
+
create(context) {
|
|
36
|
+
const sourceCode = context.sourceCode ?? context.getSourceCode!()
|
|
37
|
+
|
|
38
|
+
function check(body: AstNode | null | undefined) {
|
|
39
|
+
if (!body || EXEMPT_BODY_TYPES.has(body.type)) return
|
|
40
|
+
|
|
41
|
+
// `else if (…)` — the alternate is itself an `if`; its own body is visited separately.
|
|
42
|
+
if (body.type === "IfStatement") return
|
|
43
|
+
|
|
44
|
+
const inner = sourceCode.getText().slice(body.range[0], body.range[1])
|
|
45
|
+
|
|
46
|
+
context.report({
|
|
47
|
+
node: body,
|
|
48
|
+
message: "Expected braces around this single-statement body.",
|
|
49
|
+
fix(fixer) {
|
|
50
|
+
// Wrap inline; oxfmt expands it to a formatted multi-line block afterward.
|
|
51
|
+
return fixer.replaceTextRange(body.range, `{ ${inner} }`)
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
IfStatement(node) {
|
|
58
|
+
check(node.consequent)
|
|
59
|
+
check(node.alternate)
|
|
60
|
+
},
|
|
61
|
+
ForStatement(node) {
|
|
62
|
+
checkLoopBody(node)
|
|
63
|
+
},
|
|
64
|
+
ForInStatement(node) {
|
|
65
|
+
checkLoopBody(node)
|
|
66
|
+
},
|
|
67
|
+
ForOfStatement(node) {
|
|
68
|
+
checkLoopBody(node)
|
|
69
|
+
},
|
|
70
|
+
WhileStatement(node) {
|
|
71
|
+
checkLoopBody(node)
|
|
72
|
+
},
|
|
73
|
+
DoWhileStatement(node) {
|
|
74
|
+
checkLoopBody(node)
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function checkLoopBody(node: AstNode) {
|
|
79
|
+
const body = node.body
|
|
80
|
+
|
|
81
|
+
// A loop body is a single statement (never a statement-list array).
|
|
82
|
+
if (body && !Array.isArray(body)) {
|
|
83
|
+
check(body)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default bracesRule
|
package/src/index.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface OxlintConfigOptions {
|
|
|
28
28
|
headers?: boolean
|
|
29
29
|
/** Require a blank line before `return`/block-like statements (on by default). */
|
|
30
30
|
padding?: boolean
|
|
31
|
+
/** Require braces around single-statement work bodies (bare `return` exempt; on by default). */
|
|
32
|
+
braces?: boolean
|
|
31
33
|
/** Override the default ignore patterns. */
|
|
32
34
|
ignorePatterns?: string[]
|
|
33
35
|
/** Extra config deep-merged last; an escape hatch for per-repo tweaks. */
|
|
@@ -67,6 +69,7 @@ export function createOxlintConfig(options: OxlintConfigOptions = {}): OxlintCon
|
|
|
67
69
|
react = false,
|
|
68
70
|
headers = true,
|
|
69
71
|
padding = true,
|
|
72
|
+
braces = true,
|
|
70
73
|
ignorePatterns = DefaultIgnorePatterns,
|
|
71
74
|
overrides = {},
|
|
72
75
|
} = options
|
|
@@ -117,9 +120,14 @@ export function createOxlintConfig(options: OxlintConfigOptions = {}): OxlintCon
|
|
|
117
120
|
rules["sister-software/padding-lines"] = "warn"
|
|
118
121
|
}
|
|
119
122
|
|
|
123
|
+
if (braces) {
|
|
124
|
+
// Warning severity: a stylistic nudge (auto-fixable), not a hard CI gate.
|
|
125
|
+
rules["sister-software/require-braces"] = "warn"
|
|
126
|
+
}
|
|
127
|
+
|
|
120
128
|
return {
|
|
121
129
|
plugins,
|
|
122
|
-
...(headers || padding ? { jsPlugins: ["@sister.software/oxlint-config/plugin"] } : {}),
|
|
130
|
+
...(headers || padding || braces ? { jsPlugins: ["@sister.software/oxlint-config/plugin"] } : {}),
|
|
123
131
|
categories: { correctness: "error" },
|
|
124
132
|
ignorePatterns,
|
|
125
133
|
rules,
|
package/src/plugin-types.ts
CHANGED
|
@@ -17,7 +17,11 @@ export interface AstNode {
|
|
|
17
17
|
type: string
|
|
18
18
|
range: [number, number]
|
|
19
19
|
parent?: AstNode
|
|
20
|
-
body
|
|
20
|
+
/** A statement-list body (block/program) is an array; a loop body is a single statement. */
|
|
21
|
+
body?: AstNode[] | AstNode
|
|
22
|
+
/** `if`/`else` branches, for the require-braces rule. */
|
|
23
|
+
consequent?: AstNode
|
|
24
|
+
alternate?: AstNode | null
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
export interface SourceCode {
|
package/src/plugin.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* names) exposing all of Sister Software's custom rules under the `sister-software/` namespace.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { bracesRule } from "./braces-plugin.js"
|
|
9
10
|
import { headerRule } from "./headers-plugin.js"
|
|
10
11
|
import { paddingRule } from "./padding-plugin.js"
|
|
11
12
|
import type { Plugin } from "./plugin-types.js"
|
|
@@ -15,6 +16,7 @@ const sisterSoftwarePlugin: Plugin = {
|
|
|
15
16
|
rules: {
|
|
16
17
|
"require-file-header": headerRule,
|
|
17
18
|
"padding-lines": paddingRule,
|
|
19
|
+
"require-braces": bracesRule,
|
|
18
20
|
},
|
|
19
21
|
}
|
|
20
22
|
|