oxlint-plugin-arch 0.3.3 → 0.3.4

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,8 +1,16 @@
1
1
  # Changelog
2
2
 
3
- ## 0.3.2
3
+ ## Unreleased
4
4
 
5
- - Fix `chain-newline` reporting already-wrapped chains whose root is parenthesized, like `(db as TestDb)\n .insertInto()`, spans several lines, or has a comment between two links.
5
+ - Options that took one regex string now also take a non-empty list, where any pattern may match: `pattern`, `allowPattern`, `allowNamePattern`, `denyTypePattern`, `hookPattern`, `rootPattern`, `factoryPattern`, `forbid`, and `require`. Single strings keep their behavior.
6
+ - `no-type-declarations` `allowPattern` entries starting with `=` or `^=` match a type alias's value (`= ` plus its source text) instead of its name, so `'^= ReturnType<typeof '` keeps factory return types next to their factory. A name pattern that starts with `=` or `^=` is now read as a value pattern.
7
+ - Fix `declaration-name` `pattern` with `g` or `y` flags rejecting every name after the first match.
8
+
9
+ ## 0.3.3
10
+
11
+ Version 0.3.2 was tagged but never published to npm; 0.3.3 contains its fixes.
12
+
13
+ - Fix `chain-newline` reporting chains that already have one call per line when the root is parenthesized (`(db as TestDb)\n .insertInto()`), the root spans several lines, or a comment sits between two links.
6
14
  - Autofix `call-array-multiline` arrays with parenthesized elements, like `Promise.all([(a), (b)])`.
7
15
 
8
16
  ## 0.3.1
