@effect/language-service 0.63.2 → 0.64.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 +44 -0
- package/cli.js +1250 -402
- package/cli.js.map +1 -1
- package/effect-lsp-patch-utils.js +424 -62
- package/effect-lsp-patch-utils.js.map +1 -1
- package/index.js +445 -70
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/transform.js +424 -62
- package/transform.js.map +1 -1
|
@@ -2699,6 +2699,12 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
2699
2699
|
);
|
|
2700
2700
|
const extendsCauseYieldableError = cachedBy(
|
|
2701
2701
|
fn("TypeParser.extendsCauseYieldableError")(function* (givenType) {
|
|
2702
|
+
if (givenType.flags & ts.TypeFlags.Never) {
|
|
2703
|
+
return yield* typeParserIssue("Type is never", givenType);
|
|
2704
|
+
}
|
|
2705
|
+
if (givenType.flags & ts.TypeFlags.Any) {
|
|
2706
|
+
return yield* typeParserIssue("Type is any", givenType);
|
|
2707
|
+
}
|
|
2702
2708
|
const symbols = yield* findSymbolsMatchingPackageAndExportedName("effect", "YieldableError")();
|
|
2703
2709
|
for (const [symbol3, sourceFile] of symbols) {
|
|
2704
2710
|
const causeFile = yield* pipe(isCauseTypeSourceFile(sourceFile), orElse2(() => void_));
|
|
@@ -2793,18 +2799,19 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
2793
2799
|
);
|
|
2794
2800
|
const effectType = cachedBy(
|
|
2795
2801
|
fn("TypeParser.effectType")(function* (type, atLocation) {
|
|
2796
|
-
let result = typeParserIssue("Type has no effect variance struct", type, atLocation);
|
|
2797
2802
|
const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
|
|
2798
|
-
(_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
|
|
2803
|
+
(_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
|
|
2799
2804
|
);
|
|
2805
|
+
if (propertiesSymbols.length === 0) {
|
|
2806
|
+
return yield* typeParserIssue("Type has no effect variance struct", type, atLocation);
|
|
2807
|
+
}
|
|
2800
2808
|
propertiesSymbols.sort(
|
|
2801
2809
|
(a, b) => ts.symbolName(b).indexOf("EffectTypeId") - ts.symbolName(a).indexOf("EffectTypeId")
|
|
2802
2810
|
);
|
|
2803
|
-
|
|
2811
|
+
return yield* firstSuccessOf(propertiesSymbols.map((propertySymbol) => {
|
|
2804
2812
|
const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
|
|
2805
|
-
|
|
2806
|
-
}
|
|
2807
|
-
return yield* result;
|
|
2813
|
+
return effectVarianceStruct(propertyType, atLocation);
|
|
2814
|
+
}));
|
|
2808
2815
|
}),
|
|
2809
2816
|
"TypeParser.effectType",
|
|
2810
2817
|
(type) => type
|
|
@@ -2843,22 +2850,18 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
2843
2850
|
fn("TypeParser.layerType")(function* (type, atLocation) {
|
|
2844
2851
|
yield* pipeableType(type, atLocation);
|
|
2845
2852
|
const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
|
|
2846
|
-
(_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
|
|
2853
|
+
(_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
|
|
2847
2854
|
);
|
|
2855
|
+
if (propertiesSymbols.length === 0) {
|
|
2856
|
+
return yield* typeParserIssue("Type has no layer variance struct", type, atLocation);
|
|
2857
|
+
}
|
|
2848
2858
|
propertiesSymbols.sort(
|
|
2849
2859
|
(a, b) => ts.symbolName(b).indexOf("LayerTypeId") - ts.symbolName(a).indexOf("LayerTypeId")
|
|
2850
2860
|
);
|
|
2851
|
-
|
|
2861
|
+
return yield* firstSuccessOf(propertiesSymbols.map((propertySymbol) => {
|
|
2852
2862
|
const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
atLocation
|
|
2856
|
-
));
|
|
2857
|
-
if (isSome2(varianceArgs)) {
|
|
2858
|
-
return varianceArgs.value;
|
|
2859
|
-
}
|
|
2860
|
-
}
|
|
2861
|
-
return yield* typeParserIssue("Type has no layer variance struct", type, atLocation);
|
|
2863
|
+
return layerVarianceStruct(propertyType, atLocation);
|
|
2864
|
+
}));
|
|
2862
2865
|
}),
|
|
2863
2866
|
"TypeParser.layerType",
|
|
2864
2867
|
(type) => type
|
|
@@ -3155,20 +3158,16 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3155
3158
|
const ast = typeChecker.getPropertyOfType(type, "ast");
|
|
3156
3159
|
if (!ast) return yield* typeParserIssue("Has no 'ast' property", type, atLocation);
|
|
3157
3160
|
const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
|
|
3158
|
-
(_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
|
|
3161
|
+
(_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
|
|
3159
3162
|
);
|
|
3163
|
+
if (propertiesSymbols.length === 0) {
|
|
3164
|
+
return yield* typeParserIssue("Type has no schema variance struct", type, atLocation);
|
|
3165
|
+
}
|
|
3160
3166
|
propertiesSymbols.sort((a, b) => ts.symbolName(b).indexOf("TypeId") - ts.symbolName(a).indexOf("TypeId"));
|
|
3161
|
-
|
|
3167
|
+
return yield* firstSuccessOf(propertiesSymbols.map((propertySymbol) => {
|
|
3162
3168
|
const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
atLocation
|
|
3166
|
-
));
|
|
3167
|
-
if (isSome2(varianceArgs)) {
|
|
3168
|
-
return varianceArgs.value;
|
|
3169
|
-
}
|
|
3170
|
-
}
|
|
3171
|
-
return yield* typeParserIssue("Type has no schema variance struct", type, atLocation);
|
|
3169
|
+
return effectSchemaVarianceStruct(propertyType, atLocation);
|
|
3170
|
+
}));
|
|
3172
3171
|
}),
|
|
3173
3172
|
"TypeParser.effectSchemaType",
|
|
3174
3173
|
(type) => type
|
|
@@ -3204,33 +3203,54 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3204
3203
|
fn("TypeParser.contextTag")(function* (type, atLocation) {
|
|
3205
3204
|
yield* pipeableType(type, atLocation);
|
|
3206
3205
|
const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
|
|
3207
|
-
(_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
|
|
3206
|
+
(_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
|
|
3208
3207
|
);
|
|
3208
|
+
if (propertiesSymbols.length === 0) {
|
|
3209
|
+
return yield* typeParserIssue("Type has no tag variance struct", type, atLocation);
|
|
3210
|
+
}
|
|
3209
3211
|
propertiesSymbols.sort((a, b) => ts.symbolName(b).indexOf("TypeId") - ts.symbolName(a).indexOf("TypeId"));
|
|
3210
|
-
|
|
3212
|
+
return yield* firstSuccessOf(propertiesSymbols.map((propertySymbol) => {
|
|
3211
3213
|
const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
atLocation
|
|
3215
|
-
));
|
|
3216
|
-
if (isSome2(varianceArgs)) {
|
|
3217
|
-
return varianceArgs.value;
|
|
3218
|
-
}
|
|
3219
|
-
}
|
|
3220
|
-
return yield* typeParserIssue("Type has no tag variance struct", type, atLocation);
|
|
3214
|
+
return contextTagVarianceStruct(propertyType, atLocation);
|
|
3215
|
+
}));
|
|
3221
3216
|
}),
|
|
3222
3217
|
"TypeParser.contextTag",
|
|
3223
3218
|
(type) => type
|
|
3224
3219
|
);
|
|
3220
|
+
const effectFunctionImportedName = cachedBy(
|
|
3221
|
+
fn("TypeParser.effectFunctionImportedName")(function* (sourceFile) {
|
|
3222
|
+
return tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Function");
|
|
3223
|
+
}),
|
|
3224
|
+
"TypeParser.effectFunctionImportedName",
|
|
3225
|
+
(node) => node
|
|
3226
|
+
);
|
|
3225
3227
|
const pipeCall = cachedBy(
|
|
3226
3228
|
function(node) {
|
|
3227
3229
|
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.name) && ts.idText(node.expression.name) === "pipe") {
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3230
|
+
const baseExpression = node.expression.expression;
|
|
3231
|
+
return pipe(
|
|
3232
|
+
effectFunctionImportedName(tsUtils.getSourceFileOfNode(node)),
|
|
3233
|
+
flatMap2((functionIdentifier) => {
|
|
3234
|
+
if (functionIdentifier && ts.isIdentifier(baseExpression) && ts.idText(baseExpression) === functionIdentifier) {
|
|
3235
|
+
if (node.arguments.length === 0) {
|
|
3236
|
+
return typeParserIssue("Node is not a pipe call", void 0, node);
|
|
3237
|
+
}
|
|
3238
|
+
const [subject, ...args2] = node.arguments;
|
|
3239
|
+
return succeed({
|
|
3240
|
+
node,
|
|
3241
|
+
subject,
|
|
3242
|
+
args: args2,
|
|
3243
|
+
kind: "pipe"
|
|
3244
|
+
});
|
|
3245
|
+
}
|
|
3246
|
+
return succeed({
|
|
3247
|
+
node,
|
|
3248
|
+
subject: baseExpression,
|
|
3249
|
+
args: Array.from(node.arguments),
|
|
3250
|
+
kind: "pipeable"
|
|
3251
|
+
});
|
|
3252
|
+
})
|
|
3253
|
+
);
|
|
3234
3254
|
}
|
|
3235
3255
|
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && ts.idText(node.expression) === "pipe" && node.arguments.length > 0) {
|
|
3236
3256
|
const [subject, ...args2] = node.arguments;
|
|
@@ -3245,17 +3265,10 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3245
3265
|
fn("TypeParser.scopeType")(function* (type, atLocation) {
|
|
3246
3266
|
yield* pipeableType(type, atLocation);
|
|
3247
3267
|
const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
|
|
3248
|
-
(_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
|
|
3268
|
+
(_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
|
|
3249
3269
|
);
|
|
3250
|
-
propertiesSymbols.
|
|
3251
|
-
|
|
3252
|
-
);
|
|
3253
|
-
for (const propertySymbol of propertiesSymbols) {
|
|
3254
|
-
const computedPropertyExpression = propertySymbol.valueDeclaration.name;
|
|
3255
|
-
const symbol3 = typeChecker.getSymbolAtLocation(computedPropertyExpression.expression);
|
|
3256
|
-
if (symbol3 && ts.symbolName(symbol3) === "ScopeTypeId") {
|
|
3257
|
-
return type;
|
|
3258
|
-
}
|
|
3270
|
+
if (propertiesSymbols.some((s) => ts.symbolName(s).indexOf("ScopeTypeId") !== -1)) {
|
|
3271
|
+
return type;
|
|
3259
3272
|
}
|
|
3260
3273
|
return yield* typeParserIssue("Type has no scope type id", type, atLocation);
|
|
3261
3274
|
}),
|
|
@@ -3761,12 +3774,38 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3761
3774
|
"TypeParser.extendsEffectSqlModelClass",
|
|
3762
3775
|
(atLocation) => atLocation
|
|
3763
3776
|
);
|
|
3777
|
+
const isEffectLayerTypeSourceFile = cachedBy(
|
|
3778
|
+
fn("TypeParser.isEffectLayerTypeSourceFile")(function* (sourceFile) {
|
|
3779
|
+
const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
|
|
3780
|
+
if (!moduleSymbol) return yield* typeParserIssue("Node has no symbol", void 0, sourceFile);
|
|
3781
|
+
const layerTypeSymbol = typeChecker.tryGetMemberInModuleExports("Layer", moduleSymbol);
|
|
3782
|
+
if (!layerTypeSymbol) return yield* typeParserIssue("Layer type not found", void 0, sourceFile);
|
|
3783
|
+
const type = typeChecker.getDeclaredTypeOfSymbol(layerTypeSymbol);
|
|
3784
|
+
yield* layerType(type, sourceFile);
|
|
3785
|
+
return sourceFile;
|
|
3786
|
+
}),
|
|
3787
|
+
"TypeParser.isEffectLayerTypeSourceFile",
|
|
3788
|
+
(sourceFile) => sourceFile
|
|
3789
|
+
);
|
|
3790
|
+
const isNodeReferenceToEffectLayerModuleApi = (memberName) => cachedBy(
|
|
3791
|
+
fn("TypeParser.isNodeReferenceToEffectLayerModuleApi")(function* (node) {
|
|
3792
|
+
return yield* isNodeReferenceToExportOfPackageModule(
|
|
3793
|
+
node,
|
|
3794
|
+
"effect",
|
|
3795
|
+
isEffectLayerTypeSourceFile,
|
|
3796
|
+
memberName
|
|
3797
|
+
);
|
|
3798
|
+
}),
|
|
3799
|
+
`TypeParser.isNodeReferenceToEffectLayerModuleApi(${memberName})`,
|
|
3800
|
+
(node) => node
|
|
3801
|
+
);
|
|
3764
3802
|
return {
|
|
3765
3803
|
isNodeReferenceToEffectModuleApi,
|
|
3766
3804
|
isNodeReferenceToEffectSchemaModuleApi,
|
|
3767
3805
|
isNodeReferenceToEffectDataModuleApi,
|
|
3768
3806
|
isNodeReferenceToEffectContextModuleApi,
|
|
3769
3807
|
isNodeReferenceToEffectSqlModelModuleApi,
|
|
3808
|
+
isNodeReferenceToEffectLayerModuleApi,
|
|
3770
3809
|
effectType,
|
|
3771
3810
|
strictEffectType,
|
|
3772
3811
|
layerType,
|
|
@@ -3900,6 +3939,100 @@ var anyUnknownInErrorContext = createDiagnostic({
|
|
|
3900
3939
|
})
|
|
3901
3940
|
});
|
|
3902
3941
|
|
|
3942
|
+
// src/diagnostics/catchAllToMapError.ts
|
|
3943
|
+
var catchAllToMapError = createDiagnostic({
|
|
3944
|
+
name: "catchAllToMapError",
|
|
3945
|
+
code: 39,
|
|
3946
|
+
description: "Suggests using Effect.mapError instead of Effect.catchAll when the callback only wraps the error with Effect.fail",
|
|
3947
|
+
severity: "suggestion",
|
|
3948
|
+
apply: fn("catchAllToMapError.apply")(function* (sourceFile, report) {
|
|
3949
|
+
const ts = yield* service(TypeScriptApi);
|
|
3950
|
+
const typeParser = yield* service(TypeParser);
|
|
3951
|
+
const getFunctionBody = (node) => {
|
|
3952
|
+
if (ts.isArrowFunction(node)) {
|
|
3953
|
+
return node.body;
|
|
3954
|
+
}
|
|
3955
|
+
if (ts.isFunctionExpression(node)) {
|
|
3956
|
+
return node.body;
|
|
3957
|
+
}
|
|
3958
|
+
return void 0;
|
|
3959
|
+
};
|
|
3960
|
+
const getEffectFailCallInfo = (body) => {
|
|
3961
|
+
return gen(function* () {
|
|
3962
|
+
if (ts.isCallExpression(body)) {
|
|
3963
|
+
const isFailCall = yield* pipe(
|
|
3964
|
+
typeParser.isNodeReferenceToEffectModuleApi("fail")(body.expression),
|
|
3965
|
+
option
|
|
3966
|
+
);
|
|
3967
|
+
if (isSome2(isFailCall) && body.arguments.length >= 1) {
|
|
3968
|
+
return some2({ failCall: body, failArg: body.arguments[0] });
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
if (ts.isBlock(body)) {
|
|
3972
|
+
const statements = body.statements;
|
|
3973
|
+
if (statements.length === 1) {
|
|
3974
|
+
const stmt = statements[0];
|
|
3975
|
+
if (ts.isReturnStatement(stmt) && stmt.expression && ts.isCallExpression(stmt.expression)) {
|
|
3976
|
+
const isFailCall = yield* pipe(
|
|
3977
|
+
typeParser.isNodeReferenceToEffectModuleApi("fail")(stmt.expression.expression),
|
|
3978
|
+
option
|
|
3979
|
+
);
|
|
3980
|
+
if (isSome2(isFailCall) && stmt.expression.arguments.length >= 1) {
|
|
3981
|
+
return some2({ failCall: stmt.expression, failArg: stmt.expression.arguments[0] });
|
|
3982
|
+
}
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
return none2();
|
|
3987
|
+
});
|
|
3988
|
+
};
|
|
3989
|
+
const nodeToVisit = [];
|
|
3990
|
+
const appendNodeToVisit = (node) => {
|
|
3991
|
+
nodeToVisit.push(node);
|
|
3992
|
+
return void 0;
|
|
3993
|
+
};
|
|
3994
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
3995
|
+
while (nodeToVisit.length > 0) {
|
|
3996
|
+
const node = nodeToVisit.shift();
|
|
3997
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
3998
|
+
if (ts.isCallExpression(node)) {
|
|
3999
|
+
const isCatchAllCall = yield* pipe(
|
|
4000
|
+
typeParser.isNodeReferenceToEffectModuleApi("catchAll")(node.expression),
|
|
4001
|
+
option
|
|
4002
|
+
);
|
|
4003
|
+
if (isSome2(isCatchAllCall)) {
|
|
4004
|
+
const callback = node.arguments[0];
|
|
4005
|
+
if (!callback) continue;
|
|
4006
|
+
const functionBody = getFunctionBody(callback);
|
|
4007
|
+
if (!functionBody) continue;
|
|
4008
|
+
const failCallInfo = yield* getEffectFailCallInfo(functionBody);
|
|
4009
|
+
if (isNone2(failCallInfo)) continue;
|
|
4010
|
+
const { failArg, failCall } = failCallInfo.value;
|
|
4011
|
+
report({
|
|
4012
|
+
location: node.expression,
|
|
4013
|
+
messageText: `You can use Effect.mapError instead of Effect.catchAll + Effect.fail to transform the error type.`,
|
|
4014
|
+
fixes: [{
|
|
4015
|
+
fixName: "catchAllToMapError_fix",
|
|
4016
|
+
description: "Replace with Effect.mapError",
|
|
4017
|
+
apply: gen(function* () {
|
|
4018
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
4019
|
+
if (ts.isPropertyAccessExpression(node.expression)) {
|
|
4020
|
+
changeTracker.replaceNode(
|
|
4021
|
+
sourceFile,
|
|
4022
|
+
node.expression.name,
|
|
4023
|
+
ts.factory.createIdentifier("mapError")
|
|
4024
|
+
);
|
|
4025
|
+
}
|
|
4026
|
+
changeTracker.replaceNode(sourceFile, failCall, failArg);
|
|
4027
|
+
})
|
|
4028
|
+
}]
|
|
4029
|
+
});
|
|
4030
|
+
}
|
|
4031
|
+
}
|
|
4032
|
+
}
|
|
4033
|
+
})
|
|
4034
|
+
});
|
|
4035
|
+
|
|
3903
4036
|
// src/diagnostics/catchUnfailableEffect.ts
|
|
3904
4037
|
var catchUnfailableEffect = createDiagnostic({
|
|
3905
4038
|
name: "catchUnfailableEffect",
|
|
@@ -4462,6 +4595,65 @@ var genericEffectServices = createDiagnostic({
|
|
|
4462
4595
|
})
|
|
4463
4596
|
});
|
|
4464
4597
|
|
|
4598
|
+
// src/diagnostics/globalErrorInEffectCatch.ts
|
|
4599
|
+
var globalErrorInEffectCatch = createDiagnostic({
|
|
4600
|
+
name: "globalErrorInEffectCatch",
|
|
4601
|
+
code: 36,
|
|
4602
|
+
description: "Warns when catch callbacks return global Error type instead of typed errors",
|
|
4603
|
+
severity: "warning",
|
|
4604
|
+
apply: fn("globalErrorInEffectCatch.apply")(function* (sourceFile, report) {
|
|
4605
|
+
const ts = yield* service(TypeScriptApi);
|
|
4606
|
+
const typeParser = yield* service(TypeParser);
|
|
4607
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
4608
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
4609
|
+
const nodeToVisit = [];
|
|
4610
|
+
const appendNodeToVisit = (node) => {
|
|
4611
|
+
nodeToVisit.push(node);
|
|
4612
|
+
return void 0;
|
|
4613
|
+
};
|
|
4614
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
4615
|
+
while (nodeToVisit.length > 0) {
|
|
4616
|
+
const node = nodeToVisit.shift();
|
|
4617
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
4618
|
+
if (ts.isCallExpression(node)) {
|
|
4619
|
+
const isEffectWithCatch = yield* pipe(
|
|
4620
|
+
typeParser.isNodeReferenceToEffectModuleApi("tryPromise")(node.expression),
|
|
4621
|
+
orElse2(() => typeParser.isNodeReferenceToEffectModuleApi("try")(node.expression)),
|
|
4622
|
+
orElse2(() => typeParser.isNodeReferenceToEffectModuleApi("tryMap")(node.expression)),
|
|
4623
|
+
orElse2(() => typeParser.isNodeReferenceToEffectModuleApi("tryMapPromise")(node.expression)),
|
|
4624
|
+
orElse2(() => void_)
|
|
4625
|
+
);
|
|
4626
|
+
if (isEffectWithCatch) {
|
|
4627
|
+
const signature = typeChecker.getResolvedSignature(node);
|
|
4628
|
+
if (signature) {
|
|
4629
|
+
const objectType = typeChecker.getParameterType(signature, 0);
|
|
4630
|
+
const catchFunctionSymbol = typeChecker.getPropertyOfType(objectType, "catch");
|
|
4631
|
+
if (catchFunctionSymbol) {
|
|
4632
|
+
const catchFunctionType = typeChecker.getTypeOfSymbolAtLocation(catchFunctionSymbol, node);
|
|
4633
|
+
const signatures = typeChecker.getSignaturesOfType(catchFunctionType, ts.SignatureKind.Call);
|
|
4634
|
+
if (signatures.length > 0) {
|
|
4635
|
+
const returnType = typeChecker.getReturnTypeOfSignature(signatures[0]);
|
|
4636
|
+
if (returnType && typeCheckerUtils.isGlobalErrorType(returnType)) {
|
|
4637
|
+
const nodeText = sourceFile.text.substring(
|
|
4638
|
+
ts.getTokenPosOfNode(node.expression, sourceFile),
|
|
4639
|
+
node.expression.end
|
|
4640
|
+
);
|
|
4641
|
+
report({
|
|
4642
|
+
location: node.expression,
|
|
4643
|
+
messageText: `The 'catch' callback in ${nodeText} returns the global Error type. It's not recommended to use the global Error type in Effect failures as they can get merged together.
|
|
4644
|
+
Instead, use tagged errors or custom errors with a discriminator property to get properly type-checked errors.`,
|
|
4645
|
+
fixes: []
|
|
4646
|
+
});
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
})
|
|
4655
|
+
});
|
|
4656
|
+
|
|
4465
4657
|
// src/diagnostics/globalErrorInEffectFailure.ts
|
|
4466
4658
|
var globalErrorInEffectFailure = createDiagnostic({
|
|
4467
4659
|
name: "globalErrorInEffectFailure",
|
|
@@ -4492,7 +4684,7 @@ var globalErrorInEffectFailure = createDiagnostic({
|
|
|
4492
4684
|
return sync(
|
|
4493
4685
|
() => report({
|
|
4494
4686
|
location: node,
|
|
4495
|
-
messageText: `Effect.fail is called with the global Error type. It's not recommended to use the global Error type in Effect failures as they can get merged together. Instead, use tagged errors
|
|
4687
|
+
messageText: `Effect.fail is called with the global Error type. It's not recommended to use the global Error type in Effect failures as they can get merged together. Instead, use tagged errors or custom errors with a discriminator property to get properly type-checked errors.`,
|
|
4496
4688
|
fixes: []
|
|
4497
4689
|
})
|
|
4498
4690
|
);
|
|
@@ -4647,6 +4839,119 @@ var importFromBarrel = createDiagnostic({
|
|
|
4647
4839
|
})
|
|
4648
4840
|
});
|
|
4649
4841
|
|
|
4842
|
+
// src/diagnostics/layerMergeAllWithDependencies.ts
|
|
4843
|
+
var layerMergeAllWithDependencies = createDiagnostic({
|
|
4844
|
+
name: "layerMergeAllWithDependencies",
|
|
4845
|
+
code: 37,
|
|
4846
|
+
description: "Detects interdependencies in Layer.mergeAll calls where one layer provides a service that another layer requires",
|
|
4847
|
+
severity: "warning",
|
|
4848
|
+
apply: fn("layerMergeAllWithDependencies.apply")(function* (sourceFile, report) {
|
|
4849
|
+
const ts = yield* service(TypeScriptApi);
|
|
4850
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
4851
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
4852
|
+
const typeParser = yield* service(TypeParser);
|
|
4853
|
+
const tsUtils = yield* service(TypeScriptUtils);
|
|
4854
|
+
const layerModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(
|
|
4855
|
+
sourceFile,
|
|
4856
|
+
"effect",
|
|
4857
|
+
"Layer"
|
|
4858
|
+
) || "Layer";
|
|
4859
|
+
const nodeToVisit = [];
|
|
4860
|
+
const appendNodeToVisit = (node) => {
|
|
4861
|
+
nodeToVisit.push(node);
|
|
4862
|
+
return void 0;
|
|
4863
|
+
};
|
|
4864
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
4865
|
+
while (nodeToVisit.length > 0) {
|
|
4866
|
+
const node = nodeToVisit.shift();
|
|
4867
|
+
if (ts.isCallExpression(node)) {
|
|
4868
|
+
const checkLayerMergeAll = yield* pipe(
|
|
4869
|
+
typeParser.isNodeReferenceToEffectLayerModuleApi("mergeAll")(node.expression),
|
|
4870
|
+
orElse2(() => void_)
|
|
4871
|
+
);
|
|
4872
|
+
if (checkLayerMergeAll) {
|
|
4873
|
+
const layerArgs = node.arguments;
|
|
4874
|
+
if (layerArgs.length > 1) {
|
|
4875
|
+
const layerInfos = [];
|
|
4876
|
+
const actuallyProvidedMap = /* @__PURE__ */ new Map();
|
|
4877
|
+
for (const arg of layerArgs) {
|
|
4878
|
+
const argType = typeCheckerUtils.getTypeAtLocation(arg);
|
|
4879
|
+
if (!argType) continue;
|
|
4880
|
+
const layerTypeParsedOption = yield* option(typeParser.layerType(argType, arg));
|
|
4881
|
+
if (isNone2(layerTypeParsedOption)) continue;
|
|
4882
|
+
const layerTypeParsed = layerTypeParsedOption.value;
|
|
4883
|
+
const providedMembers = typeCheckerUtils.unrollUnionMembers(layerTypeParsed.ROut);
|
|
4884
|
+
for (const providedType of providedMembers) {
|
|
4885
|
+
if (providedType.flags & ts.TypeFlags.Never) continue;
|
|
4886
|
+
const isPassThrough = typeChecker.isTypeAssignableTo(providedType, layerTypeParsed.RIn);
|
|
4887
|
+
if (!isPassThrough) {
|
|
4888
|
+
actuallyProvidedMap.set(providedType, arg);
|
|
4889
|
+
}
|
|
4890
|
+
}
|
|
4891
|
+
layerInfos.push({
|
|
4892
|
+
arg,
|
|
4893
|
+
requirementsType: layerTypeParsed.RIn
|
|
4894
|
+
});
|
|
4895
|
+
}
|
|
4896
|
+
const providerToConsumers = /* @__PURE__ */ new Map();
|
|
4897
|
+
for (const layer of layerInfos) {
|
|
4898
|
+
for (const [providedType, providerArg] of actuallyProvidedMap) {
|
|
4899
|
+
if (providerArg === layer.arg) continue;
|
|
4900
|
+
if (typeChecker.isTypeAssignableTo(providedType, layer.requirementsType)) {
|
|
4901
|
+
const consumers = providerToConsumers.get(providerArg) || [];
|
|
4902
|
+
consumers.push({ consumer: layer.arg, providedType });
|
|
4903
|
+
providerToConsumers.set(providerArg, consumers);
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4906
|
+
}
|
|
4907
|
+
for (const [providerArg, consumers] of providerToConsumers) {
|
|
4908
|
+
const providedTypes = Array.from(new Set(consumers.map((c) => typeChecker.typeToString(c.providedType)))).join(", ");
|
|
4909
|
+
report({
|
|
4910
|
+
location: providerArg,
|
|
4911
|
+
messageText: `This layer provides ${providedTypes} 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.`,
|
|
4912
|
+
fixes: [{
|
|
4913
|
+
fixName: "layerMergeAllWithDependencies_fix",
|
|
4914
|
+
description: "Move layer to Layer.provideMerge",
|
|
4915
|
+
apply: gen(function* () {
|
|
4916
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
4917
|
+
const providerIndex = layerArgs.indexOf(providerArg);
|
|
4918
|
+
if (providerIndex === -1) return;
|
|
4919
|
+
const providerArgNode = providerArg;
|
|
4920
|
+
if (providerIndex === 0 && layerArgs.length > 1) {
|
|
4921
|
+
changeTracker.deleteRange(sourceFile, {
|
|
4922
|
+
pos: providerArgNode.pos,
|
|
4923
|
+
end: layerArgs[1].pos
|
|
4924
|
+
});
|
|
4925
|
+
} else if (providerIndex > 0) {
|
|
4926
|
+
changeTracker.deleteRange(sourceFile, {
|
|
4927
|
+
pos: layerArgs[providerIndex - 1].end,
|
|
4928
|
+
end: providerArgNode.end
|
|
4929
|
+
});
|
|
4930
|
+
}
|
|
4931
|
+
const provideMergeCall = ts.factory.createCallExpression(
|
|
4932
|
+
ts.factory.createPropertyAccessExpression(
|
|
4933
|
+
ts.factory.createIdentifier(layerModuleIdentifier),
|
|
4934
|
+
ts.factory.createIdentifier("provideMerge")
|
|
4935
|
+
),
|
|
4936
|
+
void 0,
|
|
4937
|
+
[providerArgNode]
|
|
4938
|
+
);
|
|
4939
|
+
changeTracker.insertNodeAt(sourceFile, node.end, provideMergeCall, {
|
|
4940
|
+
prefix: ".pipe("
|
|
4941
|
+
});
|
|
4942
|
+
changeTracker.insertText(sourceFile, node.end, ")");
|
|
4943
|
+
})
|
|
4944
|
+
}]
|
|
4945
|
+
});
|
|
4946
|
+
}
|
|
4947
|
+
}
|
|
4948
|
+
}
|
|
4949
|
+
}
|
|
4950
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
4951
|
+
}
|
|
4952
|
+
})
|
|
4953
|
+
});
|
|
4954
|
+
|
|
4650
4955
|
// src/diagnostics/leakingRequirements.ts
|
|
4651
4956
|
var leakingRequirements = createDiagnostic({
|
|
4652
4957
|
name: "leakingRequirements",
|
|
@@ -4745,6 +5050,7 @@ var leakingRequirements = createDiagnostic({
|
|
|
4745
5050
|
location: node,
|
|
4746
5051
|
messageText: `This Service is leaking the ${requirements.map((_) => typeChecker.typeToString(_)).join(" | ")} requirement.
|
|
4747
5052
|
If these requirements cannot be cached and are expected to be provided per method invocation (e.g. HttpServerRequest), you can either safely disable this diagnostic for this line through quickfixes or mark the service declaration with a JSDoc @effect-leakable-service.
|
|
5053
|
+
Services should usually be collected in the layer creation body, and then provided at each method that requires them.
|
|
4748
5054
|
More info at https://effect.website/docs/requirements-management/layers/#avoiding-requirement-leakage`,
|
|
4749
5055
|
fixes: []
|
|
4750
5056
|
});
|
|
@@ -5154,6 +5460,55 @@ var missingEffectServiceDependency = createDiagnostic({
|
|
|
5154
5460
|
})
|
|
5155
5461
|
});
|
|
5156
5462
|
|
|
5463
|
+
// src/diagnostics/missingLayerContext.ts
|
|
5464
|
+
var missingLayerContext = createDiagnostic({
|
|
5465
|
+
name: "missingLayerContext",
|
|
5466
|
+
code: 38,
|
|
5467
|
+
description: "Reports missing service requirements in Layer context channel",
|
|
5468
|
+
severity: "error",
|
|
5469
|
+
apply: fn("missingLayerContext.apply")(function* (sourceFile, report) {
|
|
5470
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
5471
|
+
const typeParser = yield* service(TypeParser);
|
|
5472
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
5473
|
+
const checkForMissingContextTypes = (node, expectedType, valueNode, realType) => pipe(
|
|
5474
|
+
all(
|
|
5475
|
+
typeParser.layerType(expectedType, node),
|
|
5476
|
+
typeParser.layerType(realType, valueNode)
|
|
5477
|
+
),
|
|
5478
|
+
map4(
|
|
5479
|
+
([expectedLayer, realLayer]) => typeCheckerUtils.getMissingTypeEntriesInTargetType(
|
|
5480
|
+
realLayer.RIn,
|
|
5481
|
+
expectedLayer.RIn
|
|
5482
|
+
)
|
|
5483
|
+
)
|
|
5484
|
+
);
|
|
5485
|
+
const sortTypes = sort(typeCheckerUtils.deterministicTypeOrder);
|
|
5486
|
+
const entries = getEffectLspPatchSourceFileMetadata(sourceFile)?.relationErrors || typeCheckerUtils.expectedAndRealType(sourceFile);
|
|
5487
|
+
for (const [node, expectedType, valueNode, realType] of entries) {
|
|
5488
|
+
if (expectedType !== realType) {
|
|
5489
|
+
yield* pipe(
|
|
5490
|
+
checkForMissingContextTypes(
|
|
5491
|
+
node,
|
|
5492
|
+
expectedType,
|
|
5493
|
+
valueNode,
|
|
5494
|
+
realType
|
|
5495
|
+
),
|
|
5496
|
+
map4(
|
|
5497
|
+
(missingTypes) => missingTypes.length > 0 ? report(
|
|
5498
|
+
{
|
|
5499
|
+
location: node,
|
|
5500
|
+
messageText: `Missing '${sortTypes(missingTypes).map((_) => typeChecker.typeToString(_)).join(" | ")}' in the expected Layer context.`,
|
|
5501
|
+
fixes: []
|
|
5502
|
+
}
|
|
5503
|
+
) : void 0
|
|
5504
|
+
),
|
|
5505
|
+
ignore
|
|
5506
|
+
);
|
|
5507
|
+
}
|
|
5508
|
+
}
|
|
5509
|
+
})
|
|
5510
|
+
});
|
|
5511
|
+
|
|
5157
5512
|
// src/diagnostics/missingReturnYieldStar.ts
|
|
5158
5513
|
var missingReturnYieldStar = createDiagnostic({
|
|
5159
5514
|
name: "missingReturnYieldStar",
|
|
@@ -5691,9 +6046,11 @@ var parse2 = fn("writeTagClassAccessors.parse")(function* (node) {
|
|
|
5691
6046
|
const typeParser = yield* service(TypeParser);
|
|
5692
6047
|
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
5693
6048
|
if (!ts.isClassDeclaration(node)) return yield* fail("not a class declaration");
|
|
5694
|
-
const { Service, accessors: accessors2, className } = yield* pipe(
|
|
5695
|
-
typeParser.extendsEffectService(node),
|
|
5696
|
-
orElse2(
|
|
6049
|
+
const { Service, accessors: accessors2, className, kind } = yield* pipe(
|
|
6050
|
+
map4(typeParser.extendsEffectService(node), (_) => ({ kind: "effectService", ..._ })),
|
|
6051
|
+
orElse2(
|
|
6052
|
+
() => map4(typeParser.extendsEffectTag(node), (_) => ({ kind: "effectTag", accessors: true, ..._ }))
|
|
6053
|
+
),
|
|
5697
6054
|
orElse2(() => fail("not a class extending Effect.Service call"))
|
|
5698
6055
|
);
|
|
5699
6056
|
if (accessors2 !== true) return yield* fail("accessors are not enabled in the Effect.Service call");
|
|
@@ -5715,7 +6072,7 @@ var parse2 = fn("writeTagClassAccessors.parse")(function* (node) {
|
|
|
5715
6072
|
const hash2 = involvedMembers.map(({ property, propertyType }) => {
|
|
5716
6073
|
return ts.symbolName(property) + ": " + typeChecker.typeToString(propertyType);
|
|
5717
6074
|
}).concat([ts.idText(className)]).join("\n");
|
|
5718
|
-
return { Service, className, atLocation: node, hash: cyrb53(hash2), involvedMembers };
|
|
6075
|
+
return { Service, className, atLocation: node, hash: cyrb53(hash2), involvedMembers, kind };
|
|
5719
6076
|
});
|
|
5720
6077
|
var writeTagClassAccessors = createRefactor({
|
|
5721
6078
|
name: "writeTagClassAccessors",
|
|
@@ -7640,9 +7997,10 @@ var unsupportedServiceAccessors = createDiagnostic({
|
|
|
7640
7997
|
);
|
|
7641
7998
|
if (missingMembers.length > 0) {
|
|
7642
7999
|
const memberNames = missingMembers.map(({ property }) => `'${ts.symbolName(property)}'`).join(", ");
|
|
8000
|
+
const suggestedFix = parseResult.kind === "effectTag" ? "\nEffect.Tag does not allow to disable accessors, so you may want to use Context.Tag instead." : "";
|
|
7643
8001
|
report({
|
|
7644
8002
|
location: parseResult.className,
|
|
7645
|
-
messageText: `Even if accessors are enabled, accessors for ${memberNames} won't be available because the signature have generic type parameters or multiple call signatures
|
|
8003
|
+
messageText: `Even if accessors are enabled, accessors for ${memberNames} won't be available because the signature have generic type parameters or multiple call signatures.${suggestedFix}`,
|
|
7646
8004
|
fixes: [{
|
|
7647
8005
|
fixName: "unsupportedServiceAccessors_enableCodegen",
|
|
7648
8006
|
description: "Enable accessors codegen",
|
|
@@ -7663,6 +8021,7 @@ var unsupportedServiceAccessors = createDiagnostic({
|
|
|
7663
8021
|
// src/diagnostics.ts
|
|
7664
8022
|
var diagnostics = [
|
|
7665
8023
|
anyUnknownInErrorContext,
|
|
8024
|
+
catchAllToMapError,
|
|
7666
8025
|
catchUnfailableEffect,
|
|
7667
8026
|
classSelfMismatch,
|
|
7668
8027
|
duplicatePackage,
|
|
@@ -7670,6 +8029,7 @@ var diagnostics = [
|
|
|
7670
8029
|
missingEffectContext,
|
|
7671
8030
|
missingEffectError,
|
|
7672
8031
|
missingEffectServiceDependency,
|
|
8032
|
+
missingLayerContext,
|
|
7673
8033
|
floatingEffect,
|
|
7674
8034
|
missingStarInYieldEffectGen,
|
|
7675
8035
|
unnecessaryEffectGen,
|
|
@@ -7697,7 +8057,9 @@ var diagnostics = [
|
|
|
7697
8057
|
runEffectInsideEffect,
|
|
7698
8058
|
schemaUnionOfLiterals,
|
|
7699
8059
|
schemaStructWithTag,
|
|
7700
|
-
|
|
8060
|
+
globalErrorInEffectCatch,
|
|
8061
|
+
globalErrorInEffectFailure,
|
|
8062
|
+
layerMergeAllWithDependencies
|
|
7701
8063
|
];
|
|
7702
8064
|
|
|
7703
8065
|
// src/effect-lsp-patch-utils.ts
|