@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.
@@ -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 && ts.isPropertySignature(_.valueDeclaration) && ts.isComputedPropertyName(_.valueDeclaration.name)
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
- for (const propertySymbol of propertiesSymbols) {
2811
+ return yield* firstSuccessOf(propertiesSymbols.map((propertySymbol) => {
2804
2812
  const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
2805
- result = pipe(result, orElse2(() => effectVarianceStruct(propertyType, atLocation)));
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 && ts.isPropertySignature(_.valueDeclaration) && ts.isComputedPropertyName(_.valueDeclaration.name)
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
- for (const propertySymbol of propertiesSymbols) {
2861
+ return yield* firstSuccessOf(propertiesSymbols.map((propertySymbol) => {
2852
2862
  const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
2853
- const varianceArgs = yield* option(layerVarianceStruct(
2854
- propertyType,
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 && ts.isPropertySignature(_.valueDeclaration) && ts.isComputedPropertyName(_.valueDeclaration.name)
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
- for (const propertySymbol of propertiesSymbols) {
3167
+ return yield* firstSuccessOf(propertiesSymbols.map((propertySymbol) => {
3162
3168
  const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
3163
- const varianceArgs = yield* option(effectSchemaVarianceStruct(
3164
- propertyType,
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 && ts.isPropertySignature(_.valueDeclaration) && ts.isComputedPropertyName(_.valueDeclaration.name)
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
- for (const propertySymbol of propertiesSymbols) {
3212
+ return yield* firstSuccessOf(propertiesSymbols.map((propertySymbol) => {
3211
3213
  const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
3212
- const varianceArgs = yield* option(contextTagVarianceStruct(
3213
- propertyType,
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
- return succeed({
3229
- node,
3230
- subject: node.expression.expression,
3231
- args: Array.from(node.arguments),
3232
- kind: "pipeable"
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 && ts.isPropertySignature(_.valueDeclaration) && ts.isComputedPropertyName(_.valueDeclaration.name)
3268
+ (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
3249
3269
  );
3250
- propertiesSymbols.sort(
3251
- (a, b) => ts.symbolName(b).indexOf("ScopeTypeId") - ts.symbolName(a).indexOf("ScopeTypeId")
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 (Data.TaggedError) or custom errors with a discriminator property to get properly type-checked 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(() => map4(typeParser.extendsEffectTag(node), (_) => ({ accessors: true, ..._ }))),
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
- globalErrorInEffectFailure
8060
+ globalErrorInEffectCatch,
8061
+ globalErrorInEffectFailure,
8062
+ layerMergeAllWithDependencies
7701
8063
  ];
7702
8064
 
7703
8065
  // src/effect-lsp-patch-utils.ts