@effect/language-service 0.25.1 → 0.27.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect/language-service",
3
- "version": "0.25.1",
3
+ "version": "0.27.0",
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
@@ -2499,11 +2499,16 @@ function make2(ts, typeChecker) {
2499
2499
  const pipeCall = cachedBy(
2500
2500
  function(node) {
2501
2501
  if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.name) && node.expression.name.text === "pipe") {
2502
- return succeed({ node, subject: node.expression.expression, args: Array.from(node.arguments) });
2502
+ return succeed({
2503
+ node,
2504
+ subject: node.expression.expression,
2505
+ args: Array.from(node.arguments),
2506
+ kind: "pipeable"
2507
+ });
2503
2508
  }
2504
2509
  if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "pipe" && node.arguments.length > 0) {
2505
2510
  const [subject, ...args2] = node.arguments;
2506
- return succeed({ node, subject, args: args2 });
2511
+ return succeed({ node, subject, args: args2, kind: "pipe" });
2507
2512
  }
2508
2513
  return typeParserIssue("Node is not a pipe call", void 0, node);
2509
2514
  },
@@ -2542,6 +2547,7 @@ function make2(ts, typeChecker) {
2542
2547
  unnecessaryEffectGen: unnecessaryEffectGen2,
2543
2548
  effectSchemaType,
2544
2549
  contextTag,
2550
+ pipeableType,
2545
2551
  pipeCall,
2546
2552
  scopeType
2547
2553
  };
@@ -3475,10 +3481,77 @@ Consider using "scoped" instead to get rid of the scope in the requirements.`,
3475
3481
  })
3476
3482
  });
3477
3483
 
3484
+ // src/diagnostics/strictBooleanExpressions.ts
3485
+ var strictBooleanExpressions = createDiagnostic({
3486
+ name: "strictBooleanExpressions",
3487
+ code: 17,
3488
+ severity: "off",
3489
+ apply: fn("strictBooleanExpressions.apply")(function* (sourceFile, report) {
3490
+ const ts = yield* service(TypeScriptApi);
3491
+ const typeChecker = yield* service(TypeCheckerApi);
3492
+ const conditionChecks = /* @__PURE__ */ new WeakMap();
3493
+ const nodeToVisit = [];
3494
+ const appendNodeToVisit = (node) => {
3495
+ nodeToVisit.push(node);
3496
+ return void 0;
3497
+ };
3498
+ ts.forEachChild(sourceFile, appendNodeToVisit);
3499
+ while (nodeToVisit.length > 0) {
3500
+ const node = nodeToVisit.shift();
3501
+ ts.forEachChild(node, appendNodeToVisit);
3502
+ const nodes = [];
3503
+ if (ts.isIfStatement(node)) {
3504
+ conditionChecks.set(node, true);
3505
+ nodes.push(node.expression);
3506
+ } else if (ts.isWhileStatement(node)) {
3507
+ conditionChecks.set(node, true);
3508
+ nodes.push(node.expression);
3509
+ } else if (ts.isConditionalExpression(node)) {
3510
+ conditionChecks.set(node, true);
3511
+ nodes.push(node.condition);
3512
+ } else if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.ExclamationToken) {
3513
+ conditionChecks.set(node, true);
3514
+ nodes.push(node.operand);
3515
+ } else if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.BarBarToken) {
3516
+ if (conditionChecks.has(node.parent)) conditionChecks.set(node, true);
3517
+ nodes.push(node.left);
3518
+ nodes.push(node.right);
3519
+ } else if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken) {
3520
+ if (conditionChecks.has(node.parent)) conditionChecks.set(node, true);
3521
+ nodes.push(node.left);
3522
+ nodes.push(node.right);
3523
+ }
3524
+ for (const nodeToCheck of nodes) {
3525
+ if (!nodeToCheck) continue;
3526
+ if (!conditionChecks.has(nodeToCheck.parent)) continue;
3527
+ const nodeType = typeChecker.getTypeAtLocation(nodeToCheck);
3528
+ const constrainedType = typeChecker.getBaseConstraintOfType(nodeType);
3529
+ let typesToCheck = [constrainedType || nodeType];
3530
+ while (typesToCheck.length > 0) {
3531
+ const type = typesToCheck.pop();
3532
+ if (type.isUnion()) {
3533
+ typesToCheck = typesToCheck.concat(type.types);
3534
+ continue;
3535
+ }
3536
+ if (type.flags & ts.TypeFlags.Boolean) continue;
3537
+ if (type.flags & ts.TypeFlags.Never) continue;
3538
+ if (type.flags & ts.TypeFlags.BooleanLiteral) continue;
3539
+ const typeName = typeChecker.typeToString(type);
3540
+ report({
3541
+ node: nodeToCheck,
3542
+ messageText: `Unexpected \`${typeName}\` type in condition, expected strictly a boolean instead.`,
3543
+ fixes: []
3544
+ });
3545
+ }
3546
+ }
3547
+ }
3548
+ })
3549
+ });
3550
+
3478
3551
  // src/diagnostics/tryCatchInEffectGen.ts
