@effect/language-service 0.28.1 → 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
package/cli.js CHANGED
@@ -32291,6 +32291,110 @@ var missingStarInYieldEffectGen = createDiagnostic({
32291
32291
  })
32292
32292
  });
32293
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
+
32294
32398
  // src/diagnostics/returnEffectInGen.ts
32295
32399
  var returnEffectInGen = createDiagnostic({
32296
32400
  name: "returnEffectInGen",
@@ -32752,7 +32856,8 @@ var diagnostics = [
32752
32856
  scopeInLayerEffect,
32753
32857
  effectInVoidSuccess,
32754
32858
  unnecessaryPipeChain,
32755
- strictBooleanExpressions
32859
+ strictBooleanExpressions,
32860
+ multipleEffectProvide
32756
32861
  ];
32757
32862
 
32758
32863
  // src/cli.ts