@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect/language-service",
3
- "version": "0.28.1",
3
+ "version": "0.28.2",
4
4
  "description": "A Language-Service Plugin to Refactor and Diagnostic effect-ts projects",
5
5
  "main": "index.cjs",
6
6
  "bin": {
package/transform.js CHANGED
@@ -3362,6 +3362,110 @@ var missingStarInYieldEffectGen = createDiagnostic({
3362
3362
  })
3363
3363
  });
3364
3364
 
3365
+ // src/diagnostics/multipleEffectProvide.ts
3366
+ var multipleEffectProvide = createDiagnostic({
3367
+ name: "multipleEffectProvide",
3368
+ code: 18,
3369
+ severity: "warning",
3370
+ apply: fn("multipleEffectProvide.apply")(function* (sourceFile, report) {
3371
+ const ts = yield* service(TypeScriptApi);
3372
+ const typeChecker = yield* service(TypeCheckerApi);
3373
+ const typeParser = yield* service(TypeParser);
3374
+ const effectModuleIdentifier = yield* pipe(
3375
+ findImportedModuleIdentifierByPackageAndNameOrBarrel(
3376
+ sourceFile,
3377
+ "effect",
3378
+ "Effect"
3379
+ ),
3380
+ map3((_) => _.text),
3381
+ orElse2(() => succeed("Effect"))
3382
+ );
3383
+ const layerModuleIdentifier = yield* pipe(
3384
+ findImportedModuleIdentifierByPackageAndNameOrBarrel(
3385
+ sourceFile,
3386
+ "effect",
3387
+ "Layer"
3388
+ ),
3389
+ map3((_) => _.text),
3390
+ orElse2(() => succeed("Layer"))
3391
+ );
3392
+ const parseEffectProvideLayer = (node) => {
3393
+ if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.name) && node.expression.name.text === "provide" && node.arguments.length > 0) {
3394
+ const layer = node.arguments[0];
3395
+ const type = typeChecker.getTypeAtLocation(layer);
3396
+ return pipe(
3397
+ typeParser.importedEffectModule(node.expression.expression),
3398
+ flatMap2(() => typeParser.layerType(type, layer)),
3399
+ map3(() => ({ layer, node })),
3400
+ orElse2(() => void_)
3401
+ );
3402
+ }
3403
+ return void_;
3404
+ };
3405
+ const parsePipeCall = (node) => gen(function* () {
3406
+ const { args: args2 } = yield* typeParser.pipeCall(node);
3407
+ let currentChunk = 0;
3408
+ const previousLayers = [[]];
3409
+ for (const pipeArg of args2) {
3410
+ const parsedProvide = yield* parseEffectProvideLayer(pipeArg);
3411
+ if (parsedProvide) {
3412
+ previousLayers[currentChunk].push(parsedProvide);
3413
+ } else {
3414
+ currentChunk++;
3415
+ previousLayers.push([]);
3416
+ }
3417
+ }
3418
+ for (const chunk of previousLayers) {
3419
+ if (chunk.length < 2) continue;
3420
+ report({
3421
+ node: chunk[0].node,
3422
+ 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.",
3423
+ fixes: [{
3424
+ fixName: "multipleEffectProvide_fix",
3425
+ description: "Combine into a single provide",
3426
+ apply: gen(function* () {
3427
+ const changeTracker = yield* service(ChangeTracker);
3428
+ changeTracker.deleteRange(sourceFile, {
3429
+ pos: chunk[0].node.getStart(sourceFile),
3430
+ end: chunk[chunk.length - 1].node.getEnd()
3431
+ });
3432
+ const newNode = ts.factory.createCallExpression(
3433
+ ts.factory.createPropertyAccessExpression(
3434
+ ts.factory.createIdentifier(effectModuleIdentifier),
3435
+ ts.factory.createIdentifier("provide")
3436
+ ),
3437
+ void 0,
3438
+ [ts.factory.createCallExpression(
3439
+ ts.factory.createPropertyAccessExpression(
3440
+ ts.factory.createIdentifier(layerModuleIdentifier),
3441
+ ts.factory.createIdentifier("mergeAll")
3442
+ ),
3443
+ void 0,
3444
+ chunk.map((c) => c.layer)
3445
+ )]
3446
+ );
3447
+ changeTracker.insertNodeAt(sourceFile, chunk[0].node.getStart(sourceFile), newNode);
3448
+ })
3449
+ }]
3450
+ });
3451
+ }
3452
+ });
3453
+ const nodeToVisit = [];
3454
+ const appendNodeToVisit = (node) => {
3455
+ nodeToVisit.push(node);
3456
+ return void 0;
3457
+ };
3458
+ ts.forEachChild(sourceFile, appendNodeToVisit);
3459
+ while (nodeToVisit.length > 0) {
3460
+ const node = nodeToVisit.shift();
3461
+ ts.forEachChild(node, appendNodeToVisit);
3462
+ if (ts.isCallExpression(node)) {
3463
+ yield* pipe(parsePipeCall(node), ignore);
3464
+ }
3465
+ }
3466
+ })
3467
+ });
3468
+
3365
3469
  // src/diagnostics/returnEffectInGen.ts
3366
3470
  var returnEffectInGen = createDiagnostic({
3367
3471
  name: "returnEffectInGen",
@@ -3823,7 +3927,8 @@ var diagnostics = [
3823
3927
  scopeInLayerEffect,
3824
3928
  effectInVoidSuccess,
3825
3929
  unnecessaryPipeChain,
3826
- strictBooleanExpressions
3930
+ strictBooleanExpressions,
3931
+ multipleEffectProvide
3827
3932
  ];
3828
3933
 
3829
3934
  // src/transform.ts