@effect/language-service 0.69.2 → 0.71.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 +2 -1
- package/cli.js +103 -24
- package/cli.js.map +1 -1
- package/effect-lsp-patch-utils.js +102 -23
- package/effect-lsp-patch-utils.js.map +1 -1
- package/index.js +102 -23
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/transform.js +102 -23
- package/transform.js.map +1 -1
package/README.md
CHANGED
|
@@ -78,12 +78,13 @@ And you're done! You'll now be able to use a set of refactors and diagnostics th
|
|
|
78
78
|
- Warn when using `Schema.Union` with multiple `Schema.Literal` calls that can be simplified to a single `Schema.Literal` call
|
|
79
79
|
- Suggest using `Schema.TaggedStruct` instead of `Schema.Struct` when a `_tag` field with `Schema.Literal` is present to make the tag optional in the constructor
|
|
80
80
|
- Warn when using `yield* Effect.fail()` with yieldable error types that can be yielded directly
|
|
81
|
-
- Warn when
|
|
81
|
+
- Warn when the global `Error` type is used in an Effect failure channel, recommending tagged errors
|
|
82
82
|
- Warn when `Layer.mergeAll` contains layers with interdependencies (where one layer provides a service that another layer in the same call requires)
|
|
83
83
|
- Suggest using `Effect.fn` for functions that return `Effect.gen` for better tracing and concise syntax
|
|
84
84
|
- Warn when `Effect.fn` or `Effect.fnUntraced` is used as an IIFE (Immediately Invoked Function Expression), suggesting `Effect.gen` instead
|
|
85
85
|
- Suggest removing redundant identifier argument when it equals the tag value in `Schema.TaggedClass`, `Schema.TaggedError`, or `Schema.TaggedRequest`
|
|
86
86
|
- Suggest using `Schema.is` instead of `instanceof` for Effect Schema types
|
|
87
|
+
- Suggest using `Effect.void` instead of `Effect.succeed(undefined)` or `Effect.succeed(void 0)`
|
|
87
88
|
|
|
88
89
|
### Completions
|
|
89
90
|
|
package/cli.js
CHANGED
|
@@ -30214,7 +30214,7 @@ var runMain3 = runMain2;
|
|
|
30214
30214
|
// package.json
|
|
30215
30215
|
var package_default = {
|
|
30216
30216
|
name: "@effect/language-service",
|
|
30217
|
-
version: "0.
|
|
30217
|
+
version: "0.71.0",
|
|
30218
30218
|
packageManager: "pnpm@8.11.0",
|
|
30219
30219
|
publishConfig: {
|
|
30220
30220
|
access: "public",
|
|
@@ -36899,6 +36899,57 @@ var effectMapVoid = createDiagnostic({
|
|
|
36899
36899
|
})
|
|
36900
36900
|
});
|
|
36901
36901
|
|
|
36902
|
+
// src/diagnostics/effectSucceedWithVoid.ts
|
|
36903
|
+
var effectSucceedWithVoid = createDiagnostic({
|
|
36904
|
+
name: "effectSucceedWithVoid",
|
|
36905
|
+
code: 47,
|
|
36906
|
+
description: "Suggests using Effect.void instead of Effect.succeed(undefined) or Effect.succeed(void 0)",
|
|
36907
|
+
severity: "suggestion",
|
|
36908
|
+
apply: fn2("effectSucceedWithVoid.apply")(function* (sourceFile, report) {
|
|
36909
|
+
const ts = yield* service2(TypeScriptApi);
|
|
36910
|
+
const typeParser = yield* service2(TypeParser);
|
|
36911
|
+
const tsUtils = yield* service2(TypeScriptUtils);
|
|
36912
|
+
const nodeToVisit = [];
|
|
36913
|
+
const appendNodeToVisit = (node) => {
|
|
36914
|
+
nodeToVisit.push(node);
|
|
36915
|
+
return void 0;
|
|
36916
|
+
};
|
|
36917
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
36918
|
+
while (nodeToVisit.length > 0) {
|
|
36919
|
+
const node = nodeToVisit.shift();
|
|
36920
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
36921
|
+
if (ts.isCallExpression(node)) {
|
|
36922
|
+
const isSucceedCall = yield* pipe(
|
|
36923
|
+
typeParser.isNodeReferenceToEffectModuleApi("succeed")(node.expression),
|
|
36924
|
+
option5
|
|
36925
|
+
);
|
|
36926
|
+
if (isSome2(isSucceedCall)) {
|
|
36927
|
+
const argument = node.arguments[0];
|
|
36928
|
+
if (!argument) continue;
|
|
36929
|
+
if (!tsUtils.isVoidExpression(argument)) continue;
|
|
36930
|
+
report({
|
|
36931
|
+
location: node,
|
|
36932
|
+
messageText: "Effect.void can be used instead of Effect.succeed(undefined) or Effect.succeed(void 0)",
|
|
36933
|
+
fixes: [{
|
|
36934
|
+
fixName: "effectSucceedWithVoid_fix",
|
|
36935
|
+
description: "Replace with Effect.void",
|
|
36936
|
+
apply: gen3(function* () {
|
|
36937
|
+
const changeTracker = yield* service2(ChangeTracker);
|
|
36938
|
+
const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
|
|
36939
|
+
const newNode = ts.factory.createPropertyAccessExpression(
|
|
36940
|
+
ts.factory.createIdentifier(effectModuleIdentifier),
|
|
36941
|
+
ts.factory.createIdentifier("void")
|
|
36942
|
+
);
|
|
36943
|
+
changeTracker.replaceNode(sourceFile, node, newNode);
|
|
36944
|
+
})
|
|
36945
|
+
}]
|
|
36946
|
+
});
|
|
36947
|
+
}
|
|
36948
|
+
}
|
|
36949
|
+
}
|
|
36950
|
+
})
|
|
36951
|
+
});
|
|
36952
|
+
|
|
36902
36953
|
// src/diagnostics/floatingEffect.ts
|
|
36903
36954
|
var floatingEffect = createDiagnostic({
|
|
36904
36955
|
name: "floatingEffect",
|
|
@@ -37042,8 +37093,7 @@ var globalErrorInEffectCatch = createDiagnostic({
|
|
|
37042
37093
|
);
|
|
37043
37094
|
report({
|
|
37044
37095
|
location: node.expression,
|
|
37045
|
-
messageText: `The 'catch' callback in ${nodeText} returns
|
|
37046
|
-
Instead, use tagged errors or custom errors with a discriminator property to get properly type-checked errors.`,
|
|
37096
|
+
messageText: `The 'catch' callback in ${nodeText} 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.`,
|
|
37047
37097
|
fixes: []
|
|
37048
37098
|
});
|
|
37049
37099
|
}
|
|
@@ -37060,7 +37110,7 @@ Instead, use tagged errors or custom errors with a discriminator property to get
|
|
|
37060
37110
|
var globalErrorInEffectFailure = createDiagnostic({
|
|
37061
37111
|
name: "globalErrorInEffectFailure",
|
|
37062
37112
|
code: 35,
|
|
37063
|
-
description: "Warns when
|
|
37113
|
+
description: "Warns when the global Error type is used in an Effect failure channel",
|
|
37064
37114
|
severity: "warning",
|
|
37065
37115
|
apply: fn2("globalErrorInEffectFailure.apply")(function* (sourceFile, report) {
|
|
37066
37116
|
const ts = yield* service2(TypeScriptApi);
|
|
@@ -37075,27 +37125,35 @@ var globalErrorInEffectFailure = createDiagnostic({
|
|
|
37075
37125
|
while (nodeToVisit.length > 0) {
|
|
37076
37126
|
const node = nodeToVisit.shift();
|
|
37077
37127
|
ts.forEachChild(node, appendNodeToVisit);
|
|
37078
|
-
if (ts.
|
|
37079
|
-
|
|
37080
|
-
|
|
37081
|
-
|
|
37082
|
-
|
|
37083
|
-
|
|
37084
|
-
|
|
37085
|
-
|
|
37086
|
-
|
|
37087
|
-
|
|
37088
|
-
|
|
37089
|
-
|
|
37090
|
-
|
|
37091
|
-
|
|
37092
|
-
|
|
37128
|
+
if (ts.isNewExpression(node)) {
|
|
37129
|
+
const newExpressionType = typeCheckerUtils.getTypeAtLocation(node);
|
|
37130
|
+
if (!newExpressionType || !typeCheckerUtils.isGlobalErrorType(newExpressionType)) {
|
|
37131
|
+
continue;
|
|
37132
|
+
}
|
|
37133
|
+
let current = node.parent;
|
|
37134
|
+
while (current) {
|
|
37135
|
+
const currentType = typeCheckerUtils.getTypeAtLocation(current);
|
|
37136
|
+
if (currentType) {
|
|
37137
|
+
const effectTypeResult = yield* pipe(
|
|
37138
|
+
typeParser.effectType(currentType, current),
|
|
37139
|
+
option5
|
|
37140
|
+
);
|
|
37141
|
+
if (effectTypeResult._tag === "Some") {
|
|
37142
|
+
const effectType = effectTypeResult.value;
|
|
37143
|
+
const failureMembers = typeCheckerUtils.unrollUnionMembers(effectType.E);
|
|
37144
|
+
const hasGlobalError = failureMembers.some((member) => typeCheckerUtils.isGlobalErrorType(member));
|
|
37145
|
+
if (hasGlobalError) {
|
|
37146
|
+
report({
|
|
37147
|
+
location: node,
|
|
37148
|
+
messageText: `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.`,
|
|
37149
|
+
fixes: []
|
|
37150
|
+
});
|
|
37093
37151
|
}
|
|
37152
|
+
break;
|
|
37094
37153
|
}
|
|
37095
|
-
|
|
37096
|
-
|
|
37097
|
-
|
|
37098
|
-
);
|
|
37154
|
+
}
|
|
37155
|
+
current = current.parent;
|
|
37156
|
+
}
|
|
37099
37157
|
}
|
|
37100
37158
|
}
|
|
37101
37159
|
})
|
|
@@ -37684,9 +37742,29 @@ var missedPipeableOpportunity = createDiagnostic({
|
|
|
37684
37742
|
transformations: flow2.transformations.slice(0, firstPipeableIndex)
|
|
37685
37743
|
});
|
|
37686
37744
|
const afterTransformations = flow2.transformations.slice(pipeableEndIndex);
|
|
37745
|
+
const getOriginalSubjectNode = () => {
|
|
37746
|
+
if (firstPipeableIndex === 0) {
|
|
37747
|
+
return flow2.subject.node;
|
|
37748
|
+
}
|
|
37749
|
+
let current = flow2.node;
|
|
37750
|
+
for (let i = flow2.transformations.length; i > firstPipeableIndex; i--) {
|
|
37751
|
+
const t = flow2.transformations[i - 1];
|
|
37752
|
+
if (t.kind === "call" && ts.isCallExpression(current) && current.arguments.length > 0) {
|
|
37753
|
+
current = current.arguments[0];
|
|
37754
|
+
} else {
|
|
37755
|
+
return void 0;
|
|
37756
|
+
}
|
|
37757
|
+
}
|
|
37758
|
+
return current;
|
|
37759
|
+
};
|
|
37760
|
+
const originalSubjectNode = getOriginalSubjectNode();
|
|
37761
|
+
const subjectText = originalSubjectNode ? sourceFile.text.slice(
|
|
37762
|
+
ts.getTokenPosOfNode(originalSubjectNode, sourceFile),
|
|
37763
|
+
originalSubjectNode.end
|
|
37764
|
+
).trim() : "";
|
|
37687
37765
|
report({
|
|
37688
37766
|
location: flow2.node,
|
|
37689
|
-
messageText: `Nested function calls can be converted to pipeable style for better readability.`,
|
|
37767
|
+
messageText: `Nested function calls can be converted to pipeable style for better readability; consider using ${subjectText}.pipe(...) instead.`,
|
|
37690
37768
|
fixes: [{
|
|
37691
37769
|
fixName: "missedPipeableOpportunity_fix",
|
|
37692
37770
|
description: "Convert to pipe style",
|
|
@@ -39741,6 +39819,7 @@ var diagnostics = [
|
|
|
39741
39819
|
globalErrorInEffectFailure,
|
|
39742
39820
|
layerMergeAllWithDependencies,
|
|
39743
39821
|
effectMapVoid,
|
|
39822
|
+
effectSucceedWithVoid,
|
|
39744
39823
|
effectFnIife,
|
|
39745
39824
|
effectFnOpportunity,
|
|
39746
39825
|
redundantSchemaTagIdentifier,
|