@effect/language-service 0.28.0 → 0.28.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/README.md CHANGED
@@ -45,6 +45,7 @@ And you're done! You'll now be able to use a set of refactors and diagnostics th
45
45
  - Multiple versions of Effect in your project
46
46
  - Warn on leaking requirements in Effect services
47
47
  - Warn on Scope as requirement of a Layer
48
+ - Warn on subsequent `Effect.provide` anti-pattern
48
49
  - Unnecessary usages of `Effect.gen` or `pipe()`
49
50
  - Warn when importing from a barrel file instead of from the module directly
50
51
  - Warn on usage of try/catch inside `Effect.gen` and family
@@ -65,6 +66,7 @@ And you're done! You'll now be able to use a set of refactors and diagnostics th
65
66
  - Transform an async function definition, into an Effect by using Effect.gen.
66
67
  - Transform an async function definition, into an Effect by using Effect.gen, and generating a tagged error for each promise call.
67
68
  - Transform a function returning an Effect.gen into a Effect.fn
69
+ - Implement Service accessors in an `Effect.Service` or `Context.Tag` declaration
68
70
  - Function calls to pipe: Transform a set of function calls to a pipe() call.
69
71
  - Pipe to datafirst: Transform a pipe() call into a series of datafirst function calls (where available).
70
72
  - Toggle return type signature: With a single refactor, adds or removes type annotations from the definition.
package/cli.js CHANGED
@@ -31471,6 +31471,47 @@ function make62(ts2, typeChecker) {
31471
31471
  "TypeParser.scopeType",
31472
31472
  (type2) => type2
31473
31473
  );
31474
+ const promiseLike = cachedBy(
31475
+ function(type2, atLocation) {
31476
+ const thenProperty = type2.getProperty("then");
31477
+ if (!thenProperty) return typeParserIssue("not a promise - missing then property", type2, atLocation);
31478
+ const thenType = typeChecker.getTypeOfSymbolAtLocation(thenProperty, atLocation);
31479
+ if (!thenType) return typeParserIssue("not a promise - missing then property", type2, atLocation);
31480
+ for (const callSignature of thenType.getCallSignatures()) {
31481
+ const parameter = callSignature.parameters[0];
31482
+ if (!parameter) continue;
31483
+ const parameterType = callSignature.getTypeParameterAtPosition(0);
31484
+ if (!parameterType) continue;
31485
+ let callbackCallSignatures = [];
31486
+ let toTest = [parameterType];
31487
+ while (toTest.length > 0) {
31488
+ const type3 = toTest.shift();
31489
+ if (!type3) continue;
31490
+ const callSignatures = type3.getCallSignatures();
31491
+ callbackCallSignatures = callbackCallSignatures.concat(callSignatures);
31492
+ if (type3.isUnion()) {
31493
+ toTest = toTest.concat(type3.types);
31494
+ }
31495
+ }
31496
+ for (const callableType of callbackCallSignatures) {
31497
+ const callbackParameter = callableType.parameters[0];
31498
+ if (!callbackParameter) {
31499
+ continue;
31500
+ }
31501
+ const callbackParameterType = callableType.getTypeParameterAtPosition(0);
31502
+ if (!callbackParameterType) {
31503
+ continue;
31504
+ }
31505
+ return succeed17({
31506
+ type: callbackParameterType
31507
+ });
31508
+ }
31509
+ }
31510
+ return typeParserIssue("not a promise", type2, atLocation);
31511
+ },
31512
+ "TypeParser.promiseLike",
31513
+ (type2) => type2
31514
+ );
31474
31515
  return {
31475
31516
  effectType,
31476
31517
  strictEffectType,
@@ -31486,7 +31527,8 @@ function make62(ts2, typeChecker) {
31486
31527
  contextTag,
31487
31528
  pipeableType,
31488
31529
  pipeCall,
31489
- scopeType
31530
+ scopeType,
31531
+ promiseLike
31490
31532
  };
31491
31533
  }
31492
31534
 
@@ -32249,6 +32291,110 @@ var missingStarInYieldEffectGen = createDiagnostic({
32249
32291
  })
32250
32292
  });
32251
32293
 
