oxlint-plugin-arch 0.3.3 โ 0.4.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/CHANGELOG.md +12 -2
- package/README.md +46 -3
- package/dist/index.js +248 -80
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Unreleased
|
|
4
4
|
|
|
5
|
-
-
|
|
5
|
+
- Add `prefer-namespace-type-import` with a configurable named-type-import limit (default 3), module-specific namespace names, and scope-aware autofixes that qualify type references while reporting unsafe rewrites without a fix.
|
|
6
|
+
|
|
7
|
+
- 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.
|
|
8
|
+
- `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.
|
|
9
|
+
- Fix `declaration-name` `pattern` with `g` or `y` flags rejecting every name after the first match.
|
|
10
|
+
|
|
11
|
+
## 0.3.3
|
|
12
|
+
|
|
13
|
+
Version 0.3.2 was tagged but never published to npm; 0.3.3 contains its fixes.
|
|
14
|
+
|
|
15
|
+
- 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
16
|
- Autofix `call-array-multiline` arrays with parenthesized elements, like `Promise.all([(a), (b)])`.
|
|
7
17
|
|
|
8
18
|
## 0.3.1
|
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.**
|
|
9
|
+
- ๐ **Safe autofix.** Layout and comment rules only touch whitespace or comments. 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
|
---
|
|
@@ -65,7 +65,7 @@ export default defineConfig({
|
|
|
65
65
|
| --- | --- |
|
|
66
66
|
| [`minimal.oxlint.config.ts`](examples/minimal.oxlint.config.ts) | You want the smallest possible starting point. |
|
|
67
67
|
| [`monorepo.oxlint.config.ts`](examples/monorepo.oxlint.config.ts) | โญ You are starting an `apps/server` + `apps/web` monorepo and want a strict, copy-ready setup. |
|
|
68
|
-
| [`full.oxlint.config.ts`](examples/full.oxlint.config.ts) | You want to see every one of the
|
|
68
|
+
| [`full.oxlint.config.ts`](examples/full.oxlint.config.ts) | You want to see every one of the 43 rules with its options. |
|
|
69
69
|
|
|
70
70
|
> [!TIP]
|
|
71
71
|
> `monorepo.oxlint.config.ts` already loads `oxlint-plugin-arch`, so you can copy it as-is and adjust globs and names. The other two load `../src/index.ts`; change that specifier to `oxlint-plugin-arch` when you copy them.
|
|
@@ -74,7 +74,9 @@ export default defineConfig({
|
|
|
74
74
|
|
|
75
75
|
## ๐งฉ Rules
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
43 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
|
+
|
|
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.
|
|
78
80
|
|
|
79
81
|
### ๐ Files & folders
|
|
80
82
|
|
|
@@ -115,6 +117,7 @@ export default defineConfig({
|
|
|
115
117
|
| `no-type-declarations` | No type aliases or interfaces in matched files, so types live in one place. |
|
|
116
118
|
| `no-runtime-in-types` | Type modules stay free of runtime code. Opt in to also ban runtime imports and re-exports. |
|
|
117
119
|
| `no-imported-type-alias` | No exported aliases that only rename an imported type. |
|
|
120
|
+
| `prefer-namespace-type-import` ๐ง | Long type-only named imports become a namespace import with qualified references. [Details โ](#prefer-namespace-type-import) |
|
|
118
121
|
|
|
119
122
|
### ๐งช Schemas & APIs
|
|
120
123
|
|
|
@@ -198,6 +201,46 @@ Use `no-type-declarations` separately to control where named types may live. Wit
|
|
|
198
201
|
|
|
199
202
|
</details>
|
|
200
203
|
|
|
204
|
+
### `prefer-namespace-type-import`
|
|
205
|
+
|
|
206
|
+
Prefers a namespace when a type-only import has **more than** `max` named specifiers. Both `import type { A, B }` and `import { type A, type B }` are checked; any runtime or default binding leaves the declaration alone.
|
|
207
|
+
|
|
208
|
+
| Option | Default | What it does |
|
|
209
|
+
| --- | --- | --- |
|
|
210
|
+
| `max` | `3` | Maximum named specifiers allowed (non-negative integer). Three passes; four fails. |
|
|
211
|
+
| `names` | `{}` | Exact module-path-to-namespace-name map, such as `{ '#/types/room.types': 'RoomTypes' }`. Values must be ASCII binding identifiers, not reserved words. |
|
|
212
|
+
|
|
213
|
+
The default name is the last path segment in PascalCase, stripping JS/TS extensions and adding `Types` unless already present: `#/types/room.types` โ `RoomTypes`, `./harness.types.ts` โ `HarnessTypes`, `@orbs/server/client` โ `ClientTypes`. Other punctuation separates words; names starting with a digit get a `Module` prefix.
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
// Before
|
|
217
|
+
import type { A, B as LocalB, C, D } from './room.types'
|
|
218
|
+
export type Result = [A, LocalB, C, D]
|
|
219
|
+
|
|
220
|
+
// After
|
|
221
|
+
import type * as RoomTypes from './room.types'
|
|
222
|
+
export type Result = [RoomTypes.A, RoomTypes.B, RoomTypes.C, RoomTypes.D]
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
The autofix updates scope-resolved type references, generics, indexed access, type-position `typeof`, interface heritage, and class implementations, including TSX. Shadowed local names are untouched. Exported type declarations are updated, but local export lists (`export type { A }`, `export { type A }`, or `export { A }`) are **report-only**; direct re-exports from another module are untouched.
|
|
226
|
+
|
|
227
|
+
The diagnostic explains why an autofix was skipped: comments inside the import, an existing namespace import from the same module (no merging), a namespace name colliding with any binding/reference in the file or another proposed namespace, an unsupported imported name, or a reference outside the supported type syntax. Multiple qualifying imports from the same module are report-only too. Choose distinct `names` overrides to resolve collisions between different modules. Fixes preserve the module string, import attributes, semicolon, and comments outside the import.
|
|
228
|
+
|
|
229
|
+
### `no-type-declarations`
|
|
230
|
+
|
|
231
|
+
Rejects type aliases and interfaces in matched files, so types live in one place.
|
|
232
|
+
|
|
233
|
+
| Option | Default | What it does |
|
|
234
|
+
| --- | --- | --- |
|
|
235
|
+
| `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. |
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
// allowPattern: ['(Service|Deps)$', '^= ReturnType<typeof ']
|
|
239
|
+
export type AiRetry = ReturnType<typeof makeAiRetry> // โ
value matches
|
|
240
|
+
export interface OrderDeps { db: Database } // โ
name matches
|
|
241
|
+
export type OrderCreateParams = { id: string } // โ move it to your types file
|
|
242
|
+
```
|
|
243
|
+
|
|
201
244
|
### `only-export-constants`
|
|
202
245
|
|
|
203
246
|
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.
|
|
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.
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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 =
|
|
1096
|
-
let expected =
|
|
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
|
|
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
|
|
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 =
|
|
1122
|
-
expected =
|
|
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:
|
|
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 =
|
|
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
|
|
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:
|
|
1223
|
+
pattern: optionsPatternSchema,
|
|
1203
1224
|
flags: { type: "string" },
|
|
1204
1225
|
ignoreTypeExports: { type: "boolean" },
|
|
1205
1226
|
allDeclarations: { type: "boolean" },
|
|
1206
|
-
allowPattern:
|
|
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 =
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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.
|
|
1865
|
+
const matched = keySets.find((item) => optionsPatternsTest(item.patterns, name));
|
|
1843
1866
|
if (matched) {
|
|
1844
1867
|
return matched;
|
|
1845
1868
|
}
|
|
1846
|
-
if (fallback
|
|
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:
|
|
1936
|
+
allowPattern: optionsPatternSchema,
|
|
1914
1937
|
detectComponents: { type: "boolean" },
|
|
1915
|
-
hookPattern:
|
|
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 =
|
|
1931
|
-
const hooks =
|
|
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
|
|
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:
|
|
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 =
|
|
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 =
|
|
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
|
|
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:
|
|
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 =
|
|
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
|
|
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
|
|
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:
|
|
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 =
|
|
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 =
|
|
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
|
|
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:
|
|
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
|
|
3520
|
+
let allowedNames = [];
|
|
3521
|
+
let allowedValues = [];
|
|
3498
3522
|
function check(node) {
|
|
3499
|
-
if (
|
|
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
|
-
|
|
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:
|
|
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 =
|
|
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
|
|
3731
|
+
} else if (optionsPatternsTest(denied, candidate.name)) {
|
|
3704
3732
|
context.report({
|
|
3705
3733
|
node: candidate.node,
|
|
3706
3734
|
messageId: "deniedType",
|
|
@@ -3995,9 +4023,142 @@ var paddingBetweenStatements = defineRule36({
|
|
|
3995
4023
|
}
|
|
3996
4024
|
});
|
|
3997
4025
|
|
|
3998
|
-
// src/rules/
|
|
4026
|
+
// src/rules/prefer-namespace-type-import.ts
|
|
3999
4027
|
import { defineRule as defineRule37 } from "@oxlint/plugins";
|
|
4000
|
-
var
|
|
4028
|
+
var bindingName = /^[A-Za-z_$][\w$]*$/;
|
|
4029
|
+
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(" "));
|
|
4030
|
+
function namespaceName(source, names) {
|
|
4031
|
+
if (Object.hasOwn(names, source)) {
|
|
4032
|
+
return names[source];
|
|
4033
|
+
}
|
|
4034
|
+
const stem = namingFileBasename(source).replace(/(?:\.d)?\.[cm]?[jt]sx?$/i, "");
|
|
4035
|
+
const name = namingPascalCase(stem.replace(/[^A-Za-z0-9_$.-]/g, "-")) || "Module";
|
|
4036
|
+
const prefixed = /^\d/.test(name) ? `Module${name}` : name;
|
|
4037
|
+
return prefixed.endsWith("Types") ? prefixed : `${prefixed}Types`;
|
|
4038
|
+
}
|
|
4039
|
+
function isTypeOnlyNamedImport(node) {
|
|
4040
|
+
return node.specifiers.length > 0 && node.specifiers.every((specifier) => specifier.type === "ImportSpecifier" && (node.importKind === "type" || specifier.importKind === "type"));
|
|
4041
|
+
}
|
|
4042
|
+
function safeReference(node) {
|
|
4043
|
+
let current = node;
|
|
4044
|
+
let parent = current.parent;
|
|
4045
|
+
while (parent?.type === "TSQualifiedName" && parent.left === current || parent?.type === "MemberExpression" && !parent.computed && parent.object === current) {
|
|
4046
|
+
current = parent;
|
|
4047
|
+
parent = current.parent;
|
|
4048
|
+
}
|
|
4049
|
+
return parent?.type === "TSTypeReference" && parent.typeName === current || parent?.type === "TSTypeQuery" && parent.exprName === current || (parent?.type === "TSInterfaceHeritage" || parent?.type === "TSClassImplements") && parent.expression === current;
|
|
4050
|
+
}
|
|
4051
|
+
var preferNamespaceTypeImport = defineRule37({
|
|
4052
|
+
meta: {
|
|
4053
|
+
type: "suggestion",
|
|
4054
|
+
fixable: "code",
|
|
4055
|
+
schema: [{
|
|
4056
|
+
type: "object",
|
|
4057
|
+
additionalProperties: false,
|
|
4058
|
+
properties: {
|
|
4059
|
+
max: { type: "integer", minimum: 0 },
|
|
4060
|
+
names: { type: "object", additionalProperties: { type: "string", pattern: bindingName.source } }
|
|
4061
|
+
}
|
|
4062
|
+
}],
|
|
4063
|
+
defaultOptions: [{ max: 3, names: {} }],
|
|
4064
|
+
messages: {
|
|
4065
|
+
namespace: "Use a type namespace '{{name}}' for {{count}} named imports (maximum {{max}}).{{reason}}"
|
|
4066
|
+
}
|
|
4067
|
+
},
|
|
4068
|
+
createOnce(context) {
|
|
4069
|
+
const imports = [];
|
|
4070
|
+
return {
|
|
4071
|
+
before() {
|
|
4072
|
+
imports.length = 0;
|
|
4073
|
+
},
|
|
4074
|
+
ImportDeclaration(node) {
|
|
4075
|
+
imports.push(node);
|
|
4076
|
+
},
|
|
4077
|
+
"Program:exit"() {
|
|
4078
|
+
const { max, names } = optionsFirst(context);
|
|
4079
|
+
const candidates = imports.filter((node) => isTypeOnlyNamedImport(node) && node.specifiers.length > max);
|
|
4080
|
+
if (candidates.length === 0) {
|
|
4081
|
+
return;
|
|
4082
|
+
}
|
|
4083
|
+
const sourceCode = context.sourceCode;
|
|
4084
|
+
const occupied = new Set;
|
|
4085
|
+
for (const scope of sourceCode.scopeManager.scopes) {
|
|
4086
|
+
for (const variable of scope.variables) {
|
|
4087
|
+
occupied.add(variable.name);
|
|
4088
|
+
}
|
|
4089
|
+
for (const reference of scope.references) {
|
|
4090
|
+
occupied.add(reference.identifier.name);
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4093
|
+
const proposed = candidates.map((node) => namespaceName(node.source.value, names));
|
|
4094
|
+
const comments = sourceCode.getAllComments();
|
|
4095
|
+
const reports = [];
|
|
4096
|
+
const edits = [];
|
|
4097
|
+
for (const [index, node] of candidates.entries()) {
|
|
4098
|
+
const name = proposed[index];
|
|
4099
|
+
let reason = "";
|
|
4100
|
+
const replacements = new Map;
|
|
4101
|
+
if (!bindingName.test(name) || reservedNames.has(name)) {
|
|
4102
|
+
reason = "invalid namespace binding";
|
|
4103
|
+
} else if (imports.some((other) => other.source.value === node.source.value && other.specifiers.some((specifier) => specifier.type === "ImportNamespaceSpecifier"))) {
|
|
4104
|
+
reason = "existing namespace import from this module";
|
|
4105
|
+
} else if (occupied.has(name) || proposed.some((other, otherIndex) => otherIndex !== index && other === name)) {
|
|
4106
|
+
reason = "namespace name collides with a binding, reference, or another proposed namespace";
|
|
4107
|
+
} else if (comments.some((comment) => comment.range[0] >= node.range[0] && comment.range[1] <= node.range[1])) {
|
|
4108
|
+
reason = "comment inside import";
|
|
4109
|
+
} else {
|
|
4110
|
+
const variables = sourceCode.getDeclaredVariables(node);
|
|
4111
|
+
for (const specifier of node.specifiers) {
|
|
4112
|
+
if (specifier.type !== "ImportSpecifier") {
|
|
4113
|
+
continue;
|
|
4114
|
+
}
|
|
4115
|
+
const imported = specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value;
|
|
4116
|
+
const variable = variables.find((binding) => binding.identifiers.some((id) => id.range[0] === specifier.local.range[0]));
|
|
4117
|
+
if (!bindingName.test(imported) || !variable || variable.defs.length !== 1) {
|
|
4118
|
+
reason = "unsupported imported name or binding";
|
|
4119
|
+
break;
|
|
4120
|
+
}
|
|
4121
|
+
for (const reference of variable.references) {
|
|
4122
|
+
const identifier = reference.identifier;
|
|
4123
|
+
if (identifier.parent.type === "ExportSpecifier") {
|
|
4124
|
+
reason = "local re-export";
|
|
4125
|
+
break;
|
|
4126
|
+
}
|
|
4127
|
+
if (!safeReference(identifier)) {
|
|
4128
|
+
reason = "unsupported reference syntax";
|
|
4129
|
+
break;
|
|
4130
|
+
}
|
|
4131
|
+
replacements.set(identifier, `${name}.${imported}`);
|
|
4132
|
+
}
|
|
4133
|
+
if (reason) {
|
|
4134
|
+
break;
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
reports.push({ node, name, reason });
|
|
4139
|
+
if (!reason) {
|
|
4140
|
+
edits.push({ range: [node.range[0], node.source.range[0]], text: `import type * as ${name} from ` });
|
|
4141
|
+
for (const [identifier, text] of replacements) {
|
|
4142
|
+
edits.push({ range: identifier.range, text });
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4146
|
+
for (const { node, name, reason } of reports) {
|
|
4147
|
+
context.report({
|
|
4148
|
+
node,
|
|
4149
|
+
messageId: "namespace",
|
|
4150
|
+
data: { name, count: node.specifiers.length, max, reason: reason ? ` Autofix skipped: ${reason}.` : "" },
|
|
4151
|
+
fix: reason ? undefined : (fixer) => edits.map((edit) => fixer.replaceTextRange(edit.range, edit.text))
|
|
4152
|
+
});
|
|
4153
|
+
}
|
|
4154
|
+
}
|
|
4155
|
+
};
|
|
4156
|
+
}
|
|
4157
|
+
});
|
|
4158
|
+
|
|
4159
|
+
// src/rules/require-file-factory.ts
|
|
4160
|
+
import { defineRule as defineRule38 } from "@oxlint/plugins";
|
|
4161
|
+
var requireFileFactory = defineRule38({
|
|
4001
4162
|
meta: {
|
|
4002
4163
|
type: "problem",
|
|
4003
4164
|
schema: [{
|
|
@@ -4043,8 +4204,8 @@ var requireFileFactory = defineRule37({
|
|
|
4043
4204
|
});
|
|
4044
4205
|
|
|
4045
4206
|
// src/rules/require-object-params.ts
|
|
4046
|
-
import { defineRule as
|
|
4047
|
-
var requireObjectParams =
|
|
4207
|
+
import { defineRule as defineRule39 } from "@oxlint/plugins";
|
|
4208
|
+
var requireObjectParams = defineRule39({
|
|
4048
4209
|
meta: {
|
|
4049
4210
|
type: "problem",
|
|
4050
4211
|
schema: [{
|
|
@@ -4053,7 +4214,7 @@ var requireObjectParams = defineRule38({
|
|
|
4053
4214
|
properties: {
|
|
4054
4215
|
maxParams: { type: "integer", minimum: 1 },
|
|
4055
4216
|
allDeclarations: { type: "boolean" },
|
|
4056
|
-
allowPattern:
|
|
4217
|
+
allowPattern: optionsPatternSchema
|
|
4057
4218
|
}
|
|
4058
4219
|
}],
|
|
4059
4220
|
messages: {
|
|
@@ -4064,12 +4225,12 @@ var requireObjectParams = defineRule38({
|
|
|
4064
4225
|
return {
|
|
4065
4226
|
Program(program) {
|
|
4066
4227
|
const { maxParams = 1, allDeclarations = false, allowPattern } = optionsFirst(context, {});
|
|
4067
|
-
const allowed =
|
|
4228
|
+
const allowed = optionsOptionalPatterns(allowPattern);
|
|
4068
4229
|
const seen = new Set;
|
|
4069
4230
|
const functions = allDeclarations ? [...exportsCollectFunctions(program), ...declarationsTopLevelUnexportedFunctions(program)] : exportsCollectFunctions(program);
|
|
4070
4231
|
for (const item of functions) {
|
|
4071
4232
|
const key = `${item.name}:${item.node.start}:${item.node.end}`;
|
|
4072
|
-
if (seen.has(key) || allowed
|
|
4233
|
+
if (seen.has(key) || optionsPatternsTest(allowed, item.name)) {
|
|
4073
4234
|
continue;
|
|
4074
4235
|
}
|
|
4075
4236
|
seen.add(key);
|
|
@@ -4092,8 +4253,8 @@ var requireObjectParams = defineRule38({
|
|
|
4092
4253
|
});
|
|
4093
4254
|
|
|
4094
4255
|
// src/rules/require-orpc-output.ts
|
|
4095
|
-
import { defineRule as
|
|
4096
|
-
var requireOrpcOutput =
|
|
4256
|
+
import { defineRule as defineRule40 } from "@oxlint/plugins";
|
|
4257
|
+
var requireOrpcOutput = defineRule40({
|
|
4097
4258
|
meta: {
|
|
4098
4259
|
type: "problem",
|
|
4099
4260
|
schema: [{
|
|
@@ -4135,8 +4296,8 @@ var requireOrpcOutput = defineRule39({
|
|
|
4135
4296
|
});
|
|
4136
4297
|
|
|
4137
4298
|
// src/rules/require-paired-call.ts
|
|
4138
|
-
import { defineRule as
|
|
4139
|
-
var requirePairedCall =
|
|
4299
|
+
import { defineRule as defineRule41 } from "@oxlint/plugins";
|
|
4300
|
+
var requirePairedCall = defineRule41({
|
|
4140
4301
|
meta: {
|
|
4141
4302
|
type: "problem",
|
|
4142
4303
|
schema: [{
|
|
@@ -4211,8 +4372,8 @@ var requirePairedCall = defineRule40({
|
|
|
4211
4372
|
});
|
|
4212
4373
|
|
|
4213
4374
|
// src/rules/route-surface.ts
|
|
4214
|
-
import { defineRule as
|
|
4215
|
-
var routeSurface =
|
|
4375
|
+
import { defineRule as defineRule42 } from "@oxlint/plugins";
|
|
4376
|
+
var routeSurface = defineRule42({
|
|
4216
4377
|
meta: {
|
|
4217
4378
|
type: "problem",
|
|
4218
4379
|
schema: [{
|
|
@@ -4254,7 +4415,7 @@ var routeSurface = defineRule41({
|
|
|
4254
4415
|
});
|
|
4255
4416
|
|
|
4256
4417
|
// src/rules/test-title-pattern.ts
|
|
4257
|
-
import { defineRule as
|
|
4418
|
+
import { defineRule as defineRule43 } from "@oxlint/plugins";
|
|
4258
4419
|
var TEST_MODIFIERS = new Set(["only", "skip", "concurrent", "todo", "each"]);
|
|
4259
4420
|
function isConfiguredName(name, callees) {
|
|
4260
4421
|
if (!name) {
|
|
@@ -4291,7 +4452,10 @@ function regexMatches(regex, value) {
|
|
|
4291
4452
|
regex.lastIndex = 0;
|
|
4292
4453
|
return regex.test(value);
|
|
4293
4454
|
}
|
|
4294
|
-
|
|
4455
|
+
function titlePatterns(pattern, flags) {
|
|
4456
|
+
return pattern === undefined ? [] : [pattern].flat().map((source) => ({ regex: new RegExp(source, flags), pattern: source }));
|
|
4457
|
+
}
|
|
4458
|
+
var testTitlePattern = defineRule43({
|
|
4295
4459
|
meta: {
|
|
4296
4460
|
type: "suggestion",
|
|
4297
4461
|
schema: [{
|
|
@@ -4299,8 +4463,8 @@ var testTitlePattern = defineRule42({
|
|
|
4299
4463
|
additionalProperties: false,
|
|
4300
4464
|
properties: {
|
|
4301
4465
|
callees: { type: "array", items: { type: "string" } },
|
|
4302
|
-
forbid:
|
|
4303
|
-
require:
|
|
4466
|
+
forbid: optionsPatternSchema,
|
|
4467
|
+
require: optionsPatternSchema,
|
|
4304
4468
|
flags: { type: "string", pattern: "^[dgimsuvy]*$" }
|
|
4305
4469
|
}
|
|
4306
4470
|
}],
|
|
@@ -4315,16 +4479,18 @@ var testTitlePattern = defineRule42({
|
|
|
4315
4479
|
},
|
|
4316
4480
|
createOnce(context) {
|
|
4317
4481
|
let callees = [];
|
|
4318
|
-
let forbid;
|
|
4319
|
-
let required;
|
|
4482
|
+
let forbid = [];
|
|
4483
|
+
let required = [];
|
|
4484
|
+
let requiredLabel = "";
|
|
4320
4485
|
return {
|
|
4321
4486
|
before() {
|
|
4322
4487
|
const options = optionsFirst(context);
|
|
4323
4488
|
callees = options.callees;
|
|
4324
4489
|
const flags = options.flags ?? "";
|
|
4325
|
-
forbid =
|
|
4326
|
-
required =
|
|
4327
|
-
|
|
4490
|
+
forbid = titlePatterns(options.forbid, flags);
|
|
4491
|
+
required = titlePatterns(options.require, flags);
|
|
4492
|
+
requiredLabel = optionsPatternLabel(options.require);
|
|
4493
|
+
if (forbid.length === 0 && required.length === 0) {
|
|
4328
4494
|
return false;
|
|
4329
4495
|
}
|
|
4330
4496
|
},
|
|
@@ -4340,18 +4506,19 @@ var testTitlePattern = defineRule42({
|
|
|
4340
4506
|
if (text === undefined) {
|
|
4341
4507
|
return;
|
|
4342
4508
|
}
|
|
4343
|
-
|
|
4509
|
+
const forbidden = forbid.find((item) => regexMatches(item.regex, text));
|
|
4510
|
+
if (forbidden) {
|
|
4344
4511
|
context.report({
|
|
4345
4512
|
node: title,
|
|
4346
4513
|
messageId: "forbidden",
|
|
4347
|
-
data: { pattern:
|
|
4514
|
+
data: { pattern: forbidden.pattern }
|
|
4348
4515
|
});
|
|
4349
4516
|
}
|
|
4350
|
-
if (required && !regexMatches(
|
|
4517
|
+
if (required.length > 0 && !required.some((item) => regexMatches(item.regex, text))) {
|
|
4351
4518
|
context.report({
|
|
4352
4519
|
node: title,
|
|
4353
4520
|
messageId: "required",
|
|
4354
|
-
data: { pattern:
|
|
4521
|
+
data: { pattern: requiredLabel }
|
|
4355
4522
|
});
|
|
4356
4523
|
}
|
|
4357
4524
|
}
|
|
@@ -4399,6 +4566,7 @@ var src_default = eslintCompatPlugin({
|
|
|
4399
4566
|
"only-export-components": onlyExportComponents,
|
|
4400
4567
|
"only-export-constants": onlyExportConstants,
|
|
4401
4568
|
"padding-between-statements": paddingBetweenStatements,
|
|
4569
|
+
"prefer-namespace-type-import": preferNamespaceTypeImport,
|
|
4402
4570
|
"require-file-factory": requireFileFactory,
|
|
4403
4571
|
"require-object-params": requireObjectParams,
|
|
4404
4572
|
"require-orpc-output": requireOrpcOutput,
|