@effect/language-service 0.63.2 → 0.64.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect/language-service",
3
- "version": "0.63.2",
3
+ "version": "0.64.1",
4
4
  "description": "A Language-Service Plugin to Refactor and Diagnostic effect-ts projects",
5
5
  "main": "index.cjs",
6
6
  "bin": {
package/transform.js CHANGED
@@ -2695,6 +2695,12 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
2695
2695
  );
2696
2696
  const extendsCauseYieldableError = cachedBy(
2697
2697
  fn("TypeParser.extendsCauseYieldableError")(function* (givenType) {
2698
+ if (givenType.flags & ts.TypeFlags.Never) {
2699
+ return yield* typeParserIssue("Type is never", givenType);
2700
+ }
2701
+ if (givenType.flags & ts.TypeFlags.Any) {
2702
+ return yield* typeParserIssue("Type is any", givenType);
2703
+ }
2698
2704
  const symbols = yield* findSymbolsMatchingPackageAndExportedName("effect", "YieldableError")();
2699
2705
  for (const [symbol3, sourceFile] of symbols) {
2700
2706
  const causeFile = yield* pipe(isCauseTypeSourceFile(sourceFile), orElse2(() => void_));
@@ -2789,18 +2795,19 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
2789
2795
  );
2790
2796
  const effectType = cachedBy(
2791
2797
  fn("TypeParser.effectType")(function* (type, atLocation) {
2792
- let result = typeParserIssue("Type has no effect variance struct", type, atLocation);
2793
2798
  const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
2794
- (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration && ts.isPropertySignature(_.valueDeclaration) && ts.isComputedPropertyName(_.valueDeclaration.name)
2799
+ (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
2795
2800
  );
2801
+ if (propertiesSymbols.length === 0) {
2802
+ return yield* typeParserIssue("Type has no effect variance struct", type, atLocation);
2803
+ }
2796
2804
  propertiesSymbols.sort(
2797
2805
  (a, b) => ts.symbolName(b).indexOf("EffectTypeId") - ts.symbolName(a).indexOf("EffectTypeId")
2798
2806
  );
2799
- for (const propertySymbol of propertiesSymbols) {
2807
+ return yield* firstSuccessOf(propertiesSymbols.map((propertySymbol) => {
2800
2808
  const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
2801
- result = pipe(result, orElse2(() => effectVarianceStruct(propertyType, atLocation)));
2802
- }
2803
- return yield* result;
2809
+ return effectVarianceStruct(propertyType, atLocation);
2810
+ }));
2804
2811
  }),
2805
2812
  "TypeParser.effectType",
2806
2813
  (type) => type
@@ -2839,22 +2846,18 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
2839
2846
  fn("TypeParser.layerType")(function* (type, atLocation) {
2840
2847
  yield* pipeableType(type, atLocation);
2841
2848
  const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
2842
- (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration && ts.isPropertySignature(_.valueDeclaration) && ts.isComputedPropertyName(_.valueDeclaration.name)
2849
+ (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
2843
2850
  );
2851
+ if (propertiesSymbols.length === 0) {
2852
+ return yield* typeParserIssue("Type has no layer variance struct", type, atLocation);
2853
+ }
2844
2854
  propertiesSymbols.sort(
2845
2855
  (a, b) => ts.symbolName(b).indexOf("LayerTypeId") - ts.symbolName(a).indexOf("LayerTypeId")
2846
2856
  );
2847
- for (const propertySymbol of propertiesSymbols) {
2857
+ return yield* firstSuccessOf(propertiesSymbols.map((propertySymbol) => {
2848
2858
  const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
2849
- const varianceArgs = yield* option(layerVarianceStruct(
2850
- propertyType,
2851
- atLocation
2852
- ));
2853
- if (isSome2(varianceArgs)) {
2854
- return varianceArgs.value;
2855
- }
2856
- }
2857
- return yield* typeParserIssue("Type has no layer variance struct", type, atLocation);
2859
+ return layerVarianceStruct(propertyType, atLocation);
2860
+ }));
2858
2861
  }),
2859
2862
  "TypeParser.layerType",
2860
2863
  (type) => type
@@ -3151,20 +3154,16 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
3151
3154
  const ast = typeChecker.getPropertyOfType(type, "ast");
3152
3155
  if (!ast) return yield* typeParserIssue("Has no 'ast' property", type, atLocation);
3153
3156
  const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
3154
- (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration && ts.isPropertySignature(_.valueDeclaration) && ts.isComputedPropertyName(_.valueDeclaration.name)
3157
+ (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
3155
3158
  );
3159
+ if (propertiesSymbols.length === 0) {
3160
+ return yield* typeParserIssue("Type has no schema variance struct", type, atLocation);
3161
+ }
3156
3162
  propertiesSymbols.sort((a, b) => ts.symbolName(b).indexOf("TypeId") - ts.symbolName(a).indexOf("TypeId"));
3157
- for (const propertySymbol of propertiesSymbols) {
3163
+ return yield* firstSuccessOf(propertiesSymbols.map((propertySymbol) => {
3158
3164
  const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
3159
- const varianceArgs = yield* option(effectSchemaVarianceStruct(
3160
- propertyType,
3161
- atLocation
3162
- ));
3163
- if (isSome2(varianceArgs)) {
3164
- return varianceArgs.value;
3165
- }
3166
- }
3167
- return yield* typeParserIssue("Type has no schema variance struct", type, atLocation);
3165
+ return effectSchemaVarianceStruct(propertyType, atLocation);
3166
+ }));
3168
3167
  }),
3169
3168
  "TypeParser.effectSchemaType",
3170
3169
  (type) => type
@@ -3200,33 +3199,54 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
3200
3199
  fn("TypeParser.contextTag")(function* (type, atLocation) {
3201
3200
  yield* pipeableType(type, atLocation);
3202
3201
  const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
3203
- (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration && ts.isPropertySignature(_.valueDeclaration) && ts.isComputedPropertyName(_.valueDeclaration.name)
3202
+ (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
3204
3203
  );
3204
+ if (propertiesSymbols.length === 0) {
3205
+ return yield* typeParserIssue("Type has no tag variance struct", type, atLocation);
3206
+ }
3205
3207
  propertiesSymbols.sort((a, b) => ts.symbolName(b).indexOf("TypeId") - ts.symbolName(a).indexOf("TypeId"));
3206
- for (const propertySymbol of propertiesSymbols) {
3208
+ return yield* firstSuccessOf(propertiesSymbols.map((propertySymbol) => {
3207
3209
  const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
3208
- const varianceArgs = yield* option(contextTagVarianceStruct(
3209
- propertyType,
3210
- atLocation
3211
- ));
3212
- if (isSome2(varianceArgs)) {
3213
- return varianceArgs.value;
3214
- }
3215
- }
3216
- return yield* typeParserIssue("Type has no tag variance struct", type, atLocation);
3210
+ return contextTagVarianceStruct(propertyType, atLocation);
3211
+ }));
3217
3212
  }),
3218
3213
  "TypeParser.contextTag",
3219
3214
  (type) => type
3220
3215
  );
3216
+ const effectFunctionImportedName = cachedBy(
3217
+ fn("TypeParser.effectFunctionImportedName")(function* (sourceFile) {
3218
+ return tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Function");
3219
+ }),
3220
+ "TypeParser.effectFunctionImportedName",
3221
+ (node) => node
3222
+ );
3221
3223
  const pipeCall = cachedBy(
3222
3224
  function(node) {
3223
3225
  if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.name) && ts.idText(node.expression.name) === "pipe") {
3224
- return succeed({
3225
- node,
3226
- subject: node.expression.expression,
3227
- args: Array.from(node.arguments),
3228
- kind: "pipeable"
3229
- });
3226
+ const baseExpression = node.expression.expression;
3227
+ return pipe(
3228
+ effectFunctionImportedName(tsUtils.getSourceFileOfNode(node)),
3229
+ flatMap2((functionIdentifier) => {
3230
+ if (functionIdentifier && ts.isIdentifier(baseExpression) && ts.idText(baseExpression) === functionIdentifier) {
3231
+ if (node.arguments.length === 0) {
3232
+ return typeParserIssue("Node is not a pipe call", void 0, node);
3233
+ }
3234
+ const [subject, ...args2] = node.arguments;
3235
+ return succeed({
3236
+ node,
3237
+ subject,
3238
+ args: args2,
3239
+ kind: "pipe"
3240
+ });
3241
+ }
3242
+ return succeed({
3243
+ node,
3244
+ subject: baseExpression,
3245
+ args: Array.from(node.arguments),
3246
+ kind: "pipeable"
3247
+ });
3248
+ })
3249
+ );
3230
3250
  }
3231
3251
  if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && ts.idText(node.expression) === "pipe" && node.arguments.length > 0) {
3232
3252
  const [subject, ...args2] = node.arguments;
@@ -3241,17 +3261,10 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
3241
3261
  fn("TypeParser.scopeType")(function* (type, atLocation) {
3242
3262
  yield* pipeableType(type, atLocation);
3243
3263
  const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
3244
- (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration && ts.isPropertySignature(_.valueDeclaration) && ts.isComputedPropertyName(_.valueDeclaration.name)
3264
+ (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
3245
3265
  );
3246
- propertiesSymbols.sort(
3247
- (a, b) => ts.symbolName(b).indexOf("ScopeTypeId") - ts.symbolName(a).indexOf("ScopeTypeId")
3248
- );
3249
- for (const propertySymbol of propertiesSymbols) {
3250
- const computedPropertyExpression = propertySymbol.valueDeclaration.name;
3251
- const symbol3 = typeChecker.getSymbolAtLocation(computedPropertyExpression.expression);
3252
- if (symbol3 && ts.symbolName(symbol3) === "ScopeTypeId") {
3253
- return type;
3254
- }
3266
+ if (propertiesSymbols.some((s) => ts.symbolName(s).indexOf("ScopeTypeId") !== -1)) {
3267
+ return type;
3255
3268
  }
3256
3269
  return yield* typeParserIssue("Type has no scope type id", type, atLocation);
3257
3270
  }),
@@ -3757,12 +3770,38 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
3757
3770
  "TypeParser.extendsEffectSqlModelClass",
3758
3771
  (atLocation) => atLocation
3759
3772
  );
3773
+ const isEffectLayerTypeSourceFile = cachedBy(
3774
+ fn("TypeParser.isEffectLayerTypeSourceFile")(function* (sourceFile) {
3775
+ const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
3776
+ if (!moduleSymbol) return yield* typeParserIssue("Node has no symbol", void 0, sourceFile);
3777
+ const layerTypeSymbol = typeChecker.tryGetMemberInModuleExports("Layer", moduleSymbol);
3778
+ if (!layerTypeSymbol) return yield* typeParserIssue("Layer type not found", void 0, sourceFile);
3779
+ const type = typeChecker.getDeclaredTypeOfSymbol(layerTypeSymbol);
3780
+ yield* layerType(type, sourceFile);
3781
+ return sourceFile;
3782
+ }),
3783
+ "TypeParser.isEffectLayerTypeSourceFile",
3784
+ (sourceFile) => sourceFile
3785
+ );
3786
+ const isNodeReferenceToEffectLayerModuleApi = (memberName) => cachedBy(
3787
+ fn("TypeParser.isNodeReferenceToEffectLayerModuleApi")(function* (node) {
3788
+ return yield* isNodeReferenceToExportOfPackageModule(
3789
+ node,
3790
+ "effect",
3791
+ isEffectLayerTypeSourceFile,
3792
+ memberName
3793
+ );
3794
+ }),
3795
+ `TypeParser.isNodeReferenceToEffectLayerModuleApi(${memberName})`,
3796
+ (node) => node
3797
+ );
3760
3798
  return {
3761
3799
  isNodeReferenceToEffectModuleApi,
3762
3800
  isNodeReferenceToEffectSchemaModuleApi,
3763
3801
  isNodeReferenceToEffectDataModuleApi,
3764
3802
  isNodeReferenceToEffectContextModuleApi,
3765
3803
  isNodeReferenceToEffectSqlModelModuleApi,
3804
+ isNodeReferenceToEffectLayerModuleApi,
3766
3805
  effectType,
3767
3806
  strictEffectType,
3768
3807
  layerType,
@@ -3896,6 +3935,100 @@ var anyUnknownInErrorContext = createDiagnostic({
3896
3935
  })
3897
3936
  });
3898
3937
 
3938
+ // src/diagnostics/catchAllToMapError.ts
3939
+ var catchAllToMapError = createDiagnostic({
3940
+ name: "catchAllToMapError",
3941
+ code: 39,
3942
+ description: "Suggests using Effect.mapError instead of Effect.catchAll when the callback only wraps the error with Effect.fail",
3943
+ severity: "suggestion",
3944
+ apply: fn("catchAllToMapError.apply")(function* (sourceFile, report) {
3945
+ const ts = yield* service(TypeScriptApi);
3946
+ const typeParser = yield* service(TypeParser);
3947
+ const getFunctionBody = (node) => {
3948
+ if (ts.isArrowFunction(node)) {
3949
+ return node.body;
3950
+ }
3951
+ if (ts.isFunctionExpression(node)) {
3952
+ return node.body;
3953
+ }
3954
+ return void 0;
3955
+ };
3956
+ const getEffectFailCallInfo = (body) => {
3957
+ return gen(function* () {
3958
+ if (ts.isCallExpression(body)) {
3959
+ const isFailCall = yield* pipe(
3960
+ typeParser.isNodeReferenceToEffectModuleApi("fail")(body.expression),
3961
+ option
3962
+ );
3963
+ if (isSome2(isFailCall) && body.arguments.length >= 1) {
3964
+ return some2({ failCall: body, failArg: body.arguments[0] });
3965
+ }
3966
+ }
3967
+ if (ts.isBlock(body)) {
3968
+ const statements = body.statements;
3969
+ if (statements.length === 1) {
3970
+ const stmt = statements[0];
3971
+ if (ts.isReturnStatement(stmt) && stmt.expression && ts.isCallExpression(stmt.expression)) {
3972
+ const isFailCall = yield* pipe(
3973
+ typeParser.isNodeReferenceToEffectModuleApi("fail")(stmt.expression.expression),
3974
+ option
3975
+ );
3976
+ if (isSome2(isFailCall) && stmt.expression.arguments.length >= 1) {
3977
+ return some2({ failCall: stmt.expression, failArg: stmt.expression.arguments[0] });
3978
+ }
3979
+ }
3980
+ }
3981
+ }
3982
+ return none2();
3983
+ });
3984
+ };
3985
+ const nodeToVisit = [];
3986
+ const appendNodeToVisit = (node) => {
3987
+ nodeToVisit.push(node);
3988
+ return void 0;
3989
+ };
3990
+ ts.forEachChild(sourceFile, appendNodeToVisit);
3991
+ while (nodeToVisit.length > 0) {
3992
+ const node = nodeToVisit.shift();
3993
+ ts.forEachChild(node, appendNodeToVisit);
3994
+ if (ts.isCallExpression(node)) {
3995
+ const isCatchAllCall = yield* pipe(
3996
+ typeParser.isNodeReferenceToEffectModuleApi("catchAll")(node.expression),
3997
+ option
3998
+ );
3999
+ if (isSome2(isCatchAllCall)) {
4000
+ const callback = node.arguments[0];
4001
+ if (!callback) continue;
4002
+ const functionBody = getFunctionBody(callback);
4003
+ if (!functionBody) continue;
4004
+ const failCallInfo = yield* getEffectFailCallInfo(functionBody);
4005
+ if (isNone2(failCallInfo)) continue;
4006
+ const { failArg, failCall } = failCallInfo.value;
4007
+ report({
4008
+ location: node.expression,
4009
+ messageText: `You can use Effect.mapError instead of Effect.catchAll + Effect.fail to transform the error type.`,
4010
+ fixes: [{
4011
+ fixName: "catchAllToMapError_fix",
4012
+ description: "Replace with Effect.mapError",
4013
+ apply: gen(function* () {
4014
+ const changeTracker = yield* service(ChangeTracker);
4015
+ if (ts.isPropertyAccessExpression(node.expression)) {
4016
+ changeTracker.replaceNode(
4017
+ sourceFile,
4018
+ node.expression.name,
4019
+ ts.factory.createIdentifier("mapError")
4020
+ );
4021
+ }
4022
+ changeTracker.replaceNode(sourceFile, failCall, failArg);
4023
+ })
4024
+ }]
4025
+ });
4026
+ }
4027
+ }
4028
+ }
4029
+ })
4030
+ });
4031
+
3899
4032
  // src/diagnostics/catchUnfailableEffect.ts
3900
4033
  var catchUnfailableEffect = createDiagnostic({
3901
4034
  name: "catchUnfailableEffect",
@@ -4458,6 +4591,65 @@ var genericEffectServices = createDiagnostic({
4458
4591
  })
4459
4592
  });
4460
4593
 
4594
+ // src/diagnostics/globalErrorInEffectCatch.ts
4595
+ var globalErrorInEffectCatch = createDiagnostic({
4596
+ name: "globalErrorInEffectCatch",
4597
+ code: 36,
4598
+ description: "Warns when catch callbacks return global Error type instead of typed errors",
4599
+ severity: "warning",
4600
+ apply: fn("globalErrorInEffectCatch.apply")(function* (sourceFile, report) {
4601
+ const ts = yield* service(TypeScriptApi);
4602
+ const typeParser = yield* service(TypeParser);
4603
+ const typeChecker = yield* service(TypeCheckerApi);
4604
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
4605
+ const nodeToVisit = [];
4606
+ const appendNodeToVisit = (node) => {
4607
+ nodeToVisit.push(node);
4608
+ return void 0;
4609
+ };
4610
+ ts.forEachChild(sourceFile, appendNodeToVisit);
4611
+ while (nodeToVisit.length > 0) {
4612
+ const node = nodeToVisit.shift();
4613
+ ts.forEachChild(node, appendNodeToVisit);
4614
+ if (ts.isCallExpression(node)) {
4615
+ const isEffectWithCatch = yield* pipe(
4616
+ typeParser.isNodeReferenceToEffectModuleApi("tryPromise")(node.expression),
4617
+ orElse2(() => typeParser.isNodeReferenceToEffectModuleApi("try")(node.expression)),
4618
+ orElse2(() => typeParser.isNodeReferenceToEffectModuleApi("tryMap")(node.expression)),
4619
+ orElse2(() => typeParser.isNodeReferenceToEffectModuleApi("tryMapPromise")(node.expression)),
4620
+ orElse2(() => void_)
4621
+ );
4622
+ if (isEffectWithCatch) {
4623
+ const signature = typeChecker.getResolvedSignature(node);
4624
+ if (signature) {
4625
+ const objectType = typeChecker.getParameterType(signature, 0);
4626
+ const catchFunctionSymbol = typeChecker.getPropertyOfType(objectType, "catch");
4627
+ if (catchFunctionSymbol) {
4628
+ const catchFunctionType = typeChecker.getTypeOfSymbolAtLocation(catchFunctionSymbol, node);
4629
+ const signatures = typeChecker.getSignaturesOfType(catchFunctionType, ts.SignatureKind.Call);
4630
+ if (signatures.length > 0) {
4631
+ const returnType = typeChecker.getReturnTypeOfSignature(signatures[0]);
4632
+ if (returnType && typeCheckerUtils.isGlobalErrorType(returnType)) {
4633
+ const nodeText = sourceFile.text.substring(
4634
+ ts.getTokenPosOfNode(node.expression, sourceFile),
4635
+ node.expression.end
4636
+ );
4637
+ report({
4638
+ location: node.expression,
4639
+ 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.
4640
+ Instead, use tagged errors or custom errors with a discriminator property to get properly type-checked errors.`,
4641
+ fixes: []
4642
+ });
4643
+ }
4644
+ }
4645
+ }
4646
+ }
4647
+ }
4648
+ }
4649
+ }
4650
+ })
4651
+ });
4652
+
4461
4653
  // src/diagnostics/globalErrorInEffectFailure.ts
4462
4654
  var globalErrorInEffectFailure = createDiagnostic({
4463
4655
  name: "globalErrorInEffectFailure",
@@ -4488,7 +4680,7 @@ var globalErrorInEffectFailure = createDiagnostic({
4488
4680
  return sync(
4489
4681
  () => report({
4490
4682
  location: node,
4491
- 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.`,
4683
+ 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.`,
4492
4684
  fixes: []
4493
4685
  })
4494
4686
  );
@@ -4643,6 +4835,119 @@ var importFromBarrel = createDiagnostic({
4643
4835
  })
4644
4836
  });
4645
4837
 
4838
+ // src/diagnostics/layerMergeAllWithDependencies.ts
4839
+ var layerMergeAllWithDependencies = createDiagnostic({
4840
+ name: "layerMergeAllWithDependencies",
4841
+ code: 37,
4842
+ description: "Detects interdependencies in Layer.mergeAll calls where one layer provides a service that another layer requires",
4843
+ severity: "warning",
4844
+ apply: fn("layerMergeAllWithDependencies.apply")(function* (sourceFile, report) {
4845
+ const ts = yield* service(TypeScriptApi);
4846
+ const typeChecker = yield* service(TypeCheckerApi);
4847
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
4848
+ const typeParser = yield* service(TypeParser);
4849
+ const tsUtils = yield* service(TypeScriptUtils);
4850
+ const layerModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(
4851
+ sourceFile,
4852
+ "effect",
4853
+ "Layer"
4854
+ ) || "Layer";
4855
+ const nodeToVisit = [];
4856
+ const appendNodeToVisit = (node) => {
4857
+ nodeToVisit.push(node);
4858
+ return void 0;
4859
+ };
4860
+ ts.forEachChild(sourceFile, appendNodeToVisit);
4861
+ while (nodeToVisit.length > 0) {
4862
+ const node = nodeToVisit.shift();
4863
+ if (ts.isCallExpression(node)) {
4864
+ const checkLayerMergeAll = yield* pipe(
4865
+ typeParser.isNodeReferenceToEffectLayerModuleApi("mergeAll")(node.expression),
4866
+ orElse2(() => void_)
4867
+ );
4868
+ if (checkLayerMergeAll) {
4869
+ const layerArgs = node.arguments;
4870
+ if (layerArgs.length > 1) {
4871
+ const layerInfos = [];
4872
+ const actuallyProvidedMap = /* @__PURE__ */ new Map();
4873
+ for (const arg of layerArgs) {
4874
+ const argType = typeCheckerUtils.getTypeAtLocation(arg);
4875
+ if (!argType) continue;
4876
+ const layerTypeParsedOption = yield* option(typeParser.layerType(argType, arg));
4877
+ if (isNone2(layerTypeParsedOption)) continue;
4878
+ const layerTypeParsed = layerTypeParsedOption.value;
4879
+ const providedMembers = typeCheckerUtils.unrollUnionMembers(layerTypeParsed.ROut);
4880
+ for (const providedType of providedMembers) {
4881
+ if (providedType.flags & ts.TypeFlags.Never) continue;
4882
+ const isPassThrough = typeChecker.isTypeAssignableTo(providedType, layerTypeParsed.RIn);
4883
+ if (!isPassThrough) {
4884
+ actuallyProvidedMap.set(providedType, arg);
4885
+ }
4886
+ }
4887
+ layerInfos.push({
4888
+ arg,
4889
+ requirementsType: layerTypeParsed.RIn
4890
+ });
4891
+ }
4892
+ const providerToConsumers = /* @__PURE__ */ new Map();
4893
+ for (const layer of layerInfos) {
4894
+ for (const [providedType, providerArg] of actuallyProvidedMap) {
4895
+ if (providerArg === layer.arg) continue;
4896
+ if (typeChecker.isTypeAssignableTo(providedType, layer.requirementsType)) {
4897
+ const consumers = providerToConsumers.get(providerArg) || [];
4898
+ consumers.push({ consumer: layer.arg, providedType });
4899
+ providerToConsumers.set(providerArg, consumers);
4900
+ }
4901
+ }
4902
+ }
4903
+ for (const [providerArg, consumers] of providerToConsumers) {
4904
+ const providedTypes = Array.from(new Set(consumers.map((c) => typeChecker.typeToString(c.providedType)))).join(", ");
4905
+ report({
4906
+ location: providerArg,
4907
+ 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.`,
4908
+ fixes: [{
4909
+ fixName: "layerMergeAllWithDependencies_fix",
4910
+ description: "Move layer to Layer.provideMerge",
4911
+ apply: gen(function* () {
4912
+ const changeTracker = yield* service(ChangeTracker);
4913
+ const providerIndex = layerArgs.indexOf(providerArg);
4914
+ if (providerIndex === -1) return;
4915
+ const providerArgNode = providerArg;
4916
+ if (providerIndex === 0 && layerArgs.length > 1) {
4917
+ changeTracker.deleteRange(sourceFile, {
4918
+ pos: providerArgNode.pos,
4919
+ end: layerArgs[1].pos
4920
+ });
4921
+ } else if (providerIndex > 0) {
4922
+ changeTracker.deleteRange(sourceFile, {
4923
+ pos: layerArgs[providerIndex - 1].end,
4924
+ end: providerArgNode.end
4925
+ });
4926
+ }
4927
+ const provideMergeCall = ts.factory.createCallExpression(
4928
+ ts.factory.createPropertyAccessExpression(
4929
+ ts.factory.createIdentifier(layerModuleIdentifier),
4930
+ ts.factory.createIdentifier("provideMerge")
4931
+ ),
4932
+ void 0,
4933
+ [providerArgNode]
4934
+ );
4935
+ changeTracker.insertNodeAt(sourceFile, node.end, provideMergeCall, {
4936
+ prefix: ".pipe("
4937
+ });
4938
+ changeTracker.insertText(sourceFile, node.end, ")");
4939
+ })
4940
+ }]
4941
+ });
4942
+ }
4943
+ }
4944
+ }
4945
+ }
4946
+ ts.forEachChild(node, appendNodeToVisit);
4947
+ }
4948
+ })
4949
+ });
4950
+
4646
4951
  // src/diagnostics/leakingRequirements.ts
4647
4952
  var leakingRequirements = createDiagnostic({
4648
4953
  name: "leakingRequirements",
@@ -4741,6 +5046,7 @@ var leakingRequirements = createDiagnostic({
4741
5046
  location: node,
4742
5047
  messageText: `This Service is leaking the ${requirements.map((_) => typeChecker.typeToString(_)).join(" | ")} requirement.
4743
5048
  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.
5049
+ Services should usually be collected in the layer creation body, and then provided at each method that requires them.
4744
5050
  More info at https://effect.website/docs/requirements-management/layers/#avoiding-requirement-leakage`,
4745
5051
  fixes: []
4746
5052
  });
@@ -5150,6 +5456,55 @@ var missingEffectServiceDependency = createDiagnostic({
5150
5456
  })
5151
5457
  });
5152
5458
 
5459
+ // src/diagnostics/missingLayerContext.ts
5460
+ var missingLayerContext = createDiagnostic({
5461
+ name: "missingLayerContext",
5462
+ code: 38,
5463
+ description: "Reports missing service requirements in Layer context channel",
5464
+ severity: "error",
5465
+ apply: fn("missingLayerContext.apply")(function* (sourceFile, report) {
5466
+ const typeChecker = yield* service(TypeCheckerApi);
5467
+ const typeParser = yield* service(TypeParser);
5468
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
5469
+ const checkForMissingContextTypes = (node, expectedType, valueNode, realType) => pipe(
5470
+ all(
5471
+ typeParser.layerType(expectedType, node),
5472
+ typeParser.layerType(realType, valueNode)
5473
+ ),
5474
+ map4(
5475
+ ([expectedLayer, realLayer]) => typeCheckerUtils.getMissingTypeEntriesInTargetType(
5476
+ realLayer.RIn,
5477
+ expectedLayer.RIn
5478
+ )
5479
+ )
5480
+ );
5481
+ const sortTypes = sort(typeCheckerUtils.deterministicTypeOrder);
5482
+ const entries = getEffectLspPatchSourceFileMetadata(sourceFile)?.relationErrors || typeCheckerUtils.expectedAndRealType(sourceFile);
5483
+ for (const [node, expectedType, valueNode, realType] of entries) {
5484
+ if (expectedType !== realType) {
5485
+ yield* pipe(
5486
+ checkForMissingContextTypes(
5487
+ node,
5488
+ expectedType,
5489
+ valueNode,
5490
+ realType
5491
+ ),
5492
+ map4(
5493
+ (missingTypes) => missingTypes.length > 0 ? report(
5494
+ {
5495
+ location: node,
5496
+ messageText: `Missing '${sortTypes(missingTypes).map((_) => typeChecker.typeToString(_)).join(" | ")}' in the expected Layer context.`,
5497
+ fixes: []
5498
+ }
5499
+ ) : void 0
5500
+ ),
5501
+ ignore
5502
+ );
5503
+ }
5504
+ }
5505
+ })
5506
+ });
5507
+
5153
5508
  // src/diagnostics/missingReturnYieldStar.ts
5154
5509
  var missingReturnYieldStar = createDiagnostic({
5155
5510
  name: "missingReturnYieldStar",
@@ -5687,9 +6042,11 @@ var parse2 = fn("writeTagClassAccessors.parse")(function* (node) {
5687
6042
  const typeParser = yield* service(TypeParser);
5688
6043
  const typeCheckerUtils = yield* service(TypeCheckerUtils);
5689
6044
  if (!ts.isClassDeclaration(node)) return yield* fail("not a class declaration");
5690
- const { Service, accessors: accessors2, className } = yield* pipe(
5691
- typeParser.extendsEffectService(node),
5692
- orElse2(() => map4(typeParser.extendsEffectTag(node), (_) => ({ accessors: true, ..._ }))),
6045
+ const { Service, accessors: accessors2, className, kind } = yield* pipe(
6046
+ map4(typeParser.extendsEffectService(node), (_) => ({ kind: "effectService", ..._ })),
6047
+ orElse2(
6048
+ () => map4(typeParser.extendsEffectTag(node), (_) => ({ kind: "effectTag", accessors: true, ..._ }))
6049
+ ),
5693
6050
  orElse2(() => fail("not a class extending Effect.Service call"))
5694
6051
  );
5695
6052
  if (accessors2 !== true) return yield* fail("accessors are not enabled in the Effect.Service call");
@@ -5711,7 +6068,7 @@ var parse2 = fn("writeTagClassAccessors.parse")(function* (node) {
5711
6068
  const hash2 = involvedMembers.map(({ property, propertyType }) => {
5712
6069
  return ts.symbolName(property) + ": " + typeChecker.typeToString(propertyType);
5713
6070
  }).concat([ts.idText(className)]).join("\n");
5714
- return { Service, className, atLocation: node, hash: cyrb53(hash2), involvedMembers };
6071
+ return { Service, className, atLocation: node, hash: cyrb53(hash2), involvedMembers, kind };
5715
6072
  });
5716
6073
  var writeTagClassAccessors = createRefactor({
5717
6074
  name: "writeTagClassAccessors",
@@ -7636,9 +7993,10 @@ var unsupportedServiceAccessors = createDiagnostic({
7636
7993
  );
7637
7994
  if (missingMembers.length > 0) {
7638
7995
  const memberNames = missingMembers.map(({ property }) => `'${ts.symbolName(property)}'`).join(", ");
7996
+ const suggestedFix = parseResult.kind === "effectTag" ? "\nEffect.Tag does not allow to disable accessors, so you may want to use Context.Tag instead." : "";
7639
7997
  report({
7640
7998
  location: parseResult.className,
7641
- messageText: `Even if accessors are enabled, accessors for ${memberNames} won't be available because the signature have generic type parameters or multiple call signatures.`,
7999
+ 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}`,
7642
8000
  fixes: [{
7643
8001
  fixName: "unsupportedServiceAccessors_enableCodegen",
7644
8002
  description: "Enable accessors codegen",
@@ -7659,6 +8017,7 @@ var unsupportedServiceAccessors = createDiagnostic({
7659
8017
  // src/diagnostics.ts
7660
8018
  var diagnostics = [
7661
8019
  anyUnknownInErrorContext,
8020
+ catchAllToMapError,
7662
8021
  catchUnfailableEffect,
7663
8022
  classSelfMismatch,
7664
8023
  duplicatePackage,
@@ -7666,6 +8025,7 @@ var diagnostics = [
7666
8025
  missingEffectContext,
7667
8026
  missingEffectError,
7668
8027
  missingEffectServiceDependency,
8028
+ missingLayerContext,
7669
8029
  floatingEffect,
7670
8030
  missingStarInYieldEffectGen,
7671
8031
  unnecessaryEffectGen,
@@ -7693,7 +8053,9 @@ var diagnostics = [
7693
8053
  runEffectInsideEffect,
7694
8054
  schemaUnionOfLiterals,
7695
8055
  schemaStructWithTag,
7696
- globalErrorInEffectFailure
8056
+ globalErrorInEffectCatch,
8057
+ globalErrorInEffectFailure,
8058
+ layerMergeAllWithDependencies
7697
8059
  ];
7698
8060
 
7699
8061
  // src/transform.ts