oxlint-plugin-arch 0.12.2 → 0.12.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +4 -4
- package/dist/index.js +921 -135
- 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 +36 -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/code.types.d.ts +22 -0
- package/dist/types/layout.types.d.ts +22 -0
- package/dist/types/preset.types.d.ts +10 -0
- package/dist/utils/ast.utils.d.ts +7 -1
- package/dist/utils/source.utils.d.ts +6 -2
- package/dist/vendor/dillon-anti-slop/index.js +33 -0
- package/package.json +1 -1
package/dist/preset/preset.d.ts
CHANGED
package/dist/preset/preset.js
CHANGED
|
@@ -18,6 +18,7 @@ var presetRecipes = {
|
|
|
18
18
|
react: { react: true, tailwind: true, schemas: true },
|
|
19
19
|
desktop: { console: true },
|
|
20
20
|
library: { schemas: true },
|
|
21
|
+
effect: { schemas: true, effect: true },
|
|
21
22
|
ui: presetUi,
|
|
22
23
|
kit: presetUi,
|
|
23
24
|
cli: { console: true }
|
|
@@ -399,6 +400,7 @@ var presetGroupOrder = [
|
|
|
399
400
|
"schemas",
|
|
400
401
|
"sql",
|
|
401
402
|
"orpc",
|
|
403
|
+
"effect",
|
|
402
404
|
"react",
|
|
403
405
|
"forms",
|
|
404
406
|
"tanstack",
|
|
@@ -460,6 +462,7 @@ var presetGroupOptions = {
|
|
|
460
462
|
schemas: { level: "error" },
|
|
461
463
|
forms: { level: "error", resolver: "standardSchemaResolver" },
|
|
462
464
|
sql: { level: "error", sanitizers: ["escapeLikeWildcards"] },
|
|
465
|
+
effect: { level: "error", width: 160, exports: undefined, skip: undefined },
|
|
463
466
|
console: { level: "error" },
|
|
464
467
|
deny: { level: "error", imports: {}, calls: {} },
|
|
465
468
|
externals: { level: "error", packages: presetExternals }
|
|
@@ -806,7 +809,8 @@ function presetBackendConventions(root, key, value, denied) {
|
|
|
806
809
|
]));
|
|
807
810
|
conventions[posix2.join(servicesKey, domain)] = presetDeny(Object.fromEntries(imports));
|
|
808
811
|
}
|
|
809
|
-
|
|
812
|
+
const testFolders = ["tests", "test"];
|
|
813
|
+
for (const tests of testFolders) {
|
|
810
814
|
if (exists(posix2.join(tests, "kits"))) {
|
|
811
815
|
conventions[tests] = presetDeny(Object.fromEntries(presetKitImports.map((pattern) => [pattern, "Import the kit file you need."])));
|
|
812
816
|
conventions[posix2.join(tests, "kits")] = { naming: { files: presetKitFiles } };
|
|
@@ -818,7 +822,7 @@ function presetBackendConventions(root, key, value, denied) {
|
|
|
818
822
|
for (const child of presetDirectories(root, posix2.join(key, folder)).map((path) => posix2.relative(key, path))) {
|
|
819
823
|
if (posix2.basename(child) === "migrations") {
|
|
820
824
|
conventions[child] = { layout: false, naming: { files: presetMigrationFiles, unique: presetMigrationNumber } };
|
|
821
|
-
} else if (child.split("/").length < 8) {
|
|
825
|
+
} else if (child.split("/").length < 8 && !testFolders.includes(posix2.basename(child))) {
|
|
822
826
|
queue.push(child);
|
|
823
827
|
}
|
|
824
828
|
}
|
|
@@ -1070,6 +1074,34 @@ var denyGroup = (project, settings) => {
|
|
|
1070
1074
|
return overrides;
|
|
1071
1075
|
};
|
|
1072
1076
|
|
|
1077
|
+
// src/preset/groups/effect.ts
|
|
1078
|
+
var effectGroup = (project, settings) => {
|
|
1079
|
+
const files = [`**/${presetExtensions}`];
|
|
1080
|
+
const max = settings.width;
|
|
1081
|
+
return [
|
|
1082
|
+
presetOverride(files, {
|
|
1083
|
+
"arch/code-trivial-functions": "off",
|
|
1084
|
+
"arch/code-branch-once": "error",
|
|
1085
|
+
"arch/code-function-nesting": ["error", { max: 4 }],
|
|
1086
|
+
"arch/code-inline": ["error", { max, nesting: 2, keep: ["^[A-Z]", "^layer"], allow: "^(?:decode|encode)[A-Z]", services: ["of"] }],
|
|
1087
|
+
"arch/code-yield-argument": "error",
|
|
1088
|
+
"arch/layout-arrow-body": ["error", { max }],
|
|
1089
|
+
"arch/layout-call-arguments": ["error", { split: false, hug: { max }, join: { max, nesting: 2, keep: ["Layer.mergeAll"] } }],
|
|
1090
|
+
"arch/layout-call-depth": ["error", { max: 2 }],
|
|
1091
|
+
"arch/layout-items": "error",
|
|
1092
|
+
"arch/layout-pipe": "error",
|
|
1093
|
+
"arch/layout-ternary": ["error", { fit: false, branch: { max: 3 } }]
|
|
1094
|
+
}),
|
|
1095
|
+
presetOverride(files, {
|
|
1096
|
+
"arch/code-justified-call": [
|
|
1097
|
+
"error",
|
|
1098
|
+
{ calls: ["Effect.promise"], message: "Effect.promise turns a rejection into a defect; otherwise use Effect.tryPromise with a tagged error." }
|
|
1099
|
+
],
|
|
1100
|
+
"arch/code-traced": ["error", { exports: settings.exports, skip: settings.skip, message: "An exported function or service method gets a span: use Effect.fn('<name>')." }]
|
|
1101
|
+
}, project.testFiles)
|
|
1102
|
+
];
|
|
1103
|
+
};
|
|
1104
|
+
|
|
1073
1105
|
// src/preset/groups/expo.ts
|
|
1074
1106
|
var expoGroup = (project, settings) => {
|
|
1075
1107
|
const { expoWeb, testFiles } = project;
|
|
@@ -1097,6 +1129,7 @@ var expoGroup = (project, settings) => {
|
|
|
1097
1129
|
}
|
|
1098
1130
|
]
|
|
1099
1131
|
}, testFiles),
|
|
1132
|
+
presetOverride(presetScopes(expoWeb), { "svg-jsx/camel-case-colon": "off", "svg-jsx/no-style-string": "off" }),
|
|
1100
1133
|
presetOverride(presetScopes(expoWeb, "app/**/*+api.ts"), {
|
|
1101
1134
|
"arch/files-route": ["error", { export: "GET", exports: ["HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"], hooks: [], intrinsic: false }]
|
|
1102
1135
|
})
|
|
@@ -2183,6 +2216,7 @@ var presetBuilders = {
|
|
|
2183
2216
|
schemas: schemasGroup,
|
|
2184
2217
|
sql: sqlGroup,
|
|
2185
2218
|
orpc: orpcGroup,
|
|
2219
|
+
effect: effectGroup,
|
|
2186
2220
|
react: reactGroup,
|
|
2187
2221
|
forms: formsGroup,
|
|
2188
2222
|
tanstack: tanstackGroup,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const codeBranchOnce: import("@oxlint/plugins").Rule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const codeFunctionNesting: import("@oxlint/plugins").Rule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const codeInline: import("@oxlint/plugins").Rule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const codeJustifiedCall: import("@oxlint/plugins").Rule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const codeTraced: import("@oxlint/plugins").Rule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const codeYieldArgument: import("@oxlint/plugins").Rule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const layoutArrowBody: import("@oxlint/plugins").Rule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const layoutCallDepth: import("@oxlint/plugins").Rule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const layoutItems: import("@oxlint/plugins").Rule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const layoutPipe: import("@oxlint/plugins").Rule;
|
|
@@ -57,3 +57,25 @@ export interface CodeTrivialFunctionsGuardState {
|
|
|
57
57
|
returned: boolean;
|
|
58
58
|
globalArray: (node: ESTree.Node) => boolean;
|
|
59
59
|
}
|
|
60
|
+
export interface CodeInlineOptions {
|
|
61
|
+
max?: number;
|
|
62
|
+
nesting?: number;
|
|
63
|
+
allow?: OptionsPattern;
|
|
64
|
+
keep?: OptionsPattern;
|
|
65
|
+
services?: string[];
|
|
66
|
+
}
|
|
67
|
+
export interface CodeFunctionNestingOptions {
|
|
68
|
+
max?: number;
|
|
69
|
+
}
|
|
70
|
+
export interface CodeJustifiedCallOptions {
|
|
71
|
+
calls: string[];
|
|
72
|
+
comment?: string;
|
|
73
|
+
message?: string;
|
|
74
|
+
}
|
|
75
|
+
export interface CodeTracedOptions {
|
|
76
|
+
untraced?: string[];
|
|
77
|
+
exports?: OptionsPattern;
|
|
78
|
+
services?: string[];
|
|
79
|
+
skip?: OptionsPattern;
|
|
80
|
+
message?: string;
|
|
81
|
+
}
|
|
@@ -8,6 +8,15 @@ export interface LayoutEdit {
|
|
|
8
8
|
export interface LayoutCallArgumentsOptions {
|
|
9
9
|
min?: number;
|
|
10
10
|
indent?: number;
|
|
11
|
+
split?: boolean;
|
|
12
|
+
hug?: {
|
|
13
|
+
max: number;
|
|
14
|
+
};
|
|
15
|
+
join?: {
|
|
16
|
+
max: number;
|
|
17
|
+
nesting?: number;
|
|
18
|
+
keep?: string[];
|
|
19
|
+
};
|
|
11
20
|
}
|
|
12
21
|
export interface LayoutCallArrayOptions {
|
|
13
22
|
callees?: string[];
|
|
@@ -52,4 +61,17 @@ export type LayoutPaddingKind = 'variable' | 'block' | 'control' | 'return' | 'o
|
|
|
52
61
|
export interface LayoutTernaryOptions {
|
|
53
62
|
max?: number;
|
|
54
63
|
indent?: number;
|
|
64
|
+
fit?: boolean;
|
|
65
|
+
branch?: {
|
|
66
|
+
max: number;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export interface LayoutItemsOptions {
|
|
70
|
+
indent?: SourceIndent;
|
|
71
|
+
}
|
|
72
|
+
export interface LayoutCallDepthOptions {
|
|
73
|
+
max?: number;
|
|
74
|
+
}
|
|
75
|
+
export interface LayoutArrowBodyOptions {
|
|
76
|
+
max?: number;
|
|
55
77
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { OptionsPattern } from './options.types.ts';
|
|
1
2
|
import type { OxlintConfig, OxlintOverride } from 'oxlint';
|
|
2
3
|
export type PresetLevel = 'error' | 'warn';
|
|
3
4
|
export type PresetSwitch = boolean | 'warn';
|
|
@@ -120,6 +121,11 @@ export interface PresetOrpcOptions {
|
|
|
120
121
|
export interface PresetFormsOptions {
|
|
121
122
|
resolver?: string;
|
|
122
123
|
}
|
|
124
|
+
export interface PresetEffectOptions {
|
|
125
|
+
width?: number;
|
|
126
|
+
exports?: OptionsPattern;
|
|
127
|
+
skip?: OptionsPattern;
|
|
128
|
+
}
|
|
123
129
|
export interface PresetSqlOptions {
|
|
124
130
|
sanitizers?: string[];
|
|
125
131
|
}
|
|
@@ -145,6 +151,7 @@ export interface PresetGroups {
|
|
|
145
151
|
schemas?: PresetSwitch;
|
|
146
152
|
forms?: PresetSwitch | PresetFormsOptions;
|
|
147
153
|
sql?: PresetSwitch | PresetSqlOptions;
|
|
154
|
+
effect?: PresetSwitch | PresetEffectOptions;
|
|
148
155
|
console?: boolean;
|
|
149
156
|
deny?: PresetSwitch | PresetDenyOptions;
|
|
150
157
|
externals?: PresetSwitch | PresetExternalsOptions;
|
|
@@ -190,6 +197,9 @@ export interface PresetResolvedGroups {
|
|
|
190
197
|
schemas: PresetEmptyOptions;
|
|
191
198
|
forms: Required<PresetFormsOptions>;
|
|
192
199
|
sql: Required<PresetSqlOptions>;
|
|
200
|
+
effect: Pick<PresetEffectOptions, 'exports' | 'skip'> & {
|
|
201
|
+
width: number;
|
|
202
|
+
};
|
|
193
203
|
console: PresetEmptyOptions;
|
|
194
204
|
deny: {
|
|
195
205
|
imports: Record<string, string>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AstFunction } from '../types/ast.types.ts';
|
|
1
|
+
import type { AstCall, AstFunction } from '../types/ast.types.ts';
|
|
2
2
|
import type { ESTree } from '@oxlint/plugins';
|
|
3
3
|
export declare function astVisit(node: unknown, ancestors: ESTree.Node[], callback: (node: ESTree.Node, ancestors: ESTree.Node[]) => void): void;
|
|
4
4
|
export declare function astMatchesCallee(name: string | undefined, callees: readonly string[], modifiers: ReadonlySet<string>): boolean;
|
|
@@ -17,3 +17,9 @@ export declare function astMemberName(node: ESTree.MemberExpression): string | u
|
|
|
17
17
|
export declare function astDottedName(node: ESTree.Node): string | undefined;
|
|
18
18
|
export declare function astCallName(node: ESTree.CallExpression): string | undefined;
|
|
19
19
|
export declare function astSelectorRule(selector: string, message: string): import("@oxlint/plugins").Rule;
|
|
20
|
+
export declare function astIsOneLine(node: ESTree.Node): boolean;
|
|
21
|
+
export declare function astIsInlineFunction(node: ESTree.Node): node is AstFunction;
|
|
22
|
+
export declare function astIsCall(node: ESTree.Node): node is AstCall;
|
|
23
|
+
export declare function astIsPipe(node: AstCall): boolean;
|
|
24
|
+
export declare function astSome(value: unknown, test: (node: ESTree.Node) => boolean, enter?: (node: ESTree.Node) => boolean): boolean;
|
|
25
|
+
export declare function astCallDepth(value: unknown): number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SourceIndent } from '../types/source.types.ts';
|
|
2
|
-
import type
|
|
2
|
+
import type * as PluginsTypes from '@oxlint/plugins';
|
|
3
3
|
export declare const sourceIndentSchema: {
|
|
4
4
|
anyOf: ({
|
|
5
5
|
type: string;
|
|
@@ -11,8 +11,12 @@ export declare const sourceIndentSchema: {
|
|
|
11
11
|
enum: string[];
|
|
12
12
|
})[];
|
|
13
13
|
};
|
|
14
|
-
export declare function sourceCommentText(comment: Pick<ESTree.Comment, 'type' | 'value'>): string;
|
|
14
|
+
export declare function sourceCommentText(comment: Pick<PluginsTypes.ESTree.Comment, 'type' | 'value'>): string;
|
|
15
15
|
export declare function sourceIsDirective(text: string): boolean;
|
|
16
16
|
export declare function sourceLineIndent(text: string, offset: number): string;
|
|
17
17
|
export declare function sourceIndentUnit(indent?: SourceIndent): string;
|
|
18
18
|
export declare function sourceNewline(text: string): string;
|
|
19
|
+
export declare function sourceLineStart(text: string, offset: number): number;
|
|
20
|
+
export declare function sourceLineEnd(text: string, offset: number): number;
|
|
21
|
+
export declare function sourceItemGaps(open: number, items: readonly PluginsTypes.Ranged[], close: number): PluginsTypes.Range[];
|
|
22
|
+
export declare function sourceRespace(fixer: PluginsTypes.Fixer, text: string, gaps: PluginsTypes.Range[], spacing: (index: number, last: boolean) => string): PluginsTypes.Fix[] | null;
|
|
@@ -327,6 +327,39 @@ function astSelectorRule(selector, message) {
|
|
|
327
327
|
}
|
|
328
328
|
});
|
|
329
329
|
}
|
|
330
|
+
function astIsOneLine(node) {
|
|
331
|
+
return node.loc.start.line === node.loc.end.line;
|
|
332
|
+
}
|
|
333
|
+
function astIsInlineFunction(node) {
|
|
334
|
+
return node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression";
|
|
335
|
+
}
|
|
336
|
+
function astIsCall(node) {
|
|
337
|
+
return node.type === "CallExpression" || node.type === "NewExpression";
|
|
338
|
+
}
|
|
339
|
+
function astIsPipe(node) {
|
|
340
|
+
return node.callee.type === "MemberExpression" && astMemberName(node.callee) === "pipe";
|
|
341
|
+
}
|
|
342
|
+
function astChildren(node) {
|
|
343
|
+
return Object.entries(node).filter(([key]) => !astSkippedKeys.has(key)).map(([, child]) => child);
|
|
344
|
+
}
|
|
345
|
+
function astSome(value, test, enter = () => true) {
|
|
346
|
+
if (Array.isArray(value)) {
|
|
347
|
+
return value.some((child) => astSome(child, test, enter));
|
|
348
|
+
}
|
|
349
|
+
return astIsNode(value) && (test(value) || enter(value) && astChildren(value).some((child) => astSome(child, test, enter)));
|
|
350
|
+
}
|
|
351
|
+
function astCallDepth(value) {
|
|
352
|
+
if (Array.isArray(value)) {
|
|
353
|
+
return Math.max(0, ...value.map(astCallDepth));
|
|
354
|
+
}
|
|
355
|
+
if (!astIsNode(value) || astIsInlineFunction(value)) {
|
|
356
|
+
return 0;
|
|
357
|
+
}
|
|
358
|
+
if (astIsCall(value)) {
|
|
359
|
+
return Math.max(value.arguments.length === 0 ? 0 : 1 + astCallDepth(value.arguments), astCallDepth(value.callee));
|
|
360
|
+
}
|
|
361
|
+
return astCallDepth(astChildren(value));
|
|
362
|
+
}
|
|
330
363
|
|
|
331
364
|
// src/vendor/dillon-anti-slop/shared/type-reference-name.ts
|
|
332
365
|
function typeReferenceName(type) {
|