package/README.md CHANGED
@@ -76,6 +76,8 @@ export default defineConfig({
76
76
 
77
77
  42 rules in 9 groups. 🔧 means the rule can autofix. Each rule file in [`src/rules/`](src/rules) explains its behavior in plain English, and the tests in [`src/tests/`](src/tests) show every option shape.
78
78
 
79
+ Options that take one regex string (`pattern`, `allowPattern`, `forbid`, `require`, and the other `*Pattern` options) also take a non-empty list; any pattern in the list may match. Options that were already lists, such as `allowPatterns` and `allowCallees`, are unchanged.
80
+
79
81
  ### 📁 Files & folders
80
82
 
81
83
  | Rule | What it enforces |
@@ -198,6 +200,21 @@ Use `no-type-declarations` separately to control where named types may live. Wit
198
200
 
199
201
  </details>
200
202
 
203
+ ### `no-type-declarations`
204
+
205
+ Rejects type aliases and interfaces in matched files, so types live in one place.
206
+
207
+ | Option | Default | What it does |
208
+ | --- | --- | --- |
209
+ | `allowPattern` | none | Keep a declaration whose **name** matches. A pattern that starts with `=` or `^=` matches a type alias's **value** instead: `= ` followed by the value's source text, without surrounding parentheses or comments. Interfaces have no value, so only name patterns apply to them. |
210
+
211
+ ```ts
212
+ // allowPattern: ['(Service|Deps)$', '^= ReturnType<typeof ']
213
+ export type AiRetry = ReturnType<typeof makeAiRetry> // ✅ value matches
214
+ export interface OrderDeps { db: Database } // ✅ name matches
215
+ export type OrderCreateParams = { id: string } // ❌ move it to your types file
216
+ ```
217
+
201
218
  ### `only-export-constants`
202
219
 
203
220
  Every runtime export must point to a local `const`. Direct, destructured, renamed, and default exports of local constants all work.
package/dist/index.js CHANGED
@@ -551,6 +551,27 @@ function factoriesObjectPropertyName(property) {
551
551
  function optionsFirst(context, fallback) {
552
552
  return context.options[0] ?? fallback;
553
553
  }
554
+ var optionsPatternSchema = {
555
+ anyOf: [
556
+ { type: "string" },
557
+ { type: "array", items: { type: "string" }, minItems: 1 }
558
+ ]
559
+ };
560
+ function optionsPatterns(pattern, flags) {
561
+ return pattern === undefined ? [] : [pattern].flat().map((source) => new RegExp(source, flags));
562
+ }
563
+ function optionsOptionalPatterns(pattern, flags) {
564
+ return optionsPatterns(pattern === undefined ? undefined : [pattern].flat().filter((source) => source !== ""), flags);
565
+ }
566
+ function optionsPatternLabel(pattern) {
567
+ return pattern === undefined ? "" : [pattern].flat().join(" | ");
568
+ }
569
+ function optionsPatternsTest(patterns, value) {
570
+ return patterns.some((pattern) => {
571
+ pattern.lastIndex = 0;
572
+ return pattern.test(value);
573
+ });
574
+ }
554
575
  // src/utils/helpers/orpc.ts
555
576
  function orpcFindChainCall(expression, method) {
556
577
  if (expression.type === "CallExpression") {
@@ -947,7 +968,7 @@ function chainNewlineRootIdentifier(node) {
947
968
  function chainNewlineMatches(root, links, group) {
948
969
  if (links.length < group.minDepth)
949
970
  return false;
950
- const hasCriteria = group.methods !== undefined || group.onlyMethods !== undefined || group.rootPattern !== undefined;
971
+ const hasCriteria = group.methods !== undefined || group.onlyMethods !== undefined || group.rootPatterns.length > 0;
951
972
  if (!hasCriteria)
952
973
  return true;
953
974
  if (group.methods && links.some((link) => link.methodName !== null && group.methods?.has(link.methodName))) {
@@ -956,7 +977,7 @@ function chainNewlineMatches(root, links, group) {
956
977
  if (group.onlyMethods && links.every((link) => link.methodName !== null && group.onlyMethods?.has(link.methodName)))
957
978
  return true;
958
979
  const rootName = chainNewlineRootIdentifier(root);
959
- return rootName !== null && group.rootPattern !== undefined && group.rootPattern.test(rootName);
980
+ return rootName !== null && optionsPatternsTest(group.rootPatterns, rootName);
960
981
  }
961
982
  var chainNewline = defineRule2({
962
983
  meta: {
@@ -975,7 +996,7 @@ var chainNewline = defineRule2({
975
996
  minDepth: { type: "integer", minimum: 1 },
976
997
  methods: { type: "array", items: { type: "string" } },
977
998
  onlyMethods: { type: "array", items: { type: "string" } },
978
- rootPattern: { type: "string" }
999
+ rootPattern: optionsPatternSchema
979
1000
  },
980
1001
  required: ["minDepth"]
981
1002
  }
@@ -1001,7 +1022,7 @@ var chainNewline = defineRule2({
1001
1022
  minDepth: group.minDepth,
1002
1023
  methods: group.methods ? new Set(group.methods) : undefined,
1003
1024
  onlyMethods: group.onlyMethods ? new Set(group.onlyMethods) : undefined,
1004
- rootPattern: group.rootPattern === undefined ? undefined : new RegExp(group.rootPattern)
1025
+ rootPatterns: optionsPatterns(group.rootPattern)
1005
1026
  }));
1006
1027
  indentUnit = layoutIndentUnit(options.indent);
1007
1028
  newline = layoutNewline(context.sourceCode.text);
@@ -1074,14 +1095,14 @@ var declarationName = defineRule3({
1074
1095
  type: "array",
1075
1096
  items: { type: "string", enum: [...declarationsKinds] }
1076
1097
  },
1077
- pattern: { type: "string" },
1098
+ pattern: optionsPatternSchema,
1078
1099
  flags: { type: "string" },
1079
1100
  stem: { type: "string", enum: [...namingStemModes] },
1080
1101
  trailingRoles: { type: "array", items: { type: "string" } },
1081
1102
  roleSeparators: { type: "array", items: { type: "string" } },
1082
1103
  normalize: { type: "string", enum: ["remove-separators", "none"] },
1083
1104
  singularize: { type: "string", enum: ["none", "trailing-s"] },
1084
- allowPattern: { type: "string" }
1105
+ allowPattern: optionsPatternSchema
1085
1106
  }
1086
1107
  }],
1087
1108
  messages: {
@@ -1092,23 +1113,23 @@ var declarationName = defineRule3({
1092
1113
  createOnce(context) {
1093
1114
  let options = {};
1094
1115
  let kinds = null;
1095
- let allowed = null;
1096
- let expected = null;
1116
+ let allowed = [];
1117
+ let expected = [];
1097
1118
  let prefixes = [];
1098
1119
  function check(node) {
1099
1120
  const item = declarationsNamedFromNode(node);
1100
- if (!item || kinds && !kinds.has(item.kind) || allowed?.test(item.name)) {
1121
+ if (!item || kinds && !kinds.has(item.kind) || optionsPatternsTest(allowed, item.name)) {
1101
1122
  return;
1102
1123
  }
1103
1124
  const comparableName = options.normalize === "none" ? item.name : item.name.replaceAll("_", "").toLowerCase();
1104
- const matches = expected ? expected.test(item.name) : prefixes.some((prefix) => comparableName.startsWith(prefix));
1125
+ const matches = expected.length > 0 ? optionsPatternsTest(expected, item.name) : prefixes.some((prefix) => comparableName.startsWith(prefix));
1105
1126
  if (matches) {
1106
1127
  return;
1107
1128
  }
1108
1129
  context.report({
1109
1130
  node: item.node,
1110
- messageId: expected ? "pattern" : "prefix",
1111
- data: expected ? { name: item.name, pattern: options.pattern ?? "" } : { name: item.name, prefix: prefixes.join(" or ") }
1131
+ messageId: expected.length > 0 ? "pattern" : "prefix",
1132
+ data: expected.length > 0 ? { name: item.name, pattern: optionsPatternLabel([options.pattern ?? []].flat().filter((source) => source !== "")) } : { name: item.name, prefix: prefixes.join(" or ") }
1112
1133
  });
1113
1134
  }
1114
1135
  return {
@@ -1118,8 +1139,8 @@ var declarationName = defineRule3({
1118
1139
  normalize: "remove-separators"
1119
1140
  });
1120
1141
  kinds = options.kinds && options.kinds.length > 0 ? new Set(options.kinds) : null;
1121
- allowed = options.allowPattern ? new RegExp(options.allowPattern) : null;
1122
- expected = options.pattern ? new RegExp(options.pattern, options.flags) : null;
1142
+ allowed = optionsOptionalPatterns(options.allowPattern);
1143
+ expected = optionsOptionalPatterns(options.pattern, options.flags);
1123
1144
  const basename = namingFileBasename(context.filename).replace(/\.(tsx?|jsx?)$/, "");
1124
1145
  const stem = namingFileStem(basename, options.stem, options.trailingRoles, options.roleSeparators);
1125
1146
  prefixes = namingFilePrefixes(stem, options.normalize ?? "remove-separators", options.singularize ?? "none");
@@ -1149,7 +1170,7 @@ var exportFilePrefix = defineRule4({
1149
1170
  normalize: { type: "string", enum: ["remove-separators", "none"] },
1150
1171
  singularize: { type: "string", enum: ["none", "trailing-s"] },
1151
1172
  allDeclarations: { type: "boolean" },
1152
- allowPattern: { type: "string" }
1173
+ allowPattern: optionsPatternSchema
1153
1174
  }
1154
1175
  }],
1155
1176
  messages: {
@@ -1166,7 +1187,7 @@ var exportFilePrefix = defineRule4({
1166
1187
  const basename = namingFileBasename(context.filename).replace(/\.(tsx?|jsx?)$/, "");
1167
1188
  const stem = namingFileStem(basename, options.stem, options.trailingRoles, options.roleSeparators);
1168
1189
  const prefixes = namingFilePrefixes(stem, options.normalize ?? "remove-separators", options.singularize ?? "none");
1169
- const allowed = options.allowPattern ? new RegExp(options.allowPattern) : null;
1190
+ const allowed = optionsOptionalPatterns(options.allowPattern);
1170
1191
  const names = options.allDeclarations ? declarationsCollectNamed(program) : exportsCollect(program).map((binding) => ({
1171
1192
  name: binding.localName ?? binding.exportedName,
1172
1193
  node: binding.node
@@ -1175,7 +1196,7 @@ var exportFilePrefix = defineRule4({
1175
1196
  for (const item of names) {
1176
1197
  const key = `${item.name}:${item.node.start}:${item.node.end}`;
1177
1198
  const comparableName = options.normalize === "none" ? item.name : item.name.replaceAll("_", "").toLowerCase();
1178
- if (seen.has(key) || allowed?.test(item.name) || prefixes.some((prefix) => comparableName.startsWith(prefix))) {
1199
+ if (seen.has(key) || optionsPatternsTest(allowed, item.name) || prefixes.some((prefix) => comparableName.startsWith(prefix))) {
1179
1200
  continue;
1180
1201
  }
1181
1202
  seen.add(key);
@@ -1199,11 +1220,11 @@ var exportNamePattern = defineRule5({
1199
1220
  type: "object",
1200
1221
  additionalProperties: false,
1201
1222
  properties: {
1202
- pattern: { type: "string" },
1223
+ pattern: optionsPatternSchema,
1203
1224
  flags: { type: "string" },
1204
1225
  ignoreTypeExports: { type: "boolean" },
1205
1226
  allDeclarations: { type: "boolean" },
1206
- allowPattern: { type: "string" }
1227
+ allowPattern: optionsPatternSchema
1207
1228
  },
1208
1229
  required: ["pattern"]
1209
1230
  }],
@@ -1221,19 +1242,20 @@ var exportNamePattern = defineRule5({
1221
1242
  allDeclarations = false,
1222
1243
  allowPattern
1223
1244
  } = optionsFirst(context);
1224
- const allowed = allowPattern ? new RegExp(allowPattern) : null;
1245
+ const allowed = optionsOptionalPatterns(allowPattern);
1246
+ const expected = optionsPatterns(pattern, flags);
1225
1247
  const names = allDeclarations ? declarationsCollectNamed(program) : exportsCollect(program).flatMap((binding) => ignoreTypeExports && binding.typeOnly ? [] : [{ name: binding.localName ?? binding.exportedName, node: binding.node }]);
1226
1248
  const seen = new Set;
1227
1249
  for (const item of names) {
1228
1250
  const key = `${item.name}:${item.node.start}:${item.node.end}`;
1229
- if (seen.has(key) || allowed?.test(item.name) || new RegExp(pattern, flags).test(item.name)) {
1251
+ if (seen.has(key) || optionsPatternsTest(allowed, item.name) || optionsPatternsTest(expected, item.name)) {
1230
1252
  continue;
1231
1253
  }
1232
1254
  seen.add(key);
1233
1255
  context.report({
1234
1256
  node: item.node,
1235
1257
  messageId: "pattern",
1236
- data: { name: item.name, pattern }
1258
+ data: { name: item.name, pattern: optionsPatternLabel(pattern) }
1237
1259
  });
1238
1260
  }
1239
1261
  }
@@ -1312,7 +1334,7 @@ var filenameMatch = defineRule7({
1312
1334
  type: "object",
1313
1335
  additionalProperties: false,
1314
1336
  properties: {
1315
- pattern: { type: "string" },
1337
+ pattern: optionsPatternSchema,
1316
1338
  message: { type: "string" },
1317
1339
  flags: { type: "string" }
1318
1340
  },
@@ -1326,7 +1348,8 @@ var filenameMatch = defineRule7({
1326
1348
  return {
1327
1349
  Program(program) {
1328
1350
  const { pattern, message, flags } = optionsFirst(context);
1329
- if (!new RegExp(pattern, flags).test(namingFileBasename(context.filename))) {
1351
+ const patterns = optionsPatterns(pattern, flags);
1352
+ if (!optionsPatternsTest(patterns, namingFileBasename(context.filename))) {
1330
1353
  context.report({
1331
1354
  node: program,
1332
1355
  messageId: "mismatch",
@@ -1798,7 +1821,7 @@ import { defineRule as defineRule13 } from "@oxlint/plugins";
1798
1821
  function compileFactoryKeys(pattern, keys, requireKeys) {
1799
1822
  const allowedList = keys.concat(requireKeys.filter((key) => !keys.includes(key)));
1800
1823
  return {
1801
- pattern: new RegExp(pattern),
1824
+ patterns: optionsPatterns(pattern),
1802
1825
  allowed: new Set(allowedList),
1803
1826
  allowedList,
1804
1827
  requireKeys
@@ -1812,7 +1835,7 @@ var noExtraFactoryKeys = defineRule13({
1812
1835
  additionalProperties: false,
1813
1836
  properties: {
1814
1837
  keys: { type: "array", items: { type: "string" } },
1815
- factoryPattern: { type: "string" },
1838
+ factoryPattern: optionsPatternSchema,
1816
1839
  requireKeys: { type: "array", items: { type: "string" } },
1817
1840
  keySets: {
1818
1841
  type: "array",
@@ -1820,7 +1843,7 @@ var noExtraFactoryKeys = defineRule13({
1820
1843
  type: "object",
1821
1844
  additionalProperties: false,
1822
1845
  properties: {
1823
- factoryPattern: { type: "string" },
1846
+ factoryPattern: optionsPatternSchema,
1824
1847
  keys: { type: "array", items: { type: "string" } },
1825
1848
  requireKeys: { type: "array", items: { type: "string" } }
1826
1849
  },
@@ -1839,11 +1862,11 @@ var noExtraFactoryKeys = defineRule13({
1839
1862
  let keySets = [];
1840
1863
  let fallback;
1841
1864
  function resolveFactoryKeys(name) {
1842
- const matched = keySets.find((item) => item.pattern.test(name));
1865
+ const matched = keySets.find((item) => optionsPatternsTest(item.patterns, name));
1843
1866
  if (matched) {
1844
1867
  return matched;
1845
1868
  }
1846
- if (fallback?.pattern.test(name)) {
1869
+ if (fallback && optionsPatternsTest(fallback.patterns, name)) {
1847
1870
  return fallback;
1848
1871
  }
1849
1872
  }
@@ -1910,9 +1933,9 @@ var noFileLevelHelpers = defineRule14({
1910
1933
  type: "object",
1911
1934
  additionalProperties: false,
1912
1935
  properties: {
1913
- allowPattern: { type: "string" },
1936
+ allowPattern: optionsPatternSchema,
1914
1937
  detectComponents: { type: "boolean" },
1915
- hookPattern: { type: "string" }
1938
+ hookPattern: optionsPatternSchema
1916
1939
  }
1917
1940
  }],
1918
1941
  messages: {
@@ -1927,10 +1950,10 @@ var noFileLevelHelpers = defineRule14({
1927
1950
  detectComponents = true,
1928
1951
  hookPattern = "^use[A-Z]"
1929
1952
  } = optionsFirst(context, {});
1930
- const allowed = allowPattern ? new RegExp(allowPattern) : null;
1931
- const hooks = new RegExp(hookPattern);
1953
+ const allowed = optionsOptionalPatterns(allowPattern);
1954
+ const hooks = optionsPatterns(hookPattern);
1932
1955
  for (const candidate of declarationsFileLevelFunctionCandidates(program)) {
1933
- if (detectComponents && reactComponentsIsLike(program, candidate) || hooks.test(candidate.name) || allowed?.test(candidate.name)) {
1956
+ if (detectComponents && reactComponentsIsLike(program, candidate) || optionsPatternsTest(hooks, candidate.name) || optionsPatternsTest(allowed, candidate.name)) {
1934
1957
  continue;
1935
1958
  }
1936
1959
  context.report({
@@ -2443,7 +2466,7 @@ var noModuleMutableState = defineRule21({
2443
2466
  type: "array",
2444
2467
  items: { type: "string", enum: ["let", "var"] }
2445
2468
  },
2446
- allowNamePattern: { type: "string" }
2469
+ allowNamePattern: optionsPatternSchema
2447
2470
  }
2448
2471
  }],
2449
2472
  messages: {
@@ -2452,12 +2475,12 @@ var noModuleMutableState = defineRule21({
2452
2475
  },
2453
2476
  createOnce(context) {
2454
2477
  let kindSet = new Set(["let", "var"]);
2455
- let allowed = null;
2478
+ let allowed = [];
2456
2479
  return {
2457
2480
  before() {
2458
2481
  const { kinds = ["let", "var"], allowNamePattern } = optionsFirst(context, {});
2459
2482
  kindSet = new Set(kinds);
2460
- allowed = allowNamePattern ? new RegExp(allowNamePattern) : null;
2483
+ allowed = optionsOptionalPatterns(allowNamePattern);
2461
2484
  },
2462
2485
  VariableDeclaration(node) {
2463
2486
  if (node.declare || !kindSet.has(node.kind)) {
@@ -2468,7 +2491,7 @@ var noModuleMutableState = defineRule21({
2468
2491
  }
2469
2492
  for (const declarator of node.declarations) {
2470
2493
  for (const binding of moduleMutableStateBindings(declarator.id)) {
2471
- if (!allowed?.test(binding.name)) {
2494
+ if (!optionsPatternsTest(allowed, binding.name)) {
2472
2495
  context.report({
2473
2496
  node: binding.node,
2474
2497
  messageId: "moduleState"
@@ -3349,7 +3372,7 @@ var noTopLevelFunctions = defineRule29({
3349
3372
  additionalProperties: false,
3350
3373
  properties: {
3351
3374
  banReExports: { type: "boolean" },
3352
- allowPattern: { type: "string" }
3375
+ allowPattern: optionsPatternSchema
3353
3376
  }
3354
3377
  }],
3355
3378
  defaultOptions: [{ banReExports: true }],
@@ -3362,7 +3385,7 @@ var noTopLevelFunctions = defineRule29({
3362
3385
  return {
3363
3386
  Program(program) {
3364
3387
  const { banReExports, allowPattern } = optionsFirst(context);
3365
- const allowed = allowPattern ? new RegExp(allowPattern) : null;
3388
+ const allowed = optionsOptionalPatterns(allowPattern);
3366
3389
  for (const statement of program.body) {
3367
3390
  if (banReExports && (statement.type === "ExportAllDeclaration" || statement.type === "ExportNamedDeclaration" && statement.source)) {
3368
3391
  context.report({ node: statement, messageId: "noReExport" });
@@ -3370,7 +3393,7 @@ var noTopLevelFunctions = defineRule29({
3370
3393
  }
3371
3394
  const declaration = statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" ? statement.declaration : statement;
3372
3395
  if (declaration?.type === "FunctionDeclaration") {
3373
- if (!allowed?.test(declarationsFunctionName(declaration))) {
3396
+ if (!optionsPatternsTest(allowed, declarationsFunctionName(declaration))) {
3374
3397
  context.report({
3375
3398
  node: declaration,
3376
3399
  messageId: "noFunction",
@@ -3383,7 +3406,7 @@ var noTopLevelFunctions = defineRule29({
3383
3406
  continue;
3384
3407
  }
3385
3408
  for (const item of declaration.declarations) {
3386
- if ((item.init?.type === "FunctionExpression" || item.init?.type === "ArrowFunctionExpression") && !allowed?.test(declarationsFunctionName(item))) {
3409
+ if ((item.init?.type === "FunctionExpression" || item.init?.type === "ArrowFunctionExpression") && !optionsPatternsTest(allowed, declarationsFunctionName(item))) {
3387
3410
  context.report({
3388
3411
  node: item,
3389
3412
  messageId: "noFunction",
@@ -3432,7 +3455,7 @@ var noTrivialFunctions = defineRule30({
3432
3455
  type: "object",
3433
3456
  additionalProperties: false,
3434
3457
  properties: {
3435
- allowPattern: { type: "string" },
3458
+ allowPattern: optionsPatternSchema,
3436
3459
  allowCallees: { type: "array", items: { type: "string" } },
3437
3460
  allowAsync: { type: "boolean" }
3438
3461
  }
@@ -3442,13 +3465,13 @@ var noTrivialFunctions = defineRule30({
3442
3465
  }
3443
3466
  },
3444
3467
  createOnce(context) {
3445
- let allowed = null;
3468
+ let allowed = [];
3446
3469
  let allowedCallees = [];
3447
3470
  let allowAsync = false;
3448
3471
  return {
3449
3472
  before() {
3450
3473
  const { allowPattern, allowCallees: calleePatterns = [], allowAsync: asyncAllowed = false } = optionsFirst(context, {});
3451
- allowed = allowPattern ? new RegExp(allowPattern) : null;
3474
+ allowed = optionsOptionalPatterns(allowPattern);
3452
3475
  allowedCallees = calleePatterns.map((pattern) => new RegExp(pattern));
3453
3476
  allowAsync = asyncAllowed;
3454
3477
  },
@@ -3456,7 +3479,7 @@ var noTrivialFunctions = defineRule30({
3456
3479
  const seen = new Set;
3457
3480
  for (const item of [...exportsCollectFunctions(program), ...declarationsTopLevelUnexportedFunctions(program)]) {
3458
3481
  const key = `${item.name}:${item.node.start}:${item.node.end}`;
3459
- if (seen.has(key) || allowed?.test(item.name) || !declarationsIsTrivialFunction(item.node)) {
3482
+ if (seen.has(key) || optionsPatternsTest(allowed, item.name) || !declarationsIsTrivialFunction(item.node)) {
3460
3483
  continue;
3461
3484
  }
3462
3485
  const expression = forwardedExpression(item.node);
@@ -3486,7 +3509,7 @@ var noTypeDeclarations = defineRule31({
3486
3509
  type: "object",
3487
3510
  additionalProperties: false,
3488
3511
  properties: {
3489
- allowPattern: { type: "string" }
3512
+ allowPattern: optionsPatternSchema
3490
3513
  }
3491
3514
  }],
3492
3515
  messages: {
@@ -3494,11 +3517,13 @@ var noTypeDeclarations = defineRule31({
3494
3517
  }
3495
3518
  },
3496
3519
  createOnce(context) {
3497
- let allowed = null;
3520
+ let allowedNames = [];
3521
+ let allowedValues = [];
3498
3522
  function check(node) {
3499
- if (allowed?.test(node.id.name)) {
3523
+ if (optionsPatternsTest(allowedNames, node.id.name))
3524
+ return;
3525
+ if (node.type === "TSTypeAliasDeclaration" && allowedValues.length > 0 && optionsPatternsTest(allowedValues, `= ${context.sourceCode.getText(node.typeAnnotation)}`))
3500
3526
  return;
3501
- }
3502
3527
  context.report({
3503
3528
  node,
3504
3529
  messageId: "typeDeclaration",
@@ -3508,7 +3533,10 @@ var noTypeDeclarations = defineRule31({
3508
3533
  return {
3509
3534
  before() {
3510
3535
  const { allowPattern } = optionsFirst(context, {});
3511
- allowed = allowPattern ? new RegExp(allowPattern) : null;
3536
+ const sources = allowPattern === undefined ? [] : [allowPattern].flat();
3537
+ const isValuePattern = (source) => /^\^?=/.test(source);
3538
+ allowedNames = optionsOptionalPatterns(sources.filter((source) => !isValuePattern(source)));
3539
+ allowedValues = optionsOptionalPatterns(sources.filter(isValuePattern));
3512
3540
  },
3513
3541
  TSTypeAliasDeclaration: check,
3514
3542
  TSInterfaceDeclaration: check
@@ -3671,7 +3699,7 @@ var onlyExportComponents = defineRule34({
3671
3699
  properties: {
3672
3700
  matchFileName: { type: "boolean" },
3673
3701
  allowTypeExports: { type: "boolean" },
3674
- denyTypePattern: { type: "string" }
3702
+ denyTypePattern: optionsPatternSchema
3675
3703
  }
3676
3704
  }],
3677
3705
  messages: {
@@ -3690,7 +3718,7 @@ var onlyExportComponents = defineRule34({
3690
3718
  denyTypePattern
3691
3719
  } = optionsFirst(context, {});
3692
3720
  const expected = reactComponentsPascalFromBasename(namingFileBasename(context.filename));
3693
- const denied = denyTypePattern ? new RegExp(denyTypePattern) : null;
3721
+ const denied = optionsOptionalPatterns(denyTypePattern);
3694
3722
  const components = [];
3695
3723
  for (const candidate of reactComponentsExportCandidates(program)) {
3696
3724
  if (candidate.typeOnly) {
@@ -3700,7 +3728,7 @@ var onlyExportComponents = defineRule34({
3700
3728
  messageId: "typeExport",
3701
3729
  data: { name: candidate.name, expected }
3702
3730
  });
3703
- } else if (denied?.test(candidate.name)) {
3731
+ } else if (optionsPatternsTest(denied, candidate.name)) {
3704
3732
  context.report({
3705
3733
  node: candidate.node,
3706
3734
  messageId: "deniedType",
@@ -4053,7 +4081,7 @@ var requireObjectParams = defineRule38({
4053
4081
  properties: {
4054
4082
  maxParams: { type: "integer", minimum: 1 },
4055
4083
  allDeclarations: { type: "boolean" },
4056
- allowPattern: { type: "string" }
4084
+ allowPattern: optionsPatternSchema
4057
4085
  }
4058
4086
  }],
4059
4087
  messages: {
@@ -4064,12 +4092,12 @@ var requireObjectParams = defineRule38({
4064
4092
  return {
4065
4093
  Program(program) {
4066
4094
  const { maxParams = 1, allDeclarations = false, allowPattern } = optionsFirst(context, {});
4067
- const allowed = allowPattern ? new RegExp(allowPattern) : null;
4095
+ const allowed = optionsOptionalPatterns(allowPattern);
4068
4096
  const seen = new Set;
4069
4097
  const functions = allDeclarations ? [...exportsCollectFunctions(program), ...declarationsTopLevelUnexportedFunctions(program)] : exportsCollectFunctions(program);
4070
4098
  for (const item of functions) {
4071
4099
  const key = `${item.name}:${item.node.start}:${item.node.end}`;
4072
- if (seen.has(key) || allowed?.test(item.name)) {
4100
+ if (seen.has(key) || optionsPatternsTest(allowed, item.name)) {
4073
4101
  continue;
4074
4102
  }
4075
4103
  seen.add(key);
@@ -4291,6 +4319,9 @@ function regexMatches(regex, value) {
4291
4319
  regex.lastIndex = 0;
4292
4320
  return regex.test(value);
4293
4321
  }
4322
+ function titlePatterns(pattern, flags) {
4323
+ return pattern === undefined ? [] : [pattern].flat().map((source) => ({ regex: new RegExp(source, flags), pattern: source }));
4324
+ }
4294
4325
  var testTitlePattern = defineRule42({
4295
4326
  meta: {
4296
4327
  type: "suggestion",
@@ -4299,8 +4330,8 @@ var testTitlePattern = defineRule42({
4299
4330
  additionalProperties: false,
4300
4331
  properties: {
4301
4332
  callees: { type: "array", items: { type: "string" } },
4302
- forbid: { type: "string" },
4303
- require: { type: "string" },
4333
+ forbid: optionsPatternSchema,
4334
+ require: optionsPatternSchema,
4304
4335
  flags: { type: "string", pattern: "^[dgimsuvy]*$" }
4305
4336
  }
4306
4337
  }],
@@ -4315,16 +4346,18 @@ var testTitlePattern = defineRule42({
4315
4346
  },
4316
4347
  createOnce(context) {
4317
4348
  let callees = [];
4318
- let forbid;
4319
- let required;
4349
+ let forbid = [];
4350
+ let required = [];
4351
+ let requiredLabel = "";
4320
4352
  return {
4321
4353
  before() {
4322
4354
  const options = optionsFirst(context);
4323
4355
  callees = options.callees;
4324
4356
  const flags = options.flags ?? "";
4325
- forbid = options.forbid === undefined ? undefined : { regex: new RegExp(options.forbid, flags), pattern: options.forbid };
4326
- required = options.require === undefined ? undefined : { regex: new RegExp(options.require, flags), pattern: options.require };
4327
- if (forbid === undefined && required === undefined) {
4357
+ forbid = titlePatterns(options.forbid, flags);
4358
+ required = titlePatterns(options.require, flags);
4359
+ requiredLabel = optionsPatternLabel(options.require);
4360
+ if (forbid.length === 0 && required.length === 0) {
4328
4361
  return false;
4329
4362
  }
4330
4363
  },
@@ -4340,18 +4373,19 @@ var testTitlePattern = defineRule42({
4340
4373
  if (text === undefined) {
4341
4374
  return;
4342
4375
  }
4343
- if (forbid && regexMatches(forbid.regex, text)) {
4376
+ const forbidden = forbid.find((item) => regexMatches(item.regex, text));
4377
+ if (forbidden) {
4344
4378
  context.report({
4345
4379
  node: title,
4346
4380
  messageId: "forbidden",
4347
- data: { pattern: forbid.pattern }
4381
+ data: { pattern: forbidden.pattern }
4348
4382
  });
4349
4383
  }
4350
- if (required && !regexMatches(required.regex, text)) {
4384
+ if (required.length > 0 && !required.some((item) => regexMatches(item.regex, text))) {
4351
4385
  context.report({
4352
4386
  node: title,
4353
4387
  messageId: "required",
4354
- data: { pattern: required.pattern }
4388
+ data: { pattern: requiredLabel }
4355
4389
  });
4356
4390
  }
4357
4391
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-arch",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Configurable architecture rules for Oxlint JavaScript plugins.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",