@effect/language-service 0.80.0 → 0.81.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -55
- package/cli.js +1848 -274
- package/cli.js.map +1 -1
- package/effect-lsp-patch-utils.js +107 -3
- package/effect-lsp-patch-utils.js.map +1 -1
- package/index.js +107 -3
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/transform.js +107 -3
- package/transform.js.map +1 -1
package/cli.js
CHANGED
|
@@ -25719,7 +25719,7 @@ var runWith2 = (command, config) => {
|
|
|
25719
25719
|
// package.json
|
|
25720
25720
|
var package_default = {
|
|
25721
25721
|
name: "@effect/language-service",
|
|
25722
|
-
version: "0.
|
|
25722
|
+
version: "0.81.0",
|
|
25723
25723
|
publishConfig: {
|
|
25724
25724
|
access: "public",
|
|
25725
25725
|
directory: "dist"
|
|
@@ -31800,6 +31800,7 @@ var anyUnknownInErrorContext = createDiagnostic({
|
|
|
31800
31800
|
name: "anyUnknownInErrorContext",
|
|
31801
31801
|
code: 28,
|
|
31802
31802
|
description: "Detects 'any' or 'unknown' types in Effect error or requirements channels",
|
|
31803
|
+
group: "correctness",
|
|
31803
31804
|
severity: "off",
|
|
31804
31805
|
fixable: false,
|
|
31805
31806
|
supportedEffect: ["v3", "v4"],
|
|
@@ -31905,6 +31906,7 @@ var catchAllToMapError = createDiagnostic({
|
|
|
31905
31906
|
name: "catchAllToMapError",
|
|
31906
31907
|
code: 39,
|
|
31907
31908
|
description: "Suggests using Effect.mapError instead of Effect.catchAll when the callback only wraps the error with Effect.fail",
|
|
31909
|
+
group: "style",
|
|
31908
31910
|
severity: "suggestion",
|
|
31909
31911
|
fixable: true,
|
|
31910
31912
|
supportedEffect: ["v3", "v4"],
|
|
@@ -32004,6 +32006,7 @@ var catchUnfailableEffect = createDiagnostic({
|
|
|
32004
32006
|
name: "catchUnfailableEffect",
|
|
32005
32007
|
code: 2,
|
|
32006
32008
|
description: "Warns when using error handling on Effects that never fail (error type is 'never')",
|
|
32009
|
+
group: "antipattern",
|
|
32007
32010
|
severity: "suggestion",
|
|
32008
32011
|
fixable: false,
|
|
32009
32012
|
supportedEffect: ["v3", "v4"],
|
|
@@ -32055,6 +32058,7 @@ var classSelfMismatch = createDiagnostic({
|
|
|
32055
32058
|
name: "classSelfMismatch",
|
|
32056
32059
|
code: 20,
|
|
32057
32060
|
description: "Ensures Self type parameter matches the class name in Service/Tag/Schema classes",
|
|
32061
|
+
group: "correctness",
|
|
32058
32062
|
severity: "error",
|
|
32059
32063
|
fixable: true,
|
|
32060
32064
|
supportedEffect: ["v3", "v4"],
|
|
@@ -32195,6 +32199,7 @@ var deterministicKeys = createDiagnostic({
|
|
|
32195
32199
|
name: "deterministicKeys",
|
|
32196
32200
|
code: 25,
|
|
32197
32201
|
description: "Enforces deterministic naming for service/tag/error identifiers based on class names",
|
|
32202
|
+
group: "style",
|
|
32198
32203
|
severity: "off",
|
|
32199
32204
|
fixable: true,
|
|
32200
32205
|
supportedEffect: ["v3", "v4"],
|
|
@@ -32314,6 +32319,7 @@ var duplicatePackage = createDiagnostic({
|
|
|
32314
32319
|
name: "duplicatePackage",
|
|
32315
32320
|
code: 6,
|
|
32316
32321
|
description: "Detects when multiple versions of the same Effect package are loaded",
|
|
32322
|
+
group: "correctness",
|
|
32317
32323
|
severity: "warning",
|
|
32318
32324
|
fixable: false,
|
|
32319
32325
|
supportedEffect: ["v3", "v4"],
|
|
@@ -32345,6 +32351,7 @@ var effectFnIife = createDiagnostic({
|
|
|
32345
32351
|
name: "effectFnIife",
|
|
32346
32352
|
code: 46,
|
|
32347
32353
|
description: "Effect.fn or Effect.fnUntraced is called as an IIFE (Immediately Invoked Function Expression). Use Effect.gen instead.",
|
|
32354
|
+
group: "antipattern",
|
|
32348
32355
|
severity: "warning",
|
|
32349
32356
|
fixable: true,
|
|
32350
32357
|
supportedEffect: ["v3", "v4"],
|
|
@@ -32449,6 +32456,7 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
32449
32456
|
name: "effectFnOpportunity",
|
|
32450
32457
|
code: 41,
|
|
32451
32458
|
description: "Suggests using Effect.fn for functions that returns an Effect",
|
|
32459
|
+
group: "style",
|
|
32452
32460
|
severity: "suggestion",
|
|
32453
32461
|
fixable: true,
|
|
32454
32462
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33043,6 +33051,7 @@ var effectGenUsesAdapter = createDiagnostic({
|
|
|
33043
33051
|
name: "effectGenUsesAdapter",
|
|
33044
33052
|
code: 23,
|
|
33045
33053
|
description: "Warns when using the deprecated adapter parameter in Effect.gen",
|
|
33054
|
+
group: "antipattern",
|
|
33046
33055
|
severity: "warning",
|
|
33047
33056
|
fixable: false,
|
|
33048
33057
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33083,6 +33092,7 @@ var effectInFailure = createDiagnostic({
|
|
|
33083
33092
|
name: "effectInFailure",
|
|
33084
33093
|
code: 49,
|
|
33085
33094
|
description: "Warns when an Effect is used inside an Effect failure channel",
|
|
33095
|
+
group: "antipattern",
|
|
33086
33096
|
severity: "warning",
|
|
33087
33097
|
fixable: false,
|
|
33088
33098
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33149,6 +33159,7 @@ var effectInVoidSuccess = createDiagnostic({
|
|
|
33149
33159
|
name: "effectInVoidSuccess",
|
|
33150
33160
|
code: 14,
|
|
33151
33161
|
description: "Detects nested Effects in void success channels that may cause unexecuted effects",
|
|
33162
|
+
group: "antipattern",
|
|
33152
33163
|
severity: "warning",
|
|
33153
33164
|
fixable: false,
|
|
33154
33165
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33200,6 +33211,7 @@ var effectMapVoid = createDiagnostic({
|
|
|
33200
33211
|
name: "effectMapVoid",
|
|
33201
33212
|
code: 40,
|
|
33202
33213
|
description: "Suggests using Effect.asVoid instead of Effect.map(() => void 0), Effect.map(() => undefined), or Effect.map(() => {})",
|
|
33214
|
+
group: "style",
|
|
33203
33215
|
severity: "suggestion",
|
|
33204
33216
|
fixable: true,
|
|
33205
33217
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33266,6 +33278,7 @@ var effectSucceedWithVoid = createDiagnostic({
|
|
|
33266
33278
|
name: "effectSucceedWithVoid",
|
|
33267
33279
|
code: 47,
|
|
33268
33280
|
description: "Suggests using Effect.void instead of Effect.succeed(undefined) or Effect.succeed(void 0)",
|
|
33281
|
+
group: "style",
|
|
33269
33282
|
severity: "suggestion",
|
|
33270
33283
|
fixable: true,
|
|
33271
33284
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33319,6 +33332,7 @@ var extendsNativeError = createDiagnostic({
|
|
|
33319
33332
|
name: "extendsNativeError",
|
|
33320
33333
|
code: 50,
|
|
33321
33334
|
description: "Warns when a class directly extends the native Error class",
|
|
33335
|
+
group: "effectNative",
|
|
33322
33336
|
severity: "off",
|
|
33323
33337
|
fixable: false,
|
|
33324
33338
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33371,6 +33385,7 @@ var floatingEffect = createDiagnostic({
|
|
|
33371
33385
|
name: "floatingEffect",
|
|
33372
33386
|
code: 3,
|
|
33373
33387
|
description: "Ensures Effects are yielded or assigned to variables, not left floating",
|
|
33388
|
+
group: "correctness",
|
|
33374
33389
|
severity: "error",
|
|
33375
33390
|
fixable: false,
|
|
33376
33391
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33424,6 +33439,7 @@ var genericEffectServices = createDiagnostic({
|
|
|
33424
33439
|
name: "genericEffectServices",
|
|
33425
33440
|
code: 10,
|
|
33426
33441
|
description: "Prevents services with type parameters that cannot be discriminated at runtime",
|
|
33442
|
+
group: "correctness",
|
|
33427
33443
|
severity: "warning",
|
|
33428
33444
|
fixable: false,
|
|
33429
33445
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33473,6 +33489,7 @@ var globalErrorInEffectCatch = createDiagnostic({
|
|
|
33473
33489
|
name: "globalErrorInEffectCatch",
|
|
33474
33490
|
code: 36,
|
|
33475
33491
|
description: "Warns when catch callbacks return global Error type instead of typed errors",
|
|
33492
|
+
group: "antipattern",
|
|
33476
33493
|
severity: "warning",
|
|
33477
33494
|
fixable: false,
|
|
33478
33495
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33535,6 +33552,7 @@ var globalErrorInEffectFailure = createDiagnostic({
|
|
|
33535
33552
|
name: "globalErrorInEffectFailure",
|
|
33536
33553
|
code: 35,
|
|
33537
33554
|
description: "Warns when the global Error type is used in an Effect failure channel",
|
|
33555
|
+
group: "antipattern",
|
|
33538
33556
|
severity: "warning",
|
|
33539
33557
|
fixable: false,
|
|
33540
33558
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33585,11 +33603,54 @@ var globalErrorInEffectFailure = createDiagnostic({
|
|
|
33585
33603
|
})
|
|
33586
33604
|
});
|
|
33587
33605
|
|
|
33606
|
+
// src/diagnostics/globalFetch.ts
|
|
33607
|
+
var globalFetch = createDiagnostic({
|
|
33608
|
+
name: "globalFetch",
|
|
33609
|
+
code: 53,
|
|
33610
|
+
description: "Warns when using the global fetch function instead of the Effect HTTP client",
|
|
33611
|
+
group: "effectNative",
|
|
33612
|
+
severity: "off",
|
|
33613
|
+
fixable: false,
|
|
33614
|
+
supportedEffect: ["v3", "v4"],
|
|
33615
|
+
apply: fn3("globalFetch.apply")(function* (sourceFile, report) {
|
|
33616
|
+
const ts = yield* service2(TypeScriptApi);
|
|
33617
|
+
const typeChecker = yield* service2(TypeCheckerApi);
|
|
33618
|
+
const typeParser = yield* service2(TypeParser);
|
|
33619
|
+
const fetchSymbol = typeChecker.resolveName("fetch", void 0, ts.SymbolFlags.Value, false);
|
|
33620
|
+
if (!fetchSymbol) return;
|
|
33621
|
+
const effectVersion = typeParser.supportedEffect();
|
|
33622
|
+
const packageName = effectVersion === "v3" ? "@effect/platform" : "effect/unstable/http";
|
|
33623
|
+
const messageText = `Prefer using HttpClient from ${packageName} instead of the global 'fetch' function.`;
|
|
33624
|
+
const nodeToVisit = [];
|
|
33625
|
+
const appendNodeToVisit = (node) => {
|
|
33626
|
+
nodeToVisit.push(node);
|
|
33627
|
+
return void 0;
|
|
33628
|
+
};
|
|
33629
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
33630
|
+
while (nodeToVisit.length > 0) {
|
|
33631
|
+
const node = nodeToVisit.shift();
|
|
33632
|
+
if (ts.isCallExpression(node)) {
|
|
33633
|
+
const symbol4 = typeChecker.getSymbolAtLocation(node.expression);
|
|
33634
|
+
const resolvedSymbol = symbol4 && symbol4.flags & ts.SymbolFlags.Alias ? typeChecker.getAliasedSymbol(symbol4) : symbol4;
|
|
33635
|
+
if (resolvedSymbol === fetchSymbol) {
|
|
33636
|
+
report({
|
|
33637
|
+
location: node.expression,
|
|
33638
|
+
messageText,
|
|
33639
|
+
fixes: []
|
|
33640
|
+
});
|
|
33641
|
+
}
|
|
33642
|
+
}
|
|
33643
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
33644
|
+
}
|
|
33645
|
+
})
|
|
33646
|
+
});
|
|
33647
|
+
|
|
33588
33648
|
// src/diagnostics/importFromBarrel.ts
|
|
33589
33649
|
var importFromBarrel = createDiagnostic({
|
|
33590
33650
|
name: "importFromBarrel",
|
|
33591
33651
|
code: 12,
|
|
33592
33652
|
description: "Suggests importing from specific module paths instead of barrel exports",
|
|
33653
|
+
group: "style",
|
|
33593
33654
|
severity: "off",
|
|
33594
33655
|
fixable: true,
|
|
33595
33656
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33732,6 +33793,7 @@ var instanceOfSchema = createDiagnostic({
|
|
|
33732
33793
|
name: "instanceOfSchema",
|
|
33733
33794
|
code: 45,
|
|
33734
33795
|
description: "Suggests using Schema.is instead of instanceof for Effect Schema types",
|
|
33796
|
+
group: "effectNative",
|
|
33735
33797
|
severity: "off",
|
|
33736
33798
|
fixable: true,
|
|
33737
33799
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33797,6 +33859,7 @@ var layerMergeAllWithDependencies = createDiagnostic({
|
|
|
33797
33859
|
name: "layerMergeAllWithDependencies",
|
|
33798
33860
|
code: 37,
|
|
33799
33861
|
description: "Detects interdependencies in Layer.mergeAll calls where one layer provides a service that another layer requires",
|
|
33862
|
+
group: "antipattern",
|
|
33800
33863
|
severity: "warning",
|
|
33801
33864
|
fixable: true,
|
|
33802
33865
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33912,6 +33975,7 @@ var leakingRequirements = createDiagnostic({
|
|
|
33912
33975
|
name: "leakingRequirements",
|
|
33913
33976
|
code: 8,
|
|
33914
33977
|
description: "Detects implementation services leaked in service methods",
|
|
33978
|
+
group: "antipattern",
|
|
33915
33979
|
severity: "suggestion",
|
|
33916
33980
|
fixable: false,
|
|
33917
33981
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34068,6 +34132,7 @@ var missedPipeableOpportunity = createDiagnostic({
|
|
|
34068
34132
|
name: "missedPipeableOpportunity",
|
|
34069
34133
|
code: 26,
|
|
34070
34134
|
description: "Enforces the use of pipeable style for nested function calls",
|
|
34135
|
+
group: "style",
|
|
34071
34136
|
severity: "off",
|
|
34072
34137
|
fixable: true,
|
|
34073
34138
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34250,6 +34315,7 @@ var missingEffectContext = createDiagnostic({
|
|
|
34250
34315
|
name: "missingEffectContext",
|
|
34251
34316
|
code: 1,
|
|
34252
34317
|
description: "Reports missing service requirements in Effect context channel",
|
|
34318
|
+
group: "correctness",
|
|
34253
34319
|
severity: "error",
|
|
34254
34320
|
fixable: false,
|
|
34255
34321
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34301,6 +34367,7 @@ var missingEffectError = createDiagnostic({
|
|
|
34301
34367
|
name: "missingEffectError",
|
|
34302
34368
|
code: 1,
|
|
34303
34369
|
description: "Reports missing error types in Effect error channel",
|
|
34370
|
+
group: "correctness",
|
|
34304
34371
|
severity: "error",
|
|
34305
34372
|
fixable: true,
|
|
34306
34373
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34444,6 +34511,7 @@ var missingEffectServiceDependency = createDiagnostic({
|
|
|
34444
34511
|
name: "missingEffectServiceDependency",
|
|
34445
34512
|
code: 22,
|
|
34446
34513
|
description: "Checks that Effect.Service dependencies satisfy all required layer inputs",
|
|
34514
|
+
group: "style",
|
|
34447
34515
|
severity: "off",
|
|
34448
34516
|
fixable: false,
|
|
34449
34517
|
supportedEffect: ["v3"],
|
|
@@ -34540,6 +34608,7 @@ var missingLayerContext = createDiagnostic({
|
|
|
34540
34608
|
name: "missingLayerContext",
|
|
34541
34609
|
code: 38,
|
|
34542
34610
|
description: "Reports missing service requirements in Layer context channel",
|
|
34611
|
+
group: "correctness",
|
|
34543
34612
|
severity: "error",
|
|
34544
34613
|
fixable: false,
|
|
34545
34614
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34591,6 +34660,7 @@ var missingReturnYieldStar = createDiagnostic({
|
|
|
34591
34660
|
name: "missingReturnYieldStar",
|
|
34592
34661
|
code: 7,
|
|
34593
34662
|
description: "Suggests using 'return yield*' for Effects with never success for better type narrowing",
|
|
34663
|
+
group: "correctness",
|
|
34594
34664
|
severity: "error",
|
|
34595
34665
|
fixable: true,
|
|
34596
34666
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34643,6 +34713,7 @@ var missingStarInYieldEffectGen = createDiagnostic({
|
|
|
34643
34713
|
name: "missingStarInYieldEffectGen",
|
|
34644
34714
|
code: 4,
|
|
34645
34715
|
description: "Enforces using 'yield*' instead of 'yield' when yielding Effects in generators",
|
|
34716
|
+
group: "correctness",
|
|
34646
34717
|
severity: "error",
|
|
34647
34718
|
fixable: true,
|
|
34648
34719
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34720,6 +34791,7 @@ var multipleEffectProvide = createDiagnostic({
|
|
|
34720
34791
|
name: "multipleEffectProvide",
|
|
34721
34792
|
code: 18,
|
|
34722
34793
|
description: "Warns against chaining Effect.provide calls which can cause service lifecycle issues",
|
|
34794
|
+
group: "antipattern",
|
|
34723
34795
|
severity: "warning",
|
|
34724
34796
|
fixable: true,
|
|
34725
34797
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34822,7 +34894,11 @@ var moduleAlternativesV3 = /* @__PURE__ */ new Map([
|
|
|
34822
34894
|
["path/win32", { alternative: "Path", module: "path", package: "@effect/platform" }],
|
|
34823
34895
|
["node:path/win32", { alternative: "Path", module: "path", package: "@effect/platform" }],
|
|
34824
34896
|
["child_process", { alternative: "CommandExecutor", module: "child_process", package: "@effect/platform" }],
|
|
34825
|
-
["node:child_process", { alternative: "CommandExecutor", module: "child_process", package: "@effect/platform" }]
|
|
34897
|
+
["node:child_process", { alternative: "CommandExecutor", module: "child_process", package: "@effect/platform" }],
|
|
34898
|
+
["http", { alternative: "HttpClient", module: "http", package: "@effect/platform" }],
|
|
34899
|
+
["node:http", { alternative: "HttpClient", module: "http", package: "@effect/platform" }],
|
|
34900
|
+
["https", { alternative: "HttpClient", module: "https", package: "@effect/platform" }],
|
|
34901
|
+
["node:https", { alternative: "HttpClient", module: "https", package: "@effect/platform" }]
|
|
34826
34902
|
]);
|
|
34827
34903
|
var moduleAlternativesV4 = /* @__PURE__ */ new Map([
|
|
34828
34904
|
["fs", { alternative: "FileSystem", module: "fs", package: "effect" }],
|
|
@@ -34836,12 +34912,17 @@ var moduleAlternativesV4 = /* @__PURE__ */ new Map([
|
|
|
34836
34912
|
["path/win32", { alternative: "Path", module: "path", package: "effect" }],
|
|
34837
34913
|
["node:path/win32", { alternative: "Path", module: "path", package: "effect" }],
|
|
34838
34914
|
["child_process", { alternative: "ChildProcess", module: "child_process", package: "effect" }],
|
|
34839
|
-
["node:child_process", { alternative: "ChildProcess", module: "child_process", package: "effect" }]
|
|
34915
|
+
["node:child_process", { alternative: "ChildProcess", module: "child_process", package: "effect" }],
|
|
34916
|
+
["http", { alternative: "HttpClient", module: "http", package: "effect/unstable/http" }],
|
|
34917
|
+
["node:http", { alternative: "HttpClient", module: "http", package: "effect/unstable/http" }],
|
|
34918
|
+
["https", { alternative: "HttpClient", module: "https", package: "effect/unstable/http" }],
|
|
34919
|
+
["node:https", { alternative: "HttpClient", module: "https", package: "effect/unstable/http" }]
|
|
34840
34920
|
]);
|
|
34841
34921
|
var nodeBuiltinImport = createDiagnostic({
|
|
34842
34922
|
name: "nodeBuiltinImport",
|
|
34843
34923
|
code: 52,
|
|
34844
34924
|
description: "Warns when importing Node.js built-in modules that have Effect-native counterparts",
|
|
34925
|
+
group: "effectNative",
|
|
34845
34926
|
severity: "off",
|
|
34846
34927
|
fixable: false,
|
|
34847
34928
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34885,6 +34966,7 @@ var nonObjectEffectServiceType = createDiagnostic({
|
|
|
34885
34966
|
name: "nonObjectEffectServiceType",
|
|
34886
34967
|
code: 24,
|
|
34887
34968
|
description: "Ensures Effect.Service types are objects, not primitives",
|
|
34969
|
+
group: "correctness",
|
|
34888
34970
|
severity: "error",
|
|
34889
34971
|
fixable: false,
|
|
34890
34972
|
supportedEffect: ["v3"],
|
|
@@ -35669,6 +35751,7 @@ var outdatedApi = createDiagnostic({
|
|
|
35669
35751
|
name: "outdatedApi",
|
|
35670
35752
|
code: 48,
|
|
35671
35753
|
description: "Detects usage of APIs that have been removed or renamed in Effect v4",
|
|
35754
|
+
group: "correctness",
|
|
35672
35755
|
severity: "warning",
|
|
35673
35756
|
fixable: false,
|
|
35674
35757
|
supportedEffect: ["v4"],
|
|
@@ -35738,6 +35821,7 @@ var outdatedEffectCodegen = createDiagnostic({
|
|
|
35738
35821
|
name: "outdatedEffectCodegen",
|
|
35739
35822
|
code: 19,
|
|
35740
35823
|
description: "Detects when generated code is outdated and needs to be regenerated",
|
|
35824
|
+
group: "correctness",
|
|
35741
35825
|
severity: "warning",
|
|
35742
35826
|
fixable: true,
|
|
35743
35827
|
supportedEffect: ["v3", "v4"],
|
|
@@ -35786,6 +35870,7 @@ var overriddenSchemaConstructor = createDiagnostic({
|
|
|
35786
35870
|
name: "overriddenSchemaConstructor",
|
|
35787
35871
|
code: 30,
|
|
35788
35872
|
description: "Prevents overriding constructors in Schema classes which breaks decoding behavior",
|
|
35873
|
+
group: "correctness",
|
|
35789
35874
|
severity: "error",
|
|
35790
35875
|
fixable: true,
|
|
35791
35876
|
supportedEffect: ["v3", "v4"],
|
|
@@ -35925,6 +36010,7 @@ var preferSchemaOverJson = createDiagnostic({
|
|
|
35925
36010
|
name: "preferSchemaOverJson",
|
|
35926
36011
|
code: 44,
|
|
35927
36012
|
description: "Suggests using Effect Schema for JSON operations instead of JSON.parse/JSON.stringify which may throw",
|
|
36013
|
+
group: "effectNative",
|
|
35928
36014
|
severity: "suggestion",
|
|
35929
36015
|
fixable: false,
|
|
35930
36016
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36037,6 +36123,7 @@ var redundantSchemaTagIdentifier = createDiagnostic({
|
|
|
36037
36123
|
name: "redundantSchemaTagIdentifier",
|
|
36038
36124
|
code: 42,
|
|
36039
36125
|
description: "Suggests removing redundant identifier argument when it equals the tag value in Schema.TaggedClass/TaggedError/TaggedRequest",
|
|
36126
|
+
group: "style",
|
|
36040
36127
|
severity: "suggestion",
|
|
36041
36128
|
fixable: true,
|
|
36042
36129
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36089,6 +36176,7 @@ var returnEffectInGen = createDiagnostic({
|
|
|
36089
36176
|
name: "returnEffectInGen",
|
|
36090
36177
|
code: 11,
|
|
36091
36178
|
description: "Warns when returning an Effect in a generator causes nested Effect<Effect<...>>",
|
|
36179
|
+
group: "antipattern",
|
|
36092
36180
|
severity: "suggestion",
|
|
36093
36181
|
fixable: true,
|
|
36094
36182
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36160,6 +36248,7 @@ var runEffectInsideEffect = createDiagnostic({
|
|
|
36160
36248
|
name: "runEffectInsideEffect",
|
|
36161
36249
|
code: 32,
|
|
36162
36250
|
description: "Suggests using Runtime methods instead of Effect.run* inside Effect contexts",
|
|
36251
|
+
group: "antipattern",
|
|
36163
36252
|
severity: "suggestion",
|
|
36164
36253
|
fixable: true,
|
|
36165
36254
|
supportedEffect: ["v3"],
|
|
@@ -36286,6 +36375,7 @@ var schemaStructWithTag = createDiagnostic({
|
|
|
36286
36375
|
name: "schemaStructWithTag",
|
|
36287
36376
|
code: 34,
|
|
36288
36377
|
description: "Suggests using Schema.TaggedStruct instead of Schema.Struct with _tag field",
|
|
36378
|
+
group: "style",
|
|
36289
36379
|
severity: "suggestion",
|
|
36290
36380
|
fixable: true,
|
|
36291
36381
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36380,9 +36470,10 @@ var schemaSyncInEffect = createDiagnostic({
|
|
|
36380
36470
|
name: "schemaSyncInEffect",
|
|
36381
36471
|
code: 43,
|
|
36382
36472
|
description: "Suggests using Effect-based Schema methods instead of sync methods inside Effect generators",
|
|
36473
|
+
group: "antipattern",
|
|
36383
36474
|
severity: "suggestion",
|
|
36384
36475
|
fixable: false,
|
|
36385
|
-
supportedEffect: ["v3"
|
|
36476
|
+
supportedEffect: ["v3"],
|
|
36386
36477
|
apply: fn3("schemaSyncInEffect.apply")(function* (sourceFile, report) {
|
|
36387
36478
|
const ts = yield* service2(TypeScriptApi);
|
|
36388
36479
|
const typeParser = yield* service2(TypeParser);
|
|
@@ -36431,6 +36522,7 @@ var schemaUnionOfLiterals = createDiagnostic({
|
|
|
36431
36522
|
name: "schemaUnionOfLiterals",
|
|
36432
36523
|
code: 33,
|
|
36433
36524
|
description: "Simplifies Schema.Union of multiple Schema.Literal calls into single Schema.Literal",
|
|
36525
|
+
group: "style",
|
|
36434
36526
|
severity: "off",
|
|
36435
36527
|
fixable: true,
|
|
36436
36528
|
supportedEffect: ["v3"],
|
|
@@ -36508,6 +36600,7 @@ var scopeInLayerEffect = createDiagnostic({
|
|
|
36508
36600
|
name: "scopeInLayerEffect",
|
|
36509
36601
|
code: 13,
|
|
36510
36602
|
description: "Suggests using Layer.scoped instead of Layer.effect when Scope is in requirements",
|
|
36603
|
+
group: "antipattern",
|
|
36511
36604
|
severity: "warning",
|
|
36512
36605
|
fixable: true,
|
|
36513
36606
|
supportedEffect: ["v3"],
|
|
@@ -36605,6 +36698,7 @@ var serviceNotAsClass = createDiagnostic({
|
|
|
36605
36698
|
name: "serviceNotAsClass",
|
|
36606
36699
|
code: 51,
|
|
36607
36700
|
description: "Warns when ServiceMap.Service is used as a variable instead of a class declaration",
|
|
36701
|
+
group: "style",
|
|
36608
36702
|
severity: "off",
|
|
36609
36703
|
fixable: true,
|
|
36610
36704
|
supportedEffect: ["v4"],
|
|
@@ -36682,6 +36776,7 @@ var strictBooleanExpressions = createDiagnostic({
|
|
|
36682
36776
|
name: "strictBooleanExpressions",
|
|
36683
36777
|
code: 17,
|
|
36684
36778
|
description: "Enforces boolean types in conditional expressions for type safety",
|
|
36779
|
+
group: "style",
|
|
36685
36780
|
severity: "off",
|
|
36686
36781
|
fixable: false,
|
|
36687
36782
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36755,6 +36850,7 @@ var strictEffectProvide = createDiagnostic({
|
|
|
36755
36850
|
name: "strictEffectProvide",
|
|
36756
36851
|
code: 27,
|
|
36757
36852
|
description: "Warns when using Effect.provide with layers outside of application entry points",
|
|
36853
|
+
group: "antipattern",
|
|
36758
36854
|
severity: "off",
|
|
36759
36855
|
fixable: false,
|
|
36760
36856
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36808,6 +36904,7 @@ var tryCatchInEffectGen = createDiagnostic({
|
|
|
36808
36904
|
name: "tryCatchInEffectGen",
|
|
36809
36905
|
code: 15,
|
|
36810
36906
|
description: "Discourages try/catch in Effect generators in favor of Effect error handling",
|
|
36907
|
+
group: "antipattern",
|
|
36811
36908
|
severity: "suggestion",
|
|
36812
36909
|
fixable: false,
|
|
36813
36910
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36867,6 +36964,7 @@ var unknownInEffectCatch = createDiagnostic({
|
|
|
36867
36964
|
name: "unknownInEffectCatch",
|
|
36868
36965
|
code: 31,
|
|
36869
36966
|
description: "Warns when catch callbacks return unknown instead of typed errors",
|
|
36967
|
+
group: "antipattern",
|
|
36870
36968
|
severity: "warning",
|
|
36871
36969
|
fixable: false,
|
|
36872
36970
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36930,6 +37028,7 @@ var unnecessaryEffectGen = createDiagnostic({
|
|
|
36930
37028
|
name: "unnecessaryEffectGen",
|
|
36931
37029
|
code: 5,
|
|
36932
37030
|
description: "Suggests removing Effect.gen when it contains only a single return statement",
|
|
37031
|
+
group: "style",
|
|
36933
37032
|
severity: "suggestion",
|
|
36934
37033
|
fixable: true,
|
|
36935
37034
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36976,6 +37075,7 @@ var unnecessaryFailYieldableError = createDiagnostic({
|
|
|
36976
37075
|
name: "unnecessaryFailYieldableError",
|
|
36977
37076
|
code: 29,
|
|
36978
37077
|
description: "Suggests yielding yieldable errors directly instead of wrapping with Effect.fail",
|
|
37078
|
+
group: "style",
|
|
36979
37079
|
severity: "suggestion",
|
|
36980
37080
|
fixable: true,
|
|
36981
37081
|
supportedEffect: ["v3", "v4"],
|
|
@@ -37037,6 +37137,7 @@ var unnecessaryPipe = createDiagnostic({
|
|
|
37037
37137
|
name: "unnecessaryPipe",
|
|
37038
37138
|
code: 9,
|
|
37039
37139
|
description: "Removes pipe calls with no arguments",
|
|
37140
|
+
group: "style",
|
|
37040
37141
|
severity: "suggestion",
|
|
37041
37142
|
fixable: true,
|
|
37042
37143
|
supportedEffect: ["v3", "v4"],
|
|
@@ -37085,6 +37186,7 @@ var unnecessaryPipeChain = createDiagnostic({
|
|
|
37085
37186
|
name: "unnecessaryPipeChain",
|
|
37086
37187
|
code: 16,
|
|
37087
37188
|
description: "Simplifies chained pipe calls into a single pipe call",
|
|
37189
|
+
group: "style",
|
|
37088
37190
|
severity: "suggestion",
|
|
37089
37191
|
fixable: true,
|
|
37090
37192
|
supportedEffect: ["v3", "v4"],
|
|
@@ -37162,6 +37264,7 @@ var unsupportedServiceAccessors = createDiagnostic({
|
|
|
37162
37264
|
name: "unsupportedServiceAccessors",
|
|
37163
37265
|
code: 21,
|
|
37164
37266
|
description: "Warns about service accessors that need codegen due to generic/complex signatures",
|
|
37267
|
+
group: "correctness",
|
|
37165
37268
|
severity: "warning",
|
|
37166
37269
|
fixable: true,
|
|
37167
37270
|
supportedEffect: ["v3", "v4"],
|
|
@@ -37239,6 +37342,7 @@ var diagnostics = [
|
|
|
37239
37342
|
leakingRequirements,
|
|
37240
37343
|
unnecessaryPipe,
|
|
37241
37344
|
genericEffectServices,
|
|
37345
|
+
globalFetch,
|
|
37242
37346
|
returnEffectInGen,
|
|
37243
37347
|
tryCatchInEffectGen,
|
|
37244
37348
|
importFromBarrel,
|
|
@@ -38201,13 +38305,6 @@ var GREEN = "\x1B[0;32m";
|
|
|
38201
38305
|
var YELLOW = "\x1B[0;33m";
|
|
38202
38306
|
var BLUE = "\x1B[0;34m";
|
|
38203
38307
|
var CYAN = "\x1B[0;36m";
|
|
38204
|
-
var WHITE = "\x1B[0;37m";
|
|
38205
|
-
var CYAN_BRIGHT = "\x1B[0;96m";
|
|
38206
|
-
var BG_BLACK_BRIGHT = "\x1B[0;100m";
|
|
38207
|
-
var BG_RED = "\x1B[41m";
|
|
38208
|
-
var BG_YELLOW = "\x1B[43m";
|
|
38209
|
-
var BG_BLUE = "\x1B[0;44m";
|
|
38210
|
-
var BG_CYAN = "\x1B[0;46m";
|
|
38211
38308
|
var ansi = (text2, code) => `${code}${text2}${RESET}`;
|
|
38212
38309
|
var ERASE_LINE = "\x1B[2K";
|
|
38213
38310
|
var CURSOR_LEFT = "\r";
|
|
@@ -38215,6 +38312,11 @@ var CURSOR_HIDE = "\x1B[?25l";
|
|
|
38215
38312
|
var CURSOR_SHOW = "\x1B[?25h";
|
|
38216
38313
|
var CURSOR_TO_0 = "\x1B[G";
|
|
38217
38314
|
var BEEP = "\x07";
|
|
38315
|
+
var ITALIC = "\x1B[0;3m";
|
|
38316
|
+
var UNDERLINE = "\x1B[0;4m";
|
|
38317
|
+
var ANSI_ESCAPE_REGEX = new RegExp(String.raw`\u001b\[[0-?]*[ -/]*[@-~]`, "g");
|
|
38318
|
+
var stripAnsi2 = (text2) => text2.replace(ANSI_ESCAPE_REGEX, "");
|
|
38319
|
+
var visibleLength = (text2) => stripAnsi2(text2).length;
|
|
38218
38320
|
|
|
38219
38321
|
// src/cli/overview.ts
|
|
38220
38322
|
var import_project_service3 = __toESM(require_dist3());
|
|
@@ -40178,9 +40280,100 @@ var computeTsConfigChanges = (current, target, lspVersion) => {
|
|
|
40178
40280
|
if (!rootObj) {
|
|
40179
40281
|
return emptyFileChangesResult();
|
|
40180
40282
|
}
|
|
40283
|
+
const buildPluginObject = (severities) => {
|
|
40284
|
+
const nameProperty = ts.factory.createPropertyAssignment(
|
|
40285
|
+
ts.factory.createStringLiteral("name"),
|
|
40286
|
+
ts.factory.createStringLiteral("@effect/language-service")
|
|
40287
|
+
);
|
|
40288
|
+
return match(severities, {
|
|
40289
|
+
onNone: () => {
|
|
40290
|
+
return ts.factory.createObjectLiteralExpression([nameProperty], false);
|
|
40291
|
+
},
|
|
40292
|
+
onSome: (sevs) => {
|
|
40293
|
+
const severityProperties = Object.entries(sevs).map(
|
|
40294
|
+
([name, severity]) => ts.factory.createPropertyAssignment(
|
|
40295
|
+
ts.factory.createStringLiteral(name),
|
|
40296
|
+
ts.factory.createStringLiteral(severity)
|
|
40297
|
+
)
|
|
40298
|
+
);
|
|
40299
|
+
const diagnosticSeverityProperty = ts.factory.createPropertyAssignment(
|
|
40300
|
+
ts.factory.createStringLiteral("diagnosticSeverity"),
|
|
40301
|
+
ts.factory.createObjectLiteralExpression(severityProperties, true)
|
|
40302
|
+
);
|
|
40303
|
+
return ts.factory.createObjectLiteralExpression(
|
|
40304
|
+
[nameProperty, diagnosticSeverityProperty],
|
|
40305
|
+
true
|
|
40306
|
+
);
|
|
40307
|
+
}
|
|
40308
|
+
});
|
|
40309
|
+
};
|
|
40310
|
+
const schemaPropertyAssignment = ts.factory.createPropertyAssignment(
|
|
40311
|
+
ts.factory.createStringLiteral("$schema"),
|
|
40312
|
+
ts.factory.createStringLiteral(TSCONFIG_SCHEMA_URL)
|
|
40313
|
+
);
|
|
40181
40314
|
const compilerOptionsProperty = findPropertyInObject(ts, rootObj, "compilerOptions");
|
|
40182
40315
|
if (!compilerOptionsProperty) {
|
|
40183
|
-
|
|
40316
|
+
if (isNone2(lspVersion)) {
|
|
40317
|
+
return emptyFileChangesResult();
|
|
40318
|
+
}
|
|
40319
|
+
const textChanges2 = ts.textChanges;
|
|
40320
|
+
const host2 = createMinimalHost(ts);
|
|
40321
|
+
const formatOptions2 = { indentSize: 2, tabSize: 2 };
|
|
40322
|
+
const formatContext2 = ts.formatting.getFormatContext(formatOptions2, host2);
|
|
40323
|
+
const preferences2 = {};
|
|
40324
|
+
const fileChanges2 = textChanges2.ChangeTracker.with(
|
|
40325
|
+
{ host: host2, formatContext: formatContext2, preferences: preferences2 },
|
|
40326
|
+
(tracker) => {
|
|
40327
|
+
const schemaProperty = findPropertyInObject(ts, rootObj, "$schema");
|
|
40328
|
+
const shouldAddSchema = !schemaProperty;
|
|
40329
|
+
const shouldUpdateSchema = !!schemaProperty && (!ts.isStringLiteral(schemaProperty.initializer) || schemaProperty.initializer.text !== TSCONFIG_SCHEMA_URL);
|
|
40330
|
+
if (shouldAddSchema) {
|
|
40331
|
+
descriptions.push("Add $schema to tsconfig");
|
|
40332
|
+
} else if (shouldUpdateSchema) {
|
|
40333
|
+
descriptions.push("Update $schema in tsconfig");
|
|
40334
|
+
}
|
|
40335
|
+
descriptions.push("Add compilerOptions with @effect/language-service plugin");
|
|
40336
|
+
const compilerOptionsAssignment = ts.factory.createPropertyAssignment(
|
|
40337
|
+
ts.factory.createStringLiteral("compilerOptions"),
|
|
40338
|
+
ts.factory.createObjectLiteralExpression([
|
|
40339
|
+
ts.factory.createPropertyAssignment(
|
|
40340
|
+
ts.factory.createStringLiteral("plugins"),
|
|
40341
|
+
ts.factory.createArrayLiteralExpression([buildPluginObject(target.diagnosticSeverities)], true)
|
|
40342
|
+
)
|
|
40343
|
+
], true)
|
|
40344
|
+
);
|
|
40345
|
+
const nextProperties = rootObj.properties.flatMap((property) => {
|
|
40346
|
+
if (schemaProperty && property === schemaProperty) {
|
|
40347
|
+
return [schemaPropertyAssignment];
|
|
40348
|
+
}
|
|
40349
|
+
return [property];
|
|
40350
|
+
});
|
|
40351
|
+
if (shouldAddSchema) {
|
|
40352
|
+
nextProperties.push(schemaPropertyAssignment);
|
|
40353
|
+
}
|
|
40354
|
+
nextProperties.push(compilerOptionsAssignment);
|
|
40355
|
+
tracker.replaceNode(
|
|
40356
|
+
current.sourceFile,
|
|
40357
|
+
rootObj,
|
|
40358
|
+
ts.factory.createObjectLiteralExpression(nextProperties, true)
|
|
40359
|
+
);
|
|
40360
|
+
}
|
|
40361
|
+
);
|
|
40362
|
+
const fileChange2 = fileChanges2.find((fc) => fc.fileName === current.path);
|
|
40363
|
+
const changes2 = fileChange2 ? fileChange2.textChanges : [];
|
|
40364
|
+
if (changes2.length === 0) {
|
|
40365
|
+
return { codeActions: [], messages };
|
|
40366
|
+
}
|
|
40367
|
+
return {
|
|
40368
|
+
codeActions: [{
|
|
40369
|
+
description: descriptions.join("; "),
|
|
40370
|
+
changes: [{
|
|
40371
|
+
fileName: current.path,
|
|
40372
|
+
textChanges: changes2
|
|
40373
|
+
}]
|
|
40374
|
+
}],
|
|
40375
|
+
messages
|
|
40376
|
+
};
|
|
40184
40377
|
}
|
|
40185
40378
|
if (!ts.isObjectLiteralExpression(compilerOptionsProperty.initializer)) {
|
|
40186
40379
|
return emptyFileChangesResult();
|
|
@@ -40196,10 +40389,6 @@ var computeTsConfigChanges = (current, target, lspVersion) => {
|
|
|
40196
40389
|
(tracker) => {
|
|
40197
40390
|
const schemaProperty = findPropertyInObject(ts, rootObj, "$schema");
|
|
40198
40391
|
const pluginsProperty = findPropertyInObject(ts, compilerOptions, "plugins");
|
|
40199
|
-
const schemaPropertyAssignment = ts.factory.createPropertyAssignment(
|
|
40200
|
-
ts.factory.createStringLiteral("$schema"),
|
|
40201
|
-
ts.factory.createStringLiteral(TSCONFIG_SCHEMA_URL)
|
|
40202
|
-
);
|
|
40203
40392
|
if (isNone2(lspVersion)) {
|
|
40204
40393
|
if (schemaProperty) {
|
|
40205
40394
|
descriptions.push("Remove $schema from tsconfig");
|
|
@@ -40229,33 +40418,6 @@ var computeTsConfigChanges = (current, target, lspVersion) => {
|
|
|
40229
40418
|
descriptions.push("Update $schema in tsconfig");
|
|
40230
40419
|
tracker.replaceNode(current.sourceFile, schemaProperty.initializer, schemaPropertyAssignment.initializer);
|
|
40231
40420
|
}
|
|
40232
|
-
const buildPluginObject = (severities) => {
|
|
40233
|
-
const nameProperty = ts.factory.createPropertyAssignment(
|
|
40234
|
-
ts.factory.createStringLiteral("name"),
|
|
40235
|
-
ts.factory.createStringLiteral("@effect/language-service")
|
|
40236
|
-
);
|
|
40237
|
-
return match(severities, {
|
|
40238
|
-
onNone: () => {
|
|
40239
|
-
return ts.factory.createObjectLiteralExpression([nameProperty], false);
|
|
40240
|
-
},
|
|
40241
|
-
onSome: (sevs) => {
|
|
40242
|
-
const severityProperties = Object.entries(sevs).map(
|
|
40243
|
-
([name, severity]) => ts.factory.createPropertyAssignment(
|
|
40244
|
-
ts.factory.createStringLiteral(name),
|
|
40245
|
-
ts.factory.createStringLiteral(severity)
|
|
40246
|
-
)
|
|
40247
|
-
);
|
|
40248
|
-
const diagnosticSeverityProperty = ts.factory.createPropertyAssignment(
|
|
40249
|
-
ts.factory.createStringLiteral("diagnosticSeverity"),
|
|
40250
|
-
ts.factory.createObjectLiteralExpression(severityProperties, true)
|
|
40251
|
-
);
|
|
40252
|
-
return ts.factory.createObjectLiteralExpression(
|
|
40253
|
-
[nameProperty, diagnosticSeverityProperty],
|
|
40254
|
-
true
|
|
40255
|
-
);
|
|
40256
|
-
}
|
|
40257
|
-
});
|
|
40258
|
-
};
|
|
40259
40421
|
const pluginObject = buildPluginObject(target.diagnosticSeverities);
|
|
40260
40422
|
if (!pluginsProperty) {
|
|
40261
40423
|
descriptions.push("Add plugins array with @effect/language-service plugin");
|
|
@@ -40468,244 +40630,1656 @@ var FileReadError = class extends TaggedError2("FileReadError") {
|
|
|
40468
40630
|
}
|
|
40469
40631
|
};
|
|
40470
40632
|
|
|
40471
|
-
// src/
|
|
40472
|
-
|
|
40473
|
-
|
|
40474
|
-
|
|
40475
|
-
|
|
40476
|
-
|
|
40477
|
-
|
|
40478
|
-
|
|
40479
|
-
|
|
40480
|
-
|
|
40481
|
-
|
|
40482
|
-
|
|
40483
|
-
|
|
40484
|
-
|
|
40485
|
-
|
|
40486
|
-
|
|
40487
|
-
|
|
40488
|
-
}
|
|
40489
|
-
|
|
40490
|
-
|
|
40491
|
-
|
|
40492
|
-
|
|
40493
|
-
warning: "warn",
|
|
40494
|
-
error: "err"
|
|
40495
|
-
};
|
|
40496
|
-
var MAX_SEVERITY_LENGTH = Object.values(shortNames).reduce((max2, name) => Math.max(max2, name.length), 0);
|
|
40497
|
-
function getSeverityShortName(severity) {
|
|
40498
|
-
return shortNames[severity] ?? "???";
|
|
40499
|
-
}
|
|
40500
|
-
|
|
40501
|
-
// src/cli/setup/diagnostic-prompt.ts
|
|
40502
|
-
function eraseLines2(count) {
|
|
40503
|
-
let result3 = "";
|
|
40504
|
-
for (let i = 0; i < count; i++) {
|
|
40505
|
-
if (i > 0) result3 += "\x1B[1A";
|
|
40506
|
-
result3 += ERASE_LINE;
|
|
40507
|
-
}
|
|
40508
|
-
if (count > 0) result3 += CURSOR_LEFT;
|
|
40509
|
-
return result3;
|
|
40510
|
-
}
|
|
40511
|
-
var Action2 = taggedEnum();
|
|
40512
|
-
var NEWLINE_REGEX = /\r?\n/;
|
|
40513
|
-
function eraseText2(text2, columns) {
|
|
40514
|
-
if (columns === 0) {
|
|
40515
|
-
return ERASE_LINE + CURSOR_TO_0;
|
|
40516
|
-
}
|
|
40517
|
-
let rows = 0;
|
|
40518
|
-
const lines2 = text2.split(/\r?\n/);
|
|
40519
|
-
for (const line of lines2) {
|
|
40520
|
-
rows += 1 + Math.floor(Math.max(line.length - 1, 0) / columns);
|
|
40521
|
-
}
|
|
40522
|
-
return eraseLines2(rows);
|
|
40523
|
-
}
|
|
40524
|
-
function entriesToDisplay2(cursor, total, maxVisible) {
|
|
40525
|
-
const max2 = maxVisible === void 0 ? total : maxVisible;
|
|
40526
|
-
let startIndex = Math.min(total - max2, cursor - Math.floor(max2 / 2));
|
|
40527
|
-
if (startIndex < 0) {
|
|
40528
|
-
startIndex = 0;
|
|
40529
|
-
}
|
|
40530
|
-
const endIndex = Math.min(startIndex + max2, total);
|
|
40531
|
-
return { startIndex, endIndex };
|
|
40532
|
-
}
|
|
40533
|
-
var defaultFigures2 = {
|
|
40534
|
-
arrowUp: "\u2191",
|
|
40535
|
-
arrowDown: "\u2193",
|
|
40536
|
-
tick: "\u2714",
|
|
40537
|
-
pointerSmall: "\u203A"
|
|
40538
|
-
};
|
|
40539
|
-
var figuresValue = defaultFigures2;
|
|
40540
|
-
function getSeverityStyle(severity) {
|
|
40541
|
-
const styles = {
|
|
40542
|
-
off: WHITE + BG_BLACK_BRIGHT,
|
|
40543
|
-
suggestion: WHITE + BG_CYAN,
|
|
40544
|
-
message: WHITE + BG_BLUE,
|
|
40545
|
-
warning: WHITE + BG_YELLOW,
|
|
40546
|
-
error: WHITE + BG_RED
|
|
40547
|
-
};
|
|
40548
|
-
return styles[severity];
|
|
40549
|
-
}
|
|
40550
|
-
function renderOutput(leadingSymbol, trailingSymbol, options) {
|
|
40551
|
-
const annotateLine2 = (line) => ansi(line, BOLD);
|
|
40552
|
-
const prefix = leadingSymbol + " ";
|
|
40553
|
-
return match3(options.message.split(NEWLINE_REGEX), {
|
|
40554
|
-
onEmpty: () => `${prefix}${trailingSymbol}`,
|
|
40555
|
-
onNonEmpty: (promptLines) => {
|
|
40556
|
-
const lines2 = map4(promptLines, (line) => annotateLine2(line));
|
|
40557
|
-
return `${prefix}${lines2.join("\n ")} ${trailingSymbol} `;
|
|
40558
|
-
}
|
|
40559
|
-
});
|
|
40560
|
-
}
|
|
40561
|
-
function renderDiagnostics(state, options, figs, columns) {
|
|
40562
|
-
const diagnostics3 = options.diagnostics;
|
|
40563
|
-
const toDisplay = entriesToDisplay2(state.index, diagnostics3.length, options.maxPerPage);
|
|
40564
|
-
const documents = [];
|
|
40565
|
-
for (let index = toDisplay.startIndex; index < toDisplay.endIndex; index++) {
|
|
40566
|
-
const diagnostic = diagnostics3[index];
|
|
40567
|
-
const isHighlighted = state.index === index;
|
|
40568
|
-
const currentSeverity = state.severities[diagnostic.name] ?? diagnostic.defaultSeverity;
|
|
40569
|
-
const hasChanged = currentSeverity !== diagnostic.defaultSeverity;
|
|
40570
|
-
let prefix = " ";
|
|
40571
|
-
if (index === toDisplay.startIndex && toDisplay.startIndex > 0) {
|
|
40572
|
-
prefix = figs.arrowUp;
|
|
40573
|
-
} else if (index === toDisplay.endIndex - 1 && toDisplay.endIndex < diagnostics3.length) {
|
|
40574
|
-
prefix = figs.arrowDown;
|
|
40575
|
-
}
|
|
40576
|
-
const shortName = getSeverityShortName(currentSeverity);
|
|
40577
|
-
const paddedSeverity = shortName.padEnd(MAX_SEVERITY_LENGTH, " ");
|
|
40578
|
-
const severityStr = ansi(` ${paddedSeverity} `, getSeverityStyle(currentSeverity));
|
|
40579
|
-
const nameText = hasChanged ? `${diagnostic.name}*` : diagnostic.name;
|
|
40580
|
-
const nameStr = isHighlighted ? ansi(nameText, CYAN_BRIGHT) : nameText;
|
|
40581
|
-
const mainLine = `${prefix} ${severityStr} ${nameStr}`;
|
|
40582
|
-
if (isHighlighted && diagnostic.description) {
|
|
40583
|
-
const indentWidth = 1 + 1 + (MAX_SEVERITY_LENGTH + 2) + 1;
|
|
40584
|
-
const indent = " ".repeat(indentWidth);
|
|
40585
|
-
const availableWidth = columns - indentWidth;
|
|
40586
|
-
const truncatedDescription = availableWidth > 0 && diagnostic.description.length > availableWidth ? diagnostic.description.substring(0, availableWidth - 1) + "\u2026" : diagnostic.description;
|
|
40587
|
-
const descriptionStr = ansi(indent + truncatedDescription, DIM);
|
|
40588
|
-
documents.push(mainLine + "\n" + descriptionStr);
|
|
40589
|
-
} else {
|
|
40590
|
-
documents.push(mainLine);
|
|
40591
|
-
}
|
|
40592
|
-
}
|
|
40593
|
-
return documents.join("\n");
|
|
40594
|
-
}
|
|
40595
|
-
function renderNextFrame(state, options) {
|
|
40596
|
-
return gen2(function* () {
|
|
40597
|
-
const terminal = yield* Terminal;
|
|
40598
|
-
const columns = yield* terminal.columns;
|
|
40599
|
-
const figs = figuresValue;
|
|
40600
|
-
const diagnosticsStr = renderDiagnostics(state, options, figs, columns);
|
|
40601
|
-
const leadingSymbol = ansi("?", CYAN_BRIGHT);
|
|
40602
|
-
const trailingSymbol = figs.pointerSmall;
|
|
40603
|
-
const promptMsg = renderOutput(leadingSymbol, trailingSymbol, options);
|
|
40604
|
-
const helpText = ansi(
|
|
40605
|
-
"Use \u2191/\u2193 to navigate, \u2190/\u2192 to change severity, Enter to finish",
|
|
40606
|
-
DIM
|
|
40607
|
-
);
|
|
40608
|
-
return CURSOR_HIDE + promptMsg + "\n" + helpText + "\n" + diagnosticsStr;
|
|
40609
|
-
});
|
|
40610
|
-
}
|
|
40611
|
-
function renderSubmission(state, options) {
|
|
40612
|
-
return gen2(function* () {
|
|
40613
|
-
const figs = figuresValue;
|
|
40614
|
-
const changedCount = Object.entries(state.severities).filter(([name, severity]) => {
|
|
40615
|
-
const diagnostic = options.diagnostics.find((d) => d.name === name);
|
|
40616
|
-
return diagnostic && severity !== diagnostic.defaultSeverity;
|
|
40617
|
-
}).length;
|
|
40618
|
-
const result3 = ansi(
|
|
40619
|
-
`${changedCount} diagnostic${changedCount === 1 ? "" : "s"} configured`,
|
|
40620
|
-
WHITE
|
|
40621
|
-
);
|
|
40622
|
-
const leadingSymbol = ansi(figs.tick, GREEN);
|
|
40623
|
-
const trailingSymbol = "";
|
|
40624
|
-
const promptMsg = renderOutput(leadingSymbol, trailingSymbol, options);
|
|
40625
|
-
return promptMsg + " " + result3 + "\n";
|
|
40626
|
-
});
|
|
40627
|
-
}
|
|
40628
|
-
function processCursorUp(state, totalCount) {
|
|
40629
|
-
const newIndex = state.index === 0 ? totalCount - 1 : state.index - 1;
|
|
40630
|
-
return succeed6(Action2.NextFrame({ state: { ...state, index: newIndex } }));
|
|
40631
|
-
}
|
|
40632
|
-
function processCursorDown(state, totalCount) {
|
|
40633
|
-
const newIndex = (state.index + 1) % totalCount;
|
|
40634
|
-
return succeed6(Action2.NextFrame({ state: { ...state, index: newIndex } }));
|
|
40635
|
-
}
|
|
40636
|
-
function processSeverityChange(state, options, direction) {
|
|
40637
|
-
const diagnostic = options.diagnostics[state.index];
|
|
40638
|
-
const currentSeverity = state.severities[diagnostic.name] ?? diagnostic.defaultSeverity;
|
|
40639
|
-
const newSeverity = cycleSeverity(currentSeverity, direction);
|
|
40640
|
-
return succeed6(Action2.NextFrame({
|
|
40641
|
-
state: {
|
|
40642
|
-
...state,
|
|
40643
|
-
severities: { ...state.severities, [diagnostic.name]: newSeverity }
|
|
40633
|
+
// src/metadata.json
|
|
40634
|
+
var metadata_default = {
|
|
40635
|
+
groups: [
|
|
40636
|
+
{
|
|
40637
|
+
id: "correctness",
|
|
40638
|
+
name: "Correctness",
|
|
40639
|
+
description: "Wrong, unsafe, or structurally invalid code patterns."
|
|
40640
|
+
},
|
|
40641
|
+
{
|
|
40642
|
+
id: "antipattern",
|
|
40643
|
+
name: "Anti-pattern",
|
|
40644
|
+
description: "Discouraged patterns that often lead to bugs or confusing behavior."
|
|
40645
|
+
},
|
|
40646
|
+
{
|
|
40647
|
+
id: "effectNative",
|
|
40648
|
+
name: "Effect-native",
|
|
40649
|
+
description: "Prefer Effect-native APIs and abstractions when available."
|
|
40650
|
+
},
|
|
40651
|
+
{
|
|
40652
|
+
id: "style",
|
|
40653
|
+
name: "Style",
|
|
40654
|
+
description: "Cleanup, consistency, and idiomatic Effect code."
|
|
40644
40655
|
}
|
|
40645
|
-
|
|
40646
|
-
|
|
40647
|
-
|
|
40648
|
-
|
|
40649
|
-
|
|
40650
|
-
|
|
40651
|
-
|
|
40652
|
-
|
|
40653
|
-
|
|
40656
|
+
],
|
|
40657
|
+
rules: [
|
|
40658
|
+
{
|
|
40659
|
+
name: "anyUnknownInErrorContext",
|
|
40660
|
+
group: "correctness",
|
|
40661
|
+
description: "Detects 'any' or 'unknown' types in Effect error or requirements channels",
|
|
40662
|
+
defaultSeverity: "off",
|
|
40663
|
+
fixable: false,
|
|
40664
|
+
supportedEffect: [
|
|
40665
|
+
"v3",
|
|
40666
|
+
"v4"
|
|
40667
|
+
],
|
|
40668
|
+
preview: {
|
|
40669
|
+
sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.gen(function*() {\n yield* Effect.services<unknown>()\n return yield* Effect.fail<any>("boom")\n})\n',
|
|
40670
|
+
diagnostics: [
|
|
40671
|
+
{
|
|
40672
|
+
start: 46,
|
|
40673
|
+
end: 53,
|
|
40674
|
+
text: "This has unknown in the requirements channel and any in the error channel which is not recommended.\nOnly service identifiers should appear in the requirements channel.\nHaving an unknown or any error type is not useful. Consider instead using specific error types baked by Data.TaggedError for example. effect(anyUnknownInErrorContext)"
|
|
40675
|
+
},
|
|
40676
|
+
{
|
|
40677
|
+
start: 90,
|
|
40678
|
+
end: 116,
|
|
40679
|
+
text: "This has unknown in the requirements channel which is not recommended.\nOnly service identifiers should appear in the requirements channel. effect(anyUnknownInErrorContext)"
|
|
40680
|
+
},
|
|
40681
|
+
{
|
|
40682
|
+
start: 133,
|
|
40683
|
+
end: 157,
|
|
40684
|
+
text: "This has any in the error channel which is not recommended.\nHaving an unknown or any error type is not useful. Consider instead using specific error types baked by Data.TaggedError for example. effect(anyUnknownInErrorContext)"
|
|
40685
|
+
}
|
|
40686
|
+
]
|
|
40654
40687
|
}
|
|
40655
|
-
|
|
40656
|
-
|
|
40657
|
-
|
|
40688
|
+
},
|
|
40689
|
+
{
|
|
40690
|
+
name: "classSelfMismatch",
|
|
40691
|
+
group: "correctness",
|
|
40692
|
+
description: "Ensures Self type parameter matches the class name in Service/Tag/Schema classes",
|
|
40693
|
+
defaultSeverity: "error",
|
|
40694
|
+
fixable: true,
|
|
40695
|
+
supportedEffect: [
|
|
40696
|
+
"v3",
|
|
40697
|
+
"v4"
|
|
40698
|
+
],
|
|
40699
|
+
preview: {
|
|
40700
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\ninterface ServiceShape { value: number }\n\nexport class InvalidContextTag\n extends Effect.Tag("ValidContextTag")<ValidContextTag, ServiceShape>() {}\n\ndeclare class ValidContextTag {}\n',
|
|
40701
|
+
diagnostics: [
|
|
40702
|
+
{
|
|
40703
|
+
start: 154,
|
|
40704
|
+
end: 169,
|
|
40705
|
+
text: "Self type parameter should be 'InvalidContextTag' effect(classSelfMismatch)"
|
|
40706
|
+
}
|
|
40707
|
+
]
|
|
40658
40708
|
}
|
|
40659
|
-
|
|
40660
|
-
|
|
40709
|
+
},
|
|
40710
|
+
{
|
|
40711
|
+
name: "duplicatePackage",
|
|
40712
|
+
group: "correctness",
|
|
40713
|
+
description: "Detects when multiple versions of the same Effect package are loaded",
|
|
40714
|
+
defaultSeverity: "warning",
|
|
40715
|
+
fixable: false,
|
|
40716
|
+
supportedEffect: [
|
|
40717
|
+
"v3",
|
|
40718
|
+
"v4"
|
|
40719
|
+
],
|
|
40720
|
+
preview: {
|
|
40721
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\n// This preview only reports when the workspace resolves duplicated\n// Effect package versions.\nexport const preview = Effect.succeed(true)\n',
|
|
40722
|
+
diagnostics: []
|
|
40661
40723
|
}
|
|
40662
|
-
|
|
40663
|
-
|
|
40724
|
+
},
|
|
40725
|
+
{
|
|
40726
|
+
name: "floatingEffect",
|
|
40727
|
+
group: "correctness",
|
|
40728
|
+
description: "Ensures Effects are yielded or assigned to variables, not left floating",
|
|
40729
|
+
defaultSeverity: "error",
|
|
40730
|
+
fixable: false,
|
|
40731
|
+
supportedEffect: [
|
|
40732
|
+
"v3",
|
|
40733
|
+
"v4"
|
|
40734
|
+
],
|
|
40735
|
+
preview: {
|
|
40736
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\nEffect.log("forgotten")\n',
|
|
40737
|
+
diagnostics: [
|
|
40738
|
+
{
|
|
40739
|
+
start: 41,
|
|
40740
|
+
end: 64,
|
|
40741
|
+
text: "Effect must be yielded or assigned to a variable. effect(floatingEffect)"
|
|
40742
|
+
}
|
|
40743
|
+
]
|
|
40664
40744
|
}
|
|
40665
|
-
|
|
40666
|
-
|
|
40667
|
-
|
|
40745
|
+
},
|
|
40746
|
+
{
|
|
40747
|
+
name: "genericEffectServices",
|
|
40748
|
+
group: "correctness",
|
|
40749
|
+
description: "Prevents services with type parameters that cannot be discriminated at runtime",
|
|
40750
|
+
defaultSeverity: "warning",
|
|
40751
|
+
fixable: false,
|
|
40752
|
+
supportedEffect: [
|
|
40753
|
+
"v3",
|
|
40754
|
+
"v4"
|
|
40755
|
+
],
|
|
40756
|
+
preview: {
|
|
40757
|
+
sourceText: 'import { Effect } from "effect"\n\nexport class Preview<_A> extends Effect.Service<Preview<any>>()("Preview", {\n succeed: {}\n}) {}\n',
|
|
40758
|
+
diagnostics: [
|
|
40759
|
+
{
|
|
40760
|
+
start: 46,
|
|
40761
|
+
end: 53,
|
|
40762
|
+
text: "Effect Services with type parameters are not supported because they cannot be properly discriminated at runtime, which may cause unexpected behavior. effect(genericEffectServices)"
|
|
40763
|
+
}
|
|
40764
|
+
]
|
|
40668
40765
|
}
|
|
40669
|
-
|
|
40670
|
-
|
|
40766
|
+
},
|
|
40767
|
+
{
|
|
40768
|
+
name: "missingEffectContext",
|
|
40769
|
+
group: "correctness",
|
|
40770
|
+
description: "Reports missing service requirements in Effect context channel",
|
|
40771
|
+
defaultSeverity: "error",
|
|
40772
|
+
fixable: false,
|
|
40773
|
+
supportedEffect: [
|
|
40774
|
+
"v3",
|
|
40775
|
+
"v4"
|
|
40776
|
+
],
|
|
40777
|
+
preview: {
|
|
40778
|
+
sourceText: 'import { Effect, ServiceMap } from "effect"\n\nclass Db extends ServiceMap.Service<Db>()("Db", { make: Effect.succeed({}) }) {}\n\n// @ts-expect-error\nexport const preview: Effect.Effect<void> = Db.asEffect().pipe(Effect.asVoid)\n',
|
|
40779
|
+
diagnostics: [
|
|
40780
|
+
{
|
|
40781
|
+
start: 160,
|
|
40782
|
+
end: 167,
|
|
40783
|
+
text: "Missing 'Db' in the expected Effect context. effect(missingEffectContext)"
|
|
40784
|
+
}
|
|
40785
|
+
]
|
|
40671
40786
|
}
|
|
40672
|
-
}
|
|
40673
|
-
|
|
40674
|
-
|
|
40675
|
-
|
|
40676
|
-
|
|
40677
|
-
|
|
40678
|
-
|
|
40679
|
-
|
|
40680
|
-
|
|
40681
|
-
|
|
40682
|
-
|
|
40683
|
-
|
|
40684
|
-
|
|
40685
|
-
|
|
40686
|
-
|
|
40687
|
-
|
|
40688
|
-
|
|
40689
|
-
|
|
40690
|
-
|
|
40691
|
-
|
|
40692
|
-
|
|
40693
|
-
|
|
40694
|
-
|
|
40695
|
-
|
|
40696
|
-
|
|
40697
|
-
|
|
40698
|
-
|
|
40699
|
-
|
|
40700
|
-
|
|
40701
|
-
|
|
40702
|
-
|
|
40703
|
-
|
|
40704
|
-
|
|
40705
|
-
|
|
40706
|
-
|
|
40707
|
-
|
|
40708
|
-
|
|
40787
|
+
},
|
|
40788
|
+
{
|
|
40789
|
+
name: "missingEffectError",
|
|
40790
|
+
group: "correctness",
|
|
40791
|
+
description: "Reports missing error types in Effect error channel",
|
|
40792
|
+
defaultSeverity: "error",
|
|
40793
|
+
fixable: true,
|
|
40794
|
+
supportedEffect: [
|
|
40795
|
+
"v3",
|
|
40796
|
+
"v4"
|
|
40797
|
+
],
|
|
40798
|
+
preview: {
|
|
40799
|
+
sourceText: 'import type * as Effect from "effect/Effect"\nimport { Data } from "effect"\n\nclass Boom extends Data.TaggedError("Boom")<{}> {}\ndeclare const effect: Effect.Effect<number, Boom>\n\n// @ts-expect-error\nexport const preview: () => Effect.Effect<number> = () => effect\n',
|
|
40800
|
+
diagnostics: [
|
|
40801
|
+
{
|
|
40802
|
+
start: 256,
|
|
40803
|
+
end: 262,
|
|
40804
|
+
text: "Missing 'Boom' in the expected Effect errors. effect(missingEffectError)"
|
|
40805
|
+
}
|
|
40806
|
+
]
|
|
40807
|
+
}
|
|
40808
|
+
},
|
|
40809
|
+
{
|
|
40810
|
+
name: "missingLayerContext",
|
|
40811
|
+
group: "correctness",
|
|
40812
|
+
description: "Reports missing service requirements in Layer context channel",
|
|
40813
|
+
defaultSeverity: "error",
|
|
40814
|
+
fixable: false,
|
|
40815
|
+
supportedEffect: [
|
|
40816
|
+
"v3",
|
|
40817
|
+
"v4"
|
|
40818
|
+
],
|
|
40819
|
+
preview: {
|
|
40820
|
+
sourceText: 'import { Effect, Layer, ServiceMap } from "effect"\n\nclass A extends ServiceMap.Service<A>()("A", { make: Effect.succeed({}) }) {\n static Default = Layer.effect(this, this.make)\n}\ndeclare const layer: Layer.Layer<A, never, A>\n// @ts-expect-error\nexport const preview: Layer.Layer<A> = layer\n',
|
|
40821
|
+
diagnostics: [
|
|
40822
|
+
{
|
|
40823
|
+
start: 259,
|
|
40824
|
+
end: 266,
|
|
40825
|
+
text: "Missing 'A' in the expected Layer context. effect(missingLayerContext)"
|
|
40826
|
+
}
|
|
40827
|
+
]
|
|
40828
|
+
}
|
|
40829
|
+
},
|
|
40830
|
+
{
|
|
40831
|
+
name: "missingReturnYieldStar",
|
|
40832
|
+
group: "correctness",
|
|
40833
|
+
description: "Suggests using 'return yield*' for Effects with never success for better type narrowing",
|
|
40834
|
+
defaultSeverity: "error",
|
|
40835
|
+
fixable: true,
|
|
40836
|
+
supportedEffect: [
|
|
40837
|
+
"v3",
|
|
40838
|
+
"v4"
|
|
40839
|
+
],
|
|
40840
|
+
preview: {
|
|
40841
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\nexport const preview = Effect.gen(function*() {\n yield* Effect.log("before")\n yield* Effect.fail("boom")\n})\n',
|
|
40842
|
+
diagnostics: [
|
|
40843
|
+
{
|
|
40844
|
+
start: 121,
|
|
40845
|
+
end: 147,
|
|
40846
|
+
text: "It is recommended to use return yield* for Effects that never succeed to signal a definitive exit point for type narrowing and tooling support. effect(missingReturnYieldStar)"
|
|
40847
|
+
}
|
|
40848
|
+
]
|
|
40849
|
+
}
|
|
40850
|
+
},
|
|
40851
|
+
{
|
|
40852
|
+
name: "missingStarInYieldEffectGen",
|
|
40853
|
+
group: "correctness",
|
|
40854
|
+
description: "Enforces using 'yield*' instead of 'yield' when yielding Effects in generators",
|
|
40855
|
+
defaultSeverity: "error",
|
|
40856
|
+
fixable: true,
|
|
40857
|
+
supportedEffect: [
|
|
40858
|
+
"v3",
|
|
40859
|
+
"v4"
|
|
40860
|
+
],
|
|
40861
|
+
preview: {
|
|
40862
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\nexport const preview = Effect.gen(function*() {\n const value = yield Effect.succeed(1)\n return value\n})\n',
|
|
40863
|
+
diagnostics: [
|
|
40864
|
+
{
|
|
40865
|
+
start: 75,
|
|
40866
|
+
end: 83,
|
|
40867
|
+
text: "Seems like you used yield instead of yield* inside this Effect.gen. effect(missingStarInYieldEffectGen)"
|
|
40868
|
+
},
|
|
40869
|
+
{
|
|
40870
|
+
start: 105,
|
|
40871
|
+
end: 128,
|
|
40872
|
+
text: "When yielding Effects inside Effect.gen, you should use yield* instead of yield. effect(missingStarInYieldEffectGen)"
|
|
40873
|
+
}
|
|
40874
|
+
]
|
|
40875
|
+
}
|
|
40876
|
+
},
|
|
40877
|
+
{
|
|
40878
|
+
name: "nonObjectEffectServiceType",
|
|
40879
|
+
group: "correctness",
|
|
40880
|
+
description: "Ensures Effect.Service types are objects, not primitives",
|
|
40881
|
+
defaultSeverity: "error",
|
|
40882
|
+
fixable: false,
|
|
40883
|
+
supportedEffect: [
|
|
40884
|
+
"v3"
|
|
40885
|
+
],
|
|
40886
|
+
preview: {
|
|
40887
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\nexport class BadService extends Effect.Service<BadService>()("BadService", {\n // @ts-expect-error\n succeed: "hello" as const\n}) {}\n',
|
|
40888
|
+
diagnostics: [
|
|
40889
|
+
{
|
|
40890
|
+
start: 142,
|
|
40891
|
+
end: 149,
|
|
40892
|
+
text: "Effect.Service requires the service type to be an object {} and not a primitive type. \nConsider wrapping the value in an object, or manually using Context.Tag or Effect.Tag if you want to use a primitive instead. effect(nonObjectEffectServiceType)"
|
|
40893
|
+
}
|
|
40894
|
+
]
|
|
40895
|
+
}
|
|
40896
|
+
},
|
|
40897
|
+
{
|
|
40898
|
+
name: "outdatedApi",
|
|
40899
|
+
group: "correctness",
|
|
40900
|
+
description: "Detects usage of APIs that have been removed or renamed in Effect v4",
|
|
40901
|
+
defaultSeverity: "warning",
|
|
40902
|
+
fixable: false,
|
|
40903
|
+
supportedEffect: [
|
|
40904
|
+
"v4"
|
|
40905
|
+
],
|
|
40906
|
+
preview: {
|
|
40907
|
+
sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.gen(function*() {\n // @ts-expect-error\n return yield* Effect.runtime()\n})\n',
|
|
40908
|
+
diagnostics: [
|
|
40909
|
+
{
|
|
40910
|
+
start: 0,
|
|
40911
|
+
end: 0,
|
|
40912
|
+
text: "This project targets Effect v4, but is using Effect v3 APIs. To find the correct API to use, clone and consult the github.com/effect-ts/effect-smol repository for the corresponding v4 replacement. effect(outdatedApi)"
|
|
40913
|
+
},
|
|
40914
|
+
{
|
|
40915
|
+
start: 126,
|
|
40916
|
+
end: 133,
|
|
40917
|
+
text: "runtime is an Effect v3 API, but the project is using Effect v4. effect(outdatedApi)"
|
|
40918
|
+
}
|
|
40919
|
+
]
|
|
40920
|
+
}
|
|
40921
|
+
},
|
|
40922
|
+
{
|
|
40923
|
+
name: "outdatedEffectCodegen",
|
|
40924
|
+
group: "correctness",
|
|
40925
|
+
description: "Detects when generated code is outdated and needs to be regenerated",
|
|
40926
|
+
defaultSeverity: "warning",
|
|
40927
|
+
fixable: true,
|
|
40928
|
+
supportedEffect: [
|
|
40929
|
+
"v3",
|
|
40930
|
+
"v4"
|
|
40931
|
+
],
|
|
40932
|
+
preview: {
|
|
40933
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\n// @effect-codegens accessors:stale-preview\nexport class Preview extends Effect.Service<Preview>()("Preview", {\n accessors: true,\n effect: Effect.succeed({ value: Effect.succeed(1) })\n}) {}\n',
|
|
40934
|
+
diagnostics: [
|
|
40935
|
+
{
|
|
40936
|
+
start: 61,
|
|
40937
|
+
end: 76,
|
|
40938
|
+
text: "Codegen accessors result is outdated effect(outdatedEffectCodegen)"
|
|
40939
|
+
}
|
|
40940
|
+
]
|
|
40941
|
+
}
|
|
40942
|
+
},
|
|
40943
|
+
{
|
|
40944
|
+
name: "overriddenSchemaConstructor",
|
|
40945
|
+
group: "correctness",
|
|
40946
|
+
description: "Prevents overriding constructors in Schema classes which breaks decoding behavior",
|
|
40947
|
+
defaultSeverity: "error",
|
|
40948
|
+
fixable: true,
|
|
40949
|
+
supportedEffect: [
|
|
40950
|
+
"v3",
|
|
40951
|
+
"v4"
|
|
40952
|
+
],
|
|
40953
|
+
preview: {
|
|
40954
|
+
sourceText: 'import * as Schema from "effect/Schema"\n\nexport class User extends Schema.Class<User>("User")({ name: Schema.String }) {\n constructor(readonly input: { name: string }) { super(input) }\n}\n',
|
|
40955
|
+
diagnostics: [
|
|
40956
|
+
{
|
|
40957
|
+
start: 123,
|
|
40958
|
+
end: 185,
|
|
40959
|
+
text: "Classes extending Schema must not override the constructor; this is because it silently breaks the schema decoding behaviour. If that's needed, we recommend instead to use a static 'new' method that constructs the instance. effect(overriddenSchemaConstructor)"
|
|
40960
|
+
}
|
|
40961
|
+
]
|
|
40962
|
+
}
|
|
40963
|
+
},
|
|
40964
|
+
{
|
|
40965
|
+
name: "unsupportedServiceAccessors",
|
|
40966
|
+
group: "correctness",
|
|
40967
|
+
description: "Warns about service accessors that need codegen due to generic/complex signatures",
|
|
40968
|
+
defaultSeverity: "warning",
|
|
40969
|
+
fixable: true,
|
|
40970
|
+
supportedEffect: [
|
|
40971
|
+
"v3",
|
|
40972
|
+
"v4"
|
|
40973
|
+
],
|
|
40974
|
+
preview: {
|
|
40975
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\nexport class Preview extends Effect.Service<Preview>()("Preview", {\n accessors: true,\n effect: Effect.succeed({ get: <A>(value: A) => Effect.succeed(value) })\n}) {}\n',
|
|
40976
|
+
diagnostics: [
|
|
40977
|
+
{
|
|
40978
|
+
start: 54,
|
|
40979
|
+
end: 61,
|
|
40980
|
+
text: "Even if accessors are enabled, accessors for 'get' won't be available because the signature have generic type parameters or multiple call signatures. effect(unsupportedServiceAccessors)"
|
|
40981
|
+
}
|
|
40982
|
+
]
|
|
40983
|
+
}
|
|
40984
|
+
},
|
|
40985
|
+
{
|
|
40986
|
+
name: "catchUnfailableEffect",
|
|
40987
|
+
group: "antipattern",
|
|
40988
|
+
description: "Warns when using error handling on Effects that never fail (error type is 'never')",
|
|
40989
|
+
defaultSeverity: "suggestion",
|
|
40990
|
+
fixable: false,
|
|
40991
|
+
supportedEffect: [
|
|
40992
|
+
"v3",
|
|
40993
|
+
"v4"
|
|
40994
|
+
],
|
|
40995
|
+
preview: {
|
|
40996
|
+
sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.succeed(1).pipe(\n Effect.catch(() => Effect.succeed(0))\n)\n',
|
|
40997
|
+
diagnostics: [
|
|
40998
|
+
{
|
|
40999
|
+
start: 82,
|
|
41000
|
+
end: 94,
|
|
41001
|
+
text: "Looks like the previous effect never fails, so probably this error handling will never be triggered. effect(catchUnfailableEffect)"
|
|
41002
|
+
}
|
|
41003
|
+
]
|
|
41004
|
+
}
|
|
41005
|
+
},
|
|
41006
|
+
{
|
|
41007
|
+
name: "effectFnIife",
|
|
41008
|
+
group: "antipattern",
|
|
41009
|
+
description: "Effect.fn or Effect.fnUntraced is called as an IIFE (Immediately Invoked Function Expression). Use Effect.gen instead.",
|
|
41010
|
+
defaultSeverity: "warning",
|
|
41011
|
+
fixable: true,
|
|
41012
|
+
supportedEffect: [
|
|
41013
|
+
"v3",
|
|
41014
|
+
"v4"
|
|
41015
|
+
],
|
|
41016
|
+
preview: {
|
|
41017
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\nexport const preview = Effect.fn("preview")(function*() {\n return yield* Effect.succeed(1)\n})()\n',
|
|
41018
|
+
diagnostics: [
|
|
41019
|
+
{
|
|
41020
|
+
start: 64,
|
|
41021
|
+
end: 137,
|
|
41022
|
+
text: `Effect.fn returns a reusable function that can take arguments, but here it's called immediately. Use Effect.gen instead with Effect.withSpan("preview") piped in the end to mantain tracing spans. effect(effectFnIife)`
|
|
41023
|
+
}
|
|
41024
|
+
]
|
|
41025
|
+
}
|
|
41026
|
+
},
|
|
41027
|
+
{
|
|
41028
|
+
name: "effectGenUsesAdapter",
|
|
41029
|
+
group: "antipattern",
|
|
41030
|
+
description: "Warns when using the deprecated adapter parameter in Effect.gen",
|
|
41031
|
+
defaultSeverity: "warning",
|
|
41032
|
+
fixable: false,
|
|
41033
|
+
supportedEffect: [
|
|
41034
|
+
"v3",
|
|
41035
|
+
"v4"
|
|
41036
|
+
],
|
|
41037
|
+
preview: {
|
|
41038
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\nexport const preview = Effect.gen(function*(_) {\n return yield* Effect.succeed(1)\n})\n',
|
|
41039
|
+
diagnostics: [
|
|
41040
|
+
{
|
|
41041
|
+
start: 85,
|
|
41042
|
+
end: 86,
|
|
41043
|
+
text: "The adapter of Effect.gen is not required anymore, it is now just an alias of pipe. effect(effectGenUsesAdapter)"
|
|
41044
|
+
}
|
|
41045
|
+
]
|
|
41046
|
+
}
|
|
41047
|
+
},
|
|
41048
|
+
{
|
|
41049
|
+
name: "effectInFailure",
|
|
41050
|
+
group: "antipattern",
|
|
41051
|
+
description: "Warns when an Effect is used inside an Effect failure channel",
|
|
41052
|
+
defaultSeverity: "warning",
|
|
41053
|
+
fixable: false,
|
|
41054
|
+
supportedEffect: [
|
|
41055
|
+
"v3",
|
|
41056
|
+
"v4"
|
|
41057
|
+
],
|
|
41058
|
+
preview: {
|
|
41059
|
+
sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.try({\n try: () => JSON.parse("{"),\n catch: (error) => Effect.logError(error)\n})\n',
|
|
41060
|
+
diagnostics: [
|
|
41061
|
+
{
|
|
41062
|
+
start: 46,
|
|
41063
|
+
end: 53,
|
|
41064
|
+
text: "The error channel contains an Effect (Effect<void, never, never>). Putting Effect computations in the failure channel is not intended; keep only failure types there. effect(effectInFailure)"
|
|
41065
|
+
},
|
|
41066
|
+
{
|
|
41067
|
+
start: 56,
|
|
41068
|
+
end: 144,
|
|
41069
|
+
text: "The error channel contains an Effect (Effect<void, never, never>). Putting Effect computations in the failure channel is not intended; keep only failure types there. effect(effectInFailure)"
|
|
41070
|
+
}
|
|
41071
|
+
]
|
|
41072
|
+
}
|
|
41073
|
+
},
|
|
41074
|
+
{
|
|
41075
|
+
name: "effectInVoidSuccess",
|
|
41076
|
+
group: "antipattern",
|
|
41077
|
+
description: "Detects nested Effects in void success channels that may cause unexecuted effects",
|
|
41078
|
+
defaultSeverity: "warning",
|
|
41079
|
+
fixable: false,
|
|
41080
|
+
supportedEffect: [
|
|
41081
|
+
"v3",
|
|
41082
|
+
"v4"
|
|
41083
|
+
],
|
|
41084
|
+
preview: {
|
|
41085
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\nexport const preview: Effect.Effect<void> = Effect.succeed(\n Effect.log("nested")\n)\n',
|
|
41086
|
+
diagnostics: [
|
|
41087
|
+
{
|
|
41088
|
+
start: 54,
|
|
41089
|
+
end: 61,
|
|
41090
|
+
text: "There is a nested 'Effect<void, never, never>' in the 'void' success channel, beware that this could lead to nested Effect<Effect<...>> that won't be executed. effect(effectInVoidSuccess)"
|
|
41091
|
+
}
|
|
41092
|
+
]
|
|
41093
|
+
}
|
|
41094
|
+
},
|
|
41095
|
+
{
|
|
41096
|
+
name: "globalErrorInEffectCatch",
|
|
41097
|
+
group: "antipattern",
|
|
41098
|
+
description: "Warns when catch callbacks return global Error type instead of typed errors",
|
|
41099
|
+
defaultSeverity: "warning",
|
|
41100
|
+
fixable: false,
|
|
41101
|
+
supportedEffect: [
|
|
41102
|
+
"v3",
|
|
41103
|
+
"v4"
|
|
41104
|
+
],
|
|
41105
|
+
preview: {
|
|
41106
|
+
sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.tryPromise({\n try: async () => 1,\n catch: (error) => new Error(String(error))\n})\n',
|
|
41107
|
+
diagnostics: [
|
|
41108
|
+
{
|
|
41109
|
+
start: 56,
|
|
41110
|
+
end: 73,
|
|
41111
|
+
text: "The 'catch' callback in Effect.tryPromise returns global 'Error', which loses type safety as untagged errors merge together. Consider using a tagged error and optionally wrapping the original in a 'cause' property. effect(globalErrorInEffectCatch)"
|
|
41112
|
+
}
|
|
41113
|
+
]
|
|
41114
|
+
}
|
|
41115
|
+
},
|
|
41116
|
+
{
|
|
41117
|
+
name: "globalErrorInEffectFailure",
|
|
41118
|
+
group: "antipattern",
|
|
41119
|
+
description: "Warns when the global Error type is used in an Effect failure channel",
|
|
41120
|
+
defaultSeverity: "warning",
|
|
41121
|
+
fixable: false,
|
|
41122
|
+
supportedEffect: [
|
|
41123
|
+
"v3",
|
|
41124
|
+
"v4"
|
|
41125
|
+
],
|
|
41126
|
+
preview: {
|
|
41127
|
+
sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.fail(new Error("boom"))\n',
|
|
41128
|
+
diagnostics: [
|
|
41129
|
+
{
|
|
41130
|
+
start: 68,
|
|
41131
|
+
end: 85,
|
|
41132
|
+
text: "Global 'Error' loses type safety as untagged errors merge together in the Effect failure channel. Consider using a tagged error and optionally wrapping the original in a 'cause' property. effect(globalErrorInEffectFailure)"
|
|
41133
|
+
}
|
|
41134
|
+
]
|
|
41135
|
+
}
|
|
41136
|
+
},
|
|
41137
|
+
{
|
|
41138
|
+
name: "layerMergeAllWithDependencies",
|
|
41139
|
+
group: "antipattern",
|
|
41140
|
+
description: "Detects interdependencies in Layer.mergeAll calls where one layer provides a service that another layer requires",
|
|
41141
|
+
defaultSeverity: "warning",
|
|
41142
|
+
fixable: true,
|
|
41143
|
+
supportedEffect: [
|
|
41144
|
+
"v3",
|
|
41145
|
+
"v4"
|
|
41146
|
+
],
|
|
41147
|
+
preview: {
|
|
41148
|
+
sourceText: 'import { Effect, Layer, ServiceMap } from "effect"\n\nclass A extends ServiceMap.Service<A>()("A", { make: Effect.succeed({}) }) {\n static Default = Layer.effect(this, this.make)\n}\nclass B extends ServiceMap.Service<B>()("B", { make: Effect.as(A.asEffect(), {}) }) {\n static Default = Layer.effect(this, this.make)\n}\nexport const preview = Layer.mergeAll(A.Default, B.Default)\n',
|
|
41149
|
+
diagnostics: [
|
|
41150
|
+
{
|
|
41151
|
+
start: 355,
|
|
41152
|
+
end: 364,
|
|
41153
|
+
text: "This layer provides A which is required by another layer in the same Layer.mergeAll call. Layer.mergeAll creates layers in parallel, so dependencies between layers will not be satisfied. Consider moving this layer into a Layer.provideMerge after the Layer.mergeAll. effect(layerMergeAllWithDependencies)"
|
|
41154
|
+
}
|
|
41155
|
+
]
|
|
41156
|
+
}
|
|
41157
|
+
},
|
|
41158
|
+
{
|
|
41159
|
+
name: "leakingRequirements",
|
|
41160
|
+
group: "antipattern",
|
|
41161
|
+
description: "Detects implementation services leaked in service methods",
|
|
41162
|
+
defaultSeverity: "suggestion",
|
|
41163
|
+
fixable: false,
|
|
41164
|
+
supportedEffect: [
|
|
41165
|
+
"v3",
|
|
41166
|
+
"v4"
|
|
41167
|
+
],
|
|
41168
|
+
preview: {
|
|
41169
|
+
sourceText: 'import type { Effect } from "effect"\nimport { ServiceMap } from "effect"\n\nclass FileSystem extends ServiceMap.Service<FileSystem, {\n write: (s: string) => Effect.Effect<void>\n}>()("FileSystem") {}\n\nexport class Cache extends ServiceMap.Service<Cache, {\n read: Effect.Effect<string, never, FileSystem>\n save: () => Effect.Effect<void, never, FileSystem>\n}>()("Cache") {}\n',
|
|
41170
|
+
diagnostics: [
|
|
41171
|
+
{
|
|
41172
|
+
start: 212,
|
|
41173
|
+
end: 217,
|
|
41174
|
+
text: "Methods of this Service require `FileSystem` from every caller.\n\nThis leaks implementation details into the service's public type \u2014 callers shouldn't need to know *how* the service works internally, only *what* it provides.\n\nResolve these dependencies at Layer creation and provide them to each method, so the service's type reflects its purpose, not its implementation.\n\nTo suppress this diagnostic for specific dependency types that are intentionally passed through (e.g., HttpServerRequest), add `@effect-leakable-service` JSDoc to their interface declarations (e.g., the `FileSystem` interface), not to this service.\n\nMore info and examples at https://effect.website/docs/requirements-management/layers/#avoiding-requirement-leakage effect(leakingRequirements)"
|
|
41175
|
+
}
|
|
41176
|
+
]
|
|
41177
|
+
}
|
|
41178
|
+
},
|
|
41179
|
+
{
|
|
41180
|
+
name: "multipleEffectProvide",
|
|
41181
|
+
group: "antipattern",
|
|
41182
|
+
description: "Warns against chaining Effect.provide calls which can cause service lifecycle issues",
|
|
41183
|
+
defaultSeverity: "warning",
|
|
41184
|
+
fixable: true,
|
|
41185
|
+
supportedEffect: [
|
|
41186
|
+
"v3",
|
|
41187
|
+
"v4"
|
|
41188
|
+
],
|
|
41189
|
+
preview: {
|
|
41190
|
+
sourceText: 'import { Effect, Layer, ServiceMap } from "effect"\n\nclass A extends ServiceMap.Service<A>()("A", { make: Effect.succeed({}) }) {\n static Default = Layer.effect(this, this.make)\n}\nclass B extends ServiceMap.Service<B>()("B", { make: Effect.succeed({}) }) {\n static Default = Layer.effect(this, this.make)\n}\nexport const preview = Effect.void.pipe(Effect.provide(A.Default), Effect.provide(B.Default))\n',
|
|
41191
|
+
diagnostics: [
|
|
41192
|
+
{
|
|
41193
|
+
start: 348,
|
|
41194
|
+
end: 373,
|
|
41195
|
+
text: "Avoid chaining Effect.provide calls, as this can lead to service lifecycle issues. Instead, merge layers and provide them in a single call. effect(multipleEffectProvide)"
|
|
41196
|
+
}
|
|
41197
|
+
]
|
|
41198
|
+
}
|
|
41199
|
+
},
|
|
41200
|
+
{
|
|
41201
|
+
name: "returnEffectInGen",
|
|
41202
|
+
group: "antipattern",
|
|
41203
|
+
description: "Warns when returning an Effect in a generator causes nested Effect<Effect<...>>",
|
|
41204
|
+
defaultSeverity: "suggestion",
|
|
41205
|
+
fixable: true,
|
|
41206
|
+
supportedEffect: [
|
|
41207
|
+
"v3",
|
|
41208
|
+
"v4"
|
|
41209
|
+
],
|
|
41210
|
+
preview: {
|
|
41211
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\nexport const preview = Effect.gen(function*() {\n return Effect.succeed(1)\n})\n',
|
|
41212
|
+
diagnostics: [
|
|
41213
|
+
{
|
|
41214
|
+
start: 91,
|
|
41215
|
+
end: 115,
|
|
41216
|
+
text: "You are returning an Effect-able type inside a generator function, and will result in nested Effect<Effect<...>>.\nMaybe you wanted to return yield* instead?\nNested Effect-able types may be intended if you plan to later manually flatten or unwrap this Effect, if so you can safely disable this diagnostic for this line through quickfixes. effect(returnEffectInGen)"
|
|
41217
|
+
}
|
|
41218
|
+
]
|
|
41219
|
+
}
|
|
41220
|
+
},
|
|
41221
|
+
{
|
|
41222
|
+
name: "runEffectInsideEffect",
|
|
41223
|
+
group: "antipattern",
|
|
41224
|
+
description: "Suggests using Runtime methods instead of Effect.run* inside Effect contexts",
|
|
41225
|
+
defaultSeverity: "suggestion",
|
|
41226
|
+
fixable: true,
|
|
41227
|
+
supportedEffect: [
|
|
41228
|
+
"v3"
|
|
41229
|
+
],
|
|
41230
|
+
preview: {
|
|
41231
|
+
sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.gen(function*() {\n const run = () => Effect.runSync(Effect.succeed(1))\n return run()\n})\n',
|
|
41232
|
+
diagnostics: [
|
|
41233
|
+
{
|
|
41234
|
+
start: 101,
|
|
41235
|
+
end: 115,
|
|
41236
|
+
text: "Using Effect.runSync inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.\nConsider extracting the Runtime by using for example Effect.runtime and then use Runtime.runSync with the extracted runtime instead. effect(runEffectInsideEffect)"
|
|
41237
|
+
}
|
|
41238
|
+
]
|
|
41239
|
+
}
|
|
41240
|
+
},
|
|
41241
|
+
{
|
|
41242
|
+
name: "schemaSyncInEffect",
|
|
41243
|
+
group: "antipattern",
|
|
41244
|
+
description: "Suggests using Effect-based Schema methods instead of sync methods inside Effect generators",
|
|
41245
|
+
defaultSeverity: "suggestion",
|
|
41246
|
+
fixable: false,
|
|
41247
|
+
supportedEffect: [
|
|
41248
|
+
"v3"
|
|
41249
|
+
],
|
|
41250
|
+
preview: {
|
|
41251
|
+
sourceText: 'import * as Effect from "effect/Effect"\nimport * as Schema from "effect/Schema"\n\nconst Person = Schema.Struct({ name: Schema.String, age: Schema.Number })\n\nexport const preview = Effect.gen(function*() {\n const input = yield* Effect.succeed({ name: "Ada", age: 1 })\n return Schema.decodeSync(Person)(input)\n})\n',
|
|
41252
|
+
diagnostics: [
|
|
41253
|
+
{
|
|
41254
|
+
start: 276,
|
|
41255
|
+
end: 293,
|
|
41256
|
+
text: "Using Schema.decodeSync inside an Effect generator is not recommended. Use Schema.decode instead to get properly typed error channel. effect(schemaSyncInEffect)"
|
|
41257
|
+
}
|
|
41258
|
+
]
|
|
41259
|
+
}
|
|
41260
|
+
},
|
|
41261
|
+
{
|
|
41262
|
+
name: "scopeInLayerEffect",
|
|
41263
|
+
group: "antipattern",
|
|
41264
|
+
description: "Suggests using Layer.scoped instead of Layer.effect when Scope is in requirements",
|
|
41265
|
+
defaultSeverity: "warning",
|
|
41266
|
+
fixable: true,
|
|
41267
|
+
supportedEffect: [
|
|
41268
|
+
"v3"
|
|
41269
|
+
],
|
|
41270
|
+
preview: {
|
|
41271
|
+
sourceText: 'import * as Context from "effect/Context"\nimport * as Effect from "effect/Effect"\nimport * as Layer from "effect/Layer"\n\nclass Cache extends Context.Tag("Cache")<Cache, { ok: true }>() {}\nexport const preview = Layer.effect(Cache, Effect.gen(function*() {\n yield* Effect.addFinalizer(() => Effect.void)\n return { ok: true as const }\n}))\n',
|
|
41272
|
+
diagnostics: [
|
|
41273
|
+
{
|
|
41274
|
+
start: 211,
|
|
41275
|
+
end: 338,
|
|
41276
|
+
text: 'Seems like you are constructing a layer with a scope in the requirements.\nConsider using "scoped" instead to get rid of the scope in the requirements. effect(scopeInLayerEffect)'
|
|
41277
|
+
}
|
|
41278
|
+
]
|
|
41279
|
+
}
|
|
41280
|
+
},
|
|
41281
|
+
{
|
|
41282
|
+
name: "strictEffectProvide",
|
|
41283
|
+
group: "antipattern",
|
|
41284
|
+
description: "Warns when using Effect.provide with layers outside of application entry points",
|
|
41285
|
+
defaultSeverity: "off",
|
|
41286
|
+
fixable: false,
|
|
41287
|
+
supportedEffect: [
|
|
41288
|
+
"v3",
|
|
41289
|
+
"v4"
|
|
41290
|
+
],
|
|
41291
|
+
preview: {
|
|
41292
|
+
sourceText: 'import { Effect, Layer, ServiceMap } from "effect"\n\nclass Config extends ServiceMap.Service<Config>()("Config", { make: Effect.succeed({}) }) {\n static Default = Layer.effect(this, this.make)\n}\nexport const preview = Effect.void.pipe(Effect.provide(Config.Default))\n',
|
|
41293
|
+
diagnostics: [
|
|
41294
|
+
{
|
|
41295
|
+
start: 235,
|
|
41296
|
+
end: 265,
|
|
41297
|
+
text: "Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide)"
|
|
41298
|
+
}
|
|
41299
|
+
]
|
|
41300
|
+
}
|
|
41301
|
+
},
|
|
41302
|
+
{
|
|
41303
|
+
name: "tryCatchInEffectGen",
|
|
41304
|
+
group: "antipattern",
|
|
41305
|
+
description: "Discourages try/catch in Effect generators in favor of Effect error handling",
|
|
41306
|
+
defaultSeverity: "suggestion",
|
|
41307
|
+
fixable: false,
|
|
41308
|
+
supportedEffect: [
|
|
41309
|
+
"v3",
|
|
41310
|
+
"v4"
|
|
41311
|
+
],
|
|
41312
|
+
preview: {
|
|
41313
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\nexport const preview = Effect.gen(function*() {\n try { return yield* Effect.succeed(1) }\n catch { return 0 }\n})\n',
|
|
41314
|
+
diagnostics: [
|
|
41315
|
+
{
|
|
41316
|
+
start: 91,
|
|
41317
|
+
end: 151,
|
|
41318
|
+
text: "Avoid using try/catch inside Effect generators. Use Effect's error handling mechanisms instead (e.g. Effect.try, Effect.tryPromise, Effect.catch, Effect.catchTag). effect(tryCatchInEffectGen)"
|
|
41319
|
+
}
|
|
41320
|
+
]
|
|
41321
|
+
}
|
|
41322
|
+
},
|
|
41323
|
+
{
|
|
41324
|
+
name: "unknownInEffectCatch",
|
|
41325
|
+
group: "antipattern",
|
|
41326
|
+
description: "Warns when catch callbacks return unknown instead of typed errors",
|
|
41327
|
+
defaultSeverity: "warning",
|
|
41328
|
+
fixable: false,
|
|
41329
|
+
supportedEffect: [
|
|
41330
|
+
"v3",
|
|
41331
|
+
"v4"
|
|
41332
|
+
],
|
|
41333
|
+
preview: {
|
|
41334
|
+
sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.tryPromise({\n try: async () => 1,\n catch: (error) => error\n})\n',
|
|
41335
|
+
diagnostics: [
|
|
41336
|
+
{
|
|
41337
|
+
start: 56,
|
|
41338
|
+
end: 73,
|
|
41339
|
+
text: "The 'catch' callback in Effect.tryPromise returns 'unknown'. The catch callback should be used to provide typed errors.\nConsider wrapping unknown errors into Effect's Data.TaggedError for example, or narrow down the type to the specific error raised. effect(unknownInEffectCatch)"
|
|
41340
|
+
}
|
|
41341
|
+
]
|
|
41342
|
+
}
|
|
41343
|
+
},
|
|
41344
|
+
{
|
|
41345
|
+
name: "extendsNativeError",
|
|
41346
|
+
group: "effectNative",
|
|
41347
|
+
description: "Warns when a class directly extends the native Error class",
|
|
41348
|
+
defaultSeverity: "off",
|
|
41349
|
+
fixable: false,
|
|
41350
|
+
supportedEffect: [
|
|
41351
|
+
"v3",
|
|
41352
|
+
"v4"
|
|
41353
|
+
],
|
|
41354
|
+
preview: {
|
|
41355
|
+
sourceText: "\nexport class PreviewError extends Error {}\n",
|
|
41356
|
+
diagnostics: [
|
|
41357
|
+
{
|
|
41358
|
+
start: 14,
|
|
41359
|
+
end: 26,
|
|
41360
|
+
text: "Avoid extending the native 'Error' class directly. Consider using a tagged error (e.g. Data.TaggedError) to maintain type safety in the Effect failure channel. effect(extendsNativeError)"
|
|
41361
|
+
}
|
|
41362
|
+
]
|
|
41363
|
+
}
|
|
41364
|
+
},
|
|
41365
|
+
{
|
|
41366
|
+
name: "globalFetch",
|
|
41367
|
+
group: "effectNative",
|
|
41368
|
+
description: "Warns when using the global fetch function instead of the Effect HTTP client",
|
|
41369
|
+
defaultSeverity: "off",
|
|
41370
|
+
fixable: false,
|
|
41371
|
+
supportedEffect: [
|
|
41372
|
+
"v3",
|
|
41373
|
+
"v4"
|
|
41374
|
+
],
|
|
41375
|
+
preview: {
|
|
41376
|
+
sourceText: '\nexport const preview = fetch("https://example.com")\n',
|
|
41377
|
+
diagnostics: [
|
|
41378
|
+
{
|
|
41379
|
+
start: 24,
|
|
41380
|
+
end: 29,
|
|
41381
|
+
text: "Prefer using HttpClient from @effect/platform instead of the global 'fetch' function. effect(globalFetch)"
|
|
41382
|
+
}
|
|
41383
|
+
]
|
|
41384
|
+
}
|
|
41385
|
+
},
|
|
41386
|
+
{
|
|
41387
|
+
name: "instanceOfSchema",
|
|
41388
|
+
group: "effectNative",
|
|
41389
|
+
description: "Suggests using Schema.is instead of instanceof for Effect Schema types",
|
|
41390
|
+
defaultSeverity: "off",
|
|
41391
|
+
fixable: true,
|
|
41392
|
+
supportedEffect: [
|
|
41393
|
+
"v3",
|
|
41394
|
+
"v4"
|
|
41395
|
+
],
|
|
41396
|
+
preview: {
|
|
41397
|
+
sourceText: 'import { Schema } from "effect"\n\nclass User extends Schema.Class<User>("User")({ name: Schema.String }) {}\ndeclare const value: unknown\nexport const preview = value instanceof User\n',
|
|
41398
|
+
diagnostics: [
|
|
41399
|
+
{
|
|
41400
|
+
start: 159,
|
|
41401
|
+
end: 180,
|
|
41402
|
+
text: "Consider using Schema.is instead of instanceof for Effect Schema types. effect(instanceOfSchema)"
|
|
41403
|
+
}
|
|
41404
|
+
]
|
|
41405
|
+
}
|
|
41406
|
+
},
|
|
41407
|
+
{
|
|
41408
|
+
name: "nodeBuiltinImport",
|
|
41409
|
+
group: "effectNative",
|
|
41410
|
+
description: "Warns when importing Node.js built-in modules that have Effect-native counterparts",
|
|
41411
|
+
defaultSeverity: "off",
|
|
41412
|
+
fixable: false,
|
|
41413
|
+
supportedEffect: [
|
|
41414
|
+
"v3",
|
|
41415
|
+
"v4"
|
|
41416
|
+
],
|
|
41417
|
+
preview: {
|
|
41418
|
+
sourceText: 'import fs from "node:fs"\n\nexport const preview = fs.readFileSync\n',
|
|
41419
|
+
diagnostics: [
|
|
41420
|
+
{
|
|
41421
|
+
start: 15,
|
|
41422
|
+
end: 24,
|
|
41423
|
+
text: "Prefer using FileSystem from @effect/platform instead of the Node.js 'fs' module. effect(nodeBuiltinImport)"
|
|
41424
|
+
}
|
|
41425
|
+
]
|
|
41426
|
+
}
|
|
41427
|
+
},
|
|
41428
|
+
{
|
|
41429
|
+
name: "preferSchemaOverJson",
|
|
41430
|
+
group: "effectNative",
|
|
41431
|
+
description: "Suggests using Effect Schema for JSON operations instead of JSON.parse/JSON.stringify which may throw",
|
|
41432
|
+
defaultSeverity: "suggestion",
|
|
41433
|
+
fixable: false,
|
|
41434
|
+
supportedEffect: [
|
|
41435
|
+
"v3",
|
|
41436
|
+
"v4"
|
|
41437
|
+
],
|
|
41438
|
+
preview: {
|
|
41439
|
+
sourceText: `import { Effect } from "effect"
|
|
41440
|
+
|
|
41441
|
+
export const preview = Effect.gen(function*() {
|
|
41442
|
+
const text = yield* Effect.succeed('{"ok":true}')
|
|
41443
|
+
return JSON.parse(text)
|
|
41444
|
+
})
|
|
41445
|
+
`,
|
|
41446
|
+
diagnostics: [
|
|
41447
|
+
{
|
|
41448
|
+
start: 142,
|
|
41449
|
+
end: 158,
|
|
41450
|
+
text: "Consider using Effect Schema for JSON operations instead of JSON.parse/JSON.stringify effect(preferSchemaOverJson)"
|
|
41451
|
+
}
|
|
41452
|
+
]
|
|
41453
|
+
}
|
|
41454
|
+
},
|
|
41455
|
+
{
|
|
41456
|
+
name: "catchAllToMapError",
|
|
41457
|
+
group: "style",
|
|
41458
|
+
description: "Suggests using Effect.mapError instead of Effect.catchAll when the callback only wraps the error with Effect.fail",
|
|
41459
|
+
defaultSeverity: "suggestion",
|
|
41460
|
+
fixable: true,
|
|
41461
|
+
supportedEffect: [
|
|
41462
|
+
"v3",
|
|
41463
|
+
"v4"
|
|
41464
|
+
],
|
|
41465
|
+
preview: {
|
|
41466
|
+
sourceText: 'import { Effect } from "effect"\n\nclass WrappedError {\n constructor(readonly cause: unknown) {}\n}\n\nexport const preview = Effect.fail("boom").pipe(\n Effect.catch((cause) => Effect.fail(new WrappedError(cause)))\n)\n',
|
|
41467
|
+
diagnostics: [
|
|
41468
|
+
{
|
|
41469
|
+
start: 150,
|
|
41470
|
+
end: 162,
|
|
41471
|
+
text: "You can use Effect.mapError instead of Effect.catch + Effect.fail to transform the error type. effect(catchAllToMapError)"
|
|
41472
|
+
}
|
|
41473
|
+
]
|
|
41474
|
+
}
|
|
41475
|
+
},
|
|
41476
|
+
{
|
|
41477
|
+
name: "deterministicKeys",
|
|
41478
|
+
group: "style",
|
|
41479
|
+
description: "Enforces deterministic naming for service/tag/error identifiers based on class names",
|
|
41480
|
+
defaultSeverity: "off",
|
|
41481
|
+
fixable: true,
|
|
41482
|
+
supportedEffect: [
|
|
41483
|
+
"v3",
|
|
41484
|
+
"v4"
|
|
41485
|
+
],
|
|
41486
|
+
preview: {
|
|
41487
|
+
sourceText: 'import { ServiceMap } from "effect"\n\nexport class RenamedService\n extends ServiceMap.Service<RenamedService, {}>()("CustomIdentifier") {}\n',
|
|
41488
|
+
diagnostics: [
|
|
41489
|
+
{
|
|
41490
|
+
start: 116,
|
|
41491
|
+
end: 134,
|
|
41492
|
+
text: "Key should be '@effect/harness-effect-v4/examples/diagnostics/deterministicKeys_preview/RenamedService' effect(deterministicKeys)"
|
|
41493
|
+
}
|
|
41494
|
+
]
|
|
41495
|
+
}
|
|
41496
|
+
},
|
|
41497
|
+
{
|
|
41498
|
+
name: "effectFnOpportunity",
|
|
41499
|
+
group: "style",
|
|
41500
|
+
description: "Suggests using Effect.fn for functions that returns an Effect",
|
|
41501
|
+
defaultSeverity: "suggestion",
|
|
41502
|
+
fixable: true,
|
|
41503
|
+
supportedEffect: [
|
|
41504
|
+
"v3",
|
|
41505
|
+
"v4"
|
|
41506
|
+
],
|
|
41507
|
+
preview: {
|
|
41508
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\nexport const preview = () => Effect.gen(function*() {\n return yield* Effect.succeed(1)\n})\n',
|
|
41509
|
+
diagnostics: [
|
|
41510
|
+
{
|
|
41511
|
+
start: 54,
|
|
41512
|
+
end: 61,
|
|
41513
|
+
text: "Can be rewritten as a reusable function: Effect.fn(function*() { ... }) effect(effectFnOpportunity)"
|
|
41514
|
+
}
|
|
41515
|
+
]
|
|
41516
|
+
}
|
|
41517
|
+
},
|
|
41518
|
+
{
|
|
41519
|
+
name: "effectMapVoid",
|
|
41520
|
+
group: "style",
|
|
41521
|
+
description: "Suggests using Effect.asVoid instead of Effect.map(() => void 0), Effect.map(() => undefined), or Effect.map(() => {})",
|
|
41522
|
+
defaultSeverity: "suggestion",
|
|
41523
|
+
fixable: true,
|
|
41524
|
+
supportedEffect: [
|
|
41525
|
+
"v3",
|
|
41526
|
+
"v4"
|
|
41527
|
+
],
|
|
41528
|
+
preview: {
|
|
41529
|
+
sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.succeed(1).pipe(\n Effect.map(() => undefined)\n)\n',
|
|
41530
|
+
diagnostics: [
|
|
41531
|
+
{
|
|
41532
|
+
start: 82,
|
|
41533
|
+
end: 92,
|
|
41534
|
+
text: "Effect.asVoid can be used instead to discard the success value effect(effectMapVoid)"
|
|
41535
|
+
}
|
|
41536
|
+
]
|
|
41537
|
+
}
|
|
41538
|
+
},
|
|
41539
|
+
{
|
|
41540
|
+
name: "effectSucceedWithVoid",
|
|
41541
|
+
group: "style",
|
|
41542
|
+
description: "Suggests using Effect.void instead of Effect.succeed(undefined) or Effect.succeed(void 0)",
|
|
41543
|
+
defaultSeverity: "suggestion",
|
|
41544
|
+
fixable: true,
|
|
41545
|
+
supportedEffect: [
|
|
41546
|
+
"v3",
|
|
41547
|
+
"v4"
|
|
41548
|
+
],
|
|
41549
|
+
preview: {
|
|
41550
|
+
sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.succeed(undefined)\n',
|
|
41551
|
+
diagnostics: [
|
|
41552
|
+
{
|
|
41553
|
+
start: 56,
|
|
41554
|
+
end: 81,
|
|
41555
|
+
text: "Effect.void can be used instead of Effect.succeed(undefined) or Effect.succeed(void 0) effect(effectSucceedWithVoid)"
|
|
41556
|
+
}
|
|
41557
|
+
]
|
|
41558
|
+
}
|
|
41559
|
+
},
|
|
41560
|
+
{
|
|
41561
|
+
name: "importFromBarrel",
|
|
41562
|
+
group: "style",
|
|
41563
|
+
description: "Suggests importing from specific module paths instead of barrel exports",
|
|
41564
|
+
defaultSeverity: "off",
|
|
41565
|
+
fixable: true,
|
|
41566
|
+
supportedEffect: [
|
|
41567
|
+
"v3",
|
|
41568
|
+
"v4"
|
|
41569
|
+
],
|
|
41570
|
+
preview: {
|
|
41571
|
+
sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.void\n',
|
|
41572
|
+
diagnostics: [
|
|
41573
|
+
{
|
|
41574
|
+
start: 9,
|
|
41575
|
+
end: 15,
|
|
41576
|
+
text: "Importing from barrel module effect is not allowed. effect(importFromBarrel)"
|
|
41577
|
+
}
|
|
41578
|
+
]
|
|
41579
|
+
}
|
|
41580
|
+
},
|
|
41581
|
+
{
|
|
41582
|
+
name: "missedPipeableOpportunity",
|
|
41583
|
+
group: "style",
|
|
41584
|
+
description: "Enforces the use of pipeable style for nested function calls",
|
|
41585
|
+
defaultSeverity: "off",
|
|
41586
|
+
fixable: true,
|
|
41587
|
+
supportedEffect: [
|
|
41588
|
+
"v3",
|
|
41589
|
+
"v4"
|
|
41590
|
+
],
|
|
41591
|
+
preview: {
|
|
41592
|
+
sourceText: 'import { identity, Schema } from "effect"\n\nconst User = Schema.Struct({ id: Schema.Number })\nexport const preview = identity(identity(Schema.decodeEffect(User)({ id: 1 })))\n',
|
|
41593
|
+
diagnostics: [
|
|
41594
|
+
{
|
|
41595
|
+
start: 116,
|
|
41596
|
+
end: 172,
|
|
41597
|
+
text: "Nested function calls can be converted to pipeable style for better readability; consider using Schema.decodeEffect(User)({ id: 1 }).pipe(...) instead. effect(missedPipeableOpportunity)"
|
|
41598
|
+
}
|
|
41599
|
+
]
|
|
41600
|
+
}
|
|
41601
|
+
},
|
|
41602
|
+
{
|
|
41603
|
+
name: "missingEffectServiceDependency",
|
|
41604
|
+
group: "style",
|
|
41605
|
+
description: "Checks that Effect.Service dependencies satisfy all required layer inputs",
|
|
41606
|
+
defaultSeverity: "off",
|
|
41607
|
+
fixable: false,
|
|
41608
|
+
supportedEffect: [
|
|
41609
|
+
"v3"
|
|
41610
|
+
],
|
|
41611
|
+
preview: {
|
|
41612
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\nclass Db extends Effect.Service<Db>()("Db", { succeed: { ok: true } }) {}\nexport class Repo extends Effect.Service<Repo>()("Repo", {\n effect: Effect.gen(function*() {\n yield* Db\n return { all: Effect.succeed([] as Array<number>) }\n })\n}) {}\n',
|
|
41613
|
+
diagnostics: [
|
|
41614
|
+
{
|
|
41615
|
+
start: 128,
|
|
41616
|
+
end: 132,
|
|
41617
|
+
text: "Service 'Db' is required but not provided by dependencies effect(missingEffectServiceDependency)"
|
|
41618
|
+
}
|
|
41619
|
+
]
|
|
41620
|
+
}
|
|
41621
|
+
},
|
|
41622
|
+
{
|
|
41623
|
+
name: "redundantSchemaTagIdentifier",
|
|
41624
|
+
group: "style",
|
|
41625
|
+
description: "Suggests removing redundant identifier argument when it equals the tag value in Schema.TaggedClass/TaggedError/TaggedRequest",
|
|
41626
|
+
defaultSeverity: "suggestion",
|
|
41627
|
+
fixable: true,
|
|
41628
|
+
supportedEffect: [
|
|
41629
|
+
"v3",
|
|
41630
|
+
"v4"
|
|
41631
|
+
],
|
|
41632
|
+
preview: {
|
|
41633
|
+
sourceText: 'import * as Schema from "effect/Schema"\n\nexport class Preview\n extends Schema.TaggedClass<Preview>("Preview")("Preview", {\n value: Schema.String\n }) {}\n',
|
|
41634
|
+
diagnostics: [
|
|
41635
|
+
{
|
|
41636
|
+
start: 100,
|
|
41637
|
+
end: 109,
|
|
41638
|
+
text: "Identifier 'Preview' is redundant since it equals the _tag value effect(redundantSchemaTagIdentifier)"
|
|
41639
|
+
}
|
|
41640
|
+
]
|
|
41641
|
+
}
|
|
41642
|
+
},
|
|
41643
|
+
{
|
|
41644
|
+
name: "schemaStructWithTag",
|
|
41645
|
+
group: "style",
|
|
41646
|
+
description: "Suggests using Schema.TaggedStruct instead of Schema.Struct with _tag field",
|
|
41647
|
+
defaultSeverity: "suggestion",
|
|
41648
|
+
fixable: true,
|
|
41649
|
+
supportedEffect: [
|
|
41650
|
+
"v3",
|
|
41651
|
+
"v4"
|
|
41652
|
+
],
|
|
41653
|
+
preview: {
|
|
41654
|
+
sourceText: 'import * as Schema from "effect/Schema"\n\nexport const preview = Schema.Struct({\n _tag: Schema.Literal("User"),\n name: Schema.String\n})\n',
|
|
41655
|
+
diagnostics: [
|
|
41656
|
+
{
|
|
41657
|
+
start: 64,
|
|
41658
|
+
end: 136,
|
|
41659
|
+
text: "Schema.Struct with a _tag field can be simplified to Schema.TaggedStruct to make the tag optional in the constructor. effect(schemaStructWithTag)"
|
|
41660
|
+
}
|
|
41661
|
+
]
|
|
41662
|
+
}
|
|
41663
|
+
},
|
|
41664
|
+
{
|
|
41665
|
+
name: "schemaUnionOfLiterals",
|
|
41666
|
+
group: "style",
|
|
41667
|
+
description: "Simplifies Schema.Union of multiple Schema.Literal calls into single Schema.Literal",
|
|
41668
|
+
defaultSeverity: "off",
|
|
41669
|
+
fixable: true,
|
|
41670
|
+
supportedEffect: [
|
|
41671
|
+
"v3"
|
|
41672
|
+
],
|
|
41673
|
+
preview: {
|
|
41674
|
+
sourceText: 'import * as Schema from "effect/Schema"\n\nexport const preview = Schema.Union(Schema.Literal("a"), Schema.Literal("b"))\n',
|
|
41675
|
+
diagnostics: [
|
|
41676
|
+
{
|
|
41677
|
+
start: 64,
|
|
41678
|
+
end: 118,
|
|
41679
|
+
text: "A Schema.Union of multiple Schema.Literal calls can be simplified to a single Schema.Literal call. effect(schemaUnionOfLiterals)"
|
|
41680
|
+
}
|
|
41681
|
+
]
|
|
41682
|
+
}
|
|
41683
|
+
},
|
|
41684
|
+
{
|
|
41685
|
+
name: "serviceNotAsClass",
|
|
41686
|
+
group: "style",
|
|
41687
|
+
description: "Warns when ServiceMap.Service is used as a variable instead of a class declaration",
|
|
41688
|
+
defaultSeverity: "off",
|
|
41689
|
+
fixable: true,
|
|
41690
|
+
supportedEffect: [
|
|
41691
|
+
"v4"
|
|
41692
|
+
],
|
|
41693
|
+
preview: {
|
|
41694
|
+
sourceText: 'import { ServiceMap } from "effect"\n\nexport const Preview = ServiceMap.Service<{ port: number }>("Preview")\n',
|
|
41695
|
+
diagnostics: [
|
|
41696
|
+
{
|
|
41697
|
+
start: 60,
|
|
41698
|
+
end: 107,
|
|
41699
|
+
text: 'ServiceMap.Service should be used in a class declaration instead of as a variable. Use: class Preview extends ServiceMap.Service<Preview, { port: number }>()("Preview") {} effect(serviceNotAsClass)'
|
|
41700
|
+
}
|
|
41701
|
+
]
|
|
41702
|
+
}
|
|
41703
|
+
},
|
|
41704
|
+
{
|
|
41705
|
+
name: "strictBooleanExpressions",
|
|
41706
|
+
group: "style",
|
|
41707
|
+
description: "Enforces boolean types in conditional expressions for type safety",
|
|
41708
|
+
defaultSeverity: "off",
|
|
41709
|
+
fixable: false,
|
|
41710
|
+
supportedEffect: [
|
|
41711
|
+
"v3",
|
|
41712
|
+
"v4"
|
|
41713
|
+
],
|
|
41714
|
+
preview: {
|
|
41715
|
+
sourceText: "\ndeclare const value: string | undefined\nexport const preview = value ? 1 : 0\n",
|
|
41716
|
+
diagnostics: [
|
|
41717
|
+
{
|
|
41718
|
+
start: 64,
|
|
41719
|
+
end: 69,
|
|
41720
|
+
text: "Unexpected `string` type in condition, expected strictly a boolean instead. effect(strictBooleanExpressions)"
|
|
41721
|
+
},
|
|
41722
|
+
{
|
|
41723
|
+
start: 64,
|
|
41724
|
+
end: 69,
|
|
41725
|
+
text: "Unexpected `undefined` type in condition, expected strictly a boolean instead. effect(strictBooleanExpressions)"
|
|
41726
|
+
}
|
|
41727
|
+
]
|
|
41728
|
+
}
|
|
41729
|
+
},
|
|
41730
|
+
{
|
|
41731
|
+
name: "unnecessaryEffectGen",
|
|
41732
|
+
group: "style",
|
|
41733
|
+
description: "Suggests removing Effect.gen when it contains only a single return statement",
|
|
41734
|
+
defaultSeverity: "suggestion",
|
|
41735
|
+
fixable: true,
|
|
41736
|
+
supportedEffect: [
|
|
41737
|
+
"v3",
|
|
41738
|
+
"v4"
|
|
41739
|
+
],
|
|
41740
|
+
preview: {
|
|
41741
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\nexport const preview = Effect.gen(function*() {\n return yield* Effect.succeed(1)\n})\n',
|
|
41742
|
+
diagnostics: [
|
|
41743
|
+
{
|
|
41744
|
+
start: 64,
|
|
41745
|
+
end: 125,
|
|
41746
|
+
text: "This Effect.gen contains a single return statement. effect(unnecessaryEffectGen)"
|
|
41747
|
+
}
|
|
41748
|
+
]
|
|
41749
|
+
}
|
|
41750
|
+
},
|
|
41751
|
+
{
|
|
41752
|
+
name: "unnecessaryFailYieldableError",
|
|
41753
|
+
group: "style",
|
|
41754
|
+
description: "Suggests yielding yieldable errors directly instead of wrapping with Effect.fail",
|
|
41755
|
+
defaultSeverity: "suggestion",
|
|
41756
|
+
fixable: true,
|
|
41757
|
+
supportedEffect: [
|
|
41758
|
+
"v3",
|
|
41759
|
+
"v4"
|
|
41760
|
+
],
|
|
41761
|
+
preview: {
|
|
41762
|
+
sourceText: 'import * as Data from "effect/Data"\nimport * as Effect from "effect/Effect"\n\nclass Boom extends Data.TaggedError("Boom")<{}> {}\nexport const preview = Effect.gen(function*() {\n yield* Effect.fail(new Boom())\n})\n',
|
|
41763
|
+
diagnostics: [
|
|
41764
|
+
{
|
|
41765
|
+
start: 178,
|
|
41766
|
+
end: 208,
|
|
41767
|
+
text: "This Effect.fail call uses a yieldable error type as argument. You can yield* the error directly instead. effect(unnecessaryFailYieldableError)"
|
|
41768
|
+
}
|
|
41769
|
+
]
|
|
41770
|
+
}
|
|
41771
|
+
},
|
|
41772
|
+
{
|
|
41773
|
+
name: "unnecessaryPipe",
|
|
41774
|
+
group: "style",
|
|
41775
|
+
description: "Removes pipe calls with no arguments",
|
|
41776
|
+
defaultSeverity: "suggestion",
|
|
41777
|
+
fixable: true,
|
|
41778
|
+
supportedEffect: [
|
|
41779
|
+
"v3",
|
|
41780
|
+
"v4"
|
|
41781
|
+
],
|
|
41782
|
+
preview: {
|
|
41783
|
+
sourceText: 'import { pipe } from "effect/Function"\n\nexport const preview = pipe(1)\n',
|
|
41784
|
+
diagnostics: [
|
|
41785
|
+
{
|
|
41786
|
+
start: 63,
|
|
41787
|
+
end: 70,
|
|
41788
|
+
text: "This pipe call contains no arguments. effect(unnecessaryPipe)"
|
|
41789
|
+
}
|
|
41790
|
+
]
|
|
41791
|
+
}
|
|
41792
|
+
},
|
|
41793
|
+
{
|
|
41794
|
+
name: "unnecessaryPipeChain",
|
|
41795
|
+
group: "style",
|
|
41796
|
+
description: "Simplifies chained pipe calls into a single pipe call",
|
|
41797
|
+
defaultSeverity: "suggestion",
|
|
41798
|
+
fixable: true,
|
|
41799
|
+
supportedEffect: [
|
|
41800
|
+
"v3",
|
|
41801
|
+
"v4"
|
|
41802
|
+
],
|
|
41803
|
+
preview: {
|
|
41804
|
+
sourceText: 'import * as Effect from "effect/Effect"\n\nexport const preview = Effect.succeed(1).pipe(Effect.asVoid).pipe(Effect.as("done"))\n',
|
|
41805
|
+
diagnostics: [
|
|
41806
|
+
{
|
|
41807
|
+
start: 64,
|
|
41808
|
+
end: 125,
|
|
41809
|
+
text: "Chained pipe calls can be simplified to a single pipe call effect(unnecessaryPipeChain)"
|
|
41810
|
+
}
|
|
41811
|
+
]
|
|
41812
|
+
}
|
|
41813
|
+
}
|
|
41814
|
+
]
|
|
41815
|
+
};
|
|
41816
|
+
|
|
41817
|
+
// src/cli/setup/diagnostic-info.ts
|
|
41818
|
+
var diagnosticMetadata = metadata_default;
|
|
41819
|
+
function getDiagnosticGroups() {
|
|
41820
|
+
return diagnosticMetadata.groups;
|
|
41821
|
+
}
|
|
41822
|
+
function getDiagnosticMetadataRules() {
|
|
41823
|
+
return diagnosticMetadata.rules;
|
|
41824
|
+
}
|
|
41825
|
+
function getAllDiagnostics() {
|
|
41826
|
+
const diagnosticsByName = new Map(diagnostics.map((diagnostic) => [diagnostic.name, diagnostic]));
|
|
41827
|
+
return getDiagnosticMetadataRules().flatMap((metadataRule) => {
|
|
41828
|
+
const diagnostic = diagnosticsByName.get(metadataRule.name);
|
|
41829
|
+
if (!diagnostic) {
|
|
41830
|
+
return [];
|
|
41831
|
+
}
|
|
41832
|
+
return [{
|
|
41833
|
+
name: diagnostic.name,
|
|
41834
|
+
code: diagnostic.code,
|
|
41835
|
+
group: metadataRule.group,
|
|
41836
|
+
defaultSeverity: diagnostic.severity,
|
|
41837
|
+
description: metadataRule.description,
|
|
41838
|
+
preview: metadataRule.preview
|
|
41839
|
+
}];
|
|
41840
|
+
});
|
|
41841
|
+
}
|
|
41842
|
+
function cycleSeverity(current, direction) {
|
|
41843
|
+
const order = ["off", "suggestion", "message", "warning", "error"];
|
|
41844
|
+
const currentIndex = order.indexOf(current);
|
|
41845
|
+
if (direction === "right") {
|
|
41846
|
+
return order[(currentIndex + 1) % order.length];
|
|
41847
|
+
} else {
|
|
41848
|
+
return order[(currentIndex - 1 + order.length) % order.length];
|
|
41849
|
+
}
|
|
41850
|
+
}
|
|
41851
|
+
var shortNames = {
|
|
41852
|
+
off: "off",
|
|
41853
|
+
suggestion: "sugg",
|
|
41854
|
+
message: "info",
|
|
41855
|
+
warning: "warn",
|
|
41856
|
+
error: "err"
|
|
41857
|
+
};
|
|
41858
|
+
var MAX_SEVERITY_LENGTH = Object.values(shortNames).reduce((max2, name) => Math.max(max2, name.length), 0);
|
|
41859
|
+
function getSeverityShortName(severity) {
|
|
41860
|
+
return shortNames[severity] ?? "???";
|
|
41861
|
+
}
|
|
41862
|
+
|
|
41863
|
+
// src/cli/setup/diagnostic-prompt.ts
|
|
41864
|
+
var diagnosticGroups = getDiagnosticGroups();
|
|
41865
|
+
var Action2 = taggedEnum();
|
|
41866
|
+
var SEARCH_ICON = "/";
|
|
41867
|
+
var MIN_PREVIEW_AND_MESSAGES_LINES = 18;
|
|
41868
|
+
function getControlsLegend(searchText) {
|
|
41869
|
+
const searchLegend = searchText.length === 0 ? `${SEARCH_ICON} type to search` : `${SEARCH_ICON} searching: ${searchText}`;
|
|
41870
|
+
return `\u2190/\u2192 change rule \u2191/\u2193 change severity ${searchLegend}`;
|
|
41871
|
+
}
|
|
41872
|
+
function getSeveritySymbol(severity) {
|
|
41873
|
+
const symbols = {
|
|
41874
|
+
off: ".",
|
|
41875
|
+
suggestion: "?",
|
|
41876
|
+
message: "i",
|
|
41877
|
+
warning: "!",
|
|
41878
|
+
error: "x"
|
|
41879
|
+
};
|
|
41880
|
+
return symbols[severity];
|
|
41881
|
+
}
|
|
41882
|
+
function getSeverityStyle(severity) {
|
|
41883
|
+
const styles = {
|
|
41884
|
+
off: DIM,
|
|
41885
|
+
suggestion: DIM,
|
|
41886
|
+
message: BLUE,
|
|
41887
|
+
warning: YELLOW,
|
|
41888
|
+
error: RED
|
|
41889
|
+
};
|
|
41890
|
+
return styles[severity];
|
|
41891
|
+
}
|
|
41892
|
+
function renderEntry(entry, severity, isSelected) {
|
|
41893
|
+
const symbol4 = ansi(getSeveritySymbol(severity), getSeverityStyle(severity));
|
|
41894
|
+
const name = isSelected ? ansi(entry.name, UNDERLINE) : entry.name;
|
|
41895
|
+
return `${symbol4} ${name}`;
|
|
41896
|
+
}
|
|
41897
|
+
function rowsForLength(length, columns) {
|
|
41898
|
+
if (columns <= 0) {
|
|
41899
|
+
return 1;
|
|
41900
|
+
}
|
|
41901
|
+
return Math.max(1, 1 + Math.floor(Math.max(length - 1, 0) / columns));
|
|
41902
|
+
}
|
|
41903
|
+
function eraseRenderedLines(lines2, columns) {
|
|
41904
|
+
let result3 = "";
|
|
41905
|
+
for (let lineIndex = lines2.length - 1; lineIndex >= 0; lineIndex--) {
|
|
41906
|
+
const rows = rowsForLength(visibleLength(lines2[lineIndex] ?? ""), columns);
|
|
41907
|
+
for (let rowIndex = 0; rowIndex < rows; rowIndex++) {
|
|
41908
|
+
result3 += ERASE_LINE;
|
|
41909
|
+
if (!(lineIndex === 0 && rowIndex === rows - 1)) {
|
|
41910
|
+
result3 += "\x1B[1A";
|
|
41911
|
+
}
|
|
41912
|
+
}
|
|
41913
|
+
}
|
|
41914
|
+
if (lines2.length > 0) {
|
|
41915
|
+
result3 += CURSOR_LEFT;
|
|
41916
|
+
}
|
|
41917
|
+
return result3;
|
|
41918
|
+
}
|
|
41919
|
+
function wrapPaddedText(text2, initialPadding, endPadding, columns) {
|
|
41920
|
+
if (text2.length === 0) {
|
|
41921
|
+
return [initialPadding + endPadding];
|
|
41922
|
+
}
|
|
41923
|
+
const available = Math.max(columns - visibleLength(initialPadding) - visibleLength(endPadding), 1);
|
|
41924
|
+
const lines2 = [];
|
|
41925
|
+
let remaining = text2;
|
|
41926
|
+
while (remaining.length > 0) {
|
|
41927
|
+
const chunk = remaining.slice(0, available);
|
|
41928
|
+
lines2.push(initialPadding + chunk + endPadding);
|
|
41929
|
+
remaining = remaining.slice(chunk.length);
|
|
41930
|
+
}
|
|
41931
|
+
return lines2;
|
|
41932
|
+
}
|
|
41933
|
+
function wrapListItemText(text2, firstLinePadding, continuationPadding, endPadding, columns) {
|
|
41934
|
+
if (text2.length === 0) {
|
|
41935
|
+
return [firstLinePadding + endPadding];
|
|
41936
|
+
}
|
|
41937
|
+
const firstAvailable = Math.max(columns - visibleLength(firstLinePadding) - visibleLength(endPadding), 1);
|
|
41938
|
+
const continuationAvailable = Math.max(
|
|
41939
|
+
columns - visibleLength(continuationPadding) - visibleLength(endPadding),
|
|
41940
|
+
1
|
|
41941
|
+
);
|
|
41942
|
+
const lines2 = [];
|
|
41943
|
+
let remaining = text2;
|
|
41944
|
+
let isFirstLine = true;
|
|
41945
|
+
while (remaining.length > 0) {
|
|
41946
|
+
const padding = isFirstLine ? firstLinePadding : continuationPadding;
|
|
41947
|
+
const available = isFirstLine ? firstAvailable : continuationAvailable;
|
|
41948
|
+
const chunk = remaining.slice(0, available);
|
|
41949
|
+
lines2.push(padding + chunk + endPadding);
|
|
41950
|
+
remaining = remaining.slice(chunk.length);
|
|
41951
|
+
isFirstLine = false;
|
|
41952
|
+
}
|
|
41953
|
+
return lines2;
|
|
41954
|
+
}
|
|
41955
|
+
function renderPaddedLine(text2, initialPadding, endPadding, columns) {
|
|
41956
|
+
const available = Math.max(columns - visibleLength(initialPadding) - visibleLength(endPadding), 0);
|
|
41957
|
+
const truncated = visibleLength(text2) <= available ? text2 : text2.slice(0, available);
|
|
41958
|
+
const padding = Math.max(available - visibleLength(truncated), 0);
|
|
41959
|
+
return initialPadding + truncated + " ".repeat(padding) + endPadding;
|
|
41960
|
+
}
|
|
41961
|
+
function mergeHighlightRanges(ranges) {
|
|
41962
|
+
const sorted = ranges.filter((range2) => range2.end > range2.start).slice().sort((a, b) => a.start - b.start);
|
|
41963
|
+
const merged = [];
|
|
41964
|
+
for (const range2 of sorted) {
|
|
41965
|
+
const previous = merged[merged.length - 1];
|
|
41966
|
+
if (!previous || range2.start > previous.end) {
|
|
41967
|
+
merged.push(range2);
|
|
41968
|
+
continue;
|
|
41969
|
+
}
|
|
41970
|
+
merged[merged.length - 1] = {
|
|
41971
|
+
start: previous.start,
|
|
41972
|
+
end: Math.max(previous.end, range2.end)
|
|
41973
|
+
};
|
|
41974
|
+
}
|
|
41975
|
+
return merged;
|
|
41976
|
+
}
|
|
41977
|
+
function stylePreviewLine(line, lineStart, ranges) {
|
|
41978
|
+
if (line.length === 0) {
|
|
41979
|
+
return "";
|
|
41980
|
+
}
|
|
41981
|
+
const lineEnd = lineStart + line.length;
|
|
41982
|
+
let cursor = 0;
|
|
41983
|
+
let rendered = "";
|
|
41984
|
+
for (const range2 of ranges) {
|
|
41985
|
+
const start = Math.max(range2.start, lineStart);
|
|
41986
|
+
const end = Math.min(range2.end, lineEnd);
|
|
41987
|
+
if (end <= start) {
|
|
41988
|
+
continue;
|
|
41989
|
+
}
|
|
41990
|
+
const startIndex = start - lineStart;
|
|
41991
|
+
const endIndex = end - lineStart;
|
|
41992
|
+
if (cursor < startIndex) {
|
|
41993
|
+
rendered += ansi(line.slice(cursor, startIndex), DIM);
|
|
41994
|
+
}
|
|
41995
|
+
rendered += ansi(line.slice(startIndex, endIndex), UNDERLINE);
|
|
41996
|
+
cursor = endIndex;
|
|
41997
|
+
}
|
|
41998
|
+
if (cursor < line.length) {
|
|
41999
|
+
rendered += ansi(line.slice(cursor), DIM);
|
|
42000
|
+
}
|
|
42001
|
+
return rendered;
|
|
42002
|
+
}
|
|
42003
|
+
function wrapSourceLine(line, lineStart, availableWidth) {
|
|
42004
|
+
if (line.length === 0) {
|
|
42005
|
+
return [{ text: "", start: lineStart, end: lineStart }];
|
|
42006
|
+
}
|
|
42007
|
+
const width = Math.max(availableWidth, 1);
|
|
42008
|
+
const wrapped = [];
|
|
42009
|
+
let offset = 0;
|
|
42010
|
+
while (offset < line.length) {
|
|
42011
|
+
const text2 = line.slice(offset, offset + width);
|
|
42012
|
+
wrapped.push({
|
|
42013
|
+
text: text2,
|
|
42014
|
+
start: lineStart + offset,
|
|
42015
|
+
end: lineStart + offset + text2.length
|
|
42016
|
+
});
|
|
42017
|
+
offset += text2.length;
|
|
42018
|
+
}
|
|
42019
|
+
return wrapped;
|
|
42020
|
+
}
|
|
42021
|
+
function wrapPreviewSourceText(sourceText, columns) {
|
|
42022
|
+
const availableWidth = Math.max(columns - 2, 1);
|
|
42023
|
+
const logicalLines = sourceText.split("\n");
|
|
42024
|
+
const wrapped = [];
|
|
42025
|
+
let offset = 0;
|
|
42026
|
+
for (const line of logicalLines) {
|
|
42027
|
+
for (const wrappedLine of wrapSourceLine(line, offset, availableWidth)) {
|
|
42028
|
+
wrapped.push(wrappedLine);
|
|
42029
|
+
}
|
|
42030
|
+
offset += line.length + 1;
|
|
42031
|
+
}
|
|
42032
|
+
return wrapped;
|
|
42033
|
+
}
|
|
42034
|
+
function renderPreviewSourceText(sourceText, diagnostics3, columns) {
|
|
42035
|
+
const ranges = mergeHighlightRanges(diagnostics3.map((diagnostic) => ({
|
|
42036
|
+
start: diagnostic.start,
|
|
42037
|
+
end: diagnostic.end
|
|
42038
|
+
})));
|
|
42039
|
+
return wrapPreviewSourceText(sourceText, columns).map((line) => {
|
|
42040
|
+
const rendered = stylePreviewLine(line.text, line.start, ranges);
|
|
42041
|
+
return CURSOR_TO_0 + renderPaddedLine(rendered, " ", "", columns);
|
|
42042
|
+
});
|
|
42043
|
+
}
|
|
42044
|
+
function renderPreviewMessages(diagnostics3, columns) {
|
|
42045
|
+
const messages = Array.from(new Set(diagnostics3.map((diagnostic) => diagnostic.text)));
|
|
42046
|
+
return messages.flatMap((message) => {
|
|
42047
|
+
let isFirstBlock = true;
|
|
42048
|
+
return message.split("\n").flatMap((line) => {
|
|
42049
|
+
const wrappedLines = wrapListItemText(line, isFirstBlock ? "- " : " ", " ", "", columns);
|
|
42050
|
+
isFirstBlock = false;
|
|
42051
|
+
return wrappedLines.map((wrappedLine) => CURSOR_TO_0 + ansi(wrappedLine, DIM + ITALIC));
|
|
42052
|
+
});
|
|
42053
|
+
});
|
|
42054
|
+
}
|
|
42055
|
+
function renderDivider(columns, legendText) {
|
|
42056
|
+
if (legendText === void 0) {
|
|
42057
|
+
return ansi("\u2500".repeat(Math.max(columns, 0)), DIM);
|
|
42058
|
+
}
|
|
42059
|
+
const legend = ` ${legendText} `;
|
|
42060
|
+
const legendLength = visibleLength(legend);
|
|
42061
|
+
if (columns <= legendLength) {
|
|
42062
|
+
return ansi(legend.slice(0, Math.max(columns, 0)), DIM);
|
|
42063
|
+
}
|
|
42064
|
+
const remaining = columns - legendLength;
|
|
42065
|
+
const left = "\u2500".repeat(Math.floor(remaining / 2));
|
|
42066
|
+
const right = "\u2500".repeat(remaining - left.length);
|
|
42067
|
+
return ansi(left + legend + right, DIM);
|
|
42068
|
+
}
|
|
42069
|
+
function renderSelectedRuleDivider(selected, severities, columns) {
|
|
42070
|
+
if (!selected) {
|
|
42071
|
+
return renderDivider(columns);
|
|
42072
|
+
}
|
|
42073
|
+
const currentSeverity = severities[selected.name] ?? selected.defaultSeverity;
|
|
42074
|
+
const text2 = `${selected.name} (currently set as ${getSeverityShortName(currentSeverity)})`;
|
|
42075
|
+
return renderDivider(columns, text2);
|
|
42076
|
+
}
|
|
42077
|
+
function matchesSearch(entry, searchText) {
|
|
42078
|
+
if (searchText.length === 0) {
|
|
42079
|
+
return true;
|
|
42080
|
+
}
|
|
42081
|
+
const normalized = searchText.toLowerCase();
|
|
42082
|
+
return entry.name.toLowerCase().includes(normalized) || entry.description.toLowerCase().includes(normalized) || entry.previewSourceText.toLowerCase().includes(normalized);
|
|
42083
|
+
}
|
|
42084
|
+
function getFilteredEntries(entries2, searchText) {
|
|
42085
|
+
return entries2.filter((entry) => matchesSearch(entry, searchText));
|
|
42086
|
+
}
|
|
42087
|
+
function normalizeStartIndex(length, startIndex) {
|
|
42088
|
+
if (length <= 0) {
|
|
42089
|
+
return 0;
|
|
42090
|
+
}
|
|
42091
|
+
return (startIndex % length + length) % length;
|
|
42092
|
+
}
|
|
42093
|
+
function isPrintableInput(input) {
|
|
42094
|
+
const printablePattern = new RegExp(String.raw`^[^\u0000-\u001F\u007F]+$`, "u");
|
|
42095
|
+
return !input.key.ctrl && !input.key.meta && input.input !== void 0 && input.input.length > 0 && printablePattern.test(input.input);
|
|
42096
|
+
}
|
|
42097
|
+
function buildVisibleEntries(entries2, severities, startIndex, columns) {
|
|
42098
|
+
if (entries2.length === 0) {
|
|
42099
|
+
return {
|
|
42100
|
+
fullLine: renderPaddedLine(ansi("No matching rules", DIM), " ", " ", columns),
|
|
42101
|
+
visibleRules: []
|
|
42102
|
+
};
|
|
42103
|
+
}
|
|
42104
|
+
const reservedColumns = 4;
|
|
42105
|
+
const itemColumns = Math.max(columns - reservedColumns, 0);
|
|
42106
|
+
const separator = " ";
|
|
42107
|
+
const visibleEntries = [];
|
|
42108
|
+
const visibleRules = [];
|
|
42109
|
+
let currentLength = 0;
|
|
42110
|
+
let seenCount = 0;
|
|
42111
|
+
while (seenCount < entries2.length) {
|
|
42112
|
+
const index = (startIndex + seenCount) % entries2.length;
|
|
42113
|
+
const rule = entries2[index];
|
|
42114
|
+
const currentSeverity = severities[rule.name] ?? rule.defaultSeverity;
|
|
42115
|
+
const entry = renderEntry(rule, currentSeverity, seenCount === 0);
|
|
42116
|
+
const nextLength = visibleEntries.length === 0 ? visibleLength(entry) : currentLength + separator.length + visibleLength(entry);
|
|
42117
|
+
if (nextLength > itemColumns) {
|
|
42118
|
+
break;
|
|
42119
|
+
}
|
|
42120
|
+
visibleEntries.push(entry);
|
|
42121
|
+
visibleRules.push(rule);
|
|
42122
|
+
currentLength = nextLength;
|
|
42123
|
+
seenCount++;
|
|
42124
|
+
}
|
|
42125
|
+
const leftMarker = entries2.length > 1 ? ansi("\u2190", DIM) : " ";
|
|
42126
|
+
const rightMarker = entries2.length > 1 ? ansi("\u2192", DIM) : " ";
|
|
42127
|
+
return {
|
|
42128
|
+
fullLine: renderPaddedLine(visibleEntries.join(separator), `${leftMarker} `, ` ${rightMarker}`, columns),
|
|
42129
|
+
visibleRules
|
|
42130
|
+
};
|
|
42131
|
+
}
|
|
42132
|
+
function buildGroupLine(selected, columns) {
|
|
42133
|
+
if (!selected) {
|
|
42134
|
+
return renderPaddedLine("", " ", " ", columns);
|
|
42135
|
+
}
|
|
42136
|
+
const selectedIndex = diagnosticGroups.findIndex((group) => group.id === selected.group);
|
|
42137
|
+
const rotatedGroups = diagnosticGroups.map(
|
|
42138
|
+
(_, index) => diagnosticGroups[(selectedIndex + index) % diagnosticGroups.length]
|
|
42139
|
+
);
|
|
42140
|
+
const content = rotatedGroups.map((group, index) => {
|
|
42141
|
+
const label = group.name.toUpperCase();
|
|
42142
|
+
return index === 0 ? ansi(label, BOLD) : ansi(label, DIM);
|
|
42143
|
+
}).join(" ");
|
|
42144
|
+
return renderPaddedLine(content, " ", " ", columns);
|
|
42145
|
+
}
|
|
42146
|
+
function buildFrame(entries2, searchText, severities, startIndex, columns) {
|
|
42147
|
+
const visible = buildVisibleEntries(entries2, severities, startIndex, columns);
|
|
42148
|
+
const selected = visible.visibleRules[0];
|
|
42149
|
+
const wrappedDescription = wrapPaddedText(selected?.description ?? "", " ", "", columns);
|
|
42150
|
+
const previewLines = renderPreviewSourceText(
|
|
42151
|
+
selected?.previewSourceText ?? "",
|
|
42152
|
+
selected?.previewDiagnostics ?? [],
|
|
42153
|
+
columns
|
|
42154
|
+
);
|
|
42155
|
+
const previewMessages = renderPreviewMessages(selected?.previewDiagnostics ?? [], columns);
|
|
42156
|
+
const previewSectionLines = [...previewLines, ...previewMessages];
|
|
42157
|
+
const paddingLines = Array.from(
|
|
42158
|
+
{ length: Math.max(MIN_PREVIEW_AND_MESSAGES_LINES - previewSectionLines.length, 0) },
|
|
42159
|
+
() => ""
|
|
42160
|
+
);
|
|
42161
|
+
return [
|
|
42162
|
+
CURSOR_HIDE + renderSelectedRuleDivider(selected, severities, columns),
|
|
42163
|
+
...previewSectionLines,
|
|
42164
|
+
...paddingLines,
|
|
42165
|
+
CURSOR_TO_0 + renderDivider(columns),
|
|
42166
|
+
CURSOR_TO_0 + buildGroupLine(selected, columns),
|
|
42167
|
+
CURSOR_TO_0 + visible.fullLine,
|
|
42168
|
+
...wrappedDescription.map((line) => CURSOR_TO_0 + ansi(line, DIM)),
|
|
42169
|
+
CURSOR_TO_0 + renderDivider(columns, getControlsLegend(searchText)),
|
|
42170
|
+
""
|
|
42171
|
+
];
|
|
42172
|
+
}
|
|
42173
|
+
function buildState(entries2, startIndex, searchText, severities) {
|
|
42174
|
+
return gen2(function* () {
|
|
42175
|
+
const terminal = yield* Terminal;
|
|
42176
|
+
const columns = Math.max(yield* terminal.columns, 1);
|
|
42177
|
+
const filteredEntries = getFilteredEntries(entries2, searchText);
|
|
42178
|
+
const normalizedStartIndex = normalizeStartIndex(filteredEntries.length, startIndex);
|
|
42179
|
+
const renderedLines = buildFrame(filteredEntries, searchText, severities, normalizedStartIndex, columns);
|
|
42180
|
+
return {
|
|
42181
|
+
startIndex: normalizedStartIndex,
|
|
42182
|
+
searchText,
|
|
42183
|
+
severities,
|
|
42184
|
+
renderedColumns: columns,
|
|
42185
|
+
renderedLines
|
|
42186
|
+
};
|
|
42187
|
+
});
|
|
42188
|
+
}
|
|
42189
|
+
function renderSubmission(state, entries2) {
|
|
42190
|
+
return succeed6(
|
|
42191
|
+
CURSOR_TO_0 + JSON.stringify(
|
|
42192
|
+
Object.fromEntries(
|
|
42193
|
+
entries2.flatMap((entry) => {
|
|
42194
|
+
const severity = state.severities[entry.name];
|
|
42195
|
+
return severity !== void 0 && severity !== entry.defaultSeverity ? [[entry.name, severity]] : [];
|
|
42196
|
+
})
|
|
42197
|
+
)
|
|
42198
|
+
) + "\n"
|
|
42199
|
+
);
|
|
42200
|
+
}
|
|
42201
|
+
function handleProcess(entries2) {
|
|
42202
|
+
return (input, state) => {
|
|
42203
|
+
const filteredEntries = getFilteredEntries(entries2, state.searchText);
|
|
42204
|
+
switch (input.key.name) {
|
|
42205
|
+
case "backspace":
|
|
42206
|
+
return buildState(entries2, 0, state.searchText.slice(0, -1), state.severities).pipe(
|
|
42207
|
+
map6((nextState) => Action2.NextFrame({ state: nextState }))
|
|
42208
|
+
);
|
|
42209
|
+
case "left":
|
|
42210
|
+
if (filteredEntries.length === 0) return succeed6(Action2.Beep());
|
|
42211
|
+
return buildState(entries2, state.startIndex - 1, state.searchText, state.severities).pipe(
|
|
42212
|
+
map6((nextState) => Action2.NextFrame({ state: nextState }))
|
|
42213
|
+
);
|
|
42214
|
+
case "right":
|
|
42215
|
+
if (filteredEntries.length === 0) return succeed6(Action2.Beep());
|
|
42216
|
+
return buildState(entries2, state.startIndex + 1, state.searchText, state.severities).pipe(
|
|
42217
|
+
map6((nextState) => Action2.NextFrame({ state: nextState }))
|
|
42218
|
+
);
|
|
42219
|
+
case "up":
|
|
42220
|
+
case "down":
|
|
42221
|
+
if (filteredEntries.length === 0) return succeed6(Action2.Beep());
|
|
42222
|
+
return buildState(entries2, state.startIndex, state.searchText, {
|
|
42223
|
+
...state.severities,
|
|
42224
|
+
[filteredEntries[state.startIndex].name]: cycleSeverity(
|
|
42225
|
+
state.severities[filteredEntries[state.startIndex].name] ?? filteredEntries[state.startIndex].defaultSeverity,
|
|
42226
|
+
input.key.name === "up" ? "left" : "right"
|
|
42227
|
+
)
|
|
42228
|
+
}).pipe(map6((nextState) => Action2.NextFrame({ state: nextState })));
|
|
42229
|
+
case "enter":
|
|
42230
|
+
case "return":
|
|
42231
|
+
return succeed6(Action2.Submit({
|
|
42232
|
+
value: Object.fromEntries(
|
|
42233
|
+
entries2.flatMap((entry) => {
|
|
42234
|
+
const severity = state.severities[entry.name];
|
|
42235
|
+
return severity !== void 0 && severity !== entry.defaultSeverity ? [[entry.name, severity]] : [];
|
|
42236
|
+
})
|
|
42237
|
+
)
|
|
42238
|
+
}));
|
|
42239
|
+
default:
|
|
42240
|
+
if (!isPrintableInput(input)) return succeed6(Action2.Beep());
|
|
42241
|
+
return buildState(entries2, 0, state.searchText + input.input, state.severities).pipe(
|
|
42242
|
+
map6((nextState) => Action2.NextFrame({ state: nextState }))
|
|
42243
|
+
);
|
|
42244
|
+
}
|
|
42245
|
+
};
|
|
42246
|
+
}
|
|
42247
|
+
function getPromptEntries(diagnostics3) {
|
|
42248
|
+
const diagnosticsByName = new Map(diagnostics3.map((diagnostic) => [diagnostic.name, diagnostic]));
|
|
42249
|
+
return diagnostics3.flatMap((rule) => {
|
|
42250
|
+
const diagnostic = diagnosticsByName.get(rule.name);
|
|
42251
|
+
if (!diagnostic) {
|
|
42252
|
+
return [];
|
|
42253
|
+
}
|
|
42254
|
+
return [{
|
|
42255
|
+
name: rule.name,
|
|
42256
|
+
group: diagnostic.group,
|
|
42257
|
+
description: diagnostic.description,
|
|
42258
|
+
previewSourceText: diagnostic.preview.sourceText,
|
|
42259
|
+
previewDiagnostics: diagnostic.preview.diagnostics,
|
|
42260
|
+
defaultSeverity: diagnostic.defaultSeverity
|
|
42261
|
+
}];
|
|
42262
|
+
});
|
|
42263
|
+
}
|
|
42264
|
+
function createDiagnosticPrompt(diagnostics3, initialSeverities) {
|
|
42265
|
+
const entries2 = getPromptEntries(diagnostics3);
|
|
42266
|
+
return custom(buildState(entries2, 0, "", initialSeverities), {
|
|
42267
|
+
render: (state, action2) => {
|
|
42268
|
+
switch (action2._tag) {
|
|
42269
|
+
case "Beep":
|
|
42270
|
+
return succeed6(BEEP);
|
|
42271
|
+
case "NextFrame":
|
|
42272
|
+
return succeed6(action2.state.renderedLines.join("\n"));
|
|
42273
|
+
case "Submit":
|
|
42274
|
+
return renderSubmission(state, entries2);
|
|
42275
|
+
}
|
|
42276
|
+
},
|
|
42277
|
+
process: handleProcess(entries2),
|
|
42278
|
+
clear: (state) => gen2(function* () {
|
|
42279
|
+
const terminal = yield* Terminal;
|
|
42280
|
+
const columns = yield* terminal.columns;
|
|
42281
|
+
return eraseRenderedLines(state.renderedLines, columns);
|
|
42282
|
+
})
|
|
40709
42283
|
});
|
|
40710
42284
|
}
|
|
40711
42285
|
|