oxlint-plugin-arch 0.6.2 → 0.6.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.3
4
+
5
+ - Add a trailing comma when `object-multiline` splits an object, so the fix agrees with multiline comma rules.
6
+
7
+ - Add opt-in `ternary-layout` and `call-arguments-multiline` layout rules with configurable thresholds and indentation. Presets remain unchanged.
8
+
3
9
  ## 0.6.2
4
10
 
5
11
  - Export `oxlint-plugin-arch/rules/dillon-anti-slop` and `oxlint-plugin-arch/rules/module-policy` with TypeScript declarations. Resolve the preset's three first-party plugins through public package entry points, without changing rules or policy. Document standalone registration and third-party dependency ownership; verify built exports expose every supporting rule.
package/README.md CHANGED
@@ -6,7 +6,7 @@ Keep a codebase shaped the way you designed it: files in the right folders, expo
6
6
 
7
7
  - 🗂️ **Filename-aware.** Rules derive expected names from the file they run on (`user-action.create.ts` → `makeUserActionCreate`).
8
8
  - 🎛️ **Configurable.** No app names, folders, or libraries are baked in. Globs and policy live in your config.
9
- - 🛟 **Safe autofix.** Layout and comment rules only touch whitespace or comments. Type-import fixes qualify scope-resolved references and skip unsafe rewrites.
9
+ - 🛟 **Safe autofix.** Layout and comment rules touch whitespace or comments; `object-multiline` and `call-arguments-multiline` can also add trailing commas. Type-import fixes qualify scope-resolved references and skip unsafe rewrites.
10
10
  - ⚡ **Fast.** Built on Oxlint's JS plugin API and ESTree, with no extra parser.
11
11
 
12
12
  ---