3479
3552
  var tryCatchInEffectGen = createDiagnostic({
3480
3553
  name: "tryCatchInEffectGen",
3481
- code: 12,
3554
+ code: 15,
3482
3555
  severity: "suggestion",
3483
3556
  apply: fn("tryCatchInEffectGen.apply")(function* (sourceFile, report) {
3484
3557
  const ts = yield* service(TypeScriptApi);
@@ -3608,6 +3681,80 @@ var unnecessaryPipe = createDiagnostic({
3608
3681
  })
3609
3682
  });
3610
3683
 
3684
+ // src/diagnostics/unnecessaryPipeChain.ts
3685
+ var unnecessaryPipeChain = createDiagnostic({
3686
+ name: "unnecessaryPipeChain",
3687
+ code: 16,
3688
+ severity: "suggestion",
3689
+ apply: fn("unnecessaryPipeChain.apply")(function* (sourceFile, report) {
3690
+ const ts = yield* service(TypeScriptApi);
3691
+ const typeParser = yield* service(TypeParser);
3692
+ const nodeToVisit = [];
3693
+ const appendNodeToVisit = (node) => {
3694
+ nodeToVisit.push(node);
3695
+ return void 0;
3696
+ };
3697
+ ts.forEachChild(sourceFile, appendNodeToVisit);
3698
+ while (nodeToVisit.length > 0) {
3699
+ const node = nodeToVisit.shift();
3700
+ ts.forEachChild(node, appendNodeToVisit);
3701
+ if (ts.isCallExpression(node)) {
3702
+ yield* pipe(
3703
+ typeParser.pipeCall(node),
3704
+ flatMap2(
3705
+ (pipeCall) => map3(typeParser.pipeCall(pipeCall.subject), (innerCall) => ({ pipeCall, innerCall }))
3706
+ ),
3707
+ map3(({ innerCall, pipeCall }) => {
3708
+ report({
3709
+ node,
3710
+ messageText: `Chained pipe calls can be simplified to a single pipe call`,
3711
+ fixes: [{
3712
+ fixName: "unnecessaryPipeChain_fix",
3713
+ description: "Rewrite as single pipe call",
3714
+ apply: gen(function* () {
3715
+ const changeTracker = yield* service(
3716
+ ChangeTracker
3717
+ );
3718
+ switch (innerCall.kind) {
3719
+ case "pipe": {
3720
+ changeTracker.replaceNode(
3721
+ sourceFile,
3722
+ node,
3723
+ ts.factory.createCallExpression(
3724
+ ts.factory.createIdentifier("pipe"),
3725
+ void 0,
3726
+ [innerCall.subject, ...innerCall.args, ...pipeCall.args]
3727
+ )
3728
+ );
3729
+ break;
3730
+ }
3731
+ case "pipeable": {
3732
+ changeTracker.replaceNode(
3733
+ sourceFile,
3734
+ node,
3735
+ ts.factory.createCallExpression(
3736
+ ts.factory.createPropertyAccessExpression(
3737
+ innerCall.subject,
3738
+ "pipe"
3739
+ ),
3740
+ void 0,
3741
+ [...innerCall.args, ...pipeCall.args]
3742
+ )
3743
+ );
3744
+ break;
3745
+ }
3746
+ }
3747
+ })
3748
+ }]
3749
+ });
3750
+ }),
3751
+ ignore
3752
+ );
3753
+ }
3754
+ }
3755
+ })
3756
+ });
3757
+
3611
3758
  // src/diagnostics.ts
3612
3759
  var diagnostics = [
3613
3760
  duplicatePackage,
@@ -3624,7 +3771,9 @@ var diagnostics = [
3624
3771
  tryCatchInEffectGen,
3625
3772
  importFromBarrel,
3626
3773
  scopeInLayerEffect,
3627
- effectInVoidSuccess
3774
+ effectInVoidSuccess,
3775
+ unnecessaryPipeChain,
3776
+ strictBooleanExpressions
3628
3777
  ];
3629
3778
 
3630
3779
  // src/transform.ts