@effect/language-service 0.84.0 → 0.84.2

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/index.js CHANGED
@@ -3771,10 +3771,42 @@ function makeTypeCheckerUtils(ts, typeChecker, tsUtils) {
3771
3771
  if (node.parent && ts.isJsxOpeningElement(node.parent) && node.parent.tagName === node) return;
3772
3772
  if (node.parent && ts.isJsxClosingElement(node.parent) && node.parent.tagName === node) return;
3773
3773
  if (node.parent && ts.isJsxAttribute(node.parent) && node.parent.name === node) return;
3774
+ if (isInsideTypeOnlyHeritageExpression(node)) return;
3774
3775
  if (ts.isExpression(node) || ts.isTypeNode(node)) {
3775
3776
  return typeChecker.getTypeAtLocation(node);
3776
3777
  }
3777
3778
  }
3779
+ function isInsideTypeOnlyHeritageExpression(node) {
3780
+ if (ts.isExpressionWithTypeArguments(node)) {
3781
+ return isTypeOnlyHeritageClause(node.parent);
3782
+ }
3783
+ if (!ts.isIdentifier(node) && !ts.isPropertyAccessExpression(node)) {
3784
+ return false;
3785
+ }
3786
+ for (let current = node.parent; current; current = current.parent) {
3787
+ if (ts.isPropertyAccessExpression(current)) {
3788
+ continue;
3789
+ }
3790
+ if (ts.isExpressionWithTypeArguments(current)) {
3791
+ return isTypeOnlyHeritageClause(current.parent);
3792
+ }
3793
+ return false;
3794
+ }
3795
+ return false;
3796
+ }
3797
+ function isTypeOnlyHeritageClause(node) {
3798
+ if (!node || !ts.isHeritageClause(node)) {
3799
+ return false;
3800
+ }
3801
+ const container = node.parent;
3802
+ if (!container) {
3803
+ return false;
3804
+ }
3805
+ if (ts.isInterfaceDeclaration(container)) {
3806
+ return true;
3807
+ }
3808
+ return ts.isClassLike(container) && node.token === ts.SyntaxKind.ImplementsKeyword;
3809
+ }
3778
3810
  function resolveToGlobalSymbol(symbol3) {
3779
3811
  if (symbol3.flags & ts.SymbolFlags.Alias) {
3780
3812
  symbol3 = typeChecker.getAliasedSymbol(symbol3);
@@ -7751,12 +7783,14 @@ var getParameterName = (typescript, name) => {
7751
7783
  }
7752
7784
  return "parameter";
7753
7785
  };
7754
- var hasOuterContextualFunctionType = (typescript, typeChecker, node) => {
7786
+ var hasOuterContextualFunctionType = (typescript, typeChecker, typeCheckerUtils, node) => {
7755
7787
  const contextualType = typeChecker.getContextualType(node);
7756
7788
  if (!contextualType) {
7757
7789
  return false;
7758
7790
  }
7759
- return typeChecker.getSignaturesOfType(contextualType, typescript.SignatureKind.Call).length > 0;
7791
+ return typeCheckerUtils.unrollUnionMembers(contextualType).some(
7792
+ (type) => typeChecker.getSignaturesOfType(type, typescript.SignatureKind.Call).length > 0
7793
+ );
7760
7794
  };
7761
7795
  var effectFnImplicitAny = createDiagnostic({
7762
7796
  name: "effectFnImplicitAny",
@@ -7770,6 +7804,7 @@ var effectFnImplicitAny = createDiagnostic({
7770
7804
  const ts = yield* service(TypeScriptApi);
7771
7805
  const program = yield* service(TypeScriptProgram);
7772
7806
  const typeChecker = yield* service(TypeCheckerApi);
7807
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
7773
7808
  const typeParser = yield* service(TypeParser);
7774
7809
  const noImplicitAny = program.getCompilerOptions().noImplicitAny ?? program.getCompilerOptions().strict ?? false;
7775
7810
  if (!noImplicitAny) {
@@ -7809,7 +7844,7 @@ var effectFnImplicitAny = createDiagnostic({
7809
7844
  ),
7810
7845
  orUndefined
7811
7846
  );
7812
- if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, parsed.call)) {
7847
+ if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, typeCheckerUtils, parsed.call)) {
7813
7848
  continue;
7814
7849
  }
7815
7850
  for (const parameter of parsed.fn.parameters) {
@@ -11904,16 +11939,16 @@ Nested Effect-able types may be intended if you plan to later manually flatten o
11904
11939
  var runEffectInsideEffect = createDiagnostic({
11905
11940
  name: "runEffectInsideEffect",
11906
11941
  code: 32,
11907
- description: "Suggests using Runtime methods instead of Effect.run* inside Effect contexts",
11942
+ description: "Suggests using Runtime or Effect.run*With methods instead of Effect.run* inside Effect contexts",
11908
11943
  group: "antipattern",
11909
11944
  severity: "suggestion",
11910
11945
  fixable: true,
11911
- supportedEffect: ["v3"],
11946
+ supportedEffect: ["v3", "v4"],
11912
11947
  apply: fn("runEffectInsideEffect.apply")(function* (sourceFile, report) {
11913
11948
  const ts = yield* service(TypeScriptApi);
11914
11949
  const typeParser = yield* service(TypeParser);
11915
11950
  const tsUtils = yield* service(TypeScriptUtils);
11916
- if (typeParser.supportedEffect() === "v4") return;
11951
+ const supportedEffect = typeParser.supportedEffect();
11917
11952
  const parseEffectMethod = (node, methodName) => pipe(
11918
11953
  typeParser.isNodeReferenceToEffectModuleApi(methodName)(node),
11919
11954
  map5(() => ({ node, methodName }))
@@ -11946,9 +11981,10 @@ var runEffectInsideEffect = createDiagnostic({
11946
11981
  if (scopeNode && scopeNode !== effectGen.generatorFunction) {
11947
11982
  const fixAddRuntime = gen(function* () {
11948
11983
  const changeTracker = yield* service(ChangeTracker);
11949
- const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
11950
11984
  const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
11985
+ const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
11951
11986
  let runtimeIdentifier = void 0;
11987
+ let servicesIdentifier = void 0;
11952
11988
  for (const statement of effectGen.generatorFunction.body.statements) {
11953
11989
  if (ts.isVariableStatement(statement) && statement.declarationList.declarations.length === 1) {
11954
11990
  const declaration = statement.declarationList.declarations[0];
@@ -11962,11 +11998,46 @@ var runEffectInsideEffect = createDiagnostic({
11962
11998
  if (isSome2(maybeEffectRuntime) && ts.isIdentifier(declaration.name)) {
11963
11999
  runtimeIdentifier = ts.idText(declaration.name);
11964
12000
  }
12001
+ const maybeEffectServices = yield* pipe(
12002
+ typeParser.isNodeReferenceToEffectModuleApi("services")(yieldedExpression.expression),
12003
+ option
12004
+ );
12005
+ if (isSome2(maybeEffectServices) && ts.isIdentifier(declaration.name)) {
12006
+ servicesIdentifier = ts.idText(declaration.name);
12007
+ }
11965
12008
  }
11966
12009
  }
11967
12010
  }
11968
12011
  }
11969
- if (!runtimeIdentifier) {
12012
+ if (supportedEffect === "v4" && !servicesIdentifier) {
12013
+ changeTracker.insertNodeAt(
12014
+ sourceFile,
12015
+ effectGen.body.statements[0].pos,
12016
+ ts.factory.createVariableStatement(
12017
+ void 0,
12018
+ ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
12019
+ "effectServices",
12020
+ void 0,
12021
+ void 0,
12022
+ ts.factory.createYieldExpression(
12023
+ ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
12024
+ ts.factory.createCallExpression(
12025
+ ts.factory.createPropertyAccessExpression(
12026
+ ts.factory.createIdentifier(effectModuleIdentifier),
12027
+ "services"
12028
+ ),
12029
+ [ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)],
12030
+ []
12031
+ )
12032
+ )
12033
+ )], ts.NodeFlags.Const)
12034
+ ),
12035
+ {
12036
+ prefix: "\n",
12037
+ suffix: "\n"
12038
+ }
12039
+ );
12040
+ } else if (supportedEffect === "v3" && !runtimeIdentifier) {
11970
12041
  changeTracker.insertNodeAt(
11971
12042
  sourceFile,
11972
12043
  effectGen.body.statements[0].pos,
@@ -12002,16 +12073,19 @@ var runEffectInsideEffect = createDiagnostic({
12002
12073
  changeTracker.insertText(
12003
12074
  sourceFile,
12004
12075
  node.arguments[0].pos,
12005
- `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
12076
+ supportedEffect === "v4" ? `${effectModuleIdentifier}.${isEffectRunCall.value.methodName}With(${servicesIdentifier || "effectServices"})(` : `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
12006
12077
  );
12007
12078
  });
12079
+ const v4MethodName = `${isEffectRunCall.value.methodName}With`;
12080
+ const messageText = supportedEffect === "v4" ? `Using ${nodeText} inside an Effect is not recommended. The same services should generally be used instead to run child effects.
12081
+ Consider extracting the current services by using for example Effect.services and then use Effect.${v4MethodName} with the extracted services instead.` : `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
12082
+ Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`;
12008
12083
  report({
12009
12084
  location: node.expression,
12010
- messageText: `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
12011
- Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`,
12085
+ messageText,
12012
12086
  fixes: [{
12013
12087
  fixName: "runEffectInsideEffect_fix",
12014
- description: "Use a runtime to run the Effect",
12088
+ description: supportedEffect === "v4" ? "Use the current services to run the Effect" : "Use a runtime to run the Effect",
12015
12089
  apply: fixAddRuntime
12016
12090
  }]
12017
12091
  });