oxlint-plugin-arch 0.13.0 → 0.13.2
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 +71 -0
- package/README.md +7 -7
- package/dist/index.js +2154 -504
- package/dist/preset/groups/top.d.ts +2 -0
- package/dist/preset/preset-sets.d.ts +2 -0
- package/dist/preset/preset.constants.d.ts +4 -1
- package/dist/preset/preset.js +299 -57
- package/dist/preset/preset.utils.d.ts +2 -0
- package/dist/rules/code/code-inside.d.ts +1 -0
- package/dist/rules/effect/effect-layer-keys.d.ts +1 -0
- package/dist/rules/effect/effect-service-params.d.ts +1 -0
- package/dist/rules/effect/effect-yield-name.d.ts +1 -0
- package/dist/rules/effect/effect-yields-first.d.ts +1 -0
- package/dist/rules/files/files-siblings.d.ts +1 -0
- package/dist/rules/files/files-top-level.d.ts +1 -0
- package/dist/rules/layout/layout-type-arguments.d.ts +1 -0
- package/dist/rules/naming/naming-imports.d.ts +1 -0
- package/dist/rules/naming/naming-keys.d.ts +1 -0
- package/dist/rules/naming/naming-labels.d.ts +1 -0
- package/dist/rules/naming/naming-pairs.d.ts +1 -0
- package/dist/types/boundaries.types.d.ts +33 -1
- package/dist/types/code.types.d.ts +12 -0
- package/dist/types/comments.types.d.ts +1 -0
- package/dist/types/dillon-anti-slop.types.d.ts +6 -0
- package/dist/types/effect.types.d.ts +21 -0
- package/dist/types/files.types.d.ts +33 -3
- package/dist/types/layout.types.d.ts +16 -0
- package/dist/types/naming.types.d.ts +74 -2
- package/dist/types/preset.types.d.ts +26 -0
- package/dist/types/react.types.d.ts +13 -0
- package/dist/types/types.types.d.ts +4 -0
- package/dist/utils/ast.utils.d.ts +1 -2
- package/dist/utils/boundaries.utils.d.ts +72 -4
- package/dist/utils/naming.utils.d.ts +4 -1
- package/dist/utils/options.utils.d.ts +2 -0
- package/dist/vendor/dillon-anti-slop/index.js +40 -15
- package/package.json +1 -1
- package/src/vendor/dillon-anti-slop/UPSTREAM.md +3 -0
package/dist/index.js
CHANGED
|
@@ -50,6 +50,12 @@ function optionsAssertMockingApis(patterns) {
|
|
|
50
50
|
throw new OptionsError("Test mocking APIs must be `*` or `<vi|jest|*>.<method|*>`.");
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
function optionsEscape(value) {
|
|
54
|
+
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
55
|
+
}
|
|
56
|
+
function optionsWildcards(names) {
|
|
57
|
+
return [names ?? []].flat().map((name) => new RegExp(`^${name.split("*").map(optionsEscape).join(".*")}$`));
|
|
58
|
+
}
|
|
53
59
|
|
|
54
60
|
// src/utils/ast.utils.ts
|
|
55
61
|
var astSkippedKeys = new Set(["parent", "type", "start", "end", "range", "loc", "tokens", "comments"]);
|
|
@@ -237,9 +243,6 @@ function astIsInlineFunction(node) {
|
|
|
237
243
|
function astIsCall(node) {
|
|
238
244
|
return node.type === "CallExpression" || node.type === "NewExpression";
|
|
239
245
|
}
|
|
240
|
-
function astIsPipe(node) {
|
|
241
|
-
return node.callee.type === "MemberExpression" && astMemberName(node.callee) === "pipe";
|
|
242
|
-
}
|
|
243
246
|
function astChildren(node) {
|
|
244
247
|
return Object.entries(node).filter(([key]) => !astSkippedKeys.has(key)).map(([, child]) => child);
|
|
245
248
|
}
|
|
@@ -249,17 +252,20 @@ function astSome(value, test, enter = () => true) {
|
|
|
249
252
|
}
|
|
250
253
|
return astIsNode(value) && (test(value) || enter(value) && astChildren(value).some((child) => astSome(child, test, enter)));
|
|
251
254
|
}
|
|
252
|
-
function astCallDepth(value) {
|
|
255
|
+
function astCallDepth(value, callbacks = false) {
|
|
253
256
|
if (Array.isArray(value)) {
|
|
254
|
-
return Math.max(0, ...value.map(astCallDepth));
|
|
257
|
+
return Math.max(0, ...value.map((item) => astCallDepth(item, callbacks)));
|
|
255
258
|
}
|
|
256
|
-
if (!astIsNode(value)
|
|
259
|
+
if (!astIsNode(value)) {
|
|
257
260
|
return 0;
|
|
258
261
|
}
|
|
262
|
+
if (astIsInlineFunction(value)) {
|
|
263
|
+
return callbacks ? astCallDepth(value.body, callbacks) : 0;
|
|
264
|
+
}
|
|
259
265
|
if (astIsCall(value)) {
|
|
260
|
-
return Math.max(value.arguments.length === 0 ? 0 : 1 + astCallDepth(value.arguments), astCallDepth(value.callee));
|
|
266
|
+
return Math.max(value.arguments.length === 0 ? 0 : 1 + astCallDepth(value.arguments, callbacks), astCallDepth(value.callee, callbacks));
|
|
261
267
|
}
|
|
262
|
-
return astCallDepth(astChildren(value));
|
|
268
|
+
return astCallDepth(astChildren(value), callbacks);
|
|
263
269
|
}
|
|
264
270
|
|
|
265
271
|
// src/rules/backend/backend-orpc-output.ts
|
|
@@ -455,7 +461,8 @@ var namingStemSchemaProperties = {
|
|
|
455
461
|
roles: { type: "array", items: { type: "string" } },
|
|
456
462
|
separators: { type: "array", items: { type: "string" } },
|
|
457
463
|
normalize: { type: "string", enum: ["remove-separators", "none"] },
|
|
458
|
-
singularize: { type: "string", enum: ["none", "trailing-s"] }
|
|
464
|
+
singularize: { type: "string", enum: ["none", "trailing-s"] },
|
|
465
|
+
prefix: { type: "array", items: { type: "string", enum: ["file", "folder", "parent"] }, minItems: 1 }
|
|
459
466
|
};
|
|
460
467
|
function namingBasename(filePath) {
|
|
461
468
|
return posix.basename(filePath.replaceAll("\\", "/"));
|
|
@@ -482,17 +489,73 @@ function namingFileStem(basename, mode = "before-first-dot", trailingRoles = [],
|
|
|
482
489
|
}
|
|
483
490
|
return stem;
|
|
484
491
|
}
|
|
492
|
+
function namingSingular(value) {
|
|
493
|
+
const parts = value.split(/[-_]+/).filter(Boolean);
|
|
494
|
+
const last = parts.at(-1) ?? "";
|
|
495
|
+
return last.endsWith("s") && last.length > 1 ? [...parts.slice(0, -1), last.slice(0, -1)].join("-") : value;
|
|
496
|
+
}
|
|
497
|
+
function namingFolders(filename) {
|
|
498
|
+
return filename.replaceAll("\\", "/").split("/").slice(0, -1).toReversed();
|
|
499
|
+
}
|
|
485
500
|
function namingFilePrefixesFor(filename, options) {
|
|
486
501
|
const basename = namingBasename(filename).replace(/\.(tsx?|jsx?)$/, "");
|
|
487
|
-
const
|
|
502
|
+
const [folder = "", parent = ""] = namingFolders(filename);
|
|
488
503
|
const normalize = options.normalize ?? "remove-separators";
|
|
489
|
-
const
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
504
|
+
const sources = { file: namingFileStem(basename, options.stem, options.roles, options.separators), folder, parent };
|
|
505
|
+
const prefixes = (options.prefix ?? ["file"]).flatMap((source) => {
|
|
506
|
+
const stem = sources[source];
|
|
507
|
+
return options.singularize === "trailing-s" ? [stem, namingSingular(stem)] : [stem];
|
|
508
|
+
});
|
|
509
|
+
return [...new Set(prefixes.map((stem) => namingNormalized(stem, normalize)).filter(Boolean))];
|
|
510
|
+
}
|
|
511
|
+
function namingTemplatePattern(template, placeholder = "[a-z0-9-]+") {
|
|
512
|
+
const seen = new Set;
|
|
513
|
+
const source = template.split(/(\{[^{}]+\})/).map((part) => {
|
|
514
|
+
const name = /^\{[^{}]+\}$/.test(part) ? part.slice(1, -1) : undefined;
|
|
515
|
+
if (name === undefined) {
|
|
516
|
+
return part.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
517
|
+
}
|
|
518
|
+
const again = seen.has(name);
|
|
519
|
+
seen.add(name);
|
|
520
|
+
return again ? `\\k<${name}>` : `(?<${name}>${placeholder})`;
|
|
521
|
+
}).join("");
|
|
522
|
+
try {
|
|
523
|
+
return new RegExp(`^${source}$`);
|
|
524
|
+
} catch {
|
|
525
|
+
return null;
|
|
494
526
|
}
|
|
495
|
-
|
|
527
|
+
}
|
|
528
|
+
function namingCaptures(filename, options = {}) {
|
|
529
|
+
const path = filename.replaceAll("\\", "/");
|
|
530
|
+
const file = namingBasename(path).replace(/\.(tsx?|jsx?)$/, "");
|
|
531
|
+
const [folder = "", parent = ""] = namingFolders(path);
|
|
532
|
+
const stem = namingFileStem(file, options.stem === "folder" ? undefined : options.stem, options.roles, options.separators);
|
|
533
|
+
const read = options.stem === "folder" ? folder : stem;
|
|
534
|
+
const last = read.split(/[-_]+/).at(-1) ?? "";
|
|
535
|
+
const domain = options.singularize === "trailing-s" && last.length > 1 && last.endsWith("s") ? read.slice(0, -1) : read;
|
|
536
|
+
const builtIn = { file, stem, folder, parent, domain };
|
|
537
|
+
if (options.file === undefined) {
|
|
538
|
+
return builtIn;
|
|
539
|
+
}
|
|
540
|
+
const depth = options.file.split("/").length;
|
|
541
|
+
const match = namingTemplatePattern(options.file, options.placeholder)?.exec(path.split("/").slice(-depth).join("/"));
|
|
542
|
+
return match ? { ...builtIn, ...match.groups } : null;
|
|
543
|
+
}
|
|
544
|
+
function namingExpand(template, captures, camelCase, escape = (value) => value) {
|
|
545
|
+
return template.replaceAll(/\{([A-Za-z][A-Za-z0-9]*)\}/g, (placeholder, key) => {
|
|
546
|
+
const name = key.toLowerCase();
|
|
547
|
+
if (!Object.hasOwn(captures, name)) {
|
|
548
|
+
return placeholder;
|
|
549
|
+
}
|
|
550
|
+
const value = captures[name];
|
|
551
|
+
if (key.length > 1 && key === key.toUpperCase()) {
|
|
552
|
+
return escape(value.split(/[-_\s.]+/).filter(Boolean).join("_").toUpperCase());
|
|
553
|
+
}
|
|
554
|
+
if (/^[A-Z]/.test(key)) {
|
|
555
|
+
return escape(namingPascalCase(value));
|
|
556
|
+
}
|
|
557
|
+
return escape(camelCase ? namingCamelCase(value) : value);
|
|
558
|
+
});
|
|
496
559
|
}
|
|
497
560
|
function namingHasFilePrefix(name, prefixes, normalize) {
|
|
498
561
|
const comparable = normalize === "none" ? name : name.replaceAll("_", "").toLowerCase();
|
|
@@ -842,6 +905,27 @@ import { defineRule as defineRule5 } from "@oxlint/plugins";
|
|
|
842
905
|
// src/utils/boundaries.utils.ts
|
|
843
906
|
import { builtinModules } from "node:module";
|
|
844
907
|
import { dirname, resolve } from "node:path";
|
|
908
|
+
var boundariesRuntimeConventions = {
|
|
909
|
+
server: ["server"],
|
|
910
|
+
rsc: ["rsc"],
|
|
911
|
+
actions: ["action"],
|
|
912
|
+
client: ["client"],
|
|
913
|
+
clientOnly: ["client-only"],
|
|
914
|
+
hooks: ["hooks"],
|
|
915
|
+
shared: ["utils", "constants"],
|
|
916
|
+
lib: "lib"
|
|
917
|
+
};
|
|
918
|
+
var boundariesSuffixList = { type: "array", items: { type: "string", minLength: 1 } };
|
|
919
|
+
function boundariesConventionsFor(options) {
|
|
920
|
+
return { ...boundariesRuntimeConventions, ...options.conventions };
|
|
921
|
+
}
|
|
922
|
+
function boundariesHasSuffix(suffixes, path, optionalExtension = false) {
|
|
923
|
+
const extension = optionalExtension ? "(?:\\.[cm]?[jt]sx?)?" : "\\.[cm]?[jt]sx?";
|
|
924
|
+
return suffixes.length > 0 && new RegExp(`\\.(?:${suffixes.map(optionsEscape).join("|")})${extension}$`).test(path);
|
|
925
|
+
}
|
|
926
|
+
function boundariesIsClientOnly(conventions, target) {
|
|
927
|
+
return conventions.hooks.some((folder) => target.includes(`/${folder}/`)) || conventions.clientOnly.some((suffix) => new RegExp(`\\.${optionsEscape(suffix)}(?:\\.|$)`).test(target));
|
|
928
|
+
}
|
|
845
929
|
var boundariesServerGlobals = ["process", "Bun", "Deno", "Buffer", "__dirname", "__filename", "require"];
|
|
846
930
|
var boundariesBrowserGlobals = [
|
|
847
931
|
"window",
|
|
@@ -877,7 +961,21 @@ var boundariesRuntimeProperties = {
|
|
|
877
961
|
dynamic: { type: "array", items: { type: "string" } },
|
|
878
962
|
serverPackages: { type: "array", items: { type: "string" } },
|
|
879
963
|
clientPackages: { type: "array", items: { type: "string" } },
|
|
880
|
-
hookPackages: { type: "array", items: { type: "string" } }
|
|
964
|
+
hookPackages: { type: "array", items: { type: "string" } },
|
|
965
|
+
conventions: {
|
|
966
|
+
type: "object",
|
|
967
|
+
additionalProperties: false,
|
|
968
|
+
properties: {
|
|
969
|
+
server: boundariesSuffixList,
|
|
970
|
+
rsc: boundariesSuffixList,
|
|
971
|
+
actions: boundariesSuffixList,
|
|
972
|
+
client: boundariesSuffixList,
|
|
973
|
+
clientOnly: boundariesSuffixList,
|
|
974
|
+
hooks: boundariesSuffixList,
|
|
975
|
+
shared: boundariesSuffixList,
|
|
976
|
+
lib: { type: "string", minLength: 1 }
|
|
977
|
+
}
|
|
978
|
+
}
|
|
881
979
|
};
|
|
882
980
|
var boundariesServerPackages = ["@orpc/server", "react-dom/server"];
|
|
883
981
|
var boundariesRuntimeMessages = {
|
|
@@ -908,16 +1006,21 @@ function boundariesReport(context, node, source, reason) {
|
|
|
908
1006
|
data: { source, reason }
|
|
909
1007
|
});
|
|
910
1008
|
}
|
|
911
|
-
function
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1009
|
+
function boundariesRuntimeImportVisitors(context, check, allowComputed) {
|
|
1010
|
+
return {
|
|
1011
|
+
...boundariesExportVisitors(check),
|
|
1012
|
+
ImportExpression(node) {
|
|
1013
|
+
const source = boundariesLiteralSource(node);
|
|
1014
|
+
if (source !== undefined) {
|
|
1015
|
+
check(node, source, false, false);
|
|
1016
|
+
} else if (!allowComputed()) {
|
|
1017
|
+
context.report({ node, messageId: "computed" });
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
918
1021
|
}
|
|
919
|
-
function boundariesIsSharedFile(options, file) {
|
|
920
|
-
return
|
|
1022
|
+
function boundariesIsSharedFile(options, conventions, file) {
|
|
1023
|
+
return boundariesHasSuffix(conventions.shared, file) || options.web.some((path) => file.startsWith(`${path}/${conventions.lib}/`));
|
|
921
1024
|
}
|
|
922
1025
|
function boundariesDeclaredVariable(start, name) {
|
|
923
1026
|
for (let scope = start;scope; scope = scope.upper) {
|
|
@@ -946,7 +1049,7 @@ function boundariesMemberName(node) {
|
|
|
946
1049
|
}
|
|
947
1050
|
return node.computed && node.property.type === "Literal" && typeof node.property.value === "string" ? node.property.value : null;
|
|
948
1051
|
}
|
|
949
|
-
function boundariesResolveTarget(options, file, source) {
|
|
1052
|
+
function boundariesResolveTarget(options, file, source, services = "services") {
|
|
950
1053
|
const owner = [...options.web, ...options.backend].sort((a, b) => b.length - a.length).find((root) => file.startsWith(`${root}/`));
|
|
951
1054
|
const web = owner && options.web.includes(owner) ? owner : undefined;
|
|
952
1055
|
const alias = Object.entries(options.aliases).sort(([a], [b]) => b.length - a.length).find(([prefix]) => source === prefix || source.startsWith(`${prefix}/`));
|
|
@@ -957,13 +1060,13 @@ function boundariesResolveTarget(options, file, source) {
|
|
|
957
1060
|
return resolve(dirname(file), source).replaceAll("\\", "/");
|
|
958
1061
|
}
|
|
959
1062
|
if (source.startsWith("@/") && web) {
|
|
960
|
-
return
|
|
1063
|
+
return resolve(web, source.slice(2)).replaceAll("\\", "/");
|
|
961
1064
|
}
|
|
962
1065
|
if (source.startsWith("#/") && owner) {
|
|
963
|
-
return
|
|
1066
|
+
return resolve(owner, source.slice(2)).replaceAll("\\", "/");
|
|
964
1067
|
}
|
|
965
1068
|
if (source.startsWith("#services/") && owner) {
|
|
966
|
-
return
|
|
1069
|
+
return resolve(owner, services, source.slice("#services/".length)).replaceAll("\\", "/");
|
|
967
1070
|
}
|
|
968
1071
|
return source;
|
|
969
1072
|
}
|
|
@@ -1080,7 +1183,17 @@ var boundariesDeny = defineRule6({
|
|
|
1080
1183
|
additionalProperties: false,
|
|
1081
1184
|
properties: {
|
|
1082
1185
|
imports: { type: "object", additionalProperties: { type: "string" } },
|
|
1083
|
-
calls: { type: "object", additionalProperties: { type: "string" } }
|
|
1186
|
+
calls: { type: "object", additionalProperties: { type: "string" } },
|
|
1187
|
+
only: {
|
|
1188
|
+
type: "array",
|
|
1189
|
+
items: {
|
|
1190
|
+
type: "object",
|
|
1191
|
+
additionalProperties: false,
|
|
1192
|
+
properties: { imports: optionsPatternSchema, calls: optionsPatternSchema, in: optionsPatternSchema, message: { type: "string" } },
|
|
1193
|
+
required: ["in"],
|
|
1194
|
+
anyOf: [{ required: ["imports"] }, { required: ["calls"] }]
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1084
1197
|
}
|
|
1085
1198
|
}
|
|
1086
1199
|
],
|
|
@@ -1105,8 +1218,17 @@ var boundariesDeny = defineRule6({
|
|
|
1105
1218
|
return {
|
|
1106
1219
|
before() {
|
|
1107
1220
|
const options = optionsFirst(context, {});
|
|
1108
|
-
|
|
1109
|
-
|
|
1221
|
+
const filename = context.filename.replaceAll("\\", "/");
|
|
1222
|
+
const elsewhere = (options.only ?? []).filter((entry) => ![entry.in].flat().some((place) => optionsGlob(`**/${place}{,/**}`).test(filename)));
|
|
1223
|
+
const owned = (entry) => ` ${entry.message ?? `Only ${[entry.in].flat().join(", ")} may use it.`}`;
|
|
1224
|
+
imports = [
|
|
1225
|
+
...Object.entries(options.imports ?? {}).map(([pattern, message]) => [optionsGlob(pattern), message && ` ${message}`]),
|
|
1226
|
+
...elsewhere.flatMap((entry) => [entry.imports ?? []].flat().map((pattern) => [optionsGlob(pattern), owned(entry)]))
|
|
1227
|
+
];
|
|
1228
|
+
calls = new Map([
|
|
1229
|
+
...elsewhere.flatMap((entry) => [entry.calls ?? []].flat().map((name) => [name, owned(entry)])),
|
|
1230
|
+
...Object.entries(options.calls ?? {}).map(([name, message]) => [name, message && ` ${message}`])
|
|
1231
|
+
]);
|
|
1110
1232
|
if (imports.length === 0 && calls.size === 0) {
|
|
1111
1233
|
return false;
|
|
1112
1234
|
}
|
|
@@ -1145,7 +1267,25 @@ var boundariesDeny = defineRule6({
|
|
|
1145
1267
|
import { defineRule as defineRule7 } from "@oxlint/plugins";
|
|
1146
1268
|
import { basename, resolve as resolve2 } from "node:path";
|
|
1147
1269
|
var boundariesSurfaces = ["service", "client", "server", "rsc", "utils"];
|
|
1148
|
-
var
|
|
1270
|
+
var boundariesConventions = {
|
|
1271
|
+
services: "services",
|
|
1272
|
+
registry: "service",
|
|
1273
|
+
types: "types",
|
|
1274
|
+
lib: "lib",
|
|
1275
|
+
contracts: ["types", "constants"],
|
|
1276
|
+
implementations: ["service", "utils"],
|
|
1277
|
+
implementationFolders: ["core"],
|
|
1278
|
+
operations: ["action", "query"],
|
|
1279
|
+
operationFolders: ["actions", "queries"],
|
|
1280
|
+
domainFiles: ["action", "query", "driver"],
|
|
1281
|
+
clients: ["client"]
|
|
1282
|
+
};
|
|
1283
|
+
function boundariesAny(values) {
|
|
1284
|
+
return values.length === 0 ? "(?!)" : `(?:${values.map(optionsEscape).join("|")})`;
|
|
1285
|
+
}
|
|
1286
|
+
function boundariesIsOperation(conventions, path) {
|
|
1287
|
+
return new RegExp(`/[^/]+-${boundariesAny(conventions.operations)}\\.`).test(path);
|
|
1288
|
+
}
|
|
1149
1289
|
function boundariesContains(directory, candidate) {
|
|
1150
1290
|
return candidate === directory || candidate.startsWith(`${directory}/`);
|
|
1151
1291
|
}
|
|
@@ -1155,44 +1295,56 @@ function boundariesIsSurface(options, stem) {
|
|
|
1155
1295
|
function boundariesStripExtension(path) {
|
|
1156
1296
|
return path.replace(/\.[cm]?[jt]sx?$/, "");
|
|
1157
1297
|
}
|
|
1298
|
+
function boundariesConventionsOf(options) {
|
|
1299
|
+
return { ...boundariesConventions, ...options.conventions };
|
|
1300
|
+
}
|
|
1158
1301
|
function boundariesServiceFolder(options, root) {
|
|
1159
1302
|
const nested = [...options.web, ...options.backend].filter((other) => other.length > root.length && boundariesContains(root, other));
|
|
1160
|
-
return options.services?.find((folder) => folder.startsWith(`${root}/`) && !nested.some((other) => boundariesContains(other, folder))) ?? `${root}
|
|
1303
|
+
return options.services?.find((folder) => folder.startsWith(`${root}/`) && !nested.some((other) => boundariesContains(other, folder))) ?? `${root}/${boundariesConventionsOf(options).services}`;
|
|
1161
1304
|
}
|
|
1162
|
-
function boundariesDomain(path, appRoot, services) {
|
|
1305
|
+
function boundariesDomain(conventions, path, appRoot, services) {
|
|
1163
1306
|
const relative = path.slice(appRoot.length + 1);
|
|
1164
|
-
if (relative.startsWith(
|
|
1165
|
-
return relative.slice(
|
|
1307
|
+
if (relative.startsWith(`${conventions.types}/`) && relative.endsWith(`.${conventions.types}.ts`)) {
|
|
1308
|
+
return relative.slice(conventions.types.length + 1).split(".")[0];
|
|
1166
1309
|
}
|
|
1167
1310
|
if (!boundariesContains(services, path) || path === services) {
|
|
1168
1311
|
return;
|
|
1169
1312
|
}
|
|
1170
1313
|
const parts = path.slice(services.length + 1).split("/");
|
|
1171
|
-
return parts.length > 1 ? parts[0] : parts[0].split(".")[0].replace(
|
|
1314
|
+
return parts.length > 1 ? parts[0] : parts[0].split(".")[0].replace(new RegExp(`-${boundariesAny(conventions.domainFiles)}$`), "");
|
|
1172
1315
|
}
|
|
1173
1316
|
function boundariesPrivateDomain({ options, file, target, reExport, roots, owner }) {
|
|
1174
1317
|
const roles = options.roles;
|
|
1318
|
+
const conventions = boundariesConventionsOf(options);
|
|
1175
1319
|
if (!roles?.length) {
|
|
1176
1320
|
return;
|
|
1177
1321
|
}
|
|
1178
1322
|
const targetOwner = roots.find((path) => boundariesContains(path, target));
|
|
1179
|
-
const fileDomain = owner ? boundariesDomain(file, owner, boundariesServiceFolder(options, owner)) : undefined;
|
|
1180
|
-
const targetDomain = targetOwner ? boundariesDomain(target, targetOwner, boundariesServiceFolder(options, targetOwner)) : undefined;
|
|
1323
|
+
const fileDomain = owner ? boundariesDomain(conventions, file, owner, boundariesServiceFolder(options, owner)) : undefined;
|
|
1324
|
+
const targetDomain = targetOwner ? boundariesDomain(conventions, target, targetOwner, boundariesServiceFolder(options, targetOwner)) : undefined;
|
|
1181
1325
|
const sameDomain = owner && owner === targetOwner && fileDomain && fileDomain === targetDomain;
|
|
1182
1326
|
const targetStem = boundariesStripExtension(target);
|
|
1183
|
-
const publicSurface = boundariesIsSurface(options, targetStem) && !
|
|
1184
|
-
const privateTarget = roles.some((role) => targetStem.endsWith(`.${role}`)) || targetDomain && !publicSurface &&
|
|
1327
|
+
const publicSurface = boundariesIsSurface(options, targetStem) && !boundariesIsOperation(conventions, targetStem);
|
|
1328
|
+
const privateTarget = roles.some((role) => targetStem.endsWith(`.${role}`)) || targetDomain && !publicSurface && !new RegExp(`\\.${boundariesAny(conventions.contracts)}$`).test(targetStem);
|
|
1185
1329
|
if (!privateTarget) {
|
|
1186
1330
|
return;
|
|
1187
1331
|
}
|
|
1188
|
-
if (reExport && file.endsWith(".ts") && boundariesIsSurface(options, file.slice(0, -".ts".length)) && !
|
|
1332
|
+
if (reExport && file.endsWith(".ts") && boundariesIsSurface(options, file.slice(0, -".ts".length)) && !boundariesIsOperation(conventions, file)) {
|
|
1189
1333
|
return "public service and utility surfaces must not re-export private domain internals";
|
|
1190
1334
|
}
|
|
1191
1335
|
return sameDomain ? undefined : "private domain files stay in the same domain and application; use its service API or shared utilities";
|
|
1192
1336
|
}
|
|
1337
|
+
function boundariesPortableFolder(options) {
|
|
1338
|
+
if (!options.portable) {
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
return typeof options.portable === "string" ? options.portable : boundariesConventionsOf(options).lib;
|
|
1342
|
+
}
|
|
1193
1343
|
function boundariesPortableLib(check) {
|
|
1194
1344
|
const { options, file, source, target, typeOnly, reExport, roots, web, backendImport } = check;
|
|
1195
|
-
|
|
1345
|
+
const { types } = boundariesConventionsOf(options);
|
|
1346
|
+
const lib = boundariesPortableFolder(options);
|
|
1347
|
+
if (!lib || !web || !boundariesContains(`${web}/${lib}`, file) || boundariesServiceFolder(options, web) === `${web}/${lib}`) {
|
|
1196
1348
|
return;
|
|
1197
1349
|
}
|
|
1198
1350
|
const portableTarget = target.startsWith("/") ? resolve2(target).replaceAll("\\", "/") : target;
|
|
@@ -1202,11 +1354,11 @@ function boundariesPortableLib(check) {
|
|
|
1202
1354
|
if (backendImport || backendTarget || appService || appPackage) {
|
|
1203
1355
|
return "portable frontend lib does not import application services or application APIs";
|
|
1204
1356
|
}
|
|
1205
|
-
const appType = roots.some((path) => boundariesContains(`${path}
|
|
1357
|
+
const appType = roots.some((path) => boundariesContains(`${path}/${types}`, portableTarget));
|
|
1206
1358
|
const stem = boundariesStripExtension(basename(file)).replace(/\.utils$/, "");
|
|
1207
|
-
const ownType = typeOnly && !reExport && boundariesStripExtension(portableTarget) === `${web}
|
|
1359
|
+
const ownType = typeOnly && !reExport && boundariesStripExtension(portableTarget) === `${web}/${types}/${stem}.${types}`;
|
|
1208
1360
|
if (appType && !ownType) {
|
|
1209
|
-
return `portable frontend lib may only type-import its own contract '${web}
|
|
1361
|
+
return `portable frontend lib may only type-import its own contract '${web}/${types}/${stem}.${types}.ts'; app type re-exports are forbidden`;
|
|
1210
1362
|
}
|
|
1211
1363
|
return;
|
|
1212
1364
|
}
|
|
@@ -1225,7 +1377,24 @@ var boundariesImports = defineRule7({
|
|
|
1225
1377
|
roles: { type: "array", items: { type: "string" } },
|
|
1226
1378
|
surfaces: { type: "array", items: { type: "string", minLength: 1 }, default: boundariesSurfaces },
|
|
1227
1379
|
services: { type: "array", items: { type: "string" } },
|
|
1228
|
-
portable: { type: "boolean" },
|
|
1380
|
+
portable: { anyOf: [{ type: "boolean" }, { type: "string", minLength: 1 }] },
|
|
1381
|
+
conventions: {
|
|
1382
|
+
type: "object",
|
|
1383
|
+
additionalProperties: false,
|
|
1384
|
+
properties: {
|
|
1385
|
+
services: { type: "string", minLength: 1 },
|
|
1386
|
+
registry: { type: "string", minLength: 1 },
|
|
1387
|
+
types: { type: "string", minLength: 1 },
|
|
1388
|
+
lib: { type: "string", minLength: 1 },
|
|
1389
|
+
contracts: { type: "array", items: { type: "string", minLength: 1 } },
|
|
1390
|
+
implementations: { type: "array", items: { type: "string", minLength: 1 } },
|
|
1391
|
+
implementationFolders: { type: "array", items: { type: "string", minLength: 1 } },
|
|
1392
|
+
operations: { type: "array", items: { type: "string", minLength: 1 } },
|
|
1393
|
+
operationFolders: { type: "array", items: { type: "string", minLength: 1 } },
|
|
1394
|
+
domainFiles: { type: "array", items: { type: "string", minLength: 1 } },
|
|
1395
|
+
clients: { type: "array", items: { type: "string", minLength: 1 } }
|
|
1396
|
+
}
|
|
1397
|
+
},
|
|
1229
1398
|
backendPackages: { type: "array", items: { type: "string" } },
|
|
1230
1399
|
entrypoints: { type: "array", items: { type: "string" } },
|
|
1231
1400
|
appPackages: { type: "array", items: { type: "string" } },
|
|
@@ -1244,7 +1413,8 @@ var boundariesImports = defineRule7({
|
|
|
1244
1413
|
const file = context.filename.replaceAll("\\", "/");
|
|
1245
1414
|
const roots = [...options.web, ...options.backend].sort((a, b) => b.length - a.length);
|
|
1246
1415
|
const owner = roots.find((path) => boundariesContains(path, file));
|
|
1247
|
-
const
|
|
1416
|
+
const conventions = boundariesConventionsOf(options);
|
|
1417
|
+
const target = boundariesResolveTarget(options, file, source, conventions.services);
|
|
1248
1418
|
const web = owner && options.web.includes(owner) ? owner : undefined;
|
|
1249
1419
|
const backendImport = options.backend.some((path) => boundariesContains(path, target)) || boundariesMatchesImport(options.backendPackages, source);
|
|
1250
1420
|
const imported = { options, file, source, target, typeOnly, reExport, roots, owner, web, backendImport };
|
|
@@ -1253,13 +1423,16 @@ var boundariesImports = defineRule7({
|
|
|
1253
1423
|
const serviceFolders = roots.map((path) => boundariesServiceFolder(options, path));
|
|
1254
1424
|
const serviceTarget = serviceFolders.find((folder) => boundariesContains(folder, target));
|
|
1255
1425
|
const serviceFile = serviceFolders.find((folder) => boundariesContains(folder, file));
|
|
1256
|
-
const
|
|
1426
|
+
const implementation = `(?:\\.${boundariesAny(conventions.implementations)}|-${boundariesAny(conventions.operations)}\\.|/${boundariesAny(conventions.operationFolders)}/)`;
|
|
1427
|
+
const services = optionsEscape(conventions.services);
|
|
1428
|
+
const domainContract = new RegExp(`\\.${boundariesAny(conventions.contracts)}\\.ts$`).test(file) && !typeOnly && (new RegExp(`/(?:${boundariesAny(conventions.implementationFolders)}/|${services}/.*${implementation})`).test(target) || serviceTarget !== undefined && new RegExp(`/.*${implementation}`).test(target.slice(serviceTarget.length)));
|
|
1429
|
+
const registry = `/${optionsEscape(conventions.registry)}(?:\\.ts)?$`;
|
|
1257
1430
|
const reason = [
|
|
1258
1431
|
web && backendImport && !options.entrypoints.includes(source) ? "frontend code uses only public backend entry points" : undefined,
|
|
1259
1432
|
inPackage && appTarget ? "shared packages do not import app internals" : undefined,
|
|
1260
|
-
(file.includes(
|
|
1433
|
+
(file.includes(`/${conventions.services}/`) && new RegExp(`/${services}${registry}`).test(target) || serviceFile !== undefined && new RegExp(`^${registry}`).test(target.slice(serviceFile.length))) && !typeOnly ? "service dependencies are injected rather than read from the registry" : undefined,
|
|
1261
1434
|
domainContract ? "domain types/constants use type-only imports from implementations" : undefined,
|
|
1262
|
-
file.endsWith(
|
|
1435
|
+
conventions.clients.some((client) => file.endsWith(`.${client}.ts`)) && !typeOnly && boundariesMatchesImport(options.uiPackages ?? ["react", "react-dom"], source) ? "client services are React-free; use hooks for React behavior" : undefined,
|
|
1263
1436
|
boundariesPrivateDomain(imported),
|
|
1264
1437
|
boundariesPortableLib(imported)
|
|
1265
1438
|
].findLast((candidate) => candidate !== undefined);
|
|
@@ -1300,7 +1473,8 @@ var boundariesImports = defineRule7({
|
|
|
1300
1473
|
TSImportType(node) {
|
|
1301
1474
|
const options = { ...boundariesImportsDefaults, ...optionsFirst(context) };
|
|
1302
1475
|
const file = context.filename.replaceAll("\\", "/");
|
|
1303
|
-
|
|
1476
|
+
const lib = boundariesPortableFolder(options);
|
|
1477
|
+
if (lib && options.web.some((path) => file.startsWith(`${path}/${lib}/`) && boundariesServiceFolder(options, path) !== `${path}/${lib}`)) {
|
|
1304
1478
|
check(node, node.source.value, true);
|
|
1305
1479
|
}
|
|
1306
1480
|
}
|
|
@@ -1344,6 +1518,7 @@ var boundariesNext = defineRule8({
|
|
|
1344
1518
|
},
|
|
1345
1519
|
createOnce(context) {
|
|
1346
1520
|
let options;
|
|
1521
|
+
let conventions = boundariesConventionsFor({});
|
|
1347
1522
|
let file = "";
|
|
1348
1523
|
let server = false;
|
|
1349
1524
|
let client = false;
|
|
@@ -1355,12 +1530,12 @@ var boundariesNext = defineRule8({
|
|
|
1355
1530
|
return;
|
|
1356
1531
|
}
|
|
1357
1532
|
const target = boundariesResolveTarget(options, file, source);
|
|
1358
|
-
const clientOnly =
|
|
1533
|
+
const clientOnly = boundariesIsClientOnly(conventions, target) || boundariesMatchesImport(options.clientPackages, source) || boundariesMatchesImport(options.client, source);
|
|
1359
1534
|
if (clientOnly && (server || shared)) {
|
|
1360
1535
|
boundariesReport(context, node, source, "browser-only dependencies do not belong in server, RSC or shared modules");
|
|
1361
1536
|
return;
|
|
1362
1537
|
}
|
|
1363
|
-
const serverOnly = boundariesIsServerPlatformImport(source, options.serverPackages) || boundariesIsBackendImport(options, target, source) ||
|
|
1538
|
+
const serverOnly = boundariesIsServerPlatformImport(source, options.serverPackages) || boundariesIsBackendImport(options, target, source) || boundariesHasSuffix([...conventions.server, ...conventions.rsc], target, true) || boundariesMatchesImport(options.server, source);
|
|
1364
1539
|
if (!serverOnly || server) {
|
|
1365
1540
|
return;
|
|
1366
1541
|
}
|
|
@@ -1369,6 +1544,7 @@ var boundariesNext = defineRule8({
|
|
|
1369
1544
|
return {
|
|
1370
1545
|
Program(program) {
|
|
1371
1546
|
options = { ...boundariesNextDefaults, ...optionsFirst(context) };
|
|
1547
|
+
conventions = boundariesConventionsFor(options);
|
|
1372
1548
|
reactNamespaces.clear();
|
|
1373
1549
|
file = context.filename.replaceAll("\\", "/");
|
|
1374
1550
|
const directives = new Map;
|
|
@@ -1379,9 +1555,9 @@ var boundariesNext = defineRule8({
|
|
|
1379
1555
|
}
|
|
1380
1556
|
const useServer = directives.get("use server");
|
|
1381
1557
|
const useClient = directives.get("use client");
|
|
1382
|
-
const serverName =
|
|
1383
|
-
const clientName =
|
|
1384
|
-
shared = boundariesIsSharedFile(options, file);
|
|
1558
|
+
const serverName = boundariesHasSuffix([...conventions.server, ...conventions.rsc, ...conventions.actions], file);
|
|
1559
|
+
const clientName = boundariesHasSuffix(conventions.client, file);
|
|
1560
|
+
shared = boundariesIsSharedFile(options, conventions, file);
|
|
1385
1561
|
server = !useClient && (serverName || Boolean(useServer));
|
|
1386
1562
|
client = clientName || Boolean(useClient);
|
|
1387
1563
|
if (useClient && serverName) {
|
|
@@ -1398,7 +1574,7 @@ var boundariesNext = defineRule8({
|
|
|
1398
1574
|
data: { directive: "use server", side: "client" }
|
|
1399
1575
|
});
|
|
1400
1576
|
}
|
|
1401
|
-
if (
|
|
1577
|
+
if (boundariesHasSuffix(conventions.actions, file) && !useServer) {
|
|
1402
1578
|
context.report({ node: program, messageId: "action" });
|
|
1403
1579
|
}
|
|
1404
1580
|
},
|
|
@@ -1428,10 +1604,7 @@ var boundariesNext = defineRule8({
|
|
|
1428
1604
|
boundariesReport(context, node, `${node.object.name}.${name}`, boundariesClientHookReason);
|
|
1429
1605
|
}
|
|
1430
1606
|
},
|
|
1431
|
-
...
|
|
1432
|
-
ImportExpression(node) {
|
|
1433
|
-
boundariesImportExpression(context, node, Boolean(options.dynamic?.includes(file)), (source) => check(node, source, false));
|
|
1434
|
-
},
|
|
1607
|
+
...boundariesRuntimeImportVisitors(context, check, () => Boolean(options.dynamic?.includes(file))),
|
|
1435
1608
|
"Program:exit"() {
|
|
1436
1609
|
const scope = context.sourceCode.scopeManager.scopes.at(0);
|
|
1437
1610
|
if (!scope) {
|
|
@@ -1548,11 +1721,9 @@ var boundariesTanstackDefaults = {
|
|
|
1548
1721
|
hookPackages: ["react", "@tanstack/react-query", "@tanstack/react-router"],
|
|
1549
1722
|
startPackages: ["@tanstack/react-start"],
|
|
1550
1723
|
rscPackages: ["@tanstack/react-start/rsc", "@tanstack/react-start-rsc"],
|
|
1551
|
-
renderers: ["renderServerComponent", "renderToReadableStream"]
|
|
1724
|
+
renderers: ["renderServerComponent", "renderToReadableStream"],
|
|
1725
|
+
serverHooks: ["useId"]
|
|
1552
1726
|
};
|
|
1553
|
-
function boundariesIsClientHook(name) {
|
|
1554
|
-
return /^use[A-Z]/.test(name) && name !== "useId";
|
|
1555
|
-
}
|
|
1556
1727
|
var boundariesTanstack = defineRule10({
|
|
1557
1728
|
meta: {
|
|
1558
1729
|
type: "problem",
|
|
@@ -1565,7 +1736,8 @@ var boundariesTanstack = defineRule10({
|
|
|
1565
1736
|
rpc: { type: "array", items: { type: "string" } },
|
|
1566
1737
|
startPackages: { type: "array", items: { type: "string" } },
|
|
1567
1738
|
rscPackages: { type: "array", items: { type: "string" } },
|
|
1568
|
-
renderers: { type: "array", items: { type: "string", minLength: 1 }, default: boundariesTanstackDefaults.renderers }
|
|
1739
|
+
renderers: { type: "array", items: { type: "string", minLength: 1 }, default: boundariesTanstackDefaults.renderers },
|
|
1740
|
+
serverHooks: { type: "array", items: { type: "string", minLength: 1 }, default: boundariesTanstackDefaults.serverHooks }
|
|
1569
1741
|
}
|
|
1570
1742
|
}
|
|
1571
1743
|
],
|
|
@@ -1573,6 +1745,7 @@ var boundariesTanstack = defineRule10({
|
|
|
1573
1745
|
},
|
|
1574
1746
|
createOnce(context) {
|
|
1575
1747
|
let options;
|
|
1748
|
+
let conventions = boundariesConventionsFor({});
|
|
1576
1749
|
let file = "";
|
|
1577
1750
|
let serverFile = false;
|
|
1578
1751
|
let rscFile = false;
|
|
@@ -1581,6 +1754,7 @@ var boundariesTanstack = defineRule10({
|
|
|
1581
1754
|
let referenced;
|
|
1582
1755
|
let imports = new Map;
|
|
1583
1756
|
const boundaries = [];
|
|
1757
|
+
const clientHook = (name) => /^use[A-Z]/.test(name) && !options.serverHooks.includes(name);
|
|
1584
1758
|
function imported(node) {
|
|
1585
1759
|
const entry = imports.get(node.name);
|
|
1586
1760
|
const variable = entry ? boundariesDeclaredVariable(context.sourceCode.getScope(node), node.name) : null;
|
|
@@ -1623,7 +1797,7 @@ var boundariesTanstack = defineRule10({
|
|
|
1623
1797
|
return;
|
|
1624
1798
|
}
|
|
1625
1799
|
const target = boundariesResolveTarget(options, file, source);
|
|
1626
|
-
const clientImport =
|
|
1800
|
+
const clientImport = boundariesIsClientOnly(conventions, target) || boundariesMatchesImport(options.clientPackages, source) || boundariesMatchesImport(options.client, source);
|
|
1627
1801
|
if (clientImport && (serverFile || rscFile || shared) && !clientSide(node.start)) {
|
|
1628
1802
|
const references = node.type === "ImportDeclaration" ? runtimeReferences(node) : [];
|
|
1629
1803
|
if (shared || references.length === 0 || !references.every(clientSide)) {
|
|
@@ -1631,7 +1805,7 @@ var boundariesTanstack = defineRule10({
|
|
|
1631
1805
|
}
|
|
1632
1806
|
return;
|
|
1633
1807
|
}
|
|
1634
|
-
const serverImport = boundariesIsServerPlatformImport(source, options.serverPackages) || boundariesIsBackendImport(options, target, source) ||
|
|
1808
|
+
const serverImport = boundariesIsServerPlatformImport(source, options.serverPackages) || boundariesIsBackendImport(options, target, source) || boundariesHasSuffix(conventions.server, target, true) || boundariesMatchesImport(options.server, source);
|
|
1635
1809
|
if (!renderer && !serverImport) {
|
|
1636
1810
|
return;
|
|
1637
1811
|
}
|
|
@@ -1659,10 +1833,11 @@ var boundariesTanstack = defineRule10({
|
|
|
1659
1833
|
return {
|
|
1660
1834
|
Program(program) {
|
|
1661
1835
|
options = { ...boundariesTanstackDefaults, ...optionsFirst(context) };
|
|
1836
|
+
conventions = boundariesConventionsFor(options);
|
|
1662
1837
|
file = context.filename.replaceAll("\\", "/");
|
|
1663
|
-
serverFile =
|
|
1664
|
-
rscFile =
|
|
1665
|
-
shared = boundariesIsSharedFile(options, file);
|
|
1838
|
+
serverFile = boundariesHasSuffix(conventions.server, file);
|
|
1839
|
+
rscFile = boundariesHasSuffix(conventions.rsc, file);
|
|
1840
|
+
shared = boundariesIsSharedFile(options, conventions, file);
|
|
1666
1841
|
runtimeIdentifiers.clear();
|
|
1667
1842
|
referenced = undefined;
|
|
1668
1843
|
boundaries.length = 0;
|
|
@@ -1729,7 +1904,7 @@ var boundariesTanstack = defineRule10({
|
|
|
1729
1904
|
return;
|
|
1730
1905
|
}
|
|
1731
1906
|
for (const specifier of node.specifiers) {
|
|
1732
|
-
if (specifier.type !== "ImportSpecifier" || specifier.importKind === "type" || specifier.imported.type !== "Identifier" || !
|
|
1907
|
+
if (specifier.type !== "ImportSpecifier" || specifier.importKind === "type" || specifier.imported.type !== "Identifier" || !clientHook(specifier.imported.name)) {
|
|
1733
1908
|
continue;
|
|
1734
1909
|
}
|
|
1735
1910
|
const references = runtimeReferences(node, specifier.local.name);
|
|
@@ -1738,10 +1913,7 @@ var boundariesTanstack = defineRule10({
|
|
|
1738
1913
|
}
|
|
1739
1914
|
}
|
|
1740
1915
|
},
|
|
1741
|
-
...
|
|
1742
|
-
ImportExpression(node) {
|
|
1743
|
-
boundariesImportExpression(context, node, Boolean(options.dynamic?.includes(file)), (source) => check(node, source, false));
|
|
1744
|
-
},
|
|
1916
|
+
...boundariesRuntimeImportVisitors(context, check, () => Boolean(options.dynamic?.includes(file))),
|
|
1745
1917
|
MemberExpression(node) {
|
|
1746
1918
|
const name = boundariesMemberName(node);
|
|
1747
1919
|
if (!name || node.object.type !== "Identifier") {
|
|
@@ -1751,7 +1923,7 @@ var boundariesTanstack = defineRule10({
|
|
|
1751
1923
|
if (kind === "rsc-renderers" && options.renderers.includes(name) && (shared || !serverPosition(node.start, true))) {
|
|
1752
1924
|
boundariesReport(context, node, name, "server renderers require a server execution boundary");
|
|
1753
1925
|
}
|
|
1754
|
-
if ((serverFile || rscFile) && kind === "client-hooks" &&
|
|
1926
|
+
if ((serverFile || rscFile) && kind === "client-hooks" && clientHook(name) && !clientSide(node.start)) {
|
|
1755
1927
|
boundariesReport(context, node, name, boundariesClientHookReason2);
|
|
1756
1928
|
}
|
|
1757
1929
|
},
|
|
@@ -2230,6 +2402,7 @@ var codeInline = defineRule16({
|
|
|
2230
2402
|
properties: {
|
|
2231
2403
|
max: { type: "integer", minimum: 1, default: 160 },
|
|
2232
2404
|
nesting: { type: "integer", minimum: 1, default: 2 },
|
|
2405
|
+
callbacks: { type: "boolean", default: false },
|
|
2233
2406
|
allow: optionsPatternSchema,
|
|
2234
2407
|
keep: optionsPatternSchema,
|
|
2235
2408
|
services: { type: "array", items: { type: "string" } }
|
|
@@ -2258,7 +2431,8 @@ var codeInline = defineRule16({
|
|
|
2258
2431
|
return;
|
|
2259
2432
|
}
|
|
2260
2433
|
if (uses.length === 1) {
|
|
2261
|
-
|
|
2434
|
+
const use = uses[0].identifier;
|
|
2435
|
+
if (init && (codeInlineEffectBetween(context.sourceCode.ast, node.range[1], use.range[0]) || codeInlineEnclosingCalls(use) + astCallDepth(init, options.callbacks ?? false) > (options.nesting ?? 2))) {
|
|
2262
2436
|
return;
|
|
2263
2437
|
}
|
|
2264
2438
|
context.report({
|
|
@@ -2270,7 +2444,12 @@ var codeInline = defineRule16({
|
|
|
2270
2444
|
}
|
|
2271
2445
|
const arrow = init?.type === "ArrowFunctionExpression" && init.body.type !== "BlockStatement" && !astSome(init.body, astIsInlineFunction);
|
|
2272
2446
|
const call = init?.type === "CallExpression" && !astSome(init.arguments, astIsInlineFunction) && astCallDepth(init) <= 2;
|
|
2273
|
-
|
|
2447
|
+
const text = context.sourceCode.text;
|
|
2448
|
+
const fits = init !== null && uses.every(({ identifier }) => {
|
|
2449
|
+
const line = text.slice(sourceLineStart(text, identifier.range[0]), sourceLineEnd(text, identifier.range[0]));
|
|
2450
|
+
return line.length - id.name.length + context.sourceCode.getText(init).length <= (options.max ?? 160) && codeInlineEnclosingCalls(identifier) + astCallDepth(init, options.callbacks ?? false) <= (options.nesting ?? 2);
|
|
2451
|
+
});
|
|
2452
|
+
if (!optionsPatternsTest(kept, id.name) && (arrow || call) && fits) {
|
|
2274
2453
|
context.report({
|
|
2275
2454
|
node: id,
|
|
2276
2455
|
messageId: "trivial",
|
|
@@ -2298,7 +2477,7 @@ var codeInline = defineRule16({
|
|
|
2298
2477
|
deferred ||= current.type === "FunctionDeclaration" || astIsInlineFunction(current);
|
|
2299
2478
|
}
|
|
2300
2479
|
const wrapped = codeInlineWrapped.has(init.type) && use.parent.type === "MemberExpression" && use.parent.object === use;
|
|
2301
|
-
if (service || wrapped || deferred || codeInlineEnclosingCalls(use) + astCallDepth(init) > (options.nesting ?? 2) || line.length - id.name.length + context.sourceCode.getText(init).length > (options.max ?? 160) || codeInlineEffectBetween(context.sourceCode.ast, declarator.range[1], use.range[0])) {
|
|
2480
|
+
if (service || wrapped || deferred || codeInlineEnclosingCalls(use) + astCallDepth(init, options.callbacks ?? false) > (options.nesting ?? 2) || line.length - id.name.length + context.sourceCode.getText(init).length > (options.max ?? 160) || codeInlineEffectBetween(context.sourceCode.ast, declarator.range[1], use.range[0])) {
|
|
2302
2481
|
return;
|
|
2303
2482
|
}
|
|
2304
2483
|
context.report({
|
|
@@ -2332,9 +2511,85 @@ var codeInline = defineRule16({
|
|
|
2332
2511
|
}
|
|
2333
2512
|
});
|
|
2334
2513
|
|
|
2335
|
-
// src/rules/code/code-
|
|
2514
|
+
// src/rules/code/code-inside.ts
|
|
2336
2515
|
import { defineRule as defineRule17 } from "@oxlint/plugins";
|
|
2337
|
-
|
|
2516
|
+
function codeInsideOwners(node) {
|
|
2517
|
+
const parent = node.parent;
|
|
2518
|
+
if (!parent) {
|
|
2519
|
+
return [];
|
|
2520
|
+
}
|
|
2521
|
+
const callback = node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
2522
|
+
const owner = callback && astIsCall(parent) && parent.arguments.some((item) => item === node) ? astDottedName(parent.callee) : undefined;
|
|
2523
|
+
return [...owner === undefined ? [] : [owner], ...codeInsideOwners(parent)];
|
|
2524
|
+
}
|
|
2525
|
+
var codeInside = defineRule17({
|
|
2526
|
+
meta: {
|
|
2527
|
+
type: "problem",
|
|
2528
|
+
schema: [
|
|
2529
|
+
{
|
|
2530
|
+
type: "object",
|
|
2531
|
+
additionalProperties: false,
|
|
2532
|
+
properties: {
|
|
2533
|
+
inside: {
|
|
2534
|
+
type: "array",
|
|
2535
|
+
items: {
|
|
2536
|
+
type: "object",
|
|
2537
|
+
additionalProperties: false,
|
|
2538
|
+
properties: {
|
|
2539
|
+
callee: optionsPatternSchema,
|
|
2540
|
+
calls: { type: "object", additionalProperties: { type: "string" } }
|
|
2541
|
+
},
|
|
2542
|
+
required: ["callee", "calls"]
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
},
|
|
2546
|
+
required: ["inside"]
|
|
2547
|
+
}
|
|
2548
|
+
],
|
|
2549
|
+
messages: {
|
|
2550
|
+
inside: "Do not call '{{name}}' inside {{owner}}.{{message}}"
|
|
2551
|
+
}
|
|
2552
|
+
},
|
|
2553
|
+
createOnce(context) {
|
|
2554
|
+
let entries = [];
|
|
2555
|
+
return {
|
|
2556
|
+
before() {
|
|
2557
|
+
entries = optionsFirst(context, { inside: [] }).inside.map((entry) => ({
|
|
2558
|
+
callees: optionsWildcards(entry.callee),
|
|
2559
|
+
calls: Object.entries(entry.calls).map(([name, message]) => [optionsWildcards(name)[0], message && ` ${message}`])
|
|
2560
|
+
}));
|
|
2561
|
+
if (entries.length === 0) {
|
|
2562
|
+
return false;
|
|
2563
|
+
}
|
|
2564
|
+
},
|
|
2565
|
+
CallExpression(node) {
|
|
2566
|
+
const name = astDottedName(node.callee);
|
|
2567
|
+
if (name === undefined) {
|
|
2568
|
+
return;
|
|
2569
|
+
}
|
|
2570
|
+
const banned = entries.flatMap(({ callees, calls }) => calls.flatMap(([pattern, message]) => pattern.test(name) ? [{ callees, message }] : []));
|
|
2571
|
+
if (banned.length === 0) {
|
|
2572
|
+
return;
|
|
2573
|
+
}
|
|
2574
|
+
const owners = codeInsideOwners(node);
|
|
2575
|
+
for (const { callees, message } of banned) {
|
|
2576
|
+
const owner = owners.find((item) => optionsPatternsTest(callees, item));
|
|
2577
|
+
if (owner !== undefined) {
|
|
2578
|
+
context.report({
|
|
2579
|
+
node: node.callee,
|
|
2580
|
+
messageId: "inside",
|
|
2581
|
+
data: { name, owner, message }
|
|
2582
|
+
});
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
};
|
|
2587
|
+
}
|
|
2588
|
+
});
|
|
2589
|
+
|
|
2590
|
+
// src/rules/code/code-justified-call.ts
|
|
2591
|
+
import { defineRule as defineRule18 } from "@oxlint/plugins";
|
|
2592
|
+
var codeJustifiedCall = defineRule18({
|
|
2338
2593
|
meta: {
|
|
2339
2594
|
type: "problem",
|
|
2340
2595
|
schema: [
|
|
@@ -2388,8 +2643,8 @@ var codeJustifiedCall = defineRule17({
|
|
|
2388
2643
|
});
|
|
2389
2644
|
|
|
2390
2645
|
// src/rules/code/code-literal-in.ts
|
|
2391
|
-
import { defineRule as
|
|
2392
|
-
var codeLiteralIn =
|
|
2646
|
+
import { defineRule as defineRule19 } from "@oxlint/plugins";
|
|
2647
|
+
var codeLiteralIn = defineRule19({
|
|
2393
2648
|
meta: {
|
|
2394
2649
|
type: "suggestion",
|
|
2395
2650
|
schema: [
|
|
@@ -2430,7 +2685,7 @@ var codeLiteralIn = defineRule18({
|
|
|
2430
2685
|
});
|
|
2431
2686
|
|
|
2432
2687
|
// src/rules/code/code-mutable-state.ts
|
|
2433
|
-
import { defineRule as
|
|
2688
|
+
import { defineRule as defineRule20 } from "@oxlint/plugins";
|
|
2434
2689
|
var functionScopes = new Set([
|
|
2435
2690
|
"FunctionDeclaration",
|
|
2436
2691
|
"FunctionExpression",
|
|
@@ -2447,7 +2702,7 @@ function hasAncestor(node, test) {
|
|
|
2447
2702
|
}
|
|
2448
2703
|
return false;
|
|
2449
2704
|
}
|
|
2450
|
-
var codeMutableState =
|
|
2705
|
+
var codeMutableState = defineRule20({
|
|
2451
2706
|
meta: {
|
|
2452
2707
|
type: "problem",
|
|
2453
2708
|
schema: [
|
|
@@ -2505,7 +2760,7 @@ var codeMutableState = defineRule19({
|
|
|
2505
2760
|
});
|
|
2506
2761
|
|
|
2507
2762
|
// src/rules/code/code-object-params.ts
|
|
2508
|
-
import { defineRule as
|
|
2763
|
+
import { defineRule as defineRule21 } from "@oxlint/plugins";
|
|
2509
2764
|
function isObjectParam(node) {
|
|
2510
2765
|
if (node.type === "ObjectPattern") {
|
|
2511
2766
|
return true;
|
|
@@ -2519,7 +2774,7 @@ function isObjectParam(node) {
|
|
|
2519
2774
|
const annotation = node.typeAnnotation?.typeAnnotation;
|
|
2520
2775
|
return !annotation || annotation.type === "TSTypeReference" || annotation.type === "TSTypeLiteral" || annotation.type === "TSIntersectionType";
|
|
2521
2776
|
}
|
|
2522
|
-
var codeObjectParams =
|
|
2777
|
+
var codeObjectParams = defineRule21({
|
|
2523
2778
|
meta: {
|
|
2524
2779
|
type: "problem",
|
|
2525
2780
|
schema: [
|
|
@@ -2568,8 +2823,8 @@ var codeObjectParams = defineRule20({
|
|
|
2568
2823
|
});
|
|
2569
2824
|
|
|
2570
2825
|
// src/rules/code/code-paired-call.ts
|
|
2571
|
-
import { defineRule as
|
|
2572
|
-
var codePairedCall =
|
|
2826
|
+
import { defineRule as defineRule22 } from "@oxlint/plugins";
|
|
2827
|
+
var codePairedCall = defineRule22({
|
|
2573
2828
|
meta: {
|
|
2574
2829
|
type: "problem",
|
|
2575
2830
|
schema: [
|
|
@@ -2636,7 +2891,7 @@ var codePairedCall = defineRule21({
|
|
|
2636
2891
|
});
|
|
2637
2892
|
|
|
2638
2893
|
// src/rules/code/code-promise-mutation.ts
|
|
2639
|
-
import { defineRule as
|
|
2894
|
+
import { defineRule as defineRule23 } from "@oxlint/plugins";
|
|
2640
2895
|
function mutationBinding(node) {
|
|
2641
2896
|
if (node.type === "Identifier") {
|
|
2642
2897
|
return node;
|
|
@@ -2665,7 +2920,7 @@ function mutationBindings(node) {
|
|
|
2665
2920
|
const binding = mutationBinding(node);
|
|
2666
2921
|
return binding ? [binding] : [];
|
|
2667
2922
|
}
|
|
2668
|
-
var codePromiseMutation =
|
|
2923
|
+
var codePromiseMutation = defineRule23({
|
|
2669
2924
|
meta: {
|
|
2670
2925
|
type: "problem",
|
|
2671
2926
|
schema: [
|
|
@@ -2758,8 +3013,8 @@ var codePromiseMutation = defineRule22({
|
|
|
2758
3013
|
var codeSwitch = astSelectorRule("SwitchStatement", "Avoid switch statements.");
|
|
2759
3014
|
|
|
2760
3015
|
// src/rules/code/code-traced.ts
|
|
2761
|
-
import { defineRule as
|
|
2762
|
-
var codeTraced =
|
|
3016
|
+
import { defineRule as defineRule24 } from "@oxlint/plugins";
|
|
3017
|
+
var codeTraced = defineRule24({
|
|
2763
3018
|
meta: {
|
|
2764
3019
|
type: "problem",
|
|
2765
3020
|
schema: [
|
|
@@ -2846,7 +3101,7 @@ var codeTraced = defineRule23({
|
|
|
2846
3101
|
});
|
|
2847
3102
|
|
|
2848
3103
|
// src/rules/code/code-trivial-functions.ts
|
|
2849
|
-
import { defineRule as
|
|
3104
|
+
import { defineRule as defineRule25 } from "@oxlint/plugins";
|
|
2850
3105
|
var codeTrivialFunctionsBanned = [
|
|
2851
3106
|
"isRecord",
|
|
2852
3107
|
"isPlainObject",
|
|
@@ -3004,7 +3259,7 @@ function codeTrivialFunctionsDefaults(program) {
|
|
|
3004
3259
|
}
|
|
3005
3260
|
return starts;
|
|
3006
3261
|
}
|
|
3007
|
-
var codeTrivialFunctions =
|
|
3262
|
+
var codeTrivialFunctions = defineRule25({
|
|
3008
3263
|
meta: {
|
|
3009
3264
|
type: "problem",
|
|
3010
3265
|
schema: [
|
|
@@ -3146,8 +3401,8 @@ var codeTrivialFunctions = defineRule24({
|
|
|
3146
3401
|
});
|
|
3147
3402
|
|
|
3148
3403
|
// src/rules/code/code-yield-argument.ts
|
|
3149
|
-
import { defineRule as
|
|
3150
|
-
var codeYieldArgument =
|
|
3404
|
+
import { defineRule as defineRule26 } from "@oxlint/plugins";
|
|
3405
|
+
var codeYieldArgument = defineRule26({
|
|
3151
3406
|
meta: {
|
|
3152
3407
|
type: "suggestion",
|
|
3153
3408
|
messages: {
|
|
@@ -3169,7 +3424,7 @@ var codeYieldArgument = defineRule25({
|
|
|
3169
3424
|
});
|
|
3170
3425
|
|
|
3171
3426
|
// src/rules/comments/comments-code.ts
|
|
3172
|
-
import { defineRule as
|
|
3427
|
+
import { defineRule as defineRule27 } from "@oxlint/plugins";
|
|
3173
3428
|
var commentsLineGap = /^[\t \r\n\u2028\u2029]*$/;
|
|
3174
3429
|
var commentsLineTerminator = /\r\n|[\n\r\u2028\u2029]/;
|
|
3175
3430
|
var commentsUnicodeTerminator = /[\u2028\u2029]/;
|
|
@@ -3220,7 +3475,7 @@ function commentsFollowsOpeningBrace(text, offset) {
|
|
|
3220
3475
|
}
|
|
3221
3476
|
return false;
|
|
3222
3477
|
}
|
|
3223
|
-
var commentsCode =
|
|
3478
|
+
var commentsCode = defineRule27({
|
|
3224
3479
|
meta: {
|
|
3225
3480
|
type: "suggestion",
|
|
3226
3481
|
fixable: "code",
|
|
@@ -3232,7 +3487,8 @@ var commentsCode = defineRule26({
|
|
|
3232
3487
|
why: { anyOf: [{ type: "boolean" }, { type: "string", minLength: 1 }], default: true },
|
|
3233
3488
|
allow: { type: "array", items: { type: "string" } },
|
|
3234
3489
|
jsdoc: { type: "boolean", default: false },
|
|
3235
|
-
members: { type: "string", enum: ["off", "also", "only"] }
|
|
3490
|
+
members: { type: "string", enum: ["off", "also", "only"] },
|
|
3491
|
+
steps: { type: "boolean", default: false }
|
|
3236
3492
|
}
|
|
3237
3493
|
}
|
|
3238
3494
|
],
|
|
@@ -3247,9 +3503,31 @@ var commentsCode = defineRule26({
|
|
|
3247
3503
|
let allowJsdoc = false;
|
|
3248
3504
|
let allowed = [];
|
|
3249
3505
|
let members = "off";
|
|
3506
|
+
let steps = false;
|
|
3507
|
+
let functionDepth = 0;
|
|
3250
3508
|
const memberComments = new Set;
|
|
3509
|
+
const stepStarts = new Set;
|
|
3510
|
+
function enterFunction() {
|
|
3511
|
+
functionDepth++;
|
|
3512
|
+
}
|
|
3513
|
+
function exitFunction() {
|
|
3514
|
+
functionDepth--;
|
|
3515
|
+
}
|
|
3516
|
+
function addSteps(nodes, inFunction) {
|
|
3517
|
+
if (steps && inFunction) {
|
|
3518
|
+
for (const node of nodes) {
|
|
3519
|
+
stepStarts.add(node.range[0]);
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
function isStep(comment) {
|
|
3524
|
+
const source = context.sourceCode;
|
|
3525
|
+
const before = source.getTokenBefore(comment, { includeComments: true });
|
|
3526
|
+
const after = source.getTokenAfter(comment, { includeComments: true });
|
|
3527
|
+
return comment.type === "Line" && before?.loc.end.line !== comment.loc.start.line && !(before && (before.type === "Line" || before.type === "Block") && before.loc.end.line === comment.loc.start.line - 1) && after !== null && after.type !== "Line" && after.type !== "Block" && after.loc.start.line === comment.loc.end.line + 1 && stepStarts.has(after.range[0]);
|
|
3528
|
+
}
|
|
3251
3529
|
function keepComment(comment, value, isWhy) {
|
|
3252
|
-
return allowWhy && isWhy || sourceIsDirective(value) || allowJsdoc && comment.type === "Block" && context.sourceCode.text.startsWith("/**", comment.range[0]) || allowed.some((pattern) => pattern.test(value.trim()));
|
|
3530
|
+
return allowWhy && isWhy || sourceIsDirective(value) || allowJsdoc && comment.type === "Block" && context.sourceCode.text.startsWith("/**", comment.range[0]) || allowed.some((pattern) => pattern.test(value.trim())) || steps && isStep(comment);
|
|
3253
3531
|
}
|
|
3254
3532
|
function checkMember(node) {
|
|
3255
3533
|
const parent = node.parent;
|
|
@@ -3273,7 +3551,10 @@ var commentsCode = defineRule26({
|
|
|
3273
3551
|
allowJsdoc = options.jsdoc ?? false;
|
|
3274
3552
|
allowed = (options.allow ?? []).map((pattern) => new RegExp(pattern));
|
|
3275
3553
|
members = options.members ?? "off";
|
|
3554
|
+
steps = options.steps ?? false;
|
|
3555
|
+
functionDepth = 0;
|
|
3276
3556
|
memberComments.clear();
|
|
3557
|
+
stepStarts.clear();
|
|
3277
3558
|
},
|
|
3278
3559
|
TSPropertySignature: checkMember,
|
|
3279
3560
|
TSMethodSignature: checkMember,
|
|
@@ -3282,6 +3563,17 @@ var commentsCode = defineRule26({
|
|
|
3282
3563
|
TSConstructSignatureDeclaration: checkMember,
|
|
3283
3564
|
Property: checkMember,
|
|
3284
3565
|
SpreadElement: checkMember,
|
|
3566
|
+
FunctionDeclaration: enterFunction,
|
|
3567
|
+
"FunctionDeclaration:exit": exitFunction,
|
|
3568
|
+
FunctionExpression: enterFunction,
|
|
3569
|
+
"FunctionExpression:exit": exitFunction,
|
|
3570
|
+
ArrowFunctionExpression: enterFunction,
|
|
3571
|
+
"ArrowFunctionExpression:exit": exitFunction,
|
|
3572
|
+
BlockStatement: (node) => addSteps(node.body, functionDepth > 0),
|
|
3573
|
+
SwitchCase: (node) => addSteps(node.consequent, functionDepth > 0),
|
|
3574
|
+
IfStatement: (node) => addSteps([node.consequent, ...node.alternate ? [node.alternate] : []], functionDepth > 0),
|
|
3575
|
+
"ForStatement, ForInStatement, ForOfStatement, WhileStatement, DoWhileStatement, LabeledStatement": (node) => addSteps([node.body], functionDepth > 0),
|
|
3576
|
+
ObjectExpression: (node) => addSteps(node.properties, true),
|
|
3285
3577
|
"Program:exit"(program) {
|
|
3286
3578
|
const text = context.sourceCode.text;
|
|
3287
3579
|
const found = [];
|
|
@@ -3386,9 +3678,9 @@ var commentsCode = defineRule26({
|
|
|
3386
3678
|
});
|
|
3387
3679
|
|
|
3388
3680
|
// src/rules/comments/comments-directives.ts
|
|
3389
|
-
import { defineRule as
|
|
3681
|
+
import { defineRule as defineRule28 } from "@oxlint/plugins";
|
|
3390
3682
|
var directive = /^\s*(?:oxlint|eslint)-disable(?:-next-line|-line)?\b(.*)$/s;
|
|
3391
|
-
var commentsDirectives =
|
|
3683
|
+
var commentsDirectives = defineRule28({
|
|
3392
3684
|
meta: {
|
|
3393
3685
|
type: "problem",
|
|
3394
3686
|
schema: [],
|
|
@@ -3416,9 +3708,9 @@ var commentsDirectives = defineRule27({
|
|
|
3416
3708
|
});
|
|
3417
3709
|
|
|
3418
3710
|
// src/rules/copy/copy-dashes.ts
|
|
3419
|
-
import { defineRule as
|
|
3711
|
+
import { defineRule as defineRule29 } from "@oxlint/plugins";
|
|
3420
3712
|
var copyDashesMessage = "Use a comma, colon, period or parentheses instead.";
|
|
3421
|
-
var copyDashes =
|
|
3713
|
+
var copyDashes = defineRule29({
|
|
3422
3714
|
meta: {
|
|
3423
3715
|
type: "suggestion",
|
|
3424
3716
|
schema: [
|
|
@@ -3474,7 +3766,7 @@ var copyDashes = defineRule28({
|
|
|
3474
3766
|
});
|
|
3475
3767
|
|
|
3476
3768
|
// src/rules/effect/effect-layer-file.ts
|
|
3477
|
-
import { defineRule as
|
|
3769
|
+
import { defineRule as defineRule30 } from "@oxlint/plugins";
|
|
3478
3770
|
|
|
3479
3771
|
// src/utils/effect.utils.ts
|
|
3480
3772
|
var effectBuilders = ["Effect.gen", "Effect.fn", "Effect.fnUntraced"];
|
|
@@ -3516,7 +3808,7 @@ var effectLayerFileDefaultConstructors = [
|
|
|
3516
3808
|
"Layer.sync",
|
|
3517
3809
|
"Layer.unwrap"
|
|
3518
3810
|
];
|
|
3519
|
-
var effectLayerFile =
|
|
3811
|
+
var effectLayerFile = defineRule30({
|
|
3520
3812
|
meta: {
|
|
3521
3813
|
type: "problem",
|
|
3522
3814
|
schema: [
|
|
@@ -3527,6 +3819,7 @@ var effectLayerFile = defineRule29({
|
|
|
3527
3819
|
layer: { type: "string" },
|
|
3528
3820
|
make: { type: "string" },
|
|
3529
3821
|
prefix: { type: "string" },
|
|
3822
|
+
suffix: { type: "string" },
|
|
3530
3823
|
builders: { type: "array", items: { type: "string" } },
|
|
3531
3824
|
layers: optionsPatternSchema,
|
|
3532
3825
|
services: optionsPatternSchema,
|
|
@@ -3539,8 +3832,8 @@ var effectLayerFile = defineRule29({
|
|
|
3539
3832
|
notLayer: "`{{layer}}` is built with Layer: a Layer call, a Layer pipe, or a function returning one.",
|
|
3540
3833
|
builderName: "The Effect that builds the service is named `{{make}}`, one per layer file.",
|
|
3541
3834
|
moduleMethod: "Only `{{make}}` is an Effect at module level; move this into `{{make}}`, or into a utils file if two files use it.",
|
|
3542
|
-
moduleHelper: "A layer file holds only `{{make}}`, {{prefix}}<Role> layers and `{{layer}}`; move this into `{{make}}`, or into a utils file if two files use it.",
|
|
3543
|
-
localLayer: "A Layer other than `{{layer}}` is named {{prefix}}<Role> and is not exported.",
|
|
3835
|
+
moduleHelper: "A layer file holds only `{{make}}`, {{prefix}}<Role>{{suffix}} layers and `{{layer}}`; move this into `{{make}}`, or into a utils file if two files use it.",
|
|
3836
|
+
localLayer: "A Layer other than `{{layer}}` is named {{prefix}}<Role>{{suffix}} and is not exported.",
|
|
3544
3837
|
makeReturn: "`{{make}}` returns the service through its tag (Tag.of({ … })), a Context, or a Layer.",
|
|
3545
3838
|
inlineBuilder: "Build this layer from `{{make}}`, not an inline Effect: Layer.effect(Tag, {{make}}). One built layer per file."
|
|
3546
3839
|
}
|
|
@@ -3549,6 +3842,7 @@ var effectLayerFile = defineRule29({
|
|
|
3549
3842
|
let layer = "layer";
|
|
3550
3843
|
let make = "make";
|
|
3551
3844
|
let prefix = "layer";
|
|
3845
|
+
let suffix = "";
|
|
3552
3846
|
let builders = new Set(effectBuilders);
|
|
3553
3847
|
let layers = [];
|
|
3554
3848
|
let services = [];
|
|
@@ -3565,14 +3859,15 @@ var effectLayerFile = defineRule29({
|
|
|
3565
3859
|
const options = optionsFirst(context, {});
|
|
3566
3860
|
layer = options.layer ?? "layer";
|
|
3567
3861
|
make = options.make ?? "make";
|
|
3568
|
-
|
|
3862
|
+
suffix = options.suffix ?? "";
|
|
3863
|
+
prefix = options.prefix ?? (suffix ? "" : "layer");
|
|
3569
3864
|
builders = new Set(options.builders ?? effectBuilders);
|
|
3570
3865
|
constructors = new Set(options.constructors ?? effectLayerFileDefaultConstructors);
|
|
3571
|
-
layers = optionsPatterns(options.layers ?? effectLayerFileDefaultLayers);
|
|
3866
|
+
layers = optionsPatterns(options.layers ?? [...effectLayerFileDefaultLayers, ...suffix ? [`(?:^|\\.)\\w+${optionsEscape(suffix)}$`] : []]);
|
|
3572
3867
|
services = [...optionsPatterns(options.services ?? effectLayerFileDefaultServices), ...layers];
|
|
3573
3868
|
},
|
|
3574
3869
|
Program(program) {
|
|
3575
|
-
const data = { layer, make, prefix };
|
|
3870
|
+
const data = { layer, make, prefix, suffix };
|
|
3576
3871
|
const declarators = program.body.flatMap((statement) => {
|
|
3577
3872
|
const declaration = astDeclaration(statement);
|
|
3578
3873
|
const exported = statement.type === "ExportNamedDeclaration";
|
|
@@ -3632,7 +3927,8 @@ var effectLayerFile = defineRule29({
|
|
|
3632
3927
|
data
|
|
3633
3928
|
});
|
|
3634
3929
|
}
|
|
3635
|
-
|
|
3930
|
+
const role = name.endsWith(suffix) ? name.slice(prefix.length, name.length - suffix.length) : "";
|
|
3931
|
+
if (isLayer && name !== layer && (exported || !name.startsWith(prefix) || !/^[A-Z]\w*$/.test(role))) {
|
|
3636
3932
|
context.report({
|
|
3637
3933
|
node,
|
|
3638
3934
|
messageId: "localLayer",
|
|
@@ -3669,17 +3965,147 @@ var effectLayerFile = defineRule29({
|
|
|
3669
3965
|
context.report({
|
|
3670
3966
|
node: argument,
|
|
3671
3967
|
messageId: "inlineBuilder",
|
|
3672
|
-
data: { layer, make, prefix }
|
|
3968
|
+
data: { layer, make, prefix, suffix }
|
|
3969
|
+
});
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
};
|
|
3974
|
+
}
|
|
3975
|
+
});
|
|
3976
|
+
|
|
3977
|
+
// src/rules/effect/effect-layer-keys.ts
|
|
3978
|
+
import { defineRule as defineRule31 } from "@oxlint/plugins";
|
|
3979
|
+
import { readFileSync } from "node:fs";
|
|
3980
|
+
var effectLayerKeysPairs = new Map([["{", "}"], ["(", ")"], ["[", "]"], ["<", ">"]]);
|
|
3981
|
+
function effectLayerKeysDeclared(text, declaration) {
|
|
3982
|
+
const code = text.replaceAll(/\/\*[\s\S]*?\*\/|\/\/[^\n]*|'(?:[^'\\\n]|\\.)*'|"(?:[^"\\\n]|\\.)*"/g, " ");
|
|
3983
|
+
const start = code.indexOf(`${declaration}<`);
|
|
3984
|
+
const open = start === -1 ? -1 : code.indexOf("{", start);
|
|
3985
|
+
if (open === -1) {
|
|
3986
|
+
return null;
|
|
3987
|
+
}
|
|
3988
|
+
const paths = [];
|
|
3989
|
+
const frames = [{ open: "{", members: 0 }];
|
|
3990
|
+
const key = /\s*(?:readonly\s+)?([A-Za-z_$][\w$]*)\??\s*(?=[:(<])/y;
|
|
3991
|
+
let fresh = true;
|
|
3992
|
+
for (let index = open + 1;index < code.length && frames.length > 0; index += 1) {
|
|
3993
|
+
const char = code[index];
|
|
3994
|
+
const members = frames.every((frame) => frame.open === "{");
|
|
3995
|
+
key.lastIndex = index;
|
|
3996
|
+
const match = fresh && members ? key.exec(code) : null;
|
|
3997
|
+
if (match) {
|
|
3998
|
+
const rest = code.slice(key.lastIndex).replace(/^:\s*/, "");
|
|
3999
|
+
const path = [...frames.flatMap((frame) => frame.key ?? []), match[1]].join(".");
|
|
4000
|
+
const parent = frames.at(-1);
|
|
4001
|
+
if (parent) {
|
|
4002
|
+
parent.members += 1;
|
|
4003
|
+
}
|
|
4004
|
+
if (rest.startsWith("{")) {
|
|
4005
|
+
frames.push({
|
|
4006
|
+
open: "{",
|
|
4007
|
+
key: match[1],
|
|
4008
|
+
members: 0
|
|
4009
|
+
});
|
|
4010
|
+
index = code.length - rest.length;
|
|
4011
|
+
} else {
|
|
4012
|
+
paths.push(path);
|
|
4013
|
+
fresh = false;
|
|
4014
|
+
index = key.lastIndex - 1;
|
|
4015
|
+
}
|
|
4016
|
+
continue;
|
|
4017
|
+
}
|
|
4018
|
+
if (effectLayerKeysPairs.has(char)) {
|
|
4019
|
+
frames.push({ open: char, members: 0 });
|
|
4020
|
+
} else if (char === effectLayerKeysPairs.get(frames.at(-1)?.open ?? "") && !(char === ">" && code[index - 1] === "=")) {
|
|
4021
|
+
const closed = frames.pop();
|
|
4022
|
+
if (closed?.key !== undefined && closed.members === 0) {
|
|
4023
|
+
paths.push([...frames.flatMap((frame) => frame.key ?? []), closed.key].join("."));
|
|
4024
|
+
}
|
|
4025
|
+
}
|
|
4026
|
+
fresh = members && (char === "{" || char === ";" || char === "," || char === "}" || fresh && /\s/.test(char));
|
|
4027
|
+
}
|
|
4028
|
+
return paths;
|
|
4029
|
+
}
|
|
4030
|
+
function effectLayerKeysBound(object, path) {
|
|
4031
|
+
return object.properties.flatMap((property) => {
|
|
4032
|
+
const key = declarationsPropertyName(property);
|
|
4033
|
+
if (property.type !== "Property" || key === undefined) {
|
|
4034
|
+
return [];
|
|
4035
|
+
}
|
|
4036
|
+
return property.value.type === "ObjectExpression" ? effectLayerKeysBound(property.value, [...path, key]) : [[[...path, key].join("."), property]];
|
|
4037
|
+
});
|
|
4038
|
+
}
|
|
4039
|
+
var effectLayerKeys = defineRule31({
|
|
4040
|
+
meta: {
|
|
4041
|
+
type: "problem",
|
|
4042
|
+
schema: [
|
|
4043
|
+
{
|
|
4044
|
+
type: "object",
|
|
4045
|
+
additionalProperties: false,
|
|
4046
|
+
properties: {
|
|
4047
|
+
service: { type: "string", minLength: 1 },
|
|
4048
|
+
callees: { type: "array", items: { type: "string" }, minItems: 1 },
|
|
4049
|
+
declaration: { type: "string", minLength: 1 }
|
|
4050
|
+
},
|
|
4051
|
+
required: ["service"]
|
|
4052
|
+
}
|
|
4053
|
+
],
|
|
4054
|
+
messages: {
|
|
4055
|
+
extra: "'{{path}}' is not a method of the service declared in {{file}}.",
|
|
4056
|
+
missing: "The service in {{file}} declares {{paths}}, which this layer does not bind."
|
|
4057
|
+
}
|
|
4058
|
+
},
|
|
4059
|
+
createOnce(context) {
|
|
4060
|
+
let declared = null;
|
|
4061
|
+
let callees = [];
|
|
4062
|
+
let file = "";
|
|
4063
|
+
return {
|
|
4064
|
+
before() {
|
|
4065
|
+
const options = optionsFirst(context, { service: "" });
|
|
4066
|
+
file = namingExpand(options.service, namingCaptures(context.filename) ?? {}, false);
|
|
4067
|
+
callees = optionsWildcards(options.callees ?? ["*.of", "*.bind"]);
|
|
4068
|
+
try {
|
|
4069
|
+
declared = effectLayerKeysDeclared(readFileSync(file, "utf8"), options.declaration ?? "Context.Service");
|
|
4070
|
+
} catch {
|
|
4071
|
+
declared = null;
|
|
4072
|
+
}
|
|
4073
|
+
if (!declared) {
|
|
4074
|
+
return false;
|
|
4075
|
+
}
|
|
4076
|
+
},
|
|
4077
|
+
CallExpression(node) {
|
|
4078
|
+
const first = node.arguments.at(0);
|
|
4079
|
+
if (!declared || first?.type !== "ObjectExpression" || !optionsPatternsTest(callees, astDottedName(node.callee) ?? "")) {
|
|
4080
|
+
return;
|
|
4081
|
+
}
|
|
4082
|
+
const bound = effectLayerKeysBound(first, []);
|
|
4083
|
+
const names = new Set(bound.map(([path]) => path));
|
|
4084
|
+
const short = file.split("/").slice(-2).join("/");
|
|
4085
|
+
const missing = declared.filter((path) => !names.has(path) && !bound.some(([name]) => name.startsWith(`${path}.`)));
|
|
4086
|
+
for (const [path, property] of bound) {
|
|
4087
|
+
if (!declared.some((name) => name === path || path.startsWith(`${name}.`))) {
|
|
4088
|
+
context.report({
|
|
4089
|
+
node: property,
|
|
4090
|
+
messageId: "extra",
|
|
4091
|
+
data: { path, file: short }
|
|
3673
4092
|
});
|
|
3674
4093
|
}
|
|
3675
4094
|
}
|
|
4095
|
+
if (missing.length > 0) {
|
|
4096
|
+
context.report({
|
|
4097
|
+
node: first,
|
|
4098
|
+
messageId: "missing",
|
|
4099
|
+
data: { paths: missing.map((path) => `'${path}'`).join(", "), file: short }
|
|
4100
|
+
});
|
|
4101
|
+
}
|
|
3676
4102
|
}
|
|
3677
4103
|
};
|
|
3678
4104
|
}
|
|
3679
4105
|
});
|
|
3680
4106
|
|
|
3681
4107
|
// src/rules/effect/effect-layers.ts
|
|
3682
|
-
import { defineRule as
|
|
4108
|
+
import { defineRule as defineRule32 } from "@oxlint/plugins";
|
|
3683
4109
|
var effectLayersDefaultMerge = ["Layer.mergeAll"];
|
|
3684
4110
|
var effectLayersDefaultProvide = ["Effect.provide", "Layer.provide", "Layer.provideMerge"];
|
|
3685
4111
|
var effectLayersDefaultMin = 2;
|
|
@@ -3691,7 +4117,7 @@ var effectLayersGroupSchema = {
|
|
|
3691
4117
|
min: { type: "integer", minimum: 1 }
|
|
3692
4118
|
}
|
|
3693
4119
|
};
|
|
3694
|
-
var effectLayers =
|
|
4120
|
+
var effectLayers = defineRule32({
|
|
3695
4121
|
meta: {
|
|
3696
4122
|
type: "layout",
|
|
3697
4123
|
fixable: "whitespace",
|
|
@@ -3773,11 +4199,11 @@ var effectLayers = defineRule30({
|
|
|
3773
4199
|
});
|
|
3774
4200
|
|
|
3775
4201
|
// src/rules/effect/effect-merge-pipe.ts
|
|
3776
|
-
import { defineRule as
|
|
4202
|
+
import { defineRule as defineRule33 } from "@oxlint/plugins";
|
|
3777
4203
|
var effectMergePipeDefaultMerge = ["Layer.mergeAll"];
|
|
3778
4204
|
var effectMergePipeDefaultPipe = ["pipe", "Function.pipe"];
|
|
3779
4205
|
var effectMergePipeDefaultMethods = ["pipe"];
|
|
3780
|
-
var effectMergePipe =
|
|
4206
|
+
var effectMergePipe = defineRule33({
|
|
3781
4207
|
meta: {
|
|
3782
4208
|
type: "layout",
|
|
3783
4209
|
schema: [
|
|
@@ -3822,7 +4248,7 @@ var effectMergePipe = defineRule31({
|
|
|
3822
4248
|
});
|
|
3823
4249
|
|
|
3824
4250
|
// src/rules/effect/effect-schema-first.ts
|
|
3825
|
-
import { defineRule as
|
|
4251
|
+
import { defineRule as defineRule34 } from "@oxlint/plugins";
|
|
3826
4252
|
var effectSchemaFirstDefaultRuntime = [
|
|
3827
4253
|
"Effect",
|
|
3828
4254
|
"Layer",
|
|
@@ -3884,7 +4310,7 @@ function effectSchemaFirstReference(node) {
|
|
|
3884
4310
|
}
|
|
3885
4311
|
return node.type === "TSInterfaceHeritage" ? astDottedName(node.expression) : undefined;
|
|
3886
4312
|
}
|
|
3887
|
-
var effectSchemaFirst =
|
|
4313
|
+
var effectSchemaFirst = defineRule34({
|
|
3888
4314
|
meta: {
|
|
3889
4315
|
type: "suggestion",
|
|
3890
4316
|
schema: [
|
|
@@ -3894,6 +4320,7 @@ var effectSchemaFirst = defineRule32({
|
|
|
3894
4320
|
properties: {
|
|
3895
4321
|
member: { type: "string" },
|
|
3896
4322
|
keep: optionsPatternSchema,
|
|
4323
|
+
allow: optionsPatternSchema,
|
|
3897
4324
|
runtime: { type: "array", items: { type: "string" } },
|
|
3898
4325
|
rows: {
|
|
3899
4326
|
type: "object",
|
|
@@ -3923,6 +4350,7 @@ var effectSchemaFirst = defineRule32({
|
|
|
3923
4350
|
createOnce(context) {
|
|
3924
4351
|
let member = "Type";
|
|
3925
4352
|
let keep = [];
|
|
4353
|
+
let allow = [];
|
|
3926
4354
|
let runtime = new Set(effectSchemaFirstDefaultRuntime);
|
|
3927
4355
|
let rows;
|
|
3928
4356
|
let suffix = "Record";
|
|
@@ -3987,6 +4415,7 @@ var effectSchemaFirst = defineRule32({
|
|
|
3987
4415
|
const options = optionsFirst(context, {});
|
|
3988
4416
|
member = options.member ?? "Type";
|
|
3989
4417
|
keep = optionsPatterns(options.keep);
|
|
4418
|
+
allow = optionsPatterns(options.allow);
|
|
3990
4419
|
runtime = new Set(options.runtime ?? effectSchemaFirstDefaultRuntime);
|
|
3991
4420
|
rows = options.rows ? new Set(options.rows.types ?? ["Kysely.Selectable"]) : undefined;
|
|
3992
4421
|
suffix = options.rows?.suffix ?? "Record";
|
|
@@ -4006,7 +4435,7 @@ var effectSchemaFirst = defineRule32({
|
|
|
4006
4435
|
const typed = new Set(exported.flatMap((node) => node.type === "TSTypeAliasDeclaration" && derives(node) ? [node.id.name] : []));
|
|
4007
4436
|
for (const node of exported.flatMap((declaration) => declaration.type === "VariableDeclaration" ? declaration.declarations : [])) {
|
|
4008
4437
|
const isSchema = node.init?.type === "CallExpression" || node.init?.type === "MemberExpression";
|
|
4009
|
-
if (isSchema && node.id.type === "Identifier" && !typed.has(node.id.name)) {
|
|
4438
|
+
if (isSchema && node.id.type === "Identifier" && !typed.has(node.id.name) && !optionsPatternsTest(allow, node.id.name)) {
|
|
4010
4439
|
context.report({
|
|
4011
4440
|
node,
|
|
4012
4441
|
messageId: "schemaType",
|
|
@@ -4056,8 +4485,104 @@ var effectSchemaFirst = defineRule32({
|
|
|
4056
4485
|
}
|
|
4057
4486
|
});
|
|
4058
4487
|
|
|
4488
|
+
// src/rules/effect/effect-service-params.ts
|
|
4489
|
+
import { defineRule as defineRule35 } from "@oxlint/plugins";
|
|
4490
|
+
function effectServiceParamsKey(member) {
|
|
4491
|
+
if (member.type !== "TSPropertySignature" && member.type !== "TSMethodSignature" || member.computed) {
|
|
4492
|
+
return;
|
|
4493
|
+
}
|
|
4494
|
+
return member.key.type === "Identifier" ? member.key.name : undefined;
|
|
4495
|
+
}
|
|
4496
|
+
function effectServiceParamsTypeName(name) {
|
|
4497
|
+
if (name.type === "TSQualifiedName") {
|
|
4498
|
+
return `${effectServiceParamsTypeName(name.left)}.${name.right.name}`;
|
|
4499
|
+
}
|
|
4500
|
+
return name.type === "Identifier" ? name.name : "this";
|
|
4501
|
+
}
|
|
4502
|
+
var effectServiceParams = defineRule35({
|
|
4503
|
+
meta: {
|
|
4504
|
+
type: "problem",
|
|
4505
|
+
schema: [
|
|
4506
|
+
{
|
|
4507
|
+
type: "object",
|
|
4508
|
+
additionalProperties: false,
|
|
4509
|
+
properties: {
|
|
4510
|
+
name: optionsPatternSchema,
|
|
4511
|
+
allow: optionsPatternSchema,
|
|
4512
|
+
declaration: { type: "string", minLength: 1 }
|
|
4513
|
+
}
|
|
4514
|
+
}
|
|
4515
|
+
],
|
|
4516
|
+
messages: {
|
|
4517
|
+
name: "'{{path}}' takes '{{type}}'; a service method takes a params type that matches {{pattern}}.",
|
|
4518
|
+
inline: "'{{path}}' takes an unnamed type; a service method takes a params type that matches {{pattern}}."
|
|
4519
|
+
}
|
|
4520
|
+
},
|
|
4521
|
+
createOnce(context) {
|
|
4522
|
+
let options = {};
|
|
4523
|
+
let captures = {};
|
|
4524
|
+
let allowed = [];
|
|
4525
|
+
function checkParams(params, keys) {
|
|
4526
|
+
const first = params.at(0);
|
|
4527
|
+
const annotation = first && "typeAnnotation" in first ? first.typeAnnotation?.typeAnnotation : undefined;
|
|
4528
|
+
if (!first || !annotation) {
|
|
4529
|
+
return;
|
|
4530
|
+
}
|
|
4531
|
+
const values = { ...captures, path: keys.join("-"), key: keys.at(-1) ?? "" };
|
|
4532
|
+
const sources = [options.name ?? "^{Folder}\\w*Params$"].flat().map((source) => namingExpand(source, values, true, optionsEscape));
|
|
4533
|
+
const data = { path: keys.join("."), pattern: optionsPatternLabel(sources) };
|
|
4534
|
+
if (annotation.type !== "TSTypeReference") {
|
|
4535
|
+
context.report({
|
|
4536
|
+
node: first,
|
|
4537
|
+
messageId: "inline",
|
|
4538
|
+
data
|
|
4539
|
+
});
|
|
4540
|
+
return;
|
|
4541
|
+
}
|
|
4542
|
+
const type = effectServiceParamsTypeName(annotation.typeName);
|
|
4543
|
+
const last = type.split(".").at(-1) ?? type;
|
|
4544
|
+
if (!optionsPatternsTest(allowed, type) && !optionsPatternsTest(optionsPatterns(sources), last)) {
|
|
4545
|
+
context.report({
|
|
4546
|
+
node: first,
|
|
4547
|
+
messageId: "name",
|
|
4548
|
+
data: { ...data, type }
|
|
4549
|
+
});
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4552
|
+
function check(literal, path) {
|
|
4553
|
+
for (const member of literal.members) {
|
|
4554
|
+
const key = effectServiceParamsKey(member);
|
|
4555
|
+
const type = member.type === "TSPropertySignature" ? member.typeAnnotation?.typeAnnotation : undefined;
|
|
4556
|
+
if (key === undefined) {
|
|
4557
|
+
continue;
|
|
4558
|
+
}
|
|
4559
|
+
if (member.type === "TSMethodSignature") {
|
|
4560
|
+
checkParams(member.params, [...path, key]);
|
|
4561
|
+
} else if (type?.type === "TSFunctionType") {
|
|
4562
|
+
checkParams(type.params, [...path, key]);
|
|
4563
|
+
} else if (type?.type === "TSTypeLiteral") {
|
|
4564
|
+
check(type, [...path, key]);
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
}
|
|
4568
|
+
return {
|
|
4569
|
+
before() {
|
|
4570
|
+
options = optionsFirst(context, {});
|
|
4571
|
+
captures = namingCaptures(context.filename) ?? {};
|
|
4572
|
+
allowed = optionsPatterns(options.allow);
|
|
4573
|
+
},
|
|
4574
|
+
TSTypeLiteral(node) {
|
|
4575
|
+
const call = node.parent.type === "TSTypeParameterInstantiation" ? node.parent.parent : undefined;
|
|
4576
|
+
if (call?.type === "CallExpression" && astDottedName(call.callee) === (options.declaration ?? "Context.Service")) {
|
|
4577
|
+
check(node, []);
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
};
|
|
4581
|
+
}
|
|
4582
|
+
});
|
|
4583
|
+
|
|
4059
4584
|
// src/rules/effect/effect-values-first.ts
|
|
4060
|
-
import { defineRule as
|
|
4585
|
+
import { defineRule as defineRule36 } from "@oxlint/plugins";
|
|
4061
4586
|
var effectValuesFirstDefaultPinned = [
|
|
4062
4587
|
"^Layer\\.",
|
|
4063
4588
|
"^Context\\.",
|
|
@@ -4066,7 +4591,7 @@ var effectValuesFirstDefaultPinned = [
|
|
|
4066
4591
|
"(?:^|\\.)layer[^.]*$",
|
|
4067
4592
|
"^Schema\\.(?:asserts|decode\\w*|encode\\w*|is|isSchema|isSchemaError|resolveAnnotations)$"
|
|
4068
4593
|
];
|
|
4069
|
-
var effectValuesFirst =
|
|
4594
|
+
var effectValuesFirst = defineRule36({
|
|
4070
4595
|
meta: {
|
|
4071
4596
|
type: "suggestion",
|
|
4072
4597
|
schema: [
|
|
@@ -4148,12 +4673,96 @@ var effectValuesFirst = defineRule33({
|
|
|
4148
4673
|
}
|
|
4149
4674
|
});
|
|
4150
4675
|
|
|
4151
|
-
// src/rules/
|
|
4152
|
-
import { defineRule as
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4676
|
+
// src/rules/effect/effect-yield-name.ts
|
|
4677
|
+
import { defineRule as defineRule37 } from "@oxlint/plugins";
|
|
4678
|
+
var effectYieldName = defineRule37({
|
|
4679
|
+
meta: {
|
|
4680
|
+
type: "suggestion",
|
|
4681
|
+
schema: [
|
|
4682
|
+
{
|
|
4683
|
+
type: "object",
|
|
4684
|
+
additionalProperties: false,
|
|
4685
|
+
properties: {
|
|
4686
|
+
names: { type: "object", additionalProperties: optionsPatternSchema },
|
|
4687
|
+
allow: optionsPatternSchema
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4690
|
+
],
|
|
4691
|
+
messages: {
|
|
4692
|
+
name: "Name this '{{expected}}', not '{{actual}}': it holds {{source}}."
|
|
4693
|
+
}
|
|
4694
|
+
},
|
|
4695
|
+
createOnce(context) {
|
|
4696
|
+
return {
|
|
4697
|
+
VariableDeclarator(node) {
|
|
4698
|
+
const { id, init } = node;
|
|
4699
|
+
if (id.type !== "Identifier" || init?.type !== "YieldExpression" || !init.delegate || init.argument?.type !== "MemberExpression") {
|
|
4700
|
+
return;
|
|
4701
|
+
}
|
|
4702
|
+
const service = astMemberName(init.argument);
|
|
4703
|
+
const source = astDottedName(init.argument);
|
|
4704
|
+
if (!service || !source || !/^[A-Z]/.test(service)) {
|
|
4705
|
+
return;
|
|
4706
|
+
}
|
|
4707
|
+
const options = optionsFirst(context, {});
|
|
4708
|
+
const named = options.names?.[source] ?? options.names?.[`*.${service}`];
|
|
4709
|
+
const expected = named === undefined ? [`${service.charAt(0).toLowerCase()}${service.slice(1)}`] : [named].flat();
|
|
4710
|
+
if (!expected.includes(id.name) && !optionsPatternsTest(optionsPatterns(options.allow), id.name)) {
|
|
4711
|
+
context.report({
|
|
4712
|
+
node: id,
|
|
4713
|
+
messageId: "name",
|
|
4714
|
+
data: { expected: expected.join(" or "), actual: id.name, source }
|
|
4715
|
+
});
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
};
|
|
4719
|
+
}
|
|
4720
|
+
});
|
|
4721
|
+
|
|
4722
|
+
// src/rules/effect/effect-yields-first.ts
|
|
4723
|
+
import { defineRule as defineRule38 } from "@oxlint/plugins";
|
|
4724
|
+
function effectYieldsFirstService(statement) {
|
|
4725
|
+
const init = statement.type === "VariableDeclaration" && statement.declarations.length === 1 ? statement.declarations[0].init : null;
|
|
4726
|
+
const argument = init?.type === "YieldExpression" && init.delegate ? init.argument : null;
|
|
4727
|
+
return argument?.type === "MemberExpression" && /^[A-Z]/.test(astMemberName(argument) ?? "") ? astDottedName(argument) : undefined;
|
|
4728
|
+
}
|
|
4729
|
+
var effectYieldsFirst = defineRule38({
|
|
4730
|
+
meta: {
|
|
4731
|
+
type: "suggestion",
|
|
4732
|
+
messages: {
|
|
4733
|
+
first: "Yield {{source}} with the other dependencies, at the top of the function."
|
|
4734
|
+
}
|
|
4735
|
+
},
|
|
4736
|
+
createOnce(context) {
|
|
4737
|
+
return {
|
|
4738
|
+
FunctionExpression(node) {
|
|
4739
|
+
if (!node.generator || !node.body) {
|
|
4740
|
+
return;
|
|
4741
|
+
}
|
|
4742
|
+
let started = false;
|
|
4743
|
+
for (const statement of node.body.body) {
|
|
4744
|
+
const source = effectYieldsFirstService(statement);
|
|
4745
|
+
if (source === undefined) {
|
|
4746
|
+
started = true;
|
|
4747
|
+
} else if (started) {
|
|
4748
|
+
context.report({
|
|
4749
|
+
node: statement,
|
|
4750
|
+
messageId: "first",
|
|
4751
|
+
data: { source }
|
|
4752
|
+
});
|
|
4753
|
+
}
|
|
4754
|
+
}
|
|
4755
|
+
}
|
|
4756
|
+
};
|
|
4757
|
+
}
|
|
4758
|
+
});
|
|
4759
|
+
|
|
4760
|
+
// src/rules/files/files-components-only.ts
|
|
4761
|
+
import { defineRule as defineRule39 } from "@oxlint/plugins";
|
|
4762
|
+
|
|
4763
|
+
// src/utils/react.utils.ts
|
|
4764
|
+
function reactReturnsOutput(program, node) {
|
|
4765
|
+
const expressions = astReturnExpressions(node);
|
|
4157
4766
|
if (expressions.some((expression) => astContainsJsx(expression) || expression.type === "Literal" && expression.value === null)) {
|
|
4158
4767
|
return true;
|
|
4159
4768
|
}
|
|
@@ -4221,7 +4830,7 @@ function reactIsComponent(program, candidate) {
|
|
|
4221
4830
|
function componentName(basename2) {
|
|
4222
4831
|
return basename2.replace(/\.(tsx?|jsx?)$/, "").replaceAll(/(?:^[-.]*|[-.]+)(.?)/g, (_, char) => char.toUpperCase());
|
|
4223
4832
|
}
|
|
4224
|
-
var filesComponentsOnly =
|
|
4833
|
+
var filesComponentsOnly = defineRule39({
|
|
4225
4834
|
meta: {
|
|
4226
4835
|
type: "problem",
|
|
4227
4836
|
schema: [
|
|
@@ -4309,7 +4918,7 @@ var filesComponentsOnly = defineRule34({
|
|
|
4309
4918
|
});
|
|
4310
4919
|
|
|
4311
4920
|
// src/rules/files/files-constants-only.ts
|
|
4312
|
-
import { defineRule as
|
|
4921
|
+
import { defineRule as defineRule40 } from "@oxlint/plugins";
|
|
4313
4922
|
function unwrap(expression) {
|
|
4314
4923
|
if (expression.type === "ParenthesizedExpression" || expression.type === "TSAsExpression" || expression.type === "TSSatisfiesExpression" || expression.type === "TSTypeAssertion" || expression.type === "TSNonNullExpression" || expression.type === "TSInstantiationExpression") {
|
|
4315
4924
|
return unwrap(expression.expression);
|
|
@@ -4364,7 +4973,7 @@ function collectBindings(program) {
|
|
|
4364
4973
|
}
|
|
4365
4974
|
return bindings;
|
|
4366
4975
|
}
|
|
4367
|
-
var filesConstantsOnly =
|
|
4976
|
+
var filesConstantsOnly = defineRule40({
|
|
4368
4977
|
meta: {
|
|
4369
4978
|
type: "problem",
|
|
4370
4979
|
schema: [
|
|
@@ -4463,7 +5072,7 @@ var filesConstantsOnly = defineRule35({
|
|
|
4463
5072
|
});
|
|
4464
5073
|
|
|
4465
5074
|
// src/rules/files/files-domain-constants.ts
|
|
4466
|
-
import { defineRule as
|
|
5075
|
+
import { defineRule as defineRule41 } from "@oxlint/plugins";
|
|
4467
5076
|
import { basename as basename2, dirname as dirname2 } from "node:path";
|
|
4468
5077
|
function isData(expression) {
|
|
4469
5078
|
const value = astUnwrap(expression);
|
|
@@ -4481,7 +5090,7 @@ function isData(expression) {
|
|
|
4481
5090
|
}
|
|
4482
5091
|
return value.type === "NewExpression" && value.callee.type === "Identifier" && ["Map", "Set"].includes(value.callee.name) && value.arguments[0]?.type === "ArrayExpression" && value.arguments[0].elements.length > 0;
|
|
4483
5092
|
}
|
|
4484
|
-
var filesDomainConstants =
|
|
5093
|
+
var filesDomainConstants = defineRule41({
|
|
4485
5094
|
meta: {
|
|
4486
5095
|
type: "problem",
|
|
4487
5096
|
schema: [
|
|
@@ -4546,12 +5155,8 @@ var filesDomainConstants = defineRule36({
|
|
|
4546
5155
|
});
|
|
4547
5156
|
|
|
4548
5157
|
// src/rules/files/files-exports.ts
|
|
4549
|
-
import { defineRule as
|
|
4550
|
-
|
|
4551
|
-
function escapeRegExp(value) {
|
|
4552
|
-
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4553
|
-
}
|
|
4554
|
-
var filesExports = defineRule37({
|
|
5158
|
+
import { defineRule as defineRule42 } from "@oxlint/plugins";
|
|
5159
|
+
var filesExports = defineRule42({
|
|
4555
5160
|
meta: {
|
|
4556
5161
|
type: "problem",
|
|
4557
5162
|
schema: [
|
|
@@ -4567,6 +5172,8 @@ var filesExports = defineRule37({
|
|
|
4567
5172
|
separators: { type: "array", items: { type: "string" } },
|
|
4568
5173
|
types: { type: "boolean" },
|
|
4569
5174
|
factory: { type: "string" },
|
|
5175
|
+
file: { type: "string" },
|
|
5176
|
+
placeholder: { type: "string" },
|
|
4570
5177
|
scope: { type: "string", enum: ["exports", "declarations"] }
|
|
4571
5178
|
},
|
|
4572
5179
|
anyOf: [{ required: ["names"] }, { required: ["patterns"] }, { required: ["factory"] }]
|
|
@@ -4605,16 +5212,12 @@ var filesExports = defineRule37({
|
|
|
4605
5212
|
if (options.names === undefined && options.patterns === undefined) {
|
|
4606
5213
|
return;
|
|
4607
5214
|
}
|
|
4608
|
-
const
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
const
|
|
4613
|
-
const
|
|
4614
|
-
const patterns = (options.patterns ?? []).map((template) => {
|
|
4615
|
-
const source = template.replaceAll("{Domain}", escapeRegExp(pascalDomain)).replaceAll("{domain}", escapeRegExp(camelDomain));
|
|
4616
|
-
return new RegExp(`^(?:${source})$`);
|
|
4617
|
-
});
|
|
5215
|
+
const captures = namingCaptures(context.filename, options);
|
|
5216
|
+
if (!captures) {
|
|
5217
|
+
return;
|
|
5218
|
+
}
|
|
5219
|
+
const allowed = (options.names ?? []).map((template) => namingExpand(template, captures, true));
|
|
5220
|
+
const patterns = (options.patterns ?? []).map((template) => new RegExp(`^(?:${namingExpand(template, captures, true, optionsEscape)})$`));
|
|
4618
5221
|
const allowedLabel = [...allowed, ...patterns.map((pattern) => pattern.toString())].join(", ");
|
|
4619
5222
|
for (const binding of exportsCollect(program)) {
|
|
4620
5223
|
if (options.types && binding.typeOnly) {
|
|
@@ -4635,12 +5238,12 @@ var filesExports = defineRule37({
|
|
|
4635
5238
|
});
|
|
4636
5239
|
|
|
4637
5240
|
// src/rules/files/files-externals.ts
|
|
4638
|
-
import { defineRule as
|
|
4639
|
-
import { existsSync as existsSync2, readFileSync } from "node:fs";
|
|
5241
|
+
import { defineRule as defineRule43 } from "@oxlint/plugins";
|
|
5242
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
4640
5243
|
import { builtinModules as builtinModules2 } from "node:module";
|
|
4641
5244
|
import { dirname as dirname3, join as join2 } from "node:path";
|
|
4642
5245
|
var filesExternalsConfigs = "^(?:vite|next)\\.config\\.[cm]?[jt]s$";
|
|
4643
|
-
var filesExternals =
|
|
5246
|
+
var filesExternals = defineRule43({
|
|
4644
5247
|
meta: {
|
|
4645
5248
|
type: "problem",
|
|
4646
5249
|
schema: [
|
|
@@ -4666,7 +5269,7 @@ var filesExternals = defineRule38({
|
|
|
4666
5269
|
}
|
|
4667
5270
|
let dependencies;
|
|
4668
5271
|
try {
|
|
4669
|
-
const manifest = JSON.parse(
|
|
5272
|
+
const manifest = JSON.parse(readFileSync2(join2(folder, "package.json"), "utf8"));
|
|
4670
5273
|
dependencies = [...Object.keys(manifest.dependencies ?? {}), ...Object.keys(manifest.optionalDependencies ?? {})];
|
|
4671
5274
|
} catch {
|
|
4672
5275
|
return;
|
|
@@ -4733,7 +5336,7 @@ var filesExternals = defineRule38({
|
|
|
4733
5336
|
});
|
|
4734
5337
|
|
|
4735
5338
|
// src/rules/files/files-factory-keys.ts
|
|
4736
|
-
import { defineRule as
|
|
5339
|
+
import { defineRule as defineRule44 } from "@oxlint/plugins";
|
|
4737
5340
|
function returnedObject(expression) {
|
|
4738
5341
|
if (expression?.type === "TSAsExpression" || expression?.type === "TSSatisfiesExpression" || expression?.type === "TSTypeAssertion" || expression?.type === "TSNonNullExpression") {
|
|
4739
5342
|
return returnedObject(expression.expression);
|
|
@@ -4744,7 +5347,7 @@ function compileKeys(pattern, keys, requireKeys) {
|
|
|
4744
5347
|
const allowedList = keys.concat(requireKeys.filter((key) => !keys.includes(key)));
|
|
4745
5348
|
return { patterns: optionsPatternsWithEmpty(pattern), allowed: new Set(allowedList), allowedList, required: requireKeys };
|
|
4746
5349
|
}
|
|
4747
|
-
var filesFactoryKeys =
|
|
5350
|
+
var filesFactoryKeys = defineRule44({
|
|
4748
5351
|
meta: {
|
|
4749
5352
|
type: "problem",
|
|
4750
5353
|
schema: [
|
|
@@ -4862,8 +5465,8 @@ var filesFactoryKeys = defineRule39({
|
|
|
4862
5465
|
});
|
|
4863
5466
|
|
|
4864
5467
|
// src/rules/files/files-helpers.ts
|
|
4865
|
-
import { defineRule as
|
|
4866
|
-
var filesHelpers =
|
|
5468
|
+
import { defineRule as defineRule45 } from "@oxlint/plugins";
|
|
5469
|
+
var filesHelpers = defineRule45({
|
|
4867
5470
|
meta: {
|
|
4868
5471
|
type: "problem",
|
|
4869
5472
|
schema: [
|
|
@@ -4911,8 +5514,8 @@ var filesHelpers = defineRule40({
|
|
|
4911
5514
|
});
|
|
4912
5515
|
|
|
4913
5516
|
// src/rules/files/files-no-functions.ts
|
|
4914
|
-
import { defineRule as
|
|
4915
|
-
var filesNoFunctions =
|
|
5517
|
+
import { defineRule as defineRule46 } from "@oxlint/plugins";
|
|
5518
|
+
var filesNoFunctions = defineRule46({
|
|
4916
5519
|
meta: {
|
|
4917
5520
|
type: "problem",
|
|
4918
5521
|
schema: [
|
|
@@ -4966,8 +5569,8 @@ var filesNoFunctions = defineRule41({
|
|
|
4966
5569
|
});
|
|
4967
5570
|
|
|
4968
5571
|
// src/rules/files/files-restricted.ts
|
|
4969
|
-
import { defineRule as
|
|
4970
|
-
var filesRestricted =
|
|
5572
|
+
import { defineRule as defineRule47 } from "@oxlint/plugins";
|
|
5573
|
+
var filesRestricted = defineRule47({
|
|
4971
5574
|
meta: {
|
|
4972
5575
|
type: "problem",
|
|
4973
5576
|
schema: [
|
|
@@ -4999,7 +5602,7 @@ var filesRestricted = defineRule42({
|
|
|
4999
5602
|
});
|
|
5000
5603
|
|
|
5001
5604
|
// src/rules/files/files-route.ts
|
|
5002
|
-
import { defineRule as
|
|
5605
|
+
import { defineRule as defineRule48 } from "@oxlint/plugins";
|
|
5003
5606
|
function inspectAdapter(context, item, bannedHooks, banIntrinsicJsx) {
|
|
5004
5607
|
astVisit(item.node.body, [item.node], (node) => {
|
|
5005
5608
|
if (banIntrinsicJsx && node.type === "JSXOpeningElement" && node.name.type === "JSXIdentifier" && /^[a-z]/.test(node.name.name)) {
|
|
@@ -5019,7 +5622,7 @@ function inspectAdapter(context, item, bannedHooks, banIntrinsicJsx) {
|
|
|
5019
5622
|
}
|
|
5020
5623
|
});
|
|
5021
5624
|
}
|
|
5022
|
-
var filesRoute =
|
|
5625
|
+
var filesRoute = defineRule48({
|
|
5023
5626
|
meta: {
|
|
5024
5627
|
type: "problem",
|
|
5025
5628
|
schema: [
|
|
@@ -5088,8 +5691,247 @@ var filesRoute = defineRule43({
|
|
|
5088
5691
|
}
|
|
5089
5692
|
});
|
|
5090
5693
|
|
|
5694
|
+
// src/rules/files/files-siblings.ts
|
|
5695
|
+
import { defineRule as defineRule49 } from "@oxlint/plugins";
|
|
5696
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
5697
|
+
import { dirname as dirname4, join as join3 } from "node:path";
|
|
5698
|
+
var filesSiblings = defineRule49({
|
|
5699
|
+
meta: {
|
|
5700
|
+
type: "problem",
|
|
5701
|
+
schema: [
|
|
5702
|
+
{
|
|
5703
|
+
type: "object",
|
|
5704
|
+
additionalProperties: false,
|
|
5705
|
+
properties: {
|
|
5706
|
+
require: optionsPatternSchema,
|
|
5707
|
+
message: { type: "string" }
|
|
5708
|
+
},
|
|
5709
|
+
required: ["require"]
|
|
5710
|
+
}
|
|
5711
|
+
],
|
|
5712
|
+
messages: {
|
|
5713
|
+
missing: "This file needs '{{file}}' beside it.{{message}}"
|
|
5714
|
+
}
|
|
5715
|
+
},
|
|
5716
|
+
createOnce(context) {
|
|
5717
|
+
return {
|
|
5718
|
+
Program(program) {
|
|
5719
|
+
const options = optionsFirst(context, { require: [] });
|
|
5720
|
+
const captures = namingCaptures(context.filename) ?? {};
|
|
5721
|
+
for (const template of [options.require].flat()) {
|
|
5722
|
+
const file = namingExpand(template, captures, false);
|
|
5723
|
+
if (!existsSync3(join3(dirname4(context.filename), file))) {
|
|
5724
|
+
context.report({
|
|
5725
|
+
node: program,
|
|
5726
|
+
messageId: "missing",
|
|
5727
|
+
data: { file, message: options.message ? ` ${options.message}` : "" }
|
|
5728
|
+
});
|
|
5729
|
+
}
|
|
5730
|
+
}
|
|
5731
|
+
}
|
|
5732
|
+
};
|
|
5733
|
+
}
|
|
5734
|
+
});
|
|
5735
|
+
|
|
5736
|
+
// src/rules/files/files-top-level.ts
|
|
5737
|
+
import { defineRule as defineRule50 } from "@oxlint/plugins";
|
|
5738
|
+
var filesTopLevelKinds = [
|
|
5739
|
+
"import",
|
|
5740
|
+
"export",
|
|
5741
|
+
"reexport",
|
|
5742
|
+
"default",
|
|
5743
|
+
"type",
|
|
5744
|
+
"interface",
|
|
5745
|
+
"enum",
|
|
5746
|
+
"class",
|
|
5747
|
+
"namespace",
|
|
5748
|
+
"const",
|
|
5749
|
+
"let",
|
|
5750
|
+
"var",
|
|
5751
|
+
"function",
|
|
5752
|
+
"function-const",
|
|
5753
|
+
"call",
|
|
5754
|
+
"statement"
|
|
5755
|
+
];
|
|
5756
|
+
var filesTopLevelDeclared = new Map([
|
|
5757
|
+
["FunctionDeclaration", "function"],
|
|
5758
|
+
["TSDeclareFunction", "function"],
|
|
5759
|
+
["ClassDeclaration", "class"],
|
|
5760
|
+
["TSTypeAliasDeclaration", "type"],
|
|
5761
|
+
["TSInterfaceDeclaration", "interface"],
|
|
5762
|
+
["TSEnumDeclaration", "enum"],
|
|
5763
|
+
["TSModuleDeclaration", "namespace"]
|
|
5764
|
+
]);
|
|
5765
|
+
var filesTopLevelEntries = {
|
|
5766
|
+
type: "array",
|
|
5767
|
+
items: {
|
|
5768
|
+
anyOf: [
|
|
5769
|
+
{ type: "string" },
|
|
5770
|
+
{
|
|
5771
|
+
type: "object",
|
|
5772
|
+
additionalProperties: false,
|
|
5773
|
+
properties: {
|
|
5774
|
+
kind: { anyOf: [{ type: "string", enum: [...filesTopLevelKinds] }, { type: "array", items: { type: "string", enum: [...filesTopLevelKinds] }, minItems: 1 }] },
|
|
5775
|
+
names: optionsPatternSchema,
|
|
5776
|
+
from: optionsPatternSchema,
|
|
5777
|
+
exported: { type: "boolean" },
|
|
5778
|
+
message: { type: "string" }
|
|
5779
|
+
},
|
|
5780
|
+
required: ["kind"]
|
|
5781
|
+
}
|
|
5782
|
+
]
|
|
5783
|
+
}
|
|
5784
|
+
};
|
|
5785
|
+
var filesTopLevelValues = new Map([
|
|
5786
|
+
["Literal", "<literal>"],
|
|
5787
|
+
["TemplateLiteral", "<literal>"],
|
|
5788
|
+
["ObjectExpression", "<object>"],
|
|
5789
|
+
["ArrayExpression", "<array>"],
|
|
5790
|
+
["FunctionExpression", "<function>"],
|
|
5791
|
+
["ArrowFunctionExpression", "<function>"]
|
|
5792
|
+
]);
|
|
5793
|
+
function filesTopLevelHead(node) {
|
|
5794
|
+
if (!node) {
|
|
5795
|
+
return "<none>";
|
|
5796
|
+
}
|
|
5797
|
+
if (node.type === "AwaitExpression") {
|
|
5798
|
+
return filesTopLevelHead(node.argument);
|
|
5799
|
+
}
|
|
5800
|
+
if (node.type === "ParenthesizedExpression" || node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression" || node.type === "TSTypeAssertion" || node.type === "TSNonNullExpression" || node.type === "ChainExpression") {
|
|
5801
|
+
return filesTopLevelHead(node.expression);
|
|
5802
|
+
}
|
|
5803
|
+
if (node.type === "CallExpression" || node.type === "NewExpression") {
|
|
5804
|
+
return filesTopLevelHead(node.callee);
|
|
5805
|
+
}
|
|
5806
|
+
if (node.type === "TaggedTemplateExpression") {
|
|
5807
|
+
return filesTopLevelHead(node.tag);
|
|
5808
|
+
}
|
|
5809
|
+
if (node.type === "MemberExpression") {
|
|
5810
|
+
return astDottedName(node) ?? filesTopLevelHead(node.object);
|
|
5811
|
+
}
|
|
5812
|
+
return node.type === "Identifier" ? node.name : filesTopLevelValues.get(node.type) ?? "<other>";
|
|
5813
|
+
}
|
|
5814
|
+
function filesTopLevelItems(statement) {
|
|
5815
|
+
const item = (kind, name, node2, from = "", exported2 = false) => ({ kind, name, from, exported: exported2, node: node2 });
|
|
5816
|
+
if (statement.type === "ExpressionStatement" && statement.directive) {
|
|
5817
|
+
return [];
|
|
5818
|
+
}
|
|
5819
|
+
if (statement.type === "ImportDeclaration") {
|
|
5820
|
+
return [item("import", statement.source.value, statement)];
|
|
5821
|
+
}
|
|
5822
|
+
if (statement.type === "ExportAllDeclaration") {
|
|
5823
|
+
return [item("reexport", statement.source.value, statement, "", true)];
|
|
5824
|
+
}
|
|
5825
|
+
if (statement.type === "ExportNamedDeclaration" && !statement.declaration) {
|
|
5826
|
+
const kind = statement.source ? "reexport" : "export";
|
|
5827
|
+
const names = statement.specifiers.map((specifier) => item(kind, astModuleName(specifier.exported), specifier, statement.source?.value ?? "", true));
|
|
5828
|
+
return names.length > 0 ? names : [item(kind, "", statement, statement.source?.value ?? "", true)];
|
|
5829
|
+
}
|
|
5830
|
+
const exported = statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration";
|
|
5831
|
+
const node = exported ? statement.declaration : statement;
|
|
5832
|
+
if (!node) {
|
|
5833
|
+
return [];
|
|
5834
|
+
}
|
|
5835
|
+
if (node.type === "VariableDeclaration") {
|
|
5836
|
+
return node.declarations.flatMap((declarator) => {
|
|
5837
|
+
const variable = node.kind === "using" || node.kind === "await using" ? "statement" : node.kind;
|
|
5838
|
+
const kind = declarationsIsFunctionValue(declarator.init) ? "function-const" : variable;
|
|
5839
|
+
return astBindings(declarator.id).map((binding) => item(kind, binding.name, declarator, filesTopLevelHead(declarator.init), exported));
|
|
5840
|
+
});
|
|
5841
|
+
}
|
|
5842
|
+
const declared = filesTopLevelDeclared.get(node.type);
|
|
5843
|
+
if (declared) {
|
|
5844
|
+
const id = "id" in node ? node.id : null;
|
|
5845
|
+
return [item(declared, id && "name" in id ? id.name : "", node, "", exported)];
|
|
5846
|
+
}
|
|
5847
|
+
if (statement.type === "ExportDefaultDeclaration") {
|
|
5848
|
+
return [item("default", filesTopLevelHead(node), statement, filesTopLevelHead(node), true)];
|
|
5849
|
+
}
|
|
5850
|
+
if (statement.type === "ExpressionStatement") {
|
|
5851
|
+
const expression = astUnwrap(statement.expression);
|
|
5852
|
+
if (expression.type === "CallExpression" || expression.type === "TaggedTemplateExpression") {
|
|
5853
|
+
return [item("call", filesTopLevelHead(expression), statement)];
|
|
5854
|
+
}
|
|
5855
|
+
}
|
|
5856
|
+
return [item("statement", statement.type, statement)];
|
|
5857
|
+
}
|
|
5858
|
+
var filesTopLevel = defineRule50({
|
|
5859
|
+
meta: {
|
|
5860
|
+
type: "problem",
|
|
5861
|
+
schema: [
|
|
5862
|
+
{
|
|
5863
|
+
type: "object",
|
|
5864
|
+
additionalProperties: false,
|
|
5865
|
+
properties: {
|
|
5866
|
+
allow: filesTopLevelEntries,
|
|
5867
|
+
deny: filesTopLevelEntries,
|
|
5868
|
+
message: { type: "string" },
|
|
5869
|
+
stem: { type: "string", enum: [...namingStemModes, "folder"] },
|
|
5870
|
+
singularize: { type: "string", enum: ["none", "trailing-s"] },
|
|
5871
|
+
roles: { type: "array", items: { type: "string" } },
|
|
5872
|
+
separators: { type: "array", items: { type: "string" } },
|
|
5873
|
+
file: { type: "string" },
|
|
5874
|
+
placeholder: { type: "string" }
|
|
5875
|
+
},
|
|
5876
|
+
anyOf: [{ required: ["allow"] }, { required: ["deny"] }]
|
|
5877
|
+
}
|
|
5878
|
+
],
|
|
5879
|
+
messages: {
|
|
5880
|
+
denied: "Top-level {{what}} is not allowed in this file.{{message}}",
|
|
5881
|
+
unlisted: "Top-level {{what}} is not allowed in this file; allowed: {{allowed}}.{{message}}"
|
|
5882
|
+
}
|
|
5883
|
+
},
|
|
5884
|
+
createOnce(context) {
|
|
5885
|
+
return {
|
|
5886
|
+
Program(program) {
|
|
5887
|
+
const options = optionsFirst(context, {});
|
|
5888
|
+
const captures = namingCaptures(context.filename, options);
|
|
5889
|
+
if (!captures) {
|
|
5890
|
+
return;
|
|
5891
|
+
}
|
|
5892
|
+
const patterns = (pattern) => pattern === undefined ? null : optionsPatterns([pattern].flat().map((source) => namingExpand(source, captures, true, optionsEscape)));
|
|
5893
|
+
const matcher = (entry) => {
|
|
5894
|
+
if (typeof entry !== "string") {
|
|
5895
|
+
return { kinds: [entry.kind].flat(), names: patterns(entry.names), from: patterns(entry.from), exported: entry.exported, message: entry.message ?? "" };
|
|
5896
|
+
}
|
|
5897
|
+
const kind = filesTopLevelKinds.find((name) => name === entry);
|
|
5898
|
+
return kind ? { kinds: [kind], names: null, from: null, message: "" } : { kinds: ["call"], names: optionsWildcards(entry), from: null, message: "" };
|
|
5899
|
+
};
|
|
5900
|
+
const matches = (item) => (entry) => entry.kinds.includes(item.kind) && (entry.names === null || optionsPatternsTest(entry.names, item.name)) && (entry.from === null || optionsPatternsTest(entry.from, item.from)) && (entry.exported === undefined || entry.exported === item.exported);
|
|
5901
|
+
const allow = options.allow?.map(matcher);
|
|
5902
|
+
const deny = (options.deny ?? []).map(matcher);
|
|
5903
|
+
const label = (entry) => [
|
|
5904
|
+
entry.exported === undefined ? [] : [entry.exported ? "exported" : "unexported"],
|
|
5905
|
+
[entry.kind].flat().join("/"),
|
|
5906
|
+
entry.names === undefined ? [] : ["named", [entry.names].flat().join(" | ")],
|
|
5907
|
+
entry.from === undefined ? [] : ["from", [entry.from].flat().join(" | ")]
|
|
5908
|
+
].flat().join(" ");
|
|
5909
|
+
const allowed = (options.allow ?? []).map((entry) => typeof entry === "string" ? entry : label(entry)).join(", ");
|
|
5910
|
+
const hint = options.message ? ` ${options.message}` : "";
|
|
5911
|
+
for (const item of program.body.flatMap(filesTopLevelItems)) {
|
|
5912
|
+
const what = item.name && item.kind !== "statement" ? `${item.kind} '${item.name}'` : item.kind;
|
|
5913
|
+
const denied = deny.find(matches(item));
|
|
5914
|
+
if (denied) {
|
|
5915
|
+
context.report({
|
|
5916
|
+
node: item.node,
|
|
5917
|
+
messageId: "denied",
|
|
5918
|
+
data: { what, message: denied.message ? ` ${denied.message}` : hint }
|
|
5919
|
+
});
|
|
5920
|
+
} else if (allow && !allow.some(matches(item))) {
|
|
5921
|
+
context.report({
|
|
5922
|
+
node: item.node,
|
|
5923
|
+
messageId: "unlisted",
|
|
5924
|
+
data: { what, allowed, message: hint }
|
|
5925
|
+
});
|
|
5926
|
+
}
|
|
5927
|
+
}
|
|
5928
|
+
}
|
|
5929
|
+
};
|
|
5930
|
+
}
|
|
5931
|
+
});
|
|
5932
|
+
|
|
5091
5933
|
// src/rules/files/files-types-only.ts
|
|
5092
|
-
import { defineRule as
|
|
5934
|
+
import { defineRule as defineRule51 } from "@oxlint/plugins";
|
|
5093
5935
|
function runtimeImportLocalNames(statement) {
|
|
5094
5936
|
return statement.specifiers.flatMap((specifier) => specifier.type !== "ImportSpecifier" || specifier.importKind !== "type" ? [specifier.local.name] : []);
|
|
5095
5937
|
}
|
|
@@ -5258,7 +6100,7 @@ function runtimeKind(node) {
|
|
|
5258
6100
|
}
|
|
5259
6101
|
return node.type === "FunctionDeclaration" ? "function" : "variable";
|
|
5260
6102
|
}
|
|
5261
|
-
var filesTypesOnly =
|
|
6103
|
+
var filesTypesOnly = defineRule51({
|
|
5262
6104
|
meta: {
|
|
5263
6105
|
type: "problem",
|
|
5264
6106
|
schema: [
|
|
@@ -5366,8 +6208,8 @@ var filesTypesOnly = defineRule44({
|
|
|
5366
6208
|
});
|
|
5367
6209
|
|
|
5368
6210
|
// src/rules/layout/layout-arrow-body.ts
|
|
5369
|
-
import { defineRule as
|
|
5370
|
-
var layoutArrowBody =
|
|
6211
|
+
import { defineRule as defineRule52 } from "@oxlint/plugins";
|
|
6212
|
+
var layoutArrowBody = defineRule52({
|
|
5371
6213
|
meta: {
|
|
5372
6214
|
type: "layout",
|
|
5373
6215
|
fixable: "whitespace",
|
|
@@ -5419,12 +6261,12 @@ var layoutArrowBody = defineRule45({
|
|
|
5419
6261
|
});
|
|
5420
6262
|
|
|
5421
6263
|
// src/rules/layout/layout-call-arguments.ts
|
|
5422
|
-
import { defineRule as
|
|
6264
|
+
import { defineRule as defineRule53 } from "@oxlint/plugins";
|
|
5423
6265
|
function layoutCallArgumentsHuggable(node) {
|
|
5424
6266
|
return astIsInlineFunction(node) || node.type === "ObjectExpression" || node.type === "ArrayExpression" || astIsCall(node) && node.arguments.length === 1 && (node.callee.type !== "MemberExpression" || astIsOneLine(node.callee)) && node.arguments.every(layoutCallArgumentsHuggable);
|
|
5425
6267
|
}
|
|
5426
6268
|
var layoutCallArgumentsMax = { type: "integer", minimum: 1 };
|
|
5427
|
-
var layoutCallArguments =
|
|
6269
|
+
var layoutCallArguments = defineRule53({
|
|
5428
6270
|
meta: {
|
|
5429
6271
|
type: "layout",
|
|
5430
6272
|
fixable: "whitespace",
|
|
@@ -5443,6 +6285,12 @@ var layoutCallArguments = defineRule46({
|
|
|
5443
6285
|
type: "boolean",
|
|
5444
6286
|
default: true
|
|
5445
6287
|
},
|
|
6288
|
+
pipe: {
|
|
6289
|
+
type: "object",
|
|
6290
|
+
additionalProperties: false,
|
|
6291
|
+
required: ["min"],
|
|
6292
|
+
properties: { min: { type: "integer", minimum: 2 }, methods: { type: "array", items: { type: "string" } } }
|
|
6293
|
+
},
|
|
5446
6294
|
hug: {
|
|
5447
6295
|
type: "object",
|
|
5448
6296
|
additionalProperties: false,
|
|
@@ -5465,6 +6313,7 @@ var layoutCallArguments = defineRule46({
|
|
|
5465
6313
|
],
|
|
5466
6314
|
messages: {
|
|
5467
6315
|
multiline: "A call with {{min}}+ arguments puts each argument on its own line.",
|
|
6316
|
+
pipe: "A pipe with {{min}}+ steps puts each step on its own line.",
|
|
5468
6317
|
hug: "Put every argument on its own line. Only a last callback, object or array may hug the call, after simple arguments.",
|
|
5469
6318
|
prefer: "Hug the last argument: it is a callback, object or array after simple arguments.",
|
|
5470
6319
|
join: "This fits on one line, so keep it on one line."
|
|
@@ -5474,11 +6323,13 @@ var layoutCallArguments = defineRule46({
|
|
|
5474
6323
|
let options = {};
|
|
5475
6324
|
let minArguments = 3;
|
|
5476
6325
|
let indentUnit = sourceIndentUnit();
|
|
5477
|
-
|
|
6326
|
+
let pipeMethods = new Set;
|
|
6327
|
+
const isPipe = (node) => node.callee.type === "MemberExpression" && pipeMethods.has(astMemberName(node.callee) ?? "");
|
|
6328
|
+
const split = (node, min, messageId) => {
|
|
5478
6329
|
const args = node.arguments;
|
|
5479
6330
|
const last = args.at(-1);
|
|
5480
6331
|
const source = context.sourceCode;
|
|
5481
|
-
if (args.length <
|
|
6332
|
+
if (args.length < min || !last || args.some((arg) => (arg.type === "ArrowFunctionExpression" || arg.type === "FunctionExpression") && arg.loc.start.line !== arg.loc.end.line)) {
|
|
5482
6333
|
return;
|
|
5483
6334
|
}
|
|
5484
6335
|
const text = source.text;
|
|
@@ -5500,8 +6351,8 @@ var layoutCallArguments = defineRule46({
|
|
|
5500
6351
|
const argumentIndent = `${baseIndent}${indentUnit}`;
|
|
5501
6352
|
context.report({
|
|
5502
6353
|
node,
|
|
5503
|
-
messageId
|
|
5504
|
-
data: { min: String(
|
|
6354
|
+
messageId,
|
|
6355
|
+
data: { min: String(min) },
|
|
5505
6356
|
fix: unsafe ? undefined : (fixer) => gaps.map((gap, index) => {
|
|
5506
6357
|
if (index === 0) {
|
|
5507
6358
|
return fixer.replaceTextRange(gap, `${newline}${argumentIndent}`);
|
|
@@ -5519,11 +6370,23 @@ var layoutCallArguments = defineRule46({
|
|
|
5519
6370
|
const last = args.at(-1);
|
|
5520
6371
|
const open = source.getTokenAfter(node.typeArguments ?? node.callee);
|
|
5521
6372
|
const close = source.getLastToken(node);
|
|
5522
|
-
if (!first || !last || !open || !close ||
|
|
6373
|
+
if (!first || !last || !open || !close || source.getCommentsInside(node).length > 0) {
|
|
6374
|
+
return;
|
|
6375
|
+
}
|
|
6376
|
+
if (args.every(astIsOneLine)) {
|
|
6377
|
+
if (first.loc.start.line > open.loc.end.line && close.loc.start.line === last.loc.end.line) {
|
|
6378
|
+
const newline2 = sourceNewline(text);
|
|
6379
|
+
const base2 = sourceLineIndent(text, open.range[0]);
|
|
6380
|
+
context.report({
|
|
6381
|
+
node,
|
|
6382
|
+
messageId: "hug",
|
|
6383
|
+
fix: (fixer) => sourceRespace(fixer, text, sourceItemGaps(open.range[1], args, close.range[0]), (_, end) => `${newline2}${end ? base2 : `${base2}${indentUnit}`}`)
|
|
6384
|
+
});
|
|
6385
|
+
}
|
|
5523
6386
|
return;
|
|
5524
6387
|
}
|
|
5525
6388
|
const leading = args.slice(0, -1);
|
|
5526
|
-
const canHug = !
|
|
6389
|
+
const canHug = !isPipe(node) && leading.every((arg) => astIsOneLine(arg) && astCallDepth(arg) <= 1) && layoutCallArgumentsHuggable(last);
|
|
5527
6390
|
const gaps = sourceItemGaps(open.range[1], args, close.range[0]);
|
|
5528
6391
|
if (first.loc.start.line > open.loc.end.line) {
|
|
5529
6392
|
const head = [...leading.map((arg) => source.getText(arg)), text.slice(last.range[0], sourceLineEnd(text, last.range[0]))];
|
|
@@ -5549,7 +6412,7 @@ var layoutCallArguments = defineRule46({
|
|
|
5549
6412
|
fix: (fixer) => sourceRespace(fixer, text, gaps, (_, end) => `${newline}${end ? base : inner}`)
|
|
5550
6413
|
});
|
|
5551
6414
|
};
|
|
5552
|
-
const
|
|
6415
|
+
const join4 = (node, items, open, close) => {
|
|
5553
6416
|
const source = context.sourceCode;
|
|
5554
6417
|
const text = source.text;
|
|
5555
6418
|
const first = items.at(0);
|
|
@@ -5569,14 +6432,16 @@ var layoutCallArguments = defineRule46({
|
|
|
5569
6432
|
};
|
|
5570
6433
|
const call = (node) => {
|
|
5571
6434
|
const source = context.sourceCode;
|
|
5572
|
-
if (options.
|
|
5573
|
-
split(node);
|
|
6435
|
+
if (options.pipe && isPipe(node)) {
|
|
6436
|
+
split(node, options.pipe.min, "pipe");
|
|
6437
|
+
} else if (options.split !== false) {
|
|
6438
|
+
split(node, minArguments, "multiline");
|
|
5574
6439
|
}
|
|
5575
6440
|
if (options.hug) {
|
|
5576
6441
|
hug(node, options.hug.max);
|
|
5577
6442
|
}
|
|
5578
|
-
if (options.join && !(options.join.keep ?? []).includes(source.getText(node.callee)) && !(
|
|
5579
|
-
|
|
6443
|
+
if (options.join && !(options.join.keep ?? []).includes(source.getText(node.callee)) && !(isPipe(node) && (node.arguments.length > 1 || options.pipe)) && astCallDepth(node) <= (options.join.nesting ?? Number.POSITIVE_INFINITY)) {
|
|
6444
|
+
join4(node, node.arguments, source.getTokenAfter(node.typeArguments ?? node.callee), source.getLastToken(node));
|
|
5580
6445
|
}
|
|
5581
6446
|
};
|
|
5582
6447
|
const parameters = (node) => {
|
|
@@ -5586,13 +6451,14 @@ var layoutCallArguments = defineRule46({
|
|
|
5586
6451
|
if (!options.join || !first || !last) {
|
|
5587
6452
|
return;
|
|
5588
6453
|
}
|
|
5589
|
-
|
|
6454
|
+
join4(node, node.params, source.getTokenBefore(first, { filter: (token) => token.value === "(" }), source.getTokenAfter(last, { filter: (token) => token.value === ")" }));
|
|
5590
6455
|
};
|
|
5591
6456
|
return {
|
|
5592
6457
|
before() {
|
|
5593
6458
|
options = optionsFirst(context, {});
|
|
5594
6459
|
minArguments = options.min ?? 3;
|
|
5595
6460
|
indentUnit = sourceIndentUnit(options.indent);
|
|
6461
|
+
pipeMethods = new Set(options.pipe?.methods ?? ["pipe"]);
|
|
5596
6462
|
},
|
|
5597
6463
|
CallExpression: call,
|
|
5598
6464
|
NewExpression: call,
|
|
@@ -5606,12 +6472,12 @@ var layoutCallArguments = defineRule46({
|
|
|
5606
6472
|
});
|
|
5607
6473
|
|
|
5608
6474
|
// src/rules/layout/layout-call-array.ts
|
|
5609
|
-
import { defineRule as
|
|
6475
|
+
import { defineRule as defineRule54 } from "@oxlint/plugins";
|
|
5610
6476
|
var layoutCallArrayDefaultCallees = ["Promise.all", "Promise.allSettled"];
|
|
5611
6477
|
function layoutCallArrayRoot(node) {
|
|
5612
6478
|
return node.type === "MemberExpression" ? layoutCallArrayRoot(node.object) : node;
|
|
5613
6479
|
}
|
|
5614
|
-
var layoutCallArray =
|
|
6480
|
+
var layoutCallArray = defineRule54({
|
|
5615
6481
|
meta: {
|
|
5616
6482
|
type: "layout",
|
|
5617
6483
|
fixable: "whitespace",
|
|
@@ -5727,22 +6593,29 @@ var layoutCallArray = defineRule47({
|
|
|
5727
6593
|
});
|
|
5728
6594
|
|
|
5729
6595
|
// src/rules/layout/layout-call-depth.ts
|
|
5730
|
-
import { defineRule as
|
|
5731
|
-
function layoutCallDepthOuter(node) {
|
|
6596
|
+
import { defineRule as defineRule55 } from "@oxlint/plugins";
|
|
6597
|
+
function layoutCallDepthOuter(node, callbacks) {
|
|
5732
6598
|
const parent = node.parent;
|
|
5733
|
-
if (!parent || parent.type === "ArrowFunctionExpression" || parent.type === "FunctionExpression"
|
|
6599
|
+
if (!parent || !callbacks && (parent.type === "ArrowFunctionExpression" || parent.type === "FunctionExpression")) {
|
|
5734
6600
|
return;
|
|
5735
6601
|
}
|
|
5736
|
-
|
|
6602
|
+
if (parent.type.endsWith("Statement")) {
|
|
6603
|
+
let owner = parent;
|
|
6604
|
+
while (owner?.type.endsWith("Statement")) {
|
|
6605
|
+
owner = owner.parent;
|
|
6606
|
+
}
|
|
6607
|
+
return callbacks && owner && astIsInlineFunction(owner) ? layoutCallDepthOuter(owner, callbacks) : undefined;
|
|
6608
|
+
}
|
|
6609
|
+
return astIsCall(parent) && parent.arguments.some((argument) => argument === node) ? parent : layoutCallDepthOuter(parent, callbacks);
|
|
5737
6610
|
}
|
|
5738
|
-
var layoutCallDepth =
|
|
6611
|
+
var layoutCallDepth = defineRule55({
|
|
5739
6612
|
meta: {
|
|
5740
6613
|
type: "layout",
|
|
5741
6614
|
schema: [
|
|
5742
6615
|
{
|
|
5743
6616
|
type: "object",
|
|
5744
6617
|
additionalProperties: false,
|
|
5745
|
-
properties: { max: { type: "integer", minimum: 1, default: 2 } }
|
|
6618
|
+
properties: { max: { type: "integer", minimum: 1, default: 2 }, callbacks: { type: "boolean", default: false } }
|
|
5746
6619
|
}
|
|
5747
6620
|
],
|
|
5748
6621
|
messages: {
|
|
@@ -5751,10 +6624,11 @@ var layoutCallDepth = defineRule48({
|
|
|
5751
6624
|
},
|
|
5752
6625
|
createOnce(context) {
|
|
5753
6626
|
let max = 2;
|
|
6627
|
+
let callbacks = false;
|
|
5754
6628
|
const check = (node) => {
|
|
5755
|
-
const depth = astCallDepth(node);
|
|
5756
|
-
const outer = layoutCallDepthOuter(node);
|
|
5757
|
-
if (!astIsOneLine(node) || depth <= max || outer !== undefined && astIsOneLine(outer) && astCallDepth(outer) > max) {
|
|
6629
|
+
const depth = astCallDepth(node, callbacks);
|
|
6630
|
+
const outer = layoutCallDepthOuter(node, callbacks);
|
|
6631
|
+
if (!astIsOneLine(node) || depth <= max || outer !== undefined && astIsOneLine(outer) && astCallDepth(outer, callbacks) > max) {
|
|
5758
6632
|
return;
|
|
5759
6633
|
}
|
|
5760
6634
|
context.report({
|
|
@@ -5765,7 +6639,9 @@ var layoutCallDepth = defineRule48({
|
|
|
5765
6639
|
};
|
|
5766
6640
|
return {
|
|
5767
6641
|
before() {
|
|
5768
|
-
|
|
6642
|
+
const options = optionsFirst(context, {});
|
|
6643
|
+
max = options.max ?? 2;
|
|
6644
|
+
callbacks = options.callbacks ?? false;
|
|
5769
6645
|
},
|
|
5770
6646
|
CallExpression: check,
|
|
5771
6647
|
NewExpression: check
|
|
@@ -5774,8 +6650,8 @@ var layoutCallDepth = defineRule48({
|
|
|
5774
6650
|
});
|
|
5775
6651
|
|
|
5776
6652
|
// src/rules/layout/layout-chain.ts
|
|
5777
|
-
import { defineRule as
|
|
5778
|
-
var layoutChain =
|
|
6653
|
+
import { defineRule as defineRule56 } from "@oxlint/plugins";
|
|
6654
|
+
var layoutChain = defineRule56({
|
|
5779
6655
|
meta: {
|
|
5780
6656
|
type: "layout",
|
|
5781
6657
|
fixable: "whitespace",
|
|
@@ -5894,8 +6770,8 @@ var layoutChain = defineRule49({
|
|
|
5894
6770
|
});
|
|
5895
6771
|
|
|
5896
6772
|
// src/rules/layout/layout-items.ts
|
|
5897
|
-
import { defineRule as
|
|
5898
|
-
var layoutItems =
|
|
6773
|
+
import { defineRule as defineRule57 } from "@oxlint/plugins";
|
|
6774
|
+
var layoutItems = defineRule57({
|
|
5899
6775
|
meta: {
|
|
5900
6776
|
type: "layout",
|
|
5901
6777
|
fixable: "whitespace",
|
|
@@ -5950,8 +6826,8 @@ var layoutItems = defineRule50({
|
|
|
5950
6826
|
});
|
|
5951
6827
|
|
|
5952
6828
|
// src/rules/layout/layout-jsx-attributes.ts
|
|
5953
|
-
import { defineRule as
|
|
5954
|
-
var layoutJsxAttributes =
|
|
6829
|
+
import { defineRule as defineRule58 } from "@oxlint/plugins";
|
|
6830
|
+
var layoutJsxAttributes = defineRule58({
|
|
5955
6831
|
meta: {
|
|
5956
6832
|
type: "layout",
|
|
5957
6833
|
fixable: "whitespace",
|
|
@@ -6039,8 +6915,8 @@ var layoutJsxAttributes = defineRule51({
|
|
|
6039
6915
|
});
|
|
6040
6916
|
|
|
6041
6917
|
// src/rules/layout/layout-key-value.ts
|
|
6042
|
-
import { defineRule as
|
|
6043
|
-
var layoutKeyValue =
|
|
6918
|
+
import { defineRule as defineRule59 } from "@oxlint/plugins";
|
|
6919
|
+
var layoutKeyValue = defineRule59({
|
|
6044
6920
|
meta: {
|
|
6045
6921
|
type: "layout",
|
|
6046
6922
|
fixable: "whitespace",
|
|
@@ -6076,7 +6952,7 @@ var layoutKeyValue = defineRule52({
|
|
|
6076
6952
|
});
|
|
6077
6953
|
|
|
6078
6954
|
// src/rules/layout/layout-object.ts
|
|
6079
|
-
import { defineRule as
|
|
6955
|
+
import { defineRule as defineRule60 } from "@oxlint/plugins";
|
|
6080
6956
|
function layoutObjectIsWhitespace(text) {
|
|
6081
6957
|
return /^[\t\n\r\f ]*$/.test(text);
|
|
6082
6958
|
}
|
|
@@ -6084,7 +6960,7 @@ function layoutObjectIsFlat(property) {
|
|
|
6084
6960
|
const value = property.type === "SpreadElement" ? property.argument : property.value;
|
|
6085
6961
|
return astIsOneLine(property) && (property.type === "SpreadElement" || !property.method && property.kind === "init") && value.type !== "ObjectExpression" && value.type !== "ArrayExpression" && !astIsInlineFunction(value);
|
|
6086
6962
|
}
|
|
6087
|
-
var layoutObject =
|
|
6963
|
+
var layoutObject = defineRule60({
|
|
6088
6964
|
meta: {
|
|
6089
6965
|
type: "layout",
|
|
6090
6966
|
fixable: "whitespace",
|
|
@@ -6096,6 +6972,10 @@ var layoutObject = defineRule53({
|
|
|
6096
6972
|
min: { type: "integer", minimum: 1, default: 3 },
|
|
6097
6973
|
scope: { type: "string", enum: ["call-args", "all"], default: "call-args" },
|
|
6098
6974
|
indent: { ...sourceIndentSchema, default: 2 },
|
|
6975
|
+
params: { type: "boolean", default: false },
|
|
6976
|
+
nested: { type: "boolean", default: false },
|
|
6977
|
+
siblings: { type: "boolean", default: false },
|
|
6978
|
+
inner: { type: "integer", minimum: 1 },
|
|
6099
6979
|
join: {
|
|
6100
6980
|
type: "object",
|
|
6101
6981
|
additionalProperties: false,
|
|
@@ -6111,6 +6991,11 @@ var layoutObject = defineRule53({
|
|
|
6111
6991
|
messages: {
|
|
6112
6992
|
callArgs: "Object with {{min}}+ properties in a call should have each property on its own line.",
|
|
6113
6993
|
always: "Object with {{min}}+ properties should have each property on its own line.",
|
|
6994
|
+
nested: "An object holding an object or function should have each property on its own line.",
|
|
6995
|
+
nestedName: "An object holding an object or function should have each property on its own line. Name this call first: split here, it would break a chained call or a ternary.",
|
|
6996
|
+
siblings: "A sibling call in this list splits its object; split this one too.",
|
|
6997
|
+
inner: "An object with {{inner}}+ entries inside a split object should have each entry on its own line.",
|
|
6998
|
+
params: "Destructured parameter with {{min}}+ properties should have each property on its own line.",
|
|
6114
6999
|
join: "This object fits on one line, so keep it on one line."
|
|
6115
7000
|
}
|
|
6116
7001
|
},
|
|
@@ -6120,39 +7005,167 @@ var layoutObject = defineRule53({
|
|
|
6120
7005
|
let indentUnit = sourceIndentUnit();
|
|
6121
7006
|
let newline = `
|
|
6122
7007
|
`;
|
|
6123
|
-
let
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
const properties = node.properties;
|
|
6137
|
-
const split = properties.length >= minProperties;
|
|
6138
|
-
if (split ? !astIsOneLine(node) : !join3 || astIsOneLine(node)) {
|
|
6139
|
-
return;
|
|
7008
|
+
let join4;
|
|
7009
|
+
let params = false;
|
|
7010
|
+
let nested = false;
|
|
7011
|
+
let siblings = false;
|
|
7012
|
+
let inner;
|
|
7013
|
+
const splitCounts = new WeakMap;
|
|
7014
|
+
function holdsNested(node) {
|
|
7015
|
+
return nested && astIsOneLine(node) && node.properties.some((property) => property.type === "Property" && (property.value.type === "ObjectExpression" && property.value.properties.length > 0 || astIsInlineFunction(property.value))) && !insideOneLineObject(node.parent);
|
|
7016
|
+
}
|
|
7017
|
+
function insideOneLineObject(start) {
|
|
7018
|
+
for (let ancestor = start;ancestor && !ancestor.type.endsWith("Statement"); ancestor = ancestor.parent) {
|
|
7019
|
+
if (ancestor.type === "ObjectExpression" && astIsOneLine(ancestor)) {
|
|
7020
|
+
return true;
|
|
6140
7021
|
}
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
7022
|
+
}
|
|
7023
|
+
return false;
|
|
7024
|
+
}
|
|
7025
|
+
function breaksAround(node) {
|
|
7026
|
+
for (let child = node, parent = node.parent;parent; child = parent, parent = parent.parent) {
|
|
7027
|
+
if (parent.type === "ConditionalExpression" || parent.type === "MemberExpression" && parent.object === child && astIsCall(child)) {
|
|
7028
|
+
return true;
|
|
6145
7029
|
}
|
|
6146
|
-
if (
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
7030
|
+
if (astIsInlineFunction(parent) || parent.type.endsWith("Statement") || parent.type.endsWith("Declaration")) {
|
|
7031
|
+
return false;
|
|
7032
|
+
}
|
|
7033
|
+
}
|
|
7034
|
+
return false;
|
|
7035
|
+
}
|
|
7036
|
+
function keptByInner(node) {
|
|
7037
|
+
const parent = node.parent;
|
|
7038
|
+
return inner !== undefined && node.properties.length >= inner && parent.type === "Property" && parent.value === node && !astIsOneLine(parent.parent);
|
|
7039
|
+
}
|
|
7040
|
+
function splitSiblings(node) {
|
|
7041
|
+
const call = node.parent;
|
|
7042
|
+
const list = call.parent;
|
|
7043
|
+
if (!siblings || node.properties.length === 0 || !astIsCall(call) || list?.type !== "ArrayExpression") {
|
|
7044
|
+
return 0;
|
|
7045
|
+
}
|
|
7046
|
+
let counts = splitCounts.get(list);
|
|
7047
|
+
if (!counts) {
|
|
7048
|
+
counts = new Map;
|
|
7049
|
+
for (const element of list.elements) {
|
|
7050
|
+
if (!element || !astIsCall(element)) {
|
|
7051
|
+
continue;
|
|
6151
7052
|
}
|
|
6152
|
-
const
|
|
7053
|
+
for (const [index, argument] of element.arguments.entries()) {
|
|
7054
|
+
if (argument.type === "ObjectExpression" && !astIsOneLine(argument)) {
|
|
7055
|
+
const key = `${context.sourceCode.getText(element.callee)}#${index}`;
|
|
7056
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
7057
|
+
}
|
|
7058
|
+
}
|
|
7059
|
+
}
|
|
7060
|
+
splitCounts.set(list, counts);
|
|
7061
|
+
}
|
|
7062
|
+
return counts.get(`${context.sourceCode.getText(call.callee)}#${call.arguments.indexOf(node)}`) ?? 0;
|
|
7063
|
+
}
|
|
7064
|
+
function joinBlocked(node, call) {
|
|
7065
|
+
const callee = call && astIsCall(call) ? astDottedName(call.callee) : undefined;
|
|
7066
|
+
const kept = callee !== undefined && (join4?.keep ?? []).some((name) => callee === name || callee.endsWith(`.${name}`));
|
|
7067
|
+
return kept || keptByInner(node) || splitSiblings(node) > 1 || node.properties.length === 0 || !node.properties.every(layoutObjectIsFlat) || context.sourceCode.getCommentsInside(node).length > 0;
|
|
7068
|
+
}
|
|
7069
|
+
function reportSplit(node, properties, open, close, messageId) {
|
|
7070
|
+
const source = context.sourceCode;
|
|
7071
|
+
const text = source.text;
|
|
7072
|
+
const baseIndent = sourceLineIndent(text, open);
|
|
7073
|
+
const propertyIndent = `${baseIndent}${indentUnit}`;
|
|
7074
|
+
context.report({
|
|
7075
|
+
node,
|
|
7076
|
+
messageId,
|
|
7077
|
+
data: { min: String(minProperties), inner: String(inner ?? "") },
|
|
7078
|
+
fix: (fixer) => {
|
|
7079
|
+
if (messageId === "nestedName" || properties.length === 0 || source.getCommentsInside(node).length > 0 || !layoutObjectIsWhitespace(text.slice(open + 1, properties[0].range[0]))) {
|
|
7080
|
+
return null;
|
|
7081
|
+
}
|
|
7082
|
+
const gaps = [[open + 1, properties[0].range[0]]];
|
|
7083
|
+
for (const [index, property] of properties.entries()) {
|
|
7084
|
+
const next = properties.at(index + 1);
|
|
7085
|
+
const after = property.range[1];
|
|
7086
|
+
const limit = next ? next.range[0] : close;
|
|
7087
|
+
const between = text.slice(after, limit);
|
|
7088
|
+
const comma = between.indexOf(",");
|
|
7089
|
+
if (comma === -1) {
|
|
7090
|
+
if (next || !layoutObjectIsWhitespace(between)) {
|
|
7091
|
+
return null;
|
|
7092
|
+
}
|
|
7093
|
+
gaps.push([after, close]);
|
|
7094
|
+
continue;
|
|
7095
|
+
}
|
|
7096
|
+
if (!layoutObjectIsWhitespace(between.slice(0, comma)) || !layoutObjectIsWhitespace(between.slice(comma + 1))) {
|
|
7097
|
+
return null;
|
|
7098
|
+
}
|
|
7099
|
+
gaps.push([after + comma + 1, limit]);
|
|
7100
|
+
}
|
|
7101
|
+
return gaps.map((gap, index) => fixer.replaceTextRange(gap, index === gaps.length - 1 ? `${newline}${baseIndent}` : `${newline}${propertyIndent}`));
|
|
7102
|
+
}
|
|
7103
|
+
});
|
|
7104
|
+
}
|
|
7105
|
+
function checkParams(node) {
|
|
7106
|
+
if (!params) {
|
|
7107
|
+
return;
|
|
7108
|
+
}
|
|
7109
|
+
for (const param of node.params) {
|
|
7110
|
+
const pattern = param.type === "AssignmentPattern" ? param.left : param;
|
|
7111
|
+
if (pattern.type !== "ObjectPattern" || pattern.properties.length < minProperties) {
|
|
7112
|
+
continue;
|
|
7113
|
+
}
|
|
7114
|
+
const text = context.sourceCode.text;
|
|
7115
|
+
const close = text.lastIndexOf("}", (pattern.typeAnnotation?.range[0] ?? pattern.range[1]) - 1);
|
|
7116
|
+
if (!text.slice(pattern.range[0], close).includes(`
|
|
7117
|
+
`)) {
|
|
7118
|
+
reportSplit(pattern, pattern.properties, pattern.range[0], close, "params");
|
|
7119
|
+
}
|
|
7120
|
+
}
|
|
7121
|
+
}
|
|
7122
|
+
return {
|
|
7123
|
+
before() {
|
|
7124
|
+
const options = optionsFirst(context, {});
|
|
7125
|
+
minProperties = options.min ?? 3;
|
|
7126
|
+
scope = options.scope ?? "call-args";
|
|
7127
|
+
indentUnit = sourceIndentUnit(options.indent);
|
|
7128
|
+
newline = sourceNewline(context.sourceCode.text);
|
|
7129
|
+
join4 = options.join;
|
|
7130
|
+
params = options.params ?? false;
|
|
7131
|
+
nested = options.nested ?? false;
|
|
7132
|
+
siblings = options.siblings ?? false;
|
|
7133
|
+
inner = options.inner;
|
|
7134
|
+
},
|
|
7135
|
+
FunctionDeclaration: checkParams,
|
|
7136
|
+
FunctionExpression: checkParams,
|
|
7137
|
+
ArrowFunctionExpression: checkParams,
|
|
7138
|
+
ObjectExpression(node) {
|
|
7139
|
+
const source = context.sourceCode;
|
|
7140
|
+
const text = source.text;
|
|
7141
|
+
const properties = node.properties;
|
|
7142
|
+
const parent = node.parent;
|
|
7143
|
+
const full = properties.length >= minProperties;
|
|
7144
|
+
const holds = !full && holdsNested(node);
|
|
7145
|
+
if (!full && !holds && astIsOneLine(node) && splitSiblings(node) > 0) {
|
|
7146
|
+
reportSplit(node, properties, node.range[0], node.range[1] - 1, "siblings");
|
|
7147
|
+
return;
|
|
7148
|
+
}
|
|
7149
|
+
if (astIsOneLine(node) && keptByInner(node)) {
|
|
7150
|
+
reportSplit(node, properties, node.range[0], node.range[1] - 1, "inner");
|
|
7151
|
+
return;
|
|
7152
|
+
}
|
|
7153
|
+
const split = full || holds;
|
|
7154
|
+
if (split ? !astIsOneLine(node) : !join4 || astIsOneLine(node)) {
|
|
7155
|
+
return;
|
|
7156
|
+
}
|
|
7157
|
+
const call = (parent.type === "CallExpression" || parent.type === "NewExpression") && parent.arguments.includes(node) ? parent : undefined;
|
|
7158
|
+
if (scope === "call-args" && !call) {
|
|
7159
|
+
return;
|
|
7160
|
+
}
|
|
7161
|
+
if (!split) {
|
|
7162
|
+
if (joinBlocked(node, call)) {
|
|
7163
|
+
return;
|
|
7164
|
+
}
|
|
7165
|
+
const gaps = sourceItemGaps(node.range[0] + 1, properties, node.range[1] - 1);
|
|
6153
7166
|
const comma = text.slice(...gaps.at(-1) ?? [0, 0]).includes(",") ? "," : "";
|
|
6154
7167
|
const joined = ` ${properties.map((property) => source.getText(property)).join(", ")}${comma} `;
|
|
6155
|
-
if (sourceJoinedWidth(text, node.range[0] + 1, joined, node.range[1] - 1) > (
|
|
7168
|
+
if (sourceJoinedWidth(text, node.range[0] + 1, joined, node.range[1] - 1) > (join4?.max ?? 0)) {
|
|
6156
7169
|
return;
|
|
6157
7170
|
}
|
|
6158
7171
|
context.report({
|
|
@@ -6162,46 +7175,16 @@ var layoutObject = defineRule53({
|
|
|
6162
7175
|
});
|
|
6163
7176
|
return;
|
|
6164
7177
|
}
|
|
6165
|
-
const
|
|
6166
|
-
const
|
|
6167
|
-
|
|
6168
|
-
context.report({
|
|
6169
|
-
node,
|
|
6170
|
-
messageId: scope === "all" ? "always" : "callArgs",
|
|
6171
|
-
data: { min: String(minProperties) },
|
|
6172
|
-
fix: (fixer) => {
|
|
6173
|
-
if (source.getCommentsInside(node).length > 0 || !layoutObjectIsWhitespace(text.slice(node.range[0] + 1, properties[0].range[0]))) {
|
|
6174
|
-
return null;
|
|
6175
|
-
}
|
|
6176
|
-
const gaps = [[node.range[0] + 1, properties[0].range[0]]];
|
|
6177
|
-
for (const [index, property] of properties.entries()) {
|
|
6178
|
-
const next = properties.at(index + 1);
|
|
6179
|
-
const after = property.range[1];
|
|
6180
|
-
const limit = next ? next.range[0] : close;
|
|
6181
|
-
const between = text.slice(after, limit);
|
|
6182
|
-
const comma = between.indexOf(",");
|
|
6183
|
-
if (comma === -1) {
|
|
6184
|
-
if (next || !layoutObjectIsWhitespace(between)) {
|
|
6185
|
-
return null;
|
|
6186
|
-
}
|
|
6187
|
-
gaps.push([after, close]);
|
|
6188
|
-
continue;
|
|
6189
|
-
}
|
|
6190
|
-
if (!layoutObjectIsWhitespace(between.slice(0, comma)) || !layoutObjectIsWhitespace(between.slice(comma + 1))) {
|
|
6191
|
-
return null;
|
|
6192
|
-
}
|
|
6193
|
-
gaps.push([after + comma + 1, limit]);
|
|
6194
|
-
}
|
|
6195
|
-
return gaps.map((gap, index) => fixer.replaceTextRange(gap, index === gaps.length - 1 ? `${newline}${baseIndent}` : `${newline}${propertyIndent}`));
|
|
6196
|
-
}
|
|
6197
|
-
});
|
|
7178
|
+
const messageId = scope === "all" ? "always" : "callArgs";
|
|
7179
|
+
const nestedId = breaksAround(node) ? "nestedName" : "nested";
|
|
7180
|
+
reportSplit(node, properties, node.range[0], node.range[1] - 1, holds ? nestedId : messageId);
|
|
6198
7181
|
}
|
|
6199
7182
|
};
|
|
6200
7183
|
}
|
|
6201
7184
|
});
|
|
6202
7185
|
|
|
6203
7186
|
// src/rules/layout/layout-padding.ts
|
|
6204
|
-
import { defineRule as
|
|
7187
|
+
import { defineRule as defineRule61 } from "@oxlint/plugins";
|
|
6205
7188
|
var layoutPaddingControl = new Set([
|
|
6206
7189
|
"IfStatement",
|
|
6207
7190
|
"ForStatement",
|
|
@@ -6230,7 +7213,7 @@ function layoutPaddingKind(node) {
|
|
|
6230
7213
|
function layoutPaddingIsMultilineVariable(node, kind) {
|
|
6231
7214
|
return kind === "variable" && node.loc.start.line !== node.loc.end.line;
|
|
6232
7215
|
}
|
|
6233
|
-
var layoutPadding =
|
|
7216
|
+
var layoutPadding = defineRule61({
|
|
6234
7217
|
meta: {
|
|
6235
7218
|
type: "layout",
|
|
6236
7219
|
fixable: "whitespace",
|
|
@@ -6319,23 +7302,33 @@ var layoutPadding = defineRule54({
|
|
|
6319
7302
|
});
|
|
6320
7303
|
|
|
6321
7304
|
// src/rules/layout/layout-pipe.ts
|
|
6322
|
-
import { defineRule as
|
|
6323
|
-
var layoutPipe =
|
|
7305
|
+
import { defineRule as defineRule62 } from "@oxlint/plugins";
|
|
7306
|
+
var layoutPipe = defineRule62({
|
|
6324
7307
|
meta: {
|
|
6325
7308
|
type: "layout",
|
|
7309
|
+
schema: [{ type: "object", additionalProperties: false, properties: { methods: { type: "array", items: { type: "string" } } } }],
|
|
6326
7310
|
messages: {
|
|
6327
|
-
after: "No ).
|
|
7311
|
+
after: "No ).{{method}}( after a call that spans lines. Name the value and pipe the name, or pass the call to the step data-first.",
|
|
6328
7312
|
step: "A pipe step may span lines only through a callback, object or array. Name this call first, then pass the name."
|
|
6329
7313
|
}
|
|
6330
7314
|
},
|
|
6331
7315
|
createOnce(context) {
|
|
7316
|
+
let methods = new Set;
|
|
6332
7317
|
return {
|
|
7318
|
+
before() {
|
|
7319
|
+
methods = new Set(optionsFirst(context, {}).methods ?? ["pipe"]);
|
|
7320
|
+
},
|
|
6333
7321
|
CallExpression(node) {
|
|
6334
7322
|
const callee = node.callee;
|
|
6335
|
-
|
|
6336
|
-
|
|
7323
|
+
const piped = callee.type === "MemberExpression" && methods.has(astMemberName(callee) ?? "");
|
|
7324
|
+
if (piped && astIsCall(callee.object) && !astIsOneLine(callee.object) && callee.property.loc.start.line === callee.object.loc.end.line) {
|
|
7325
|
+
context.report({
|
|
7326
|
+
node: callee.property,
|
|
7327
|
+
messageId: "after",
|
|
7328
|
+
data: { method: astMemberName(callee) ?? "pipe" }
|
|
7329
|
+
});
|
|
6337
7330
|
}
|
|
6338
|
-
if (!
|
|
7331
|
+
if (!piped) {
|
|
6339
7332
|
return;
|
|
6340
7333
|
}
|
|
6341
7334
|
for (const step of node.arguments) {
|
|
@@ -6351,7 +7344,7 @@ var layoutPipe = defineRule55({
|
|
|
6351
7344
|
});
|
|
6352
7345
|
|
|
6353
7346
|
// src/rules/layout/layout-ternary.ts
|
|
6354
|
-
import { defineRule as
|
|
7347
|
+
import { defineRule as defineRule63 } from "@oxlint/plugins";
|
|
6355
7348
|
function layoutTernaryLines(node) {
|
|
6356
7349
|
return node.loc.end.line - node.loc.start.line + 1;
|
|
6357
7350
|
}
|
|
@@ -6359,7 +7352,7 @@ function layoutTernaryBreak(between, operator, newline) {
|
|
|
6359
7352
|
const at = between.indexOf(operator);
|
|
6360
7353
|
return `${between.slice(0, at).trimEnd()}${newline}${operator} ${between.slice(at + 1).trimStart()}`;
|
|
6361
7354
|
}
|
|
6362
|
-
var layoutTernary =
|
|
7355
|
+
var layoutTernary = defineRule63({
|
|
6363
7356
|
meta: {
|
|
6364
7357
|
type: "layout",
|
|
6365
7358
|
fixable: "whitespace",
|
|
@@ -6474,14 +7467,75 @@ var layoutTernary = defineRule56({
|
|
|
6474
7467
|
}
|
|
6475
7468
|
});
|
|
6476
7469
|
|
|
7470
|
+
// src/rules/layout/layout-type-arguments.ts
|
|
7471
|
+
import { defineRule as defineRule64 } from "@oxlint/plugins";
|
|
7472
|
+
var layoutTypeArguments = defineRule64({
|
|
7473
|
+
meta: {
|
|
7474
|
+
type: "layout",
|
|
7475
|
+
fixable: "code",
|
|
7476
|
+
schema: [
|
|
7477
|
+
{
|
|
7478
|
+
type: "object",
|
|
7479
|
+
additionalProperties: false,
|
|
7480
|
+
properties: {
|
|
7481
|
+
min: { type: "integer", minimum: 2, default: 3 },
|
|
7482
|
+
indent: { ...sourceIndentSchema, default: 2 }
|
|
7483
|
+
}
|
|
7484
|
+
}
|
|
7485
|
+
],
|
|
7486
|
+
messages: {
|
|
7487
|
+
union: "A union of {{min}}+ types in type arguments puts each type on its own line."
|
|
7488
|
+
}
|
|
7489
|
+
},
|
|
7490
|
+
createOnce(context) {
|
|
7491
|
+
let minMembers = 3;
|
|
7492
|
+
let indentUnit = sourceIndentUnit();
|
|
7493
|
+
return {
|
|
7494
|
+
before() {
|
|
7495
|
+
const options = optionsFirst(context, {});
|
|
7496
|
+
minMembers = options.min ?? 3;
|
|
7497
|
+
indentUnit = sourceIndentUnit(options.indent);
|
|
7498
|
+
},
|
|
7499
|
+
TSTypeParameterInstantiation(node) {
|
|
7500
|
+
const unions = node.params.flatMap((param) => param.type === "TSUnionType" && param.types.length >= minMembers ? [param] : []);
|
|
7501
|
+
if (unions.length === 0 || !astIsOneLine(node)) {
|
|
7502
|
+
return;
|
|
7503
|
+
}
|
|
7504
|
+
const source = context.sourceCode;
|
|
7505
|
+
const text = source.text;
|
|
7506
|
+
const items = node.params.flatMap((param) => param.type === "TSUnionType" && unions.includes(param) ? param.types : [param]);
|
|
7507
|
+
const gaps = sourceItemGaps(node.range[0] + 1, items, node.range[1] - 1);
|
|
7508
|
+
const firsts = new Set(unions.map((union) => union.types[0]));
|
|
7509
|
+
const matches = gaps.map((gap) => /^\s*(,?)\s*(\|?)\s*$/.exec(text.slice(...gap)));
|
|
7510
|
+
const newline = sourceNewline(text);
|
|
7511
|
+
const base = sourceLineIndent(text, node.range[0]);
|
|
7512
|
+
const inner = `${base}${indentUnit}`;
|
|
7513
|
+
const unsafe = text[node.range[0]] !== "<" || text[node.range[1] - 1] !== ">" || source.getCommentsInside(node).length > 0 || matches.some((match) => !match);
|
|
7514
|
+
context.report({
|
|
7515
|
+
node,
|
|
7516
|
+
messageId: "union",
|
|
7517
|
+
data: { min: String(minMembers) },
|
|
7518
|
+
fix: unsafe ? undefined : (fixer) => gaps.map((gap, index) => {
|
|
7519
|
+
const comma = matches[index]?.[1] ?? "";
|
|
7520
|
+
const next = items.at(index);
|
|
7521
|
+
const bar = matches[index]?.[2] || next && firsts.has(next) ? "| " : "";
|
|
7522
|
+
return fixer.replaceTextRange(gap, next ? `${comma}${newline}${inner}${bar}` : `${comma}${newline}${base}`);
|
|
7523
|
+
})
|
|
7524
|
+
});
|
|
7525
|
+
}
|
|
7526
|
+
};
|
|
7527
|
+
}
|
|
7528
|
+
});
|
|
7529
|
+
|
|
6477
7530
|
// src/rules/naming/naming-declarations.ts
|
|
6478
|
-
import { defineRule as
|
|
7531
|
+
import { defineRule as defineRule65 } from "@oxlint/plugins";
|
|
6479
7532
|
var namingDeclarationsKinds = ["function", "const", "let", "var", "class", "type", "interface", "enum"];
|
|
6480
|
-
var namingDeclarations =
|
|
7533
|
+
var namingDeclarations = defineRule65({
|
|
6481
7534
|
meta: {
|
|
6482
7535
|
type: "problem",
|
|
6483
|
-
schema:
|
|
6484
|
-
|
|
7536
|
+
schema: {
|
|
7537
|
+
type: "array",
|
|
7538
|
+
items: {
|
|
6485
7539
|
type: "object",
|
|
6486
7540
|
additionalProperties: false,
|
|
6487
7541
|
properties: {
|
|
@@ -6492,56 +7546,61 @@ var namingDeclarations = defineRule57({
|
|
|
6492
7546
|
allow: optionsPatternSchema
|
|
6493
7547
|
}
|
|
6494
7548
|
}
|
|
6495
|
-
|
|
7549
|
+
},
|
|
6496
7550
|
messages: {
|
|
6497
7551
|
prefix: "'{{name}}' must start with file prefix '{{prefix}}'.",
|
|
6498
7552
|
pattern: "'{{name}}' must match {{pattern}}."
|
|
6499
7553
|
}
|
|
6500
7554
|
},
|
|
6501
7555
|
createOnce(context) {
|
|
6502
|
-
let
|
|
6503
|
-
let kinds = null;
|
|
6504
|
-
let allowed = [];
|
|
6505
|
-
let expected = [];
|
|
6506
|
-
let prefixes = [];
|
|
6507
|
-
let label = "";
|
|
7556
|
+
let checks = [];
|
|
6508
7557
|
function check(node) {
|
|
6509
7558
|
const declarator = node.type === "VariableDeclarator";
|
|
6510
7559
|
const kind = declarationsKindOf(node, declarator ? [node.parent] : []);
|
|
6511
7560
|
const name = declarationsIdName(node);
|
|
6512
7561
|
const loop = declarator ? node.parent.parent : null;
|
|
6513
7562
|
const loopHeader = loop?.type === "ForStatement" ? loop.init === node.parent : (loop?.type === "ForInStatement" || loop?.type === "ForOfStatement") && loop.left === node.parent;
|
|
6514
|
-
if (!kind || !name || loopHeader
|
|
7563
|
+
if (!kind || !name || loopHeader) {
|
|
6515
7564
|
return;
|
|
6516
7565
|
}
|
|
6517
|
-
|
|
6518
|
-
if (optionsPatternsTest(
|
|
6519
|
-
|
|
7566
|
+
for (const { kinds, allowed, expected, prefixes, label, normalize } of checks) {
|
|
7567
|
+
if (kinds && !kinds.has(kind) || optionsPatternsTest(allowed, name)) {
|
|
7568
|
+
continue;
|
|
7569
|
+
}
|
|
7570
|
+
if (expected.length > 0) {
|
|
7571
|
+
if (!optionsPatternsTest(expected, name)) {
|
|
7572
|
+
context.report({
|
|
7573
|
+
node,
|
|
7574
|
+
messageId: "pattern",
|
|
7575
|
+
data: { name, pattern: label }
|
|
7576
|
+
});
|
|
7577
|
+
}
|
|
7578
|
+
continue;
|
|
7579
|
+
}
|
|
7580
|
+
if (!namingHasFilePrefix(name, prefixes, normalize)) {
|
|
7581
|
+
context.report({
|
|
7582
|
+
node,
|
|
7583
|
+
messageId: "prefix",
|
|
7584
|
+
data: { name, prefix: prefixes.join(" or ") }
|
|
7585
|
+
});
|
|
6520
7586
|
}
|
|
6521
|
-
context.report({
|
|
6522
|
-
node,
|
|
6523
|
-
messageId: "pattern",
|
|
6524
|
-
data: { name, pattern: label }
|
|
6525
|
-
});
|
|
6526
|
-
return;
|
|
6527
|
-
}
|
|
6528
|
-
if (namingHasFilePrefix(name, prefixes, options.normalize)) {
|
|
6529
|
-
return;
|
|
6530
7587
|
}
|
|
6531
|
-
context.report({
|
|
6532
|
-
node,
|
|
6533
|
-
messageId: "prefix",
|
|
6534
|
-
data: { name, prefix: prefixes.join(" or ") }
|
|
6535
|
-
});
|
|
6536
7588
|
}
|
|
6537
7589
|
return {
|
|
6538
7590
|
before() {
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
7591
|
+
const given = optionsAll(context);
|
|
7592
|
+
const captures = namingCaptures(context.filename) ?? {};
|
|
7593
|
+
checks = (given.length > 0 ? given : [{}]).map((options) => {
|
|
7594
|
+
const sources = options.pattern === undefined ? [] : [options.pattern].flat().map((source) => namingExpand(source, captures, true, optionsEscape));
|
|
7595
|
+
return {
|
|
7596
|
+
kinds: options.kinds?.length ? new Set(options.kinds) : null,
|
|
7597
|
+
allowed: optionsPatterns(options.allow),
|
|
7598
|
+
expected: optionsPatterns(sources, options.flags),
|
|
7599
|
+
prefixes: namingFilePrefixesFor(context.filename, options),
|
|
7600
|
+
label: optionsPatternLabel(sources),
|
|
7601
|
+
normalize: options.normalize
|
|
7602
|
+
};
|
|
7603
|
+
});
|
|
6545
7604
|
},
|
|
6546
7605
|
FunctionDeclaration: check,
|
|
6547
7606
|
ClassDeclaration: check,
|
|
@@ -6554,33 +7613,14 @@ var namingDeclarations = defineRule57({
|
|
|
6554
7613
|
});
|
|
6555
7614
|
|
|
6556
7615
|
// src/rules/naming/naming-exports.ts
|
|
6557
|
-
import { defineRule as
|
|
7616
|
+
import { defineRule as defineRule66 } from "@oxlint/plugins";
|
|
6558
7617
|
var namingExportsScopeProperties = {
|
|
6559
7618
|
scope: { type: "string", enum: ["exports", "functions", "declarations"] },
|
|
6560
7619
|
types: { type: "boolean" },
|
|
6561
7620
|
services: { type: "boolean" },
|
|
6562
7621
|
allow: optionsPatternSchema
|
|
6563
7622
|
};
|
|
6564
|
-
|
|
6565
|
-
let pattern = "";
|
|
6566
|
-
let index = 0;
|
|
6567
|
-
while (index < template.length) {
|
|
6568
|
-
const char = template[index];
|
|
6569
|
-
if (char === "{") {
|
|
6570
|
-
const end = template.indexOf("}", index);
|
|
6571
|
-
if (end === -1) {
|
|
6572
|
-
return null;
|
|
6573
|
-
}
|
|
6574
|
-
pattern += `(?<${template.slice(index + 1, end)}>${placeholder})`;
|
|
6575
|
-
index = end + 1;
|
|
6576
|
-
continue;
|
|
6577
|
-
}
|
|
6578
|
-
pattern += /[.+^${}()|[\]\\]/.test(char) ? `\\${char}` : char;
|
|
6579
|
-
index += 1;
|
|
6580
|
-
}
|
|
6581
|
-
return new RegExp(`^${pattern}$`).exec(basename3)?.groups ?? null;
|
|
6582
|
-
}
|
|
6583
|
-
var namingExports = defineRule58({
|
|
7623
|
+
var namingExports = defineRule66({
|
|
6584
7624
|
meta: {
|
|
6585
7625
|
type: "problem",
|
|
6586
7626
|
schema: {
|
|
@@ -6610,7 +7650,7 @@ var namingExports = defineRule58({
|
|
|
6610
7650
|
mode: { type: "string", enum: ["template"] },
|
|
6611
7651
|
...namingExportsScopeProperties,
|
|
6612
7652
|
file: { type: "string" },
|
|
6613
|
-
export:
|
|
7653
|
+
export: optionsPatternSchema,
|
|
6614
7654
|
match: { type: "string", enum: ["all", "some"] },
|
|
6615
7655
|
placeholder: { type: "string" },
|
|
6616
7656
|
camelCase: { type: "boolean" }
|
|
@@ -6637,23 +7677,15 @@ var namingExports = defineRule58({
|
|
|
6637
7677
|
const allowed = optionsPatterns(options.allow);
|
|
6638
7678
|
if (options.mode === "template") {
|
|
6639
7679
|
const file = namingBasename(context.filename);
|
|
6640
|
-
const
|
|
6641
|
-
if (!
|
|
7680
|
+
const captures2 = namingCaptures(context.filename, { file: options.file, placeholder: options.placeholder });
|
|
7681
|
+
if (!captures2) {
|
|
6642
7682
|
continue;
|
|
6643
7683
|
}
|
|
6644
|
-
const
|
|
6645
|
-
|
|
6646
|
-
if (!group) {
|
|
6647
|
-
return placeholder;
|
|
6648
|
-
}
|
|
6649
|
-
if (/^[A-Z]/.test(key)) {
|
|
6650
|
-
return namingPascalCase(group);
|
|
6651
|
-
}
|
|
6652
|
-
return options.camelCase ? namingCamelCase(group) : group;
|
|
6653
|
-
});
|
|
7684
|
+
const names2 = [options.export].flat().map((template) => namingExpand(template, captures2, options.camelCase ?? false));
|
|
7685
|
+
const expected2 = names2.join(" or ");
|
|
6654
7686
|
const functions = (scope === "exports" ? exportsFunctions(program, true) : declarationsFunctions(program)).filter(({ name }) => !optionsPatternsTest(allowed, name));
|
|
6655
7687
|
if (options.match === "some") {
|
|
6656
|
-
if (functions.some((item) => item.name
|
|
7688
|
+
if (functions.some((item) => names2.includes(item.name))) {
|
|
6657
7689
|
continue;
|
|
6658
7690
|
}
|
|
6659
7691
|
const first = functions.at(0);
|
|
@@ -6665,7 +7697,7 @@ var namingExports = defineRule58({
|
|
|
6665
7697
|
continue;
|
|
6666
7698
|
}
|
|
6667
7699
|
for (const item of functions) {
|
|
6668
|
-
if (item.name
|
|
7700
|
+
if (!names2.includes(item.name)) {
|
|
6669
7701
|
context.report({
|
|
6670
7702
|
node: item.node,
|
|
6671
7703
|
messageId: "mismatch",
|
|
@@ -6687,7 +7719,9 @@ var namingExports = defineRule58({
|
|
|
6687
7719
|
names.push(...declarationsPublicServiceMethods(program));
|
|
6688
7720
|
}
|
|
6689
7721
|
const prefixes = options.mode === "pattern" ? [] : namingFilePrefixesFor(context.filename, options);
|
|
6690
|
-
const
|
|
7722
|
+
const captures = namingCaptures(context.filename) ?? {};
|
|
7723
|
+
const sources = options.mode === "pattern" ? [options.pattern].flat().map((source) => namingExpand(source, captures, true, optionsEscape)) : [];
|
|
7724
|
+
const expected = optionsPatternsWithEmpty(sources, options.mode === "pattern" ? options.flags : undefined);
|
|
6691
7725
|
for (const item of declarationsUnique(names)) {
|
|
6692
7726
|
if (optionsPatternsTest(allowed, item.name)) {
|
|
6693
7727
|
continue;
|
|
@@ -6697,7 +7731,7 @@ var namingExports = defineRule58({
|
|
|
6697
7731
|
context.report({
|
|
6698
7732
|
node: item.node,
|
|
6699
7733
|
messageId: "pattern",
|
|
6700
|
-
data: { name: item.name, pattern: optionsPatternLabel(
|
|
7734
|
+
data: { name: item.name, pattern: optionsPatternLabel(sources) }
|
|
6701
7735
|
});
|
|
6702
7736
|
}
|
|
6703
7737
|
continue;
|
|
@@ -6716,71 +7750,586 @@ var namingExports = defineRule58({
|
|
|
6716
7750
|
}
|
|
6717
7751
|
});
|
|
6718
7752
|
|
|
6719
|
-
// src/rules/naming/naming-file.ts
|
|
6720
|
-
import { defineRule as
|
|
6721
|
-
import { readdirSync as readdirSync2 } from "node:fs";
|
|
6722
|
-
import { dirname as
|
|
6723
|
-
var namingFile =
|
|
7753
|
+
// src/rules/naming/naming-file.ts
|
|
7754
|
+
import { defineRule as defineRule67 } from "@oxlint/plugins";
|
|
7755
|
+
import { readdirSync as readdirSync2 } from "node:fs";
|
|
7756
|
+
import { dirname as dirname5 } from "node:path";
|
|
7757
|
+
var namingFile = defineRule67({
|
|
7758
|
+
meta: {
|
|
7759
|
+
type: "problem",
|
|
7760
|
+
schema: {
|
|
7761
|
+
type: "array",
|
|
7762
|
+
items: {
|
|
7763
|
+
type: "object",
|
|
7764
|
+
additionalProperties: false,
|
|
7765
|
+
properties: {
|
|
7766
|
+
pattern: optionsPatternSchema,
|
|
7767
|
+
message: { type: "string" },
|
|
7768
|
+
flags: { type: "string" },
|
|
7769
|
+
unique: { type: "string", minLength: 1 },
|
|
7770
|
+
template: optionsPatternSchema,
|
|
7771
|
+
placeholder: { type: "string" }
|
|
7772
|
+
},
|
|
7773
|
+
required: ["message"],
|
|
7774
|
+
anyOf: [{ required: ["pattern"] }, { required: ["template"] }]
|
|
7775
|
+
}
|
|
7776
|
+
},
|
|
7777
|
+
messages: {
|
|
7778
|
+
mismatch: "{{message}}",
|
|
7779
|
+
duplicate: "'{{key}}' is also used by {{others}}; this part of the file name must be unique in its folder."
|
|
7780
|
+
}
|
|
7781
|
+
},
|
|
7782
|
+
createOnce(context) {
|
|
7783
|
+
return {
|
|
7784
|
+
Program(program) {
|
|
7785
|
+
const basename3 = namingBasename(context.filename);
|
|
7786
|
+
let siblings;
|
|
7787
|
+
for (const { pattern, message, flags, unique, template, placeholder } of optionsAll(context)) {
|
|
7788
|
+
const patterns = optionsPatternsWithEmpty(pattern, flags);
|
|
7789
|
+
const path = context.filename.replaceAll("\\", "/").split("/");
|
|
7790
|
+
const templates = [template ?? []].flat();
|
|
7791
|
+
const fits = templates.some((item) => namingTemplatePattern(item, placeholder)?.test(path.slice(-item.split("/").length).join("/")));
|
|
7792
|
+
if (pattern !== undefined && !optionsPatternsTest(patterns, basename3) || templates.length > 0 && !fits) {
|
|
7793
|
+
context.report({
|
|
7794
|
+
node: program,
|
|
7795
|
+
messageId: "mismatch",
|
|
7796
|
+
data: { message }
|
|
7797
|
+
});
|
|
7798
|
+
}
|
|
7799
|
+
if (unique === undefined) {
|
|
7800
|
+
continue;
|
|
7801
|
+
}
|
|
7802
|
+
const matcher = new RegExp(unique);
|
|
7803
|
+
const keyOf = (name) => {
|
|
7804
|
+
const match = matcher.exec(name);
|
|
7805
|
+
return match ? match.at(1) ?? match[0] : undefined;
|
|
7806
|
+
};
|
|
7807
|
+
const key = keyOf(basename3);
|
|
7808
|
+
if (key === undefined) {
|
|
7809
|
+
continue;
|
|
7810
|
+
}
|
|
7811
|
+
if (!siblings) {
|
|
7812
|
+
try {
|
|
7813
|
+
siblings = readdirSync2(dirname5(context.filename), { withFileTypes: true }).flatMap((entry) => entry.isFile() ? [entry.name] : []);
|
|
7814
|
+
} catch {
|
|
7815
|
+
siblings = [];
|
|
7816
|
+
}
|
|
7817
|
+
}
|
|
7818
|
+
const others = siblings.filter((name) => name !== basename3 && optionsPatternsTest(patterns, name) && keyOf(name) === key);
|
|
7819
|
+
if (others.length > 0) {
|
|
7820
|
+
context.report({
|
|
7821
|
+
node: program,
|
|
7822
|
+
messageId: "duplicate",
|
|
7823
|
+
data: { key, others: others.join(", ") }
|
|
7824
|
+
});
|
|
7825
|
+
}
|
|
7826
|
+
}
|
|
7827
|
+
}
|
|
7828
|
+
};
|
|
7829
|
+
}
|
|
7830
|
+
});
|
|
7831
|
+
|
|
7832
|
+
// src/rules/naming/naming-folder.ts
|
|
7833
|
+
import { defineRule as defineRule68 } from "@oxlint/plugins";
|
|
7834
|
+
import { posix as posix2 } from "node:path";
|
|
7835
|
+
var namingFolder = defineRule68({
|
|
7836
|
+
meta: {
|
|
7837
|
+
type: "problem",
|
|
7838
|
+
schema: [
|
|
7839
|
+
{
|
|
7840
|
+
type: "object",
|
|
7841
|
+
additionalProperties: false,
|
|
7842
|
+
properties: {
|
|
7843
|
+
singularize: { type: "string", enum: ["none", "trailing-s"] },
|
|
7844
|
+
separators: { type: "array", items: { type: "string" } },
|
|
7845
|
+
after: { type: "string" },
|
|
7846
|
+
prefixes: { type: "array", items: { type: "string", minLength: 1 } }
|
|
7847
|
+
}
|
|
7848
|
+
}
|
|
7849
|
+
],
|
|
7850
|
+
messages: {
|
|
7851
|
+
prefix: "File '{{file}}' must start with '{{expected}}' to match its domain folder '{{folder}}'."
|
|
7852
|
+
}
|
|
7853
|
+
},
|
|
7854
|
+
createOnce(context) {
|
|
7855
|
+
return {
|
|
7856
|
+
Program(program) {
|
|
7857
|
+
const { singularize = "trailing-s", separators = ["-"], after, prefixes: stripPrefixes = [] } = optionsFirst(context, {});
|
|
7858
|
+
const all = posix2.dirname(context.filename.replaceAll("\\", "/")).split("/").filter((part) => part && part !== ".");
|
|
7859
|
+
const parent = [all.at(-1) ?? ""];
|
|
7860
|
+
let folders = after ? undefined : parent;
|
|
7861
|
+
const marker = (after ?? "").split("/").filter(Boolean);
|
|
7862
|
+
for (let index = all.length - marker.length;marker.length > 0 && index >= 0 && !folders; index -= 1) {
|
|
7863
|
+
if (marker.every((part, offset) => all[index + offset] === part)) {
|
|
7864
|
+
folders = all.slice(index + marker.length);
|
|
7865
|
+
}
|
|
7866
|
+
}
|
|
7867
|
+
if (!folders) {
|
|
7868
|
+
return;
|
|
7869
|
+
}
|
|
7870
|
+
const resolved = folders.length > 0 ? folders : parent;
|
|
7871
|
+
const last = resolved.at(-1) ?? "";
|
|
7872
|
+
const joiner = separators[0] ?? "-";
|
|
7873
|
+
const singular = singularize === "trailing-s" && last.endsWith("s") && last.length > 1;
|
|
7874
|
+
const prefixes = [
|
|
7875
|
+
resolved.join(joiner),
|
|
7876
|
+
...singular ? [[...resolved.slice(0, -1), last.slice(0, -1)].join(joiner)] : []
|
|
7877
|
+
].filter(Boolean);
|
|
7878
|
+
const file = namingBasename(context.filename);
|
|
7879
|
+
const rawStem = file.replace(/\.(tsx?|jsx?)$/, "");
|
|
7880
|
+
const strip = stripPrefixes.find((prefix) => rawStem.startsWith(prefix));
|
|
7881
|
+
const stem = strip ? rawStem.slice(strip.length) : rawStem;
|
|
7882
|
+
if (prefixes.some((prefix) => stem === prefix || separators.some((separator) => stem.startsWith(`${prefix}${separator}`)))) {
|
|
7883
|
+
return;
|
|
7884
|
+
}
|
|
7885
|
+
context.report({
|
|
7886
|
+
node: program,
|
|
7887
|
+
messageId: "prefix",
|
|
7888
|
+
data: {
|
|
7889
|
+
file,
|
|
7890
|
+
folder: resolved.join("/"),
|
|
7891
|
+
expected: prefixes.map((prefix) => `${stripPrefixes[0] ?? ""}${prefix}`).join(" or ")
|
|
7892
|
+
}
|
|
7893
|
+
});
|
|
7894
|
+
}
|
|
7895
|
+
};
|
|
7896
|
+
}
|
|
7897
|
+
});
|
|
7898
|
+
|
|
7899
|
+
// src/rules/naming/naming-imports.ts
|
|
7900
|
+
import { defineRule as defineRule69 } from "@oxlint/plugins";
|
|
7901
|
+
var namingImports = defineRule69({
|
|
7902
|
+
meta: {
|
|
7903
|
+
type: "problem",
|
|
7904
|
+
schema: [
|
|
7905
|
+
{
|
|
7906
|
+
type: "object",
|
|
7907
|
+
additionalProperties: false,
|
|
7908
|
+
properties: {
|
|
7909
|
+
imports: {
|
|
7910
|
+
type: "array",
|
|
7911
|
+
items: {
|
|
7912
|
+
type: "object",
|
|
7913
|
+
additionalProperties: false,
|
|
7914
|
+
properties: {
|
|
7915
|
+
source: { type: "string" },
|
|
7916
|
+
name: optionsPatternSchema,
|
|
7917
|
+
namespace: { type: "boolean" },
|
|
7918
|
+
placeholder: { type: "string" }
|
|
7919
|
+
},
|
|
7920
|
+
required: ["source", "name"]
|
|
7921
|
+
}
|
|
7922
|
+
}
|
|
7923
|
+
},
|
|
7924
|
+
required: ["imports"]
|
|
7925
|
+
}
|
|
7926
|
+
],
|
|
7927
|
+
messages: {
|
|
7928
|
+
name: "Import '{{source}}' as '{{expected}}', not '{{actual}}'.",
|
|
7929
|
+
namespace: "Import '{{source}}' as a namespace: import * as {{expected}}."
|
|
7930
|
+
}
|
|
7931
|
+
},
|
|
7932
|
+
createOnce(context) {
|
|
7933
|
+
let entries = [];
|
|
7934
|
+
return {
|
|
7935
|
+
before() {
|
|
7936
|
+
entries = optionsFirst(context, { imports: [] }).imports.flatMap((entry) => {
|
|
7937
|
+
const pattern = namingTemplatePattern(entry.source, entry.placeholder ?? "[^/]+");
|
|
7938
|
+
return pattern ? [{ pattern, names: [entry.name].flat(), namespace: entry.namespace !== false }] : [];
|
|
7939
|
+
});
|
|
7940
|
+
if (entries.length === 0) {
|
|
7941
|
+
return false;
|
|
7942
|
+
}
|
|
7943
|
+
},
|
|
7944
|
+
ImportDeclaration(node) {
|
|
7945
|
+
const source = node.source.value;
|
|
7946
|
+
for (const entry of entries) {
|
|
7947
|
+
const match = entry.pattern.exec(source);
|
|
7948
|
+
if (!match) {
|
|
7949
|
+
continue;
|
|
7950
|
+
}
|
|
7951
|
+
const names = entry.names.map((template) => namingExpand(template, { ...match.groups }, true));
|
|
7952
|
+
const expected = names.join(" or ");
|
|
7953
|
+
for (const specifier of node.specifiers) {
|
|
7954
|
+
if (specifier.type !== "ImportNamespaceSpecifier" && entry.namespace) {
|
|
7955
|
+
context.report({
|
|
7956
|
+
node: specifier,
|
|
7957
|
+
messageId: "namespace",
|
|
7958
|
+
data: { source, expected }
|
|
7959
|
+
});
|
|
7960
|
+
} else if (specifier.type !== "ImportSpecifier" && !names.includes(specifier.local.name)) {
|
|
7961
|
+
context.report({
|
|
7962
|
+
node: specifier,
|
|
7963
|
+
messageId: "name",
|
|
7964
|
+
data: { source, expected, actual: specifier.local.name }
|
|
7965
|
+
});
|
|
7966
|
+
}
|
|
7967
|
+
}
|
|
7968
|
+
return;
|
|
7969
|
+
}
|
|
7970
|
+
}
|
|
7971
|
+
};
|
|
7972
|
+
}
|
|
7973
|
+
});
|
|
7974
|
+
|
|
7975
|
+
// src/rules/naming/naming-keys.ts
|
|
7976
|
+
import { defineRule as defineRule70 } from "@oxlint/plugins";
|
|
7977
|
+
function namingKeysNames(value) {
|
|
7978
|
+
const dotted = astDottedName(value);
|
|
7979
|
+
return dotted === undefined ? [] : [...new Set([dotted.split(".").at(-1) ?? dotted, dotted.split(".")[0]])];
|
|
7980
|
+
}
|
|
7981
|
+
function namingKeysSingular(key, options) {
|
|
7982
|
+
return options.singularize === "trailing-s" && key.length > 1 && key.endsWith("s") ? [key, key.slice(0, -1)] : [key];
|
|
7983
|
+
}
|
|
7984
|
+
function namingKeysSources(options, captures, keys) {
|
|
7985
|
+
const last = keys.at(-1) ?? "";
|
|
7986
|
+
return [options.name ?? []].flat().flatMap((source) => namingKeysSingular(last, options).map((key) => {
|
|
7987
|
+
const values = { ...captures, path: keys.join("-"), key };
|
|
7988
|
+
return namingExpand(source, values, true, optionsEscape);
|
|
7989
|
+
}));
|
|
7990
|
+
}
|
|
7991
|
+
function namingKeysFunction(value) {
|
|
7992
|
+
if (astIsInlineFunction(value)) {
|
|
7993
|
+
return value;
|
|
7994
|
+
}
|
|
7995
|
+
return value.type === "CallExpression" ? value.arguments.find(astIsInlineFunction) ?? namingKeysFunction(value.callee) : undefined;
|
|
7996
|
+
}
|
|
7997
|
+
function namingKeysYielded(node) {
|
|
7998
|
+
let found;
|
|
7999
|
+
astSome(node, (candidate) => {
|
|
8000
|
+
const call = candidate.type === "YieldExpression" && candidate.delegate && candidate.argument ? astUnwrap(candidate.argument) : undefined;
|
|
8001
|
+
found ??= call?.type === "CallExpression" ? call : undefined;
|
|
8002
|
+
return found !== undefined;
|
|
8003
|
+
});
|
|
8004
|
+
return found;
|
|
8005
|
+
}
|
|
8006
|
+
function namingKeysAssigned(fn, returned) {
|
|
8007
|
+
let found;
|
|
8008
|
+
astSome(fn.body, (candidate) => {
|
|
8009
|
+
const local = candidate.type === "VariableDeclarator" && candidate.id.type === "Identifier" && candidate.init ? candidate.id.name : undefined;
|
|
8010
|
+
const used = local !== undefined && astSome(returned, (node) => node.type === "Identifier" && node.name === local);
|
|
8011
|
+
found ??= used && candidate.type === "VariableDeclarator" && candidate.init ? namingKeysYielded(candidate.init) : undefined;
|
|
8012
|
+
return found !== undefined;
|
|
8013
|
+
});
|
|
8014
|
+
return found;
|
|
8015
|
+
}
|
|
8016
|
+
function namingKeysCalled(fn) {
|
|
8017
|
+
const returned = astReturnExpressions(fn).at(-1);
|
|
8018
|
+
if (!returned) {
|
|
8019
|
+
return;
|
|
8020
|
+
}
|
|
8021
|
+
const plain = astUnwrap(returned);
|
|
8022
|
+
const direct = !fn.generator && plain.type === "CallExpression" ? plain : undefined;
|
|
8023
|
+
const call = namingKeysYielded(returned) ?? namingKeysAssigned(fn, returned) ?? direct;
|
|
8024
|
+
return call ? astDottedName(call.callee) : undefined;
|
|
8025
|
+
}
|
|
8026
|
+
var namingKeys = defineRule70({
|
|
8027
|
+
meta: {
|
|
8028
|
+
type: "problem",
|
|
8029
|
+
schema: [
|
|
8030
|
+
{
|
|
8031
|
+
type: "object",
|
|
8032
|
+
additionalProperties: false,
|
|
8033
|
+
properties: {
|
|
8034
|
+
callees: { type: "array", items: { type: "string" }, minItems: 1 },
|
|
8035
|
+
exports: optionsPatternSchema,
|
|
8036
|
+
name: optionsPatternSchema,
|
|
8037
|
+
keys: optionsPatternSchema,
|
|
8038
|
+
inline: { type: "boolean" },
|
|
8039
|
+
delegate: { type: "boolean" },
|
|
8040
|
+
allow: optionsPatternSchema,
|
|
8041
|
+
singularize: { type: "string", enum: ["none", "trailing-s"] }
|
|
8042
|
+
},
|
|
8043
|
+
anyOf: [{ required: ["callees"] }, { required: ["exports"] }]
|
|
8044
|
+
}
|
|
8045
|
+
],
|
|
8046
|
+
messages: {
|
|
8047
|
+
name: "'{{name}}' under key '{{path}}' must match {{pattern}}.",
|
|
8048
|
+
key: "Key '{{key}}' in '{{path}}' must match {{pattern}}.",
|
|
8049
|
+
inline: "Bind a named function under '{{path}}', not an inline one.",
|
|
8050
|
+
delegate: "'{{path}}' must return a call to '{{path}}' on its service, not '{{called}}'."
|
|
8051
|
+
}
|
|
8052
|
+
},
|
|
8053
|
+
createOnce(context) {
|
|
8054
|
+
let options = {};
|
|
8055
|
+
let captures = {};
|
|
8056
|
+
let callees = [];
|
|
8057
|
+
let exported = [];
|
|
8058
|
+
let allowed = [];
|
|
8059
|
+
let keyPatterns = [];
|
|
8060
|
+
function checkValue(value, keys) {
|
|
8061
|
+
const path = keys.join(".");
|
|
8062
|
+
const fn = namingKeysFunction(value);
|
|
8063
|
+
const names = namingKeysNames(value);
|
|
8064
|
+
const sources = namingKeysSources(options, captures, keys);
|
|
8065
|
+
const called = options.delegate && fn ? namingKeysCalled(fn) : undefined;
|
|
8066
|
+
if (fn && astIsInlineFunction(value) && options.inline === false) {
|
|
8067
|
+
context.report({
|
|
8068
|
+
node: value,
|
|
8069
|
+
messageId: "inline",
|
|
8070
|
+
data: { path }
|
|
8071
|
+
});
|
|
8072
|
+
}
|
|
8073
|
+
if (called !== undefined && called.split(".").slice(1).join(".") !== path) {
|
|
8074
|
+
context.report({
|
|
8075
|
+
node: value,
|
|
8076
|
+
messageId: "delegate",
|
|
8077
|
+
data: { path, called }
|
|
8078
|
+
});
|
|
8079
|
+
}
|
|
8080
|
+
if (sources.length > 0 && names.length > 0 && !names.some((name) => optionsPatternsTest(optionsPatterns(sources), name))) {
|
|
8081
|
+
context.report({
|
|
8082
|
+
node: value,
|
|
8083
|
+
messageId: "name",
|
|
8084
|
+
data: { name: names[0], path, pattern: optionsPatternLabel(sources) }
|
|
8085
|
+
});
|
|
8086
|
+
}
|
|
8087
|
+
}
|
|
8088
|
+
function check(object, path) {
|
|
8089
|
+
for (const property of object.properties) {
|
|
8090
|
+
const key = declarationsPropertyName(property);
|
|
8091
|
+
const keys = key === undefined ? path : [...path, key];
|
|
8092
|
+
if (property.type !== "Property" || key === undefined || optionsPatternsTest(allowed, keys.join("."))) {
|
|
8093
|
+
continue;
|
|
8094
|
+
}
|
|
8095
|
+
if (keyPatterns.length > 0 && !optionsPatternsTest(keyPatterns, key)) {
|
|
8096
|
+
context.report({
|
|
8097
|
+
node: property.key,
|
|
8098
|
+
messageId: "key",
|
|
8099
|
+
data: { key, path: keys.join("."), pattern: optionsPatternLabel(options.keys) }
|
|
8100
|
+
});
|
|
8101
|
+
}
|
|
8102
|
+
if (property.value.type === "ObjectExpression") {
|
|
8103
|
+
check(property.value, keys);
|
|
8104
|
+
} else {
|
|
8105
|
+
checkValue(property.value, keys);
|
|
8106
|
+
}
|
|
8107
|
+
}
|
|
8108
|
+
}
|
|
8109
|
+
return {
|
|
8110
|
+
before() {
|
|
8111
|
+
options = optionsFirst(context, {});
|
|
8112
|
+
captures = namingCaptures(context.filename) ?? {};
|
|
8113
|
+
callees = optionsWildcards(options.callees);
|
|
8114
|
+
exported = optionsPatterns(options.exports);
|
|
8115
|
+
allowed = optionsPatterns(options.allow);
|
|
8116
|
+
keyPatterns = optionsPatterns(options.keys);
|
|
8117
|
+
if (callees.length === 0 && exported.length === 0) {
|
|
8118
|
+
return false;
|
|
8119
|
+
}
|
|
8120
|
+
},
|
|
8121
|
+
CallExpression(node) {
|
|
8122
|
+
const first = node.arguments.at(0);
|
|
8123
|
+
if (first?.type === "ObjectExpression" && optionsPatternsTest(callees, astDottedName(node.callee) ?? "")) {
|
|
8124
|
+
check(first, []);
|
|
8125
|
+
}
|
|
8126
|
+
},
|
|
8127
|
+
VariableDeclarator(node) {
|
|
8128
|
+
const statement = node.parent.parent;
|
|
8129
|
+
const top = statement?.type === "ExportNamedDeclaration" && statement.parent.type === "Program";
|
|
8130
|
+
const value = node.init ? astUnwrap(node.init) : null;
|
|
8131
|
+
if (top && node.id.type === "Identifier" && value?.type === "ObjectExpression" && optionsPatternsTest(exported, node.id.name)) {
|
|
8132
|
+
check(value, []);
|
|
8133
|
+
}
|
|
8134
|
+
}
|
|
8135
|
+
};
|
|
8136
|
+
}
|
|
8137
|
+
});
|
|
8138
|
+
|
|
8139
|
+
// src/rules/naming/naming-labels.ts
|
|
8140
|
+
import { defineRule as defineRule71 } from "@oxlint/plugins";
|
|
8141
|
+
function namingLabelsRoot(node) {
|
|
8142
|
+
return node.type === "CallExpression" ? namingLabelsRoot(node.callee) : astDottedName(node);
|
|
8143
|
+
}
|
|
8144
|
+
function namingLabelsBinding(node, within) {
|
|
8145
|
+
const parent = node.parent;
|
|
8146
|
+
if (!parent) {
|
|
8147
|
+
return;
|
|
8148
|
+
}
|
|
8149
|
+
if (parent.type === "VariableDeclarator" && parent.init === node) {
|
|
8150
|
+
return parent.id.type === "Identifier" ? parent.id.name : undefined;
|
|
8151
|
+
}
|
|
8152
|
+
if ((parent.type === "ClassDeclaration" || parent.type === "ClassExpression") && parent.superClass === node) {
|
|
8153
|
+
return parent.id?.name;
|
|
8154
|
+
}
|
|
8155
|
+
if (parent.type === "CallExpression" && parent.callee === node || parent.type === "MemberExpression" && parent.object === node) {
|
|
8156
|
+
return namingLabelsBinding(parent, within);
|
|
8157
|
+
}
|
|
8158
|
+
const owner = parent.type === "CallExpression" && parent.callee.type === "MemberExpression" ? astDottedName(parent.callee) : undefined;
|
|
8159
|
+
return owner !== undefined && optionsPatternsTest(within, owner) ? owner.split(".").at(-2) : undefined;
|
|
8160
|
+
}
|
|
8161
|
+
function namingLabelsTarget(first, property) {
|
|
8162
|
+
if (property === undefined) {
|
|
8163
|
+
return first;
|
|
8164
|
+
}
|
|
8165
|
+
const held = first.type === "ObjectExpression" ? first.properties.find((item) => declarationsPropertyName(item) === property) : undefined;
|
|
8166
|
+
return held?.type === "Property" ? held.value : undefined;
|
|
8167
|
+
}
|
|
8168
|
+
function namingLabelsPlain(text, loose) {
|
|
8169
|
+
return loose ? text.replaceAll(/[._\-/\s]/g, "").toLowerCase() : text;
|
|
8170
|
+
}
|
|
8171
|
+
function namingLabelsExpected({ entry, drop }, captures, binding) {
|
|
8172
|
+
const name = drop.reduce((text, pattern) => text.replace(pattern, ""), binding);
|
|
8173
|
+
return [entry.name ?? []].flat().map((template) => namingExpand(template, { ...captures, name }, false));
|
|
8174
|
+
}
|
|
8175
|
+
var namingLabels = defineRule71({
|
|
8176
|
+
meta: {
|
|
8177
|
+
type: "problem",
|
|
8178
|
+
schema: [
|
|
8179
|
+
{
|
|
8180
|
+
type: "object",
|
|
8181
|
+
additionalProperties: false,
|
|
8182
|
+
properties: {
|
|
8183
|
+
labels: {
|
|
8184
|
+
type: "array",
|
|
8185
|
+
items: {
|
|
8186
|
+
type: "object",
|
|
8187
|
+
additionalProperties: false,
|
|
8188
|
+
properties: {
|
|
8189
|
+
callee: optionsPatternSchema,
|
|
8190
|
+
property: { type: "string" },
|
|
8191
|
+
name: optionsPatternSchema,
|
|
8192
|
+
pattern: optionsPatternSchema,
|
|
8193
|
+
drop: optionsPatternSchema,
|
|
8194
|
+
loose: { type: "boolean" },
|
|
8195
|
+
within: optionsPatternSchema
|
|
8196
|
+
},
|
|
8197
|
+
required: ["callee"],
|
|
8198
|
+
anyOf: [{ required: ["name"] }, { required: ["pattern"] }]
|
|
8199
|
+
}
|
|
8200
|
+
}
|
|
8201
|
+
},
|
|
8202
|
+
required: ["labels"]
|
|
8203
|
+
}
|
|
8204
|
+
],
|
|
8205
|
+
messages: {
|
|
8206
|
+
name: "'{{label}}' must be '{{expected}}', after the name '{{name}}'.",
|
|
8207
|
+
pattern: "'{{label}}' must match {{pattern}}."
|
|
8208
|
+
}
|
|
8209
|
+
},
|
|
8210
|
+
createOnce(context) {
|
|
8211
|
+
let entries = [];
|
|
8212
|
+
let captures = {};
|
|
8213
|
+
return {
|
|
8214
|
+
before() {
|
|
8215
|
+
const paths = namingCaptures(context.filename) ?? {};
|
|
8216
|
+
captures = paths;
|
|
8217
|
+
entries = optionsFirst(context, { labels: [] }).labels.map((entry) => ({
|
|
8218
|
+
callees: optionsWildcards(entry.callee),
|
|
8219
|
+
entry,
|
|
8220
|
+
drop: optionsPatterns(entry.drop),
|
|
8221
|
+
within: optionsWildcards(entry.within),
|
|
8222
|
+
patterns: [entry.pattern ?? []].flat().map((source) => namingExpand(source, paths, true, optionsEscape))
|
|
8223
|
+
}));
|
|
8224
|
+
if (entries.length === 0) {
|
|
8225
|
+
return false;
|
|
8226
|
+
}
|
|
8227
|
+
},
|
|
8228
|
+
CallExpression(node) {
|
|
8229
|
+
const root = namingLabelsRoot(node.callee);
|
|
8230
|
+
const first = node.arguments.at(0);
|
|
8231
|
+
if (root === undefined || !first) {
|
|
8232
|
+
return;
|
|
8233
|
+
}
|
|
8234
|
+
for (const compiled of entries) {
|
|
8235
|
+
const target = optionsPatternsTest(compiled.callees, root) ? namingLabelsTarget(first, compiled.entry.property) : undefined;
|
|
8236
|
+
const label = target ? astStaticString(target) : undefined;
|
|
8237
|
+
if (!target || label === undefined) {
|
|
8238
|
+
continue;
|
|
8239
|
+
}
|
|
8240
|
+
if (compiled.patterns.length > 0 && !optionsPatternsTest(optionsPatterns(compiled.patterns), label)) {
|
|
8241
|
+
context.report({
|
|
8242
|
+
node: target,
|
|
8243
|
+
messageId: "pattern",
|
|
8244
|
+
data: { label, pattern: optionsPatternLabel(compiled.patterns) }
|
|
8245
|
+
});
|
|
8246
|
+
}
|
|
8247
|
+
const binding = namingLabelsBinding(node, compiled.within);
|
|
8248
|
+
const expected = binding === undefined ? [] : namingLabelsExpected(compiled, captures, binding);
|
|
8249
|
+
const plain = expected.map((item) => namingLabelsPlain(item, compiled.entry.loose));
|
|
8250
|
+
if (binding !== undefined && expected.length > 0 && !plain.includes(namingLabelsPlain(label, compiled.entry.loose))) {
|
|
8251
|
+
context.report({
|
|
8252
|
+
node: target,
|
|
8253
|
+
messageId: "name",
|
|
8254
|
+
data: { label, expected: expected.join(" or "), name: binding }
|
|
8255
|
+
});
|
|
8256
|
+
}
|
|
8257
|
+
}
|
|
8258
|
+
}
|
|
8259
|
+
};
|
|
8260
|
+
}
|
|
8261
|
+
});
|
|
8262
|
+
|
|
8263
|
+
// src/rules/naming/naming-pairs.ts
|
|
8264
|
+
import { defineRule as defineRule72 } from "@oxlint/plugins";
|
|
8265
|
+
function namingPairsArguments(node, found) {
|
|
8266
|
+
if (node.type === "MemberExpression") {
|
|
8267
|
+
return namingPairsArguments(node.object, found);
|
|
8268
|
+
}
|
|
8269
|
+
if (node.type !== "CallExpression") {
|
|
8270
|
+
return found;
|
|
8271
|
+
}
|
|
8272
|
+
const method = node.callee.type === "MemberExpression" ? astMemberName(node.callee) : undefined;
|
|
8273
|
+
const first = node.arguments.at(0);
|
|
8274
|
+
if (method !== undefined && first && !found.has(method)) {
|
|
8275
|
+
found.set(method, first);
|
|
8276
|
+
}
|
|
8277
|
+
return namingPairsArguments(node.callee, found);
|
|
8278
|
+
}
|
|
8279
|
+
var namingPairs = defineRule72({
|
|
6724
8280
|
meta: {
|
|
6725
8281
|
type: "problem",
|
|
6726
|
-
schema:
|
|
6727
|
-
|
|
6728
|
-
items: {
|
|
8282
|
+
schema: [
|
|
8283
|
+
{
|
|
6729
8284
|
type: "object",
|
|
6730
8285
|
additionalProperties: false,
|
|
6731
8286
|
properties: {
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
8287
|
+
pairs: {
|
|
8288
|
+
type: "array",
|
|
8289
|
+
items: {
|
|
8290
|
+
type: "object",
|
|
8291
|
+
additionalProperties: false,
|
|
8292
|
+
properties: {
|
|
8293
|
+
methods: { type: "array", items: { type: "string" }, minItems: 2, maxItems: 2 },
|
|
8294
|
+
strip: optionsPatternSchema,
|
|
8295
|
+
allow: optionsPatternSchema
|
|
8296
|
+
},
|
|
8297
|
+
required: ["methods"]
|
|
8298
|
+
}
|
|
8299
|
+
}
|
|
6736
8300
|
},
|
|
6737
|
-
required: ["
|
|
8301
|
+
required: ["pairs"]
|
|
6738
8302
|
}
|
|
6739
|
-
|
|
8303
|
+
],
|
|
6740
8304
|
messages: {
|
|
6741
|
-
|
|
6742
|
-
duplicate: "'{{key}}' is also used by {{others}}; this part of the file name must be unique in its folder."
|
|
8305
|
+
pair: "'{{second}}' in .{{secondMethod}}() must share its name with '{{first}}' in .{{firstMethod}}()."
|
|
6743
8306
|
}
|
|
6744
8307
|
},
|
|
6745
8308
|
createOnce(context) {
|
|
6746
8309
|
return {
|
|
6747
|
-
|
|
6748
|
-
const
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
const keyOf = (name) => {
|
|
6764
|
-
const match = matcher.exec(name);
|
|
6765
|
-
return match ? match.at(1) ?? match[0] : undefined;
|
|
6766
|
-
};
|
|
6767
|
-
const key = keyOf(basename3);
|
|
6768
|
-
if (key === undefined) {
|
|
8310
|
+
CallExpression(node) {
|
|
8311
|
+
const parent = node.parent;
|
|
8312
|
+
const chained = parent.type === "MemberExpression" && parent.object === node || parent.type === "CallExpression" && parent.callee === node;
|
|
8313
|
+
const { pairs } = optionsFirst(context, { pairs: [] });
|
|
8314
|
+
if (chained || pairs.length === 0) {
|
|
8315
|
+
return;
|
|
8316
|
+
}
|
|
8317
|
+
const found = namingPairsArguments(node, new Map);
|
|
8318
|
+
for (const { methods: [firstMethod, secondMethod], strip, allow } of pairs) {
|
|
8319
|
+
const firstNode = found.get(firstMethod);
|
|
8320
|
+
const secondNode = found.get(secondMethod);
|
|
8321
|
+
const first = firstNode ? astDottedName(firstNode)?.split(".").at(-1) : undefined;
|
|
8322
|
+
const second = secondNode ? astDottedName(secondNode)?.split(".").at(-1) : undefined;
|
|
8323
|
+
const skipped = optionsPatterns(allow);
|
|
8324
|
+
const stem = (name) => optionsPatterns(strip).reduce((text, pattern) => text.replace(pattern, ""), name);
|
|
8325
|
+
if (!secondNode || first === undefined || second === undefined || optionsPatternsTest(skipped, first) || optionsPatternsTest(skipped, second)) {
|
|
6769
8326
|
continue;
|
|
6770
8327
|
}
|
|
6771
|
-
if (
|
|
6772
|
-
try {
|
|
6773
|
-
siblings = readdirSync2(dirname4(context.filename), { withFileTypes: true }).flatMap((entry) => entry.isFile() ? [entry.name] : []);
|
|
6774
|
-
} catch {
|
|
6775
|
-
siblings = [];
|
|
6776
|
-
}
|
|
6777
|
-
}
|
|
6778
|
-
const others = siblings.filter((name) => name !== basename3 && optionsPatternsTest(patterns, name) && keyOf(name) === key);
|
|
6779
|
-
if (others.length > 0) {
|
|
8328
|
+
if (stem(first) !== stem(second)) {
|
|
6780
8329
|
context.report({
|
|
6781
|
-
node:
|
|
6782
|
-
messageId: "
|
|
6783
|
-
data: {
|
|
8330
|
+
node: secondNode,
|
|
8331
|
+
messageId: "pair",
|
|
8332
|
+
data: { first, second, firstMethod, secondMethod }
|
|
6784
8333
|
});
|
|
6785
8334
|
}
|
|
6786
8335
|
}
|
|
@@ -6789,113 +8338,175 @@ var namingFile = defineRule59({
|
|
|
6789
8338
|
}
|
|
6790
8339
|
});
|
|
6791
8340
|
|
|
6792
|
-
// src/rules/naming/naming-
|
|
6793
|
-
import { defineRule as
|
|
6794
|
-
|
|
6795
|
-
|
|
8341
|
+
// src/rules/naming/naming-types.ts
|
|
8342
|
+
import { defineRule as defineRule73 } from "@oxlint/plugins";
|
|
8343
|
+
var namingTypes = defineRule73({
|
|
8344
|
+
meta: {
|
|
8345
|
+
type: "problem",
|
|
8346
|
+
schema: [{ type: "object", additionalProperties: false, properties: { pattern: optionsPatternSchema, message: { type: "string" } } }],
|
|
8347
|
+
messages: { forbidden: "{{message}}" }
|
|
8348
|
+
},
|
|
8349
|
+
createOnce(context) {
|
|
8350
|
+
let patterns = [];
|
|
8351
|
+
let message = "";
|
|
8352
|
+
return {
|
|
8353
|
+
before() {
|
|
8354
|
+
const options = optionsFirst(context, {});
|
|
8355
|
+
patterns = optionsPatterns(options.pattern ?? "^[A-Z][a-zA-Z0-9]*$");
|
|
8356
|
+
message = options.message ?? "Use PascalCase for interfaces.";
|
|
8357
|
+
},
|
|
8358
|
+
TSInterfaceDeclaration(node) {
|
|
8359
|
+
if (!optionsPatternsTest(patterns, node.id.name)) {
|
|
8360
|
+
context.report({
|
|
8361
|
+
node,
|
|
8362
|
+
messageId: "forbidden",
|
|
8363
|
+
data: { message }
|
|
8364
|
+
});
|
|
8365
|
+
}
|
|
8366
|
+
}
|
|
8367
|
+
};
|
|
8368
|
+
}
|
|
8369
|
+
});
|
|
8370
|
+
|
|
8371
|
+
// src/rules/react/react-dynamic-classes.ts
|
|
8372
|
+
import { defineRule as defineRule74 } from "@oxlint/plugins";
|
|
8373
|
+
var reactDynamicClasses = defineRule74({
|
|
6796
8374
|
meta: {
|
|
6797
8375
|
type: "problem",
|
|
6798
8376
|
schema: [
|
|
6799
8377
|
{
|
|
6800
8378
|
type: "object",
|
|
6801
8379
|
additionalProperties: false,
|
|
6802
|
-
properties: {
|
|
6803
|
-
singularize: { type: "string", enum: ["none", "trailing-s"] },
|
|
6804
|
-
separators: { type: "array", items: { type: "string" } },
|
|
6805
|
-
after: { type: "string" },
|
|
6806
|
-
prefixes: { type: "array", items: { type: "string", minLength: 1 } }
|
|
6807
|
-
}
|
|
8380
|
+
properties: { attributes: { type: "array", items: { type: "string" } }, message: { type: "string" } }
|
|
6808
8381
|
}
|
|
6809
8382
|
],
|
|
6810
|
-
messages: {
|
|
6811
|
-
prefix: "File '{{file}}' must start with '{{expected}}' to match its domain folder '{{folder}}'."
|
|
6812
|
-
}
|
|
8383
|
+
messages: { forbidden: "{{message}}" }
|
|
6813
8384
|
},
|
|
6814
8385
|
createOnce(context) {
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
}
|
|
6826
|
-
}
|
|
6827
|
-
if (!folders) {
|
|
8386
|
+
let attributes = new Set;
|
|
8387
|
+
let message = "";
|
|
8388
|
+
const check = (node) => {
|
|
8389
|
+
for (let parent = node.parent;parent; parent = parent.parent) {
|
|
8390
|
+
if (parent.type === "JSXAttribute" && parent.name.type === "JSXIdentifier" && attributes.has(parent.name.name)) {
|
|
8391
|
+
context.report({
|
|
8392
|
+
node,
|
|
8393
|
+
messageId: "forbidden",
|
|
8394
|
+
data: { message }
|
|
8395
|
+
});
|
|
6828
8396
|
return;
|
|
6829
8397
|
}
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
const
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
8398
|
+
}
|
|
8399
|
+
};
|
|
8400
|
+
return {
|
|
8401
|
+
before() {
|
|
8402
|
+
const options = optionsFirst(context, {});
|
|
8403
|
+
attributes = new Set(options.attributes ?? ["className"]);
|
|
8404
|
+
message = options.message ?? "Write complete class names instead of constructing them dynamically.";
|
|
8405
|
+
},
|
|
8406
|
+
"JSXAttribute TemplateLiteral[expressions.length>0]": check,
|
|
8407
|
+
'JSXAttribute BinaryExpression[operator="+"]': check
|
|
8408
|
+
};
|
|
8409
|
+
}
|
|
8410
|
+
});
|
|
8411
|
+
|
|
8412
|
+
// src/rules/react/react-empty-effect.ts
|
|
8413
|
+
import { defineRule as defineRule75 } from "@oxlint/plugins";
|
|
8414
|
+
var reactEmptyEffect = defineRule75({
|
|
8415
|
+
meta: {
|
|
8416
|
+
type: "problem",
|
|
8417
|
+
schema: [
|
|
8418
|
+
{
|
|
8419
|
+
type: "object",
|
|
8420
|
+
additionalProperties: false,
|
|
8421
|
+
properties: { hooks: { type: "array", items: { type: "string" } }, message: { type: "string" } }
|
|
8422
|
+
}
|
|
8423
|
+
],
|
|
8424
|
+
messages: { forbidden: "{{message}}" }
|
|
8425
|
+
},
|
|
8426
|
+
createOnce(context) {
|
|
8427
|
+
let hooks = new Set;
|
|
8428
|
+
let message = "";
|
|
8429
|
+
return {
|
|
8430
|
+
before() {
|
|
8431
|
+
const options = optionsFirst(context, {});
|
|
8432
|
+
hooks = new Set(options.hooks ?? ["useEffect", "useLayoutEffect", "React.useEffect", "React.useLayoutEffect"]);
|
|
8433
|
+
message = options.message ?? "Remove the empty effect.";
|
|
8434
|
+
},
|
|
8435
|
+
CallExpression(node) {
|
|
8436
|
+
if (!hooks.has(astDottedName(node.callee) ?? "")) {
|
|
6843
8437
|
return;
|
|
6844
8438
|
}
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
8439
|
+
for (const argument of node.arguments) {
|
|
8440
|
+
if (astIsInlineFunction(argument) && argument.body?.type === "BlockStatement" && argument.body.body.length === 0) {
|
|
8441
|
+
context.report({
|
|
8442
|
+
node: argument.body,
|
|
8443
|
+
messageId: "forbidden",
|
|
8444
|
+
data: { message }
|
|
8445
|
+
});
|
|
6852
8446
|
}
|
|
6853
|
-
}
|
|
8447
|
+
}
|
|
6854
8448
|
}
|
|
6855
8449
|
};
|
|
6856
8450
|
}
|
|
6857
8451
|
});
|
|
6858
8452
|
|
|
6859
|
-
// src/rules/naming/naming-types.ts
|
|
6860
|
-
var namingTypes = astSelectorRule("TSInterfaceDeclaration:not([id.name=/^[A-Z][a-zA-Z0-9]*$/])", "Use PascalCase for interfaces.");
|
|
6861
|
-
|
|
6862
|
-
// src/rules/react/react-dynamic-classes.ts
|
|
6863
|
-
var reactDynamicClasses = astSelectorRule(':matches(JSXAttribute[name.name="className"] TemplateLiteral[expressions.length>0], JSXAttribute[name.name="className"] BinaryExpression[operator="+"])', "Write complete class names instead of constructing them dynamically.");
|
|
6864
|
-
|
|
6865
|
-
// src/rules/react/react-empty-effect.ts
|
|
6866
|
-
var reactEmptyEffect = astSelectorRule(':matches(CallExpression[callee.name=/^use(Layout)?Effect$/], CallExpression[callee.object.name="React"][callee.property.name=/^use(Layout)?Effect$/]) > :matches(ArrowFunctionExpression, FunctionExpression) > BlockStatement[body.length=0]', "Remove the empty effect.");
|
|
6867
|
-
|
|
6868
8453
|
// src/rules/react/react-memo.ts
|
|
6869
|
-
import { defineRule as
|
|
6870
|
-
var
|
|
6871
|
-
var reactMemo = defineRule61({
|
|
8454
|
+
import { defineRule as defineRule76 } from "@oxlint/plugins";
|
|
8455
|
+
var reactMemo = defineRule76({
|
|
6872
8456
|
meta: {
|
|
6873
8457
|
type: "problem",
|
|
6874
|
-
schema: [
|
|
8458
|
+
schema: [
|
|
8459
|
+
{
|
|
8460
|
+
type: "object",
|
|
8461
|
+
additionalProperties: false,
|
|
8462
|
+
properties: {
|
|
8463
|
+
names: { type: "array", items: { type: "string" } },
|
|
8464
|
+
sources: { type: "array", items: { type: "string" } },
|
|
8465
|
+
message: { type: "string" }
|
|
8466
|
+
}
|
|
8467
|
+
}
|
|
8468
|
+
],
|
|
6875
8469
|
messages: {
|
|
6876
|
-
memo: "
|
|
8470
|
+
memo: "{{message}}"
|
|
6877
8471
|
}
|
|
6878
8472
|
},
|
|
6879
8473
|
createOnce(context) {
|
|
8474
|
+
let names = [];
|
|
8475
|
+
let sources = [];
|
|
8476
|
+
let message = "";
|
|
6880
8477
|
return {
|
|
8478
|
+
before() {
|
|
8479
|
+
const options = optionsFirst(context, {});
|
|
8480
|
+
names = options.names ?? ["memo", "useMemo", "useCallback"];
|
|
8481
|
+
sources = options.sources ?? ["react"];
|
|
8482
|
+
message = options.message ?? "Use the React Compiler rather than manual memoization.";
|
|
8483
|
+
},
|
|
6881
8484
|
ImportDeclaration(node) {
|
|
6882
|
-
if (node.source.value
|
|
8485
|
+
if (!sources.includes(node.source.value)) {
|
|
6883
8486
|
return;
|
|
6884
8487
|
}
|
|
6885
8488
|
for (const specifier of node.specifiers) {
|
|
6886
|
-
if (specifier.type === "ImportSpecifier" &&
|
|
6887
|
-
context.report({
|
|
8489
|
+
if (specifier.type === "ImportSpecifier" && names.includes(astModuleName(specifier.imported))) {
|
|
8490
|
+
context.report({
|
|
8491
|
+
node: specifier,
|
|
8492
|
+
messageId: "memo",
|
|
8493
|
+
data: { message }
|
|
8494
|
+
});
|
|
6888
8495
|
}
|
|
6889
8496
|
}
|
|
6890
8497
|
},
|
|
6891
8498
|
MemberExpression(node) {
|
|
6892
|
-
if (node.object.type !== "Identifier" || !
|
|
8499
|
+
if (node.object.type !== "Identifier" || !names.includes(boundariesMemberName(node) ?? "")) {
|
|
6893
8500
|
return;
|
|
6894
8501
|
}
|
|
6895
8502
|
const variable = boundariesDeclaredVariable(context.sourceCode.getScope(node), node.object.name);
|
|
6896
|
-
const isReact = variable ? variable.defs.some((def) => def.type === "ImportBinding" && def.node.type !== "ImportSpecifier" && def.parent?.type === "ImportDeclaration" && def.parent.source.value
|
|
8503
|
+
const isReact = variable ? variable.defs.some((def) => def.type === "ImportBinding" && (def.node.type !== "ImportSpecifier" || astModuleName(def.node.imported) === "default") && def.parent?.type === "ImportDeclaration" && sources.includes(def.parent.source.value)) : node.object.name === "React" && sources.includes("react");
|
|
6897
8504
|
if (isReact) {
|
|
6898
|
-
context.report({
|
|
8505
|
+
context.report({
|
|
8506
|
+
node,
|
|
8507
|
+
messageId: "memo",
|
|
8508
|
+
data: { message }
|
|
8509
|
+
});
|
|
6899
8510
|
}
|
|
6900
8511
|
}
|
|
6901
8512
|
};
|
|
@@ -6903,7 +8514,7 @@ var reactMemo = defineRule61({
|
|
|
6903
8514
|
});
|
|
6904
8515
|
|
|
6905
8516
|
// src/rules/react/react-props.ts
|
|
6906
|
-
import { defineRule as
|
|
8517
|
+
import { defineRule as defineRule77 } from "@oxlint/plugins";
|
|
6907
8518
|
function reactPropsTarget(node) {
|
|
6908
8519
|
if (node?.type === "FunctionDeclaration" || node?.type === "FunctionExpression" || node?.type === "ArrowFunctionExpression") {
|
|
6909
8520
|
return { fn: node };
|
|
@@ -6930,7 +8541,7 @@ function reactIsPassThrough(type) {
|
|
|
6930
8541
|
const argument = type.typeArguments?.params.length === 1 ? type.typeArguments.params[0] : undefined;
|
|
6931
8542
|
return name.type === "Identifier" && name.name === "ComponentProps" && (argument?.type === "TSTypeQuery" || argument?.type === "TSLiteralType" && argument.literal.type === "Literal" && typeof argument.literal.value === "string");
|
|
6932
8543
|
}
|
|
6933
|
-
var reactProps =
|
|
8544
|
+
var reactProps = defineRule77({
|
|
6934
8545
|
meta: {
|
|
6935
8546
|
type: "problem",
|
|
6936
8547
|
schema: [
|
|
@@ -7029,8 +8640,8 @@ var reactProps = defineRule62({
|
|
|
7029
8640
|
});
|
|
7030
8641
|
|
|
7031
8642
|
// src/rules/react/react-query-props.ts
|
|
7032
|
-
import { defineRule as
|
|
7033
|
-
var reactQueryProps =
|
|
8643
|
+
import { defineRule as defineRule78 } from "@oxlint/plugins";
|
|
8644
|
+
var reactQueryProps = defineRule78({
|
|
7034
8645
|
meta: {
|
|
7035
8646
|
type: "problem",
|
|
7036
8647
|
schema: [
|
|
@@ -7111,7 +8722,7 @@ var reactQueryProps = defineRule63({
|
|
|
7111
8722
|
});
|
|
7112
8723
|
|
|
7113
8724
|
// src/rules/schemas/schemas-inline.ts
|
|
7114
|
-
import { defineRule as
|
|
8725
|
+
import { defineRule as defineRule79 } from "@oxlint/plugins";
|
|
7115
8726
|
|
|
7116
8727
|
// src/utils/schemas.utils.ts
|
|
7117
8728
|
var schemasElementOptionsSchema = {
|
|
@@ -7147,7 +8758,7 @@ function schemasIsAllowedElement(node, allowScalars, namespaces, structuralMetho
|
|
|
7147
8758
|
}
|
|
7148
8759
|
|
|
7149
8760
|
// src/rules/schemas/schemas-inline.ts
|
|
7150
|
-
var schemasInline =
|
|
8761
|
+
var schemasInline = defineRule79({
|
|
7151
8762
|
meta: {
|
|
7152
8763
|
type: "problem",
|
|
7153
8764
|
schema: [schemasElementOptionsSchema],
|
|
@@ -7196,11 +8807,11 @@ var schemasInline = defineRule64({
|
|
|
7196
8807
|
});
|
|
7197
8808
|
|
|
7198
8809
|
// src/rules/schemas/schemas-local.ts
|
|
7199
|
-
import { defineRule as
|
|
8810
|
+
import { defineRule as defineRule80 } from "@oxlint/plugins";
|
|
7200
8811
|
var schemasLocalImportMessage = "Do not import runtime schema constructors from {{source}}; import a named schema instead.";
|
|
7201
8812
|
var schemasLocalConstructionMessage = "Do not construct schemas locally; import a named schema instead.";
|
|
7202
8813
|
var schemasLocalCalls = ["config", "prettifyError", "treeifyError", "flattenError", "formatError"];
|
|
7203
|
-
var schemasLocal =
|
|
8814
|
+
var schemasLocal = defineRule80({
|
|
7204
8815
|
meta: {
|
|
7205
8816
|
type: "problem",
|
|
7206
8817
|
schema: [
|
|
@@ -7349,8 +8960,8 @@ var schemasLocal = defineRule65({
|
|
|
7349
8960
|
});
|
|
7350
8961
|
|
|
7351
8962
|
// src/rules/schemas/schemas-rederive.ts
|
|
7352
|
-
import { defineRule as
|
|
7353
|
-
var schemasRederive =
|
|
8963
|
+
import { defineRule as defineRule81 } from "@oxlint/plugins";
|
|
8964
|
+
var schemasRederive = defineRule81({
|
|
7354
8965
|
meta: {
|
|
7355
8966
|
type: "problem",
|
|
7356
8967
|
schema: [
|
|
@@ -7396,7 +9007,7 @@ var schemasRederive = defineRule66({
|
|
|
7396
9007
|
});
|
|
7397
9008
|
|
|
7398
9009
|
// src/rules/schemas/schemas-single-use.ts
|
|
7399
|
-
import { defineRule as
|
|
9010
|
+
import { defineRule as defineRule82 } from "@oxlint/plugins";
|
|
7400
9011
|
function isScalarSchema(node, namespaces, structuralMethods) {
|
|
7401
9012
|
if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression") {
|
|
7402
9013
|
return false;
|
|
@@ -7410,7 +9021,7 @@ function isScalarSchema(node, namespaces, structuralMethods) {
|
|
|
7410
9021
|
}
|
|
7411
9022
|
return node.callee.object.type === "CallExpression" && isScalarSchema(node.callee.object, namespaces, structuralMethods);
|
|
7412
9023
|
}
|
|
7413
|
-
var schemasSingleUse =
|
|
9024
|
+
var schemasSingleUse = defineRule82({
|
|
7414
9025
|
meta: {
|
|
7415
9026
|
type: "suggestion",
|
|
7416
9027
|
schema: [schemasElementOptionsSchema],
|
|
@@ -7486,7 +9097,7 @@ var schemasSingleUse = defineRule67({
|
|
|
7486
9097
|
});
|
|
7487
9098
|
|
|
7488
9099
|
// src/rules/tests/tests-assertions.ts
|
|
7489
|
-
import { defineRule as
|
|
9100
|
+
import { defineRule as defineRule83 } from "@oxlint/plugins";
|
|
7490
9101
|
var testsAssertionsModifiers = new Set(["for", "each", "concurrent", "sequential", "only", "skip"]);
|
|
7491
9102
|
var testsAssertionsFunctions = [
|
|
7492
9103
|
"expect",
|
|
@@ -7676,7 +9287,7 @@ function isAssertedTest(registration, sourceCode, state) {
|
|
|
7676
9287
|
}
|
|
7677
9288
|
return rows.length > 0 && rows.every((row) => hasAssertion(callback, row.bindings, new Set, state));
|
|
7678
9289
|
}
|
|
7679
|
-
var testsAssertions =
|
|
9290
|
+
var testsAssertions = defineRule83({
|
|
7680
9291
|
meta: {
|
|
7681
9292
|
type: "problem",
|
|
7682
9293
|
schema: [
|
|
@@ -7802,8 +9413,8 @@ var testsAssertions = defineRule68({
|
|
|
7802
9413
|
});
|
|
7803
9414
|
|
|
7804
9415
|
// src/rules/tests/tests-concurrent-db.ts
|
|
7805
|
-
import { defineRule as
|
|
7806
|
-
var testsConcurrentDb =
|
|
9416
|
+
import { defineRule as defineRule84 } from "@oxlint/plugins";
|
|
9417
|
+
var testsConcurrentDb = defineRule84({
|
|
7807
9418
|
meta: {
|
|
7808
9419
|
type: "problem",
|
|
7809
9420
|
schema: [
|
|
@@ -7859,7 +9470,7 @@ var testsConcurrentDb = defineRule69({
|
|
|
7859
9470
|
});
|
|
7860
9471
|
|
|
7861
9472
|
// src/rules/tests/tests-modifiers.ts
|
|
7862
|
-
import { defineRule as
|
|
9473
|
+
import { defineRule as defineRule85 } from "@oxlint/plugins";
|
|
7863
9474
|
var testsModifiersNames = ["test", "it", "describe", "suite"];
|
|
7864
9475
|
var testsModifiersPackages = new Set(["vitest", "bun:test", "@playwright/test", "@jest/globals"]);
|
|
7865
9476
|
var testsModifiersBanned = ["only", "skip", "todo"];
|
|
@@ -7878,7 +9489,7 @@ function modifierName(node) {
|
|
|
7878
9489
|
const property = astMemberName(node);
|
|
7879
9490
|
return object && property ? `${object}.${property}` : undefined;
|
|
7880
9491
|
}
|
|
7881
|
-
var testsModifiers =
|
|
9492
|
+
var testsModifiers = defineRule85({
|
|
7882
9493
|
meta: {
|
|
7883
9494
|
type: "problem",
|
|
7884
9495
|
schema: [
|
|
@@ -7932,9 +9543,9 @@ var testsModifiers = defineRule70({
|
|
|
7932
9543
|
});
|
|
7933
9544
|
|
|
7934
9545
|
// src/rules/tests/tests-titles.ts
|
|
7935
|
-
import { defineRule as
|
|
9546
|
+
import { defineRule as defineRule86 } from "@oxlint/plugins";
|
|
7936
9547
|
var testsTitlesModifiers = ["only", "skip", "concurrent", "sequential", "todo", "each", "for"];
|
|
7937
|
-
var testsTitles =
|
|
9548
|
+
var testsTitles = defineRule86({
|
|
7938
9549
|
meta: {
|
|
7939
9550
|
type: "suggestion",
|
|
7940
9551
|
schema: [
|
|
@@ -8012,11 +9623,11 @@ var testsTitles = defineRule71({
|
|
|
8012
9623
|
});
|
|
8013
9624
|
|
|
8014
9625
|
// src/rules/types/types-declarations.ts
|
|
8015
|
-
import { defineRule as
|
|
9626
|
+
import { defineRule as defineRule87 } from "@oxlint/plugins";
|
|
8016
9627
|
function isValuePattern(source) {
|
|
8017
9628
|
return /^\^?=/.test(source);
|
|
8018
9629
|
}
|
|
8019
|
-
var typesDeclarations =
|
|
9630
|
+
var typesDeclarations = defineRule87({
|
|
8020
9631
|
meta: {
|
|
8021
9632
|
type: "problem",
|
|
8022
9633
|
schema: [
|
|
@@ -8067,8 +9678,8 @@ var typesDeclarations = defineRule72({
|
|
|
8067
9678
|
});
|
|
8068
9679
|
|
|
8069
9680
|
// src/rules/types/types-duplicate.ts
|
|
8070
|
-
import { defineRule as
|
|
8071
|
-
var typesDuplicate =
|
|
9681
|
+
import { defineRule as defineRule88 } from "@oxlint/plugins";
|
|
9682
|
+
var typesDuplicate = defineRule88({
|
|
8072
9683
|
meta: {
|
|
8073
9684
|
type: "suggestion",
|
|
8074
9685
|
schema: [
|
|
@@ -8132,7 +9743,7 @@ var typesDuplicate = defineRule73({
|
|
|
8132
9743
|
});
|
|
8133
9744
|
|
|
8134
9745
|
// src/rules/types/types-imported-alias.ts
|
|
8135
|
-
import { defineRule as
|
|
9746
|
+
import { defineRule as defineRule89 } from "@oxlint/plugins";
|
|
8136
9747
|
function importedAliasTarget(declaration, imports) {
|
|
8137
9748
|
if (declaration?.type !== "TSTypeAliasDeclaration") {
|
|
8138
9749
|
return;
|
|
@@ -8144,7 +9755,7 @@ function importedAliasTarget(declaration, imports) {
|
|
|
8144
9755
|
const { name } = type.typeName;
|
|
8145
9756
|
return imports.has(name) && !declaration.typeParameters?.params.some((parameter) => parameter.name.name === name) ? name : undefined;
|
|
8146
9757
|
}
|
|
8147
|
-
var typesImportedAlias =
|
|
9758
|
+
var typesImportedAlias = defineRule89({
|
|
8148
9759
|
meta: {
|
|
8149
9760
|
type: "problem",
|
|
8150
9761
|
schema: [],
|
|
@@ -8206,7 +9817,7 @@ var typesImportedAlias = defineRule74({
|
|
|
8206
9817
|
});
|
|
8207
9818
|
|
|
8208
9819
|
// src/rules/types/types-inline.ts
|
|
8209
|
-
import { defineRule as
|
|
9820
|
+
import { defineRule as defineRule90 } from "@oxlint/plugins";
|
|
8210
9821
|
function parameterAnnotation(parameter) {
|
|
8211
9822
|
if (parameter.type === "TSParameterProperty") {
|
|
8212
9823
|
return parameterAnnotation(parameter.parameter);
|
|
@@ -8216,7 +9827,7 @@ function parameterAnnotation(parameter) {
|
|
|
8216
9827
|
}
|
|
8217
9828
|
return parameter.typeAnnotation;
|
|
8218
9829
|
}
|
|
8219
|
-
var typesInline =
|
|
9830
|
+
var typesInline = defineRule90({
|
|
8220
9831
|
meta: {
|
|
8221
9832
|
type: "problem",
|
|
8222
9833
|
schema: [
|
|
@@ -8328,11 +9939,38 @@ var typesInline = defineRule75({
|
|
|
8328
9939
|
});
|
|
8329
9940
|
|
|
8330
9941
|
// src/rules/types/types-local-alias.ts
|
|
8331
|
-
|
|
9942
|
+
import { defineRule as defineRule91 } from "@oxlint/plugins";
|
|
9943
|
+
var typesLocalAlias = defineRule91({
|
|
9944
|
+
meta: {
|
|
9945
|
+
type: "problem",
|
|
9946
|
+
schema: [{ type: "object", additionalProperties: false, properties: { allow: optionsPatternSchema, message: { type: "string" } } }],
|
|
9947
|
+
messages: { forbidden: "{{message}}" }
|
|
9948
|
+
},
|
|
9949
|
+
createOnce(context) {
|
|
9950
|
+
let allow = [];
|
|
9951
|
+
let message = "";
|
|
9952
|
+
return {
|
|
9953
|
+
before() {
|
|
9954
|
+
const options = optionsFirst(context, {});
|
|
9955
|
+
allow = optionsPatterns(options.allow);
|
|
9956
|
+
message = options.message ?? "Local component/hook contracts use interfaces named for their owner. Derived or reusable aliases belong in domain types; do not substitute an empty interface.";
|
|
9957
|
+
},
|
|
9958
|
+
TSTypeAliasDeclaration(node) {
|
|
9959
|
+
if (!optionsPatternsTest(allow, node.id.name)) {
|
|
9960
|
+
context.report({
|
|
9961
|
+
node,
|
|
9962
|
+
messageId: "forbidden",
|
|
9963
|
+
data: { message }
|
|
9964
|
+
});
|
|
9965
|
+
}
|
|
9966
|
+
}
|
|
9967
|
+
};
|
|
9968
|
+
}
|
|
9969
|
+
});
|
|
8332
9970
|
|
|
8333
9971
|
// src/rules/types/types-namespace-import.ts
|
|
8334
|
-
import { defineRule as
|
|
8335
|
-
var typesNamespaceImport =
|
|
9972
|
+
import { defineRule as defineRule92 } from "@oxlint/plugins";
|
|
9973
|
+
var typesNamespaceImport = defineRule92({
|
|
8336
9974
|
meta: {
|
|
8337
9975
|
type: "suggestion",
|
|
8338
9976
|
schema: [
|
|
@@ -8374,8 +10012,8 @@ var typesNamespaceImport = defineRule76({
|
|
|
8374
10012
|
});
|
|
8375
10013
|
|
|
8376
10014
|
// src/rules/types/types-service.ts
|
|
8377
|
-
import { defineRule as
|
|
8378
|
-
var typesService =
|
|
10015
|
+
import { defineRule as defineRule93 } from "@oxlint/plugins";
|
|
10016
|
+
var typesService = defineRule93({
|
|
8379
10017
|
meta: {
|
|
8380
10018
|
type: "problem",
|
|
8381
10019
|
schema: [
|
|
@@ -8425,12 +10063,12 @@ var typesService = defineRule77({
|
|
|
8425
10063
|
});
|
|
8426
10064
|
|
|
8427
10065
|
// src/rules/types/types-unknown.ts
|
|
8428
|
-
import { defineRule as
|
|
10066
|
+
import { defineRule as defineRule94 } from "@oxlint/plugins";
|
|
8429
10067
|
function isCatchName(node) {
|
|
8430
10068
|
const key = "key" in node && !(("computed" in node) && node.computed) ? node.key : undefined;
|
|
8431
10069
|
return key?.type === "Identifier" && key.name === "catch";
|
|
8432
10070
|
}
|
|
8433
|
-
var typesUnknown =
|
|
10071
|
+
var typesUnknown = defineRule94({
|
|
8434
10072
|
meta: { type: "problem", schema: [], messages: { forbidden: "Use an owned domain type rather than unknown." } },
|
|
8435
10073
|
createOnce(context) {
|
|
8436
10074
|
return {
|
|
@@ -8473,6 +10111,7 @@ var src_default = eslintCompatPlugin({
|
|
|
8473
10111
|
"code-double-negation": codeDoubleNegation,
|
|
8474
10112
|
"code-function-nesting": codeFunctionNesting,
|
|
8475
10113
|
"code-inline": codeInline,
|
|
10114
|
+
"code-inside": codeInside,
|
|
8476
10115
|
"code-justified-call": codeJustifiedCall,
|
|
8477
10116
|
"code-literal-in": codeLiteralIn,
|
|
8478
10117
|
"code-mutable-state": codeMutableState,
|
|
@@ -8487,10 +10126,14 @@ var src_default = eslintCompatPlugin({
|
|
|
8487
10126
|
"comments-directives": commentsDirectives,
|
|
8488
10127
|
"copy-dashes": copyDashes,
|
|
8489
10128
|
"effect-layer-file": effectLayerFile,
|
|
10129
|
+
"effect-layer-keys": effectLayerKeys,
|
|
8490
10130
|
"effect-layers": effectLayers,
|
|
8491
10131
|
"effect-merge-pipe": effectMergePipe,
|
|
8492
10132
|
"effect-schema-first": effectSchemaFirst,
|
|
10133
|
+
"effect-service-params": effectServiceParams,
|
|
8493
10134
|
"effect-values-first": effectValuesFirst,
|
|
10135
|
+
"effect-yield-name": effectYieldName,
|
|
10136
|
+
"effect-yields-first": effectYieldsFirst,
|
|
8494
10137
|
"files-components-only": filesComponentsOnly,
|
|
8495
10138
|
"files-constants-only": filesConstantsOnly,
|
|
8496
10139
|
"files-domain-constants": filesDomainConstants,
|
|
@@ -8501,6 +10144,8 @@ var src_default = eslintCompatPlugin({
|
|
|
8501
10144
|
"files-no-functions": filesNoFunctions,
|
|
8502
10145
|
"files-restricted": filesRestricted,
|
|
8503
10146
|
"files-route": filesRoute,
|
|
10147
|
+
"files-siblings": filesSiblings,
|
|
10148
|
+
"files-top-level": filesTopLevel,
|
|
8504
10149
|
"files-types-only": filesTypesOnly,
|
|
8505
10150
|
"layout-arrow-body": layoutArrowBody,
|
|
8506
10151
|
"layout-call-arguments": layoutCallArguments,
|
|
@@ -8514,10 +10159,15 @@ var src_default = eslintCompatPlugin({
|
|
|
8514
10159
|
"layout-padding": layoutPadding,
|
|
8515
10160
|
"layout-pipe": layoutPipe,
|
|
8516
10161
|
"layout-ternary": layoutTernary,
|
|
10162
|
+
"layout-type-arguments": layoutTypeArguments,
|
|
8517
10163
|
"naming-declarations": namingDeclarations,
|
|
8518
10164
|
"naming-exports": namingExports,
|
|
8519
10165
|
"naming-file": namingFile,
|
|
8520
10166
|
"naming-folder": namingFolder,
|
|
10167
|
+
"naming-imports": namingImports,
|
|
10168
|
+
"naming-keys": namingKeys,
|
|
10169
|
+
"naming-labels": namingLabels,
|
|
10170
|
+
"naming-pairs": namingPairs,
|
|
8521
10171
|
"naming-types": namingTypes,
|
|
8522
10172
|
"react-dynamic-classes": reactDynamicClasses,
|
|
8523
10173
|
"react-empty-effect": reactEmptyEffect,
|