@effect/language-service 0.57.0 → 0.58.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.
@@ -5901,6 +5901,11 @@ var runEffectInsideEffect = createDiagnostic({
5901
5901
  apply: fn("runEffectInsideEffect.apply")(function* (sourceFile, report) {
5902
5902
  const ts = yield* service(TypeScriptApi);
5903
5903
  const typeParser = yield* service(TypeParser);
5904
+ const tsUtils = yield* service(TypeScriptUtils);
5905
+ const parseEffectMethod = (node, methodName) => pipe(
5906
+ typeParser.isNodeReferenceToEffectModuleApi(methodName)(node),
5907
+ map4(() => ({ node, methodName }))
5908
+ );
5904
5909
  const nodeToVisit = [];
5905
5910
  const appendNodeToVisit = (node) => {
5906
5911
  nodeToVisit.push(node);
@@ -5911,11 +5916,12 @@ var runEffectInsideEffect = createDiagnostic({
5911
5916
  const node = nodeToVisit.shift();
5912
5917
  ts.forEachChild(node, appendNodeToVisit);
5913
5918
  if (!ts.isCallExpression(node)) continue;
5919
+ if (node.arguments.length === 0) continue;
5914
5920
  const isEffectRunCall = yield* pipe(
5915
- typeParser.isNodeReferenceToEffectModuleApi("runPromise")(node.expression),
5916
- orElse2(() => typeParser.isNodeReferenceToEffectModuleApi("runSync")(node.expression)),
5917
- orElse2(() => typeParser.isNodeReferenceToEffectModuleApi("runFork")(node.expression)),
5918
- orElse2(() => typeParser.isNodeReferenceToEffectModuleApi("runCallback")(node.expression)),
5921
+ parseEffectMethod(node.expression, "runPromise"),
5922
+ orElse2(() => parseEffectMethod(node.expression, "runSync")),
5923
+ orElse2(() => parseEffectMethod(node.expression, "runFork")),
5924
+ orElse2(() => parseEffectMethod(node.expression, "runCallback")),
5919
5925
  option
5920
5926
  );
5921
5927
  if (isNone2(isEffectRunCall)) continue;
@@ -5935,18 +5941,90 @@ var runEffectInsideEffect = createDiagnostic({
5935
5941
  orElse2(() => typeParser.effectFnGen(possiblyEffectGen)),
5936
5942
  option
5937
5943
  );
5938
- if (isSome2(isInEffectGen)) {
5944
+ if (isSome2(isInEffectGen) && isInEffectGen.value.body.statements.length > 0) {
5939
5945
  const nodeText = sourceFile.text.substring(
5940
5946
  ts.getTokenPosOfNode(node.expression, sourceFile),
5941
5947
  node.expression.end
5942
5948
  );
5943
- const messageText = nodeIntroduceScope && nodeIntroduceScope !== isInEffectGen.value.generatorFunction ? `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
5944
- Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.run* with the extracted runtime instead.` : `Using ${nodeText} inside an Effect is not recommended. Effects inside generators can usually just be yielded.`;
5945
- report({
5946
- location: node.expression,
5947
- messageText,
5948
- fixes: []
5949
- });
5949
+ if (nodeIntroduceScope && nodeIntroduceScope !== isInEffectGen.value.generatorFunction) {
5950
+ const fixAddRuntime = gen(function* () {
5951
+ const changeTracker = yield* service(ChangeTracker);
5952
+ const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
5953
+ const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
5954
+ let runtimeIdentifier = void 0;
5955
+ for (const statement of isInEffectGen.value.generatorFunction.body.statements) {
5956
+ if (ts.isVariableStatement(statement) && statement.declarationList.declarations.length === 1) {
5957
+ const declaration = statement.declarationList.declarations[0];
5958
+ if (declaration.initializer && ts.isYieldExpression(declaration.initializer) && declaration.initializer.asteriskToken && declaration.initializer.expression) {
5959
+ const yieldedExpression = declaration.initializer.expression;
5960
+ if (ts.isCallExpression(yieldedExpression)) {
5961
+ const maybeEffectRuntime = yield* pipe(
5962
+ typeParser.isNodeReferenceToEffectModuleApi("runtime")(yieldedExpression.expression),
5963
+ option
5964
+ );
5965
+ if (isSome2(maybeEffectRuntime) && ts.isIdentifier(declaration.name)) {
5966
+ runtimeIdentifier = ts.idText(declaration.name);
5967
+ }
5968
+ }
5969
+ }
5970
+ }
5971
+ }
5972
+ if (!runtimeIdentifier) {
5973
+ changeTracker.insertNodeAt(
5974
+ sourceFile,
5975
+ isInEffectGen.value.body.statements[0].pos,
5976
+ ts.factory.createVariableStatement(
5977
+ void 0,
5978
+ ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
5979
+ "effectRuntime",
5980
+ void 0,
5981
+ void 0,
5982
+ ts.factory.createYieldExpression(
5983
+ ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
5984
+ ts.factory.createCallExpression(
5985
+ ts.factory.createPropertyAccessExpression(
5986
+ ts.factory.createIdentifier(effectModuleIdentifier),
5987
+ "runtime"
5988
+ ),
5989
+ [ts.factory.createTypeReferenceNode("never")],
5990
+ []
5991
+ )
5992
+ )
5993
+ )], ts.NodeFlags.Const)
5994
+ ),
5995
+ {
5996
+ prefix: "\n",
5997
+ suffix: "\n"
5998
+ }
5999
+ );
6000
+ }
6001
+ changeTracker.deleteRange(sourceFile, {
6002
+ pos: ts.getTokenPosOfNode(node.expression, sourceFile),
6003
+ end: node.arguments[0].pos
6004
+ });
6005
+ changeTracker.insertText(
6006
+ sourceFile,
6007
+ node.arguments[0].pos,
6008
+ `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
6009
+ );
6010
+ });
6011
+ report({
6012
+ location: node.expression,
6013
+ messageText: `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
6014
+ Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`,
6015
+ fixes: [{
6016
+ fixName: "runEffectInsideEffect_fix",
6017
+ description: "Use a runtime to run the Effect",
6018
+ apply: fixAddRuntime
6019
+ }]
6020
+ });
6021
+ } else {
6022
+ report({
6023
+ location: node.expression,
6024
+ messageText: `Using ${nodeText} inside an Effect is not recommended. Effects inside generators can usually just be yielded.`,
6025
+ fixes: []
6026
+ });
6027
+ }
5950
6028
  }
5951
6029
  currentParent = currentParent.parent;
5952
6030
  }