@@ -427,7 +427,9 @@ Options that take one regex string (`pattern`, `allowPattern`, `forbid`, `requir
427
427
  | Rule | What it enforces |
428
428
  | --- | --- |
429
429
  | `padding-between-statements` 🔧 | Blank lines around functions and classes, around control flow, and before `return` in longer blocks; `multilineVariables: true` also separates multiline declarations, including exports (default `false`). |
430
- | `object-multiline` 🔧 | Objects with 3+ properties passed to a call go one property per line (`scope: 'all'` for every object). |
430
+ | `object-multiline` 🔧 | Objects with 3+ properties passed to a call go one property per line with a trailing comma (`scope: 'all'` for every object). |
431
+ | `ternary-layout` 🔧 | Join short conditionals or split long ones before `?` and `:`. [Details ↓](#ternary-layout) |
432
+ | `call-arguments-multiline` 🔧 | Calls and constructors with 3+ arguments go one argument per line. [Details ↓](#call-arguments-multiline) |
431
433
  | `key-value-same-line` 🔧 | An object key and the start of its value stay on the same line. |
432
434
  | `chain-newline` 🔧 | Long method chains go one call per line. You pick which chains with `groups`. |
433
435
  | `call-array-multiline` 🔧 | Arrays passed to `Promise.all` (or callees you list) go one element per line. |
@@ -440,6 +442,18 @@ Options that take one regex string (`pattern`, `allowPattern`, `forbid`, `requir
440
442
 
441
443
  ## 🔍 Rule details
442
444
 
445
+ ### ternary-layout
446
+
447
+ Options: `maxLength` (integer ≥ 1, default `160`) and `indent` (integer ≥ 1, default `2` spaces). The full resulting line, including its prefix and suffix, determines whether a conditional fits. Short multiline conditionals join; long single-line conditionals split before `?` and `:`, one indent deeper than their starting line. Direct template interpolations stay on one line regardless of length.
448
+
449
+ Conditionals with a conditional parent or operand, comments inside, or multiline operands are skipped. Existing long multiline conditionals are left unchanged.
450
+
451
+ ### call-arguments-multiline
452
+
453
+ Options: `minArguments` (integer ≥ 2, default `3`) and `indent` (integer ≥ 1, default `2` spaces). Calls and `new` expressions at the threshold put each argument and the closing `)` on separate lines. Arguments use one indent deeper than the line containing `(`. Fixes add a trailing comma, including after spread arguments.
454
+
455
+ Calls already using separate lines for every argument and `)` are left unchanged, including their indentation and optional trailing comma. Calls containing comments, parenthesized arguments, or multiline arrow/function arguments are skipped. These are code fixes because they can add punctuation. Both new rules are opt-in and absent from presets.
456
+
443
457
  ### File size
444
458
 
445
459
  `limits.maxFileLines?: number` sets the existing ESLint `max-lines` ceiling on checked safety scopes; default `400`. Blank lines and comments remain skipped. Use native Oxlint overrides or the existing `ruleExclusions` configuration for deliberate per-file exceptions. File size is a ceiling, not a cohesion proof.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/index.ts
2
2
  import { eslintCompatPlugin } from "@oxlint/plugins";
3
3
 
4
- // src/rules/call-array-multiline.ts
4
+ // src/rules/call-arguments-multiline.ts
5
5
  import { defineRule } from "@oxlint/plugins";
6
6
 
7
7
  // src/utils/helpers/ast.ts
@@ -981,7 +981,91 @@ function boundaryResolveTarget(options, file, source) {
981
981
  const alias = Object.entries(options.aliases).sort(([a], [b]) => b.length - a.length).find(([prefix]) => source === prefix || source.startsWith(`${prefix}/`));
982
982
  return alias ? resolve(alias[1], source.slice(alias[0].length).replace(/^\//, "")).replaceAll("\\", "/") : source.startsWith(".") ? resolve(dirname(file), source).replaceAll("\\", "/") : source.startsWith("@/") && web ? `${web}/${source.slice(2)}` : source.startsWith("#/") && owner ? `${owner}/${source.slice(2)}` : source.startsWith("#services/") && owner ? `${owner}/services/${source.slice(10)}` : source;
983
983
  }
984
+ // src/rules/call-arguments-multiline.ts
985
+ function callArgumentsIsCallback(node) {
986
+ return (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") && node.loc.start.line !== node.loc.end.line;
987
+ }
988
+ var callArgumentsMultiline = defineRule({
989
+ meta: {
990
+ type: "layout",
991
+ fixable: "code",
992
+ schema: [
993
+ {
994
+ type: "object",
995
+ additionalProperties: false,
996
+ properties: {
997
+ minArguments: {
998
+ type: "integer",
999
+ minimum: 2,
1000
+ default: 3
1001
+ },
1002
+ indent: {
1003
+ type: "integer",
1004
+ minimum: 1,
1005
+ default: 2
1006
+ }
1007
+ }
1008
+ }
1009
+ ],
1010
+ messages: {
1011
+ multiline: "A call with {{min}}+ arguments puts each argument on its own line."
1012
+ }
1013
+ },
1014
+ createOnce(context) {
1015
+ let minArguments = 3;
1016
+ let indent = 2;
1017
+ const check = (node) => {
1018
+ const args = node.arguments;
1019
+ if (args.length < minArguments || args.some(callArgumentsIsCallback) || context.sourceCode.getCommentsInside(node).length > 0) {
1020
+ return;
1021
+ }
1022
+ const text = context.sourceCode.text;
1023
+ const open = text.indexOf("(", (node.typeArguments ?? node.callee).range[1]);
1024
+ const close = node.range[1] - 1;
1025
+ const first = args[0];
1026
+ const last = args[args.length - 1];
1027
+ const gaps = [[open + 1, first.range[0]]];
1028
+ for (let index = 1;index < args.length; index++) {
1029
+ gaps.push([args[index - 1].range[1], args[index].range[0]]);
1030
+ }
1031
+ gaps.push([last.range[1], close]);
1032
+ const between = gaps.map(([start, end]) => text.slice(start, end));
1033
+ if (text[open] !== "(" || text[close] !== ")" || !/^\s*$/.test(between[0]) || between.slice(1, -1).some((gap) => !/^\s*,\s*$/.test(gap)) || !/^\s*,?\s*$/.test(between[between.length - 1])) {
1034
+ return;
1035
+ }
1036
+ if (between.every((gap) => gap.includes(`
1037
+ `))) {
1038
+ return;
1039
+ }
1040
+ const baseIndent = layoutLineIndent(text, open);
1041
+ const newline = layoutNewline(text);
1042
+ const argumentIndent = `${baseIndent}${" ".repeat(indent)}`;
1043
+ context.report({
1044
+ node,
1045
+ messageId: "multiline",
1046
+ data: { min: String(minArguments) },
1047
+ fix: (fixer) => gaps.map((gap, index) => {
1048
+ if (index === 0) {
1049
+ return fixer.replaceTextRange(gap, `${newline}${argumentIndent}`);
1050
+ }
1051
+ return fixer.replaceTextRange(gap, index === gaps.length - 1 ? `,${newline}${baseIndent}` : `,${newline}${argumentIndent}`);
1052
+ })
1053
+ });
1054
+ };
1055
+ return {
1056
+ before() {
1057
+ const options2 = optionsFirst(context, {});
1058
+ minArguments = options2.minArguments ?? 3;
1059
+ indent = options2.indent ?? 2;
1060
+ },
1061
+ CallExpression: check,
1062
+ NewExpression: check
1063
+ };
1064
+ }
1065
+ });
1066
+
984
1067
  // src/rules/call-array-multiline.ts
1068
+ import { defineRule as defineRule2 } from "@oxlint/plugins";
985
1069
  function callArrayMultilineCallee(node) {
986
1070
  if (node.type === "Identifier")
987
1071
  return { name: node.name, root: node };
@@ -1048,7 +1132,7 @@ function callArrayMultilineEdits(array, source, text, elementIndent, baseIndent,
1048
1132
  });
1049
1133
  return edits;
1050
1134
  }
1051
- var callArrayMultiline = defineRule({
1135
+ var callArrayMultiline = defineRule2({
1052
1136
  meta: {
1053
1137
  type: "layout",
1054
1138
  fixable: "whitespace",
@@ -1110,7 +1194,7 @@ var callArrayMultiline = defineRule({
1110
1194
  });
1111
1195
 
1112
1196
  // src/rules/chain-newline.ts
1113
- import { defineRule as defineRule2 } from "@oxlint/plugins";
1197
+ import { defineRule as defineRule3 } from "@oxlint/plugins";
1114
1198
  function chainNewlineCollect(node) {
1115
1199
  const links = [];
1116
1200
  let current = node;
@@ -1147,7 +1231,7 @@ function chainNewlineMatches(root, links, group) {
1147
1231
  const rootName = chainNewlineRootIdentifier(root);
1148
1232
  return rootName !== null && optionsPatternsTest(group.rootPatterns, rootName);
1149
1233
  }
1150
- var chainNewline = defineRule2({
1234
+ var chainNewline = defineRule3({
1151
1235
  meta: {
1152
1236
  type: "layout",
1153
1237
  fixable: "whitespace",
@@ -1253,7 +1337,7 @@ var chainNewline = defineRule2({
1253
1337
  });
1254
1338
 
1255
1339
  // src/rules/component-props.ts
1256
- import { defineRule as defineRule3 } from "@oxlint/plugins";
1340
+ import { defineRule as defineRule4 } from "@oxlint/plugins";
1257
1341
  function unwrapExport(statement) {
1258
1342
  return statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" ? statement.declaration : statement;
1259
1343
  }
@@ -1303,7 +1387,7 @@ function isPassThrough(type) {
1303
1387
  const argument = type.typeArguments?.params.length === 1 ? type.typeArguments.params[0] : undefined;
1304
1388
  return name === "ComponentProps" && (argument?.type === "TSTypeQuery" || argument?.type === "TSLiteralType" && argument.literal.type === "Literal" && typeof argument.literal.value === "string");
1305
1389
  }
1306
- var componentProps = defineRule3({
1390
+ var componentProps = defineRule4({
1307
1391
  meta: {
1308
1392
  type: "problem",
1309
1393
  schema: [
@@ -1374,8 +1458,8 @@ var componentProps = defineRule3({
1374
1458
  });
1375
1459
 
1376
1460
  // src/rules/declaration-name.ts
1377
- import { defineRule as defineRule4 } from "@oxlint/plugins";
1378
- var declarationName = defineRule4({
1461
+ import { defineRule as defineRule5 } from "@oxlint/plugins";
1462
+ var declarationName = defineRule5({
1379
1463
  meta: {
1380
1464
  type: "problem",
1381
1465
  schema: [
@@ -1452,8 +1536,8 @@ var declarationName = defineRule4({
1452
1536
  });
1453
1537
 
1454
1538
  // src/rules/export-file-prefix.ts
1455
- import { defineRule as defineRule5 } from "@oxlint/plugins";
1456
- var exportFilePrefix = defineRule5({
1539
+ import { defineRule as defineRule6 } from "@oxlint/plugins";
1540
+ var exportFilePrefix = defineRule6({
1457
1541
  meta: {
1458
1542
  type: "problem",
1459
1543
  schema: [
@@ -1518,8 +1602,8 @@ var exportFilePrefix = defineRule5({
1518
1602
  });
1519
1603
 
1520
1604
  // src/rules/export-name-pattern.ts
1521
- import { defineRule as defineRule6 } from "@oxlint/plugins";
1522
- var exportNamePattern = defineRule6({
1605
+ import { defineRule as defineRule7 } from "@oxlint/plugins";
1606
+ var exportNamePattern = defineRule7({
1523
1607
  meta: {
1524
1608
  type: "problem",
1525
1609
  schema: [
@@ -1572,8 +1656,8 @@ var exportNamePattern = defineRule6({
1572
1656
  });
1573
1657
 
1574
1658
  // src/rules/filename-export-name.ts
1575
- import { defineRule as defineRule7 } from "@oxlint/plugins";
1576
- var filenameExportName = defineRule7({
1659
+ import { defineRule as defineRule8 } from "@oxlint/plugins";
1660
+ var filenameExportName = defineRule8({
1577
1661
  meta: {
1578
1662
  type: "problem",
1579
1663
  schema: [
@@ -1638,8 +1722,8 @@ var filenameExportName = defineRule7({
1638
1722
  });
1639
1723
 
1640
1724
  // src/rules/filename-match.ts
1641
- import { defineRule as defineRule8 } from "@oxlint/plugins";
1642
- var filenameMatch = defineRule8({
1725
+ import { defineRule as defineRule9 } from "@oxlint/plugins";
1726
+ var filenameMatch = defineRule9({
1643
1727
  meta: {
1644
1728
  type: "problem",
1645
1729
  schema: [
@@ -1676,8 +1760,8 @@ var filenameMatch = defineRule8({
1676
1760
  });
1677
1761
 
1678
1762
  // src/rules/folder-prefix.ts
1679
- import { defineRule as defineRule9 } from "@oxlint/plugins";
1680
- var folderPrefix = defineRule9({
1763
+ import { defineRule as defineRule10 } from "@oxlint/plugins";
1764
+ var folderPrefix = defineRule10({
1681
1765
  meta: {
1682
1766
  type: "problem",
1683
1767
  schema: [
@@ -1735,7 +1819,7 @@ var folderPrefix = defineRule9({
1735
1819
  });
1736
1820
 
1737
1821
  // src/rules/jsx-attributes-multiline.ts
1738
- import { defineRule as defineRule10 } from "@oxlint/plugins";
1822
+ import { defineRule as defineRule11 } from "@oxlint/plugins";
1739
1823
  function jsxAttributesMultilineEdits(node, text, attributeIndent, baseIndent, newline) {
1740
1824
  const first = node.attributes[0];
1741
1825
  const last = node.attributes.at(-1);
@@ -1766,7 +1850,7 @@ function jsxAttributesMultilineEdits(node, text, attributeIndent, baseIndent, ne
1766
1850
  edits.push({ range: closingRange, text: `${newline}${baseIndent}` });
1767
1851
  return edits;
1768
1852
  }
1769
- var jsxAttributesMultiline = defineRule10({
1853
+ var jsxAttributesMultiline = defineRule11({
1770
1854
  meta: {
1771
1855
  type: "layout",
1772
1856
  fixable: "whitespace",
@@ -1815,8 +1899,8 @@ var jsxAttributesMultiline = defineRule10({
1815
1899
  });
1816
1900
 
1817
1901
  // src/rules/key-value-same-line.ts
1818
- import { defineRule as defineRule11 } from "@oxlint/plugins";
1819
- var keyValueSameLine = defineRule11({
1902
+ import { defineRule as defineRule12 } from "@oxlint/plugins";
1903
+ var keyValueSameLine = defineRule12({
1820
1904
  meta: {
1821
1905
  type: "layout",
1822
1906
  fixable: "whitespace",
@@ -1865,7 +1949,7 @@ var keyValueSameLine = defineRule11({
1865
1949
  });
1866
1950
 
1867
1951
  // src/rules/no-comments.ts
1868
- import { defineRule as defineRule12 } from "@oxlint/plugins";
1952
+ import { defineRule as defineRule13 } from "@oxlint/plugins";
1869
1953
  var NO_COMMENTS_LINE_GAP = /^[\t \r\n\u2028\u2029]*$/;
1870
1954
  var NO_COMMENTS_LINE_TERMINATOR = /\r\n|[\n\r\u2028\u2029]/;
1871
1955
  var NO_COMMENTS_TRAILING_LINES = /^[\t ]*(?:\r\n|[\n\r\u2028\u2029])(?:[\t ]*(?:\r\n|[\n\r\u2028\u2029]))*/;
@@ -1957,7 +2041,7 @@ function noCommentsJsxEmptyContainer(containers, start, end) {
1957
2041
  const container = containers[low - 1];
1958
2042
  return container?.range[1] >= end ? container : undefined;
1959
2043
  }
1960
- var noComments = defineRule12({
2044
+ var noComments = defineRule13({
1961
2045
  meta: {
1962
2046
  type: "suggestion",
1963
2047
  fixable: "code",
@@ -2089,8 +2173,8 @@ var noComments = defineRule12({
2089
2173
  });
2090
2174
 
2091
2175
  // src/rules/no-extra-exports.ts
2092
- import { defineRule as defineRule13 } from "@oxlint/plugins";
2093
- var noExtraExports = defineRule13({
2176
+ import { defineRule as defineRule14 } from "@oxlint/plugins";
2177
+ var noExtraExports = defineRule14({
2094
2178
  meta: {
2095
2179
  type: "problem",
2096
2180
  schema: [
@@ -2149,7 +2233,7 @@ var noExtraExports = defineRule13({
2149
2233
  });
2150
2234
 
2151
2235
  // src/rules/no-extra-factory-keys.ts
2152
- import { defineRule as defineRule14 } from "@oxlint/plugins";
2236
+ import { defineRule as defineRule15 } from "@oxlint/plugins";
2153
2237
  function compileFactoryKeys(pattern, keys, requireKeys) {
2154
2238
  const allowedList = keys.concat(requireKeys.filter((key) => !keys.includes(key)));
2155
2239
  return {
@@ -2159,7 +2243,7 @@ function compileFactoryKeys(pattern, keys, requireKeys) {
2159
2243
  requireKeys
2160
2244
  };
2161
2245
  }
2162
- var noExtraFactoryKeys = defineRule14({
2246
+ var noExtraFactoryKeys = defineRule15({
2163
2247
  meta: {
2164
2248
  type: "problem",
2165
2249
  schema: [
@@ -2263,8 +2347,8 @@ var noExtraFactoryKeys = defineRule14({
2263
2347
  });
2264
2348
 
2265
2349
  // src/rules/no-file-level-helpers.ts
2266
- import { defineRule as defineRule15 } from "@oxlint/plugins";
2267
- var noFileLevelHelpers = defineRule15({
2350
+ import { defineRule as defineRule16 } from "@oxlint/plugins";
2351
+ var noFileLevelHelpers = defineRule16({
2268
2352
  meta: {
2269
2353
  type: "problem",
2270
2354
  schema: [
@@ -2310,7 +2394,7 @@ var noFileLevelHelpers = defineRule15({
2310
2394
  });
2311
2395
 
2312
2396
  // src/rules/no-imported-type-alias.ts
2313
- import { defineRule as defineRule16 } from "@oxlint/plugins";
2397
+ import { defineRule as defineRule17 } from "@oxlint/plugins";
2314
2398
  function importedAliasTarget(declaration, imports) {
2315
2399
  if (declaration?.type !== "TSTypeAliasDeclaration" || declaration.typeAnnotation.type !== "TSTypeReference" || declaration.typeAnnotation.typeName.type !== "Identifier" || Boolean(declaration.typeAnnotation.typeArguments?.params.length) || !imports.has(declaration.typeAnnotation.typeName.name)) {
2316
2400
  return;
@@ -2320,7 +2404,7 @@ function importedAliasTarget(declaration, imports) {
2320
2404
  function specifierLocalName(specifier) {
2321
2405
  return specifier.local.type === "Identifier" ? specifier.local.name : specifier.local.value;
2322
2406
  }
2323
- var noImportedTypeAlias = defineRule16({
2407
+ var noImportedTypeAlias = defineRule17({
2324
2408
  meta: {
2325
2409
  type: "problem",
2326
2410
  schema: [],
@@ -2394,8 +2478,8 @@ var noImportedTypeAlias = defineRule16({
2394
2478
  });
2395
2479
 
2396
2480
  // src/rules/no-inline-schema-elements.ts
2397
- import { defineRule as defineRule17 } from "@oxlint/plugins";
2398
- var noInlineSchemaElements = defineRule17({
2481
+ import { defineRule as defineRule18 } from "@oxlint/plugins";
2482
+ var noInlineSchemaElements = defineRule18({
2399
2483
  meta: {
2400
2484
  type: "problem",
2401
2485
  schema: [
@@ -2456,7 +2540,7 @@ var noInlineSchemaElements = defineRule17({
2456
2540
  });
2457
2541
 
2458
2542
  // src/rules/no-inline-types.ts
2459
- import { defineRule as defineRule18 } from "@oxlint/plugins";
2543
+ import { defineRule as defineRule19 } from "@oxlint/plugins";
2460
2544
  function parameterAnnotation(parameter) {
2461
2545
  if (parameter.type === "TSParameterProperty") {
2462
2546
  return parameterAnnotation(parameter.parameter);
@@ -2466,7 +2550,7 @@ function parameterAnnotation(parameter) {
2466
2550
  }
2467
2551
  return parameter.typeAnnotation;
2468
2552
  }
2469
- var noInlineTypes = defineRule18({
2553
+ var noInlineTypes = defineRule19({
2470
2554
  meta: {
2471
2555
  type: "problem",
2472
2556
  schema: [
@@ -2536,7 +2620,7 @@ var noInlineTypes = defineRule18({
2536
2620
  });
2537
2621
 
2538
2622
  // src/rules/no-literal-in.ts
2539
- import { defineRule as defineRule19 } from "@oxlint/plugins";
2623
+ import { defineRule as defineRule20 } from "@oxlint/plugins";
2540
2624
  function literalKey(node) {
2541
2625
  if (node.type === "Literal" && typeof node.value === "string") {
2542
2626
  return node.value;
@@ -2545,7 +2629,7 @@ function literalKey(node) {
2545
2629
  return node.quasis[0]?.value.cooked ?? node.quasis[0]?.value.raw;
2546
2630
  }
2547
2631
  }
2548
- var noLiteralIn = defineRule19({
2632
+ var noLiteralIn = defineRule20({
2549
2633
  meta: {
2550
2634
  type: "suggestion",
2551
2635
  schema: [
@@ -2586,13 +2670,13 @@ var noLiteralIn = defineRule19({
2586
2670
  });
2587
2671
 
2588
2672
  // src/rules/no-local-schema-construction.ts
2589
- import { defineRule as defineRule20 } from "@oxlint/plugins";
2673
+ import { defineRule as defineRule21 } from "@oxlint/plugins";
2590
2674
  var defaultRuntimeImportMessage = "Do not import runtime schema constructors from {{source}}; import a named schema instead.";
2591
2675
  var defaultConstructionMessage = "Do not construct schemas locally; import a named schema instead.";
2592
2676
  function formatMessage(template, data) {
2593
2677
  return Object.entries(data).reduce((result, [key, value]) => result.replaceAll(`{{${key}}}`, value), template);
2594
2678
  }
2595
- var noLocalSchemaConstruction = defineRule20({
2679
+ var noLocalSchemaConstruction = defineRule21({
2596
2680
  meta: {
2597
2681
  type: "problem",
2598
2682
  schema: [
@@ -2714,8 +2798,8 @@ var noLocalSchemaConstruction = defineRule20({
2714
2798
  });
2715
2799
 
2716
2800
  // src/rules/no-member-comments.ts
2717
- import { defineRule as defineRule21 } from "@oxlint/plugins";
2718
- var noMemberComments = defineRule21({
2801
+ import { defineRule as defineRule22 } from "@oxlint/plugins";
2802
+ var noMemberComments = defineRule22({
2719
2803
  meta: {
2720
2804
  type: "suggestion",
2721
2805
  fixable: "code",
@@ -2811,7 +2895,7 @@ var noMemberComments = defineRule21({
2811
2895
  });
2812
2896
 
2813
2897
  // src/rules/no-module-mutable-state.ts
2814
- import { defineRule as defineRule22 } from "@oxlint/plugins";
2898
+ import { defineRule as defineRule23 } from "@oxlint/plugins";
2815
2899
  function moduleMutableStateBindings(pattern) {
2816
2900
  if (pattern.type === "Identifier") {
2817
2901
  return [{ name: pattern.name, node: pattern }];
@@ -2850,7 +2934,7 @@ function moduleMutableStateInsideAmbient(node) {
2850
2934
  }
2851
2935
  return false;
2852
2936
  }
2853
- var noModuleMutableState = defineRule22({
2937
+ var noModuleMutableState = defineRule23({
2854
2938
  meta: {
2855
2939
  type: "problem",
2856
2940
  schema: [
@@ -2902,7 +2986,7 @@ var noModuleMutableState = defineRule22({
2902
2986
  });
2903
2987
 
2904
2988
  // src/rules/no-promise-all-mutation.ts
2905
- import { defineRule as defineRule23 } from "@oxlint/plugins";
2989
+ import { defineRule as defineRule24 } from "@oxlint/plugins";
2906
2990
  function mutationBinding(node) {
2907
2991
  if (node.type === "Identifier") {
2908
2992
  return node;
@@ -2930,7 +3014,7 @@ function mutationBindings(node) {
2930
3014
  }
2931
3015
  }
2932
3016
  }
2933
- var noPromiseAllMutation = defineRule23({
3017
+ var noPromiseAllMutation = defineRule24({
2934
3018
  meta: {
2935
3019
  type: "problem",
2936
3020
  schema: [
@@ -3030,8 +3114,8 @@ var noPromiseAllMutation = defineRule23({
3030
3114
  });
3031
3115
 
3032
3116
  // src/rules/no-rederive-schema.ts
3033
- import { defineRule as defineRule24 } from "@oxlint/plugins";
3034
- var noRederiveSchema = defineRule24({
3117
+ import { defineRule as defineRule25 } from "@oxlint/plugins";
3118
+ var noRederiveSchema = defineRule25({
3035
3119
  meta: {
3036
3120
  type: "problem",
3037
3121
  schema: [
@@ -3075,7 +3159,7 @@ var noRederiveSchema = defineRule24({
3075
3159
  });
3076
3160
 
3077
3161
  // src/rules/no-restricted-constructor.ts
3078
- import { defineRule as defineRule25 } from "@oxlint/plugins";
3162
+ import { defineRule as defineRule26 } from "@oxlint/plugins";
3079
3163
  function restrictedConstructorEntries(constructors) {
3080
3164
  const entries = new Map;
3081
3165
  for (const item of constructors) {
@@ -3091,7 +3175,7 @@ function restrictedConstructorEntries(constructors) {
3091
3175
  }
3092
3176
  return entries;
3093
3177
  }
3094
- var noRestrictedConstructor = defineRule25({
3178
+ var noRestrictedConstructor = defineRule26({
3095
3179
  meta: {
3096
3180
  type: "problem",
3097
3181
  schema: [
@@ -3166,8 +3250,8 @@ var noRestrictedConstructor = defineRule25({
3166
3250
  });
3167
3251
 
3168
3252
  // src/rules/no-restricted-files.ts
3169
- import { defineRule as defineRule26 } from "@oxlint/plugins";
3170
- var noRestrictedFiles = defineRule26({
3253
+ import { defineRule as defineRule27 } from "@oxlint/plugins";
3254
+ var noRestrictedFiles = defineRule27({
3171
3255
  meta: {
3172
3256
  type: "problem",
3173
3257
  schema: [
@@ -3199,7 +3283,7 @@ var noRestrictedFiles = defineRule26({
3199
3283
  });
3200
3284
 
3201
3285
  // src/rules/no-restricted-token.ts
3202
- import { defineRule as defineRule27 } from "@oxlint/plugins";
3286
+ import { defineRule as defineRule28 } from "@oxlint/plugins";
3203
3287
  function tokenIsTypeOnlyImport(node) {
3204
3288
  const parent = node.parent;
3205
3289
  if (parent?.type === "ImportSpecifier") {
@@ -3210,7 +3294,7 @@ function tokenIsTypeOnlyImport(node) {
3210
3294
  function memberMatches(node, restriction) {
3211
3295
  return restriction.startsWith("*.") ? astStaticMemberName(node) === restriction.slice(2) : astDottedName(node) === restriction;
3212
3296
  }
3213
- var noRestrictedToken = defineRule27({
3297
+ var noRestrictedToken = defineRule28({
3214
3298
  meta: {
3215
3299
  type: "problem",
3216
3300
  schema: [
@@ -3307,7 +3391,7 @@ var noRestrictedToken = defineRule27({
3307
3391
  });
3308
3392
 
3309
3393
  // src/rules/no-runtime-in-types.ts
3310
- import { defineRule as defineRule28 } from "@oxlint/plugins";
3394
+ import { defineRule as defineRule29 } from "@oxlint/plugins";
3311
3395
  function isTypeOnlyImport(statement) {
3312
3396
  return statement.importKind === "type" || statement.specifiers.length > 0 && statement.specifiers.every((specifier) => specifier.type === "ImportSpecifier" && specifier.importKind === "type");
3313
3397
  }
@@ -3524,7 +3608,7 @@ function isRuntimeReExport(statement, runtimeImportLocals, valueNames, isAmbient
3524
3608
  }
3525
3609
  return statement.specifiers.some((specifier) => specifier.exportKind !== "type" && runtimeImportLocals.has(exportLocalName(specifier)));
3526
3610
  }
3527
- var noRuntimeInTypes = defineRule28({
3611
+ var noRuntimeInTypes = defineRule29({
3528
3612
  meta: {
3529
3613
  type: "problem",
3530
3614
  schema: [
@@ -3668,7 +3752,7 @@ var noRuntimeInTypes = defineRule28({
3668
3752
  });
3669
3753
 
3670
3754
  // src/rules/no-single-use-scalar-schema.ts
3671
- import { defineRule as defineRule29 } from "@oxlint/plugins";
3755
+ import { defineRule as defineRule30 } from "@oxlint/plugins";
3672
3756
  function isScalarSchema(node, namespaces, structuralMethods) {
3673
3757
  if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression") {
3674
3758
  return false;
@@ -3702,7 +3786,7 @@ function isAllowedStructuralElement(identifier, initializer, ancestors, options2
3702
3786
  const method = call.callee.type === "MemberExpression" ? astStaticMemberName(call.callee) : undefined;
3703
3787
  return Boolean(method && options2.methods.includes(method) && schemasElements(call).includes(identifier));
3704
3788
  }
3705
- var noSingleUseScalarSchema = defineRule29({
3789
+ var noSingleUseScalarSchema = defineRule30({
3706
3790
  meta: {
3707
3791
  type: "suggestion",
3708
3792
  schema: [
@@ -3787,8 +3871,8 @@ var noSingleUseScalarSchema = defineRule29({
3787
3871
  });
3788
3872
 
3789
3873
  // src/rules/no-top-level-functions.ts
3790
- import { defineRule as defineRule30 } from "@oxlint/plugins";
3791
- var noTopLevelFunctions = defineRule30({
3874
+ import { defineRule as defineRule31 } from "@oxlint/plugins";
3875
+ var noTopLevelFunctions = defineRule31({
3792
3876
  meta: {
3793
3877
  type: "problem",
3794
3878
  schema: [
@@ -3847,7 +3931,7 @@ var noTopLevelFunctions = defineRule30({
3847
3931
  });
3848
3932
 
3849
3933
  // src/rules/no-trivial-functions.ts
3850
- import { defineRule as defineRule31 } from "@oxlint/plugins";
3934
+ import { defineRule as defineRule32 } from "@oxlint/plugins";
3851
3935
 
3852
3936
  // src/constants/trivial.constants.ts
3853
3937
  var trivialBannedNames = [
@@ -3863,7 +3947,7 @@ var trivialBannedNames = [
3863
3947
  ];
3864
3948
 
3865
3949
  // src/rules/no-trivial-functions.ts
3866
- var noTrivialFunctions = defineRule31({
3950
+ var noTrivialFunctions = defineRule32({
3867
3951
  meta: {
3868
3952
  type: "problem",
3869
3953
  schema: [
@@ -3980,8 +4064,8 @@ var noTrivialFunctions = defineRule31({
3980
4064
  });
3981
4065
 
3982
4066
  // src/rules/no-type-declarations.ts
3983
- import { defineRule as defineRule32 } from "@oxlint/plugins";
3984
- var noTypeDeclarations = defineRule32({
4067
+ import { defineRule as defineRule33 } from "@oxlint/plugins";
4068
+ var noTypeDeclarations = defineRule33({
3985
4069
  meta: {
3986
4070
  type: "problem",
3987
4071
  schema: [
@@ -4026,8 +4110,8 @@ var noTypeDeclarations = defineRule32({
4026
4110
  });
4027
4111
 
4028
4112
  // src/rules/no-unescaped-like.ts
4029
- import { defineRule as defineRule33 } from "@oxlint/plugins";
4030
- var noUnescapedLike = defineRule33({
4113
+ import { defineRule as defineRule34 } from "@oxlint/plugins";
4114
+ var noUnescapedLike = defineRule34({
4031
4115
  meta: {
4032
4116
  type: "problem",
4033
4117
  schema: [
@@ -4078,7 +4162,7 @@ var noUnescapedLike = defineRule33({
4078
4162
  });
4079
4163
 
4080
4164
  // src/rules/object-multiline.ts
4081
- import { defineRule as defineRule34 } from "@oxlint/plugins";
4165
+ import { defineRule as defineRule35 } from "@oxlint/plugins";
4082
4166
  function objectMultilineIsCallArg(node) {
4083
4167
  const parent = node.parent;
4084
4168
  return (parent.type === "CallExpression" || parent.type === "NewExpression") && parent.arguments.includes(node);
@@ -4111,14 +4195,14 @@ function objectMultilineGaps(text, node) {
4111
4195
  if (!objectMultilineIsWhitespace(between.slice(0, comma)) || !objectMultilineIsWhitespace(between.slice(comma + 1))) {
4112
4196
  return null;
4113
4197
  }
4114
- gaps.push([after + comma + 1, limit]);
4198
+ gaps.push([next ? after + comma + 1 : after, limit]);
4115
4199
  }
4116
4200
  return gaps;
4117
4201
  }
4118
- var objectMultiline = defineRule34({
4202
+ var objectMultiline = defineRule35({
4119
4203
  meta: {
4120
4204
  type: "layout",
4121
- fixable: "whitespace",
4205
+ fixable: "code",
4122
4206
  schema: [
4123
4207
  {
4124
4208
  type: "object",
@@ -4168,7 +4252,7 @@ var objectMultiline = defineRule34({
4168
4252
  if (!gaps) {
4169
4253
  return null;
4170
4254
  }
4171
- return gaps.map((gap, index) => fixer.replaceTextRange(gap, `${newline}${index === gaps.length - 1 ? baseIndent : propIndent}`));
4255
+ return gaps.map((gap, index) => fixer.replaceTextRange(gap, index === gaps.length - 1 ? `,${newline}${baseIndent}` : `${newline}${propIndent}`));
4172
4256
  }
4173
4257
  });
4174
4258
  }
@@ -4177,8 +4261,8 @@ var objectMultiline = defineRule34({
4177
4261
  });
4178
4262
 
4179
4263
  // src/rules/only-export-components.ts
4180
- import { defineRule as defineRule35 } from "@oxlint/plugins";
4181
- var onlyExportComponents = defineRule35({
4264
+ import { defineRule as defineRule36 } from "@oxlint/plugins";
4265
+ var onlyExportComponents = defineRule36({
4182
4266
  meta: {
4183
4267
  type: "problem",
4184
4268
  schema: [
@@ -4258,7 +4342,7 @@ var onlyExportComponents = defineRule35({
4258
4342
  });
4259
4343
 
4260
4344
  // src/rules/only-export-constants.ts
4261
- import { defineRule as defineRule36 } from "@oxlint/plugins";
4345
+ import { defineRule as defineRule37 } from "@oxlint/plugins";
4262
4346
  function bindingNames(pattern) {
4263
4347
  switch (pattern.type) {
4264
4348
  case "Identifier":
@@ -4313,7 +4397,7 @@ function collectBindings(program) {
4313
4397
  }
4314
4398
  return bindings;
4315
4399
  }
4316
- var onlyExportConstants = defineRule36({
4400
+ var onlyExportConstants = defineRule37({
4317
4401
  meta: {
4318
4402
  type: "problem",
4319
4403
  schema: [
@@ -4401,7 +4485,7 @@ var onlyExportConstants = defineRule36({
4401
4485
  });
4402
4486
 
4403
4487
  // src/rules/padding-between-statements.ts
4404
- import { defineRule as defineRule37 } from "@oxlint/plugins";
4488
+ import { defineRule as defineRule38 } from "@oxlint/plugins";
4405
4489
  function paddingKind(node) {
4406
4490
  if (node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration") {
4407
4491
  return node.declaration ? paddingKind(node.declaration) : "other";
@@ -4453,7 +4537,7 @@ function paddingBlankLineFix(sourceText, comments2, prev, curr, newline) {
4453
4537
  }
4454
4538
  return { newlineEnd, newline };
4455
4539
  }
4456
- var paddingBetweenStatements = defineRule37({
4540
+ var paddingBetweenStatements = defineRule38({
4457
4541
  meta: {
4458
4542
  type: "layout",
4459
4543
  fixable: "whitespace",
@@ -4527,7 +4611,7 @@ var paddingBetweenStatements = defineRule37({
4527
4611
  });
4528
4612
 
4529
4613
  // src/rules/prefer-namespace-type-import.ts
4530
- import { defineRule as defineRule38 } from "@oxlint/plugins";
4614
+ import { defineRule as defineRule39 } from "@oxlint/plugins";
4531
4615
  var bindingName = /^[A-Za-z_$][\w$]*$/;
4532
4616
  var reservedNames = new Set("await break case catch class const continue debugger default delete do else enum export extends false finally for function if import in instanceof new null return super switch this throw true try typeof var void while with yield implements interface let package private protected public static arguments eval".split(" "));
4533
4617
  function namespaceName(source, names) {
@@ -4551,7 +4635,7 @@ function safeReference(node) {
4551
4635
  }
4552
4636
  return parent?.type === "TSTypeReference" && parent.typeName === current || parent?.type === "TSTypeQuery" && parent.exprName === current || (parent?.type === "TSInterfaceHeritage" || parent?.type === "TSClassImplements") && parent.expression === current;
4553
4637
  }
4554
- var preferNamespaceTypeImport = defineRule38({
4638
+ var preferNamespaceTypeImport = defineRule39({
4555
4639
  meta: {
4556
4640
  type: "suggestion",
4557
4641
  fixable: "code",
@@ -4668,8 +4752,8 @@ var preferNamespaceTypeImport = defineRule38({
4668
4752
  });
4669
4753
 
4670
4754
  // src/rules/require-file-factory.ts
4671
- import { defineRule as defineRule39 } from "@oxlint/plugins";
4672
- var requireFileFactory = defineRule39({
4755
+ import { defineRule as defineRule40 } from "@oxlint/plugins";
4756
+ var requireFileFactory = defineRule40({
4673
4757
  meta: {
4674
4758
  type: "problem",
4675
4759
  schema: [
@@ -4717,8 +4801,8 @@ var requireFileFactory = defineRule39({
4717
4801
  });
4718
4802
 
4719
4803
  // src/rules/require-object-params.ts
4720
- import { defineRule as defineRule40 } from "@oxlint/plugins";
4721
- var requireObjectParams = defineRule40({
4804
+ import { defineRule as defineRule41 } from "@oxlint/plugins";
4805
+ var requireObjectParams = defineRule41({
4722
4806
  meta: {
4723
4807
  type: "problem",
4724
4808
  schema: [
@@ -4776,8 +4860,8 @@ var requireObjectParams = defineRule40({
4776
4860
  });
4777
4861
 
4778
4862
  // src/rules/require-orpc-output.ts
4779
- import { defineRule as defineRule41 } from "@oxlint/plugins";
4780
- var requireOrpcOutput = defineRule41({
4863
+ import { defineRule as defineRule42 } from "@oxlint/plugins";
4864
+ var requireOrpcOutput = defineRule42({
4781
4865
  meta: {
4782
4866
  type: "problem",
4783
4867
  schema: [
@@ -4825,8 +4909,8 @@ var requireOrpcOutput = defineRule41({
4825
4909
  });
4826
4910
 
4827
4911
  // src/rules/require-paired-call.ts
4828
- import { defineRule as defineRule42 } from "@oxlint/plugins";
4829
- var requirePairedCall = defineRule42({
4912
+ import { defineRule as defineRule43 } from "@oxlint/plugins";
4913
+ var requirePairedCall = defineRule43({
4830
4914
  meta: {
4831
4915
  type: "problem",
4832
4916
  schema: [
@@ -4900,8 +4984,8 @@ var requirePairedCall = defineRule42({
4900
4984
  });
4901
4985
 
4902
4986
  // src/rules/route-surface.ts
4903
- import { defineRule as defineRule43 } from "@oxlint/plugins";
4904
- var routeSurface = defineRule43({
4987
+ import { defineRule as defineRule44 } from "@oxlint/plugins";
4988
+ var routeSurface = defineRule44({
4905
4989
  meta: {
4906
4990
  type: "problem",
4907
4991
  schema: [
@@ -4946,8 +5030,102 @@ var routeSurface = defineRule43({
4946
5030
  }
4947
5031
  });
4948
5032
 
5033
+ // src/rules/ternary-layout.ts
5034
+ import { defineRule as defineRule45 } from "@oxlint/plugins";
5035
+ function ternaryLayoutLineStart(text, offset) {
5036
+ return text.lastIndexOf(`
5037
+ `, offset - 1) + 1;
5038
+ }
5039
+ function ternaryLayoutLineEnd(text, offset) {
5040
+ const end = text.indexOf(`
5041
+ `, offset);
5042
+ if (end === -1) {
5043
+ return text.length;
5044
+ }
5045
+ return text[end - 1] === "\r" ? end - 1 : end;
5046
+ }
5047
+ function ternaryLayoutBreak(between, operator, newline) {
5048
+ const at = between.indexOf(operator);
5049
+ return `${between.slice(0, at).trimEnd()}${newline}${operator} ${between.slice(at + 1).trimStart()}`;
5050
+ }
5051
+ var ternaryLayout = defineRule45({
5052
+ meta: {
5053
+ type: "layout",
5054
+ fixable: "whitespace",
5055
+ schema: [
5056
+ {
5057
+ type: "object",
5058
+ additionalProperties: false,
5059
+ properties: {
5060
+ maxLength: {
5061
+ type: "integer",
5062
+ minimum: 1,
5063
+ default: 160
5064
+ },
5065
+ indent: {
5066
+ type: "integer",
5067
+ minimum: 1,
5068
+ default: 2
5069
+ }
5070
+ }
5071
+ }
5072
+ ],
5073
+ messages: {
5074
+ join: "Keep this conditional on one line ({{length}} columns; limit {{max}}, except in template literals).",
5075
+ split: "This conditional does not fit on one line ({{length}} of {{max}} columns); break before `?` and `:`."
5076
+ }
5077
+ },
5078
+ createOnce(context) {
5079
+ let maxLength = 160;
5080
+ let indent = 2;
5081
+ return {
5082
+ before() {
5083
+ const options2 = optionsFirst(context, {});
5084
+ maxLength = options2.maxLength ?? 160;
5085
+ indent = options2.indent ?? 2;
5086
+ },
5087
+ ConditionalExpression(node) {
5088
+ const source = context.sourceCode;
5089
+ const text = source.text;
5090
+ const operands = [node.test, node.consequent, node.alternate];
5091
+ if (node.parent.type === "ConditionalExpression" || operands.some((operand) => operand.type === "ConditionalExpression" || operand.loc.start.line !== operand.loc.end.line) || source.getCommentsInside(node).length > 0) {
5092
+ return;
5093
+ }
5094
+ const prefix = text.slice(ternaryLayoutLineStart(text, node.range[0]), node.range[0]);
5095
+ const suffix = text.slice(node.range[1], ternaryLayoutLineEnd(text, node.range[1]));
5096
+ const joined = text.slice(node.range[0], node.range[1]).replace(/\s*\n\s*/g, " ");
5097
+ const length = prefix.length + joined.length + suffix.length;
5098
+ const onOneLine = node.loc.start.line === node.loc.end.line;
5099
+ const interpolated = node.parent.type === "TemplateLiteral";
5100
+ if (!onOneLine && (interpolated || length <= maxLength)) {
5101
+ context.report({
5102
+ node,
5103
+ messageId: "join",
5104
+ data: { length: String(length), max: String(maxLength) },
5105
+ fix: (fixer) => fixer.replaceTextRange(node.range, joined)
5106
+ });
5107
+ }
5108
+ if (onOneLine && !interpolated && length > maxLength) {
5109
+ const newline = `${layoutNewline(text)}${layoutLineIndent(text, node.range[0])}${" ".repeat(indent)}`;
5110
+ const question = [node.test.range[1], node.consequent.range[0]];
5111
+ const colon = [node.consequent.range[1], node.alternate.range[0]];
5112
+ context.report({
5113
+ node,
5114
+ messageId: "split",
5115
+ data: { length: String(length), max: String(maxLength) },
5116
+ fix: (fixer) => [
5117
+ fixer.replaceTextRange(question, ternaryLayoutBreak(text.slice(...question), "?", newline)),
5118
+ fixer.replaceTextRange(colon, ternaryLayoutBreak(text.slice(...colon), ":", newline))
5119
+ ]
5120
+ });
5121
+ }
5122
+ }
5123
+ };
5124
+ }
5125
+ });
5126
+
4949
5127
  // src/rules/test-title-pattern.ts
4950
- import { defineRule as defineRule44 } from "@oxlint/plugins";
5128
+ import { defineRule as defineRule46 } from "@oxlint/plugins";
4951
5129
  var TEST_MODIFIERS = new Set(["only", "skip", "concurrent", "sequential", "todo", "each", "for"]);
4952
5130
  function isConfiguredName(name, callees) {
4953
5131
  if (!name) {
@@ -4987,7 +5165,7 @@ function regexMatches(regex, value) {
4987
5165
  function titlePatterns(pattern, flags) {
4988
5166
  return pattern === undefined ? [] : [pattern].flat().map((source) => ({ regex: new RegExp(source, flags), pattern: source }));
4989
5167
  }
4990
- var testTitlePattern = defineRule44({
5168
+ var testTitlePattern = defineRule46({
4991
5169
  meta: {
4992
5170
  type: "suggestion",
4993
5171
  schema: [
@@ -5066,6 +5244,7 @@ var testTitlePattern = defineRule44({
5066
5244
  var src_default = eslintCompatPlugin({
5067
5245
  meta: { name: "arch" },
5068
5246
  rules: {
5247
+ "call-arguments-multiline": callArgumentsMultiline,
5069
5248
  "call-array-multiline": callArrayMultiline,
5070
5249
  "chain-newline": chainNewline,
5071
5250
  "component-props": componentProps,
@@ -5109,6 +5288,7 @@ var src_default = eslintCompatPlugin({
5109
5288
  "require-orpc-output": requireOrpcOutput,
5110
5289
  "require-paired-call": requirePairedCall,
5111
5290
  "route-surface": routeSurface,
5291
+ "ternary-layout": ternaryLayout,
5112
5292
  "test-title-pattern": testTitlePattern
5113
5293
  }
5114
5294
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-arch",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Configurable architecture rules for Oxlint JavaScript plugins.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",