@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/index.js
CHANGED
|
@@ -8958,6 +8958,57 @@ var effectMapVoid = createDiagnostic({
|
|
|
8958
8958
|
})
|
|
8959
8959
|
});
|
|
8960
8960
|
|
|
8961
|
+
// src/diagnostics/effectSucceedWithVoid.ts
|
|
8962
|
+
var effectSucceedWithVoid = createDiagnostic({
|
|
8963
|
+
name: "effectSucceedWithVoid",
|
|
8964
|
+
code: 47,
|
|
8965
|
+
description: "Suggests using Effect.void instead of Effect.succeed(undefined) or Effect.succeed(void 0)",
|
|
8966
|
+
severity: "suggestion",
|
|
8967
|
+
apply: fn("effectSucceedWithVoid.apply")(function* (sourceFile, report) {
|
|
8968
|
+
const ts = yield* service(TypeScriptApi);
|
|
8969
|
+
const typeParser = yield* service(TypeParser);
|
|
8970
|
+
const tsUtils = yield* service(TypeScriptUtils);
|
|
8971
|
+
const nodeToVisit = [];
|
|
8972
|
+
const appendNodeToVisit = (node) => {
|
|
8973
|
+
nodeToVisit.push(node);
|
|
8974
|
+
return void 0;
|
|
8975
|
+
};
|
|
8976
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
8977
|
+
while (nodeToVisit.length > 0) {
|
|
8978
|
+
const node = nodeToVisit.shift();
|
|
8979
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
8980
|
+
if (ts.isCallExpression(node)) {
|
|
8981
|
+
const isSucceedCall = yield* pipe(
|
|
8982
|
+
typeParser.isNodeReferenceToEffectModuleApi("succeed")(node.expression),
|
|
8983
|
+
option
|
|
8984
|
+
);
|
|
8985
|
+
if (isSome2(isSucceedCall)) {
|
|
8986
|
+
const argument = node.arguments[0];
|
|
8987
|
+
if (!argument) continue;
|
|
8988
|
+
if (!tsUtils.isVoidExpression(argument)) continue;
|
|
8989
|
+
report({
|
|
8990
|
+
location: node,
|
|
8991
|
+
messageText: "Effect.void can be used instead of Effect.succeed(undefined) or Effect.succeed(void 0)",
|
|
8992
|
+
fixes: [{
|
|
8993
|
+
fixName: "effectSucceedWithVoid_fix",
|
|
8994
|
+
description: "Replace with Effect.void",
|
|
8995
|
+
apply: gen(function* () {
|
|
8996
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
8997
|
+
const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
|
|
8998
|
+
const newNode = ts.factory.createPropertyAccessExpression(
|
|
8999
|
+
ts.factory.createIdentifier(effectModuleIdentifier),
|
|
9000
|
+
ts.factory.createIdentifier("void")
|
|
9001
|
+
);
|
|
9002
|
+
changeTracker.replaceNode(sourceFile, node, newNode);
|
|
9003
|
+
})
|
|
9004
|
+
}]
|
|
9005
|
+
});
|
|
9006
|
+
}
|
|
9007
|
+
}
|
|
9008
|
+
}
|
|
9009
|
+
})
|
|
9010
|
+
});
|
|
9011
|
+
|
|
8961
9012
|
// src/diagnostics/floatingEffect.ts
|
|
8962
9013
|
var floatingEffect = createDiagnostic({
|
|
8963
9014
|
name: "floatingEffect",
|
|
@@ -9101,8 +9152,7 @@ var globalErrorInEffectCatch = createDiagnostic({
|
|
|
9101
9152
|
);
|
|
9102
9153
|
report({
|
|
9103
9154
|
location: node.expression,
|
|
9104
|
-
messageText: `The 'catch' callback in ${nodeText} returns
|
|
9105
|
-
Instead, use tagged errors or custom errors with a discriminator property to get properly type-checked errors.`,
|
|
9155
|
+
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.`,
|
|
9106
9156
|
fixes: []
|
|
9107
9157
|
});
|
|
9108
9158
|
}
|
|
@@ -9119,7 +9169,7 @@ Instead, use tagged errors or custom errors with a discriminator property to get
|
|
|
9119
9169
|
var globalErrorInEffectFailure = createDiagnostic({
|
|
9120
9170
|
name: "globalErrorInEffectFailure",
|
|
9121
9171
|
code: 35,
|
|
9122
|
-
description: "Warns when
|
|
9172
|
+
description: "Warns when the global Error type is used in an Effect failure channel",
|
|
9123
9173
|
severity: "warning",
|
|
9124
9174
|
apply: fn("globalErrorInEffectFailure.apply")(function* (sourceFile, report) {
|
|
9125
9175
|
const ts = yield* service(TypeScriptApi);
|
|
@@ -9134,27 +9184,35 @@ var globalErrorInEffectFailure = createDiagnostic({
|
|
|
9134
9184
|
while (nodeToVisit.length > 0) {
|
|
9135
9185
|
const node = nodeToVisit.shift();
|
|
9136
9186
|
ts.forEachChild(node, appendNodeToVisit);
|
|
9137
|
-
if (ts.
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
|
|
9149
|
-
|
|
9150
|
-
|
|
9151
|
-
|
|
9187
|
+
if (ts.isNewExpression(node)) {
|
|
9188
|
+
const newExpressionType = typeCheckerUtils.getTypeAtLocation(node);
|
|
9189
|
+
if (!newExpressionType || !typeCheckerUtils.isGlobalErrorType(newExpressionType)) {
|
|
9190
|
+
continue;
|
|
9191
|
+
}
|
|
9192
|
+
let current = node.parent;
|
|
9193
|
+
while (current) {
|
|
9194
|
+
const currentType = typeCheckerUtils.getTypeAtLocation(current);
|
|
9195
|
+
if (currentType) {
|
|
9196
|
+
const effectTypeResult = yield* pipe(
|
|
9197
|
+
typeParser.effectType(currentType, current),
|
|
9198
|
+
option
|
|
9199
|
+
);
|
|
9200
|
+
if (effectTypeResult._tag === "Some") {
|
|
9201
|
+
const effectType = effectTypeResult.value;
|
|
9202
|
+
const failureMembers = typeCheckerUtils.unrollUnionMembers(effectType.E);
|
|
9203
|
+
const hasGlobalError = failureMembers.some((member) => typeCheckerUtils.isGlobalErrorType(member));
|
|
9204
|
+
if (hasGlobalError) {
|
|
9205
|
+
report({
|
|
9206
|
+
location: node,
|
|
9207
|
+
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.`,
|
|
9208
|
+
fixes: []
|
|
9209
|
+
});
|
|
9152
9210
|
}
|
|
9211
|
+
break;
|
|
9153
9212
|
}
|
|
9154
|
-
|
|
9155
|
-
|
|
9156
|
-
|
|
9157
|
-
);
|
|
9213
|
+
}
|
|
9214
|
+
current = current.parent;
|
|
9215
|
+
}
|
|
9158
9216
|
}
|
|
9159
9217
|
}
|
|
9160
9218
|
})
|
|
@@ -9743,9 +9801,29 @@ var missedPipeableOpportunity = createDiagnostic({
|
|
|
9743
9801
|
transformations: flow2.transformations.slice(0, firstPipeableIndex)
|
|
9744
9802
|
});
|
|
9745
9803
|
const afterTransformations = flow2.transformations.slice(pipeableEndIndex);
|
|
9804
|
+
const getOriginalSubjectNode = () => {
|
|
9805
|
+
if (firstPipeableIndex === 0) {
|
|
9806
|
+
return flow2.subject.node;
|
|
9807
|
+
}
|
|
9808
|
+
let current = flow2.node;
|
|
9809
|
+
for (let i = flow2.transformations.length; i > firstPipeableIndex; i--) {
|
|
9810
|
+
const t = flow2.transformations[i - 1];
|
|
9811
|
+
if (t.kind === "call" && ts.isCallExpression(current) && current.arguments.length > 0) {
|
|
9812
|
+
current = current.arguments[0];
|
|
9813
|
+
} else {
|
|
9814
|
+
return void 0;
|
|
9815
|
+
}
|
|
9816
|
+
}
|
|
9817
|
+
return current;
|
|
9818
|
+
};
|
|
9819
|
+
const originalSubjectNode = getOriginalSubjectNode();
|
|
9820
|
+
const subjectText = originalSubjectNode ? sourceFile.text.slice(
|
|
9821
|
+
ts.getTokenPosOfNode(originalSubjectNode, sourceFile),
|
|
9822
|
+
originalSubjectNode.end
|
|
9823
|
+
).trim() : "";
|
|
9746
9824
|
report({
|
|
9747
9825
|
location: flow2.node,
|
|
9748
|
-
messageText: `Nested function calls can be converted to pipeable style for better readability.`,
|
|
9826
|
+
messageText: `Nested function calls can be converted to pipeable style for better readability; consider using ${subjectText}.pipe(...) instead.`,
|
|
9749
9827
|
fixes: [{
|
|
9750
9828
|
fixName: "missedPipeableOpportunity_fix",
|
|
9751
9829
|
description: "Convert to pipe style",
|
|
@@ -11800,6 +11878,7 @@ var diagnostics = [
|
|
|
11800
11878
|
globalErrorInEffectFailure,
|
|
11801
11879
|
layerMergeAllWithDependencies,
|
|
11802
11880
|
effectMapVoid,
|
|
11881
|
+
effectSucceedWithVoid,
|
|
11803
11882
|
effectFnIife,
|
|
11804
11883
|
effectFnOpportunity,
|
|
11805
11884
|
redundantSchemaTagIdentifier,
|