32294
+ // src/diagnostics/multipleEffectProvide.ts
32295
+ var multipleEffectProvide = createDiagnostic({
32296
+ name: "multipleEffectProvide",
32297
+ code: 18,
32298
+ severity: "warning",
32299
+ apply: fn("multipleEffectProvide.apply")(function* (sourceFile, report) {
32300
+ const ts2 = yield* service2(TypeScriptApi);
32301
+ const typeChecker = yield* service2(TypeCheckerApi);
32302
+ const typeParser = yield* service2(TypeParser);
32303
+ const effectModuleIdentifier = yield* pipe(
32304
+ findImportedModuleIdentifierByPackageAndNameOrBarrel(
32305
+ sourceFile,
32306
+ "effect",
32307
+ "Effect"
32308
+ ),
32309
+ map33((_) => _.text),
32310
+ orElse14(() => succeed17("Effect"))
32311
+ );
32312
+ const layerModuleIdentifier = yield* pipe(
32313
+ findImportedModuleIdentifierByPackageAndNameOrBarrel(
32314
+ sourceFile,
32315
+ "effect",
32316
+ "Layer"
32317
+ ),
32318
+ map33((_) => _.text),
32319
+ orElse14(() => succeed17("Layer"))
32320
+ );
32321
+ const parseEffectProvideLayer = (node) => {
32322
+ if (ts2.isCallExpression(node) && ts2.isPropertyAccessExpression(node.expression) && ts2.isIdentifier(node.expression.name) && node.expression.name.text === "provide" && node.arguments.length > 0) {
32323
+ const layer12 = node.arguments[0];
32324
+ const type2 = typeChecker.getTypeAtLocation(layer12);
32325
+ return pipe(
32326
+ typeParser.importedEffectModule(node.expression.expression),
32327
+ flatMap18(() => typeParser.layerType(type2, layer12)),
32328
+ map33(() => ({ layer: layer12, node })),
32329
+ orElse14(() => void_8)
32330
+ );
32331
+ }
32332
+ return void_8;
32333
+ };
32334
+ const parsePipeCall = (node) => gen3(function* () {
32335
+ const { args: args3 } = yield* typeParser.pipeCall(node);
32336
+ let currentChunk = 0;
32337
+ const previousLayers = [[]];
32338
+ for (const pipeArg of args3) {
32339
+ const parsedProvide = yield* parseEffectProvideLayer(pipeArg);
32340
+ if (parsedProvide) {
32341
+ previousLayers[currentChunk].push(parsedProvide);
32342
+ } else {
32343
+ currentChunk++;
32344
+ previousLayers.push([]);
32345
+ }
32346
+ }
32347
+ for (const chunk4 of previousLayers) {
32348
+ if (chunk4.length < 2) continue;
32349
+ report({
32350
+ node: chunk4[0].node,
32351
+ messageText: "Calling multiple subsequent times Effect.provide is an anti-pattern and can lead to service lifecycle issues. You should combine the layers and provide them once instead.",
32352
+ fixes: [{
32353
+ fixName: "multipleEffectProvide_fix",
32354
+ description: "Combine into a single provide",
32355
+ apply: gen3(function* () {
32356
+ const changeTracker = yield* service2(ChangeTracker);
32357
+ changeTracker.deleteRange(sourceFile, {
32358
+ pos: chunk4[0].node.getStart(sourceFile),
32359
+ end: chunk4[chunk4.length - 1].node.getEnd()
32360
+ });
32361
+ const newNode = ts2.factory.createCallExpression(
32362
+ ts2.factory.createPropertyAccessExpression(
32363
+ ts2.factory.createIdentifier(effectModuleIdentifier),
32364
+ ts2.factory.createIdentifier("provide")
32365
+ ),
32366
+ void 0,
32367
+ [ts2.factory.createCallExpression(
32368
+ ts2.factory.createPropertyAccessExpression(
32369
+ ts2.factory.createIdentifier(layerModuleIdentifier),
32370
+ ts2.factory.createIdentifier("mergeAll")
32371
+ ),
32372
+ void 0,
32373
+ chunk4.map((c) => c.layer)
32374
+ )]
32375
+ );
32376
+ changeTracker.insertNodeAt(sourceFile, chunk4[0].node.getStart(sourceFile), newNode);
32377
+ })
32378
+ }]
32379
+ });
32380
+ }
32381
+ });
32382
+ const nodeToVisit = [];
32383
+ const appendNodeToVisit = (node) => {
32384
+ nodeToVisit.push(node);
32385
+ return void 0;
32386
+ };
32387
+ ts2.forEachChild(sourceFile, appendNodeToVisit);
32388
+ while (nodeToVisit.length > 0) {
32389
+ const node = nodeToVisit.shift();
32390
+ ts2.forEachChild(node, appendNodeToVisit);
32391
+ if (ts2.isCallExpression(node)) {
32392
+ yield* pipe(parsePipeCall(node), ignore3);
32393
+ }
32394
+ }
32395
+ })
32396
+ });
32397
+
32252
32398
  // src/diagnostics/returnEffectInGen.ts
32253
32399
  var returnEffectInGen = createDiagnostic({
32254
32400
  name: "returnEffectInGen",
@@ -32710,7 +32856,8 @@ var diagnostics = [
32710
32856
  scopeInLayerEffect,
32711
32857
  effectInVoidSuccess,
32712
32858
  unnecessaryPipeChain,
32713
- strictBooleanExpressions
32859
+ strictBooleanExpressions,
32860
+ multipleEffectProvide
32714
32861
  ];
32715
32862
 
32716
32863
  // src/cli.ts