@effect/language-service 0.79.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 +70 -57
- package/cli.js +2022 -274
- package/cli.js.map +1 -1
- package/effect-lsp-patch-utils.js +246 -3
- package/effect-lsp-patch-utils.js.map +1 -1
- package/index.js +246 -3
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/transform.js +246 -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"
|
|
@@ -25747,13 +25747,13 @@ var package_default = {
|
|
|
25747
25747
|
],
|
|
25748
25748
|
scripts: {
|
|
25749
25749
|
build: "tsup",
|
|
25750
|
-
codegen: "node --import tsx ./scripts/
|
|
25750
|
+
codegen: "node --import tsx ./scripts/codegen.ts",
|
|
25751
25751
|
dev: "tsup --watch",
|
|
25752
25752
|
clean: "rimraf dist build .tsbuildinfo",
|
|
25753
25753
|
lint: "eslint src test",
|
|
25754
25754
|
"lint-fix": "eslint src test --fix",
|
|
25755
25755
|
check: "tsc -b tsconfig.json",
|
|
25756
|
-
"check:codegen": "node --import tsx ./scripts/
|
|
25756
|
+
"check:codegen": "node --import tsx ./scripts/codegen.ts --check",
|
|
25757
25757
|
circular: "madge --extensions ts --circular --no-color --no-spinner --warning src",
|
|
25758
25758
|
test: "vitest",
|
|
25759
25759
|
"test-update": "vitest --update",
|
|
@@ -27536,6 +27536,145 @@ var defaults = {
|
|
|
27536
27536
|
mermaidProvider: "mermaid.live",
|
|
27537
27537
|
skipDisabledOptimization: false
|
|
27538
27538
|
};
|
|
27539
|
+
var booleanSchema = (description, defaultValue) => ({
|
|
27540
|
+
type: "boolean",
|
|
27541
|
+
description,
|
|
27542
|
+
default: defaultValue
|
|
27543
|
+
});
|
|
27544
|
+
var stringArraySchema = (description, defaultValue) => ({
|
|
27545
|
+
type: "array",
|
|
27546
|
+
description,
|
|
27547
|
+
default: defaultValue,
|
|
27548
|
+
items: { type: "string" }
|
|
27549
|
+
});
|
|
27550
|
+
var stringEnumSchema = (description, values2, defaultValue) => ({
|
|
27551
|
+
type: "string",
|
|
27552
|
+
description,
|
|
27553
|
+
enum: values2,
|
|
27554
|
+
default: defaultValue
|
|
27555
|
+
});
|
|
27556
|
+
var languageServicePluginAdditionalPropertiesJsonSchema = {
|
|
27557
|
+
refactors: booleanSchema("Controls Effect refactors.", defaults.refactors),
|
|
27558
|
+
diagnostics: booleanSchema("Controls Effect diagnostics.", defaults.diagnostics),
|
|
27559
|
+
diagnosticsName: booleanSchema(
|
|
27560
|
+
"Controls whether to include the rule name in diagnostic messages.",
|
|
27561
|
+
defaults.diagnosticsName
|
|
27562
|
+
),
|
|
27563
|
+
missingDiagnosticNextLine: stringEnumSchema(
|
|
27564
|
+
"Controls the severity of warnings for unused @effect-diagnostics-next-line comments.",
|
|
27565
|
+
["off", "error", "warning", "message", "suggestion"],
|
|
27566
|
+
defaults.missingDiagnosticNextLine
|
|
27567
|
+
),
|
|
27568
|
+
includeSuggestionsInTsc: booleanSchema(
|
|
27569
|
+
"When patch mode is enabled, reports suggestion diagnostics as messages in TSC with a [suggestion] prefix.",
|
|
27570
|
+
defaults.includeSuggestionsInTsc
|
|
27571
|
+
),
|
|
27572
|
+
ignoreEffectWarningsInTscExitCode: booleanSchema(
|
|
27573
|
+
"When enabled, Effect warnings do not affect the patched tsc exit code.",
|
|
27574
|
+
defaults.ignoreEffectWarningsInTscExitCode
|
|
27575
|
+
),
|
|
27576
|
+
ignoreEffectErrorsInTscExitCode: booleanSchema(
|
|
27577
|
+
"When enabled, Effect errors do not affect the patched tsc exit code.",
|
|
27578
|
+
defaults.ignoreEffectErrorsInTscExitCode
|
|
27579
|
+
),
|
|
27580
|
+
ignoreEffectSuggestionsInTscExitCode: booleanSchema(
|
|
27581
|
+
"When enabled, Effect suggestions do not affect the patched tsc exit code.",
|
|
27582
|
+
defaults.ignoreEffectSuggestionsInTscExitCode
|
|
27583
|
+
),
|
|
27584
|
+
quickinfoEffectParameters: stringEnumSchema(
|
|
27585
|
+
"Controls when Effect quickinfo should include full type parameters.",
|
|
27586
|
+
["always", "never", "whentruncated"],
|
|
27587
|
+
defaults.quickinfoEffectParameters
|
|
27588
|
+
),
|
|
27589
|
+
quickinfo: booleanSchema("Controls Effect quickinfo.", defaults.quickinfo),
|
|
27590
|
+
quickinfoMaximumLength: {
|
|
27591
|
+
type: "number",
|
|
27592
|
+
description: "Controls the maximum quickinfo length. Use -1 to disable truncation.",
|
|
27593
|
+
default: defaults.quickinfoMaximumLength
|
|
27594
|
+
},
|
|
27595
|
+
keyPatterns: {
|
|
27596
|
+
type: "array",
|
|
27597
|
+
description: "Configures key patterns used for generated Effect service and error keys.",
|
|
27598
|
+
default: defaults.keyPatterns,
|
|
27599
|
+
items: {
|
|
27600
|
+
type: "object",
|
|
27601
|
+
properties: {
|
|
27602
|
+
target: stringEnumSchema("The key builder target.", ["service", "error", "custom"], "service"),
|
|
27603
|
+
pattern: stringEnumSchema(
|
|
27604
|
+
"The key generation pattern.",
|
|
27605
|
+
["package-identifier", "default", "default-hashed"],
|
|
27606
|
+
"default"
|
|
27607
|
+
),
|
|
27608
|
+
skipLeadingPath: stringArraySchema("Path prefixes to strip before generating keys.", ["src/"])
|
|
27609
|
+
}
|
|
27610
|
+
}
|
|
27611
|
+
},
|
|
27612
|
+
extendedKeyDetection: booleanSchema(
|
|
27613
|
+
"Enables extended heuristics when detecting key sources.",
|
|
27614
|
+
defaults.extendedKeyDetection
|
|
27615
|
+
),
|
|
27616
|
+
completions: booleanSchema("Controls Effect completions.", defaults.completions),
|
|
27617
|
+
goto: booleanSchema("Controls Effect goto references support.", defaults.goto),
|
|
27618
|
+
inlays: booleanSchema("Controls Effect inlay hints.", defaults.inlays),
|
|
27619
|
+
allowedDuplicatedPackages: stringArraySchema(
|
|
27620
|
+
"Package names that are allowed to duplicate Effect as a peer dependency.",
|
|
27621
|
+
defaults.allowedDuplicatedPackages
|
|
27622
|
+
),
|
|
27623
|
+
namespaceImportPackages: stringArraySchema(
|
|
27624
|
+
"Package names that should prefer namespace imports.",
|
|
27625
|
+
defaults.namespaceImportPackages
|
|
27626
|
+
),
|
|
27627
|
+
topLevelNamedReexports: stringEnumSchema(
|
|
27628
|
+
"For namespaceImportPackages, controls how top-level named re-exports are handled.",
|
|
27629
|
+
["ignore", "follow"],
|
|
27630
|
+
defaults.topLevelNamedReexports
|
|
27631
|
+
),
|
|
27632
|
+
barrelImportPackages: stringArraySchema(
|
|
27633
|
+
"Package names that should prefer imports from their top-level barrel file.",
|
|
27634
|
+
defaults.barrelImportPackages
|
|
27635
|
+
),
|
|
27636
|
+
importAliases: {
|
|
27637
|
+
type: "object",
|
|
27638
|
+
description: "Custom aliases to use for imported identifiers.",
|
|
27639
|
+
default: defaults.importAliases,
|
|
27640
|
+
additionalProperties: {
|
|
27641
|
+
type: "string"
|
|
27642
|
+
}
|
|
27643
|
+
},
|
|
27644
|
+
renames: booleanSchema("Controls Effect rename helpers.", defaults.renames),
|
|
27645
|
+
noExternal: booleanSchema(
|
|
27646
|
+
"Disables features that link to external websites.",
|
|
27647
|
+
defaults.noExternal
|
|
27648
|
+
),
|
|
27649
|
+
pipeableMinArgCount: {
|
|
27650
|
+
type: "number",
|
|
27651
|
+
description: "Minimum argument count required before pipeable suggestions are emitted.",
|
|
27652
|
+
default: defaults.pipeableMinArgCount
|
|
27653
|
+
},
|
|
27654
|
+
effectFn: {
|
|
27655
|
+
type: "array",
|
|
27656
|
+
description: "Configures which Effect.fn variants should be suggested.",
|
|
27657
|
+
default: defaults.effectFn,
|
|
27658
|
+
items: {
|
|
27659
|
+
type: "string",
|
|
27660
|
+
enum: ["untraced", "span", "suggested-span", "inferred-span", "no-span"]
|
|
27661
|
+
}
|
|
27662
|
+
},
|
|
27663
|
+
layerGraphFollowDepth: {
|
|
27664
|
+
type: "number",
|
|
27665
|
+
description: "Controls how deeply layer graph analysis follows dependencies.",
|
|
27666
|
+
default: defaults.layerGraphFollowDepth
|
|
27667
|
+
},
|
|
27668
|
+
mermaidProvider: {
|
|
27669
|
+
type: "string",
|
|
27670
|
+
description: "Controls which Mermaid renderer is used for layer graphs.",
|
|
27671
|
+
default: defaults.mermaidProvider
|
|
27672
|
+
},
|
|
27673
|
+
skipDisabledOptimization: booleanSchema(
|
|
27674
|
+
"When enabled, disabled diagnostics are still processed so comment-based overrides can be honored.",
|
|
27675
|
+
defaults.skipDisabledOptimization
|
|
27676
|
+
)
|
|
27677
|
+
};
|
|
27539
27678
|
function parseKeyPatterns(patterns) {
|
|
27540
27679
|
const result3 = [];
|
|
27541
27680
|
for (const entry of patterns) {
|
|
@@ -31661,6 +31800,7 @@ var anyUnknownInErrorContext = createDiagnostic({
|
|
|
31661
31800
|
name: "anyUnknownInErrorContext",
|
|
31662
31801
|
code: 28,
|
|
31663
31802
|
description: "Detects 'any' or 'unknown' types in Effect error or requirements channels",
|
|
31803
|
+
group: "correctness",
|
|
31664
31804
|
severity: "off",
|
|
31665
31805
|
fixable: false,
|
|
31666
31806
|
supportedEffect: ["v3", "v4"],
|
|
@@ -31766,6 +31906,7 @@ var catchAllToMapError = createDiagnostic({
|
|
|
31766
31906
|
name: "catchAllToMapError",
|
|
31767
31907
|
code: 39,
|
|
31768
31908
|
description: "Suggests using Effect.mapError instead of Effect.catchAll when the callback only wraps the error with Effect.fail",
|
|
31909
|
+
group: "style",
|
|
31769
31910
|
severity: "suggestion",
|
|
31770
31911
|
fixable: true,
|
|
31771
31912
|
supportedEffect: ["v3", "v4"],
|
|
@@ -31865,6 +32006,7 @@ var catchUnfailableEffect = createDiagnostic({
|
|
|
31865
32006
|
name: "catchUnfailableEffect",
|
|
31866
32007
|
code: 2,
|
|
31867
32008
|
description: "Warns when using error handling on Effects that never fail (error type is 'never')",
|
|
32009
|
+
group: "antipattern",
|
|
31868
32010
|
severity: "suggestion",
|
|
31869
32011
|
fixable: false,
|
|
31870
32012
|
supportedEffect: ["v3", "v4"],
|
|
@@ -31916,6 +32058,7 @@ var classSelfMismatch = createDiagnostic({
|
|
|
31916
32058
|
name: "classSelfMismatch",
|
|
31917
32059
|
code: 20,
|
|
31918
32060
|
description: "Ensures Self type parameter matches the class name in Service/Tag/Schema classes",
|
|
32061
|
+
group: "correctness",
|
|
31919
32062
|
severity: "error",
|
|
31920
32063
|
fixable: true,
|
|
31921
32064
|
supportedEffect: ["v3", "v4"],
|
|
@@ -32056,6 +32199,7 @@ var deterministicKeys = createDiagnostic({
|
|
|
32056
32199
|
name: "deterministicKeys",
|
|
32057
32200
|
code: 25,
|
|
32058
32201
|
description: "Enforces deterministic naming for service/tag/error identifiers based on class names",
|
|
32202
|
+
group: "style",
|
|
32059
32203
|
severity: "off",
|
|
32060
32204
|
fixable: true,
|
|
32061
32205
|
supportedEffect: ["v3", "v4"],
|
|
@@ -32175,6 +32319,7 @@ var duplicatePackage = createDiagnostic({
|
|
|
32175
32319
|
name: "duplicatePackage",
|
|
32176
32320
|
code: 6,
|
|
32177
32321
|
description: "Detects when multiple versions of the same Effect package are loaded",
|
|
32322
|
+
group: "correctness",
|
|
32178
32323
|
severity: "warning",
|
|
32179
32324
|
fixable: false,
|
|
32180
32325
|
supportedEffect: ["v3", "v4"],
|
|
@@ -32206,6 +32351,7 @@ var effectFnIife = createDiagnostic({
|
|
|
32206
32351
|
name: "effectFnIife",
|
|
32207
32352
|
code: 46,
|
|
32208
32353
|
description: "Effect.fn or Effect.fnUntraced is called as an IIFE (Immediately Invoked Function Expression). Use Effect.gen instead.",
|
|
32354
|
+
group: "antipattern",
|
|
32209
32355
|
severity: "warning",
|
|
32210
32356
|
fixable: true,
|
|
32211
32357
|
supportedEffect: ["v3", "v4"],
|
|
@@ -32310,6 +32456,7 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
32310
32456
|
name: "effectFnOpportunity",
|
|
32311
32457
|
code: 41,
|
|
32312
32458
|
description: "Suggests using Effect.fn for functions that returns an Effect",
|
|
32459
|
+
group: "style",
|
|
32313
32460
|
severity: "suggestion",
|
|
32314
32461
|
fixable: true,
|
|
32315
32462
|
supportedEffect: ["v3", "v4"],
|
|
@@ -32904,6 +33051,7 @@ var effectGenUsesAdapter = createDiagnostic({
|
|
|
32904
33051
|
name: "effectGenUsesAdapter",
|
|
32905
33052
|
code: 23,
|
|
32906
33053
|
description: "Warns when using the deprecated adapter parameter in Effect.gen",
|
|
33054
|
+
group: "antipattern",
|
|
32907
33055
|
severity: "warning",
|
|
32908
33056
|
fixable: false,
|
|
32909
33057
|
supportedEffect: ["v3", "v4"],
|
|
@@ -32944,6 +33092,7 @@ var effectInFailure = createDiagnostic({
|
|
|
32944
33092
|
name: "effectInFailure",
|
|
32945
33093
|
code: 49,
|
|
32946
33094
|
description: "Warns when an Effect is used inside an Effect failure channel",
|
|
33095
|
+
group: "antipattern",
|
|
32947
33096
|
severity: "warning",
|
|
32948
33097
|
fixable: false,
|
|
32949
33098
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33010,6 +33159,7 @@ var effectInVoidSuccess = createDiagnostic({
|
|
|
33010
33159
|
name: "effectInVoidSuccess",
|
|
33011
33160
|
code: 14,
|
|
33012
33161
|
description: "Detects nested Effects in void success channels that may cause unexecuted effects",
|
|
33162
|
+
group: "antipattern",
|
|
33013
33163
|
severity: "warning",
|
|
33014
33164
|
fixable: false,
|
|
33015
33165
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33061,6 +33211,7 @@ var effectMapVoid = createDiagnostic({
|
|
|
33061
33211
|
name: "effectMapVoid",
|
|
33062
33212
|
code: 40,
|
|
33063
33213
|
description: "Suggests using Effect.asVoid instead of Effect.map(() => void 0), Effect.map(() => undefined), or Effect.map(() => {})",
|
|
33214
|
+
group: "style",
|
|
33064
33215
|
severity: "suggestion",
|
|
33065
33216
|
fixable: true,
|
|
33066
33217
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33127,6 +33278,7 @@ var effectSucceedWithVoid = createDiagnostic({
|
|
|
33127
33278
|
name: "effectSucceedWithVoid",
|
|
33128
33279
|
code: 47,
|
|
33129
33280
|
description: "Suggests using Effect.void instead of Effect.succeed(undefined) or Effect.succeed(void 0)",
|
|
33281
|
+
group: "style",
|
|
33130
33282
|
severity: "suggestion",
|
|
33131
33283
|
fixable: true,
|
|
33132
33284
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33180,6 +33332,7 @@ var extendsNativeError = createDiagnostic({
|
|
|
33180
33332
|
name: "extendsNativeError",
|
|
33181
33333
|
code: 50,
|
|
33182
33334
|
description: "Warns when a class directly extends the native Error class",
|
|
33335
|
+
group: "effectNative",
|
|
33183
33336
|
severity: "off",
|
|
33184
33337
|
fixable: false,
|
|
33185
33338
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33232,6 +33385,7 @@ var floatingEffect = createDiagnostic({
|
|
|
33232
33385
|
name: "floatingEffect",
|
|
33233
33386
|
code: 3,
|
|
33234
33387
|
description: "Ensures Effects are yielded or assigned to variables, not left floating",
|
|
33388
|
+
group: "correctness",
|
|
33235
33389
|
severity: "error",
|
|
33236
33390
|
fixable: false,
|
|
33237
33391
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33285,6 +33439,7 @@ var genericEffectServices = createDiagnostic({
|
|
|
33285
33439
|
name: "genericEffectServices",
|
|
33286
33440
|
code: 10,
|
|
33287
33441
|
description: "Prevents services with type parameters that cannot be discriminated at runtime",
|
|
33442
|
+
group: "correctness",
|
|
33288
33443
|
severity: "warning",
|
|
33289
33444
|
fixable: false,
|
|
33290
33445
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33334,6 +33489,7 @@ var globalErrorInEffectCatch = createDiagnostic({
|
|
|
33334
33489
|
name: "globalErrorInEffectCatch",
|
|
33335
33490
|
code: 36,
|
|
33336
33491
|
description: "Warns when catch callbacks return global Error type instead of typed errors",
|
|
33492
|
+
group: "antipattern",
|
|
33337
33493
|
severity: "warning",
|
|
33338
33494
|
fixable: false,
|
|
33339
33495
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33396,6 +33552,7 @@ var globalErrorInEffectFailure = createDiagnostic({
|
|
|
33396
33552
|
name: "globalErrorInEffectFailure",
|
|
33397
33553
|
code: 35,
|
|
33398
33554
|
description: "Warns when the global Error type is used in an Effect failure channel",
|
|
33555
|
+
group: "antipattern",
|
|
33399
33556
|
severity: "warning",
|
|
33400
33557
|
fixable: false,
|
|
33401
33558
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33446,11 +33603,54 @@ var globalErrorInEffectFailure = createDiagnostic({
|
|
|
33446
33603
|
})
|
|
33447
33604
|
});
|
|
33448
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
|
+
|
|
33449
33648
|
// src/diagnostics/importFromBarrel.ts
|
|
33450
33649
|
var importFromBarrel = createDiagnostic({
|
|
33451
33650
|
name: "importFromBarrel",
|
|
33452
33651
|
code: 12,
|
|
33453
33652
|
description: "Suggests importing from specific module paths instead of barrel exports",
|
|
33653
|
+
group: "style",
|
|
33454
33654
|
severity: "off",
|
|
33455
33655
|
fixable: true,
|
|
33456
33656
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33593,6 +33793,7 @@ var instanceOfSchema = createDiagnostic({
|
|
|
33593
33793
|
name: "instanceOfSchema",
|
|
33594
33794
|
code: 45,
|
|
33595
33795
|
description: "Suggests using Schema.is instead of instanceof for Effect Schema types",
|
|
33796
|
+
group: "effectNative",
|
|
33596
33797
|
severity: "off",
|
|
33597
33798
|
fixable: true,
|
|
33598
33799
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33658,6 +33859,7 @@ var layerMergeAllWithDependencies = createDiagnostic({
|
|
|
33658
33859
|
name: "layerMergeAllWithDependencies",
|
|
33659
33860
|
code: 37,
|
|
33660
33861
|
description: "Detects interdependencies in Layer.mergeAll calls where one layer provides a service that another layer requires",
|
|
33862
|
+
group: "antipattern",
|
|
33661
33863
|
severity: "warning",
|
|
33662
33864
|
fixable: true,
|
|
33663
33865
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33773,6 +33975,7 @@ var leakingRequirements = createDiagnostic({
|
|
|
33773
33975
|
name: "leakingRequirements",
|
|
33774
33976
|
code: 8,
|
|
33775
33977
|
description: "Detects implementation services leaked in service methods",
|
|
33978
|
+
group: "antipattern",
|
|
33776
33979
|
severity: "suggestion",
|
|
33777
33980
|
fixable: false,
|
|
33778
33981
|
supportedEffect: ["v3", "v4"],
|
|
@@ -33929,6 +34132,7 @@ var missedPipeableOpportunity = createDiagnostic({
|
|
|
33929
34132
|
name: "missedPipeableOpportunity",
|
|
33930
34133
|
code: 26,
|
|
33931
34134
|
description: "Enforces the use of pipeable style for nested function calls",
|
|
34135
|
+
group: "style",
|
|
33932
34136
|
severity: "off",
|
|
33933
34137
|
fixable: true,
|
|
33934
34138
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34111,6 +34315,7 @@ var missingEffectContext = createDiagnostic({
|
|
|
34111
34315
|
name: "missingEffectContext",
|
|
34112
34316
|
code: 1,
|
|
34113
34317
|
description: "Reports missing service requirements in Effect context channel",
|
|
34318
|
+
group: "correctness",
|
|
34114
34319
|
severity: "error",
|
|
34115
34320
|
fixable: false,
|
|
34116
34321
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34162,6 +34367,7 @@ var missingEffectError = createDiagnostic({
|
|
|
34162
34367
|
name: "missingEffectError",
|
|
34163
34368
|
code: 1,
|
|
34164
34369
|
description: "Reports missing error types in Effect error channel",
|
|
34370
|
+
group: "correctness",
|
|
34165
34371
|
severity: "error",
|
|
34166
34372
|
fixable: true,
|
|
34167
34373
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34305,6 +34511,7 @@ var missingEffectServiceDependency = createDiagnostic({
|
|
|
34305
34511
|
name: "missingEffectServiceDependency",
|
|
34306
34512
|
code: 22,
|
|
34307
34513
|
description: "Checks that Effect.Service dependencies satisfy all required layer inputs",
|
|
34514
|
+
group: "style",
|
|
34308
34515
|
severity: "off",
|
|
34309
34516
|
fixable: false,
|
|
34310
34517
|
supportedEffect: ["v3"],
|
|
@@ -34401,6 +34608,7 @@ var missingLayerContext = createDiagnostic({
|
|
|
34401
34608
|
name: "missingLayerContext",
|
|
34402
34609
|
code: 38,
|
|
34403
34610
|
description: "Reports missing service requirements in Layer context channel",
|
|
34611
|
+
group: "correctness",
|
|
34404
34612
|
severity: "error",
|
|
34405
34613
|
fixable: false,
|
|
34406
34614
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34452,6 +34660,7 @@ var missingReturnYieldStar = createDiagnostic({
|
|
|
34452
34660
|
name: "missingReturnYieldStar",
|
|
34453
34661
|
code: 7,
|
|
34454
34662
|
description: "Suggests using 'return yield*' for Effects with never success for better type narrowing",
|
|
34663
|
+
group: "correctness",
|
|
34455
34664
|
severity: "error",
|
|
34456
34665
|
fixable: true,
|
|
34457
34666
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34504,6 +34713,7 @@ var missingStarInYieldEffectGen = createDiagnostic({
|
|
|
34504
34713
|
name: "missingStarInYieldEffectGen",
|
|
34505
34714
|
code: 4,
|
|
34506
34715
|
description: "Enforces using 'yield*' instead of 'yield' when yielding Effects in generators",
|
|
34716
|
+
group: "correctness",
|
|
34507
34717
|
severity: "error",
|
|
34508
34718
|
fixable: true,
|
|
34509
34719
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34581,6 +34791,7 @@ var multipleEffectProvide = createDiagnostic({
|
|
|
34581
34791
|
name: "multipleEffectProvide",
|
|
34582
34792
|
code: 18,
|
|
34583
34793
|
description: "Warns against chaining Effect.provide calls which can cause service lifecycle issues",
|
|
34794
|
+
group: "antipattern",
|
|
34584
34795
|
severity: "warning",
|
|
34585
34796
|
fixable: true,
|
|
34586
34797
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34683,7 +34894,11 @@ var moduleAlternativesV3 = /* @__PURE__ */ new Map([
|
|
|
34683
34894
|
["path/win32", { alternative: "Path", module: "path", package: "@effect/platform" }],
|
|
34684
34895
|
["node:path/win32", { alternative: "Path", module: "path", package: "@effect/platform" }],
|
|
34685
34896
|
["child_process", { alternative: "CommandExecutor", module: "child_process", package: "@effect/platform" }],
|
|
34686
|
-
["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" }]
|
|
34687
34902
|
]);
|
|
34688
34903
|
var moduleAlternativesV4 = /* @__PURE__ */ new Map([
|
|
34689
34904
|
["fs", { alternative: "FileSystem", module: "fs", package: "effect" }],
|
|
@@ -34697,12 +34912,17 @@ var moduleAlternativesV4 = /* @__PURE__ */ new Map([
|
|
|
34697
34912
|
["path/win32", { alternative: "Path", module: "path", package: "effect" }],
|
|
34698
34913
|
["node:path/win32", { alternative: "Path", module: "path", package: "effect" }],
|
|
34699
34914
|
["child_process", { alternative: "ChildProcess", module: "child_process", package: "effect" }],
|
|
34700
|
-
["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" }]
|
|
34701
34920
|
]);
|
|
34702
34921
|
var nodeBuiltinImport = createDiagnostic({
|
|
34703
34922
|
name: "nodeBuiltinImport",
|
|
34704
34923
|
code: 52,
|
|
34705
34924
|
description: "Warns when importing Node.js built-in modules that have Effect-native counterparts",
|
|
34925
|
+
group: "effectNative",
|
|
34706
34926
|
severity: "off",
|
|
34707
34927
|
fixable: false,
|
|
34708
34928
|
supportedEffect: ["v3", "v4"],
|
|
@@ -34746,6 +34966,7 @@ var nonObjectEffectServiceType = createDiagnostic({
|
|
|
34746
34966
|
name: "nonObjectEffectServiceType",
|
|
34747
34967
|
code: 24,
|
|
34748
34968
|
description: "Ensures Effect.Service types are objects, not primitives",
|
|
34969
|
+
group: "correctness",
|
|
34749
34970
|
severity: "error",
|
|
34750
34971
|
fixable: false,
|
|
34751
34972
|
supportedEffect: ["v3"],
|
|
@@ -35530,6 +35751,7 @@ var outdatedApi = createDiagnostic({
|
|
|
35530
35751
|
name: "outdatedApi",
|
|
35531
35752
|
code: 48,
|
|
35532
35753
|
description: "Detects usage of APIs that have been removed or renamed in Effect v4",
|
|
35754
|
+
group: "correctness",
|
|
35533
35755
|
severity: "warning",
|
|
35534
35756
|
fixable: false,
|
|
35535
35757
|
supportedEffect: ["v4"],
|
|
@@ -35599,6 +35821,7 @@ var outdatedEffectCodegen = createDiagnostic({
|
|
|
35599
35821
|
name: "outdatedEffectCodegen",
|
|
35600
35822
|
code: 19,
|
|
35601
35823
|
description: "Detects when generated code is outdated and needs to be regenerated",
|
|
35824
|
+
group: "correctness",
|
|
35602
35825
|
severity: "warning",
|
|
35603
35826
|
fixable: true,
|
|
35604
35827
|
supportedEffect: ["v3", "v4"],
|
|
@@ -35647,6 +35870,7 @@ var overriddenSchemaConstructor = createDiagnostic({
|
|
|
35647
35870
|
name: "overriddenSchemaConstructor",
|
|
35648
35871
|
code: 30,
|
|
35649
35872
|
description: "Prevents overriding constructors in Schema classes which breaks decoding behavior",
|
|
35873
|
+
group: "correctness",
|
|
35650
35874
|
severity: "error",
|
|
35651
35875
|
fixable: true,
|
|
35652
35876
|
supportedEffect: ["v3", "v4"],
|
|
@@ -35786,6 +36010,7 @@ var preferSchemaOverJson = createDiagnostic({
|
|
|
35786
36010
|
name: "preferSchemaOverJson",
|
|
35787
36011
|
code: 44,
|
|
35788
36012
|
description: "Suggests using Effect Schema for JSON operations instead of JSON.parse/JSON.stringify which may throw",
|
|
36013
|
+
group: "effectNative",
|
|
35789
36014
|
severity: "suggestion",
|
|
35790
36015
|
fixable: false,
|
|
35791
36016
|
supportedEffect: ["v3", "v4"],
|
|
@@ -35898,6 +36123,7 @@ var redundantSchemaTagIdentifier = createDiagnostic({
|
|
|
35898
36123
|
name: "redundantSchemaTagIdentifier",
|
|
35899
36124
|
code: 42,
|
|
35900
36125
|
description: "Suggests removing redundant identifier argument when it equals the tag value in Schema.TaggedClass/TaggedError/TaggedRequest",
|
|
36126
|
+
group: "style",
|
|
35901
36127
|
severity: "suggestion",
|
|
35902
36128
|
fixable: true,
|
|
35903
36129
|
supportedEffect: ["v3", "v4"],
|
|
@@ -35950,6 +36176,7 @@ var returnEffectInGen = createDiagnostic({
|
|
|
35950
36176
|
name: "returnEffectInGen",
|
|
35951
36177
|
code: 11,
|
|
35952
36178
|
description: "Warns when returning an Effect in a generator causes nested Effect<Effect<...>>",
|
|
36179
|
+
group: "antipattern",
|
|
35953
36180
|
severity: "suggestion",
|
|
35954
36181
|
fixable: true,
|
|
35955
36182
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36021,6 +36248,7 @@ var runEffectInsideEffect = createDiagnostic({
|
|
|
36021
36248
|
name: "runEffectInsideEffect",
|
|
36022
36249
|
code: 32,
|
|
36023
36250
|
description: "Suggests using Runtime methods instead of Effect.run* inside Effect contexts",
|
|
36251
|
+
group: "antipattern",
|
|
36024
36252
|
severity: "suggestion",
|
|
36025
36253
|
fixable: true,
|
|
36026
36254
|
supportedEffect: ["v3"],
|
|
@@ -36147,6 +36375,7 @@ var schemaStructWithTag = createDiagnostic({
|
|
|
36147
36375
|
name: "schemaStructWithTag",
|
|
36148
36376
|
code: 34,
|
|
36149
36377
|
description: "Suggests using Schema.TaggedStruct instead of Schema.Struct with _tag field",
|
|
36378
|
+
group: "style",
|
|
36150
36379
|
severity: "suggestion",
|
|
36151
36380
|
fixable: true,
|
|
36152
36381
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36241,9 +36470,10 @@ var schemaSyncInEffect = createDiagnostic({
|
|
|
36241
36470
|
name: "schemaSyncInEffect",
|
|
36242
36471
|
code: 43,
|
|
36243
36472
|
description: "Suggests using Effect-based Schema methods instead of sync methods inside Effect generators",
|
|
36473
|
+
group: "antipattern",
|
|
36244
36474
|
severity: "suggestion",
|
|
36245
36475
|
fixable: false,
|
|
36246
|
-
supportedEffect: ["v3"
|
|
36476
|
+
supportedEffect: ["v3"],
|
|
36247
36477
|
apply: fn3("schemaSyncInEffect.apply")(function* (sourceFile, report) {
|
|
36248
36478
|
const ts = yield* service2(TypeScriptApi);
|
|
36249
36479
|
const typeParser = yield* service2(TypeParser);
|
|
@@ -36292,6 +36522,7 @@ var schemaUnionOfLiterals = createDiagnostic({
|
|
|
36292
36522
|
name: "schemaUnionOfLiterals",
|
|
36293
36523
|
code: 33,
|
|
36294
36524
|
description: "Simplifies Schema.Union of multiple Schema.Literal calls into single Schema.Literal",
|
|
36525
|
+
group: "style",
|
|
36295
36526
|
severity: "off",
|
|
36296
36527
|
fixable: true,
|
|
36297
36528
|
supportedEffect: ["v3"],
|
|
@@ -36369,6 +36600,7 @@ var scopeInLayerEffect = createDiagnostic({
|
|
|
36369
36600
|
name: "scopeInLayerEffect",
|
|
36370
36601
|
code: 13,
|
|
36371
36602
|
description: "Suggests using Layer.scoped instead of Layer.effect when Scope is in requirements",
|
|
36603
|
+
group: "antipattern",
|
|
36372
36604
|
severity: "warning",
|
|
36373
36605
|
fixable: true,
|
|
36374
36606
|
supportedEffect: ["v3"],
|
|
@@ -36466,6 +36698,7 @@ var serviceNotAsClass = createDiagnostic({
|
|
|
36466
36698
|
name: "serviceNotAsClass",
|
|
36467
36699
|
code: 51,
|
|
36468
36700
|
description: "Warns when ServiceMap.Service is used as a variable instead of a class declaration",
|
|
36701
|
+
group: "style",
|
|
36469
36702
|
severity: "off",
|
|
36470
36703
|
fixable: true,
|
|
36471
36704
|
supportedEffect: ["v4"],
|
|
@@ -36543,6 +36776,7 @@ var strictBooleanExpressions = createDiagnostic({
|
|
|
36543
36776
|
name: "strictBooleanExpressions",
|
|
36544
36777
|
code: 17,
|
|
36545
36778
|
description: "Enforces boolean types in conditional expressions for type safety",
|
|
36779
|
+
group: "style",
|
|
36546
36780
|
severity: "off",
|
|
36547
36781
|
fixable: false,
|
|
36548
36782
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36616,6 +36850,7 @@ var strictEffectProvide = createDiagnostic({
|
|
|
36616
36850
|
name: "strictEffectProvide",
|
|
36617
36851
|
code: 27,
|
|
36618
36852
|
description: "Warns when using Effect.provide with layers outside of application entry points",
|
|
36853
|
+
group: "antipattern",
|
|
36619
36854
|
severity: "off",
|
|
36620
36855
|
fixable: false,
|
|
36621
36856
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36669,6 +36904,7 @@ var tryCatchInEffectGen = createDiagnostic({
|
|
|
36669
36904
|
name: "tryCatchInEffectGen",
|
|
36670
36905
|
code: 15,
|
|
36671
36906
|
description: "Discourages try/catch in Effect generators in favor of Effect error handling",
|
|
36907
|
+
group: "antipattern",
|
|
36672
36908
|
severity: "suggestion",
|
|
36673
36909
|
fixable: false,
|
|
36674
36910
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36728,6 +36964,7 @@ var unknownInEffectCatch = createDiagnostic({
|
|
|
36728
36964
|
name: "unknownInEffectCatch",
|
|
36729
36965
|
code: 31,
|
|
36730
36966
|
description: "Warns when catch callbacks return unknown instead of typed errors",
|
|
36967
|
+
group: "antipattern",
|
|
36731
36968
|
severity: "warning",
|
|
36732
36969
|
fixable: false,
|
|
36733
36970
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36791,6 +37028,7 @@ var unnecessaryEffectGen = createDiagnostic({
|
|
|
36791
37028
|
name: "unnecessaryEffectGen",
|
|
36792
37029
|
code: 5,
|
|
36793
37030
|
description: "Suggests removing Effect.gen when it contains only a single return statement",
|
|
37031
|
+
group: "style",
|
|
36794
37032
|
severity: "suggestion",
|
|
36795
37033
|
fixable: true,
|
|
36796
37034
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36837,6 +37075,7 @@ var unnecessaryFailYieldableError = createDiagnostic({
|
|
|
36837
37075
|
name: "unnecessaryFailYieldableError",
|
|
36838
37076
|
code: 29,
|
|
36839
37077
|
description: "Suggests yielding yieldable errors directly instead of wrapping with Effect.fail",
|
|
37078
|
+
group: "style",
|
|
36840
37079
|
severity: "suggestion",
|
|
36841
37080
|
fixable: true,
|
|
36842
37081
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36898,6 +37137,7 @@ var unnecessaryPipe = createDiagnostic({
|
|
|
36898
37137
|
name: "unnecessaryPipe",
|
|
36899
37138
|
code: 9,
|
|
36900
37139
|
description: "Removes pipe calls with no arguments",
|
|
37140
|
+
group: "style",
|
|
36901
37141
|
severity: "suggestion",
|
|
36902
37142
|
fixable: true,
|
|
36903
37143
|
supportedEffect: ["v3", "v4"],
|
|
@@ -36946,6 +37186,7 @@ var unnecessaryPipeChain = createDiagnostic({
|
|
|
36946
37186
|
name: "unnecessaryPipeChain",
|
|
36947
37187
|
code: 16,
|
|
36948
37188
|
description: "Simplifies chained pipe calls into a single pipe call",
|
|
37189
|
+
group: "style",
|
|
36949
37190
|
severity: "suggestion",
|
|
36950
37191
|
fixable: true,
|
|
36951
37192
|
supportedEffect: ["v3", "v4"],
|
|
@@ -37023,6 +37264,7 @@ var unsupportedServiceAccessors = createDiagnostic({
|
|
|
37023
37264
|
name: "unsupportedServiceAccessors",
|
|
37024
37265
|
code: 21,
|
|
37025
37266
|
description: "Warns about service accessors that need codegen due to generic/complex signatures",
|
|
37267
|
+
group: "correctness",
|
|
37026
37268
|
severity: "warning",
|
|
37027
37269
|
fixable: true,
|
|
37028
37270
|
supportedEffect: ["v3", "v4"],
|
|
@@ -37100,6 +37342,7 @@ var diagnostics = [
|
|
|
37100
37342
|
leakingRequirements,
|
|
37101
37343
|
unnecessaryPipe,
|
|
37102
37344
|
genericEffectServices,
|
|
37345
|
+
globalFetch,
|
|
37103
37346
|
returnEffectInGen,
|
|
37104
37347
|
tryCatchInEffectGen,
|
|
37105
37348
|
importFromBarrel,
|
|
@@ -37145,6 +37388,11 @@ var DiagnosticsFoundError = class extends TaggedError2("DiagnosticsFoundError")
|
|
|
37145
37388
|
return `Found ${this.errorsCount} errors, ${this.warningsCount} warnings and ${this.messagesCount} messages.`;
|
|
37146
37389
|
}
|
|
37147
37390
|
};
|
|
37391
|
+
var InvalidLspConfigError = class extends TaggedError2("InvalidLspConfigError") {
|
|
37392
|
+
get message() {
|
|
37393
|
+
return `Invalid JSON lsp config: ${this.lspconfig}`;
|
|
37394
|
+
}
|
|
37395
|
+
};
|
|
37148
37396
|
var categoryToSeverity = (category, tsInstance) => {
|
|
37149
37397
|
switch (category) {
|
|
37150
37398
|
case tsInstance.DiagnosticCategory.Error:
|
|
@@ -37340,9 +37588,15 @@ var diagnostics2 = Command_exports.make(
|
|
|
37340
37588
|
progress: Flag_exports.boolean("progress").pipe(
|
|
37341
37589
|
Flag_exports.withDefault(false),
|
|
37342
37590
|
Flag_exports.withDescription("Show progress as files are checked (outputs to stderr)")
|
|
37591
|
+
),
|
|
37592
|
+
lspconfig: Flag_exports.string("lspconfig").pipe(
|
|
37593
|
+
Flag_exports.optional,
|
|
37594
|
+
Flag_exports.withDescription(
|
|
37595
|
+
`An optional inline JSON lsp config that replaces the current project lsp config. e.g. '{ "effectFn": ["untraced"] }'`
|
|
37596
|
+
)
|
|
37343
37597
|
)
|
|
37344
37598
|
},
|
|
37345
|
-
fn2("diagnostics")(function* ({ file: file4, format: format3, progress, project: project2, severity, strict }) {
|
|
37599
|
+
fn2("diagnostics")(function* ({ file: file4, format: format3, lspconfig, progress, project: project2, severity, strict }) {
|
|
37346
37600
|
const path4 = yield* Path;
|
|
37347
37601
|
const severityFilter = parseSeverityFilter(severity);
|
|
37348
37602
|
const state = {
|
|
@@ -37406,7 +37660,14 @@ var diagnostics2 = Command_exports.make(
|
|
|
37406
37660
|
if (!program) continue;
|
|
37407
37661
|
const sourceFile = program.getSourceFile(filePath);
|
|
37408
37662
|
if (!sourceFile) continue;
|
|
37409
|
-
|
|
37663
|
+
let pluginConfig = extractEffectLspOptions(program.getCompilerOptions());
|
|
37664
|
+
if (isSome2(lspconfig)) {
|
|
37665
|
+
try {
|
|
37666
|
+
pluginConfig = { name: "@effect/language-service", ...JSON.parse(lspconfig.value) };
|
|
37667
|
+
} catch {
|
|
37668
|
+
return yield* new InvalidLspConfigError({ lspconfig: lspconfig.value });
|
|
37669
|
+
}
|
|
37670
|
+
}
|
|
37410
37671
|
if (!pluginConfig) continue;
|
|
37411
37672
|
const rawResults = pipe(
|
|
37412
37673
|
getSemanticDiagnosticsWithCodeFixes(diagnostics, sourceFile),
|
|
@@ -38044,13 +38305,6 @@ var GREEN = "\x1B[0;32m";
|
|
|
38044
38305
|
var YELLOW = "\x1B[0;33m";
|
|
38045
38306
|
var BLUE = "\x1B[0;34m";
|
|
38046
38307
|
var CYAN = "\x1B[0;36m";
|
|
38047
|
-
var WHITE = "\x1B[0;37m";
|
|
38048
|
-
var CYAN_BRIGHT = "\x1B[0;96m";
|
|
38049
|
-
var BG_BLACK_BRIGHT = "\x1B[0;100m";
|
|
38050
|
-
var BG_RED = "\x1B[41m";
|
|
38051
|
-
var BG_YELLOW = "\x1B[43m";
|
|
38052
|
-
var BG_BLUE = "\x1B[0;44m";
|
|
38053
|
-
var BG_CYAN = "\x1B[0;46m";
|
|
38054
38308
|
var ansi = (text2, code) => `${code}${text2}${RESET}`;
|
|
38055
38309
|
var ERASE_LINE = "\x1B[2K";
|
|
38056
38310
|
var CURSOR_LEFT = "\r";
|
|
@@ -38058,6 +38312,11 @@ var CURSOR_HIDE = "\x1B[?25l";
|
|
|
38058
38312
|
var CURSOR_SHOW = "\x1B[?25h";
|
|
38059
38313
|
var CURSOR_TO_0 = "\x1B[G";
|
|
38060
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;
|
|
38061
38320
|
|
|
38062
38321
|
// src/cli/overview.ts
|
|
38063
38322
|
var import_project_service3 = __toESM(require_dist3());
|
|
@@ -39690,6 +39949,7 @@ var assess = (input) => gen2(function* () {
|
|
|
39690
39949
|
});
|
|
39691
39950
|
|
|
39692
39951
|
// src/cli/setup/changes.ts
|
|
39952
|
+
var TSCONFIG_SCHEMA_URL = "https://raw.githubusercontent.com/Effect-TS/language-service/refs/heads/main/schema.json";
|
|
39693
39953
|
function emptyFileChangesResult() {
|
|
39694
39954
|
return {
|
|
39695
39955
|
codeActions: [],
|
|
@@ -40020,9 +40280,100 @@ var computeTsConfigChanges = (current, target, lspVersion) => {
|
|
|
40020
40280
|
if (!rootObj) {
|
|
40021
40281
|
return emptyFileChangesResult();
|
|
40022
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
|
+
);
|
|
40023
40314
|
const compilerOptionsProperty = findPropertyInObject(ts, rootObj, "compilerOptions");
|
|
40024
40315
|
if (!compilerOptionsProperty) {
|
|
40025
|
-
|
|
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
|
+
};
|
|
40026
40377
|
}
|
|
40027
40378
|
if (!ts.isObjectLiteralExpression(compilerOptionsProperty.initializer)) {
|
|
40028
40379
|
return emptyFileChangesResult();
|
|
@@ -40036,8 +40387,13 @@ var computeTsConfigChanges = (current, target, lspVersion) => {
|
|
|
40036
40387
|
const fileChanges = textChanges.ChangeTracker.with(
|
|
40037
40388
|
{ host, formatContext, preferences },
|
|
40038
40389
|
(tracker) => {
|
|
40390
|
+
const schemaProperty = findPropertyInObject(ts, rootObj, "$schema");
|
|
40039
40391
|
const pluginsProperty = findPropertyInObject(ts, compilerOptions, "plugins");
|
|
40040
40392
|
if (isNone2(lspVersion)) {
|
|
40393
|
+
if (schemaProperty) {
|
|
40394
|
+
descriptions.push("Remove $schema from tsconfig");
|
|
40395
|
+
deleteNodeFromList(tracker, current.sourceFile, rootObj.properties, schemaProperty);
|
|
40396
|
+
}
|
|
40041
40397
|
if (pluginsProperty && ts.isArrayLiteralExpression(pluginsProperty.initializer)) {
|
|
40042
40398
|
const pluginsArray = pluginsProperty.initializer;
|
|
40043
40399
|
const lspPluginElement = pluginsArray.elements.find((element) => {
|
|
@@ -40055,33 +40411,13 @@ var computeTsConfigChanges = (current, target, lspVersion) => {
|
|
|
40055
40411
|
}
|
|
40056
40412
|
}
|
|
40057
40413
|
} else {
|
|
40058
|
-
|
|
40059
|
-
|
|
40060
|
-
|
|
40061
|
-
|
|
40062
|
-
);
|
|
40063
|
-
|
|
40064
|
-
|
|
40065
|
-
return ts.factory.createObjectLiteralExpression([nameProperty], false);
|
|
40066
|
-
},
|
|
40067
|
-
onSome: (sevs) => {
|
|
40068
|
-
const severityProperties = Object.entries(sevs).map(
|
|
40069
|
-
([name, severity]) => ts.factory.createPropertyAssignment(
|
|
40070
|
-
ts.factory.createStringLiteral(name),
|
|
40071
|
-
ts.factory.createStringLiteral(severity)
|
|
40072
|
-
)
|
|
40073
|
-
);
|
|
40074
|
-
const diagnosticSeverityProperty = ts.factory.createPropertyAssignment(
|
|
40075
|
-
ts.factory.createStringLiteral("diagnosticSeverity"),
|
|
40076
|
-
ts.factory.createObjectLiteralExpression(severityProperties, true)
|
|
40077
|
-
);
|
|
40078
|
-
return ts.factory.createObjectLiteralExpression(
|
|
40079
|
-
[nameProperty, diagnosticSeverityProperty],
|
|
40080
|
-
true
|
|
40081
|
-
);
|
|
40082
|
-
}
|
|
40083
|
-
});
|
|
40084
|
-
};
|
|
40414
|
+
if (!schemaProperty) {
|
|
40415
|
+
descriptions.push("Add $schema to tsconfig");
|
|
40416
|
+
insertNodeAtEndOfList(tracker, current.sourceFile, rootObj.properties, schemaPropertyAssignment);
|
|
40417
|
+
} else if (!ts.isStringLiteral(schemaProperty.initializer) || schemaProperty.initializer.text !== TSCONFIG_SCHEMA_URL) {
|
|
40418
|
+
descriptions.push("Update $schema in tsconfig");
|
|
40419
|
+
tracker.replaceNode(current.sourceFile, schemaProperty.initializer, schemaPropertyAssignment.initializer);
|
|
40420
|
+
}
|
|
40085
40421
|
const pluginObject = buildPluginObject(target.diagnosticSeverities);
|
|
40086
40422
|
if (!pluginsProperty) {
|
|
40087
40423
|
descriptions.push("Add plugins array with @effect/language-service plugin");
|
|
@@ -40294,244 +40630,1656 @@ var FileReadError = class extends TaggedError2("FileReadError") {
|
|
|
40294
40630
|
}
|
|
40295
40631
|
};
|
|
40296
40632
|
|
|
40297
|
-
// src/
|
|
40298
|
-
|
|
40299
|
-
|
|
40300
|
-
|
|
40301
|
-
|
|
40302
|
-
|
|
40303
|
-
|
|
40304
|
-
|
|
40305
|
-
|
|
40306
|
-
|
|
40307
|
-
|
|
40308
|
-
|
|
40309
|
-
|
|
40310
|
-
|
|
40311
|
-
|
|
40312
|
-
|
|
40313
|
-
|
|
40314
|
-
}
|
|
40315
|
-
|
|
40316
|
-
|
|
40317
|
-
|
|
40318
|
-
|
|
40319
|
-
warning: "warn",
|
|
40320
|
-
error: "err"
|
|
40321
|
-
};
|
|
40322
|
-
var MAX_SEVERITY_LENGTH = Object.values(shortNames).reduce((max2, name) => Math.max(max2, name.length), 0);
|
|
40323
|
-
function getSeverityShortName(severity) {
|
|
40324
|
-
return shortNames[severity] ?? "???";
|
|
40325
|
-
}
|
|
40326
|
-
|
|
40327
|
-
// src/cli/setup/diagnostic-prompt.ts
|
|
40328
|
-
function eraseLines2(count) {
|
|
40329
|
-
let result3 = "";
|
|
40330
|
-
for (let i = 0; i < count; i++) {
|
|
40331
|
-
if (i > 0) result3 += "\x1B[1A";
|
|
40332
|
-
result3 += ERASE_LINE;
|
|
40333
|
-
}
|
|
40334
|
-
if (count > 0) result3 += CURSOR_LEFT;
|
|
40335
|
-
return result3;
|
|
40336
|
-
}
|
|
40337
|
-
var Action2 = taggedEnum();
|
|
40338
|
-
var NEWLINE_REGEX = /\r?\n/;
|
|
40339
|
-
function eraseText2(text2, columns) {
|
|
40340
|
-
if (columns === 0) {
|
|
40341
|
-
return ERASE_LINE + CURSOR_TO_0;
|
|
40342
|
-
}
|
|
40343
|
-
let rows = 0;
|
|
40344
|
-
const lines2 = text2.split(/\r?\n/);
|
|
40345
|
-
for (const line of lines2) {
|
|
40346
|
-
rows += 1 + Math.floor(Math.max(line.length - 1, 0) / columns);
|
|
40347
|
-
}
|
|
40348
|
-
return eraseLines2(rows);
|
|
40349
|
-
}
|
|
40350
|
-
function entriesToDisplay2(cursor, total, maxVisible) {
|
|
40351
|
-
const max2 = maxVisible === void 0 ? total : maxVisible;
|
|
40352
|
-
let startIndex = Math.min(total - max2, cursor - Math.floor(max2 / 2));
|
|
40353
|
-
if (startIndex < 0) {
|
|
40354
|
-
startIndex = 0;
|
|
40355
|
-
}
|
|
40356
|
-
const endIndex = Math.min(startIndex + max2, total);
|
|
40357
|
-
return { startIndex, endIndex };
|
|
40358
|
-
}
|
|
40359
|
-
var defaultFigures2 = {
|
|
40360
|
-
arrowUp: "\u2191",
|
|
40361
|
-
arrowDown: "\u2193",
|
|
40362
|
-
tick: "\u2714",
|
|
40363
|
-
pointerSmall: "\u203A"
|
|
40364
|
-
};
|
|
40365
|
-
var figuresValue = defaultFigures2;
|
|
40366
|
-
function getSeverityStyle(severity) {
|
|
40367
|
-
const styles = {
|
|
40368
|
-
off: WHITE + BG_BLACK_BRIGHT,
|
|
40369
|
-
suggestion: WHITE + BG_CYAN,
|
|
40370
|
-
message: WHITE + BG_BLUE,
|
|
40371
|
-
warning: WHITE + BG_YELLOW,
|
|
40372
|
-
error: WHITE + BG_RED
|
|
40373
|
-
};
|
|
40374
|
-
return styles[severity];
|
|
40375
|
-
}
|
|
40376
|
-
function renderOutput(leadingSymbol, trailingSymbol, options) {
|
|
40377
|
-
const annotateLine2 = (line) => ansi(line, BOLD);
|
|
40378
|
-
const prefix = leadingSymbol + " ";
|
|
40379
|
-
return match3(options.message.split(NEWLINE_REGEX), {
|
|
40380
|
-
onEmpty: () => `${prefix}${trailingSymbol}`,
|
|
40381
|
-
onNonEmpty: (promptLines) => {
|
|
40382
|
-
const lines2 = map4(promptLines, (line) => annotateLine2(line));
|
|
40383
|
-
return `${prefix}${lines2.join("\n ")} ${trailingSymbol} `;
|
|
40384
|
-
}
|
|
40385
|
-
});
|
|
40386
|
-
}
|
|
40387
|
-
function renderDiagnostics(state, options, figs, columns) {
|
|
40388
|
-
const diagnostics3 = options.diagnostics;
|
|
40389
|
-
const toDisplay = entriesToDisplay2(state.index, diagnostics3.length, options.maxPerPage);
|
|
40390
|
-
const documents = [];
|
|
40391
|
-
for (let index = toDisplay.startIndex; index < toDisplay.endIndex; index++) {
|
|
40392
|
-
const diagnostic = diagnostics3[index];
|
|
40393
|
-
const isHighlighted = state.index === index;
|
|
40394
|
-
const currentSeverity = state.severities[diagnostic.name] ?? diagnostic.defaultSeverity;
|
|
40395
|
-
const hasChanged = currentSeverity !== diagnostic.defaultSeverity;
|
|
40396
|
-
let prefix = " ";
|
|
40397
|
-
if (index === toDisplay.startIndex && toDisplay.startIndex > 0) {
|
|
40398
|
-
prefix = figs.arrowUp;
|
|
40399
|
-
} else if (index === toDisplay.endIndex - 1 && toDisplay.endIndex < diagnostics3.length) {
|
|
40400
|
-
prefix = figs.arrowDown;
|
|
40401
|
-
}
|
|
40402
|
-
const shortName = getSeverityShortName(currentSeverity);
|
|
40403
|
-
const paddedSeverity = shortName.padEnd(MAX_SEVERITY_LENGTH, " ");
|
|
40404
|
-
const severityStr = ansi(` ${paddedSeverity} `, getSeverityStyle(currentSeverity));
|
|
40405
|
-
const nameText = hasChanged ? `${diagnostic.name}*` : diagnostic.name;
|
|
40406
|
-
const nameStr = isHighlighted ? ansi(nameText, CYAN_BRIGHT) : nameText;
|
|
40407
|
-
const mainLine = `${prefix} ${severityStr} ${nameStr}`;
|
|
40408
|
-
if (isHighlighted && diagnostic.description) {
|
|
40409
|
-
const indentWidth = 1 + 1 + (MAX_SEVERITY_LENGTH + 2) + 1;
|
|
40410
|
-
const indent = " ".repeat(indentWidth);
|
|
40411
|
-
const availableWidth = columns - indentWidth;
|
|
40412
|
-
const truncatedDescription = availableWidth > 0 && diagnostic.description.length > availableWidth ? diagnostic.description.substring(0, availableWidth - 1) + "\u2026" : diagnostic.description;
|
|
40413
|
-
const descriptionStr = ansi(indent + truncatedDescription, DIM);
|
|
40414
|
-
documents.push(mainLine + "\n" + descriptionStr);
|
|
40415
|
-
} else {
|
|
40416
|
-
documents.push(mainLine);
|
|
40417
|
-
}
|
|
40418
|
-
}
|
|
40419
|
-
return documents.join("\n");
|
|
40420
|
-
}
|
|
40421
|
-
function renderNextFrame(state, options) {
|
|
40422
|
-
return gen2(function* () {
|
|
40423
|
-
const terminal = yield* Terminal;
|
|
40424
|
-
const columns = yield* terminal.columns;
|
|
40425
|
-
const figs = figuresValue;
|
|
40426
|
-
const diagnosticsStr = renderDiagnostics(state, options, figs, columns);
|
|
40427
|
-
const leadingSymbol = ansi("?", CYAN_BRIGHT);
|
|
40428
|
-
const trailingSymbol = figs.pointerSmall;
|
|
40429
|
-
const promptMsg = renderOutput(leadingSymbol, trailingSymbol, options);
|
|
40430
|
-
const helpText = ansi(
|
|
40431
|
-
"Use \u2191/\u2193 to navigate, \u2190/\u2192 to change severity, Enter to finish",
|
|
40432
|
-
DIM
|
|
40433
|
-
);
|
|
40434
|
-
return CURSOR_HIDE + promptMsg + "\n" + helpText + "\n" + diagnosticsStr;
|
|
40435
|
-
});
|
|
40436
|
-
}
|
|
40437
|
-
function renderSubmission(state, options) {
|
|
40438
|
-
return gen2(function* () {
|
|
40439
|
-
const figs = figuresValue;
|
|
40440
|
-
const changedCount = Object.entries(state.severities).filter(([name, severity]) => {
|
|
40441
|
-
const diagnostic = options.diagnostics.find((d) => d.name === name);
|
|
40442
|
-
return diagnostic && severity !== diagnostic.defaultSeverity;
|
|
40443
|
-
}).length;
|
|
40444
|
-
const result3 = ansi(
|
|
40445
|
-
`${changedCount} diagnostic${changedCount === 1 ? "" : "s"} configured`,
|
|
40446
|
-
WHITE
|
|
40447
|
-
);
|
|
40448
|
-
const leadingSymbol = ansi(figs.tick, GREEN);
|
|
40449
|
-
const trailingSymbol = "";
|
|
40450
|
-
const promptMsg = renderOutput(leadingSymbol, trailingSymbol, options);
|
|
40451
|
-
return promptMsg + " " + result3 + "\n";
|
|
40452
|
-
});
|
|
40453
|
-
}
|
|
40454
|
-
function processCursorUp(state, totalCount) {
|
|
40455
|
-
const newIndex = state.index === 0 ? totalCount - 1 : state.index - 1;
|
|
40456
|
-
return succeed6(Action2.NextFrame({ state: { ...state, index: newIndex } }));
|
|
40457
|
-
}
|
|
40458
|
-
function processCursorDown(state, totalCount) {
|
|
40459
|
-
const newIndex = (state.index + 1) % totalCount;
|
|
40460
|
-
return succeed6(Action2.NextFrame({ state: { ...state, index: newIndex } }));
|
|
40461
|
-
}
|
|
40462
|
-
function processSeverityChange(state, options, direction) {
|
|
40463
|
-
const diagnostic = options.diagnostics[state.index];
|
|
40464
|
-
const currentSeverity = state.severities[diagnostic.name] ?? diagnostic.defaultSeverity;
|
|
40465
|
-
const newSeverity = cycleSeverity(currentSeverity, direction);
|
|
40466
|
-
return succeed6(Action2.NextFrame({
|
|
40467
|
-
state: {
|
|
40468
|
-
...state,
|
|
40469
|
-
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."
|
|
40470
40655
|
}
|
|
40471
|
-
|
|
40472
|
-
|
|
40473
|
-
|
|
40474
|
-
|
|
40475
|
-
|
|
40476
|
-
|
|
40477
|
-
|
|
40478
|
-
|
|
40479
|
-
|
|
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
|
+
]
|
|
40480
40687
|
}
|
|
40481
|
-
|
|
40482
|
-
|
|
40483
|
-
|
|
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
|
+
]
|
|
40484
40708
|
}
|
|
40485
|
-
|
|
40486
|
-
|
|
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: []
|
|
40487
40723
|
}
|
|
40488
|
-
|
|
40489
|
-
|
|
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
|
+
]
|
|
40490
40744
|
}
|
|
40491
|
-
|
|
40492
|
-
|
|
40493
|
-
|
|
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
|
+
]
|
|
40494
40765
|
}
|
|
40495
|
-
|
|
40496
|
-
|
|
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
|
+
]
|
|
40497
40786
|
}
|
|
40498
|
-
}
|
|
40499
|
-
|
|
40500
|
-
|
|
40501
|
-
|
|
40502
|
-
|
|
40503
|
-
|
|
40504
|
-
|
|
40505
|
-
|
|
40506
|
-
|
|
40507
|
-
|
|
40508
|
-
|
|
40509
|
-
|
|
40510
|
-
|
|
40511
|
-
|
|
40512
|
-
|
|
40513
|
-
|
|
40514
|
-
|
|
40515
|
-
|
|
40516
|
-
|
|
40517
|
-
|
|
40518
|
-
|
|
40519
|
-
|
|
40520
|
-
|
|
40521
|
-
|
|
40522
|
-
|
|
40523
|
-
|
|
40524
|
-
|
|
40525
|
-
|
|
40526
|
-
|
|
40527
|
-
|
|
40528
|
-
|
|
40529
|
-
|
|
40530
|
-
|
|
40531
|
-
|
|
40532
|
-
|
|
40533
|
-
|
|
40534
|
-
|
|
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
|
+
})
|
|
40535
42283
|
});
|
|
40536
42284
|
}
|
|
40537
42285
|
|