@effect/language-service 0.57.0 → 0.57.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/index.js CHANGED
@@ -8367,6 +8367,11 @@ var runEffectInsideEffect = createDiagnostic({
8367
8367
  apply: fn("runEffectInsideEffect.apply")(function* (sourceFile, report) {
8368
8368
  const ts = yield* service(TypeScriptApi);
8369
8369
  const typeParser = yield* service(TypeParser);
8370
+ const tsUtils = yield* service(TypeScriptUtils);
8371
+ const parseEffectMethod = (node, methodName) => pipe(
8372
+ typeParser.isNodeReferenceToEffectModuleApi(methodName)(node),
8373
+ map5(() => ({ node, methodName }))
8374
+ );
8370
8375
  const nodeToVisit = [];
8371
8376
  const appendNodeToVisit = (node) => {
8372
8377
  nodeToVisit.push(node);
@@ -8377,11 +8382,12 @@ var runEffectInsideEffect = createDiagnostic({
8377
8382
  const node = nodeToVisit.shift();
8378
8383
  ts.forEachChild(node, appendNodeToVisit);
8379
8384
  if (!ts.isCallExpression(node)) continue;
8385
+ if (node.arguments.length === 0) continue;
8380
8386
  const isEffectRunCall = yield* pipe(
8381
- typeParser.isNodeReferenceToEffectModuleApi("runPromise")(node.expression),
8382
- orElse2(() => typeParser.isNodeReferenceToEffectModuleApi("runSync")(node.expression)),
8383
- orElse2(() => typeParser.isNodeReferenceToEffectModuleApi("runFork")(node.expression)),
8384
- orElse2(() => typeParser.isNodeReferenceToEffectModuleApi("runCallback")(node.expression)),
8387
+ parseEffectMethod(node.expression, "runPromise"),
8388
+ orElse2(() => parseEffectMethod(node.expression, "runSync")),
8389
+ orElse2(() => parseEffectMethod(node.expression, "runFork")),
8390
+ orElse2(() => parseEffectMethod(node.expression, "runCallback")),
8385
8391
  option
8386
8392
  );
8387
8393
  if (isNone2(isEffectRunCall)) continue;
@@ -8401,18 +8407,90 @@ var runEffectInsideEffect = createDiagnostic({
8401
8407
  orElse2(() => typeParser.effectFnGen(possiblyEffectGen)),
8402
8408
  option
8403
8409
  );
8404
- if (isSome2(isInEffectGen)) {
8410
+ if (isSome2(isInEffectGen) && isInEffectGen.value.body.statements.length > 0) {
8405
8411
  const nodeText = sourceFile.text.substring(
8406
8412
  ts.getTokenPosOfNode(node.expression, sourceFile),
8407
8413
  node.expression.end
8408
8414
  );
8409
- 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.
8410
- 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.`;
8411
- report({
8412
- location: node.expression,
8413
- messageText,
8414
- fixes: []
8415
- });
8415
+ if (nodeIntroduceScope && nodeIntroduceScope !== isInEffectGen.value.generatorFunction) {
8416
+ const fixAddRuntime = gen(function* () {
8417
+ const changeTracker = yield* service(ChangeTracker);
8418
+ const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
8419
+ const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
8420
+ let runtimeIdentifier = void 0;
8421
+ for (const statement of isInEffectGen.value.generatorFunction.body.statements) {
8422
+ if (ts.isVariableStatement(statement) && statement.declarationList.declarations.length === 1) {
8423
+ const declaration = statement.declarationList.declarations[0];
8424
+ if (declaration.initializer && ts.isYieldExpression(declaration.initializer) && declaration.initializer.asteriskToken && declaration.initializer.expression) {
8425
+ const yieldedExpression = declaration.initializer.expression;
8426
+ if (ts.isCallExpression(yieldedExpression)) {
8427
+ const maybeEffectRuntime = yield* pipe(
8428
+ typeParser.isNodeReferenceToEffectModuleApi("runtime")(yieldedExpression.expression),
8429
+ option
8430
+ );
8431
+ if (isSome2(maybeEffectRuntime) && ts.isIdentifier(declaration.name)) {
8432
+ runtimeIdentifier = ts.idText(declaration.name);
8433
+ }
8434
+ }
8435
+ }
8436
+ }
8437
+ }
8438
+ if (!runtimeIdentifier) {
8439
+ changeTracker.insertNodeAt(
8440
+ sourceFile,
8441
+ isInEffectGen.value.body.statements[0].pos,
8442
+ ts.factory.createVariableStatement(
8443
+ void 0,
8444
+ ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
8445
+ "effectRuntime",
8446
+ void 0,
8447
+ void 0,
8448
+ ts.factory.createYieldExpression(
8449
+ ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
8450
+ ts.factory.createCallExpression(
8451
+ ts.factory.createPropertyAccessExpression(
8452
+ ts.factory.createIdentifier(effectModuleIdentifier),
8453
+ "runtime"
8454
+ ),
8455
+ [ts.factory.createTypeReferenceNode("never")],
8456
+ []
8457
+ )
8458
+ )
8459
+ )], ts.NodeFlags.Const)
8460
+ ),
8461
+ {
8462
+ prefix: "\n",
8463
+ suffix: "\n"
8464
+ }
8465
+ );
8466
+ }
8467
+ changeTracker.deleteRange(sourceFile, {
8468
+ pos: ts.getTokenPosOfNode(node.expression, sourceFile),
8469
+ end: node.arguments[0].pos
8470
+ });
8471
+ changeTracker.insertText(
8472
+ sourceFile,
8473
+ node.arguments[0].pos,
8474
+ `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
8475
+ );
8476
+ });
8477
+ report({
8478
+ location: node.expression,
8479
+ messageText: `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
8480
+ Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`,
8481
+ fixes: [{
8482
+ fixName: "runEffectInsideEffect_fix",
8483
+ description: "Use a runtime to run the Effect",
8484
+ apply: fixAddRuntime
8485
+ }]
8486
+ });
8487
+ } else {
8488
+ report({
8489
+ location: node.expression,
8490
+ messageText: `Using ${nodeText} inside an Effect is not recommended. Effects inside generators can usually just be yielded.`,
8491
+ fixes: []
8492
+ });
8493
+ }
8416
8494
  }
8417
8495
  currentParent = currentParent.parent;
8418
8496
  }