oxlint-plugin-arch 0.12.3 → 0.12.5
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 +63 -0
- package/README.md +4 -4
- package/dist/index.js +1246 -280
- package/dist/preset/groups/effect.d.ts +2 -0
- package/dist/preset/preset-recipes.d.ts +4 -0
- package/dist/preset/preset.d.ts +4 -0
- package/dist/preset/preset.js +35 -2
- package/dist/rules/code/code-branch-once.d.ts +1 -0
- package/dist/rules/code/code-function-nesting.d.ts +1 -0
- package/dist/rules/code/code-inline.d.ts +1 -0
- package/dist/rules/code/code-justified-call.d.ts +1 -0
- package/dist/rules/code/code-traced.d.ts +1 -0
- package/dist/rules/code/code-yield-argument.d.ts +1 -0
- package/dist/rules/layout/layout-arrow-body.d.ts +1 -0
- package/dist/rules/layout/layout-call-depth.d.ts +1 -0
- package/dist/rules/layout/layout-items.d.ts +1 -0
- package/dist/rules/layout/layout-pipe.d.ts +1 -0
- package/dist/types/ast.types.d.ts +1 -0
- package/dist/types/backend.types.d.ts +1 -0
- package/dist/types/boundaries.types.d.ts +7 -0
- package/dist/types/code.types.d.ts +25 -0
- package/dist/types/comments.types.d.ts +1 -1
- package/dist/types/files.types.d.ts +6 -1
- package/dist/types/layout.types.d.ts +32 -2
- package/dist/types/preset.types.d.ts +10 -0
- package/dist/types/react.types.d.ts +1 -0
- package/dist/types/tests.types.d.ts +7 -0
- package/dist/types/types.types.d.ts +4 -0
- package/dist/utils/ast.utils.d.ts +7 -1
- package/dist/utils/source.utils.d.ts +7 -2
- package/dist/vendor/dillon-anti-slop/index.js +33 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -225,6 +225,39 @@ function astSelectorRule(selector, message) {
|
|
|
225
225
|
}
|
|
226
226
|
});
|
|
227
227
|
}
|
|
228
|
+
function astIsOneLine(node) {
|
|
229
|
+
return node.loc.start.line === node.loc.end.line;
|
|
230
|
+
}
|
|
231
|
+
function astIsInlineFunction(node) {
|
|
232
|
+
return node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression";
|
|
233
|
+
}
|
|
234
|
+
function astIsCall(node) {
|
|
235
|
+
return node.type === "CallExpression" || node.type === "NewExpression";
|
|
236
|
+
}
|
|
237
|
+
function astIsPipe(node) {
|
|
238
|
+
return node.callee.type === "MemberExpression" && astMemberName(node.callee) === "pipe";
|
|
239
|
+
}
|
|
240
|
+
function astChildren(node) {
|
|
241
|
+
return Object.entries(node).filter(([key]) => !astSkippedKeys.has(key)).map(([, child]) => child);
|
|
242
|
+
}
|
|
243
|
+
function astSome(value, test, enter = () => true) {
|
|
244
|
+
if (Array.isArray(value)) {
|
|
245
|
+
return value.some((child) => astSome(child, test, enter));
|
|
246
|
+
}
|
|
247
|
+
return astIsNode(value) && (test(value) || enter(value) && astChildren(value).some((child) => astSome(child, test, enter)));
|
|
248
|
+
}
|
|
249
|
+
function astCallDepth(value) {
|
|
250
|
+
if (Array.isArray(value)) {
|
|
251
|
+
return Math.max(0, ...value.map(astCallDepth));
|
|
252
|
+
}
|
|
253
|
+
if (!astIsNode(value) || astIsInlineFunction(value)) {
|
|
254
|
+
return 0;
|
|
255
|
+
}
|
|
256
|
+
if (astIsCall(value)) {
|
|
257
|
+
return Math.max(value.arguments.length === 0 ? 0 : 1 + astCallDepth(value.arguments), astCallDepth(value.callee));
|
|
258
|
+
}
|
|
259
|
+
return astCallDepth(astChildren(value));
|
|
260
|
+
}
|
|
228
261
|
|
|
229
262
|
// src/rules/backend/backend-orpc-output.ts
|
|
230
263
|
function backendFindChainCall(expression, method) {
|
|
@@ -477,14 +510,15 @@ var backendRegistry = defineRule3({
|
|
|
477
510
|
registry: { type: "string", minLength: 1 },
|
|
478
511
|
services: { type: "string", minLength: 1 },
|
|
479
512
|
skip: { type: "array", items: { type: "string" } },
|
|
480
|
-
object: { type: "string", minLength: 1 }
|
|
513
|
+
object: { type: "string", minLength: 1 },
|
|
514
|
+
file: { type: "string", minLength: 1, default: "{folder}.service.ts" }
|
|
481
515
|
},
|
|
482
516
|
required: ["registry", "services"]
|
|
483
517
|
}
|
|
484
518
|
],
|
|
485
519
|
messages: {
|
|
486
520
|
unregistered: "Register the {{folder}} service here under '{{key}}'.",
|
|
487
|
-
missing: "Domain folder '{{folder}}' needs {{
|
|
521
|
+
missing: "Domain folder '{{folder}}' needs {{file}}; add it, or list the folder in skip."
|
|
488
522
|
}
|
|
489
523
|
},
|
|
490
524
|
createOnce(context) {
|
|
@@ -526,11 +560,12 @@ var backendRegistry = defineRule3({
|
|
|
526
560
|
const domains = entries.filter((entry) => entry.isDirectory() && !(options.skip ?? []).includes(entry.name)).map((entry) => entry.name);
|
|
527
561
|
for (const folder of domains) {
|
|
528
562
|
const key = namingCamelCase(folder);
|
|
529
|
-
|
|
563
|
+
const file = (options.file ?? "{folder}.service.ts").replaceAll("{folder}", folder);
|
|
564
|
+
if (!existsSync(join(options.services, folder, file))) {
|
|
530
565
|
context.report({
|
|
531
566
|
node: program,
|
|
532
567
|
messageId: "missing",
|
|
533
|
-
data: { folder }
|
|
568
|
+
data: { folder, file }
|
|
534
569
|
});
|
|
535
570
|
} else if (![key, `${key}s`, key.replace(/y$/, "ies")].some((name) => keys.has(name))) {
|
|
536
571
|
context.report({
|
|
@@ -1103,11 +1138,14 @@ var boundariesDeny = defineRule6({
|
|
|
1103
1138
|
// src/rules/boundaries/boundaries-imports.ts
|
|
1104
1139
|
import { defineRule as defineRule7 } from "@oxlint/plugins";
|
|
1105
1140
|
import { basename, resolve as resolve2 } from "node:path";
|
|
1106
|
-
var
|
|
1141
|
+
var boundariesSurfaces = ["service", "client", "server", "rsc", "utils"];
|
|
1107
1142
|
var boundariesActionOrQuery = /\/(?:[^/]+)-(?:action|query)\./;
|
|
1108
1143
|
function boundariesContains(directory, candidate) {
|
|
1109
1144
|
return candidate === directory || candidate.startsWith(`${directory}/`);
|
|
1110
1145
|
}
|
|
1146
|
+
function boundariesIsSurface(options, stem) {
|
|
1147
|
+
return (options.surfaces ?? boundariesSurfaces).some((surface) => stem.endsWith(`.${surface}`));
|
|
1148
|
+
}
|
|
1111
1149
|
function boundariesStripExtension(path) {
|
|
1112
1150
|
return path.replace(/\.[cm]?[jt]sx?$/, "");
|
|
1113
1151
|
}
|
|
@@ -1136,12 +1174,12 @@ function boundariesPrivateDomain({ options, file, target, reExport, roots, owner
|
|
|
1136
1174
|
const targetDomain = targetOwner ? boundariesDomain(target, targetOwner, boundariesServiceFolder(options, targetOwner)) : undefined;
|
|
1137
1175
|
const sameDomain = owner && owner === targetOwner && fileDomain && fileDomain === targetDomain;
|
|
1138
1176
|
const targetStem = boundariesStripExtension(target);
|
|
1139
|
-
const publicSurface =
|
|
1177
|
+
const publicSurface = boundariesIsSurface(options, targetStem) && !boundariesActionOrQuery.test(targetStem);
|
|
1140
1178
|
const privateTarget = roles.some((role) => targetStem.endsWith(`.${role}`)) || targetDomain && !publicSurface && !/\.(?:types|constants)$/.test(targetStem);
|
|
1141
1179
|
if (!privateTarget) {
|
|
1142
1180
|
return;
|
|
1143
1181
|
}
|
|
1144
|
-
if (reExport &&
|
|
1182
|
+
if (reExport && file.endsWith(".ts") && boundariesIsSurface(options, file.slice(0, -".ts".length)) && !boundariesActionOrQuery.test(file)) {
|
|
1145
1183
|
return "public service and utility surfaces must not re-export private domain internals";
|
|
1146
1184
|
}
|
|
1147
1185
|
return sameDomain ? undefined : "private domain files stay in the same domain and application; use its service API or shared utilities";
|
|
@@ -1179,6 +1217,7 @@ var boundariesImports = defineRule7({
|
|
|
1179
1217
|
backend: { type: "array", items: { type: "string" } },
|
|
1180
1218
|
packages: { anyOf: [{ type: "string" }, { type: "array", items: { type: "string" } }] },
|
|
1181
1219
|
roles: { type: "array", items: { type: "string" } },
|
|
1220
|
+
surfaces: { type: "array", items: { type: "string", minLength: 1 }, default: boundariesSurfaces },
|
|
1182
1221
|
services: { type: "array", items: { type: "string" } },
|
|
1183
1222
|
portable: { type: "boolean" },
|
|
1184
1223
|
backendPackages: { type: "array", items: { type: "string" } },
|
|
@@ -1274,11 +1313,9 @@ var boundariesNextDefaults = {
|
|
|
1274
1313
|
serverPackages: [...boundariesServerPackages, "server-only", "next/headers", "next/cache", "next/server"],
|
|
1275
1314
|
clientPackages: ["client-only", "react-dom/client"],
|
|
1276
1315
|
hookPackages: ["react"],
|
|
1277
|
-
publicEnv: "NEXT_PUBLIC_"
|
|
1316
|
+
publicEnv: "NEXT_PUBLIC_",
|
|
1317
|
+
serverHooks: ["useId", "use"]
|
|
1278
1318
|
};
|
|
1279
|
-
function boundariesIsClientHook(name) {
|
|
1280
|
-
return /^use[A-Z]/.test(name) && name !== "useId" && name !== "use";
|
|
1281
|
-
}
|
|
1282
1319
|
var boundariesNext = defineRule8({
|
|
1283
1320
|
meta: {
|
|
1284
1321
|
type: "problem",
|
|
@@ -1286,7 +1323,11 @@ var boundariesNext = defineRule8({
|
|
|
1286
1323
|
{
|
|
1287
1324
|
type: "object",
|
|
1288
1325
|
additionalProperties: false,
|
|
1289
|
-
properties: {
|
|
1326
|
+
properties: {
|
|
1327
|
+
...boundariesRuntimeProperties,
|
|
1328
|
+
publicEnv: { type: "string" },
|
|
1329
|
+
serverHooks: { type: "array", items: { type: "string", minLength: 1 }, default: boundariesNextDefaults.serverHooks }
|
|
1330
|
+
}
|
|
1290
1331
|
}
|
|
1291
1332
|
],
|
|
1292
1333
|
messages: {
|
|
@@ -1302,6 +1343,7 @@ var boundariesNext = defineRule8({
|
|
|
1302
1343
|
let client = false;
|
|
1303
1344
|
let shared = false;
|
|
1304
1345
|
const reactNamespaces = new Set;
|
|
1346
|
+
const clientHook = (name) => /^use[A-Z]/.test(name) && !options.serverHooks.includes(name);
|
|
1305
1347
|
function check(node, source, typeOnly) {
|
|
1306
1348
|
if (typeOnly) {
|
|
1307
1349
|
return;
|
|
@@ -1365,14 +1407,14 @@ var boundariesNext = defineRule8({
|
|
|
1365
1407
|
reactNamespaces.add(specifier);
|
|
1366
1408
|
continue;
|
|
1367
1409
|
}
|
|
1368
|
-
if (specifier.importKind !== "type" && specifier.imported.type === "Identifier" &&
|
|
1410
|
+
if (specifier.importKind !== "type" && specifier.imported.type === "Identifier" && clientHook(specifier.imported.name)) {
|
|
1369
1411
|
boundariesReport(context, specifier, specifier.imported.name, boundariesClientHookReason);
|
|
1370
1412
|
}
|
|
1371
1413
|
}
|
|
1372
1414
|
},
|
|
1373
1415
|
MemberExpression(node) {
|
|
1374
1416
|
const name = boundariesMemberName(node);
|
|
1375
|
-
if (!name || !
|
|
1417
|
+
if (!name || !clientHook(name) || node.object.type !== "Identifier") {
|
|
1376
1418
|
return;
|
|
1377
1419
|
}
|
|
1378
1420
|
const binding = boundariesDeclaredVariable(context.sourceCode.getScope(node.object), node.object.name)?.defs[0]?.node;
|
|
@@ -1416,12 +1458,54 @@ var boundariesNext = defineRule8({
|
|
|
1416
1458
|
});
|
|
1417
1459
|
|
|
1418
1460
|
// src/rules/boundaries/boundaries-rpc-database.ts
|
|
1419
|
-
|
|
1420
|
-
var
|
|
1461
|
+
import { defineRule as defineRule9 } from "@oxlint/plugins";
|
|
1462
|
+
var boundariesRpcDatabaseMethods = ["selectFrom", "insertInto", "updateTable", "deleteFrom", "mergeInto", "executeQuery"];
|
|
1463
|
+
var boundariesRpcDatabase = defineRule9({
|
|
1464
|
+
meta: {
|
|
1465
|
+
type: "problem",
|
|
1466
|
+
schema: [
|
|
1467
|
+
{
|
|
1468
|
+
type: "object",
|
|
1469
|
+
additionalProperties: false,
|
|
1470
|
+
properties: {
|
|
1471
|
+
message: { type: "string" },
|
|
1472
|
+
methods: { type: "array", items: { type: "string", minLength: 1 }, default: boundariesRpcDatabaseMethods }
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
],
|
|
1476
|
+
messages: { forbidden: "{{message}}" }
|
|
1477
|
+
},
|
|
1478
|
+
createOnce(context) {
|
|
1479
|
+
let message = "";
|
|
1480
|
+
let methods = new Set;
|
|
1481
|
+
return {
|
|
1482
|
+
before() {
|
|
1483
|
+
const options = optionsFirst(context, {});
|
|
1484
|
+
message = options.message ?? "RPC handlers delegate database work to services.";
|
|
1485
|
+
methods = new Set(options.methods ?? boundariesRpcDatabaseMethods);
|
|
1486
|
+
},
|
|
1487
|
+
CallExpression(node) {
|
|
1488
|
+
const callee = node.callee;
|
|
1489
|
+
if (callee.type !== "MemberExpression") {
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
const property = callee.property;
|
|
1493
|
+
const literal = property.type === "Literal" && typeof property.value === "string" ? property.value : undefined;
|
|
1494
|
+
const name = callee.computed ? literal : astMemberName(callee);
|
|
1495
|
+
if (name !== undefined && methods.has(name)) {
|
|
1496
|
+
context.report({
|
|
1497
|
+
node,
|
|
1498
|
+
messageId: "forbidden",
|
|
1499
|
+
data: { message }
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
1421
1506
|
|
|
1422
1507
|
// src/rules/boundaries/boundaries-tanstack.ts
|
|
1423
|
-
import { defineRule as
|
|
1424
|
-
var boundariesRenderers = ["renderServerComponent", "renderToReadableStream"];
|
|
1508
|
+
import { defineRule as defineRule10 } from "@oxlint/plugins";
|
|
1425
1509
|
var boundariesChainMethods = ["handler", "server", "client", "middleware", "validator", "inputValidator"];
|
|
1426
1510
|
var boundariesSplitFactories = ["createIsomorphicFn", "createMiddleware"];
|
|
1427
1511
|
var boundariesRuntimeTypeWrappers = [
|
|
@@ -1457,12 +1541,13 @@ var boundariesTanstackDefaults = {
|
|
|
1457
1541
|
clientPackages: ["react-dom/client", "@tanstack/react-start/client-only", "@tanstack/solid-start/client-only", "@tanstack/vue-start/client-only"],
|
|
1458
1542
|
hookPackages: ["react", "@tanstack/react-query", "@tanstack/react-router"],
|
|
1459
1543
|
startPackages: ["@tanstack/react-start"],
|
|
1460
|
-
rscPackages: ["@tanstack/react-start/rsc", "@tanstack/react-start-rsc"]
|
|
1544
|
+
rscPackages: ["@tanstack/react-start/rsc", "@tanstack/react-start-rsc"],
|
|
1545
|
+
renderers: ["renderServerComponent", "renderToReadableStream"]
|
|
1461
1546
|
};
|
|
1462
|
-
function
|
|
1547
|
+
function boundariesIsClientHook(name) {
|
|
1463
1548
|
return /^use[A-Z]/.test(name) && name !== "useId";
|
|
1464
1549
|
}
|
|
1465
|
-
var boundariesTanstack =
|
|
1550
|
+
var boundariesTanstack = defineRule10({
|
|
1466
1551
|
meta: {
|
|
1467
1552
|
type: "problem",
|
|
1468
1553
|
schema: [
|
|
@@ -1473,7 +1558,8 @@ var boundariesTanstack = defineRule9({
|
|
|
1473
1558
|
...boundariesRuntimeProperties,
|
|
1474
1559
|
rpc: { type: "array", items: { type: "string" } },
|
|
1475
1560
|
startPackages: { type: "array", items: { type: "string" } },
|
|
1476
|
-
rscPackages: { type: "array", items: { type: "string" } }
|
|
1561
|
+
rscPackages: { type: "array", items: { type: "string" } },
|
|
1562
|
+
renderers: { type: "array", items: { type: "string", minLength: 1 }, default: boundariesTanstackDefaults.renderers }
|
|
1477
1563
|
}
|
|
1478
1564
|
}
|
|
1479
1565
|
],
|
|
@@ -1629,7 +1715,7 @@ var boundariesTanstack = defineRule9({
|
|
|
1629
1715
|
return;
|
|
1630
1716
|
}
|
|
1631
1717
|
for (const specifier of node.specifiers) {
|
|
1632
|
-
if (options.rscPackages.includes(node.source.value) && specifier.type === "ImportSpecifier" && specifier.importKind !== "type" && specifier.imported.type === "Identifier" &&
|
|
1718
|
+
if (options.rscPackages.includes(node.source.value) && specifier.type === "ImportSpecifier" && specifier.importKind !== "type" && specifier.imported.type === "Identifier" && options.renderers.includes(specifier.imported.name)) {
|
|
1633
1719
|
check(node, node.source.value, false, false, specifier);
|
|
1634
1720
|
}
|
|
1635
1721
|
}
|
|
@@ -1637,7 +1723,7 @@ var boundariesTanstack = defineRule9({
|
|
|
1637
1723
|
return;
|
|
1638
1724
|
}
|
|
1639
1725
|
for (const specifier of node.specifiers) {
|
|
1640
|
-
if (specifier.type !== "ImportSpecifier" || specifier.importKind === "type" || specifier.imported.type !== "Identifier" || !
|
|
1726
|
+
if (specifier.type !== "ImportSpecifier" || specifier.importKind === "type" || specifier.imported.type !== "Identifier" || !boundariesIsClientHook(specifier.imported.name)) {
|
|
1641
1727
|
continue;
|
|
1642
1728
|
}
|
|
1643
1729
|
const references = runtimeReferences(node, specifier.local.name);
|
|
@@ -1656,10 +1742,10 @@ var boundariesTanstack = defineRule9({
|
|
|
1656
1742
|
return;
|
|
1657
1743
|
}
|
|
1658
1744
|
const kind = imported(node.object);
|
|
1659
|
-
if (kind === "rsc-renderers" &&
|
|
1745
|
+
if (kind === "rsc-renderers" && options.renderers.includes(name) && (shared || !serverPosition(node.start, true))) {
|
|
1660
1746
|
boundariesReport(context, node, name, "server renderers require a server execution boundary");
|
|
1661
1747
|
}
|
|
1662
|
-
if ((serverFile || rscFile) && kind === "client-hooks" &&
|
|
1748
|
+
if ((serverFile || rscFile) && kind === "client-hooks" && boundariesIsClientHook(name) && !clientSide(node.start)) {
|
|
1663
1749
|
boundariesReport(context, node, name, boundariesClientHookReason2);
|
|
1664
1750
|
}
|
|
1665
1751
|
},
|
|
@@ -1691,8 +1777,8 @@ var boundariesTanstack = defineRule9({
|
|
|
1691
1777
|
});
|
|
1692
1778
|
|
|
1693
1779
|
// src/rules/boundaries/boundaries-token-owner.ts
|
|
1694
|
-
import { defineRule as
|
|
1695
|
-
var boundariesTokenOwner =
|
|
1780
|
+
import { defineRule as defineRule11 } from "@oxlint/plugins";
|
|
1781
|
+
var boundariesTokenOwner = defineRule11({
|
|
1696
1782
|
meta: {
|
|
1697
1783
|
type: "problem",
|
|
1698
1784
|
schema: [
|
|
@@ -1786,9 +1872,68 @@ var boundariesTokenOwner = defineRule10({
|
|
|
1786
1872
|
}
|
|
1787
1873
|
});
|
|
1788
1874
|
|
|
1875
|
+
// src/rules/code/code-branch-once.ts
|
|
1876
|
+
import { defineRule as defineRule12 } from "@oxlint/plugins";
|
|
1877
|
+
function codeBranchOnceSubjects(node, source) {
|
|
1878
|
+
if (node.type === "LogicalExpression") {
|
|
1879
|
+
return [...codeBranchOnceSubjects(node.left, source), ...codeBranchOnceSubjects(node.right, source)];
|
|
1880
|
+
}
|
|
1881
|
+
if (node.type === "UnaryExpression" && node.operator === "!") {
|
|
1882
|
+
return codeBranchOnceSubjects(node.argument, source);
|
|
1883
|
+
}
|
|
1884
|
+
if (node.type !== "BinaryExpression" || node.operator !== "===" && node.operator !== "!==") {
|
|
1885
|
+
return [];
|
|
1886
|
+
}
|
|
1887
|
+
const sides = [node.left, node.right];
|
|
1888
|
+
const member = sides.find((side) => side.type === "MemberExpression");
|
|
1889
|
+
const literal = sides.some((side) => side.type === "Literal" && typeof side.value === "string");
|
|
1890
|
+
return member && literal ? [source.getText(member)] : [];
|
|
1891
|
+
}
|
|
1892
|
+
var codeBranchOnce = defineRule12({
|
|
1893
|
+
meta: {
|
|
1894
|
+
type: "suggestion",
|
|
1895
|
+
schema: [
|
|
1896
|
+
{
|
|
1897
|
+
type: "object",
|
|
1898
|
+
additionalProperties: false,
|
|
1899
|
+
properties: { min: { type: "integer", minimum: 2, default: 2 } }
|
|
1900
|
+
}
|
|
1901
|
+
],
|
|
1902
|
+
messages: {
|
|
1903
|
+
once: "{{subject}} is compared to a string in {{count}} ternaries in this file. Branch on it once and return a record."
|
|
1904
|
+
}
|
|
1905
|
+
},
|
|
1906
|
+
createOnce(context) {
|
|
1907
|
+
let groups = new Map;
|
|
1908
|
+
let min = 2;
|
|
1909
|
+
return {
|
|
1910
|
+
before() {
|
|
1911
|
+
groups = new Map;
|
|
1912
|
+
min = optionsFirst(context, {}).min ?? 2;
|
|
1913
|
+
},
|
|
1914
|
+
ConditionalExpression(node) {
|
|
1915
|
+
for (const subject of new Set(codeBranchOnceSubjects(node.test, context.sourceCode))) {
|
|
1916
|
+
groups.set(subject, [...groups.get(subject) ?? [], node]);
|
|
1917
|
+
}
|
|
1918
|
+
},
|
|
1919
|
+
"Program:exit"() {
|
|
1920
|
+
for (const [subject, ternaries] of groups) {
|
|
1921
|
+
for (const node of ternaries.length >= min ? ternaries : []) {
|
|
1922
|
+
context.report({
|
|
1923
|
+
node,
|
|
1924
|
+
messageId: "once",
|
|
1925
|
+
data: { subject, count: String(ternaries.length) }
|
|
1926
|
+
});
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
};
|
|
1931
|
+
}
|
|
1932
|
+
});
|
|
1933
|
+
|
|
1789
1934
|
// src/rules/code/code-constructor.ts
|
|
1790
|
-
import { defineRule as
|
|
1791
|
-
var codeConstructor =
|
|
1935
|
+
import { defineRule as defineRule13 } from "@oxlint/plugins";
|
|
1936
|
+
var codeConstructor = defineRule13({
|
|
1792
1937
|
meta: {
|
|
1793
1938
|
type: "problem",
|
|
1794
1939
|
schema: [
|
|
@@ -1871,9 +2016,304 @@ var codeConstructor = defineRule11({
|
|
|
1871
2016
|
// src/rules/code/code-double-negation.ts
|
|
1872
2017
|
var codeDoubleNegation = astSelectorRule('UnaryExpression[operator="!"] > UnaryExpression[operator="!"]', "Use Boolean(value) or an explicit comparison.");
|
|
1873
2018
|
|
|
2019
|
+
// src/rules/code/code-function-nesting.ts
|
|
2020
|
+
import { defineRule as defineRule14 } from "@oxlint/plugins";
|
|
2021
|
+
var codeFunctionNesting = defineRule14({
|
|
2022
|
+
meta: {
|
|
2023
|
+
type: "suggestion",
|
|
2024
|
+
schema: [
|
|
2025
|
+
{
|
|
2026
|
+
type: "object",
|
|
2027
|
+
additionalProperties: false,
|
|
2028
|
+
properties: { max: { type: "integer", minimum: 1, default: 4 } }
|
|
2029
|
+
}
|
|
2030
|
+
],
|
|
2031
|
+
messages: {
|
|
2032
|
+
deep: "Functions nest {{depth}} deep (max {{max}}). Name the inner step."
|
|
2033
|
+
}
|
|
2034
|
+
},
|
|
2035
|
+
createOnce(context) {
|
|
2036
|
+
let max = 4;
|
|
2037
|
+
const check = (node) => {
|
|
2038
|
+
let depth = 0;
|
|
2039
|
+
for (let current = node;current; current = current.parent) {
|
|
2040
|
+
depth += current.type === "FunctionDeclaration" || astIsInlineFunction(current) ? 1 : 0;
|
|
2041
|
+
}
|
|
2042
|
+
if (depth === max + 1) {
|
|
2043
|
+
context.report({
|
|
2044
|
+
node,
|
|
2045
|
+
messageId: "deep",
|
|
2046
|
+
data: { depth: String(depth), max: String(max) }
|
|
2047
|
+
});
|
|
2048
|
+
}
|
|
2049
|
+
};
|
|
2050
|
+
return {
|
|
2051
|
+
before() {
|
|
2052
|
+
max = optionsFirst(context, {}).max ?? 4;
|
|
2053
|
+
},
|
|
2054
|
+
ArrowFunctionExpression: check,
|
|
2055
|
+
FunctionExpression: check,
|
|
2056
|
+
FunctionDeclaration: check
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
});
|
|
2060
|
+
|
|
2061
|
+
// src/rules/code/code-inline.ts
|
|
2062
|
+
import { defineRule as defineRule15 } from "@oxlint/plugins";
|
|
2063
|
+
|
|
2064
|
+
// src/utils/source.utils.ts
|
|
2065
|
+
var sourceDirective = /^\s*(?:\/\s*<(?:reference\b|amd-[\w-]+\b)|@ts-(?:check|ignore|expect-error|nocheck)\b|(?:#|@)__(?:PURE|NO_SIDE_EFFECTS)__\b|@jsx(?:ImportSource|Runtime|Frag)?\b|@vite-ignore\b|(?:[#@]\s*)?sourceMappingURL=|(?:es|ox)lint-(?:disable|enable)|prettier-ignore|biome-ignore|[cv]8 ignore|istanbul ignore|webpack[A-Z])/;
|
|
2066
|
+
var sourceLegalBlock = "\x00legal-block\x00";
|
|
2067
|
+
var sourceIndentSchema = {
|
|
2068
|
+
anyOf: [
|
|
2069
|
+
{ type: "integer", minimum: 0 },
|
|
2070
|
+
{ type: "string", enum: ["tab"] }
|
|
2071
|
+
]
|
|
2072
|
+
};
|
|
2073
|
+
function sourceCommentText(comment) {
|
|
2074
|
+
const value = (comment.type === "Block" ? comment.value.replace(/^\s*\* ?/gm, "") : comment.value).trimStart();
|
|
2075
|
+
return comment.type === "Block" && comment.value.startsWith("!") ? sourceLegalBlock + value : value;
|
|
2076
|
+
}
|
|
2077
|
+
function sourceIsDirective(text) {
|
|
2078
|
+
return text.startsWith(sourceLegalBlock) || sourceDirective.test(text);
|
|
2079
|
+
}
|
|
2080
|
+
function sourceLineIndent(text, offset) {
|
|
2081
|
+
const lineStart = text.lastIndexOf(`
|
|
2082
|
+
`, offset - 1) + 1;
|
|
2083
|
+
return /^[\t ]*/.exec(text.slice(lineStart, offset))?.[0] ?? "";
|
|
2084
|
+
}
|
|
2085
|
+
function sourceIndentUnit(indent = 2) {
|
|
2086
|
+
return indent === "tab" ? "\t" : " ".repeat(indent);
|
|
2087
|
+
}
|
|
2088
|
+
function sourceNewline(text) {
|
|
2089
|
+
return text.includes(`\r
|
|
2090
|
+
`) ? `\r
|
|
2091
|
+
` : `
|
|
2092
|
+
`;
|
|
2093
|
+
}
|
|
2094
|
+
function sourceLineStart(text, offset) {
|
|
2095
|
+
return text.lastIndexOf(`
|
|
2096
|
+
`, offset - 1) + 1;
|
|
2097
|
+
}
|
|
2098
|
+
function sourceLineEnd(text, offset) {
|
|
2099
|
+
const end = text.indexOf(`
|
|
2100
|
+
`, offset);
|
|
2101
|
+
return end === -1 ? text.length : end;
|
|
2102
|
+
}
|
|
2103
|
+
function sourceJoinedWidth(text, start, joined, end) {
|
|
2104
|
+
return start - sourceLineStart(text, start) + joined.length + text.slice(end, sourceLineEnd(text, end)).trimEnd().length;
|
|
2105
|
+
}
|
|
2106
|
+
function sourceItemGaps(open, items, close) {
|
|
2107
|
+
const edges = [open, ...items.flatMap((item) => item.range), close];
|
|
2108
|
+
return Array.from({ length: items.length + 1 }, (_, index) => [edges[index * 2] ?? open, edges[index * 2 + 1] ?? close]);
|
|
2109
|
+
}
|
|
2110
|
+
function sourceRespace(fixer, text, gaps, spacing) {
|
|
2111
|
+
const matches = gaps.map((gap) => /^\s*(,?)\s*$/.exec(text.slice(...gap)));
|
|
2112
|
+
if (matches.some((match) => match === null)) {
|
|
2113
|
+
return null;
|
|
2114
|
+
}
|
|
2115
|
+
return gaps.map((gap, index) => fixer.replaceTextRange(gap, `${matches[index]?.[1] ?? ""}${spacing(index, index === gaps.length - 1)}`));
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
// src/rules/code/code-inline.ts
|
|
2119
|
+
function codeInlineIsData(node) {
|
|
2120
|
+
return node.type === "ObjectExpression" || node.type === "ArrayExpression" || (node.type === "TSSatisfiesExpression" || node.type === "TSAsExpression") && codeInlineIsData(node.expression);
|
|
2121
|
+
}
|
|
2122
|
+
function codeInlineOuterCall(node) {
|
|
2123
|
+
const parent = node.parent;
|
|
2124
|
+
if (!parent || astIsInlineFunction(parent) || parent.type.endsWith("Statement")) {
|
|
2125
|
+
return;
|
|
2126
|
+
}
|
|
2127
|
+
return astIsCall(parent) && parent.arguments.some((argument) => argument === node) ? parent : codeInlineOuterCall(parent);
|
|
2128
|
+
}
|
|
2129
|
+
function codeInlineEnclosingCalls(node) {
|
|
2130
|
+
const outer = codeInlineOuterCall(node);
|
|
2131
|
+
return outer && astIsOneLine(outer) ? 1 + codeInlineEnclosingCalls(outer) : 0;
|
|
2132
|
+
}
|
|
2133
|
+
var codeInlineWrapped = new Set(["ConditionalExpression", "LogicalExpression", "BinaryExpression", "ArrowFunctionExpression"]);
|
|
2134
|
+
var codeInlineEffects = new Set([
|
|
2135
|
+
"CallExpression",
|
|
2136
|
+
"NewExpression",
|
|
2137
|
+
"AssignmentExpression",
|
|
2138
|
+
"UpdateExpression",
|
|
2139
|
+
"AwaitExpression",
|
|
2140
|
+
"YieldExpression",
|
|
2141
|
+
"TaggedTemplateExpression",
|
|
2142
|
+
"ImportExpression"
|
|
2143
|
+
]);
|
|
2144
|
+
function codeInlineEffectBetween(program, start, end) {
|
|
2145
|
+
return astSome(program, (node) => node.range[0] >= start && node.range[1] <= end && (codeInlineEffects.has(node.type) || node.type === "UnaryExpression" && node.operator === "delete"), (node) => node.range[1] > start && node.range[0] < end && !(node.range[0] >= start && (node.type === "FunctionDeclaration" || astIsInlineFunction(node))));
|
|
2146
|
+
}
|
|
2147
|
+
var codeInline = defineRule15({
|
|
2148
|
+
meta: {
|
|
2149
|
+
type: "suggestion",
|
|
2150
|
+
schema: [
|
|
2151
|
+
{
|
|
2152
|
+
type: "object",
|
|
2153
|
+
additionalProperties: false,
|
|
2154
|
+
properties: {
|
|
2155
|
+
max: { type: "integer", minimum: 1, default: 160 },
|
|
2156
|
+
nesting: { type: "integer", minimum: 1, default: 2 },
|
|
2157
|
+
allow: optionsPatternSchema,
|
|
2158
|
+
keep: optionsPatternSchema,
|
|
2159
|
+
services: { type: "array", items: { type: "string" } }
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
],
|
|
2163
|
+
messages: {
|
|
2164
|
+
once: "{{name}} is used once: inline it.",
|
|
2165
|
+
trivial: "{{name}} only wraps one call or expression: inline it at each use."
|
|
2166
|
+
}
|
|
2167
|
+
},
|
|
2168
|
+
createOnce(context) {
|
|
2169
|
+
let options = {};
|
|
2170
|
+
let allowed = [];
|
|
2171
|
+
let kept = [];
|
|
2172
|
+
const reads = (node) => {
|
|
2173
|
+
const variable = context.sourceCode.getDeclaredVariables(node).at(0);
|
|
2174
|
+
return variable ? variable.references.filter((reference) => !reference.init) : [];
|
|
2175
|
+
};
|
|
2176
|
+
const top = (node, id, init) => {
|
|
2177
|
+
if (id.type !== "Identifier" || !astIsOneLine(node) || init && codeInlineIsData(init) || optionsPatternsTest(allowed, id.name)) {
|
|
2178
|
+
return;
|
|
2179
|
+
}
|
|
2180
|
+
const uses = reads(node);
|
|
2181
|
+
if (uses.some((reference) => reference.identifier.parent.type === "ExportSpecifier")) {
|
|
2182
|
+
return;
|
|
2183
|
+
}
|
|
2184
|
+
if (uses.length === 1) {
|
|
2185
|
+
if (init && codeInlineEffectBetween(context.sourceCode.ast, node.range[1], uses[0].identifier.range[0])) {
|
|
2186
|
+
return;
|
|
2187
|
+
}
|
|
2188
|
+
context.report({
|
|
2189
|
+
node: id,
|
|
2190
|
+
messageId: "once",
|
|
2191
|
+
data: { name: id.name }
|
|
2192
|
+
});
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
const arrow = init?.type === "ArrowFunctionExpression" && init.body.type !== "BlockStatement" && !astSome(init.body, astIsInlineFunction);
|
|
2196
|
+
const call = init?.type === "CallExpression" && !astSome(init.arguments, astIsInlineFunction) && astCallDepth(init) <= 2;
|
|
2197
|
+
if (!optionsPatternsTest(kept, id.name) && (arrow || call)) {
|
|
2198
|
+
context.report({
|
|
2199
|
+
node: id,
|
|
2200
|
+
messageId: "trivial",
|
|
2201
|
+
data: { name: id.name }
|
|
2202
|
+
});
|
|
2203
|
+
}
|
|
2204
|
+
};
|
|
2205
|
+
const local = (declarator) => {
|
|
2206
|
+
const { id, init } = declarator;
|
|
2207
|
+
const yields = (node) => node.type === "YieldExpression";
|
|
2208
|
+
if (id.type !== "Identifier" || !init || !astIsOneLine(declarator) || codeInlineIsData(init) || astSome(init, yields, (node) => !astIsInlineFunction(node))) {
|
|
2209
|
+
return;
|
|
2210
|
+
}
|
|
2211
|
+
const uses = reads(declarator);
|
|
2212
|
+
const use = uses.at(0)?.identifier;
|
|
2213
|
+
if (!use || uses.length > 1 || optionsPatternsTest(allowed, id.name)) {
|
|
2214
|
+
return;
|
|
2215
|
+
}
|
|
2216
|
+
const text = context.sourceCode.text;
|
|
2217
|
+
const line = text.slice(sourceLineStart(text, use.range[0]), sourceLineEnd(text, use.range[0]));
|
|
2218
|
+
const owner = use.parent.type === "Property" ? use.parent.parent.parent : null;
|
|
2219
|
+
const service = owner && astIsCall(owner) && owner.callee.type === "MemberExpression" && (options.services ?? []).includes(astMemberName(owner.callee) ?? "");
|
|
2220
|
+
let deferred = false;
|
|
2221
|
+
for (let current = use.parent;current && current !== declarator.parent.parent; current = current.parent) {
|
|
2222
|
+
deferred ||= current.type === "FunctionDeclaration" || astIsInlineFunction(current);
|
|
2223
|
+
}
|
|
2224
|
+
const wrapped = codeInlineWrapped.has(init.type) && use.parent.type === "MemberExpression" && use.parent.object === use;
|
|
2225
|
+
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])) {
|
|
2226
|
+
return;
|
|
2227
|
+
}
|
|
2228
|
+
context.report({
|
|
2229
|
+
node: id,
|
|
2230
|
+
messageId: "once",
|
|
2231
|
+
data: { name: id.name }
|
|
2232
|
+
});
|
|
2233
|
+
};
|
|
2234
|
+
return {
|
|
2235
|
+
before() {
|
|
2236
|
+
options = optionsFirst(context, {});
|
|
2237
|
+
allowed = optionsPatterns(options.allow);
|
|
2238
|
+
kept = optionsPatterns(options.keep);
|
|
2239
|
+
},
|
|
2240
|
+
VariableDeclaration(node) {
|
|
2241
|
+
if (node.kind === "const" && node.parent.type === "BlockStatement") {
|
|
2242
|
+
node.declarations.forEach(local);
|
|
2243
|
+
}
|
|
2244
|
+
},
|
|
2245
|
+
Program(program) {
|
|
2246
|
+
for (const statement of program.body) {
|
|
2247
|
+
if (statement.type === "VariableDeclaration" && statement.kind === "const") {
|
|
2248
|
+
statement.declarations.forEach((declarator) => top(declarator, declarator.id, declarator.init));
|
|
2249
|
+
}
|
|
2250
|
+
if (statement.type === "FunctionDeclaration" && statement.id) {
|
|
2251
|
+
top(statement, statement.id, null);
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2257
|
+
});
|
|
2258
|
+
|
|
2259
|
+
// src/rules/code/code-justified-call.ts
|
|
2260
|
+
import { defineRule as defineRule16 } from "@oxlint/plugins";
|
|
2261
|
+
var codeJustifiedCall = defineRule16({
|
|
2262
|
+
meta: {
|
|
2263
|
+
type: "problem",
|
|
2264
|
+
schema: [
|
|
2265
|
+
{
|
|
2266
|
+
type: "object",
|
|
2267
|
+
additionalProperties: false,
|
|
2268
|
+
required: ["calls"],
|
|
2269
|
+
properties: {
|
|
2270
|
+
calls: { type: "array", items: { type: "string" }, minItems: 1 },
|
|
2271
|
+
comment: { type: "string", default: "SAFETY:" },
|
|
2272
|
+
message: { type: "string" }
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
],
|
|
2276
|
+
messages: {
|
|
2277
|
+
missing: "{{call}} needs a `// {{comment}} <why>` comment on the line above.{{message}}"
|
|
2278
|
+
}
|
|
2279
|
+
},
|
|
2280
|
+
createOnce(context) {
|
|
2281
|
+
let calls = new Set;
|
|
2282
|
+
let comment = "SAFETY:";
|
|
2283
|
+
let message = "";
|
|
2284
|
+
let comments = [];
|
|
2285
|
+
const justified = (line) => {
|
|
2286
|
+
const above = comments.find((candidate) => candidate.loc.end.line === line - 1);
|
|
2287
|
+
if (!above) {
|
|
2288
|
+
return false;
|
|
2289
|
+
}
|
|
2290
|
+
return above.value.trimStart().startsWith(`${comment} `) || above.type === "Line" && justified(above.loc.start.line);
|
|
2291
|
+
};
|
|
2292
|
+
return {
|
|
2293
|
+
before() {
|
|
2294
|
+
const options = optionsFirst(context, { calls: [] });
|
|
2295
|
+
calls = new Set(options.calls);
|
|
2296
|
+
comment = options.comment ?? "SAFETY:";
|
|
2297
|
+
message = options.message ? ` ${options.message}` : "";
|
|
2298
|
+
comments = context.sourceCode.getAllComments();
|
|
2299
|
+
},
|
|
2300
|
+
CallExpression(node) {
|
|
2301
|
+
const call = astDottedName(node.callee);
|
|
2302
|
+
if (call && calls.has(call) && !justified(node.loc.start.line)) {
|
|
2303
|
+
context.report({
|
|
2304
|
+
node,
|
|
2305
|
+
messageId: "missing",
|
|
2306
|
+
data: { call, comment, message }
|
|
2307
|
+
});
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
};
|
|
2311
|
+
}
|
|
2312
|
+
});
|
|
2313
|
+
|
|
1874
2314
|
// src/rules/code/code-literal-in.ts
|
|
1875
|
-
import { defineRule as
|
|
1876
|
-
var codeLiteralIn =
|
|
2315
|
+
import { defineRule as defineRule17 } from "@oxlint/plugins";
|
|
2316
|
+
var codeLiteralIn = defineRule17({
|
|
1877
2317
|
meta: {
|
|
1878
2318
|
type: "suggestion",
|
|
1879
2319
|
schema: [
|
|
@@ -1914,7 +2354,7 @@ var codeLiteralIn = defineRule12({
|
|
|
1914
2354
|
});
|
|
1915
2355
|
|
|
1916
2356
|
// src/rules/code/code-mutable-state.ts
|
|
1917
|
-
import { defineRule as
|
|
2357
|
+
import { defineRule as defineRule18 } from "@oxlint/plugins";
|
|
1918
2358
|
var functionScopes = new Set([
|
|
1919
2359
|
"FunctionDeclaration",
|
|
1920
2360
|
"FunctionExpression",
|
|
@@ -1931,7 +2371,7 @@ function hasAncestor(node, test) {
|
|
|
1931
2371
|
}
|
|
1932
2372
|
return false;
|
|
1933
2373
|
}
|
|
1934
|
-
var codeMutableState =
|
|
2374
|
+
var codeMutableState = defineRule18({
|
|
1935
2375
|
meta: {
|
|
1936
2376
|
type: "problem",
|
|
1937
2377
|
schema: [
|
|
@@ -1989,7 +2429,7 @@ var codeMutableState = defineRule13({
|
|
|
1989
2429
|
});
|
|
1990
2430
|
|
|
1991
2431
|
// src/rules/code/code-object-params.ts
|
|
1992
|
-
import { defineRule as
|
|
2432
|
+
import { defineRule as defineRule19 } from "@oxlint/plugins";
|
|
1993
2433
|
function isObjectParam(node) {
|
|
1994
2434
|
if (node.type === "ObjectPattern") {
|
|
1995
2435
|
return true;
|
|
@@ -2003,7 +2443,7 @@ function isObjectParam(node) {
|
|
|
2003
2443
|
const annotation = node.typeAnnotation?.typeAnnotation;
|
|
2004
2444
|
return !annotation || annotation.type === "TSTypeReference" || annotation.type === "TSTypeLiteral" || annotation.type === "TSIntersectionType";
|
|
2005
2445
|
}
|
|
2006
|
-
var codeObjectParams =
|
|
2446
|
+
var codeObjectParams = defineRule19({
|
|
2007
2447
|
meta: {
|
|
2008
2448
|
type: "problem",
|
|
2009
2449
|
schema: [
|
|
@@ -2052,8 +2492,8 @@ var codeObjectParams = defineRule14({
|
|
|
2052
2492
|
});
|
|
2053
2493
|
|
|
2054
2494
|
// src/rules/code/code-paired-call.ts
|
|
2055
|
-
import { defineRule as
|
|
2056
|
-
var codePairedCall =
|
|
2495
|
+
import { defineRule as defineRule20 } from "@oxlint/plugins";
|
|
2496
|
+
var codePairedCall = defineRule20({
|
|
2057
2497
|
meta: {
|
|
2058
2498
|
type: "problem",
|
|
2059
2499
|
schema: [
|
|
@@ -2084,7 +2524,6 @@ var codePairedCall = defineRule15({
|
|
|
2084
2524
|
}
|
|
2085
2525
|
},
|
|
2086
2526
|
createOnce(context) {
|
|
2087
|
-
const aliases = new Map;
|
|
2088
2527
|
let pairs = [];
|
|
2089
2528
|
return {
|
|
2090
2529
|
before() {
|
|
@@ -2092,26 +2531,10 @@ var codePairedCall = defineRule15({
|
|
|
2092
2531
|
const all = when && require2 ? [{ when, require: require2 }, ...configured] : configured;
|
|
2093
2532
|
pairs = all.map((pair) => ({ ...pair, firstWhen: null, sawRequired: false }));
|
|
2094
2533
|
},
|
|
2095
|
-
Program(program) {
|
|
2096
|
-
aliases.clear();
|
|
2097
|
-
for (const statement of program.body) {
|
|
2098
|
-
if (statement.type !== "ImportDeclaration") {
|
|
2099
|
-
continue;
|
|
2100
|
-
}
|
|
2101
|
-
for (const specifier of statement.specifiers) {
|
|
2102
|
-
if (specifier.type !== "ImportSpecifier") {
|
|
2103
|
-
continue;
|
|
2104
|
-
}
|
|
2105
|
-
const imported = astModuleName(specifier.imported);
|
|
2106
|
-
if (imported !== specifier.local.name) {
|
|
2107
|
-
aliases.set(specifier.local.name, imported);
|
|
2108
|
-
}
|
|
2109
|
-
}
|
|
2110
|
-
}
|
|
2111
|
-
},
|
|
2112
2534
|
CallExpression(node) {
|
|
2113
|
-
const
|
|
2114
|
-
const
|
|
2535
|
+
const callee = node.callee;
|
|
2536
|
+
const binding = callee.type === "Identifier" ? boundariesDeclaredVariable(context.sourceCode.getScope(node), callee.name)?.defs.at(0)?.node : undefined;
|
|
2537
|
+
const name = binding?.type === "ImportSpecifier" ? astModuleName(binding.imported) : astCallName(node);
|
|
2115
2538
|
for (const pair of pairs) {
|
|
2116
2539
|
if (name === pair.when) {
|
|
2117
2540
|
pair.firstWhen ??= node;
|
|
@@ -2137,7 +2560,7 @@ var codePairedCall = defineRule15({
|
|
|
2137
2560
|
});
|
|
2138
2561
|
|
|
2139
2562
|
// src/rules/code/code-promise-mutation.ts
|
|
2140
|
-
import { defineRule as
|
|
2563
|
+
import { defineRule as defineRule21 } from "@oxlint/plugins";
|
|
2141
2564
|
function mutationBinding(node) {
|
|
2142
2565
|
if (node.type === "Identifier") {
|
|
2143
2566
|
return node;
|
|
@@ -2166,7 +2589,7 @@ function mutationBindings(node) {
|
|
|
2166
2589
|
const binding = mutationBinding(node);
|
|
2167
2590
|
return binding ? [binding] : [];
|
|
2168
2591
|
}
|
|
2169
|
-
var codePromiseMutation =
|
|
2592
|
+
var codePromiseMutation = defineRule21({
|
|
2170
2593
|
meta: {
|
|
2171
2594
|
type: "problem",
|
|
2172
2595
|
schema: [
|
|
@@ -2258,8 +2681,96 @@ var codePromiseMutation = defineRule16({
|
|
|
2258
2681
|
// src/rules/code/code-switch.ts
|
|
2259
2682
|
var codeSwitch = astSelectorRule("SwitchStatement", "Avoid switch statements.");
|
|
2260
2683
|
|
|
2684
|
+
// src/rules/code/code-traced.ts
|
|
2685
|
+
import { defineRule as defineRule22 } from "@oxlint/plugins";
|
|
2686
|
+
var codeTraced = defineRule22({
|
|
2687
|
+
meta: {
|
|
2688
|
+
type: "problem",
|
|
2689
|
+
schema: [
|
|
2690
|
+
{
|
|
2691
|
+
type: "object",
|
|
2692
|
+
additionalProperties: false,
|
|
2693
|
+
properties: {
|
|
2694
|
+
untraced: { type: "array", items: { type: "string" }, default: ["Effect.fnUntraced"] },
|
|
2695
|
+
exports: optionsPatternSchema,
|
|
2696
|
+
services: { type: "array", items: { type: "string" }, default: ["of"] },
|
|
2697
|
+
skip: optionsPatternSchema,
|
|
2698
|
+
message: { type: "string" }
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
],
|
|
2702
|
+
messages: {
|
|
2703
|
+
surface: "{{message}}"
|
|
2704
|
+
}
|
|
2705
|
+
},
|
|
2706
|
+
createOnce(context) {
|
|
2707
|
+
let untraced = new Set;
|
|
2708
|
+
let services = new Set;
|
|
2709
|
+
let exported = true;
|
|
2710
|
+
let skipped = [];
|
|
2711
|
+
let message = "";
|
|
2712
|
+
const isUntraced = (node) => node?.type === "CallExpression" && untraced.has(astDottedName(node.callee) ?? "");
|
|
2713
|
+
const resolves = (value) => {
|
|
2714
|
+
if (value.type !== "Identifier") {
|
|
2715
|
+
return isUntraced(value);
|
|
2716
|
+
}
|
|
2717
|
+
let scope = context.sourceCode.getScope(value);
|
|
2718
|
+
while (scope && !scope.set.has(value.name)) {
|
|
2719
|
+
scope = scope.upper;
|
|
2720
|
+
}
|
|
2721
|
+
const declaration = scope?.set.get(value.name)?.defs.at(0)?.node;
|
|
2722
|
+
return declaration?.type === "VariableDeclarator" && isUntraced(declaration.init);
|
|
2723
|
+
};
|
|
2724
|
+
return {
|
|
2725
|
+
before() {
|
|
2726
|
+
const options = optionsFirst(context, {});
|
|
2727
|
+
const files = optionsPatterns(options.exports);
|
|
2728
|
+
untraced = new Set(options.untraced ?? ["Effect.fnUntraced"]);
|
|
2729
|
+
services = new Set(options.services ?? ["of"]);
|
|
2730
|
+
exported = files.length === 0 || optionsPatternsTest(files, context.filename.replaceAll("\\", "/"));
|
|
2731
|
+
skipped = optionsPatterns(options.skip);
|
|
2732
|
+
message = options.message ?? `An exported function or service method gets a span: replace ${[...untraced].join(", ")} with its traced form.`;
|
|
2733
|
+
},
|
|
2734
|
+
ExportNamedDeclaration(node) {
|
|
2735
|
+
if (!exported) {
|
|
2736
|
+
return;
|
|
2737
|
+
}
|
|
2738
|
+
const declarators = node.declaration?.type === "VariableDeclaration" ? node.declaration.declarations.filter((declarator) => isUntraced(declarator.init)) : [];
|
|
2739
|
+
const specifiers = node.source ? [] : node.specifiers.filter((specifier) => resolves(specifier.local));
|
|
2740
|
+
for (const surface of [...declarators, ...specifiers]) {
|
|
2741
|
+
context.report({
|
|
2742
|
+
node: surface,
|
|
2743
|
+
messageId: "surface",
|
|
2744
|
+
data: { message }
|
|
2745
|
+
});
|
|
2746
|
+
}
|
|
2747
|
+
},
|
|
2748
|
+
CallExpression(node) {
|
|
2749
|
+
const service = node.arguments.at(0);
|
|
2750
|
+
const callee = node.callee;
|
|
2751
|
+
if (callee.type !== "MemberExpression" || !services.has(astMemberName(callee) ?? "") || service?.type !== "ObjectExpression") {
|
|
2752
|
+
return;
|
|
2753
|
+
}
|
|
2754
|
+
const tag = callee.object.type === "MemberExpression" ? astMemberName(callee.object) : astDottedName(callee.object);
|
|
2755
|
+
if (optionsPatternsTest(skipped, tag ?? "")) {
|
|
2756
|
+
return;
|
|
2757
|
+
}
|
|
2758
|
+
for (const property of service.properties) {
|
|
2759
|
+
if (property.type === "Property" && resolves(property.value)) {
|
|
2760
|
+
context.report({
|
|
2761
|
+
node: property,
|
|
2762
|
+
messageId: "surface",
|
|
2763
|
+
data: { message }
|
|
2764
|
+
});
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
};
|
|
2769
|
+
}
|
|
2770
|
+
});
|
|
2771
|
+
|
|
2261
2772
|
// src/rules/code/code-trivial-functions.ts
|
|
2262
|
-
import { defineRule as
|
|
2773
|
+
import { defineRule as defineRule23 } from "@oxlint/plugins";
|
|
2263
2774
|
var codeTrivialFunctionsBanned = [
|
|
2264
2775
|
"isRecord",
|
|
2265
2776
|
"isPlainObject",
|
|
@@ -2389,7 +2900,7 @@ function guardStatement(node, state) {
|
|
|
2389
2900
|
return guardFallback(declaration.init);
|
|
2390
2901
|
});
|
|
2391
2902
|
}
|
|
2392
|
-
var codeTrivialFunctions =
|
|
2903
|
+
var codeTrivialFunctions = defineRule23({
|
|
2393
2904
|
meta: {
|
|
2394
2905
|
type: "problem",
|
|
2395
2906
|
schema: [
|
|
@@ -2519,41 +3030,31 @@ var codeTrivialFunctions = defineRule17({
|
|
|
2519
3030
|
}
|
|
2520
3031
|
});
|
|
2521
3032
|
|
|
2522
|
-
// src/rules/
|
|
2523
|
-
import { defineRule as
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
}
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
return /^[\t ]*/.exec(text.slice(lineStart, offset))?.[0] ?? "";
|
|
2545
|
-
}
|
|
2546
|
-
function sourceIndentUnit(indent = 2) {
|
|
2547
|
-
return indent === "tab" ? "\t" : " ".repeat(indent);
|
|
2548
|
-
}
|
|
2549
|
-
function sourceNewline(text) {
|
|
2550
|
-
return text.includes(`\r
|
|
2551
|
-
`) ? `\r
|
|
2552
|
-
` : `
|
|
2553
|
-
`;
|
|
2554
|
-
}
|
|
3033
|
+
// src/rules/code/code-yield-argument.ts
|
|
3034
|
+
import { defineRule as defineRule24 } from "@oxlint/plugins";
|
|
3035
|
+
var codeYieldArgument = defineRule24({
|
|
3036
|
+
meta: {
|
|
3037
|
+
type: "suggestion",
|
|
3038
|
+
messages: {
|
|
3039
|
+
named: "Yield into a named const first, then pass the name."
|
|
3040
|
+
}
|
|
3041
|
+
},
|
|
3042
|
+
createOnce(context) {
|
|
3043
|
+
return {
|
|
3044
|
+
YieldExpression(node) {
|
|
3045
|
+
const parent = node.parent;
|
|
3046
|
+
const argument = astIsCall(parent) && parent.arguments.some((item) => item === node);
|
|
3047
|
+
const receiver = parent.type === "MemberExpression" && parent.object === node;
|
|
3048
|
+
if (node.delegate && (argument || receiver)) {
|
|
3049
|
+
context.report({ node, messageId: "named" });
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
};
|
|
3053
|
+
}
|
|
3054
|
+
});
|
|
2555
3055
|
|
|
2556
3056
|
// src/rules/comments/comments-code.ts
|
|
3057
|
+
import { defineRule as defineRule25 } from "@oxlint/plugins";
|
|
2557
3058
|
var commentsLineGap = /^[\t \r\n\u2028\u2029]*$/;
|
|
2558
3059
|
var commentsLineTerminator = /\r\n|[\n\r\u2028\u2029]/;
|
|
2559
3060
|
var commentsUnicodeTerminator = /[\u2028\u2029]/;
|
|
@@ -2604,7 +3105,7 @@ function commentsFollowsOpeningBrace(text, offset) {
|
|
|
2604
3105
|
}
|
|
2605
3106
|
return false;
|
|
2606
3107
|
}
|
|
2607
|
-
var commentsCode =
|
|
3108
|
+
var commentsCode = defineRule25({
|
|
2608
3109
|
meta: {
|
|
2609
3110
|
type: "suggestion",
|
|
2610
3111
|
fixable: "code",
|
|
@@ -2613,7 +3114,7 @@ var commentsCode = defineRule18({
|
|
|
2613
3114
|
type: "object",
|
|
2614
3115
|
additionalProperties: false,
|
|
2615
3116
|
properties: {
|
|
2616
|
-
why: { type: "boolean", default: true },
|
|
3117
|
+
why: { anyOf: [{ type: "boolean" }, { type: "string", minLength: 1 }], default: true },
|
|
2617
3118
|
allow: { type: "array", items: { type: "string" } },
|
|
2618
3119
|
jsdoc: { type: "boolean", default: false },
|
|
2619
3120
|
members: { type: "string", enum: ["off", "also", "only"] }
|
|
@@ -2627,6 +3128,7 @@ var commentsCode = defineRule18({
|
|
|
2627
3128
|
},
|
|
2628
3129
|
createOnce(context) {
|
|
2629
3130
|
let allowWhy = true;
|
|
3131
|
+
let whyPrefix = "Why: ";
|
|
2630
3132
|
let allowJsdoc = false;
|
|
2631
3133
|
let allowed = [];
|
|
2632
3134
|
let members = "off";
|
|
@@ -2651,7 +3153,8 @@ var commentsCode = defineRule18({
|
|
|
2651
3153
|
return {
|
|
2652
3154
|
before() {
|
|
2653
3155
|
const options = optionsFirst(context, {});
|
|
2654
|
-
allowWhy = options.why
|
|
3156
|
+
allowWhy = options.why !== false;
|
|
3157
|
+
whyPrefix = `${typeof options.why === "string" ? options.why.trim() : "Why:"} `;
|
|
2655
3158
|
allowJsdoc = options.jsdoc ?? false;
|
|
2656
3159
|
allowed = (options.allow ?? []).map((pattern) => new RegExp(pattern));
|
|
2657
3160
|
members = options.members ?? "off";
|
|
@@ -2680,7 +3183,7 @@ var commentsCode = defineRule18({
|
|
|
2680
3183
|
mightContainJsxComment = true;
|
|
2681
3184
|
}
|
|
2682
3185
|
const followsPrevious = previous !== undefined && comment.loc.start.line === previous.loc.end.line + 1 && commentsLineGap.test(text.slice(previous.range[1], comment.range[0]));
|
|
2683
|
-
const isWhy = value.startsWith(
|
|
3186
|
+
const isWhy = value.startsWith(whyPrefix) || comment.type === "Line" && whyContinuation && followsPrevious;
|
|
2684
3187
|
if (/^(?:eslint|oxlint)-disable-next-line\b/.test(value)) {
|
|
2685
3188
|
protectedLines.push(comment.loc.end.line + 1);
|
|
2686
3189
|
}
|
|
@@ -2768,9 +3271,9 @@ var commentsCode = defineRule18({
|
|
|
2768
3271
|
});
|
|
2769
3272
|
|
|
2770
3273
|
// src/rules/comments/comments-directives.ts
|
|
2771
|
-
import { defineRule as
|
|
3274
|
+
import { defineRule as defineRule26 } from "@oxlint/plugins";
|
|
2772
3275
|
var directive = /^\s*(?:oxlint|eslint)-disable(?:-next-line|-line)?\b(.*)$/s;
|
|
2773
|
-
var commentsDirectives =
|
|
3276
|
+
var commentsDirectives = defineRule26({
|
|
2774
3277
|
meta: {
|
|
2775
3278
|
type: "problem",
|
|
2776
3279
|
schema: [],
|
|
@@ -2798,9 +3301,9 @@ var commentsDirectives = defineRule19({
|
|
|
2798
3301
|
});
|
|
2799
3302
|
|
|
2800
3303
|
// src/rules/copy/copy-dashes.ts
|
|
2801
|
-
import { defineRule as
|
|
3304
|
+
import { defineRule as defineRule27 } from "@oxlint/plugins";
|
|
2802
3305
|
var copyDashesMessage = "Use a comma, colon, period or parentheses instead.";
|
|
2803
|
-
var copyDashes =
|
|
3306
|
+
var copyDashes = defineRule27({
|
|
2804
3307
|
meta: {
|
|
2805
3308
|
type: "suggestion",
|
|
2806
3309
|
schema: [
|
|
@@ -2856,7 +3359,7 @@ var copyDashes = defineRule20({
|
|
|
2856
3359
|
});
|
|
2857
3360
|
|
|
2858
3361
|
// src/rules/files/files-components-only.ts
|
|
2859
|
-
import { defineRule as
|
|
3362
|
+
import { defineRule as defineRule28 } from "@oxlint/plugins";
|
|
2860
3363
|
|
|
2861
3364
|
// src/utils/react.utils.ts
|
|
2862
3365
|
function reactReturnsOutput(program, node) {
|
|
@@ -2928,7 +3431,7 @@ function reactIsComponent(program, candidate) {
|
|
|
2928
3431
|
function componentName(basename2) {
|
|
2929
3432
|
return basename2.replace(/\.(tsx?|jsx?)$/, "").replaceAll(/(?:^[-.]*|[-.]+)(.?)/g, (_, char) => char.toUpperCase());
|
|
2930
3433
|
}
|
|
2931
|
-
var filesComponentsOnly =
|
|
3434
|
+
var filesComponentsOnly = defineRule28({
|
|
2932
3435
|
meta: {
|
|
2933
3436
|
type: "problem",
|
|
2934
3437
|
schema: [
|
|
@@ -2936,7 +3439,7 @@ var filesComponentsOnly = defineRule21({
|
|
|
2936
3439
|
type: "object",
|
|
2937
3440
|
additionalProperties: false,
|
|
2938
3441
|
properties: {
|
|
2939
|
-
filename: { type: "boolean" },
|
|
3442
|
+
filename: { oneOf: [{ type: "boolean" }, { type: "string", enum: ["each"] }] },
|
|
2940
3443
|
roles: { type: "array", items: { type: "string" } },
|
|
2941
3444
|
types: { type: "boolean" },
|
|
2942
3445
|
deny: optionsPatternSchema
|
|
@@ -2997,10 +3500,13 @@ var filesComponentsOnly = defineRule21({
|
|
|
2997
3500
|
return;
|
|
2998
3501
|
}
|
|
2999
3502
|
const names = components.map((candidate) => candidate.name.toLowerCase());
|
|
3000
|
-
if (expectedKeys.some((key) => names.includes(key) || names.every((name) => name.startsWith(key)))) {
|
|
3503
|
+
if (matchFileName !== "each" && expectedKeys.some((key) => names.includes(key) || names.every((name) => name.startsWith(key)))) {
|
|
3001
3504
|
return;
|
|
3002
3505
|
}
|
|
3003
3506
|
for (const { name, node } of components) {
|
|
3507
|
+
if (matchFileName === "each" && expectedKeys.some((key) => name.toLowerCase().startsWith(key))) {
|
|
3508
|
+
continue;
|
|
3509
|
+
}
|
|
3004
3510
|
context.report({
|
|
3005
3511
|
node,
|
|
3006
3512
|
messageId: "nameMismatch",
|
|
@@ -3013,7 +3519,7 @@ var filesComponentsOnly = defineRule21({
|
|
|
3013
3519
|
});
|
|
3014
3520
|
|
|
3015
3521
|
// src/rules/files/files-constants-only.ts
|
|
3016
|
-
import { defineRule as
|
|
3522
|
+
import { defineRule as defineRule29 } from "@oxlint/plugins";
|
|
3017
3523
|
function unwrap(expression) {
|
|
3018
3524
|
if (expression.type === "ParenthesizedExpression" || expression.type === "TSAsExpression" || expression.type === "TSSatisfiesExpression" || expression.type === "TSTypeAssertion" || expression.type === "TSNonNullExpression" || expression.type === "TSInstantiationExpression") {
|
|
3019
3525
|
return unwrap(expression.expression);
|
|
@@ -3068,7 +3574,7 @@ function collectBindings(program) {
|
|
|
3068
3574
|
}
|
|
3069
3575
|
return bindings;
|
|
3070
3576
|
}
|
|
3071
|
-
var filesConstantsOnly =
|
|
3577
|
+
var filesConstantsOnly = defineRule29({
|
|
3072
3578
|
meta: {
|
|
3073
3579
|
type: "problem",
|
|
3074
3580
|
schema: [
|
|
@@ -3167,7 +3673,7 @@ var filesConstantsOnly = defineRule22({
|
|
|
3167
3673
|
});
|
|
3168
3674
|
|
|
3169
3675
|
// src/rules/files/files-domain-constants.ts
|
|
3170
|
-
import { defineRule as
|
|
3676
|
+
import { defineRule as defineRule30 } from "@oxlint/plugins";
|
|
3171
3677
|
import { basename as basename2, dirname as dirname2 } from "node:path";
|
|
3172
3678
|
function isData(expression) {
|
|
3173
3679
|
const value = astUnwrap(expression);
|
|
@@ -3185,14 +3691,21 @@ function isData(expression) {
|
|
|
3185
3691
|
}
|
|
3186
3692
|
return value.type === "NewExpression" && value.callee.type === "Identifier" && ["Map", "Set"].includes(value.callee.name) && value.arguments[0]?.type === "ArrayExpression" && value.arguments[0].elements.length > 0;
|
|
3187
3693
|
}
|
|
3188
|
-
var filesDomainConstants =
|
|
3694
|
+
var filesDomainConstants = defineRule30({
|
|
3189
3695
|
meta: {
|
|
3190
3696
|
type: "problem",
|
|
3191
3697
|
schema: [
|
|
3192
3698
|
{
|
|
3193
3699
|
type: "object",
|
|
3194
3700
|
additionalProperties: false,
|
|
3195
|
-
properties: {
|
|
3701
|
+
properties: {
|
|
3702
|
+
data: { type: "boolean" },
|
|
3703
|
+
services: { type: "boolean" },
|
|
3704
|
+
pattern: { ...optionsPatternSchema, default: "^[A-Z][A-Z0-9_]+$" },
|
|
3705
|
+
folders: { type: "array", items: { type: "string", minLength: 1 }, default: ["services"] },
|
|
3706
|
+
roles: { type: "array", items: { type: "string", minLength: 1 }, default: ["action", "query"] },
|
|
3707
|
+
file: { type: "string", minLength: 1, default: "{domain}.constants.ts" }
|
|
3708
|
+
}
|
|
3196
3709
|
}
|
|
3197
3710
|
],
|
|
3198
3711
|
messages: {
|
|
@@ -3201,11 +3714,19 @@ var filesDomainConstants = defineRule23({
|
|
|
3201
3714
|
},
|
|
3202
3715
|
createOnce(context) {
|
|
3203
3716
|
let includeData = false;
|
|
3717
|
+
let names = [];
|
|
3718
|
+
let folders = [];
|
|
3719
|
+
let roles = [];
|
|
3720
|
+
let target = "";
|
|
3204
3721
|
let serviceObjects = new Set;
|
|
3205
3722
|
return {
|
|
3206
3723
|
Program(program) {
|
|
3207
3724
|
const options = optionsFirst(context, {});
|
|
3208
3725
|
includeData = Boolean(options.data);
|
|
3726
|
+
names = optionsPatterns(options.pattern ?? "^[A-Z][A-Z0-9_]+$");
|
|
3727
|
+
folders = options.folders ?? ["services"];
|
|
3728
|
+
roles = options.roles ?? ["action", "query"];
|
|
3729
|
+
target = options.file ?? "{domain}.constants.ts";
|
|
3209
3730
|
serviceObjects = new Set(options.services ? declarationsPublicServiceMethods(program).map((method) => method.binding) : []);
|
|
3210
3731
|
},
|
|
3211
3732
|
VariableDeclarator(node) {
|
|
@@ -3215,15 +3736,19 @@ var filesDomainConstants = defineRule23({
|
|
|
3215
3736
|
return;
|
|
3216
3737
|
}
|
|
3217
3738
|
const name = node.id.type === "Identifier" ? node.id.name : undefined;
|
|
3218
|
-
if ((!name ||
|
|
3739
|
+
if ((!name || !optionsPatternsTest(names, name)) && (!includeData || !node.init || !isData(node.init))) {
|
|
3219
3740
|
return;
|
|
3220
3741
|
}
|
|
3221
3742
|
const file = context.filename.replaceAll("\\", "/");
|
|
3222
|
-
const
|
|
3743
|
+
const folder = basename2(dirname2(file));
|
|
3744
|
+
const stem = basename2(file).split(".")[0];
|
|
3745
|
+
const role = roles.find((candidate) => stem.endsWith(`-${candidate}`));
|
|
3746
|
+
const domain = role ? stem.slice(0, -role.length - 1) : stem;
|
|
3747
|
+
const owner = folders.some((candidate) => file.includes(`/${candidate}/`) && folder !== candidate) ? folder : domain;
|
|
3223
3748
|
context.report({
|
|
3224
3749
|
node,
|
|
3225
3750
|
messageId: "forbidden",
|
|
3226
|
-
data: { name: name ?? "<destructured binding>", file:
|
|
3751
|
+
data: { name: name ?? "<destructured binding>", file: target.replaceAll("{domain}", owner) }
|
|
3227
3752
|
});
|
|
3228
3753
|
}
|
|
3229
3754
|
};
|
|
@@ -3231,11 +3756,11 @@ var filesDomainConstants = defineRule23({
|
|
|
3231
3756
|
});
|
|
3232
3757
|
|
|
3233
3758
|
// src/rules/files/files-exports.ts
|
|
3234
|
-
import { defineRule as
|
|
3759
|
+
import { defineRule as defineRule31 } from "@oxlint/plugins";
|
|
3235
3760
|
function escapeRegExp(value) {
|
|
3236
3761
|
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3237
3762
|
}
|
|
3238
|
-
var filesExports =
|
|
3763
|
+
var filesExports = defineRule31({
|
|
3239
3764
|
meta: {
|
|
3240
3765
|
type: "problem",
|
|
3241
3766
|
schema: [
|
|
@@ -3316,11 +3841,12 @@ var filesExports = defineRule24({
|
|
|
3316
3841
|
});
|
|
3317
3842
|
|
|
3318
3843
|
// src/rules/files/files-externals.ts
|
|
3319
|
-
import { defineRule as
|
|
3844
|
+
import { defineRule as defineRule32 } from "@oxlint/plugins";
|
|
3320
3845
|
import { existsSync as existsSync2, readFileSync } from "node:fs";
|
|
3321
3846
|
import { builtinModules as builtinModules2 } from "node:module";
|
|
3322
3847
|
import { dirname as dirname3, join as join2 } from "node:path";
|
|
3323
|
-
var
|
|
3848
|
+
var filesExternalsConfigs = "^(?:vite|next)\\.config\\.[cm]?[jt]s$";
|
|
3849
|
+
var filesExternals = defineRule32({
|
|
3324
3850
|
meta: {
|
|
3325
3851
|
type: "problem",
|
|
3326
3852
|
schema: [
|
|
@@ -3328,7 +3854,8 @@ var filesExternals = defineRule25({
|
|
|
3328
3854
|
type: "object",
|
|
3329
3855
|
additionalProperties: false,
|
|
3330
3856
|
properties: {
|
|
3331
|
-
packages: { type: "array", items: { type: "string", minLength: 1 } }
|
|
3857
|
+
packages: { type: "array", items: { type: "string", minLength: 1 } },
|
|
3858
|
+
files: { ...optionsPatternSchema, default: filesExternalsConfigs }
|
|
3332
3859
|
}
|
|
3333
3860
|
}
|
|
3334
3861
|
],
|
|
@@ -3384,7 +3911,8 @@ var filesExternals = defineRule25({
|
|
|
3384
3911
|
}
|
|
3385
3912
|
return {
|
|
3386
3913
|
before() {
|
|
3387
|
-
|
|
3914
|
+
const files = optionsPatterns(optionsFirst(context, {}).files ?? filesExternalsConfigs);
|
|
3915
|
+
if (!optionsPatternsTest(files, namingBasename(context.filename))) {
|
|
3388
3916
|
return false;
|
|
3389
3917
|
}
|
|
3390
3918
|
},
|
|
@@ -3411,7 +3939,7 @@ var filesExternals = defineRule25({
|
|
|
3411
3939
|
});
|
|
3412
3940
|
|
|
3413
3941
|
// src/rules/files/files-factory-keys.ts
|
|
3414
|
-
import { defineRule as
|
|
3942
|
+
import { defineRule as defineRule33 } from "@oxlint/plugins";
|
|
3415
3943
|
function returnedObject(expression) {
|
|
3416
3944
|
if (expression?.type === "TSAsExpression" || expression?.type === "TSSatisfiesExpression" || expression?.type === "TSTypeAssertion" || expression?.type === "TSNonNullExpression") {
|
|
3417
3945
|
return returnedObject(expression.expression);
|
|
@@ -3422,7 +3950,7 @@ function compileKeys(pattern, keys, requireKeys) {
|
|
|
3422
3950
|
const allowedList = keys.concat(requireKeys.filter((key) => !keys.includes(key)));
|
|
3423
3951
|
return { patterns: optionsPatternsWithEmpty(pattern), allowed: new Set(allowedList), allowedList, required: requireKeys };
|
|
3424
3952
|
}
|
|
3425
|
-
var filesFactoryKeys =
|
|
3953
|
+
var filesFactoryKeys = defineRule33({
|
|
3426
3954
|
meta: {
|
|
3427
3955
|
type: "problem",
|
|
3428
3956
|
schema: [
|
|
@@ -3540,8 +4068,8 @@ var filesFactoryKeys = defineRule26({
|
|
|
3540
4068
|
});
|
|
3541
4069
|
|
|
3542
4070
|
// src/rules/files/files-helpers.ts
|
|
3543
|
-
import { defineRule as
|
|
3544
|
-
var filesHelpers =
|
|
4071
|
+
import { defineRule as defineRule34 } from "@oxlint/plugins";
|
|
4072
|
+
var filesHelpers = defineRule34({
|
|
3545
4073
|
meta: {
|
|
3546
4074
|
type: "problem",
|
|
3547
4075
|
schema: [
|
|
@@ -3589,8 +4117,8 @@ var filesHelpers = defineRule27({
|
|
|
3589
4117
|
});
|
|
3590
4118
|
|
|
3591
4119
|
// src/rules/files/files-no-functions.ts
|
|
3592
|
-
import { defineRule as
|
|
3593
|
-
var filesNoFunctions =
|
|
4120
|
+
import { defineRule as defineRule35 } from "@oxlint/plugins";
|
|
4121
|
+
var filesNoFunctions = defineRule35({
|
|
3594
4122
|
meta: {
|
|
3595
4123
|
type: "problem",
|
|
3596
4124
|
schema: [
|
|
@@ -3644,8 +4172,8 @@ var filesNoFunctions = defineRule28({
|
|
|
3644
4172
|
});
|
|
3645
4173
|
|
|
3646
4174
|
// src/rules/files/files-restricted.ts
|
|
3647
|
-
import { defineRule as
|
|
3648
|
-
var filesRestricted =
|
|
4175
|
+
import { defineRule as defineRule36 } from "@oxlint/plugins";
|
|
4176
|
+
var filesRestricted = defineRule36({
|
|
3649
4177
|
meta: {
|
|
3650
4178
|
type: "problem",
|
|
3651
4179
|
schema: [
|
|
@@ -3677,7 +4205,7 @@ var filesRestricted = defineRule29({
|
|
|
3677
4205
|
});
|
|
3678
4206
|
|
|
3679
4207
|
// src/rules/files/files-route.ts
|
|
3680
|
-
import { defineRule as
|
|
4208
|
+
import { defineRule as defineRule37 } from "@oxlint/plugins";
|
|
3681
4209
|
function inspectAdapter(context, item, bannedHooks, banIntrinsicJsx) {
|
|
3682
4210
|
astVisit(item.node.body, [item.node], (node) => {
|
|
3683
4211
|
if (banIntrinsicJsx && node.type === "JSXOpeningElement" && node.name.type === "JSXIdentifier" && /^[a-z]/.test(node.name.name)) {
|
|
@@ -3697,7 +4225,7 @@ function inspectAdapter(context, item, bannedHooks, banIntrinsicJsx) {
|
|
|
3697
4225
|
}
|
|
3698
4226
|
});
|
|
3699
4227
|
}
|
|
3700
|
-
var filesRoute =
|
|
4228
|
+
var filesRoute = defineRule37({
|
|
3701
4229
|
meta: {
|
|
3702
4230
|
type: "problem",
|
|
3703
4231
|
schema: [
|
|
@@ -3767,7 +4295,7 @@ var filesRoute = defineRule30({
|
|
|
3767
4295
|
});
|
|
3768
4296
|
|
|
3769
4297
|
// src/rules/files/files-types-only.ts
|
|
3770
|
-
import { defineRule as
|
|
4298
|
+
import { defineRule as defineRule38 } from "@oxlint/plugins";
|
|
3771
4299
|
function runtimeImportLocalNames(statement) {
|
|
3772
4300
|
return statement.specifiers.flatMap((specifier) => specifier.type !== "ImportSpecifier" || specifier.importKind !== "type" ? [specifier.local.name] : []);
|
|
3773
4301
|
}
|
|
@@ -3936,7 +4464,7 @@ function runtimeKind(node) {
|
|
|
3936
4464
|
}
|
|
3937
4465
|
return node.type === "FunctionDeclaration" ? "function" : "variable";
|
|
3938
4466
|
}
|
|
3939
|
-
var filesTypesOnly =
|
|
4467
|
+
var filesTypesOnly = defineRule38({
|
|
3940
4468
|
meta: {
|
|
3941
4469
|
type: "problem",
|
|
3942
4470
|
schema: [
|
|
@@ -4043,9 +4571,66 @@ var filesTypesOnly = defineRule31({
|
|
|
4043
4571
|
}
|
|
4044
4572
|
});
|
|
4045
4573
|
|
|
4574
|
+
// src/rules/layout/layout-arrow-body.ts
|
|
4575
|
+
import { defineRule as defineRule39 } from "@oxlint/plugins";
|
|
4576
|
+
var layoutArrowBody = defineRule39({
|
|
4577
|
+
meta: {
|
|
4578
|
+
type: "layout",
|
|
4579
|
+
fixable: "whitespace",
|
|
4580
|
+
schema: [
|
|
4581
|
+
{
|
|
4582
|
+
type: "object",
|
|
4583
|
+
additionalProperties: false,
|
|
4584
|
+
properties: { max: { type: "integer", minimum: 1, default: 160 } }
|
|
4585
|
+
}
|
|
4586
|
+
],
|
|
4587
|
+
messages: {
|
|
4588
|
+
sameLine: "Start the arrow body on the same line as =>. When the line is too long, break its arguments."
|
|
4589
|
+
}
|
|
4590
|
+
},
|
|
4591
|
+
createOnce(context) {
|
|
4592
|
+
let max = 160;
|
|
4593
|
+
return {
|
|
4594
|
+
before() {
|
|
4595
|
+
max = optionsFirst(context, {}).max ?? 160;
|
|
4596
|
+
},
|
|
4597
|
+
ArrowFunctionExpression(node) {
|
|
4598
|
+
const source = context.sourceCode;
|
|
4599
|
+
const body = node.body;
|
|
4600
|
+
const arrow = source.getTokenBefore(body, { filter: (token) => token.value === "=>" });
|
|
4601
|
+
const next = arrow ? source.getTokenAfter(arrow) : null;
|
|
4602
|
+
if (body.type === "BlockStatement" || !arrow || !next || next.loc.start.line === arrow.loc.end.line || source.commentsExistBetween(arrow, next)) {
|
|
4603
|
+
return;
|
|
4604
|
+
}
|
|
4605
|
+
let root = body;
|
|
4606
|
+
let calls = 0;
|
|
4607
|
+
while (root.type === "CallExpression" && root.callee.type === "MemberExpression") {
|
|
4608
|
+
calls += 1;
|
|
4609
|
+
root = root.callee.object;
|
|
4610
|
+
}
|
|
4611
|
+
const open = astIsCall(body) && astIsOneLine(body) ? source.getTokenAfter(body.typeArguments ?? body.callee) : null;
|
|
4612
|
+
const callEnd = open ? open.range[1] : sourceLineEnd(source.text, next.range[0]);
|
|
4613
|
+
const headEnd = calls >= 2 && astIsOneLine(body) ? root.range[1] : callEnd;
|
|
4614
|
+
if (arrow.loc.end.column + 1 + source.text.slice(next.range[0], headEnd).length > max) {
|
|
4615
|
+
return;
|
|
4616
|
+
}
|
|
4617
|
+
context.report({
|
|
4618
|
+
node: body,
|
|
4619
|
+
messageId: "sameLine",
|
|
4620
|
+
fix: (fixer) => fixer.replaceTextRange([arrow.range[1], next.range[0]], " ")
|
|
4621
|
+
});
|
|
4622
|
+
}
|
|
4623
|
+
};
|
|
4624
|
+
}
|
|
4625
|
+
});
|
|
4626
|
+
|
|
4046
4627
|
// src/rules/layout/layout-call-arguments.ts
|
|
4047
|
-
import { defineRule as
|
|
4048
|
-
|
|
4628
|
+
import { defineRule as defineRule40 } from "@oxlint/plugins";
|
|
4629
|
+
function layoutCallArgumentsHuggable(node) {
|
|
4630
|
+
return astIsInlineFunction(node) || node.type === "ObjectExpression" || node.type === "ArrayExpression" || astIsCall(node) && node.arguments.length === 1 && (node.callee.type !== "MemberExpression" || astIsOneLine(node.callee)) && node.arguments.every(layoutCallArgumentsHuggable);
|
|
4631
|
+
}
|
|
4632
|
+
var layoutCallArgumentsMax = { type: "integer", minimum: 1 };
|
|
4633
|
+
var layoutCallArguments = defineRule40({
|
|
4049
4634
|
meta: {
|
|
4050
4635
|
type: "layout",
|
|
4051
4636
|
fixable: "whitespace",
|
|
@@ -4059,22 +4644,43 @@ var layoutCallArguments = defineRule32({
|
|
|
4059
4644
|
minimum: 2,
|
|
4060
4645
|
default: 3
|
|
4061
4646
|
},
|
|
4062
|
-
indent: {
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
default:
|
|
4647
|
+
indent: { ...sourceIndentSchema, default: 2 },
|
|
4648
|
+
split: {
|
|
4649
|
+
type: "boolean",
|
|
4650
|
+
default: true
|
|
4651
|
+
},
|
|
4652
|
+
hug: {
|
|
4653
|
+
type: "object",
|
|
4654
|
+
additionalProperties: false,
|
|
4655
|
+
required: ["max"],
|
|
4656
|
+
properties: { max: layoutCallArgumentsMax }
|
|
4657
|
+
},
|
|
4658
|
+
join: {
|
|
4659
|
+
type: "object",
|
|
4660
|
+
additionalProperties: false,
|
|
4661
|
+
required: ["max"],
|
|
4662
|
+
properties: {
|
|
4663
|
+
max: layoutCallArgumentsMax,
|
|
4664
|
+
nesting: layoutCallArgumentsMax,
|
|
4665
|
+
items: layoutCallArgumentsMax,
|
|
4666
|
+
keep: { type: "array", items: { type: "string" } }
|
|
4667
|
+
}
|
|
4066
4668
|
}
|
|
4067
4669
|
}
|
|
4068
4670
|
}
|
|
4069
4671
|
],
|
|
4070
4672
|
messages: {
|
|
4071
|
-
multiline: "A call with {{min}}+ arguments puts each argument on its own line."
|
|
4673
|
+
multiline: "A call with {{min}}+ arguments puts each argument on its own line.",
|
|
4674
|
+
hug: "Put every argument on its own line. Only a last callback, object or array may hug the call, after simple arguments.",
|
|
4675
|
+
prefer: "Hug the last argument: it is a callback, object or array after simple arguments.",
|
|
4676
|
+
join: "This fits on one line, so keep it on one line."
|
|
4072
4677
|
}
|
|
4073
4678
|
},
|
|
4074
4679
|
createOnce(context) {
|
|
4680
|
+
let options = {};
|
|
4075
4681
|
let minArguments = 3;
|
|
4076
|
-
let
|
|
4077
|
-
const
|
|
4682
|
+
let indentUnit = sourceIndentUnit();
|
|
4683
|
+
const split = (node) => {
|
|
4078
4684
|
const args = node.arguments;
|
|
4079
4685
|
const last = args.at(-1);
|
|
4080
4686
|
const source = context.sourceCode;
|
|
@@ -4097,39 +4703,121 @@ var layoutCallArguments = defineRule32({
|
|
|
4097
4703
|
const unsafe = source.getCommentsInside(node).length > 0 || text[open] !== "(" || text[close] !== ")" || !/^\s*$/.test(between[0]) || between.slice(1, -1).some((gap) => !/^\s*,\s*$/.test(gap)) || !/^\s*,?\s*$/.test(between.at(-1) ?? "");
|
|
4098
4704
|
const baseIndent = sourceLineIndent(text, open);
|
|
4099
4705
|
const newline = sourceNewline(text);
|
|
4100
|
-
const argumentIndent = `${baseIndent}${
|
|
4706
|
+
const argumentIndent = `${baseIndent}${indentUnit}`;
|
|
4707
|
+
context.report({
|
|
4708
|
+
node,
|
|
4709
|
+
messageId: "multiline",
|
|
4710
|
+
data: { min: String(minArguments) },
|
|
4711
|
+
fix: unsafe ? undefined : (fixer) => gaps.map((gap, index) => {
|
|
4712
|
+
if (index === 0) {
|
|
4713
|
+
return fixer.replaceTextRange(gap, `${newline}${argumentIndent}`);
|
|
4714
|
+
}
|
|
4715
|
+
const comma = between[index].includes(",") ? "," : "";
|
|
4716
|
+
return fixer.replaceTextRange(gap, `${comma}${newline}${index === gaps.length - 1 ? baseIndent : argumentIndent}`);
|
|
4717
|
+
})
|
|
4718
|
+
});
|
|
4719
|
+
};
|
|
4720
|
+
const hug = (node, max) => {
|
|
4721
|
+
const source = context.sourceCode;
|
|
4722
|
+
const text = source.text;
|
|
4723
|
+
const args = node.arguments;
|
|
4724
|
+
const first = args.at(0);
|
|
4725
|
+
const last = args.at(-1);
|
|
4726
|
+
const open = source.getTokenAfter(node.typeArguments ?? node.callee);
|
|
4727
|
+
const close = source.getLastToken(node);
|
|
4728
|
+
if (!first || !last || !open || !close || args.every(astIsOneLine) || source.getCommentsInside(node).length > 0) {
|
|
4729
|
+
return;
|
|
4730
|
+
}
|
|
4731
|
+
const leading = args.slice(0, -1);
|
|
4732
|
+
const canHug = !astIsPipe(node) && leading.every((arg) => astIsOneLine(arg) && astCallDepth(arg) <= 1) && layoutCallArgumentsHuggable(last);
|
|
4733
|
+
const gaps = sourceItemGaps(open.range[1], args, close.range[0]);
|
|
4734
|
+
if (first.loc.start.line > open.loc.end.line) {
|
|
4735
|
+
const head = [...leading.map((arg) => source.getText(arg)), text.slice(last.range[0], sourceLineEnd(text, last.range[0]))];
|
|
4736
|
+
const hugged = `${text.slice(sourceLineStart(text, open.range[0]), open.range[1])}${head.join(", ")}`;
|
|
4737
|
+
if (canHug && args.length <= 2 && hugged.length <= max) {
|
|
4738
|
+
context.report({
|
|
4739
|
+
node,
|
|
4740
|
+
messageId: "prefer",
|
|
4741
|
+
fix: (fixer) => sourceRespace(fixer, text, gaps, (index, end) => index === 0 || end ? "" : " ")
|
|
4742
|
+
});
|
|
4743
|
+
}
|
|
4744
|
+
return;
|
|
4745
|
+
}
|
|
4746
|
+
if (astIsOneLine(node) || canHug) {
|
|
4747
|
+
return;
|
|
4748
|
+
}
|
|
4749
|
+
const newline = sourceNewline(text);
|
|
4750
|
+
const base = sourceLineIndent(text, open.range[0]);
|
|
4751
|
+
const inner = `${base}${indentUnit}`;
|
|
4752
|
+
context.report({
|
|
4753
|
+
node,
|
|
4754
|
+
messageId: "hug",
|
|
4755
|
+
fix: (fixer) => sourceRespace(fixer, text, gaps, (_, end) => `${newline}${end ? base : inner}`)
|
|
4756
|
+
});
|
|
4757
|
+
};
|
|
4758
|
+
const join3 = (node, items, open, close) => {
|
|
4759
|
+
const source = context.sourceCode;
|
|
4760
|
+
const text = source.text;
|
|
4761
|
+
const first = items.at(0);
|
|
4762
|
+
if (!first || !open || !close || items.length > Math.min(options.join?.items ?? 2, options.split === false ? Number.POSITIVE_INFINITY : minArguments - 1) || first.loc.start.line === open.loc.end.line || !items.every(astIsOneLine) || source.commentsExistBetween(open, close)) {
|
|
4763
|
+
return;
|
|
4764
|
+
}
|
|
4765
|
+
const gaps = sourceItemGaps(open.range[1], items, close.range[0]);
|
|
4766
|
+
const comma = text.slice(...gaps.at(-1) ?? [0, 0]).includes(",") ? "," : "";
|
|
4767
|
+
if (sourceJoinedWidth(text, open.range[1], `${items.map((item) => source.getText(item)).join(", ")}${comma}`, close.range[0]) > (options.join?.max ?? 0)) {
|
|
4768
|
+
return;
|
|
4769
|
+
}
|
|
4101
4770
|
context.report({
|
|
4102
4771
|
node,
|
|
4103
|
-
messageId: "
|
|
4104
|
-
|
|
4105
|
-
fix: unsafe ? undefined : (fixer) => gaps.map((gap, index) => {
|
|
4106
|
-
if (index === 0) {
|
|
4107
|
-
return fixer.replaceTextRange(gap, `${newline}${argumentIndent}`);
|
|
4108
|
-
}
|
|
4109
|
-
const comma = between[index].includes(",") ? "," : "";
|
|
4110
|
-
return fixer.replaceTextRange(gap, `${comma}${newline}${index === gaps.length - 1 ? baseIndent : argumentIndent}`);
|
|
4111
|
-
})
|
|
4772
|
+
messageId: "join",
|
|
4773
|
+
fix: (fixer) => sourceRespace(fixer, text, gaps, (index, end) => index === 0 || end ? "" : " ")
|
|
4112
4774
|
});
|
|
4113
4775
|
};
|
|
4776
|
+
const call = (node) => {
|
|
4777
|
+
const source = context.sourceCode;
|
|
4778
|
+
if (options.split !== false) {
|
|
4779
|
+
split(node);
|
|
4780
|
+
}
|
|
4781
|
+
if (options.hug) {
|
|
4782
|
+
hug(node, options.hug.max);
|
|
4783
|
+
}
|
|
4784
|
+
if (options.join && !(options.join.keep ?? []).includes(source.getText(node.callee)) && !(astIsPipe(node) && node.arguments.length > 1) && astCallDepth(node) <= (options.join.nesting ?? Number.POSITIVE_INFINITY)) {
|
|
4785
|
+
join3(node, node.arguments, source.getTokenAfter(node.typeArguments ?? node.callee), source.getLastToken(node));
|
|
4786
|
+
}
|
|
4787
|
+
};
|
|
4788
|
+
const parameters = (node) => {
|
|
4789
|
+
const source = context.sourceCode;
|
|
4790
|
+
const first = node.params.at(0);
|
|
4791
|
+
const last = node.params.at(-1);
|
|
4792
|
+
if (!options.join || !first || !last) {
|
|
4793
|
+
return;
|
|
4794
|
+
}
|
|
4795
|
+
join3(node, node.params, source.getTokenBefore(first, { filter: (token) => token.value === "(" }), source.getTokenAfter(last, { filter: (token) => token.value === ")" }));
|
|
4796
|
+
};
|
|
4114
4797
|
return {
|
|
4115
4798
|
before() {
|
|
4116
|
-
|
|
4799
|
+
options = optionsFirst(context, {});
|
|
4117
4800
|
minArguments = options.min ?? 3;
|
|
4118
|
-
|
|
4801
|
+
indentUnit = sourceIndentUnit(options.indent);
|
|
4119
4802
|
},
|
|
4120
|
-
CallExpression:
|
|
4121
|
-
NewExpression:
|
|
4803
|
+
CallExpression: call,
|
|
4804
|
+
NewExpression: call,
|
|
4805
|
+
FunctionExpression: parameters,
|
|
4806
|
+
FunctionDeclaration: parameters,
|
|
4807
|
+
ArrowFunctionExpression: parameters,
|
|
4808
|
+
TSFunctionType: parameters,
|
|
4809
|
+
TSMethodSignature: parameters
|
|
4122
4810
|
};
|
|
4123
4811
|
}
|
|
4124
4812
|
});
|
|
4125
4813
|
|
|
4126
4814
|
// src/rules/layout/layout-call-array.ts
|
|
4127
|
-
import { defineRule as
|
|
4815
|
+
import { defineRule as defineRule41 } from "@oxlint/plugins";
|
|
4128
4816
|
var layoutCallArrayDefaultCallees = ["Promise.all", "Promise.allSettled"];
|
|
4129
4817
|
function layoutCallArrayRoot(node) {
|
|
4130
4818
|
return node.type === "MemberExpression" ? layoutCallArrayRoot(node.object) : node;
|
|
4131
4819
|
}
|
|
4132
|
-
var layoutCallArray =
|
|
4820
|
+
var layoutCallArray = defineRule41({
|
|
4133
4821
|
meta: {
|
|
4134
4822
|
type: "layout",
|
|
4135
4823
|
fixable: "whitespace",
|
|
@@ -4244,9 +4932,56 @@ var layoutCallArray = defineRule33({
|
|
|
4244
4932
|
}
|
|
4245
4933
|
});
|
|
4246
4934
|
|
|
4935
|
+
// src/rules/layout/layout-call-depth.ts
|
|
4936
|
+
import { defineRule as defineRule42 } from "@oxlint/plugins";
|
|
4937
|
+
function layoutCallDepthOuter(node) {
|
|
4938
|
+
const parent = node.parent;
|
|
4939
|
+
if (!parent || parent.type === "ArrowFunctionExpression" || parent.type === "FunctionExpression" || parent.type.endsWith("Statement")) {
|
|
4940
|
+
return;
|
|
4941
|
+
}
|
|
4942
|
+
return astIsCall(parent) && parent.arguments.some((argument) => argument === node) ? parent : layoutCallDepthOuter(parent);
|
|
4943
|
+
}
|
|
4944
|
+
var layoutCallDepth = defineRule42({
|
|
4945
|
+
meta: {
|
|
4946
|
+
type: "layout",
|
|
4947
|
+
schema: [
|
|
4948
|
+
{
|
|
4949
|
+
type: "object",
|
|
4950
|
+
additionalProperties: false,
|
|
4951
|
+
properties: { max: { type: "integer", minimum: 1, default: 2 } }
|
|
4952
|
+
}
|
|
4953
|
+
],
|
|
4954
|
+
messages: {
|
|
4955
|
+
deep: "Calls nest {{depth}} deep on one line (max {{max}}). Break the outer call or name an inner value."
|
|
4956
|
+
}
|
|
4957
|
+
},
|
|
4958
|
+
createOnce(context) {
|
|
4959
|
+
let max = 2;
|
|
4960
|
+
const check = (node) => {
|
|
4961
|
+
const depth = astCallDepth(node);
|
|
4962
|
+
const outer = layoutCallDepthOuter(node);
|
|
4963
|
+
if (!astIsOneLine(node) || depth <= max || outer !== undefined && astIsOneLine(outer) && astCallDepth(outer) > max) {
|
|
4964
|
+
return;
|
|
4965
|
+
}
|
|
4966
|
+
context.report({
|
|
4967
|
+
node,
|
|
4968
|
+
messageId: "deep",
|
|
4969
|
+
data: { depth: String(depth), max: String(max) }
|
|
4970
|
+
});
|
|
4971
|
+
};
|
|
4972
|
+
return {
|
|
4973
|
+
before() {
|
|
4974
|
+
max = optionsFirst(context, {}).max ?? 2;
|
|
4975
|
+
},
|
|
4976
|
+
CallExpression: check,
|
|
4977
|
+
NewExpression: check
|
|
4978
|
+
};
|
|
4979
|
+
}
|
|
4980
|
+
});
|
|
4981
|
+
|
|
4247
4982
|
// src/rules/layout/layout-chain.ts
|
|
4248
|
-
import { defineRule as
|
|
4249
|
-
var layoutChain =
|
|
4983
|
+
import { defineRule as defineRule43 } from "@oxlint/plugins";
|
|
4984
|
+
var layoutChain = defineRule43({
|
|
4250
4985
|
meta: {
|
|
4251
4986
|
type: "layout",
|
|
4252
4987
|
fixable: "whitespace",
|
|
@@ -4364,9 +5099,65 @@ var layoutChain = defineRule34({
|
|
|
4364
5099
|
}
|
|
4365
5100
|
});
|
|
4366
5101
|
|
|
5102
|
+
// src/rules/layout/layout-items.ts
|
|
5103
|
+
import { defineRule as defineRule44 } from "@oxlint/plugins";
|
|
5104
|
+
var layoutItems = defineRule44({
|
|
5105
|
+
meta: {
|
|
5106
|
+
type: "layout",
|
|
5107
|
+
fixable: "whitespace",
|
|
5108
|
+
schema: [
|
|
5109
|
+
{
|
|
5110
|
+
type: "object",
|
|
5111
|
+
additionalProperties: false,
|
|
5112
|
+
properties: { indent: { ...sourceIndentSchema, default: 2 } }
|
|
5113
|
+
}
|
|
5114
|
+
],
|
|
5115
|
+
messages: {
|
|
5116
|
+
object: "A multi-line object puts each property on its own line.",
|
|
5117
|
+
array: "A multi-line array puts each element on its own line."
|
|
5118
|
+
}
|
|
5119
|
+
},
|
|
5120
|
+
createOnce(context) {
|
|
5121
|
+
let indentUnit = sourceIndentUnit();
|
|
5122
|
+
const check = (node) => {
|
|
5123
|
+
const source = context.sourceCode;
|
|
5124
|
+
const items = node.type === "ObjectExpression" ? node.properties : node.elements;
|
|
5125
|
+
const present = items.filter((item) => item !== null);
|
|
5126
|
+
const open = source.getFirstToken(node);
|
|
5127
|
+
const close = source.getLastToken(node);
|
|
5128
|
+
if (!open || !close || astIsOneLine(node) || present.length < 2) {
|
|
5129
|
+
return;
|
|
5130
|
+
}
|
|
5131
|
+
const packed = [open, ...present].some((item, index) => {
|
|
5132
|
+
const next = present.at(index);
|
|
5133
|
+
return next !== undefined && item.loc.end.line === next.loc.start.line;
|
|
5134
|
+
});
|
|
5135
|
+
if (!packed) {
|
|
5136
|
+
return;
|
|
5137
|
+
}
|
|
5138
|
+
const text = source.text;
|
|
5139
|
+
const newline = sourceNewline(text);
|
|
5140
|
+
const base = sourceLineIndent(text, node.range[0]);
|
|
5141
|
+
const gaps = sourceItemGaps(open.range[1], present, close.range[0]);
|
|
5142
|
+
context.report({
|
|
5143
|
+
node,
|
|
5144
|
+
messageId: node.type === "ObjectExpression" ? "object" : "array",
|
|
5145
|
+
fix: (fixer) => present.length === items.length ? sourceRespace(fixer, text, gaps, (_, end) => `${newline}${base}${end ? "" : indentUnit}`) : null
|
|
5146
|
+
});
|
|
5147
|
+
};
|
|
5148
|
+
return {
|
|
5149
|
+
before() {
|
|
5150
|
+
indentUnit = sourceIndentUnit(optionsFirst(context, {}).indent);
|
|
5151
|
+
},
|
|
5152
|
+
ObjectExpression: check,
|
|
5153
|
+
ArrayExpression: check
|
|
5154
|
+
};
|
|
5155
|
+
}
|
|
5156
|
+
});
|
|
5157
|
+
|
|
4367
5158
|
// src/rules/layout/layout-jsx-attributes.ts
|
|
4368
|
-
import { defineRule as
|
|
4369
|
-
var layoutJsxAttributes =
|
|
5159
|
+
import { defineRule as defineRule45 } from "@oxlint/plugins";
|
|
5160
|
+
var layoutJsxAttributes = defineRule45({
|
|
4370
5161
|
meta: {
|
|
4371
5162
|
type: "layout",
|
|
4372
5163
|
fixable: "whitespace",
|
|
@@ -4376,12 +5167,19 @@ var layoutJsxAttributes = defineRule35({
|
|
|
4376
5167
|
additionalProperties: false,
|
|
4377
5168
|
properties: {
|
|
4378
5169
|
min: { type: "integer", minimum: 1 },
|
|
4379
|
-
indent: sourceIndentSchema
|
|
5170
|
+
indent: sourceIndentSchema,
|
|
5171
|
+
join: {
|
|
5172
|
+
type: "object",
|
|
5173
|
+
additionalProperties: false,
|
|
5174
|
+
required: ["max"],
|
|
5175
|
+
properties: { max: { type: "integer", minimum: 1 } }
|
|
5176
|
+
}
|
|
4380
5177
|
}
|
|
4381
5178
|
}
|
|
4382
5179
|
],
|
|
4383
5180
|
messages: {
|
|
4384
|
-
multiline: "Put every JSX attribute and the closing token on its own line."
|
|
5181
|
+
multiline: "Put every JSX attribute and the closing token on its own line.",
|
|
5182
|
+
join: "This tag fits on one line, so keep it on one line."
|
|
4385
5183
|
}
|
|
4386
5184
|
},
|
|
4387
5185
|
createOnce(context) {
|
|
@@ -4389,35 +5187,52 @@ var layoutJsxAttributes = defineRule35({
|
|
|
4389
5187
|
let indentUnit = sourceIndentUnit();
|
|
4390
5188
|
let newline = `
|
|
4391
5189
|
`;
|
|
5190
|
+
let joinMax;
|
|
4392
5191
|
return {
|
|
4393
5192
|
before() {
|
|
4394
5193
|
const options = optionsFirst(context, {});
|
|
4395
5194
|
minAttributes = options.min ?? 3;
|
|
4396
5195
|
indentUnit = sourceIndentUnit(options.indent);
|
|
4397
5196
|
newline = sourceNewline(context.sourceCode.text);
|
|
5197
|
+
joinMax = options.join?.max;
|
|
4398
5198
|
},
|
|
4399
5199
|
JSXOpeningElement(node) {
|
|
4400
|
-
if (node.attributes.length < minAttributes || node.loc.start.line !== node.loc.end.line) {
|
|
4401
|
-
return;
|
|
4402
|
-
}
|
|
4403
5200
|
const source = context.sourceCode;
|
|
4404
5201
|
const text = source.text;
|
|
5202
|
+
const attributes = node.attributes;
|
|
5203
|
+
const split = attributes.length >= minAttributes;
|
|
5204
|
+
if (split ? !astIsOneLine(node) : joinMax === undefined || astIsOneLine(node)) {
|
|
5205
|
+
return;
|
|
5206
|
+
}
|
|
5207
|
+
if (!split) {
|
|
5208
|
+
const start = (node.typeArguments ?? node.name).range[1];
|
|
5209
|
+
if (attributes.length === 0 || !attributes.every(astIsOneLine) || text.slice(node.range[0], start).includes(`
|
|
5210
|
+
`) || source.getCommentsInside(node).length > 0) {
|
|
5211
|
+
return;
|
|
5212
|
+
}
|
|
5213
|
+
const end = node.range[1] - (node.selfClosing ? 2 : 1);
|
|
5214
|
+
const ranges = sourceItemGaps(start, attributes, end);
|
|
5215
|
+
const closing = node.selfClosing ? " " : "";
|
|
5216
|
+
if (sourceJoinedWidth(text, start, ` ${attributes.map((attribute) => source.getText(attribute)).join(" ")}${closing}`, end) > (joinMax ?? 0)) {
|
|
5217
|
+
return;
|
|
5218
|
+
}
|
|
5219
|
+
context.report({
|
|
5220
|
+
node,
|
|
5221
|
+
messageId: "join",
|
|
5222
|
+
fix: (fixer) => ranges.some((range) => !/^\s*$/.test(text.slice(...range))) ? null : ranges.map((range, index) => fixer.replaceTextRange(range, index === ranges.length - 1 ? closing : " "))
|
|
5223
|
+
});
|
|
5224
|
+
return;
|
|
5225
|
+
}
|
|
4405
5226
|
const baseIndent = sourceLineIndent(text, node.range[0]);
|
|
4406
5227
|
const attributeIndent = baseIndent + indentUnit;
|
|
4407
|
-
const attributes = node.attributes;
|
|
4408
5228
|
context.report({
|
|
4409
5229
|
node,
|
|
4410
5230
|
messageId: "multiline",
|
|
4411
5231
|
fix: (fixer) => {
|
|
4412
|
-
|
|
4413
|
-
if (source.getCommentsInside(node).length > 0 || !last) {
|
|
5232
|
+
if (source.getCommentsInside(node).length > 0) {
|
|
4414
5233
|
return null;
|
|
4415
5234
|
}
|
|
4416
|
-
const ranges = [
|
|
4417
|
-
[(node.typeArguments ?? node.name).range[1], attributes[0].range[0]],
|
|
4418
|
-
...attributes.slice(1).map((attribute, index) => [attributes[index].range[1], attribute.range[0]]),
|
|
4419
|
-
[last.range[1], node.range[1] - (node.selfClosing ? 2 : 1)]
|
|
4420
|
-
];
|
|
5235
|
+
const ranges = sourceItemGaps((node.typeArguments ?? node.name).range[1], attributes, node.range[1] - (node.selfClosing ? 2 : 1));
|
|
4421
5236
|
if (ranges.some((range) => !/^\s*$/.test(text.slice(...range)))) {
|
|
4422
5237
|
return null;
|
|
4423
5238
|
}
|
|
@@ -4430,8 +5245,8 @@ var layoutJsxAttributes = defineRule35({
|
|
|
4430
5245
|
});
|
|
4431
5246
|
|
|
4432
5247
|
// src/rules/layout/layout-key-value.ts
|
|
4433
|
-
import { defineRule as
|
|
4434
|
-
var layoutKeyValue =
|
|
5248
|
+
import { defineRule as defineRule46 } from "@oxlint/plugins";
|
|
5249
|
+
var layoutKeyValue = defineRule46({
|
|
4435
5250
|
meta: {
|
|
4436
5251
|
type: "layout",
|
|
4437
5252
|
fixable: "whitespace",
|
|
@@ -4467,11 +5282,15 @@ var layoutKeyValue = defineRule36({
|
|
|
4467
5282
|
});
|
|
4468
5283
|
|
|
4469
5284
|
// src/rules/layout/layout-object.ts
|
|
4470
|
-
import { defineRule as
|
|
5285
|
+
import { defineRule as defineRule47 } from "@oxlint/plugins";
|
|
4471
5286
|
function layoutObjectIsWhitespace(text) {
|
|
4472
5287
|
return /^[\t\n\r\f ]*$/.test(text);
|
|
4473
5288
|
}
|
|
4474
|
-
|
|
5289
|
+
function layoutObjectIsFlat(property) {
|
|
5290
|
+
const value = property.type === "SpreadElement" ? property.argument : property.value;
|
|
5291
|
+
return astIsOneLine(property) && (property.type === "SpreadElement" || !property.method && property.kind === "init") && value.type !== "ObjectExpression" && value.type !== "ArrayExpression" && !astIsInlineFunction(value);
|
|
5292
|
+
}
|
|
5293
|
+
var layoutObject = defineRule47({
|
|
4475
5294
|
meta: {
|
|
4476
5295
|
type: "layout",
|
|
4477
5296
|
fixable: "whitespace",
|
|
@@ -4482,13 +5301,23 @@ var layoutObject = defineRule37({
|
|
|
4482
5301
|
properties: {
|
|
4483
5302
|
min: { type: "integer", minimum: 1, default: 3 },
|
|
4484
5303
|
scope: { type: "string", enum: ["call-args", "all"], default: "call-args" },
|
|
4485
|
-
indent: { ...sourceIndentSchema, default: 2 }
|
|
5304
|
+
indent: { ...sourceIndentSchema, default: 2 },
|
|
5305
|
+
join: {
|
|
5306
|
+
type: "object",
|
|
5307
|
+
additionalProperties: false,
|
|
5308
|
+
required: ["max"],
|
|
5309
|
+
properties: {
|
|
5310
|
+
max: { type: "integer", minimum: 1 },
|
|
5311
|
+
keep: { type: "array", items: { type: "string" } }
|
|
5312
|
+
}
|
|
5313
|
+
}
|
|
4486
5314
|
}
|
|
4487
5315
|
}
|
|
4488
5316
|
],
|
|
4489
5317
|
messages: {
|
|
4490
5318
|
callArgs: "Object with {{min}}+ properties in a call should have each property on its own line.",
|
|
4491
|
-
always: "Object with {{min}}+ properties should have each property on its own line."
|
|
5319
|
+
always: "Object with {{min}}+ properties should have each property on its own line.",
|
|
5320
|
+
join: "This object fits on one line, so keep it on one line."
|
|
4492
5321
|
}
|
|
4493
5322
|
},
|
|
4494
5323
|
createOnce(context) {
|
|
@@ -4497,6 +5326,7 @@ var layoutObject = defineRule37({
|
|
|
4497
5326
|
let indentUnit = sourceIndentUnit();
|
|
4498
5327
|
let newline = `
|
|
4499
5328
|
`;
|
|
5329
|
+
let join3;
|
|
4500
5330
|
return {
|
|
4501
5331
|
before() {
|
|
4502
5332
|
const options = optionsFirst(context, {});
|
|
@@ -4504,21 +5334,43 @@ var layoutObject = defineRule37({
|
|
|
4504
5334
|
scope = options.scope ?? "call-args";
|
|
4505
5335
|
indentUnit = sourceIndentUnit(options.indent);
|
|
4506
5336
|
newline = sourceNewline(context.sourceCode.text);
|
|
5337
|
+
join3 = options.join;
|
|
4507
5338
|
},
|
|
4508
5339
|
ObjectExpression(node) {
|
|
4509
|
-
|
|
5340
|
+
const source = context.sourceCode;
|
|
5341
|
+
const text = source.text;
|
|
5342
|
+
const properties = node.properties;
|
|
5343
|
+
const split = properties.length >= minProperties;
|
|
5344
|
+
if (split ? !astIsOneLine(node) : !join3 || astIsOneLine(node)) {
|
|
4510
5345
|
return;
|
|
4511
5346
|
}
|
|
4512
5347
|
const parent = node.parent;
|
|
4513
|
-
|
|
5348
|
+
const call = (parent.type === "CallExpression" || parent.type === "NewExpression") && parent.arguments.includes(node) ? parent : undefined;
|
|
5349
|
+
if (scope === "call-args" && !call) {
|
|
5350
|
+
return;
|
|
5351
|
+
}
|
|
5352
|
+
if (!split) {
|
|
5353
|
+
const callee = call ? astDottedName(call.callee) : undefined;
|
|
5354
|
+
const kept = callee !== undefined && (join3?.keep ?? []).some((name) => callee === name || callee.endsWith(`.${name}`));
|
|
5355
|
+
if (kept || properties.length === 0 || !properties.every(layoutObjectIsFlat) || source.getCommentsInside(node).length > 0) {
|
|
5356
|
+
return;
|
|
5357
|
+
}
|
|
5358
|
+
const gaps = sourceItemGaps(node.range[0] + 1, properties, node.range[1] - 1);
|
|
5359
|
+
const comma = text.slice(...gaps.at(-1) ?? [0, 0]).includes(",") ? "," : "";
|
|
5360
|
+
const joined = ` ${properties.map((property) => source.getText(property)).join(", ")}${comma} `;
|
|
5361
|
+
if (sourceJoinedWidth(text, node.range[0] + 1, joined, node.range[1] - 1) > (join3?.max ?? 0)) {
|
|
5362
|
+
return;
|
|
5363
|
+
}
|
|
5364
|
+
context.report({
|
|
5365
|
+
node,
|
|
5366
|
+
messageId: "join",
|
|
5367
|
+
fix: (fixer) => sourceRespace(fixer, text, gaps, () => " ")
|
|
5368
|
+
});
|
|
4514
5369
|
return;
|
|
4515
5370
|
}
|
|
4516
|
-
const source = context.sourceCode;
|
|
4517
|
-
const text = source.text;
|
|
4518
5371
|
const baseIndent = sourceLineIndent(text, node.range[0]);
|
|
4519
5372
|
const propertyIndent = `${baseIndent}${indentUnit}`;
|
|
4520
5373
|
const close = node.range[1] - 1;
|
|
4521
|
-
const properties = node.properties;
|
|
4522
5374
|
context.report({
|
|
4523
5375
|
node,
|
|
4524
5376
|
messageId: scope === "all" ? "always" : "callArgs",
|
|
@@ -4555,7 +5407,7 @@ var layoutObject = defineRule37({
|
|
|
4555
5407
|
});
|
|
4556
5408
|
|
|
4557
5409
|
// src/rules/layout/layout-padding.ts
|
|
4558
|
-
import { defineRule as
|
|
5410
|
+
import { defineRule as defineRule48 } from "@oxlint/plugins";
|
|
4559
5411
|
var layoutPaddingControl = new Set([
|
|
4560
5412
|
"IfStatement",
|
|
4561
5413
|
"ForStatement",
|
|
@@ -4584,7 +5436,7 @@ function layoutPaddingKind(node) {
|
|
|
4584
5436
|
function layoutPaddingIsMultilineVariable(node, kind) {
|
|
4585
5437
|
return kind === "variable" && node.loc.start.line !== node.loc.end.line;
|
|
4586
5438
|
}
|
|
4587
|
-
var layoutPadding =
|
|
5439
|
+
var layoutPadding = defineRule48({
|
|
4588
5440
|
meta: {
|
|
4589
5441
|
type: "layout",
|
|
4590
5442
|
fixable: "whitespace",
|
|
@@ -4672,13 +5524,48 @@ var layoutPadding = defineRule38({
|
|
|
4672
5524
|
}
|
|
4673
5525
|
});
|
|
4674
5526
|
|
|
5527
|
+
// src/rules/layout/layout-pipe.ts
|
|
5528
|
+
import { defineRule as defineRule49 } from "@oxlint/plugins";
|
|
5529
|
+
var layoutPipe = defineRule49({
|
|
5530
|
+
meta: {
|
|
5531
|
+
type: "layout",
|
|
5532
|
+
messages: {
|
|
5533
|
+
after: "No ).pipe( after a call that spans lines. Name the value and pipe the name, or pass the call to the step data-first.",
|
|
5534
|
+
step: "A pipe step may span lines only through a callback, object or array. Name this call first, then pass the name."
|
|
5535
|
+
}
|
|
5536
|
+
},
|
|
5537
|
+
createOnce(context) {
|
|
5538
|
+
return {
|
|
5539
|
+
CallExpression(node) {
|
|
5540
|
+
const callee = node.callee;
|
|
5541
|
+
if (callee.type === "MemberExpression" && astMemberName(callee) === "pipe" && astIsCall(callee.object) && !astIsOneLine(callee.object) && callee.property.loc.start.line === callee.object.loc.end.line) {
|
|
5542
|
+
context.report({ node: callee.property, messageId: "after" });
|
|
5543
|
+
}
|
|
5544
|
+
if (!astIsPipe(node)) {
|
|
5545
|
+
return;
|
|
5546
|
+
}
|
|
5547
|
+
for (const step of node.arguments) {
|
|
5548
|
+
for (const argument of astIsCall(step) ? step.arguments : []) {
|
|
5549
|
+
if (astIsCall(argument) && !astIsOneLine(argument)) {
|
|
5550
|
+
context.report({ node: argument, messageId: "step" });
|
|
5551
|
+
}
|
|
5552
|
+
}
|
|
5553
|
+
}
|
|
5554
|
+
}
|
|
5555
|
+
};
|
|
5556
|
+
}
|
|
5557
|
+
});
|
|
5558
|
+
|
|
4675
5559
|
// src/rules/layout/layout-ternary.ts
|
|
4676
|
-
import { defineRule as
|
|
5560
|
+
import { defineRule as defineRule50 } from "@oxlint/plugins";
|
|
5561
|
+
function layoutTernaryLines(node) {
|
|
5562
|
+
return node.loc.end.line - node.loc.start.line + 1;
|
|
5563
|
+
}
|
|
4677
5564
|
function layoutTernaryBreak(between, operator, newline) {
|
|
4678
5565
|
const at = between.indexOf(operator);
|
|
4679
5566
|
return `${between.slice(0, at).trimEnd()}${newline}${operator} ${between.slice(at + 1).trimStart()}`;
|
|
4680
5567
|
}
|
|
4681
|
-
var layoutTernary =
|
|
5568
|
+
var layoutTernary = defineRule50({
|
|
4682
5569
|
meta: {
|
|
4683
5570
|
type: "layout",
|
|
4684
5571
|
fixable: "whitespace",
|
|
@@ -4692,30 +5579,62 @@ var layoutTernary = defineRule39({
|
|
|
4692
5579
|
minimum: 1,
|
|
4693
5580
|
default: 160
|
|
4694
5581
|
},
|
|
4695
|
-
indent: {
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
default:
|
|
5582
|
+
indent: { ...sourceIndentSchema, default: 2 },
|
|
5583
|
+
fit: {
|
|
5584
|
+
type: "boolean",
|
|
5585
|
+
default: true
|
|
5586
|
+
},
|
|
5587
|
+
branch: {
|
|
5588
|
+
type: "object",
|
|
5589
|
+
additionalProperties: false,
|
|
5590
|
+
required: ["max"],
|
|
5591
|
+
properties: { max: { type: "integer", minimum: 1 } }
|
|
4699
5592
|
}
|
|
4700
5593
|
}
|
|
4701
5594
|
}
|
|
4702
5595
|
],
|
|
4703
5596
|
messages: {
|
|
5597
|
+
long: "This ternary branch spans {{lines}} lines (max {{max}}). Name it.",
|
|
5598
|
+
yield: "A yield* ternary takes one-line operands. Name the multi-line branch.",
|
|
4704
5599
|
join: "Keep this conditional on one line ({{length}} columns; limit {{max}}, except in template literals).",
|
|
4705
5600
|
split: "This conditional does not fit on one line ({{length}} of {{max}} columns); break before `?` and `:`."
|
|
4706
5601
|
}
|
|
4707
5602
|
},
|
|
4708
5603
|
createOnce(context) {
|
|
4709
5604
|
let maxLength = 160;
|
|
4710
|
-
let
|
|
5605
|
+
let indentUnit = sourceIndentUnit();
|
|
5606
|
+
let fit = true;
|
|
5607
|
+
let branchMax;
|
|
5608
|
+
const branch = (node, max) => {
|
|
5609
|
+
const long = [node.consequent, node.alternate].filter((operand) => layoutTernaryLines(operand) > max);
|
|
5610
|
+
for (const operand of long) {
|
|
5611
|
+
context.report({
|
|
5612
|
+
node: operand,
|
|
5613
|
+
messageId: "long",
|
|
5614
|
+
data: { lines: String(layoutTernaryLines(operand)), max: String(max) }
|
|
5615
|
+
});
|
|
5616
|
+
}
|
|
5617
|
+
const yielded = node.parent.type === "YieldExpression" && node.parent.delegate;
|
|
5618
|
+
if (yielded && long.length === 0 && [node.test, node.consequent, node.alternate].some((operand) => layoutTernaryLines(operand) > 1)) {
|
|
5619
|
+
context.report({ node, messageId: "yield" });
|
|
5620
|
+
}
|
|
5621
|
+
};
|
|
4711
5622
|
return {
|
|
4712
5623
|
before() {
|
|
4713
5624
|
const options = optionsFirst(context, {});
|
|
4714
5625
|
maxLength = options.max ?? 160;
|
|
4715
|
-
|
|
5626
|
+
indentUnit = sourceIndentUnit(options.indent);
|
|
5627
|
+
fit = options.fit ?? true;
|
|
5628
|
+
branchMax = options.branch?.max;
|
|
4716
5629
|
},
|
|
4717
5630
|
ConditionalExpression(node) {
|
|
4718
5631
|
const source = context.sourceCode;
|
|
5632
|
+
if (branchMax !== undefined) {
|
|
5633
|
+
branch(node, branchMax);
|
|
5634
|
+
}
|
|
5635
|
+
if (!fit) {
|
|
5636
|
+
return;
|
|
5637
|
+
}
|
|
4719
5638
|
const operands = [node.test, node.consequent, node.alternate];
|
|
4720
5639
|
if (node.parent.type === "ConditionalExpression" || operands.some((operand) => operand.type === "ConditionalExpression" || operand.loc.start.line !== operand.loc.end.line) || source.getCommentsInside(node).length > 0) {
|
|
4721
5640
|
return;
|
|
@@ -4744,7 +5663,7 @@ var layoutTernary = defineRule39({
|
|
|
4744
5663
|
if (!onOneLine || interpolated || length <= maxLength) {
|
|
4745
5664
|
return;
|
|
4746
5665
|
}
|
|
4747
|
-
const newline = `${sourceNewline(text)}${sourceLineIndent(text, node.range[0])}${
|
|
5666
|
+
const newline = `${sourceNewline(text)}${sourceLineIndent(text, node.range[0])}${indentUnit}`;
|
|
4748
5667
|
const question = [node.test.range[1], node.consequent.range[0]];
|
|
4749
5668
|
const colon = [node.consequent.range[1], node.alternate.range[0]];
|
|
4750
5669
|
context.report({
|
|
@@ -4762,9 +5681,9 @@ var layoutTernary = defineRule39({
|
|
|
4762
5681
|
});
|
|
4763
5682
|
|
|
4764
5683
|
// src/rules/naming/naming-declarations.ts
|
|
4765
|
-
import { defineRule as
|
|
5684
|
+
import { defineRule as defineRule51 } from "@oxlint/plugins";
|
|
4766
5685
|
var namingDeclarationsKinds = ["function", "const", "let", "var", "class", "type", "interface", "enum"];
|
|
4767
|
-
var namingDeclarations =
|
|
5686
|
+
var namingDeclarations = defineRule51({
|
|
4768
5687
|
meta: {
|
|
4769
5688
|
type: "problem",
|
|
4770
5689
|
schema: [
|
|
@@ -4796,11 +5715,9 @@ var namingDeclarations = defineRule40({
|
|
|
4796
5715
|
const declarator = node.type === "VariableDeclarator";
|
|
4797
5716
|
const kind = declarationsKindOf(node, declarator ? [node.parent] : []);
|
|
4798
5717
|
const name = declarationsIdName(node);
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
}
|
|
4803
|
-
if (!kind || !name || insideLoop || kinds && !kinds.has(kind) || optionsPatternsTest(allowed, name)) {
|
|
5718
|
+
const loop = declarator ? node.parent.parent : null;
|
|
5719
|
+
const loopHeader = loop?.type === "ForStatement" ? loop.init === node.parent : (loop?.type === "ForInStatement" || loop?.type === "ForOfStatement") && loop.left === node.parent;
|
|
5720
|
+
if (!kind || !name || loopHeader || kinds && !kinds.has(kind) || optionsPatternsTest(allowed, name)) {
|
|
4804
5721
|
return;
|
|
4805
5722
|
}
|
|
4806
5723
|
if (expected.length > 0) {
|
|
@@ -4843,7 +5760,7 @@ var namingDeclarations = defineRule40({
|
|
|
4843
5760
|
});
|
|
4844
5761
|
|
|
4845
5762
|
// src/rules/naming/naming-exports.ts
|
|
4846
|
-
import { defineRule as
|
|
5763
|
+
import { defineRule as defineRule52 } from "@oxlint/plugins";
|
|
4847
5764
|
var namingExportsScopeProperties = {
|
|
4848
5765
|
scope: { type: "string", enum: ["exports", "functions", "declarations"] },
|
|
4849
5766
|
types: { type: "boolean" },
|
|
@@ -4869,7 +5786,7 @@ function matchTemplate(basename3, template, placeholder = "[a-z0-9-]+") {
|
|
|
4869
5786
|
}
|
|
4870
5787
|
return new RegExp(`^${pattern}$`).exec(basename3)?.groups ?? null;
|
|
4871
5788
|
}
|
|
4872
|
-
var namingExports =
|
|
5789
|
+
var namingExports = defineRule52({
|
|
4873
5790
|
meta: {
|
|
4874
5791
|
type: "problem",
|
|
4875
5792
|
schema: {
|
|
@@ -4964,9 +5881,12 @@ var namingExports = defineRule41({
|
|
|
4964
5881
|
}
|
|
4965
5882
|
continue;
|
|
4966
5883
|
}
|
|
4967
|
-
const names = scope === "declarations" ? [...declarations ??= declarationsNamed(program)] : (bindings ??= exportsCollect(program)).flatMap((binding) =>
|
|
5884
|
+
const names = scope === "declarations" ? [...declarations ??= declarationsNamed(program)] : (bindings ??= exportsCollect(program)).flatMap((binding) => {
|
|
5885
|
+
const name = binding.default ? binding.localName ?? binding.exportedName : binding.exportedName;
|
|
5886
|
+
return options.types && binding.typeOnly ? [] : [{ name, node: binding.node }];
|
|
5887
|
+
});
|
|
4968
5888
|
if (scope === "functions") {
|
|
4969
|
-
const exported = new Set(names.map((item) => item.name));
|
|
5889
|
+
const exported = new Set([...names.map((item) => item.name), ...(bindings ?? []).flatMap((binding) => binding.localName ?? [])]);
|
|
4970
5890
|
names.push(...(declarations ??= declarationsNamed(program)).filter(({ name, node }) => !exported.has(name) && (node.type === "FunctionDeclaration" || node.type === "VariableDeclarator" && declarationsIsFunctionValue(node.init))));
|
|
4971
5891
|
}
|
|
4972
5892
|
if (options.services) {
|
|
@@ -5003,10 +5923,10 @@ var namingExports = defineRule41({
|
|
|
5003
5923
|
});
|
|
5004
5924
|
|
|
5005
5925
|
// src/rules/naming/naming-file.ts
|
|
5006
|
-
import { defineRule as
|
|
5926
|
+
import { defineRule as defineRule53 } from "@oxlint/plugins";
|
|
5007
5927
|
import { readdirSync as readdirSync2 } from "node:fs";
|
|
5008
5928
|
import { dirname as dirname4 } from "node:path";
|
|
5009
|
-
var namingFile =
|
|
5929
|
+
var namingFile = defineRule53({
|
|
5010
5930
|
meta: {
|
|
5011
5931
|
type: "problem",
|
|
5012
5932
|
schema: {
|
|
@@ -5076,9 +5996,9 @@ var namingFile = defineRule42({
|
|
|
5076
5996
|
});
|
|
5077
5997
|
|
|
5078
5998
|
// src/rules/naming/naming-folder.ts
|
|
5079
|
-
import { defineRule as
|
|
5999
|
+
import { defineRule as defineRule54 } from "@oxlint/plugins";
|
|
5080
6000
|
import { posix as posix2 } from "node:path";
|
|
5081
|
-
var namingFolder =
|
|
6001
|
+
var namingFolder = defineRule54({
|
|
5082
6002
|
meta: {
|
|
5083
6003
|
type: "problem",
|
|
5084
6004
|
schema: [
|
|
@@ -5152,9 +6072,9 @@ var reactDynamicClasses = astSelectorRule(':matches(JSXAttribute[name.name="clas
|
|
|
5152
6072
|
var reactEmptyEffect = astSelectorRule(':matches(CallExpression[callee.name=/^use(Layout)?Effect$/], CallExpression[callee.object.name="React"][callee.property.name=/^use(Layout)?Effect$/]) > :matches(ArrowFunctionExpression, FunctionExpression) > BlockStatement[body.length=0]', "Remove the empty effect.");
|
|
5153
6073
|
|
|
5154
6074
|
// src/rules/react/react-memo.ts
|
|
5155
|
-
import { defineRule as
|
|
6075
|
+
import { defineRule as defineRule55 } from "@oxlint/plugins";
|
|
5156
6076
|
var reactMemoNames = ["memo", "useMemo", "useCallback"];
|
|
5157
|
-
var reactMemo =
|
|
6077
|
+
var reactMemo = defineRule55({
|
|
5158
6078
|
meta: {
|
|
5159
6079
|
type: "problem",
|
|
5160
6080
|
schema: [],
|
|
@@ -5189,7 +6109,7 @@ var reactMemo = defineRule44({
|
|
|
5189
6109
|
});
|
|
5190
6110
|
|
|
5191
6111
|
// src/rules/react/react-props.ts
|
|
5192
|
-
import { defineRule as
|
|
6112
|
+
import { defineRule as defineRule56 } from "@oxlint/plugins";
|
|
5193
6113
|
function reactPropsTarget(node) {
|
|
5194
6114
|
if (node?.type === "FunctionDeclaration" || node?.type === "FunctionExpression" || node?.type === "ArrowFunctionExpression") {
|
|
5195
6115
|
return { fn: node };
|
|
@@ -5216,7 +6136,7 @@ function reactIsPassThrough(type) {
|
|
|
5216
6136
|
const argument = type.typeArguments?.params.length === 1 ? type.typeArguments.params[0] : undefined;
|
|
5217
6137
|
return name.type === "Identifier" && name.name === "ComponentProps" && (argument?.type === "TSTypeQuery" || argument?.type === "TSLiteralType" && argument.literal.type === "Literal" && typeof argument.literal.value === "string");
|
|
5218
6138
|
}
|
|
5219
|
-
var reactProps =
|
|
6139
|
+
var reactProps = defineRule56({
|
|
5220
6140
|
meta: {
|
|
5221
6141
|
type: "problem",
|
|
5222
6142
|
schema: [
|
|
@@ -5224,7 +6144,8 @@ var reactProps = defineRule45({
|
|
|
5224
6144
|
type: "object",
|
|
5225
6145
|
additionalProperties: false,
|
|
5226
6146
|
properties: {
|
|
5227
|
-
prefix: { type: "boolean" }
|
|
6147
|
+
prefix: { type: "boolean" },
|
|
6148
|
+
suffix: { type: "string", minLength: 1, default: "Props" }
|
|
5228
6149
|
}
|
|
5229
6150
|
}
|
|
5230
6151
|
],
|
|
@@ -5238,7 +6159,7 @@ var reactProps = defineRule45({
|
|
|
5238
6159
|
createOnce(context) {
|
|
5239
6160
|
return {
|
|
5240
6161
|
Program(program) {
|
|
5241
|
-
const { prefix: filePrefix = false } = optionsFirst(context, {});
|
|
6162
|
+
const { prefix: filePrefix = false, suffix = "Props" } = optionsFirst(context, {});
|
|
5242
6163
|
const stem = namingFileStem(namingBasename(context.filename).replace(/\.(tsx?|jsx?)$/, ""));
|
|
5243
6164
|
const prefix = namingNormalized(stem, "remove-separators");
|
|
5244
6165
|
const exported = new Set(exportsCollect(program).flatMap((binding) => binding.typeOnly ? [] : [binding.localName]));
|
|
@@ -5263,7 +6184,7 @@ var reactProps = defineRule45({
|
|
|
5263
6184
|
continue;
|
|
5264
6185
|
}
|
|
5265
6186
|
const type = target.typeArgument ?? reactParameterType(parameter);
|
|
5266
|
-
const expected = `${candidate.name}
|
|
6187
|
+
const expected = `${candidate.name}${suffix}`;
|
|
5267
6188
|
const data = { name: candidate.name, expected };
|
|
5268
6189
|
if (filePrefix && !exported.has(candidate.name) && !candidate.name.toLowerCase().startsWith(prefix)) {
|
|
5269
6190
|
context.report({
|
|
@@ -5314,7 +6235,7 @@ var reactProps = defineRule45({
|
|
|
5314
6235
|
});
|
|
5315
6236
|
|
|
5316
6237
|
// src/rules/schemas/schemas-inline.ts
|
|
5317
|
-
import { defineRule as
|
|
6238
|
+
import { defineRule as defineRule57 } from "@oxlint/plugins";
|
|
5318
6239
|
|
|
5319
6240
|
// src/utils/schemas.utils.ts
|
|
5320
6241
|
var schemasElementOptionsSchema = {
|
|
@@ -5350,7 +6271,7 @@ function schemasIsAllowedElement(node, allowScalars, namespaces, structuralMetho
|
|
|
5350
6271
|
}
|
|
5351
6272
|
|
|
5352
6273
|
// src/rules/schemas/schemas-inline.ts
|
|
5353
|
-
var schemasInline =
|
|
6274
|
+
var schemasInline = defineRule57({
|
|
5354
6275
|
meta: {
|
|
5355
6276
|
type: "problem",
|
|
5356
6277
|
schema: [schemasElementOptionsSchema],
|
|
@@ -5399,11 +6320,11 @@ var schemasInline = defineRule46({
|
|
|
5399
6320
|
});
|
|
5400
6321
|
|
|
5401
6322
|
// src/rules/schemas/schemas-local.ts
|
|
5402
|
-
import { defineRule as
|
|
6323
|
+
import { defineRule as defineRule58 } from "@oxlint/plugins";
|
|
5403
6324
|
var schemasLocalImportMessage = "Do not import runtime schema constructors from {{source}}; import a named schema instead.";
|
|
5404
6325
|
var schemasLocalConstructionMessage = "Do not construct schemas locally; import a named schema instead.";
|
|
5405
6326
|
var schemasLocalCalls = ["config", "prettifyError", "treeifyError", "flattenError", "formatError"];
|
|
5406
|
-
var schemasLocal =
|
|
6327
|
+
var schemasLocal = defineRule58({
|
|
5407
6328
|
meta: {
|
|
5408
6329
|
type: "problem",
|
|
5409
6330
|
schema: [
|
|
@@ -5433,8 +6354,6 @@ var schemasLocal = defineRule47({
|
|
|
5433
6354
|
let packages = new Set;
|
|
5434
6355
|
let calls = new Set;
|
|
5435
6356
|
let pending = [];
|
|
5436
|
-
let schemaUses = new Set;
|
|
5437
|
-
let callUses = new Set;
|
|
5438
6357
|
let importMessage = schemasLocalImportMessage;
|
|
5439
6358
|
let constructionMessage = schemasLocalConstructionMessage;
|
|
5440
6359
|
function isAllowedCall(node, member) {
|
|
@@ -5466,8 +6385,6 @@ var schemasLocal = defineRule47({
|
|
|
5466
6385
|
packages = new Set(options.packages);
|
|
5467
6386
|
calls = new Set(options.calls ?? schemasLocalCalls);
|
|
5468
6387
|
pending = [];
|
|
5469
|
-
schemaUses = new Set;
|
|
5470
|
-
callUses = new Set;
|
|
5471
6388
|
importMessage = options.importMessage ?? options.message ?? schemasLocalImportMessage;
|
|
5472
6389
|
constructionMessage = options.constructionMessage ?? options.message ?? schemasLocalConstructionMessage;
|
|
5473
6390
|
if ((options.allow ?? []).some((suffix) => filename.endsWith(suffix)) || (options.patterns ?? []).some((pattern) => new RegExp(pattern).test(filename))) {
|
|
@@ -5491,18 +6408,8 @@ var schemasLocal = defineRule47({
|
|
|
5491
6408
|
}
|
|
5492
6409
|
pending.push({ node, locals });
|
|
5493
6410
|
},
|
|
5494
|
-
Identifier(node) {
|
|
5495
|
-
if (!constructors.has(node.name) || node.parent.type.startsWith("Import")) {
|
|
5496
|
-
return;
|
|
5497
|
-
}
|
|
5498
|
-
if (isAllowedCall(node, node.parent)) {
|
|
5499
|
-
callUses.add(node.name);
|
|
5500
|
-
return;
|
|
5501
|
-
}
|
|
5502
|
-
schemaUses.add(node.name);
|
|
5503
|
-
},
|
|
5504
6411
|
"Program:exit"() {
|
|
5505
|
-
const runtime = pending.filter(({ locals }) =>
|
|
6412
|
+
const runtime = pending.filter(({ node, locals }) => context.sourceCode.getDeclaredVariables(node).filter((variable) => locals.includes(variable.name)).some((variable) => variable.references.length === 0 || variable.references.some((reference) => !isAllowedCall(reference.identifier, reference.identifier.parent))));
|
|
5506
6413
|
for (const { node } of runtime) {
|
|
5507
6414
|
reportRuntimeImport(node, node.source.value);
|
|
5508
6415
|
}
|
|
@@ -5528,8 +6435,8 @@ var schemasLocal = defineRule47({
|
|
|
5528
6435
|
});
|
|
5529
6436
|
|
|
5530
6437
|
// src/rules/schemas/schemas-rederive.ts
|
|
5531
|
-
import { defineRule as
|
|
5532
|
-
var schemasRederive =
|
|
6438
|
+
import { defineRule as defineRule59 } from "@oxlint/plugins";
|
|
6439
|
+
var schemasRederive = defineRule59({
|
|
5533
6440
|
meta: {
|
|
5534
6441
|
type: "problem",
|
|
5535
6442
|
schema: [
|
|
@@ -5575,7 +6482,7 @@ var schemasRederive = defineRule48({
|
|
|
5575
6482
|
});
|
|
5576
6483
|
|
|
5577
6484
|
// src/rules/schemas/schemas-single-use.ts
|
|
5578
|
-
import { defineRule as
|
|
6485
|
+
import { defineRule as defineRule60 } from "@oxlint/plugins";
|
|
5579
6486
|
function isScalarSchema(node, namespaces, structuralMethods) {
|
|
5580
6487
|
if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression") {
|
|
5581
6488
|
return false;
|
|
@@ -5589,7 +6496,7 @@ function isScalarSchema(node, namespaces, structuralMethods) {
|
|
|
5589
6496
|
}
|
|
5590
6497
|
return node.callee.object.type === "CallExpression" && isScalarSchema(node.callee.object, namespaces, structuralMethods);
|
|
5591
6498
|
}
|
|
5592
|
-
var schemasSingleUse =
|
|
6499
|
+
var schemasSingleUse = defineRule60({
|
|
5593
6500
|
meta: {
|
|
5594
6501
|
type: "suggestion",
|
|
5595
6502
|
schema: [schemasElementOptionsSchema],
|
|
@@ -5665,7 +6572,7 @@ var schemasSingleUse = defineRule49({
|
|
|
5665
6572
|
});
|
|
5666
6573
|
|
|
5667
6574
|
// src/rules/tests/tests-assertions.ts
|
|
5668
|
-
import { defineRule as
|
|
6575
|
+
import { defineRule as defineRule61 } from "@oxlint/plugins";
|
|
5669
6576
|
var testsAssertionsModifiers = new Set(["for", "each", "concurrent", "sequential", "only", "skip"]);
|
|
5670
6577
|
var testsAssertionsFunctions = [
|
|
5671
6578
|
"expect",
|
|
@@ -5855,7 +6762,7 @@ function isAssertedTest(registration, sourceCode, state) {
|
|
|
5855
6762
|
}
|
|
5856
6763
|
return rows.length > 0 && rows.every((row) => hasAssertion(callback, row.bindings, new Set, state));
|
|
5857
6764
|
}
|
|
5858
|
-
var testsAssertions =
|
|
6765
|
+
var testsAssertions = defineRule61({
|
|
5859
6766
|
meta: {
|
|
5860
6767
|
type: "problem",
|
|
5861
6768
|
schema: [
|
|
@@ -5981,17 +6888,36 @@ var testsAssertions = defineRule50({
|
|
|
5981
6888
|
});
|
|
5982
6889
|
|
|
5983
6890
|
// src/rules/tests/tests-concurrent-db.ts
|
|
5984
|
-
import { defineRule as
|
|
5985
|
-
var testsConcurrentDb =
|
|
6891
|
+
import { defineRule as defineRule62 } from "@oxlint/plugins";
|
|
6892
|
+
var testsConcurrentDb = defineRule62({
|
|
5986
6893
|
meta: {
|
|
5987
6894
|
type: "problem",
|
|
5988
|
-
schema: [
|
|
6895
|
+
schema: [
|
|
6896
|
+
{
|
|
6897
|
+
type: "object",
|
|
6898
|
+
additionalProperties: false,
|
|
6899
|
+
properties: {
|
|
6900
|
+
message: { type: "string" },
|
|
6901
|
+
fixtures: { type: "array", items: { type: "string", minLength: 1 }, default: ["db"] },
|
|
6902
|
+
callees: { type: "array", items: { type: "string", minLength: 1 }, default: ["test.concurrent"] }
|
|
6903
|
+
}
|
|
6904
|
+
}
|
|
6905
|
+
],
|
|
5989
6906
|
messages: { forbidden: "{{message}}" }
|
|
5990
6907
|
},
|
|
5991
6908
|
createOnce(context) {
|
|
6909
|
+
let message = "";
|
|
6910
|
+
let fixtures = new Set;
|
|
6911
|
+
let callees = new Set;
|
|
5992
6912
|
return {
|
|
6913
|
+
before() {
|
|
6914
|
+
const options = optionsFirst(context, {});
|
|
6915
|
+
message = options.message ?? "Obtain an isolated database fixture for concurrent tests.";
|
|
6916
|
+
fixtures = new Set(options.fixtures ?? ["db"]);
|
|
6917
|
+
callees = new Set(options.callees ?? ["test.concurrent"]);
|
|
6918
|
+
},
|
|
5993
6919
|
CallExpression(node) {
|
|
5994
|
-
const callback = astDottedName(node.callee)
|
|
6920
|
+
const callback = callees.has(astDottedName(node.callee) ?? "") ? node.arguments.findLast(declarationsIsFunctionValue) : undefined;
|
|
5995
6921
|
const parameter = callback?.params.at(0);
|
|
5996
6922
|
const patterns = parameter?.type === "ObjectPattern" ? [parameter] : [];
|
|
5997
6923
|
if (callback && parameter?.type === "Identifier") {
|
|
@@ -6004,8 +6930,8 @@ var testsConcurrentDb = defineRule51({
|
|
|
6004
6930
|
}
|
|
6005
6931
|
}
|
|
6006
6932
|
for (const property of patterns.flatMap((pattern) => pattern.properties)) {
|
|
6007
|
-
|
|
6008
|
-
|
|
6933
|
+
const key = property.type === "Property" && !property.computed ? property.key : undefined;
|
|
6934
|
+
if (fixtures.has((key?.type === "Identifier" ? key.name : key && astStaticString(key)) ?? "")) {
|
|
6009
6935
|
context.report({
|
|
6010
6936
|
node: property,
|
|
6011
6937
|
messageId: "forbidden",
|
|
@@ -6019,10 +6945,10 @@ var testsConcurrentDb = defineRule51({
|
|
|
6019
6945
|
});
|
|
6020
6946
|
|
|
6021
6947
|
// src/rules/tests/tests-modifiers.ts
|
|
6022
|
-
import { defineRule as
|
|
6948
|
+
import { defineRule as defineRule63 } from "@oxlint/plugins";
|
|
6023
6949
|
var testsModifiersNames = ["test", "it", "describe", "suite"];
|
|
6024
6950
|
var testsModifiersPackages = new Set(["vitest", "bun:test", "@playwright/test", "@jest/globals"]);
|
|
6025
|
-
var testsModifiersBanned =
|
|
6951
|
+
var testsModifiersBanned = ["only", "skip", "todo"];
|
|
6026
6952
|
var testsModifiersBuilders = new Set(["if", "runIf", "skipIf"]);
|
|
6027
6953
|
function modifierName(node) {
|
|
6028
6954
|
if (node.type === "Identifier") {
|
|
@@ -6038,14 +6964,17 @@ function modifierName(node) {
|
|
|
6038
6964
|
const property = astMemberName(node);
|
|
6039
6965
|
return object && property ? `${object}.${property}` : undefined;
|
|
6040
6966
|
}
|
|
6041
|
-
var testsModifiers =
|
|
6967
|
+
var testsModifiers = defineRule63({
|
|
6042
6968
|
meta: {
|
|
6043
6969
|
type: "problem",
|
|
6044
6970
|
schema: [
|
|
6045
6971
|
{
|
|
6046
6972
|
type: "object",
|
|
6047
6973
|
additionalProperties: false,
|
|
6048
|
-
properties: {
|
|
6974
|
+
properties: {
|
|
6975
|
+
functions: { type: "array", items: { type: "string" } },
|
|
6976
|
+
modifiers: { type: "array", items: { type: "string", minLength: 1 }, default: testsModifiersBanned }
|
|
6977
|
+
}
|
|
6049
6978
|
}
|
|
6050
6979
|
],
|
|
6051
6980
|
messages: {
|
|
@@ -6054,12 +6983,12 @@ var testsModifiers = defineRule52({
|
|
|
6054
6983
|
},
|
|
6055
6984
|
createOnce(context) {
|
|
6056
6985
|
let names = new Set;
|
|
6986
|
+
let banned = new Set;
|
|
6057
6987
|
return {
|
|
6058
6988
|
before() {
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
]);
|
|
6989
|
+
const options = optionsFirst(context, {});
|
|
6990
|
+
names = new Set([...testsModifiersNames, ...options.functions ?? []]);
|
|
6991
|
+
banned = new Set(options.modifiers ?? testsModifiersBanned);
|
|
6063
6992
|
},
|
|
6064
6993
|
ImportDeclaration(node) {
|
|
6065
6994
|
if (!testsModifiersPackages.has(node.source.value)) {
|
|
@@ -6077,7 +7006,7 @@ var testsModifiers = defineRule52({
|
|
|
6077
7006
|
},
|
|
6078
7007
|
MemberExpression(node) {
|
|
6079
7008
|
const name = modifierName(node);
|
|
6080
|
-
if (!name || !
|
|
7009
|
+
if (!name || !banned.has(name.split(".").at(-1) ?? "")) {
|
|
6081
7010
|
return;
|
|
6082
7011
|
}
|
|
6083
7012
|
if ([...names].some((test) => name.startsWith(`${test}.`))) {
|
|
@@ -6089,9 +7018,9 @@ var testsModifiers = defineRule52({
|
|
|
6089
7018
|
});
|
|
6090
7019
|
|
|
6091
7020
|
// src/rules/tests/tests-titles.ts
|
|
6092
|
-
import { defineRule as
|
|
6093
|
-
var testsTitlesModifiers =
|
|
6094
|
-
var testsTitles =
|
|
7021
|
+
import { defineRule as defineRule64 } from "@oxlint/plugins";
|
|
7022
|
+
var testsTitlesModifiers = ["only", "skip", "concurrent", "sequential", "todo", "each", "for"];
|
|
7023
|
+
var testsTitles = defineRule64({
|
|
6095
7024
|
meta: {
|
|
6096
7025
|
type: "suggestion",
|
|
6097
7026
|
schema: [
|
|
@@ -6100,6 +7029,7 @@ var testsTitles = defineRule53({
|
|
|
6100
7029
|
additionalProperties: false,
|
|
6101
7030
|
properties: {
|
|
6102
7031
|
callees: { type: "array", items: { type: "string" } },
|
|
7032
|
+
modifiers: { type: "array", items: { type: "string", minLength: 1 }, default: testsTitlesModifiers },
|
|
6103
7033
|
deny: optionsPatternSchema,
|
|
6104
7034
|
require: optionsPatternSchema,
|
|
6105
7035
|
flags: { type: "string", pattern: "^[dgimsuvy]*$" }
|
|
@@ -6119,6 +7049,7 @@ var testsTitles = defineRule53({
|
|
|
6119
7049
|
},
|
|
6120
7050
|
createOnce(context) {
|
|
6121
7051
|
let callees = [];
|
|
7052
|
+
let modifiers = new Set;
|
|
6122
7053
|
let forbid = [];
|
|
6123
7054
|
let required = [];
|
|
6124
7055
|
let requiredLabel = "";
|
|
@@ -6127,6 +7058,7 @@ var testsTitles = defineRule53({
|
|
|
6127
7058
|
const options = optionsFirst(context);
|
|
6128
7059
|
const flags = options.flags ?? "";
|
|
6129
7060
|
callees = options.callees;
|
|
7061
|
+
modifiers = new Set(options.modifiers ?? testsTitlesModifiers);
|
|
6130
7062
|
forbid = options.deny === undefined ? [] : [options.deny].flat().map((source) => ({ regex: new RegExp(source, flags), pattern: source }));
|
|
6131
7063
|
required = optionsPatternsWithEmpty(options.require, flags);
|
|
6132
7064
|
requiredLabel = optionsPatternLabel(options.require);
|
|
@@ -6137,7 +7069,7 @@ var testsTitles = defineRule53({
|
|
|
6137
7069
|
CallExpression(node) {
|
|
6138
7070
|
const title = node.arguments.at(0);
|
|
6139
7071
|
const callee = node.callee;
|
|
6140
|
-
const configured = astMatchesCallee(astDottedName(callee), callees,
|
|
7072
|
+
const configured = astMatchesCallee(astDottedName(callee), callees, modifiers) || callee.type === "CallExpression" && astMatchesCallee(astDottedName(callee.callee), callees, modifiers) || callee.type === "TaggedTemplateExpression" && astMatchesCallee(astDottedName(callee.tag), callees, modifiers);
|
|
6141
7073
|
if (!title || !configured) {
|
|
6142
7074
|
return;
|
|
6143
7075
|
}
|
|
@@ -6166,11 +7098,11 @@ var testsTitles = defineRule53({
|
|
|
6166
7098
|
});
|
|
6167
7099
|
|
|
6168
7100
|
// src/rules/types/types-declarations.ts
|
|
6169
|
-
import { defineRule as
|
|
7101
|
+
import { defineRule as defineRule65 } from "@oxlint/plugins";
|
|
6170
7102
|
function isValuePattern(source) {
|
|
6171
7103
|
return /^\^?=/.test(source);
|
|
6172
7104
|
}
|
|
6173
|
-
var typesDeclarations =
|
|
7105
|
+
var typesDeclarations = defineRule65({
|
|
6174
7106
|
meta: {
|
|
6175
7107
|
type: "problem",
|
|
6176
7108
|
schema: [
|
|
@@ -6213,7 +7145,7 @@ var typesDeclarations = defineRule54({
|
|
|
6213
7145
|
});
|
|
6214
7146
|
|
|
6215
7147
|
// src/rules/types/types-imported-alias.ts
|
|
6216
|
-
import { defineRule as
|
|
7148
|
+
import { defineRule as defineRule66 } from "@oxlint/plugins";
|
|
6217
7149
|
function importedAliasTarget(declaration, imports) {
|
|
6218
7150
|
if (declaration?.type !== "TSTypeAliasDeclaration") {
|
|
6219
7151
|
return;
|
|
@@ -6225,7 +7157,7 @@ function importedAliasTarget(declaration, imports) {
|
|
|
6225
7157
|
const { name } = type.typeName;
|
|
6226
7158
|
return imports.has(name) && !declaration.typeParameters?.params.some((parameter) => parameter.name.name === name) ? name : undefined;
|
|
6227
7159
|
}
|
|
6228
|
-
var typesImportedAlias =
|
|
7160
|
+
var typesImportedAlias = defineRule66({
|
|
6229
7161
|
meta: {
|
|
6230
7162
|
type: "problem",
|
|
6231
7163
|
schema: [],
|
|
@@ -6287,7 +7219,7 @@ var typesImportedAlias = defineRule55({
|
|
|
6287
7219
|
});
|
|
6288
7220
|
|
|
6289
7221
|
// src/rules/types/types-inline.ts
|
|
6290
|
-
import { defineRule as
|
|
7222
|
+
import { defineRule as defineRule67 } from "@oxlint/plugins";
|
|
6291
7223
|
function parameterAnnotation(parameter) {
|
|
6292
7224
|
if (parameter.type === "TSParameterProperty") {
|
|
6293
7225
|
return parameterAnnotation(parameter.parameter);
|
|
@@ -6297,7 +7229,7 @@ function parameterAnnotation(parameter) {
|
|
|
6297
7229
|
}
|
|
6298
7230
|
return parameter.typeAnnotation;
|
|
6299
7231
|
}
|
|
6300
|
-
var typesInline =
|
|
7232
|
+
var typesInline = defineRule67({
|
|
6301
7233
|
meta: {
|
|
6302
7234
|
type: "problem",
|
|
6303
7235
|
schema: [
|
|
@@ -6373,8 +7305,8 @@ var typesInline = defineRule56({
|
|
|
6373
7305
|
var typesLocalAlias = astSelectorRule("TSTypeAliasDeclaration", "Local component/hook contracts use interfaces named for their owner. Derived or reusable aliases belong in domain types; do not substitute an empty interface.");
|
|
6374
7306
|
|
|
6375
7307
|
// src/rules/types/types-namespace-import.ts
|
|
6376
|
-
import { defineRule as
|
|
6377
|
-
var typesNamespaceImport =
|
|
7308
|
+
import { defineRule as defineRule68 } from "@oxlint/plugins";
|
|
7309
|
+
var typesNamespaceImport = defineRule68({
|
|
6378
7310
|
meta: {
|
|
6379
7311
|
type: "suggestion",
|
|
6380
7312
|
schema: [
|
|
@@ -6416,26 +7348,50 @@ var typesNamespaceImport = defineRule57({
|
|
|
6416
7348
|
});
|
|
6417
7349
|
|
|
6418
7350
|
// src/rules/types/types-service.ts
|
|
6419
|
-
import { defineRule as
|
|
6420
|
-
var typesService =
|
|
7351
|
+
import { defineRule as defineRule69 } from "@oxlint/plugins";
|
|
7352
|
+
var typesService = defineRule69({
|
|
6421
7353
|
meta: {
|
|
6422
7354
|
type: "problem",
|
|
6423
|
-
schema: [
|
|
7355
|
+
schema: [
|
|
7356
|
+
{
|
|
7357
|
+
type: "object",
|
|
7358
|
+
additionalProperties: false,
|
|
7359
|
+
properties: {
|
|
7360
|
+
suffix: { type: "string", minLength: 1, default: "Service" },
|
|
7361
|
+
factory: { type: "string", minLength: 1, default: "make" }
|
|
7362
|
+
}
|
|
7363
|
+
}
|
|
7364
|
+
],
|
|
6424
7365
|
messages: {
|
|
6425
|
-
domain: "Move this type to its domain types file; only the matching factory-derived
|
|
7366
|
+
domain: "Move this type to its domain types file; only the matching factory-derived {{suffix}} alias belongs here."
|
|
6426
7367
|
}
|
|
6427
7368
|
},
|
|
6428
7369
|
createOnce(context) {
|
|
7370
|
+
let suffix = "Service";
|
|
7371
|
+
let factory = "make";
|
|
6429
7372
|
return {
|
|
7373
|
+
before() {
|
|
7374
|
+
const options = optionsFirst(context, {});
|
|
7375
|
+
suffix = options.suffix ?? "Service";
|
|
7376
|
+
factory = options.factory ?? "make";
|
|
7377
|
+
},
|
|
6430
7378
|
TSInterfaceDeclaration(node) {
|
|
6431
|
-
context.report({
|
|
7379
|
+
context.report({
|
|
7380
|
+
node,
|
|
7381
|
+
messageId: "domain",
|
|
7382
|
+
data: { suffix }
|
|
7383
|
+
});
|
|
6432
7384
|
},
|
|
6433
7385
|
TSTypeAliasDeclaration(node) {
|
|
6434
7386
|
const type = node.typeAnnotation;
|
|
6435
7387
|
const argument = type.type === "TSTypeReference" && type.typeArguments?.params.length === 1 ? type.typeArguments.params[0] : undefined;
|
|
6436
|
-
const isFactoryService = node.id.name.endsWith(
|
|
7388
|
+
const isFactoryService = node.id.name.endsWith(suffix) && type.type === "TSTypeReference" && type.typeName.type === "Identifier" && type.typeName.name === "ReturnType" && argument?.type === "TSTypeQuery" && argument.exprName.type === "Identifier" && argument.exprName.name === `${factory}${node.id.name}`;
|
|
6437
7389
|
if (!isFactoryService) {
|
|
6438
|
-
context.report({
|
|
7390
|
+
context.report({
|
|
7391
|
+
node,
|
|
7392
|
+
messageId: "domain",
|
|
7393
|
+
data: { suffix }
|
|
7394
|
+
});
|
|
6439
7395
|
}
|
|
6440
7396
|
}
|
|
6441
7397
|
};
|
|
@@ -6443,12 +7399,12 @@ var typesService = defineRule58({
|
|
|
6443
7399
|
});
|
|
6444
7400
|
|
|
6445
7401
|
// src/rules/types/types-unknown.ts
|
|
6446
|
-
import { defineRule as
|
|
7402
|
+
import { defineRule as defineRule70 } from "@oxlint/plugins";
|
|
6447
7403
|
function isCatchName(node) {
|
|
6448
7404
|
const key = "key" in node && !(("computed" in node) && node.computed) ? node.key : undefined;
|
|
6449
7405
|
return key?.type === "Identifier" && key.name === "catch";
|
|
6450
7406
|
}
|
|
6451
|
-
var typesUnknown =
|
|
7407
|
+
var typesUnknown = defineRule70({
|
|
6452
7408
|
meta: { type: "problem", schema: [], messages: { forbidden: "Use an owned domain type rather than unknown." } },
|
|
6453
7409
|
createOnce(context) {
|
|
6454
7410
|
return {
|
|
@@ -6485,15 +7441,21 @@ var src_default = eslintCompatPlugin({
|
|
|
6485
7441
|
"boundaries-rpc-database": boundariesRpcDatabase,
|
|
6486
7442
|
"boundaries-tanstack": boundariesTanstack,
|
|
6487
7443
|
"boundaries-token-owner": boundariesTokenOwner,
|
|
7444
|
+
"code-branch-once": codeBranchOnce,
|
|
6488
7445
|
"code-constructor": codeConstructor,
|
|
6489
7446
|
"code-double-negation": codeDoubleNegation,
|
|
7447
|
+
"code-function-nesting": codeFunctionNesting,
|
|
7448
|
+
"code-inline": codeInline,
|
|
7449
|
+
"code-justified-call": codeJustifiedCall,
|
|
6490
7450
|
"code-literal-in": codeLiteralIn,
|
|
6491
7451
|
"code-mutable-state": codeMutableState,
|
|
6492
7452
|
"code-object-params": codeObjectParams,
|
|
6493
7453
|
"code-paired-call": codePairedCall,
|
|
6494
7454
|
"code-promise-mutation": codePromiseMutation,
|
|
6495
7455
|
"code-switch": codeSwitch,
|
|
7456
|
+
"code-traced": codeTraced,
|
|
6496
7457
|
"code-trivial-functions": codeTrivialFunctions,
|
|
7458
|
+
"code-yield-argument": codeYieldArgument,
|
|
6497
7459
|
"comments-code": commentsCode,
|
|
6498
7460
|
"comments-directives": commentsDirectives,
|
|
6499
7461
|
"copy-dashes": copyDashes,
|
|
@@ -6508,13 +7470,17 @@ var src_default = eslintCompatPlugin({
|
|
|
6508
7470
|
"files-restricted": filesRestricted,
|
|
6509
7471
|
"files-route": filesRoute,
|
|
6510
7472
|
"files-types-only": filesTypesOnly,
|
|
7473
|
+
"layout-arrow-body": layoutArrowBody,
|
|
6511
7474
|
"layout-call-arguments": layoutCallArguments,
|
|
6512
7475
|
"layout-call-array": layoutCallArray,
|
|
7476
|
+
"layout-call-depth": layoutCallDepth,
|
|
6513
7477
|
"layout-chain": layoutChain,
|
|
7478
|
+
"layout-items": layoutItems,
|
|
6514
7479
|
"layout-jsx-attributes": layoutJsxAttributes,
|
|
6515
7480
|
"layout-key-value": layoutKeyValue,
|
|
6516
7481
|
"layout-object": layoutObject,
|
|
6517
7482
|
"layout-padding": layoutPadding,
|
|
7483
|
+
"layout-pipe": layoutPipe,
|
|
6518
7484
|
"layout-ternary": layoutTernary,
|
|
6519
7485
|
"naming-declarations": namingDeclarations,
|
|
6520
7486
|
"naming-exports": namingExports,
|