oxlint-plugin-arch 0.13.0 → 0.13.1
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 +31 -0
- package/README.md +2 -2
- package/dist/index.js +707 -201
- package/dist/preset/preset.constants.d.ts +4 -0
- package/dist/preset/preset.js +49 -17
- package/dist/preset/preset.utils.d.ts +1 -0
- package/dist/rules/layout/layout-type-arguments.d.ts +1 -0
- package/dist/types/boundaries.types.d.ts +26 -1
- package/dist/types/code.types.d.ts +1 -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 +1 -0
- package/dist/types/layout.types.d.ts +16 -0
- package/dist/types/naming.types.d.ts +4 -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/options.utils.d.ts +1 -0
- package/dist/vendor/dillon-anti-slop/index.js +37 -15
- package/package.json +1 -1
- package/src/vendor/dillon-anti-slop/UPSTREAM.md +3 -0
package/dist/index.js
CHANGED
|
@@ -50,6 +50,9 @@ 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
|
+
}
|
|
53
56
|
|
|
54
57
|
// src/utils/ast.utils.ts
|
|
55
58
|
var astSkippedKeys = new Set(["parent", "type", "start", "end", "range", "loc", "tokens", "comments"]);
|
|
@@ -237,9 +240,6 @@ function astIsInlineFunction(node) {
|
|
|
237
240
|
function astIsCall(node) {
|
|
238
241
|
return node.type === "CallExpression" || node.type === "NewExpression";
|
|
239
242
|
}
|
|
240
|
-
function astIsPipe(node) {
|
|
241
|
-
return node.callee.type === "MemberExpression" && astMemberName(node.callee) === "pipe";
|
|
242
|
-
}
|
|
243
243
|
function astChildren(node) {
|
|
244
244
|
return Object.entries(node).filter(([key]) => !astSkippedKeys.has(key)).map(([, child]) => child);
|
|
245
245
|
}
|
|
@@ -249,17 +249,20 @@ function astSome(value, test, enter = () => true) {
|
|
|
249
249
|
}
|
|
250
250
|
return astIsNode(value) && (test(value) || enter(value) && astChildren(value).some((child) => astSome(child, test, enter)));
|
|
251
251
|
}
|
|
252
|
-
function astCallDepth(value) {
|
|
252
|
+
function astCallDepth(value, callbacks = false) {
|
|
253
253
|
if (Array.isArray(value)) {
|
|
254
|
-
return Math.max(0, ...value.map(astCallDepth));
|
|
254
|
+
return Math.max(0, ...value.map((item) => astCallDepth(item, callbacks)));
|
|
255
255
|
}
|
|
256
|
-
if (!astIsNode(value)
|
|
256
|
+
if (!astIsNode(value)) {
|
|
257
257
|
return 0;
|
|
258
258
|
}
|
|
259
|
+
if (astIsInlineFunction(value)) {
|
|
260
|
+
return callbacks ? astCallDepth(value.body, callbacks) : 0;
|
|
261
|
+
}
|
|
259
262
|
if (astIsCall(value)) {
|
|
260
|
-
return Math.max(value.arguments.length === 0 ? 0 : 1 + astCallDepth(value.arguments), astCallDepth(value.callee));
|
|
263
|
+
return Math.max(value.arguments.length === 0 ? 0 : 1 + astCallDepth(value.arguments, callbacks), astCallDepth(value.callee, callbacks));
|
|
261
264
|
}
|
|
262
|
-
return astCallDepth(astChildren(value));
|
|
265
|
+
return astCallDepth(astChildren(value), callbacks);
|
|
263
266
|
}
|
|
264
267
|
|
|
265
268
|
// src/rules/backend/backend-orpc-output.ts
|
|
@@ -842,6 +845,27 @@ import { defineRule as defineRule5 } from "@oxlint/plugins";
|
|
|
842
845
|
// src/utils/boundaries.utils.ts
|
|
843
846
|
import { builtinModules } from "node:module";
|
|
844
847
|
import { dirname, resolve } from "node:path";
|
|
848
|
+
var boundariesRuntimeConventions = {
|
|
849
|
+
server: ["server"],
|
|
850
|
+
rsc: ["rsc"],
|
|
851
|
+
actions: ["action"],
|
|
852
|
+
client: ["client"],
|
|
853
|
+
clientOnly: ["client-only"],
|
|
854
|
+
hooks: ["hooks"],
|
|
855
|
+
shared: ["utils", "constants"],
|
|
856
|
+
lib: "lib"
|
|
857
|
+
};
|
|
858
|
+
var boundariesSuffixList = { type: "array", items: { type: "string", minLength: 1 } };
|
|
859
|
+
function boundariesConventionsFor(options) {
|
|
860
|
+
return { ...boundariesRuntimeConventions, ...options.conventions };
|
|
861
|
+
}
|
|
862
|
+
function boundariesHasSuffix(suffixes, path, optionalExtension = false) {
|
|
863
|
+
const extension = optionalExtension ? "(?:\\.[cm]?[jt]sx?)?" : "\\.[cm]?[jt]sx?";
|
|
864
|
+
return suffixes.length > 0 && new RegExp(`\\.(?:${suffixes.map(optionsEscape).join("|")})${extension}$`).test(path);
|
|
865
|
+
}
|
|
866
|
+
function boundariesIsClientOnly(conventions, target) {
|
|
867
|
+
return conventions.hooks.some((folder) => target.includes(`/${folder}/`)) || conventions.clientOnly.some((suffix) => new RegExp(`\\.${optionsEscape(suffix)}(?:\\.|$)`).test(target));
|
|
868
|
+
}
|
|
845
869
|
var boundariesServerGlobals = ["process", "Bun", "Deno", "Buffer", "__dirname", "__filename", "require"];
|
|
846
870
|
var boundariesBrowserGlobals = [
|
|
847
871
|
"window",
|
|
@@ -877,7 +901,21 @@ var boundariesRuntimeProperties = {
|
|
|
877
901
|
dynamic: { type: "array", items: { type: "string" } },
|
|
878
902
|
serverPackages: { type: "array", items: { type: "string" } },
|
|
879
903
|
clientPackages: { type: "array", items: { type: "string" } },
|
|
880
|
-
hookPackages: { type: "array", items: { type: "string" } }
|
|
904
|
+
hookPackages: { type: "array", items: { type: "string" } },
|
|
905
|
+
conventions: {
|
|
906
|
+
type: "object",
|
|
907
|
+
additionalProperties: false,
|
|
908
|
+
properties: {
|
|
909
|
+
server: boundariesSuffixList,
|
|
910
|
+
rsc: boundariesSuffixList,
|
|
911
|
+
actions: boundariesSuffixList,
|
|
912
|
+
client: boundariesSuffixList,
|
|
913
|
+
clientOnly: boundariesSuffixList,
|
|
914
|
+
hooks: boundariesSuffixList,
|
|
915
|
+
shared: boundariesSuffixList,
|
|
916
|
+
lib: { type: "string", minLength: 1 }
|
|
917
|
+
}
|
|
918
|
+
}
|
|
881
919
|
};
|
|
882
920
|
var boundariesServerPackages = ["@orpc/server", "react-dom/server"];
|
|
883
921
|
var boundariesRuntimeMessages = {
|
|
@@ -908,16 +946,21 @@ function boundariesReport(context, node, source, reason) {
|
|
|
908
946
|
data: { source, reason }
|
|
909
947
|
});
|
|
910
948
|
}
|
|
911
|
-
function
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
949
|
+
function boundariesRuntimeImportVisitors(context, check, allowComputed) {
|
|
950
|
+
return {
|
|
951
|
+
...boundariesExportVisitors(check),
|
|
952
|
+
ImportExpression(node) {
|
|
953
|
+
const source = boundariesLiteralSource(node);
|
|
954
|
+
if (source !== undefined) {
|
|
955
|
+
check(node, source, false, false);
|
|
956
|
+
} else if (!allowComputed()) {
|
|
957
|
+
context.report({ node, messageId: "computed" });
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
};
|
|
918
961
|
}
|
|
919
|
-
function boundariesIsSharedFile(options, file) {
|
|
920
|
-
return
|
|
962
|
+
function boundariesIsSharedFile(options, conventions, file) {
|
|
963
|
+
return boundariesHasSuffix(conventions.shared, file) || options.web.some((path) => file.startsWith(`${path}/${conventions.lib}/`));
|
|
921
964
|
}
|
|
922
965
|
function boundariesDeclaredVariable(start, name) {
|
|
923
966
|
for (let scope = start;scope; scope = scope.upper) {
|
|
@@ -946,7 +989,7 @@ function boundariesMemberName(node) {
|
|
|
946
989
|
}
|
|
947
990
|
return node.computed && node.property.type === "Literal" && typeof node.property.value === "string" ? node.property.value : null;
|
|
948
991
|
}
|
|
949
|
-
function boundariesResolveTarget(options, file, source) {
|
|
992
|
+
function boundariesResolveTarget(options, file, source, services = "services") {
|
|
950
993
|
const owner = [...options.web, ...options.backend].sort((a, b) => b.length - a.length).find((root) => file.startsWith(`${root}/`));
|
|
951
994
|
const web = owner && options.web.includes(owner) ? owner : undefined;
|
|
952
995
|
const alias = Object.entries(options.aliases).sort(([a], [b]) => b.length - a.length).find(([prefix]) => source === prefix || source.startsWith(`${prefix}/`));
|
|
@@ -957,13 +1000,13 @@ function boundariesResolveTarget(options, file, source) {
|
|
|
957
1000
|
return resolve(dirname(file), source).replaceAll("\\", "/");
|
|
958
1001
|
}
|
|
959
1002
|
if (source.startsWith("@/") && web) {
|
|
960
|
-
return
|
|
1003
|
+
return resolve(web, source.slice(2)).replaceAll("\\", "/");
|
|
961
1004
|
}
|
|
962
1005
|
if (source.startsWith("#/") && owner) {
|
|
963
|
-
return
|
|
1006
|
+
return resolve(owner, source.slice(2)).replaceAll("\\", "/");
|
|
964
1007
|
}
|
|
965
1008
|
if (source.startsWith("#services/") && owner) {
|
|
966
|
-
return
|
|
1009
|
+
return resolve(owner, services, source.slice("#services/".length)).replaceAll("\\", "/");
|
|
967
1010
|
}
|
|
968
1011
|
return source;
|
|
969
1012
|
}
|
|
@@ -1145,7 +1188,25 @@ var boundariesDeny = defineRule6({
|
|
|
1145
1188
|
import { defineRule as defineRule7 } from "@oxlint/plugins";
|
|
1146
1189
|
import { basename, resolve as resolve2 } from "node:path";
|
|
1147
1190
|
var boundariesSurfaces = ["service", "client", "server", "rsc", "utils"];
|
|
1148
|
-
var
|
|
1191
|
+
var boundariesConventions = {
|
|
1192
|
+
services: "services",
|
|
1193
|
+
registry: "service",
|
|
1194
|
+
types: "types",
|
|
1195
|
+
lib: "lib",
|
|
1196
|
+
contracts: ["types", "constants"],
|
|
1197
|
+
implementations: ["service", "utils"],
|
|
1198
|
+
implementationFolders: ["core"],
|
|
1199
|
+
operations: ["action", "query"],
|
|
1200
|
+
operationFolders: ["actions", "queries"],
|
|
1201
|
+
domainFiles: ["action", "query", "driver"],
|
|
1202
|
+
clients: ["client"]
|
|
1203
|
+
};
|
|
1204
|
+
function boundariesAny(values) {
|
|
1205
|
+
return values.length === 0 ? "(?!)" : `(?:${values.map(optionsEscape).join("|")})`;
|
|
1206
|
+
}
|
|
1207
|
+
function boundariesIsOperation(conventions, path) {
|
|
1208
|
+
return new RegExp(`/[^/]+-${boundariesAny(conventions.operations)}\\.`).test(path);
|
|
1209
|
+
}
|
|
1149
1210
|
function boundariesContains(directory, candidate) {
|
|
1150
1211
|
return candidate === directory || candidate.startsWith(`${directory}/`);
|
|
1151
1212
|
}
|
|
@@ -1155,44 +1216,56 @@ function boundariesIsSurface(options, stem) {
|
|
|
1155
1216
|
function boundariesStripExtension(path) {
|
|
1156
1217
|
return path.replace(/\.[cm]?[jt]sx?$/, "");
|
|
1157
1218
|
}
|
|
1219
|
+
function boundariesConventionsOf(options) {
|
|
1220
|
+
return { ...boundariesConventions, ...options.conventions };
|
|
1221
|
+
}
|
|
1158
1222
|
function boundariesServiceFolder(options, root) {
|
|
1159
1223
|
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}
|
|
1224
|
+
return options.services?.find((folder) => folder.startsWith(`${root}/`) && !nested.some((other) => boundariesContains(other, folder))) ?? `${root}/${boundariesConventionsOf(options).services}`;
|
|
1161
1225
|
}
|
|
1162
|
-
function boundariesDomain(path, appRoot, services) {
|
|
1226
|
+
function boundariesDomain(conventions, path, appRoot, services) {
|
|
1163
1227
|
const relative = path.slice(appRoot.length + 1);
|
|
1164
|
-
if (relative.startsWith(
|
|
1165
|
-
return relative.slice(
|
|
1228
|
+
if (relative.startsWith(`${conventions.types}/`) && relative.endsWith(`.${conventions.types}.ts`)) {
|
|
1229
|
+
return relative.slice(conventions.types.length + 1).split(".")[0];
|
|
1166
1230
|
}
|
|
1167
1231
|
if (!boundariesContains(services, path) || path === services) {
|
|
1168
1232
|
return;
|
|
1169
1233
|
}
|
|
1170
1234
|
const parts = path.slice(services.length + 1).split("/");
|
|
1171
|
-
return parts.length > 1 ? parts[0] : parts[0].split(".")[0].replace(
|
|
1235
|
+
return parts.length > 1 ? parts[0] : parts[0].split(".")[0].replace(new RegExp(`-${boundariesAny(conventions.domainFiles)}$`), "");
|
|
1172
1236
|
}
|
|
1173
1237
|
function boundariesPrivateDomain({ options, file, target, reExport, roots, owner }) {
|
|
1174
1238
|
const roles = options.roles;
|
|
1239
|
+
const conventions = boundariesConventionsOf(options);
|
|
1175
1240
|
if (!roles?.length) {
|
|
1176
1241
|
return;
|
|
1177
1242
|
}
|
|
1178
1243
|
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;
|
|
1244
|
+
const fileDomain = owner ? boundariesDomain(conventions, file, owner, boundariesServiceFolder(options, owner)) : undefined;
|
|
1245
|
+
const targetDomain = targetOwner ? boundariesDomain(conventions, target, targetOwner, boundariesServiceFolder(options, targetOwner)) : undefined;
|
|
1181
1246
|
const sameDomain = owner && owner === targetOwner && fileDomain && fileDomain === targetDomain;
|
|
1182
1247
|
const targetStem = boundariesStripExtension(target);
|
|
1183
|
-
const publicSurface = boundariesIsSurface(options, targetStem) && !
|
|
1184
|
-
const privateTarget = roles.some((role) => targetStem.endsWith(`.${role}`)) || targetDomain && !publicSurface &&
|
|
1248
|
+
const publicSurface = boundariesIsSurface(options, targetStem) && !boundariesIsOperation(conventions, targetStem);
|
|
1249
|
+
const privateTarget = roles.some((role) => targetStem.endsWith(`.${role}`)) || targetDomain && !publicSurface && !new RegExp(`\\.${boundariesAny(conventions.contracts)}$`).test(targetStem);
|
|
1185
1250
|
if (!privateTarget) {
|
|
1186
1251
|
return;
|
|
1187
1252
|
}
|
|
1188
|
-
if (reExport && file.endsWith(".ts") && boundariesIsSurface(options, file.slice(0, -".ts".length)) && !
|
|
1253
|
+
if (reExport && file.endsWith(".ts") && boundariesIsSurface(options, file.slice(0, -".ts".length)) && !boundariesIsOperation(conventions, file)) {
|
|
1189
1254
|
return "public service and utility surfaces must not re-export private domain internals";
|
|
1190
1255
|
}
|
|
1191
1256
|
return sameDomain ? undefined : "private domain files stay in the same domain and application; use its service API or shared utilities";
|
|
1192
1257
|
}
|
|
1258
|
+
function boundariesPortableFolder(options) {
|
|
1259
|
+
if (!options.portable) {
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
return typeof options.portable === "string" ? options.portable : boundariesConventionsOf(options).lib;
|
|
1263
|
+
}
|
|
1193
1264
|
function boundariesPortableLib(check) {
|
|
1194
1265
|
const { options, file, source, target, typeOnly, reExport, roots, web, backendImport } = check;
|
|
1195
|
-
|
|
1266
|
+
const { types } = boundariesConventionsOf(options);
|
|
1267
|
+
const lib = boundariesPortableFolder(options);
|
|
1268
|
+
if (!lib || !web || !boundariesContains(`${web}/${lib}`, file) || boundariesServiceFolder(options, web) === `${web}/${lib}`) {
|
|
1196
1269
|
return;
|
|
1197
1270
|
}
|
|
1198
1271
|
const portableTarget = target.startsWith("/") ? resolve2(target).replaceAll("\\", "/") : target;
|
|
@@ -1202,11 +1275,11 @@ function boundariesPortableLib(check) {
|
|
|
1202
1275
|
if (backendImport || backendTarget || appService || appPackage) {
|
|
1203
1276
|
return "portable frontend lib does not import application services or application APIs";
|
|
1204
1277
|
}
|
|
1205
|
-
const appType = roots.some((path) => boundariesContains(`${path}
|
|
1278
|
+
const appType = roots.some((path) => boundariesContains(`${path}/${types}`, portableTarget));
|
|
1206
1279
|
const stem = boundariesStripExtension(basename(file)).replace(/\.utils$/, "");
|
|
1207
|
-
const ownType = typeOnly && !reExport && boundariesStripExtension(portableTarget) === `${web}
|
|
1280
|
+
const ownType = typeOnly && !reExport && boundariesStripExtension(portableTarget) === `${web}/${types}/${stem}.${types}`;
|
|
1208
1281
|
if (appType && !ownType) {
|
|
1209
|
-
return `portable frontend lib may only type-import its own contract '${web}
|
|
1282
|
+
return `portable frontend lib may only type-import its own contract '${web}/${types}/${stem}.${types}.ts'; app type re-exports are forbidden`;
|
|
1210
1283
|
}
|
|
1211
1284
|
return;
|
|
1212
1285
|
}
|
|
@@ -1225,7 +1298,24 @@ var boundariesImports = defineRule7({
|
|
|
1225
1298
|
roles: { type: "array", items: { type: "string" } },
|
|
1226
1299
|
surfaces: { type: "array", items: { type: "string", minLength: 1 }, default: boundariesSurfaces },
|
|
1227
1300
|
services: { type: "array", items: { type: "string" } },
|
|
1228
|
-
portable: { type: "boolean" },
|
|
1301
|
+
portable: { anyOf: [{ type: "boolean" }, { type: "string", minLength: 1 }] },
|
|
1302
|
+
conventions: {
|
|
1303
|
+
type: "object",
|
|
1304
|
+
additionalProperties: false,
|
|
1305
|
+
properties: {
|
|
1306
|
+
services: { type: "string", minLength: 1 },
|
|
1307
|
+
registry: { type: "string", minLength: 1 },
|
|
1308
|
+
types: { type: "string", minLength: 1 },
|
|
1309
|
+
lib: { type: "string", minLength: 1 },
|
|
1310
|
+
contracts: { type: "array", items: { type: "string", minLength: 1 } },
|
|
1311
|
+
implementations: { type: "array", items: { type: "string", minLength: 1 } },
|
|
1312
|
+
implementationFolders: { type: "array", items: { type: "string", minLength: 1 } },
|
|
1313
|
+
operations: { type: "array", items: { type: "string", minLength: 1 } },
|
|
1314
|
+
operationFolders: { type: "array", items: { type: "string", minLength: 1 } },
|
|
1315
|
+
domainFiles: { type: "array", items: { type: "string", minLength: 1 } },
|
|
1316
|
+
clients: { type: "array", items: { type: "string", minLength: 1 } }
|
|
1317
|
+
}
|
|
1318
|
+
},
|
|
1229
1319
|
backendPackages: { type: "array", items: { type: "string" } },
|
|
1230
1320
|
entrypoints: { type: "array", items: { type: "string" } },
|
|
1231
1321
|
appPackages: { type: "array", items: { type: "string" } },
|
|
@@ -1244,7 +1334,8 @@ var boundariesImports = defineRule7({
|
|
|
1244
1334
|
const file = context.filename.replaceAll("\\", "/");
|
|
1245
1335
|
const roots = [...options.web, ...options.backend].sort((a, b) => b.length - a.length);
|
|
1246
1336
|
const owner = roots.find((path) => boundariesContains(path, file));
|
|
1247
|
-
const
|
|
1337
|
+
const conventions = boundariesConventionsOf(options);
|
|
1338
|
+
const target = boundariesResolveTarget(options, file, source, conventions.services);
|
|
1248
1339
|
const web = owner && options.web.includes(owner) ? owner : undefined;
|
|
1249
1340
|
const backendImport = options.backend.some((path) => boundariesContains(path, target)) || boundariesMatchesImport(options.backendPackages, source);
|
|
1250
1341
|
const imported = { options, file, source, target, typeOnly, reExport, roots, owner, web, backendImport };
|
|
@@ -1253,13 +1344,16 @@ var boundariesImports = defineRule7({
|
|
|
1253
1344
|
const serviceFolders = roots.map((path) => boundariesServiceFolder(options, path));
|
|
1254
1345
|
const serviceTarget = serviceFolders.find((folder) => boundariesContains(folder, target));
|
|
1255
1346
|
const serviceFile = serviceFolders.find((folder) => boundariesContains(folder, file));
|
|
1256
|
-
const
|
|
1347
|
+
const implementation = `(?:\\.${boundariesAny(conventions.implementations)}|-${boundariesAny(conventions.operations)}\\.|/${boundariesAny(conventions.operationFolders)}/)`;
|
|
1348
|
+
const services = optionsEscape(conventions.services);
|
|
1349
|
+
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)));
|
|
1350
|
+
const registry = `/${optionsEscape(conventions.registry)}(?:\\.ts)?$`;
|
|
1257
1351
|
const reason = [
|
|
1258
1352
|
web && backendImport && !options.entrypoints.includes(source) ? "frontend code uses only public backend entry points" : undefined,
|
|
1259
1353
|
inPackage && appTarget ? "shared packages do not import app internals" : undefined,
|
|
1260
|
-
(file.includes(
|
|
1354
|
+
(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
1355
|
domainContract ? "domain types/constants use type-only imports from implementations" : undefined,
|
|
1262
|
-
file.endsWith(
|
|
1356
|
+
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
1357
|
boundariesPrivateDomain(imported),
|
|
1264
1358
|
boundariesPortableLib(imported)
|
|
1265
1359
|
].findLast((candidate) => candidate !== undefined);
|
|
@@ -1300,7 +1394,8 @@ var boundariesImports = defineRule7({
|
|
|
1300
1394
|
TSImportType(node) {
|
|
1301
1395
|
const options = { ...boundariesImportsDefaults, ...optionsFirst(context) };
|
|
1302
1396
|
const file = context.filename.replaceAll("\\", "/");
|
|
1303
|
-
|
|
1397
|
+
const lib = boundariesPortableFolder(options);
|
|
1398
|
+
if (lib && options.web.some((path) => file.startsWith(`${path}/${lib}/`) && boundariesServiceFolder(options, path) !== `${path}/${lib}`)) {
|
|
1304
1399
|
check(node, node.source.value, true);
|
|
1305
1400
|
}
|
|
1306
1401
|
}
|
|
@@ -1344,6 +1439,7 @@ var boundariesNext = defineRule8({
|
|
|
1344
1439
|
},
|
|
1345
1440
|
createOnce(context) {
|
|
1346
1441
|
let options;
|
|
1442
|
+
let conventions = boundariesConventionsFor({});
|
|
1347
1443
|
let file = "";
|
|
1348
1444
|
let server = false;
|
|
1349
1445
|
let client = false;
|
|
@@ -1355,12 +1451,12 @@ var boundariesNext = defineRule8({
|
|
|
1355
1451
|
return;
|
|
1356
1452
|
}
|
|
1357
1453
|
const target = boundariesResolveTarget(options, file, source);
|
|
1358
|
-
const clientOnly =
|
|
1454
|
+
const clientOnly = boundariesIsClientOnly(conventions, target) || boundariesMatchesImport(options.clientPackages, source) || boundariesMatchesImport(options.client, source);
|
|
1359
1455
|
if (clientOnly && (server || shared)) {
|
|
1360
1456
|
boundariesReport(context, node, source, "browser-only dependencies do not belong in server, RSC or shared modules");
|
|
1361
1457
|
return;
|
|
1362
1458
|
}
|
|
1363
|
-
const serverOnly = boundariesIsServerPlatformImport(source, options.serverPackages) || boundariesIsBackendImport(options, target, source) ||
|
|
1459
|
+
const serverOnly = boundariesIsServerPlatformImport(source, options.serverPackages) || boundariesIsBackendImport(options, target, source) || boundariesHasSuffix([...conventions.server, ...conventions.rsc], target, true) || boundariesMatchesImport(options.server, source);
|
|
1364
1460
|
if (!serverOnly || server) {
|
|
1365
1461
|
return;
|
|
1366
1462
|
}
|
|
@@ -1369,6 +1465,7 @@ var boundariesNext = defineRule8({
|
|
|
1369
1465
|
return {
|
|
1370
1466
|
Program(program) {
|
|
1371
1467
|
options = { ...boundariesNextDefaults, ...optionsFirst(context) };
|
|
1468
|
+
conventions = boundariesConventionsFor(options);
|
|
1372
1469
|
reactNamespaces.clear();
|
|
1373
1470
|
file = context.filename.replaceAll("\\", "/");
|
|
1374
1471
|
const directives = new Map;
|
|
@@ -1379,9 +1476,9 @@ var boundariesNext = defineRule8({
|
|
|
1379
1476
|
}
|
|
1380
1477
|
const useServer = directives.get("use server");
|
|
1381
1478
|
const useClient = directives.get("use client");
|
|
1382
|
-
const serverName =
|
|
1383
|
-
const clientName =
|
|
1384
|
-
shared = boundariesIsSharedFile(options, file);
|
|
1479
|
+
const serverName = boundariesHasSuffix([...conventions.server, ...conventions.rsc, ...conventions.actions], file);
|
|
1480
|
+
const clientName = boundariesHasSuffix(conventions.client, file);
|
|
1481
|
+
shared = boundariesIsSharedFile(options, conventions, file);
|
|
1385
1482
|
server = !useClient && (serverName || Boolean(useServer));
|
|
1386
1483
|
client = clientName || Boolean(useClient);
|
|
1387
1484
|
if (useClient && serverName) {
|
|
@@ -1398,7 +1495,7 @@ var boundariesNext = defineRule8({
|
|
|
1398
1495
|
data: { directive: "use server", side: "client" }
|
|
1399
1496
|
});
|
|
1400
1497
|
}
|
|
1401
|
-
if (
|
|
1498
|
+
if (boundariesHasSuffix(conventions.actions, file) && !useServer) {
|
|
1402
1499
|
context.report({ node: program, messageId: "action" });
|
|
1403
1500
|
}
|
|
1404
1501
|
},
|
|
@@ -1428,10 +1525,7 @@ var boundariesNext = defineRule8({
|
|
|
1428
1525
|
boundariesReport(context, node, `${node.object.name}.${name}`, boundariesClientHookReason);
|
|
1429
1526
|
}
|
|
1430
1527
|
},
|
|
1431
|
-
...
|
|
1432
|
-
ImportExpression(node) {
|
|
1433
|
-
boundariesImportExpression(context, node, Boolean(options.dynamic?.includes(file)), (source) => check(node, source, false));
|
|
1434
|
-
},
|
|
1528
|
+
...boundariesRuntimeImportVisitors(context, check, () => Boolean(options.dynamic?.includes(file))),
|
|
1435
1529
|
"Program:exit"() {
|
|
1436
1530
|
const scope = context.sourceCode.scopeManager.scopes.at(0);
|
|
1437
1531
|
if (!scope) {
|
|
@@ -1548,11 +1642,9 @@ var boundariesTanstackDefaults = {
|
|
|
1548
1642
|
hookPackages: ["react", "@tanstack/react-query", "@tanstack/react-router"],
|
|
1549
1643
|
startPackages: ["@tanstack/react-start"],
|
|
1550
1644
|
rscPackages: ["@tanstack/react-start/rsc", "@tanstack/react-start-rsc"],
|
|
1551
|
-
renderers: ["renderServerComponent", "renderToReadableStream"]
|
|
1645
|
+
renderers: ["renderServerComponent", "renderToReadableStream"],
|
|
1646
|
+
serverHooks: ["useId"]
|
|
1552
1647
|
};
|
|
1553
|
-
function boundariesIsClientHook(name) {
|
|
1554
|
-
return /^use[A-Z]/.test(name) && name !== "useId";
|
|
1555
|
-
}
|
|
1556
1648
|
var boundariesTanstack = defineRule10({
|
|
1557
1649
|
meta: {
|
|
1558
1650
|
type: "problem",
|
|
@@ -1565,7 +1657,8 @@ var boundariesTanstack = defineRule10({
|
|
|
1565
1657
|
rpc: { type: "array", items: { type: "string" } },
|
|
1566
1658
|
startPackages: { type: "array", items: { type: "string" } },
|
|
1567
1659
|
rscPackages: { type: "array", items: { type: "string" } },
|
|
1568
|
-
renderers: { type: "array", items: { type: "string", minLength: 1 }, default: boundariesTanstackDefaults.renderers }
|
|
1660
|
+
renderers: { type: "array", items: { type: "string", minLength: 1 }, default: boundariesTanstackDefaults.renderers },
|
|
1661
|
+
serverHooks: { type: "array", items: { type: "string", minLength: 1 }, default: boundariesTanstackDefaults.serverHooks }
|
|
1569
1662
|
}
|
|
1570
1663
|
}
|
|
1571
1664
|
],
|
|
@@ -1573,6 +1666,7 @@ var boundariesTanstack = defineRule10({
|
|
|
1573
1666
|
},
|
|
1574
1667
|
createOnce(context) {
|
|
1575
1668
|
let options;
|
|
1669
|
+
let conventions = boundariesConventionsFor({});
|
|
1576
1670
|
let file = "";
|
|
1577
1671
|
let serverFile = false;
|
|
1578
1672
|
let rscFile = false;
|
|
@@ -1581,6 +1675,7 @@ var boundariesTanstack = defineRule10({
|
|
|
1581
1675
|
let referenced;
|
|
1582
1676
|
let imports = new Map;
|
|
1583
1677
|
const boundaries = [];
|
|
1678
|
+
const clientHook = (name) => /^use[A-Z]/.test(name) && !options.serverHooks.includes(name);
|
|
1584
1679
|
function imported(node) {
|
|
1585
1680
|
const entry = imports.get(node.name);
|
|
1586
1681
|
const variable = entry ? boundariesDeclaredVariable(context.sourceCode.getScope(node), node.name) : null;
|
|
@@ -1623,7 +1718,7 @@ var boundariesTanstack = defineRule10({
|
|
|
1623
1718
|
return;
|
|
1624
1719
|
}
|
|
1625
1720
|
const target = boundariesResolveTarget(options, file, source);
|
|
1626
|
-
const clientImport =
|
|
1721
|
+
const clientImport = boundariesIsClientOnly(conventions, target) || boundariesMatchesImport(options.clientPackages, source) || boundariesMatchesImport(options.client, source);
|
|
1627
1722
|
if (clientImport && (serverFile || rscFile || shared) && !clientSide(node.start)) {
|
|
1628
1723
|
const references = node.type === "ImportDeclaration" ? runtimeReferences(node) : [];
|
|
1629
1724
|
if (shared || references.length === 0 || !references.every(clientSide)) {
|
|
@@ -1631,7 +1726,7 @@ var boundariesTanstack = defineRule10({
|
|
|
1631
1726
|
}
|
|
1632
1727
|
return;
|
|
1633
1728
|
}
|
|
1634
|
-
const serverImport = boundariesIsServerPlatformImport(source, options.serverPackages) || boundariesIsBackendImport(options, target, source) ||
|
|
1729
|
+
const serverImport = boundariesIsServerPlatformImport(source, options.serverPackages) || boundariesIsBackendImport(options, target, source) || boundariesHasSuffix(conventions.server, target, true) || boundariesMatchesImport(options.server, source);
|
|
1635
1730
|
if (!renderer && !serverImport) {
|
|
1636
1731
|
return;
|
|
1637
1732
|
}
|
|
@@ -1659,10 +1754,11 @@ var boundariesTanstack = defineRule10({
|
|
|
1659
1754
|
return {
|
|
1660
1755
|
Program(program) {
|
|
1661
1756
|
options = { ...boundariesTanstackDefaults, ...optionsFirst(context) };
|
|
1757
|
+
conventions = boundariesConventionsFor(options);
|
|
1662
1758
|
file = context.filename.replaceAll("\\", "/");
|
|
1663
|
-
serverFile =
|
|
1664
|
-
rscFile =
|
|
1665
|
-
shared = boundariesIsSharedFile(options, file);
|
|
1759
|
+
serverFile = boundariesHasSuffix(conventions.server, file);
|
|
1760
|
+
rscFile = boundariesHasSuffix(conventions.rsc, file);
|
|
1761
|
+
shared = boundariesIsSharedFile(options, conventions, file);
|
|
1666
1762
|
runtimeIdentifiers.clear();
|
|
1667
1763
|
referenced = undefined;
|
|
1668
1764
|
boundaries.length = 0;
|
|
@@ -1729,7 +1825,7 @@ var boundariesTanstack = defineRule10({
|
|
|
1729
1825
|
return;
|
|
1730
1826
|
}
|
|
1731
1827
|
for (const specifier of node.specifiers) {
|
|
1732
|
-
if (specifier.type !== "ImportSpecifier" || specifier.importKind === "type" || specifier.imported.type !== "Identifier" || !
|
|
1828
|
+
if (specifier.type !== "ImportSpecifier" || specifier.importKind === "type" || specifier.imported.type !== "Identifier" || !clientHook(specifier.imported.name)) {
|
|
1733
1829
|
continue;
|
|
1734
1830
|
}
|
|
1735
1831
|
const references = runtimeReferences(node, specifier.local.name);
|
|
@@ -1738,10 +1834,7 @@ var boundariesTanstack = defineRule10({
|
|
|
1738
1834
|
}
|
|
1739
1835
|
}
|
|
1740
1836
|
},
|
|
1741
|
-
...
|
|
1742
|
-
ImportExpression(node) {
|
|
1743
|
-
boundariesImportExpression(context, node, Boolean(options.dynamic?.includes(file)), (source) => check(node, source, false));
|
|
1744
|
-
},
|
|
1837
|
+
...boundariesRuntimeImportVisitors(context, check, () => Boolean(options.dynamic?.includes(file))),
|
|
1745
1838
|
MemberExpression(node) {
|
|
1746
1839
|
const name = boundariesMemberName(node);
|
|
1747
1840
|
if (!name || node.object.type !== "Identifier") {
|
|
@@ -1751,7 +1844,7 @@ var boundariesTanstack = defineRule10({
|
|
|
1751
1844
|
if (kind === "rsc-renderers" && options.renderers.includes(name) && (shared || !serverPosition(node.start, true))) {
|
|
1752
1845
|
boundariesReport(context, node, name, "server renderers require a server execution boundary");
|
|
1753
1846
|
}
|
|
1754
|
-
if ((serverFile || rscFile) && kind === "client-hooks" &&
|
|
1847
|
+
if ((serverFile || rscFile) && kind === "client-hooks" && clientHook(name) && !clientSide(node.start)) {
|
|
1755
1848
|
boundariesReport(context, node, name, boundariesClientHookReason2);
|
|
1756
1849
|
}
|
|
1757
1850
|
},
|
|
@@ -2230,6 +2323,7 @@ var codeInline = defineRule16({
|
|
|
2230
2323
|
properties: {
|
|
2231
2324
|
max: { type: "integer", minimum: 1, default: 160 },
|
|
2232
2325
|
nesting: { type: "integer", minimum: 1, default: 2 },
|
|
2326
|
+
callbacks: { type: "boolean", default: false },
|
|
2233
2327
|
allow: optionsPatternSchema,
|
|
2234
2328
|
keep: optionsPatternSchema,
|
|
2235
2329
|
services: { type: "array", items: { type: "string" } }
|
|
@@ -2258,7 +2352,8 @@ var codeInline = defineRule16({
|
|
|
2258
2352
|
return;
|
|
2259
2353
|
}
|
|
2260
2354
|
if (uses.length === 1) {
|
|
2261
|
-
|
|
2355
|
+
const use = uses[0].identifier;
|
|
2356
|
+
if (init && (codeInlineEffectBetween(context.sourceCode.ast, node.range[1], use.range[0]) || codeInlineEnclosingCalls(use) + astCallDepth(init, options.callbacks ?? false) > (options.nesting ?? 2))) {
|
|
2262
2357
|
return;
|
|
2263
2358
|
}
|
|
2264
2359
|
context.report({
|
|
@@ -2270,7 +2365,12 @@ var codeInline = defineRule16({
|
|
|
2270
2365
|
}
|
|
2271
2366
|
const arrow = init?.type === "ArrowFunctionExpression" && init.body.type !== "BlockStatement" && !astSome(init.body, astIsInlineFunction);
|
|
2272
2367
|
const call = init?.type === "CallExpression" && !astSome(init.arguments, astIsInlineFunction) && astCallDepth(init) <= 2;
|
|
2273
|
-
|
|
2368
|
+
const text = context.sourceCode.text;
|
|
2369
|
+
const fits = init !== null && uses.every(({ identifier }) => {
|
|
2370
|
+
const line = text.slice(sourceLineStart(text, identifier.range[0]), sourceLineEnd(text, identifier.range[0]));
|
|
2371
|
+
return line.length - id.name.length + context.sourceCode.getText(init).length <= (options.max ?? 160) && codeInlineEnclosingCalls(identifier) + astCallDepth(init, options.callbacks ?? false) <= (options.nesting ?? 2);
|
|
2372
|
+
});
|
|
2373
|
+
if (!optionsPatternsTest(kept, id.name) && (arrow || call) && fits) {
|
|
2274
2374
|
context.report({
|
|
2275
2375
|
node: id,
|
|
2276
2376
|
messageId: "trivial",
|
|
@@ -2298,7 +2398,7 @@ var codeInline = defineRule16({
|
|
|
2298
2398
|
deferred ||= current.type === "FunctionDeclaration" || astIsInlineFunction(current);
|
|
2299
2399
|
}
|
|
2300
2400
|
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])) {
|
|
2401
|
+
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
2402
|
return;
|
|
2303
2403
|
}
|
|
2304
2404
|
context.report({
|
|
@@ -3232,7 +3332,8 @@ var commentsCode = defineRule26({
|
|
|
3232
3332
|
why: { anyOf: [{ type: "boolean" }, { type: "string", minLength: 1 }], default: true },
|
|
3233
3333
|
allow: { type: "array", items: { type: "string" } },
|
|
3234
3334
|
jsdoc: { type: "boolean", default: false },
|
|
3235
|
-
members: { type: "string", enum: ["off", "also", "only"] }
|
|
3335
|
+
members: { type: "string", enum: ["off", "also", "only"] },
|
|
3336
|
+
steps: { type: "boolean", default: false }
|
|
3236
3337
|
}
|
|
3237
3338
|
}
|
|
3238
3339
|
],
|
|
@@ -3247,9 +3348,31 @@ var commentsCode = defineRule26({
|
|
|
3247
3348
|
let allowJsdoc = false;
|
|
3248
3349
|
let allowed = [];
|
|
3249
3350
|
let members = "off";
|
|
3351
|
+
let steps = false;
|
|
3352
|
+
let functionDepth = 0;
|
|
3250
3353
|
const memberComments = new Set;
|
|
3354
|
+
const stepStarts = new Set;
|
|
3355
|
+
function enterFunction() {
|
|
3356
|
+
functionDepth++;
|
|
3357
|
+
}
|
|
3358
|
+
function exitFunction() {
|
|
3359
|
+
functionDepth--;
|
|
3360
|
+
}
|
|
3361
|
+
function addSteps(nodes, inFunction) {
|
|
3362
|
+
if (steps && inFunction) {
|
|
3363
|
+
for (const node of nodes) {
|
|
3364
|
+
stepStarts.add(node.range[0]);
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
function isStep(comment) {
|
|
3369
|
+
const source = context.sourceCode;
|
|
3370
|
+
const before = source.getTokenBefore(comment, { includeComments: true });
|
|
3371
|
+
const after = source.getTokenAfter(comment, { includeComments: true });
|
|
3372
|
+
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]);
|
|
3373
|
+
}
|
|
3251
3374
|
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()));
|
|
3375
|
+
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
3376
|
}
|
|
3254
3377
|
function checkMember(node) {
|
|
3255
3378
|
const parent = node.parent;
|
|
@@ -3273,7 +3396,10 @@ var commentsCode = defineRule26({
|
|
|
3273
3396
|
allowJsdoc = options.jsdoc ?? false;
|
|
3274
3397
|
allowed = (options.allow ?? []).map((pattern) => new RegExp(pattern));
|
|
3275
3398
|
members = options.members ?? "off";
|
|
3399
|
+
steps = options.steps ?? false;
|
|
3400
|
+
functionDepth = 0;
|
|
3276
3401
|
memberComments.clear();
|
|
3402
|
+
stepStarts.clear();
|
|
3277
3403
|
},
|
|
3278
3404
|
TSPropertySignature: checkMember,
|
|
3279
3405
|
TSMethodSignature: checkMember,
|
|
@@ -3282,6 +3408,17 @@ var commentsCode = defineRule26({
|
|
|
3282
3408
|
TSConstructSignatureDeclaration: checkMember,
|
|
3283
3409
|
Property: checkMember,
|
|
3284
3410
|
SpreadElement: checkMember,
|
|
3411
|
+
FunctionDeclaration: enterFunction,
|
|
3412
|
+
"FunctionDeclaration:exit": exitFunction,
|
|
3413
|
+
FunctionExpression: enterFunction,
|
|
3414
|
+
"FunctionExpression:exit": exitFunction,
|
|
3415
|
+
ArrowFunctionExpression: enterFunction,
|
|
3416
|
+
"ArrowFunctionExpression:exit": exitFunction,
|
|
3417
|
+
BlockStatement: (node) => addSteps(node.body, functionDepth > 0),
|
|
3418
|
+
SwitchCase: (node) => addSteps(node.consequent, functionDepth > 0),
|
|
3419
|
+
IfStatement: (node) => addSteps([node.consequent, ...node.alternate ? [node.alternate] : []], functionDepth > 0),
|
|
3420
|
+
"ForStatement, ForInStatement, ForOfStatement, WhileStatement, DoWhileStatement, LabeledStatement": (node) => addSteps([node.body], functionDepth > 0),
|
|
3421
|
+
ObjectExpression: (node) => addSteps(node.properties, true),
|
|
3285
3422
|
"Program:exit"(program) {
|
|
3286
3423
|
const text = context.sourceCode.text;
|
|
3287
3424
|
const found = [];
|
|
@@ -3527,6 +3664,7 @@ var effectLayerFile = defineRule29({
|
|
|
3527
3664
|
layer: { type: "string" },
|
|
3528
3665
|
make: { type: "string" },
|
|
3529
3666
|
prefix: { type: "string" },
|
|
3667
|
+
suffix: { type: "string" },
|
|
3530
3668
|
builders: { type: "array", items: { type: "string" } },
|
|
3531
3669
|
layers: optionsPatternSchema,
|
|
3532
3670
|
services: optionsPatternSchema,
|
|
@@ -3539,8 +3677,8 @@ var effectLayerFile = defineRule29({
|
|
|
3539
3677
|
notLayer: "`{{layer}}` is built with Layer: a Layer call, a Layer pipe, or a function returning one.",
|
|
3540
3678
|
builderName: "The Effect that builds the service is named `{{make}}`, one per layer file.",
|
|
3541
3679
|
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.",
|
|
3680
|
+
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.",
|
|
3681
|
+
localLayer: "A Layer other than `{{layer}}` is named {{prefix}}<Role>{{suffix}} and is not exported.",
|
|
3544
3682
|
makeReturn: "`{{make}}` returns the service through its tag (Tag.of({ … })), a Context, or a Layer.",
|
|
3545
3683
|
inlineBuilder: "Build this layer from `{{make}}`, not an inline Effect: Layer.effect(Tag, {{make}}). One built layer per file."
|
|
3546
3684
|
}
|
|
@@ -3549,6 +3687,7 @@ var effectLayerFile = defineRule29({
|
|
|
3549
3687
|
let layer = "layer";
|
|
3550
3688
|
let make = "make";
|
|
3551
3689
|
let prefix = "layer";
|
|
3690
|
+
let suffix = "";
|
|
3552
3691
|
let builders = new Set(effectBuilders);
|
|
3553
3692
|
let layers = [];
|
|
3554
3693
|
let services = [];
|
|
@@ -3565,14 +3704,15 @@ var effectLayerFile = defineRule29({
|
|
|
3565
3704
|
const options = optionsFirst(context, {});
|
|
3566
3705
|
layer = options.layer ?? "layer";
|
|
3567
3706
|
make = options.make ?? "make";
|
|
3568
|
-
|
|
3707
|
+
suffix = options.suffix ?? "";
|
|
3708
|
+
prefix = options.prefix ?? (suffix ? "" : "layer");
|
|
3569
3709
|
builders = new Set(options.builders ?? effectBuilders);
|
|
3570
3710
|
constructors = new Set(options.constructors ?? effectLayerFileDefaultConstructors);
|
|
3571
|
-
layers = optionsPatterns(options.layers ?? effectLayerFileDefaultLayers);
|
|
3711
|
+
layers = optionsPatterns(options.layers ?? [...effectLayerFileDefaultLayers, ...suffix ? [`(?:^|\\.)\\w+${optionsEscape(suffix)}$`] : []]);
|
|
3572
3712
|
services = [...optionsPatterns(options.services ?? effectLayerFileDefaultServices), ...layers];
|
|
3573
3713
|
},
|
|
3574
3714
|
Program(program) {
|
|
3575
|
-
const data = { layer, make, prefix };
|
|
3715
|
+
const data = { layer, make, prefix, suffix };
|
|
3576
3716
|
const declarators = program.body.flatMap((statement) => {
|
|
3577
3717
|
const declaration = astDeclaration(statement);
|
|
3578
3718
|
const exported = statement.type === "ExportNamedDeclaration";
|
|
@@ -3632,7 +3772,8 @@ var effectLayerFile = defineRule29({
|
|
|
3632
3772
|
data
|
|
3633
3773
|
});
|
|
3634
3774
|
}
|
|
3635
|
-
|
|
3775
|
+
const role = name.endsWith(suffix) ? name.slice(prefix.length, name.length - suffix.length) : "";
|
|
3776
|
+
if (isLayer && name !== layer && (exported || !name.startsWith(prefix) || !/^[A-Z]\w*$/.test(role))) {
|
|
3636
3777
|
context.report({
|
|
3637
3778
|
node,
|
|
3638
3779
|
messageId: "localLayer",
|
|
@@ -3669,7 +3810,7 @@ var effectLayerFile = defineRule29({
|
|
|
3669
3810
|
context.report({
|
|
3670
3811
|
node: argument,
|
|
3671
3812
|
messageId: "inlineBuilder",
|
|
3672
|
-
data: { layer, make, prefix }
|
|
3813
|
+
data: { layer, make, prefix, suffix }
|
|
3673
3814
|
});
|
|
3674
3815
|
}
|
|
3675
3816
|
}
|
|
@@ -4548,9 +4689,6 @@ var filesDomainConstants = defineRule36({
|
|
|
4548
4689
|
// src/rules/files/files-exports.ts
|
|
4549
4690
|
import { defineRule as defineRule37 } from "@oxlint/plugins";
|
|
4550
4691
|
import { posix as posix2 } from "node:path";
|
|
4551
|
-
function escapeRegExp(value) {
|
|
4552
|
-
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4553
|
-
}
|
|
4554
4692
|
var filesExports = defineRule37({
|
|
4555
4693
|
meta: {
|
|
4556
4694
|
type: "problem",
|
|
@@ -4612,7 +4750,7 @@ var filesExports = defineRule37({
|
|
|
4612
4750
|
const camelDomain = namingCamelCase(domain);
|
|
4613
4751
|
const allowed = (options.names ?? []).map((template) => template.replaceAll("{Domain}", pascalDomain).replaceAll("{domain}", camelDomain));
|
|
4614
4752
|
const patterns = (options.patterns ?? []).map((template) => {
|
|
4615
|
-
const source = template.replaceAll("{Domain}",
|
|
4753
|
+
const source = template.replaceAll("{Domain}", optionsEscape(pascalDomain)).replaceAll("{domain}", optionsEscape(camelDomain));
|
|
4616
4754
|
return new RegExp(`^(?:${source})$`);
|
|
4617
4755
|
});
|
|
4618
4756
|
const allowedLabel = [...allowed, ...patterns.map((pattern) => pattern.toString())].join(", ");
|
|
@@ -5443,6 +5581,12 @@ var layoutCallArguments = defineRule46({
|
|
|
5443
5581
|
type: "boolean",
|
|
5444
5582
|
default: true
|
|
5445
5583
|
},
|
|
5584
|
+
pipe: {
|
|
5585
|
+
type: "object",
|
|
5586
|
+
additionalProperties: false,
|
|
5587
|
+
required: ["min"],
|
|
5588
|
+
properties: { min: { type: "integer", minimum: 2 }, methods: { type: "array", items: { type: "string" } } }
|
|
5589
|
+
},
|
|
5446
5590
|
hug: {
|
|
5447
5591
|
type: "object",
|
|
5448
5592
|
additionalProperties: false,
|
|
@@ -5465,6 +5609,7 @@ var layoutCallArguments = defineRule46({
|
|
|
5465
5609
|
],
|
|
5466
5610
|
messages: {
|
|
5467
5611
|
multiline: "A call with {{min}}+ arguments puts each argument on its own line.",
|
|
5612
|
+
pipe: "A pipe with {{min}}+ steps puts each step on its own line.",
|
|
5468
5613
|
hug: "Put every argument on its own line. Only a last callback, object or array may hug the call, after simple arguments.",
|
|
5469
5614
|
prefer: "Hug the last argument: it is a callback, object or array after simple arguments.",
|
|
5470
5615
|
join: "This fits on one line, so keep it on one line."
|
|
@@ -5474,11 +5619,13 @@ var layoutCallArguments = defineRule46({
|
|
|
5474
5619
|
let options = {};
|
|
5475
5620
|
let minArguments = 3;
|
|
5476
5621
|
let indentUnit = sourceIndentUnit();
|
|
5477
|
-
|
|
5622
|
+
let pipeMethods = new Set;
|
|
5623
|
+
const isPipe = (node) => node.callee.type === "MemberExpression" && pipeMethods.has(astMemberName(node.callee) ?? "");
|
|
5624
|
+
const split = (node, min, messageId) => {
|
|
5478
5625
|
const args = node.arguments;
|
|
5479
5626
|
const last = args.at(-1);
|
|
5480
5627
|
const source = context.sourceCode;
|
|
5481
|
-
if (args.length <
|
|
5628
|
+
if (args.length < min || !last || args.some((arg) => (arg.type === "ArrowFunctionExpression" || arg.type === "FunctionExpression") && arg.loc.start.line !== arg.loc.end.line)) {
|
|
5482
5629
|
return;
|
|
5483
5630
|
}
|
|
5484
5631
|
const text = source.text;
|
|
@@ -5500,8 +5647,8 @@ var layoutCallArguments = defineRule46({
|
|
|
5500
5647
|
const argumentIndent = `${baseIndent}${indentUnit}`;
|
|
5501
5648
|
context.report({
|
|
5502
5649
|
node,
|
|
5503
|
-
messageId
|
|
5504
|
-
data: { min: String(
|
|
5650
|
+
messageId,
|
|
5651
|
+
data: { min: String(min) },
|
|
5505
5652
|
fix: unsafe ? undefined : (fixer) => gaps.map((gap, index) => {
|
|
5506
5653
|
if (index === 0) {
|
|
5507
5654
|
return fixer.replaceTextRange(gap, `${newline}${argumentIndent}`);
|
|
@@ -5519,11 +5666,23 @@ var layoutCallArguments = defineRule46({
|
|
|
5519
5666
|
const last = args.at(-1);
|
|
5520
5667
|
const open = source.getTokenAfter(node.typeArguments ?? node.callee);
|
|
5521
5668
|
const close = source.getLastToken(node);
|
|
5522
|
-
if (!first || !last || !open || !close ||
|
|
5669
|
+
if (!first || !last || !open || !close || source.getCommentsInside(node).length > 0) {
|
|
5670
|
+
return;
|
|
5671
|
+
}
|
|
5672
|
+
if (args.every(astIsOneLine)) {
|
|
5673
|
+
if (first.loc.start.line > open.loc.end.line && close.loc.start.line === last.loc.end.line) {
|
|
5674
|
+
const newline2 = sourceNewline(text);
|
|
5675
|
+
const base2 = sourceLineIndent(text, open.range[0]);
|
|
5676
|
+
context.report({
|
|
5677
|
+
node,
|
|
5678
|
+
messageId: "hug",
|
|
5679
|
+
fix: (fixer) => sourceRespace(fixer, text, sourceItemGaps(open.range[1], args, close.range[0]), (_, end) => `${newline2}${end ? base2 : `${base2}${indentUnit}`}`)
|
|
5680
|
+
});
|
|
5681
|
+
}
|
|
5523
5682
|
return;
|
|
5524
5683
|
}
|
|
5525
5684
|
const leading = args.slice(0, -1);
|
|
5526
|
-
const canHug = !
|
|
5685
|
+
const canHug = !isPipe(node) && leading.every((arg) => astIsOneLine(arg) && astCallDepth(arg) <= 1) && layoutCallArgumentsHuggable(last);
|
|
5527
5686
|
const gaps = sourceItemGaps(open.range[1], args, close.range[0]);
|
|
5528
5687
|
if (first.loc.start.line > open.loc.end.line) {
|
|
5529
5688
|
const head = [...leading.map((arg) => source.getText(arg)), text.slice(last.range[0], sourceLineEnd(text, last.range[0]))];
|
|
@@ -5569,13 +5728,15 @@ var layoutCallArguments = defineRule46({
|
|
|
5569
5728
|
};
|
|
5570
5729
|
const call = (node) => {
|
|
5571
5730
|
const source = context.sourceCode;
|
|
5572
|
-
if (options.
|
|
5573
|
-
split(node);
|
|
5731
|
+
if (options.pipe && isPipe(node)) {
|
|
5732
|
+
split(node, options.pipe.min, "pipe");
|
|
5733
|
+
} else if (options.split !== false) {
|
|
5734
|
+
split(node, minArguments, "multiline");
|
|
5574
5735
|
}
|
|
5575
5736
|
if (options.hug) {
|
|
5576
5737
|
hug(node, options.hug.max);
|
|
5577
5738
|
}
|
|
5578
|
-
if (options.join && !(options.join.keep ?? []).includes(source.getText(node.callee)) && !(
|
|
5739
|
+
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)) {
|
|
5579
5740
|
join3(node, node.arguments, source.getTokenAfter(node.typeArguments ?? node.callee), source.getLastToken(node));
|
|
5580
5741
|
}
|
|
5581
5742
|
};
|
|
@@ -5593,6 +5754,7 @@ var layoutCallArguments = defineRule46({
|
|
|
5593
5754
|
options = optionsFirst(context, {});
|
|
5594
5755
|
minArguments = options.min ?? 3;
|
|
5595
5756
|
indentUnit = sourceIndentUnit(options.indent);
|
|
5757
|
+
pipeMethods = new Set(options.pipe?.methods ?? ["pipe"]);
|
|
5596
5758
|
},
|
|
5597
5759
|
CallExpression: call,
|
|
5598
5760
|
NewExpression: call,
|
|
@@ -5728,12 +5890,19 @@ var layoutCallArray = defineRule47({
|
|
|
5728
5890
|
|
|
5729
5891
|
// src/rules/layout/layout-call-depth.ts
|
|
5730
5892
|
import { defineRule as defineRule48 } from "@oxlint/plugins";
|
|
5731
|
-
function layoutCallDepthOuter(node) {
|
|
5893
|
+
function layoutCallDepthOuter(node, callbacks) {
|
|
5732
5894
|
const parent = node.parent;
|
|
5733
|
-
if (!parent || parent.type === "ArrowFunctionExpression" || parent.type === "FunctionExpression"
|
|
5895
|
+
if (!parent || !callbacks && (parent.type === "ArrowFunctionExpression" || parent.type === "FunctionExpression")) {
|
|
5734
5896
|
return;
|
|
5735
5897
|
}
|
|
5736
|
-
|
|
5898
|
+
if (parent.type.endsWith("Statement")) {
|
|
5899
|
+
let owner = parent;
|
|
5900
|
+
while (owner?.type.endsWith("Statement")) {
|
|
5901
|
+
owner = owner.parent;
|
|
5902
|
+
}
|
|
5903
|
+
return callbacks && owner && astIsInlineFunction(owner) ? layoutCallDepthOuter(owner, callbacks) : undefined;
|
|
5904
|
+
}
|
|
5905
|
+
return astIsCall(parent) && parent.arguments.some((argument) => argument === node) ? parent : layoutCallDepthOuter(parent, callbacks);
|
|
5737
5906
|
}
|
|
5738
5907
|
var layoutCallDepth = defineRule48({
|
|
5739
5908
|
meta: {
|
|
@@ -5742,7 +5911,7 @@ var layoutCallDepth = defineRule48({
|
|
|
5742
5911
|
{
|
|
5743
5912
|
type: "object",
|
|
5744
5913
|
additionalProperties: false,
|
|
5745
|
-
properties: { max: { type: "integer", minimum: 1, default: 2 } }
|
|
5914
|
+
properties: { max: { type: "integer", minimum: 1, default: 2 }, callbacks: { type: "boolean", default: false } }
|
|
5746
5915
|
}
|
|
5747
5916
|
],
|
|
5748
5917
|
messages: {
|
|
@@ -5751,10 +5920,11 @@ var layoutCallDepth = defineRule48({
|
|
|
5751
5920
|
},
|
|
5752
5921
|
createOnce(context) {
|
|
5753
5922
|
let max = 2;
|
|
5923
|
+
let callbacks = false;
|
|
5754
5924
|
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) {
|
|
5925
|
+
const depth = astCallDepth(node, callbacks);
|
|
5926
|
+
const outer = layoutCallDepthOuter(node, callbacks);
|
|
5927
|
+
if (!astIsOneLine(node) || depth <= max || outer !== undefined && astIsOneLine(outer) && astCallDepth(outer, callbacks) > max) {
|
|
5758
5928
|
return;
|
|
5759
5929
|
}
|
|
5760
5930
|
context.report({
|
|
@@ -5765,7 +5935,9 @@ var layoutCallDepth = defineRule48({
|
|
|
5765
5935
|
};
|
|
5766
5936
|
return {
|
|
5767
5937
|
before() {
|
|
5768
|
-
|
|
5938
|
+
const options = optionsFirst(context, {});
|
|
5939
|
+
max = options.max ?? 2;
|
|
5940
|
+
callbacks = options.callbacks ?? false;
|
|
5769
5941
|
},
|
|
5770
5942
|
CallExpression: check,
|
|
5771
5943
|
NewExpression: check
|
|
@@ -6096,6 +6268,10 @@ var layoutObject = defineRule53({
|
|
|
6096
6268
|
min: { type: "integer", minimum: 1, default: 3 },
|
|
6097
6269
|
scope: { type: "string", enum: ["call-args", "all"], default: "call-args" },
|
|
6098
6270
|
indent: { ...sourceIndentSchema, default: 2 },
|
|
6271
|
+
params: { type: "boolean", default: false },
|
|
6272
|
+
nested: { type: "boolean", default: false },
|
|
6273
|
+
siblings: { type: "boolean", default: false },
|
|
6274
|
+
inner: { type: "integer", minimum: 1 },
|
|
6099
6275
|
join: {
|
|
6100
6276
|
type: "object",
|
|
6101
6277
|
additionalProperties: false,
|
|
@@ -6111,6 +6287,11 @@ var layoutObject = defineRule53({
|
|
|
6111
6287
|
messages: {
|
|
6112
6288
|
callArgs: "Object with {{min}}+ properties in a call should have each property on its own line.",
|
|
6113
6289
|
always: "Object with {{min}}+ properties should have each property on its own line.",
|
|
6290
|
+
nested: "An object holding an object or function should have each property on its own line.",
|
|
6291
|
+
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.",
|
|
6292
|
+
siblings: "A sibling call in this list splits its object; split this one too.",
|
|
6293
|
+
inner: "An object with {{inner}}+ entries inside a split object should have each entry on its own line.",
|
|
6294
|
+
params: "Destructured parameter with {{min}}+ properties should have each property on its own line.",
|
|
6114
6295
|
join: "This object fits on one line, so keep it on one line."
|
|
6115
6296
|
}
|
|
6116
6297
|
},
|
|
@@ -6121,6 +6302,119 @@ var layoutObject = defineRule53({
|
|
|
6121
6302
|
let newline = `
|
|
6122
6303
|
`;
|
|
6123
6304
|
let join3;
|
|
6305
|
+
let params = false;
|
|
6306
|
+
let nested = false;
|
|
6307
|
+
let siblings = false;
|
|
6308
|
+
let inner;
|
|
6309
|
+
const splitCounts = new WeakMap;
|
|
6310
|
+
function holdsNested(node) {
|
|
6311
|
+
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);
|
|
6312
|
+
}
|
|
6313
|
+
function insideOneLineObject(start) {
|
|
6314
|
+
for (let ancestor = start;ancestor && !ancestor.type.endsWith("Statement"); ancestor = ancestor.parent) {
|
|
6315
|
+
if (ancestor.type === "ObjectExpression" && astIsOneLine(ancestor)) {
|
|
6316
|
+
return true;
|
|
6317
|
+
}
|
|
6318
|
+
}
|
|
6319
|
+
return false;
|
|
6320
|
+
}
|
|
6321
|
+
function breaksAround(node) {
|
|
6322
|
+
for (let child = node, parent = node.parent;parent; child = parent, parent = parent.parent) {
|
|
6323
|
+
if (parent.type === "ConditionalExpression" || parent.type === "MemberExpression" && parent.object === child && astIsCall(child)) {
|
|
6324
|
+
return true;
|
|
6325
|
+
}
|
|
6326
|
+
if (astIsInlineFunction(parent) || parent.type.endsWith("Statement") || parent.type.endsWith("Declaration")) {
|
|
6327
|
+
return false;
|
|
6328
|
+
}
|
|
6329
|
+
}
|
|
6330
|
+
return false;
|
|
6331
|
+
}
|
|
6332
|
+
function keptByInner(node) {
|
|
6333
|
+
const parent = node.parent;
|
|
6334
|
+
return inner !== undefined && node.properties.length >= inner && parent.type === "Property" && parent.value === node && !astIsOneLine(parent.parent);
|
|
6335
|
+
}
|
|
6336
|
+
function splitSiblings(node) {
|
|
6337
|
+
const call = node.parent;
|
|
6338
|
+
const list = call.parent;
|
|
6339
|
+
if (!siblings || node.properties.length === 0 || !astIsCall(call) || list?.type !== "ArrayExpression") {
|
|
6340
|
+
return 0;
|
|
6341
|
+
}
|
|
6342
|
+
let counts = splitCounts.get(list);
|
|
6343
|
+
if (!counts) {
|
|
6344
|
+
counts = new Map;
|
|
6345
|
+
for (const element of list.elements) {
|
|
6346
|
+
if (!element || !astIsCall(element)) {
|
|
6347
|
+
continue;
|
|
6348
|
+
}
|
|
6349
|
+
for (const [index, argument] of element.arguments.entries()) {
|
|
6350
|
+
if (argument.type === "ObjectExpression" && !astIsOneLine(argument)) {
|
|
6351
|
+
const key = `${context.sourceCode.getText(element.callee)}#${index}`;
|
|
6352
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
6353
|
+
}
|
|
6354
|
+
}
|
|
6355
|
+
}
|
|
6356
|
+
splitCounts.set(list, counts);
|
|
6357
|
+
}
|
|
6358
|
+
return counts.get(`${context.sourceCode.getText(call.callee)}#${call.arguments.indexOf(node)}`) ?? 0;
|
|
6359
|
+
}
|
|
6360
|
+
function joinBlocked(node, call) {
|
|
6361
|
+
const callee = call && astIsCall(call) ? astDottedName(call.callee) : undefined;
|
|
6362
|
+
const kept = callee !== undefined && (join3?.keep ?? []).some((name) => callee === name || callee.endsWith(`.${name}`));
|
|
6363
|
+
return kept || keptByInner(node) || splitSiblings(node) > 1 || node.properties.length === 0 || !node.properties.every(layoutObjectIsFlat) || context.sourceCode.getCommentsInside(node).length > 0;
|
|
6364
|
+
}
|
|
6365
|
+
function reportSplit(node, properties, open, close, messageId) {
|
|
6366
|
+
const source = context.sourceCode;
|
|
6367
|
+
const text = source.text;
|
|
6368
|
+
const baseIndent = sourceLineIndent(text, open);
|
|
6369
|
+
const propertyIndent = `${baseIndent}${indentUnit}`;
|
|
6370
|
+
context.report({
|
|
6371
|
+
node,
|
|
6372
|
+
messageId,
|
|
6373
|
+
data: { min: String(minProperties), inner: String(inner ?? "") },
|
|
6374
|
+
fix: (fixer) => {
|
|
6375
|
+
if (messageId === "nestedName" || properties.length === 0 || source.getCommentsInside(node).length > 0 || !layoutObjectIsWhitespace(text.slice(open + 1, properties[0].range[0]))) {
|
|
6376
|
+
return null;
|
|
6377
|
+
}
|
|
6378
|
+
const gaps = [[open + 1, properties[0].range[0]]];
|
|
6379
|
+
for (const [index, property] of properties.entries()) {
|
|
6380
|
+
const next = properties.at(index + 1);
|
|
6381
|
+
const after = property.range[1];
|
|
6382
|
+
const limit = next ? next.range[0] : close;
|
|
6383
|
+
const between = text.slice(after, limit);
|
|
6384
|
+
const comma = between.indexOf(",");
|
|
6385
|
+
if (comma === -1) {
|
|
6386
|
+
if (next || !layoutObjectIsWhitespace(between)) {
|
|
6387
|
+
return null;
|
|
6388
|
+
}
|
|
6389
|
+
gaps.push([after, close]);
|
|
6390
|
+
continue;
|
|
6391
|
+
}
|
|
6392
|
+
if (!layoutObjectIsWhitespace(between.slice(0, comma)) || !layoutObjectIsWhitespace(between.slice(comma + 1))) {
|
|
6393
|
+
return null;
|
|
6394
|
+
}
|
|
6395
|
+
gaps.push([after + comma + 1, limit]);
|
|
6396
|
+
}
|
|
6397
|
+
return gaps.map((gap, index) => fixer.replaceTextRange(gap, index === gaps.length - 1 ? `${newline}${baseIndent}` : `${newline}${propertyIndent}`));
|
|
6398
|
+
}
|
|
6399
|
+
});
|
|
6400
|
+
}
|
|
6401
|
+
function checkParams(node) {
|
|
6402
|
+
if (!params) {
|
|
6403
|
+
return;
|
|
6404
|
+
}
|
|
6405
|
+
for (const param of node.params) {
|
|
6406
|
+
const pattern = param.type === "AssignmentPattern" ? param.left : param;
|
|
6407
|
+
if (pattern.type !== "ObjectPattern" || pattern.properties.length < minProperties) {
|
|
6408
|
+
continue;
|
|
6409
|
+
}
|
|
6410
|
+
const text = context.sourceCode.text;
|
|
6411
|
+
const close = text.lastIndexOf("}", (pattern.typeAnnotation?.range[0] ?? pattern.range[1]) - 1);
|
|
6412
|
+
if (!text.slice(pattern.range[0], close).includes(`
|
|
6413
|
+
`)) {
|
|
6414
|
+
reportSplit(pattern, pattern.properties, pattern.range[0], close, "params");
|
|
6415
|
+
}
|
|
6416
|
+
}
|
|
6417
|
+
}
|
|
6124
6418
|
return {
|
|
6125
6419
|
before() {
|
|
6126
6420
|
const options = optionsFirst(context, {});
|
|
@@ -6129,24 +6423,39 @@ var layoutObject = defineRule53({
|
|
|
6129
6423
|
indentUnit = sourceIndentUnit(options.indent);
|
|
6130
6424
|
newline = sourceNewline(context.sourceCode.text);
|
|
6131
6425
|
join3 = options.join;
|
|
6426
|
+
params = options.params ?? false;
|
|
6427
|
+
nested = options.nested ?? false;
|
|
6428
|
+
siblings = options.siblings ?? false;
|
|
6429
|
+
inner = options.inner;
|
|
6132
6430
|
},
|
|
6431
|
+
FunctionDeclaration: checkParams,
|
|
6432
|
+
FunctionExpression: checkParams,
|
|
6433
|
+
ArrowFunctionExpression: checkParams,
|
|
6133
6434
|
ObjectExpression(node) {
|
|
6134
6435
|
const source = context.sourceCode;
|
|
6135
6436
|
const text = source.text;
|
|
6136
6437
|
const properties = node.properties;
|
|
6137
|
-
const
|
|
6438
|
+
const parent = node.parent;
|
|
6439
|
+
const full = properties.length >= minProperties;
|
|
6440
|
+
const holds = !full && holdsNested(node);
|
|
6441
|
+
if (!full && !holds && astIsOneLine(node) && splitSiblings(node) > 0) {
|
|
6442
|
+
reportSplit(node, properties, node.range[0], node.range[1] - 1, "siblings");
|
|
6443
|
+
return;
|
|
6444
|
+
}
|
|
6445
|
+
if (astIsOneLine(node) && keptByInner(node)) {
|
|
6446
|
+
reportSplit(node, properties, node.range[0], node.range[1] - 1, "inner");
|
|
6447
|
+
return;
|
|
6448
|
+
}
|
|
6449
|
+
const split = full || holds;
|
|
6138
6450
|
if (split ? !astIsOneLine(node) : !join3 || astIsOneLine(node)) {
|
|
6139
6451
|
return;
|
|
6140
6452
|
}
|
|
6141
|
-
const parent = node.parent;
|
|
6142
6453
|
const call = (parent.type === "CallExpression" || parent.type === "NewExpression") && parent.arguments.includes(node) ? parent : undefined;
|
|
6143
6454
|
if (scope === "call-args" && !call) {
|
|
6144
6455
|
return;
|
|
6145
6456
|
}
|
|
6146
6457
|
if (!split) {
|
|
6147
|
-
|
|
6148
|
-
const kept = callee !== undefined && (join3?.keep ?? []).some((name) => callee === name || callee.endsWith(`.${name}`));
|
|
6149
|
-
if (kept || properties.length === 0 || !properties.every(layoutObjectIsFlat) || source.getCommentsInside(node).length > 0) {
|
|
6458
|
+
if (joinBlocked(node, call)) {
|
|
6150
6459
|
return;
|
|
6151
6460
|
}
|
|
6152
6461
|
const gaps = sourceItemGaps(node.range[0] + 1, properties, node.range[1] - 1);
|
|
@@ -6162,39 +6471,9 @@ var layoutObject = defineRule53({
|
|
|
6162
6471
|
});
|
|
6163
6472
|
return;
|
|
6164
6473
|
}
|
|
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
|
-
});
|
|
6474
|
+
const messageId = scope === "all" ? "always" : "callArgs";
|
|
6475
|
+
const nestedId = breaksAround(node) ? "nestedName" : "nested";
|
|
6476
|
+
reportSplit(node, properties, node.range[0], node.range[1] - 1, holds ? nestedId : messageId);
|
|
6198
6477
|
}
|
|
6199
6478
|
};
|
|
6200
6479
|
}
|
|
@@ -6323,19 +6602,29 @@ import { defineRule as defineRule55 } from "@oxlint/plugins";
|
|
|
6323
6602
|
var layoutPipe = defineRule55({
|
|
6324
6603
|
meta: {
|
|
6325
6604
|
type: "layout",
|
|
6605
|
+
schema: [{ type: "object", additionalProperties: false, properties: { methods: { type: "array", items: { type: "string" } } } }],
|
|
6326
6606
|
messages: {
|
|
6327
|
-
after: "No ).
|
|
6607
|
+
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
6608
|
step: "A pipe step may span lines only through a callback, object or array. Name this call first, then pass the name."
|
|
6329
6609
|
}
|
|
6330
6610
|
},
|
|
6331
6611
|
createOnce(context) {
|
|
6612
|
+
let methods = new Set;
|
|
6332
6613
|
return {
|
|
6614
|
+
before() {
|
|
6615
|
+
methods = new Set(optionsFirst(context, {}).methods ?? ["pipe"]);
|
|
6616
|
+
},
|
|
6333
6617
|
CallExpression(node) {
|
|
6334
6618
|
const callee = node.callee;
|
|
6335
|
-
|
|
6336
|
-
|
|
6619
|
+
const piped = callee.type === "MemberExpression" && methods.has(astMemberName(callee) ?? "");
|
|
6620
|
+
if (piped && astIsCall(callee.object) && !astIsOneLine(callee.object) && callee.property.loc.start.line === callee.object.loc.end.line) {
|
|
6621
|
+
context.report({
|
|
6622
|
+
node: callee.property,
|
|
6623
|
+
messageId: "after",
|
|
6624
|
+
data: { method: astMemberName(callee) ?? "pipe" }
|
|
6625
|
+
});
|
|
6337
6626
|
}
|
|
6338
|
-
if (!
|
|
6627
|
+
if (!piped) {
|
|
6339
6628
|
return;
|
|
6340
6629
|
}
|
|
6341
6630
|
for (const step of node.arguments) {
|
|
@@ -6474,10 +6763,70 @@ var layoutTernary = defineRule56({
|
|
|
6474
6763
|
}
|
|
6475
6764
|
});
|
|
6476
6765
|
|
|
6477
|
-
// src/rules/
|
|
6766
|
+
// src/rules/layout/layout-type-arguments.ts
|
|
6478
6767
|
import { defineRule as defineRule57 } from "@oxlint/plugins";
|
|
6768
|
+
var layoutTypeArguments = defineRule57({
|
|
6769
|
+
meta: {
|
|
6770
|
+
type: "layout",
|
|
6771
|
+
fixable: "code",
|
|
6772
|
+
schema: [
|
|
6773
|
+
{
|
|
6774
|
+
type: "object",
|
|
6775
|
+
additionalProperties: false,
|
|
6776
|
+
properties: {
|
|
6777
|
+
min: { type: "integer", minimum: 2, default: 3 },
|
|
6778
|
+
indent: { ...sourceIndentSchema, default: 2 }
|
|
6779
|
+
}
|
|
6780
|
+
}
|
|
6781
|
+
],
|
|
6782
|
+
messages: {
|
|
6783
|
+
union: "A union of {{min}}+ types in type arguments puts each type on its own line."
|
|
6784
|
+
}
|
|
6785
|
+
},
|
|
6786
|
+
createOnce(context) {
|
|
6787
|
+
let minMembers = 3;
|
|
6788
|
+
let indentUnit = sourceIndentUnit();
|
|
6789
|
+
return {
|
|
6790
|
+
before() {
|
|
6791
|
+
const options = optionsFirst(context, {});
|
|
6792
|
+
minMembers = options.min ?? 3;
|
|
6793
|
+
indentUnit = sourceIndentUnit(options.indent);
|
|
6794
|
+
},
|
|
6795
|
+
TSTypeParameterInstantiation(node) {
|
|
6796
|
+
const unions = node.params.flatMap((param) => param.type === "TSUnionType" && param.types.length >= minMembers ? [param] : []);
|
|
6797
|
+
if (unions.length === 0 || !astIsOneLine(node)) {
|
|
6798
|
+
return;
|
|
6799
|
+
}
|
|
6800
|
+
const source = context.sourceCode;
|
|
6801
|
+
const text = source.text;
|
|
6802
|
+
const items = node.params.flatMap((param) => param.type === "TSUnionType" && unions.includes(param) ? param.types : [param]);
|
|
6803
|
+
const gaps = sourceItemGaps(node.range[0] + 1, items, node.range[1] - 1);
|
|
6804
|
+
const firsts = new Set(unions.map((union) => union.types[0]));
|
|
6805
|
+
const matches = gaps.map((gap) => /^\s*(,?)\s*(\|?)\s*$/.exec(text.slice(...gap)));
|
|
6806
|
+
const newline = sourceNewline(text);
|
|
6807
|
+
const base = sourceLineIndent(text, node.range[0]);
|
|
6808
|
+
const inner = `${base}${indentUnit}`;
|
|
6809
|
+
const unsafe = text[node.range[0]] !== "<" || text[node.range[1] - 1] !== ">" || source.getCommentsInside(node).length > 0 || matches.some((match) => !match);
|
|
6810
|
+
context.report({
|
|
6811
|
+
node,
|
|
6812
|
+
messageId: "union",
|
|
6813
|
+
data: { min: String(minMembers) },
|
|
6814
|
+
fix: unsafe ? undefined : (fixer) => gaps.map((gap, index) => {
|
|
6815
|
+
const comma = matches[index]?.[1] ?? "";
|
|
6816
|
+
const next = items.at(index);
|
|
6817
|
+
const bar = matches[index]?.[2] || next && firsts.has(next) ? "| " : "";
|
|
6818
|
+
return fixer.replaceTextRange(gap, next ? `${comma}${newline}${inner}${bar}` : `${comma}${newline}${base}`);
|
|
6819
|
+
})
|
|
6820
|
+
});
|
|
6821
|
+
}
|
|
6822
|
+
};
|
|
6823
|
+
}
|
|
6824
|
+
});
|
|
6825
|
+
|
|
6826
|
+
// src/rules/naming/naming-declarations.ts
|
|
6827
|
+
import { defineRule as defineRule58 } from "@oxlint/plugins";
|
|
6479
6828
|
var namingDeclarationsKinds = ["function", "const", "let", "var", "class", "type", "interface", "enum"];
|
|
6480
|
-
var namingDeclarations =
|
|
6829
|
+
var namingDeclarations = defineRule58({
|
|
6481
6830
|
meta: {
|
|
6482
6831
|
type: "problem",
|
|
6483
6832
|
schema: [
|
|
@@ -6554,7 +6903,7 @@ var namingDeclarations = defineRule57({
|
|
|
6554
6903
|
});
|
|
6555
6904
|
|
|
6556
6905
|
// src/rules/naming/naming-exports.ts
|
|
6557
|
-
import { defineRule as
|
|
6906
|
+
import { defineRule as defineRule59 } from "@oxlint/plugins";
|
|
6558
6907
|
var namingExportsScopeProperties = {
|
|
6559
6908
|
scope: { type: "string", enum: ["exports", "functions", "declarations"] },
|
|
6560
6909
|
types: { type: "boolean" },
|
|
@@ -6580,7 +6929,7 @@ function matchTemplate(basename3, template, placeholder = "[a-z0-9-]+") {
|
|
|
6580
6929
|
}
|
|
6581
6930
|
return new RegExp(`^${pattern}$`).exec(basename3)?.groups ?? null;
|
|
6582
6931
|
}
|
|
6583
|
-
var namingExports =
|
|
6932
|
+
var namingExports = defineRule59({
|
|
6584
6933
|
meta: {
|
|
6585
6934
|
type: "problem",
|
|
6586
6935
|
schema: {
|
|
@@ -6717,10 +7066,10 @@ var namingExports = defineRule58({
|
|
|
6717
7066
|
});
|
|
6718
7067
|
|
|
6719
7068
|
// src/rules/naming/naming-file.ts
|
|
6720
|
-
import { defineRule as
|
|
7069
|
+
import { defineRule as defineRule60 } from "@oxlint/plugins";
|
|
6721
7070
|
import { readdirSync as readdirSync2 } from "node:fs";
|
|
6722
7071
|
import { dirname as dirname4 } from "node:path";
|
|
6723
|
-
var namingFile =
|
|
7072
|
+
var namingFile = defineRule60({
|
|
6724
7073
|
meta: {
|
|
6725
7074
|
type: "problem",
|
|
6726
7075
|
schema: {
|
|
@@ -6790,9 +7139,9 @@ var namingFile = defineRule59({
|
|
|
6790
7139
|
});
|
|
6791
7140
|
|
|
6792
7141
|
// src/rules/naming/naming-folder.ts
|
|
6793
|
-
import { defineRule as
|
|
7142
|
+
import { defineRule as defineRule61 } from "@oxlint/plugins";
|
|
6794
7143
|
import { posix as posix3 } from "node:path";
|
|
6795
|
-
var namingFolder =
|
|
7144
|
+
var namingFolder = defineRule61({
|
|
6796
7145
|
meta: {
|
|
6797
7146
|
type: "problem",
|
|
6798
7147
|
schema: [
|
|
@@ -6857,45 +7206,174 @@ var namingFolder = defineRule60({
|
|
|
6857
7206
|
});
|
|
6858
7207
|
|
|
6859
7208
|
// src/rules/naming/naming-types.ts
|
|
6860
|
-
|
|
7209
|
+
import { defineRule as defineRule62 } from "@oxlint/plugins";
|
|
7210
|
+
var namingTypes = defineRule62({
|
|
7211
|
+
meta: {
|
|
7212
|
+
type: "problem",
|
|
7213
|
+
schema: [{ type: "object", additionalProperties: false, properties: { pattern: optionsPatternSchema, message: { type: "string" } } }],
|
|
7214
|
+
messages: { forbidden: "{{message}}" }
|
|
7215
|
+
},
|
|
7216
|
+
createOnce(context) {
|
|
7217
|
+
let patterns = [];
|
|
7218
|
+
let message = "";
|
|
7219
|
+
return {
|
|
7220
|
+
before() {
|
|
7221
|
+
const options = optionsFirst(context, {});
|
|
7222
|
+
patterns = optionsPatterns(options.pattern ?? "^[A-Z][a-zA-Z0-9]*$");
|
|
7223
|
+
message = options.message ?? "Use PascalCase for interfaces.";
|
|
7224
|
+
},
|
|
7225
|
+
TSInterfaceDeclaration(node) {
|
|
7226
|
+
if (!optionsPatternsTest(patterns, node.id.name)) {
|
|
7227
|
+
context.report({
|
|
7228
|
+
node,
|
|
7229
|
+
messageId: "forbidden",
|
|
7230
|
+
data: { message }
|
|
7231
|
+
});
|
|
7232
|
+
}
|
|
7233
|
+
}
|
|
7234
|
+
};
|
|
7235
|
+
}
|
|
7236
|
+
});
|
|
6861
7237
|
|
|
6862
7238
|
// src/rules/react/react-dynamic-classes.ts
|
|
6863
|
-
|
|
7239
|
+
import { defineRule as defineRule63 } from "@oxlint/plugins";
|
|
7240
|
+
var reactDynamicClasses = defineRule63({
|
|
7241
|
+
meta: {
|
|
7242
|
+
type: "problem",
|
|
7243
|
+
schema: [
|
|
7244
|
+
{
|
|
7245
|
+
type: "object",
|
|
7246
|
+
additionalProperties: false,
|
|
7247
|
+
properties: { attributes: { type: "array", items: { type: "string" } }, message: { type: "string" } }
|
|
7248
|
+
}
|
|
7249
|
+
],
|
|
7250
|
+
messages: { forbidden: "{{message}}" }
|
|
7251
|
+
},
|
|
7252
|
+
createOnce(context) {
|
|
7253
|
+
let attributes = new Set;
|
|
7254
|
+
let message = "";
|
|
7255
|
+
const check = (node) => {
|
|
7256
|
+
for (let parent = node.parent;parent; parent = parent.parent) {
|
|
7257
|
+
if (parent.type === "JSXAttribute" && parent.name.type === "JSXIdentifier" && attributes.has(parent.name.name)) {
|
|
7258
|
+
context.report({
|
|
7259
|
+
node,
|
|
7260
|
+
messageId: "forbidden",
|
|
7261
|
+
data: { message }
|
|
7262
|
+
});
|
|
7263
|
+
return;
|
|
7264
|
+
}
|
|
7265
|
+
}
|
|
7266
|
+
};
|
|
7267
|
+
return {
|
|
7268
|
+
before() {
|
|
7269
|
+
const options = optionsFirst(context, {});
|
|
7270
|
+
attributes = new Set(options.attributes ?? ["className"]);
|
|
7271
|
+
message = options.message ?? "Write complete class names instead of constructing them dynamically.";
|
|
7272
|
+
},
|
|
7273
|
+
"JSXAttribute TemplateLiteral[expressions.length>0]": check,
|
|
7274
|
+
'JSXAttribute BinaryExpression[operator="+"]': check
|
|
7275
|
+
};
|
|
7276
|
+
}
|
|
7277
|
+
});
|
|
6864
7278
|
|
|
6865
7279
|
// src/rules/react/react-empty-effect.ts
|
|
6866
|
-
|
|
7280
|
+
import { defineRule as defineRule64 } from "@oxlint/plugins";
|
|
7281
|
+
var reactEmptyEffect = defineRule64({
|
|
7282
|
+
meta: {
|
|
7283
|
+
type: "problem",
|
|
7284
|
+
schema: [
|
|
7285
|
+
{
|
|
7286
|
+
type: "object",
|
|
7287
|
+
additionalProperties: false,
|
|
7288
|
+
properties: { hooks: { type: "array", items: { type: "string" } }, message: { type: "string" } }
|
|
7289
|
+
}
|
|
7290
|
+
],
|
|
7291
|
+
messages: { forbidden: "{{message}}" }
|
|
7292
|
+
},
|
|
7293
|
+
createOnce(context) {
|
|
7294
|
+
let hooks = new Set;
|
|
7295
|
+
let message = "";
|
|
7296
|
+
return {
|
|
7297
|
+
before() {
|
|
7298
|
+
const options = optionsFirst(context, {});
|
|
7299
|
+
hooks = new Set(options.hooks ?? ["useEffect", "useLayoutEffect", "React.useEffect", "React.useLayoutEffect"]);
|
|
7300
|
+
message = options.message ?? "Remove the empty effect.";
|
|
7301
|
+
},
|
|
7302
|
+
CallExpression(node) {
|
|
7303
|
+
if (!hooks.has(astDottedName(node.callee) ?? "")) {
|
|
7304
|
+
return;
|
|
7305
|
+
}
|
|
7306
|
+
for (const argument of node.arguments) {
|
|
7307
|
+
if (astIsInlineFunction(argument) && argument.body?.type === "BlockStatement" && argument.body.body.length === 0) {
|
|
7308
|
+
context.report({
|
|
7309
|
+
node: argument.body,
|
|
7310
|
+
messageId: "forbidden",
|
|
7311
|
+
data: { message }
|
|
7312
|
+
});
|
|
7313
|
+
}
|
|
7314
|
+
}
|
|
7315
|
+
}
|
|
7316
|
+
};
|
|
7317
|
+
}
|
|
7318
|
+
});
|
|
6867
7319
|
|
|
6868
7320
|
// src/rules/react/react-memo.ts
|
|
6869
|
-
import { defineRule as
|
|
6870
|
-
var
|
|
6871
|
-
var reactMemo = defineRule61({
|
|
7321
|
+
import { defineRule as defineRule65 } from "@oxlint/plugins";
|
|
7322
|
+
var reactMemo = defineRule65({
|
|
6872
7323
|
meta: {
|
|
6873
7324
|
type: "problem",
|
|
6874
|
-
schema: [
|
|
7325
|
+
schema: [
|
|
7326
|
+
{
|
|
7327
|
+
type: "object",
|
|
7328
|
+
additionalProperties: false,
|
|
7329
|
+
properties: {
|
|
7330
|
+
names: { type: "array", items: { type: "string" } },
|
|
7331
|
+
sources: { type: "array", items: { type: "string" } },
|
|
7332
|
+
message: { type: "string" }
|
|
7333
|
+
}
|
|
7334
|
+
}
|
|
7335
|
+
],
|
|
6875
7336
|
messages: {
|
|
6876
|
-
memo: "
|
|
7337
|
+
memo: "{{message}}"
|
|
6877
7338
|
}
|
|
6878
7339
|
},
|
|
6879
7340
|
createOnce(context) {
|
|
7341
|
+
let names = [];
|
|
7342
|
+
let sources = [];
|
|
7343
|
+
let message = "";
|
|
6880
7344
|
return {
|
|
7345
|
+
before() {
|
|
7346
|
+
const options = optionsFirst(context, {});
|
|
7347
|
+
names = options.names ?? ["memo", "useMemo", "useCallback"];
|
|
7348
|
+
sources = options.sources ?? ["react"];
|
|
7349
|
+
message = options.message ?? "Use the React Compiler rather than manual memoization.";
|
|
7350
|
+
},
|
|
6881
7351
|
ImportDeclaration(node) {
|
|
6882
|
-
if (node.source.value
|
|
7352
|
+
if (!sources.includes(node.source.value)) {
|
|
6883
7353
|
return;
|
|
6884
7354
|
}
|
|
6885
7355
|
for (const specifier of node.specifiers) {
|
|
6886
|
-
if (specifier.type === "ImportSpecifier" &&
|
|
6887
|
-
context.report({
|
|
7356
|
+
if (specifier.type === "ImportSpecifier" && names.includes(astModuleName(specifier.imported))) {
|
|
7357
|
+
context.report({
|
|
7358
|
+
node: specifier,
|
|
7359
|
+
messageId: "memo",
|
|
7360
|
+
data: { message }
|
|
7361
|
+
});
|
|
6888
7362
|
}
|
|
6889
7363
|
}
|
|
6890
7364
|
},
|
|
6891
7365
|
MemberExpression(node) {
|
|
6892
|
-
if (node.object.type !== "Identifier" || !
|
|
7366
|
+
if (node.object.type !== "Identifier" || !names.includes(boundariesMemberName(node) ?? "")) {
|
|
6893
7367
|
return;
|
|
6894
7368
|
}
|
|
6895
7369
|
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
|
|
7370
|
+
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
7371
|
if (isReact) {
|
|
6898
|
-
context.report({
|
|
7372
|
+
context.report({
|
|
7373
|
+
node,
|
|
7374
|
+
messageId: "memo",
|
|
7375
|
+
data: { message }
|
|
7376
|
+
});
|
|
6899
7377
|
}
|
|
6900
7378
|
}
|
|
6901
7379
|
};
|
|
@@ -6903,7 +7381,7 @@ var reactMemo = defineRule61({
|
|
|
6903
7381
|
});
|
|
6904
7382
|
|
|
6905
7383
|
// src/rules/react/react-props.ts
|
|
6906
|
-
import { defineRule as
|
|
7384
|
+
import { defineRule as defineRule66 } from "@oxlint/plugins";
|
|
6907
7385
|
function reactPropsTarget(node) {
|
|
6908
7386
|
if (node?.type === "FunctionDeclaration" || node?.type === "FunctionExpression" || node?.type === "ArrowFunctionExpression") {
|
|
6909
7387
|
return { fn: node };
|
|
@@ -6930,7 +7408,7 @@ function reactIsPassThrough(type) {
|
|
|
6930
7408
|
const argument = type.typeArguments?.params.length === 1 ? type.typeArguments.params[0] : undefined;
|
|
6931
7409
|
return name.type === "Identifier" && name.name === "ComponentProps" && (argument?.type === "TSTypeQuery" || argument?.type === "TSLiteralType" && argument.literal.type === "Literal" && typeof argument.literal.value === "string");
|
|
6932
7410
|
}
|
|
6933
|
-
var reactProps =
|
|
7411
|
+
var reactProps = defineRule66({
|
|
6934
7412
|
meta: {
|
|
6935
7413
|
type: "problem",
|
|
6936
7414
|
schema: [
|
|
@@ -7029,8 +7507,8 @@ var reactProps = defineRule62({
|
|
|
7029
7507
|
});
|
|
7030
7508
|
|
|
7031
7509
|
// src/rules/react/react-query-props.ts
|
|
7032
|
-
import { defineRule as
|
|
7033
|
-
var reactQueryProps =
|
|
7510
|
+
import { defineRule as defineRule67 } from "@oxlint/plugins";
|
|
7511
|
+
var reactQueryProps = defineRule67({
|
|
7034
7512
|
meta: {
|
|
7035
7513
|
type: "problem",
|
|
7036
7514
|
schema: [
|
|
@@ -7111,7 +7589,7 @@ var reactQueryProps = defineRule63({
|
|
|
7111
7589
|
});
|
|
7112
7590
|
|
|
7113
7591
|
// src/rules/schemas/schemas-inline.ts
|
|
7114
|
-
import { defineRule as
|
|
7592
|
+
import { defineRule as defineRule68 } from "@oxlint/plugins";
|
|
7115
7593
|
|
|
7116
7594
|
// src/utils/schemas.utils.ts
|
|
7117
7595
|
var schemasElementOptionsSchema = {
|
|
@@ -7147,7 +7625,7 @@ function schemasIsAllowedElement(node, allowScalars, namespaces, structuralMetho
|
|
|
7147
7625
|
}
|
|
7148
7626
|
|
|
7149
7627
|
// src/rules/schemas/schemas-inline.ts
|
|
7150
|
-
var schemasInline =
|
|
7628
|
+
var schemasInline = defineRule68({
|
|
7151
7629
|
meta: {
|
|
7152
7630
|
type: "problem",
|
|
7153
7631
|
schema: [schemasElementOptionsSchema],
|
|
@@ -7196,11 +7674,11 @@ var schemasInline = defineRule64({
|
|
|
7196
7674
|
});
|
|
7197
7675
|
|
|
7198
7676
|
// src/rules/schemas/schemas-local.ts
|
|
7199
|
-
import { defineRule as
|
|
7677
|
+
import { defineRule as defineRule69 } from "@oxlint/plugins";
|
|
7200
7678
|
var schemasLocalImportMessage = "Do not import runtime schema constructors from {{source}}; import a named schema instead.";
|
|
7201
7679
|
var schemasLocalConstructionMessage = "Do not construct schemas locally; import a named schema instead.";
|
|
7202
7680
|
var schemasLocalCalls = ["config", "prettifyError", "treeifyError", "flattenError", "formatError"];
|
|
7203
|
-
var schemasLocal =
|
|
7681
|
+
var schemasLocal = defineRule69({
|
|
7204
7682
|
meta: {
|
|
7205
7683
|
type: "problem",
|
|
7206
7684
|
schema: [
|
|
@@ -7349,8 +7827,8 @@ var schemasLocal = defineRule65({
|
|
|
7349
7827
|
});
|
|
7350
7828
|
|
|
7351
7829
|
// src/rules/schemas/schemas-rederive.ts
|
|
7352
|
-
import { defineRule as
|
|
7353
|
-
var schemasRederive =
|
|
7830
|
+
import { defineRule as defineRule70 } from "@oxlint/plugins";
|
|
7831
|
+
var schemasRederive = defineRule70({
|
|
7354
7832
|
meta: {
|
|
7355
7833
|
type: "problem",
|
|
7356
7834
|
schema: [
|
|
@@ -7396,7 +7874,7 @@ var schemasRederive = defineRule66({
|
|
|
7396
7874
|
});
|
|
7397
7875
|
|
|
7398
7876
|
// src/rules/schemas/schemas-single-use.ts
|
|
7399
|
-
import { defineRule as
|
|
7877
|
+
import { defineRule as defineRule71 } from "@oxlint/plugins";
|
|
7400
7878
|
function isScalarSchema(node, namespaces, structuralMethods) {
|
|
7401
7879
|
if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression") {
|
|
7402
7880
|
return false;
|
|
@@ -7410,7 +7888,7 @@ function isScalarSchema(node, namespaces, structuralMethods) {
|
|
|
7410
7888
|
}
|
|
7411
7889
|
return node.callee.object.type === "CallExpression" && isScalarSchema(node.callee.object, namespaces, structuralMethods);
|
|
7412
7890
|
}
|
|
7413
|
-
var schemasSingleUse =
|
|
7891
|
+
var schemasSingleUse = defineRule71({
|
|
7414
7892
|
meta: {
|
|
7415
7893
|
type: "suggestion",
|
|
7416
7894
|
schema: [schemasElementOptionsSchema],
|
|
@@ -7486,7 +7964,7 @@ var schemasSingleUse = defineRule67({
|
|
|
7486
7964
|
});
|
|
7487
7965
|
|
|
7488
7966
|
// src/rules/tests/tests-assertions.ts
|
|
7489
|
-
import { defineRule as
|
|
7967
|
+
import { defineRule as defineRule72 } from "@oxlint/plugins";
|
|
7490
7968
|
var testsAssertionsModifiers = new Set(["for", "each", "concurrent", "sequential", "only", "skip"]);
|
|
7491
7969
|
var testsAssertionsFunctions = [
|
|
7492
7970
|
"expect",
|
|
@@ -7676,7 +8154,7 @@ function isAssertedTest(registration, sourceCode, state) {
|
|
|
7676
8154
|
}
|
|
7677
8155
|
return rows.length > 0 && rows.every((row) => hasAssertion(callback, row.bindings, new Set, state));
|
|
7678
8156
|
}
|
|
7679
|
-
var testsAssertions =
|
|
8157
|
+
var testsAssertions = defineRule72({
|
|
7680
8158
|
meta: {
|
|
7681
8159
|
type: "problem",
|
|
7682
8160
|
schema: [
|
|
@@ -7802,8 +8280,8 @@ var testsAssertions = defineRule68({
|
|
|
7802
8280
|
});
|
|
7803
8281
|
|
|
7804
8282
|
// src/rules/tests/tests-concurrent-db.ts
|
|
7805
|
-
import { defineRule as
|
|
7806
|
-
var testsConcurrentDb =
|
|
8283
|
+
import { defineRule as defineRule73 } from "@oxlint/plugins";
|
|
8284
|
+
var testsConcurrentDb = defineRule73({
|
|
7807
8285
|
meta: {
|
|
7808
8286
|
type: "problem",
|
|
7809
8287
|
schema: [
|
|
@@ -7859,7 +8337,7 @@ var testsConcurrentDb = defineRule69({
|
|
|
7859
8337
|
});
|
|
7860
8338
|
|
|
7861
8339
|
// src/rules/tests/tests-modifiers.ts
|
|
7862
|
-
import { defineRule as
|
|
8340
|
+
import { defineRule as defineRule74 } from "@oxlint/plugins";
|
|
7863
8341
|
var testsModifiersNames = ["test", "it", "describe", "suite"];
|
|
7864
8342
|
var testsModifiersPackages = new Set(["vitest", "bun:test", "@playwright/test", "@jest/globals"]);
|
|
7865
8343
|
var testsModifiersBanned = ["only", "skip", "todo"];
|
|
@@ -7878,7 +8356,7 @@ function modifierName(node) {
|
|
|
7878
8356
|
const property = astMemberName(node);
|
|
7879
8357
|
return object && property ? `${object}.${property}` : undefined;
|
|
7880
8358
|
}
|
|
7881
|
-
var testsModifiers =
|
|
8359
|
+
var testsModifiers = defineRule74({
|
|
7882
8360
|
meta: {
|
|
7883
8361
|
type: "problem",
|
|
7884
8362
|
schema: [
|
|
@@ -7932,9 +8410,9 @@ var testsModifiers = defineRule70({
|
|
|
7932
8410
|
});
|
|
7933
8411
|
|
|
7934
8412
|
// src/rules/tests/tests-titles.ts
|
|
7935
|
-
import { defineRule as
|
|
8413
|
+
import { defineRule as defineRule75 } from "@oxlint/plugins";
|
|
7936
8414
|
var testsTitlesModifiers = ["only", "skip", "concurrent", "sequential", "todo", "each", "for"];
|
|
7937
|
-
var testsTitles =
|
|
8415
|
+
var testsTitles = defineRule75({
|
|
7938
8416
|
meta: {
|
|
7939
8417
|
type: "suggestion",
|
|
7940
8418
|
schema: [
|
|
@@ -8012,11 +8490,11 @@ var testsTitles = defineRule71({
|
|
|
8012
8490
|
});
|
|
8013
8491
|
|
|
8014
8492
|
// src/rules/types/types-declarations.ts
|
|
8015
|
-
import { defineRule as
|
|
8493
|
+
import { defineRule as defineRule76 } from "@oxlint/plugins";
|
|
8016
8494
|
function isValuePattern(source) {
|
|
8017
8495
|
return /^\^?=/.test(source);
|
|
8018
8496
|
}
|
|
8019
|
-
var typesDeclarations =
|
|
8497
|
+
var typesDeclarations = defineRule76({
|
|
8020
8498
|
meta: {
|
|
8021
8499
|
type: "problem",
|
|
8022
8500
|
schema: [
|
|
@@ -8067,8 +8545,8 @@ var typesDeclarations = defineRule72({
|
|
|
8067
8545
|
});
|
|
8068
8546
|
|
|
8069
8547
|
// src/rules/types/types-duplicate.ts
|
|
8070
|
-
import { defineRule as
|
|
8071
|
-
var typesDuplicate =
|
|
8548
|
+
import { defineRule as defineRule77 } from "@oxlint/plugins";
|
|
8549
|
+
var typesDuplicate = defineRule77({
|
|
8072
8550
|
meta: {
|
|
8073
8551
|
type: "suggestion",
|
|
8074
8552
|
schema: [
|
|
@@ -8132,7 +8610,7 @@ var typesDuplicate = defineRule73({
|
|
|
8132
8610
|
});
|
|
8133
8611
|
|
|
8134
8612
|
// src/rules/types/types-imported-alias.ts
|
|
8135
|
-
import { defineRule as
|
|
8613
|
+
import { defineRule as defineRule78 } from "@oxlint/plugins";
|
|
8136
8614
|
function importedAliasTarget(declaration, imports) {
|
|
8137
8615
|
if (declaration?.type !== "TSTypeAliasDeclaration") {
|
|
8138
8616
|
return;
|
|
@@ -8144,7 +8622,7 @@ function importedAliasTarget(declaration, imports) {
|
|
|
8144
8622
|
const { name } = type.typeName;
|
|
8145
8623
|
return imports.has(name) && !declaration.typeParameters?.params.some((parameter) => parameter.name.name === name) ? name : undefined;
|
|
8146
8624
|
}
|
|
8147
|
-
var typesImportedAlias =
|
|
8625
|
+
var typesImportedAlias = defineRule78({
|
|
8148
8626
|
meta: {
|
|
8149
8627
|
type: "problem",
|
|
8150
8628
|
schema: [],
|
|
@@ -8206,7 +8684,7 @@ var typesImportedAlias = defineRule74({
|
|
|
8206
8684
|
});
|
|
8207
8685
|
|
|
8208
8686
|
// src/rules/types/types-inline.ts
|
|
8209
|
-
import { defineRule as
|
|
8687
|
+
import { defineRule as defineRule79 } from "@oxlint/plugins";
|
|
8210
8688
|
function parameterAnnotation(parameter) {
|
|
8211
8689
|
if (parameter.type === "TSParameterProperty") {
|
|
8212
8690
|
return parameterAnnotation(parameter.parameter);
|
|
@@ -8216,7 +8694,7 @@ function parameterAnnotation(parameter) {
|
|
|
8216
8694
|
}
|
|
8217
8695
|
return parameter.typeAnnotation;
|
|
8218
8696
|
}
|
|
8219
|
-
var typesInline =
|
|
8697
|
+
var typesInline = defineRule79({
|
|
8220
8698
|
meta: {
|
|
8221
8699
|
type: "problem",
|
|
8222
8700
|
schema: [
|
|
@@ -8328,11 +8806,38 @@ var typesInline = defineRule75({
|
|
|
8328
8806
|
});
|
|
8329
8807
|
|
|
8330
8808
|
// src/rules/types/types-local-alias.ts
|
|
8331
|
-
|
|
8809
|
+
import { defineRule as defineRule80 } from "@oxlint/plugins";
|
|
8810
|
+
var typesLocalAlias = defineRule80({
|
|
8811
|
+
meta: {
|
|
8812
|
+
type: "problem",
|
|
8813
|
+
schema: [{ type: "object", additionalProperties: false, properties: { allow: optionsPatternSchema, message: { type: "string" } } }],
|
|
8814
|
+
messages: { forbidden: "{{message}}" }
|
|
8815
|
+
},
|
|
8816
|
+
createOnce(context) {
|
|
8817
|
+
let allow = [];
|
|
8818
|
+
let message = "";
|
|
8819
|
+
return {
|
|
8820
|
+
before() {
|
|
8821
|
+
const options = optionsFirst(context, {});
|
|
8822
|
+
allow = optionsPatterns(options.allow);
|
|
8823
|
+
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.";
|
|
8824
|
+
},
|
|
8825
|
+
TSTypeAliasDeclaration(node) {
|
|
8826
|
+
if (!optionsPatternsTest(allow, node.id.name)) {
|
|
8827
|
+
context.report({
|
|
8828
|
+
node,
|
|
8829
|
+
messageId: "forbidden",
|
|
8830
|
+
data: { message }
|
|
8831
|
+
});
|
|
8832
|
+
}
|
|
8833
|
+
}
|
|
8834
|
+
};
|
|
8835
|
+
}
|
|
8836
|
+
});
|
|
8332
8837
|
|
|
8333
8838
|
// src/rules/types/types-namespace-import.ts
|
|
8334
|
-
import { defineRule as
|
|
8335
|
-
var typesNamespaceImport =
|
|
8839
|
+
import { defineRule as defineRule81 } from "@oxlint/plugins";
|
|
8840
|
+
var typesNamespaceImport = defineRule81({
|
|
8336
8841
|
meta: {
|
|
8337
8842
|
type: "suggestion",
|
|
8338
8843
|
schema: [
|
|
@@ -8374,8 +8879,8 @@ var typesNamespaceImport = defineRule76({
|
|
|
8374
8879
|
});
|
|
8375
8880
|
|
|
8376
8881
|
// src/rules/types/types-service.ts
|
|
8377
|
-
import { defineRule as
|
|
8378
|
-
var typesService =
|
|
8882
|
+
import { defineRule as defineRule82 } from "@oxlint/plugins";
|
|
8883
|
+
var typesService = defineRule82({
|
|
8379
8884
|
meta: {
|
|
8380
8885
|
type: "problem",
|
|
8381
8886
|
schema: [
|
|
@@ -8425,12 +8930,12 @@ var typesService = defineRule77({
|
|
|
8425
8930
|
});
|
|
8426
8931
|
|
|
8427
8932
|
// src/rules/types/types-unknown.ts
|
|
8428
|
-
import { defineRule as
|
|
8933
|
+
import { defineRule as defineRule83 } from "@oxlint/plugins";
|
|
8429
8934
|
function isCatchName(node) {
|
|
8430
8935
|
const key = "key" in node && !(("computed" in node) && node.computed) ? node.key : undefined;
|
|
8431
8936
|
return key?.type === "Identifier" && key.name === "catch";
|
|
8432
8937
|
}
|
|
8433
|
-
var typesUnknown =
|
|
8938
|
+
var typesUnknown = defineRule83({
|
|
8434
8939
|
meta: { type: "problem", schema: [], messages: { forbidden: "Use an owned domain type rather than unknown." } },
|
|
8435
8940
|
createOnce(context) {
|
|
8436
8941
|
return {
|
|
@@ -8514,6 +9019,7 @@ var src_default = eslintCompatPlugin({
|
|
|
8514
9019
|
"layout-padding": layoutPadding,
|
|
8515
9020
|
"layout-pipe": layoutPipe,
|
|
8516
9021
|
"layout-ternary": layoutTernary,
|
|
9022
|
+
"layout-type-arguments": layoutTypeArguments,
|
|
8517
9023
|
"naming-declarations": namingDeclarations,
|
|
8518
9024
|
"naming-exports": namingExports,
|
|
8519
9025
|
"naming-file": namingFile,
|