@effect/language-service 0.55.2 → 0.55.4

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.55.2",
3
+ "version": "0.55.4",
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
@@ -870,8 +870,9 @@ var contAll = Symbol.for("Nano.contAll");
870
870
  var NanoYield = Symbol.for("Nano.yield");
871
871
  var args = Symbol.for("Nano.args");
872
872
  var NanoDefectException = class {
873
- constructor(message) {
873
+ constructor(message, lastSpan) {
874
874
  this.message = message;
875
+ this.lastSpan = lastSpan;
875
876
  }
876
877
  _tag = "@effect/language-service/NanoDefectException";
877
878
  };
@@ -929,6 +930,7 @@ var NanoFiber = class {
929
930
  _services = {};
930
931
  _cache = {};
931
932
  _perf = false;
933
+ _lastSpan = "";
932
934
  runLoop(nano) {
933
935
  let current = nano;
934
936
  while (true) {
@@ -959,17 +961,21 @@ var WithSpanProto = {
959
961
  [evaluate](fiber) {
960
962
  const [fa, name] = this[args];
961
963
  if (!fiber._perf) return fa;
964
+ const previousSpan = fiber._lastSpan;
965
+ fiber._lastSpan = name;
962
966
  const start = performance.now();
963
967
  timingsCount[name] = (timingsCount[name] || 0) + 1;
964
968
  return match(fa, {
965
969
  onSuccess: (_) => {
966
970
  const end = performance.now();
967
971
  timings[name] = (timings[name] || 0) + (end - start);
972
+ fiber._lastSpan = previousSpan;
968
973
  return succeed(_);
969
974
  },
970
975
  onFailure: (_) => {
971
976
  const end = performance.now();
972
977
  timings[name] = (timings[name] || 0) + (end - start);
978
+ fiber._lastSpan = previousSpan;
973
979
  return fail(_);
974
980
  }
975
981
  });
@@ -980,19 +986,16 @@ var withSpan = (name) => (fa) => {
980
986
  nano[args] = [fa, name];
981
987
  return nano;
982
988
  };
983
- var unsafeRun = (nano) => {
984
- const fiber = new NanoFiber();
985
- const result = fiber.runLoop(nano);
986
- if (result._tag === "Success") {
987
- return right2(result.value);
988
- }
989
- return left2(result.value);
990
- };
991
989
  var run = (nano) => {
990
+ const fiber = new NanoFiber();
992
991
  try {
993
- return unsafeRun(nano);
992
+ const result = fiber.runLoop(nano);
993
+ if (result._tag === "Success") {
994
+ return right2(result.value);
995
+ }
996
+ return left2(result.value);
994
997
  } catch (e) {
995
- return left2(new NanoDefectException(e));
998
+ return left2(new NanoDefectException(e, fiber._lastSpan));
996
999
  }
997
1000
  };
998
1001
  var OnSuccessProto = {
@@ -1099,7 +1102,7 @@ var ServiceProto = {
1099
1102
  return cont2 ? cont2[contA](value, fiber) : fiber.yieldWith(succeed(value));
1100
1103
  }
1101
1104
  const cont = fiber.getCont(contE);
1102
- return cont ? cont[contE](tag, fiber) : fiber.yieldWith(fail(new NanoDefectException(`Service ${tag.key} not found`)));
1105
+ return cont ? cont[contE](tag, fiber) : fiber.yieldWith(fail(new NanoDefectException(`Service ${tag.key} not found`, fiber._lastSpan)));
1103
1106
  }
1104
1107
  };
1105
1108
  var service = (tag) => {
@@ -1767,6 +1770,30 @@ function makeTypeScriptUtils(ts) {
1767
1770
  }
1768
1771
  return node;
1769
1772
  }
1773
+ function isOuterExpression(node, kinds = ts.OuterExpressionKinds.All) {
1774
+ switch (node.kind) {
1775
+ case ts.SyntaxKind.ParenthesizedExpression:
1776
+ return (kinds & ts.OuterExpressionKinds.Parentheses) !== 0;
1777
+ case ts.SyntaxKind.TypeAssertionExpression:
1778
+ case ts.SyntaxKind.AsExpression:
1779
+ return (kinds & ts.OuterExpressionKinds.TypeAssertions) !== 0;
1780
+ case ts.SyntaxKind.SatisfiesExpression:
1781
+ return (kinds & (ts.OuterExpressionKinds.TypeAssertions | ts.OuterExpressionKinds.Satisfies)) !== 0;
1782
+ case ts.SyntaxKind.ExpressionWithTypeArguments:
1783
+ return (kinds & ts.OuterExpressionKinds.ExpressionsWithTypeArguments) !== 0;
1784
+ case ts.SyntaxKind.NonNullExpression:
1785
+ return (kinds & ts.OuterExpressionKinds.NonNullAssertions) !== 0;
1786
+ case ts.SyntaxKind.PartiallyEmittedExpression:
1787
+ return (kinds & ts.OuterExpressionKinds.PartiallyEmittedExpressions) !== 0;
1788
+ }
1789
+ return false;
1790
+ }
1791
+ function skipOuterExpressions(node, kinds = ts.OuterExpressionKinds.All) {
1792
+ while (isOuterExpression(node, kinds)) {
1793
+ node = node.expression;
1794
+ }
1795
+ return node;
1796
+ }
1770
1797
  return {
1771
1798
  findNodeAtPositionIncludingTrivia,
1772
1799
  parsePackageContentNameAndVersionFromScope,
@@ -1787,7 +1814,9 @@ function makeTypeScriptUtils(ts) {
1787
1814
  createEffectGenCallExpressionWithBlock,
1788
1815
  createReturnYieldStarStatement,
1789
1816
  parseAccessedExpressionForCompletion,
1790
- getSourceFileOfNode
1817
+ getSourceFileOfNode,
1818
+ isOuterExpression,
1819
+ skipOuterExpressions
1791
1820
  };
1792
1821
  }
1793
1822
 
@@ -1872,6 +1901,14 @@ var createDiagnosticExecutor = fn("LSP.createCommentDirectivesProcessor")(
1872
1901
  result = node2;
1873
1902
  return;
1874
1903
  }
1904
+ if (ts.isPropertyAssignment(node2)) {
1905
+ const realStart = ts.getTokenPosOfNode(node2, sourceFile);
1906
+ const starts = sourceFile.getLineStarts().filter((start) => start >= node2.pos && start <= realStart);
1907
+ if (starts.length > 0) {
1908
+ result = node2;
1909
+ return;
1910
+ }
1911
+ }
1875
1912
  if (result) return;
1876
1913
  if (node2.parent) find(node2.parent);
1877
1914
  }
@@ -2483,6 +2520,7 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
2483
2520
  const getSourceFilesDeclaringSymbolModule = (packageName) => cachedBy(
2484
2521
  fn("TypeParser.getSourceFilesDeclaringSymbolModule")(function* (symbol3) {
2485
2522
  const result = [];
2523
+ if (!symbol3) return result;
2486
2524
  if (!symbol3.declarations) return yield* typeParserIssue("Symbol has no declarations", void 0, void 0);
2487
2525
  for (const sourceFile of symbol3.declarations) {
2488
2526
  if (!ts.isSourceFile(sourceFile)) continue;
@@ -2520,6 +2558,7 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
2520
2558
  const getSourceFilesDeclaringSymbolExportedUnderPackageModule = (packageName, memberName) => cachedBy(
2521
2559
  fn("TypeParser.getSourceFilesDeclaringSymbolUnderPackageExportedMember")(function* (symbol3) {
2522
2560
  const result = [];
2561
+ if (!symbol3) return result;
2523
2562
  if (!symbol3.declarations) return yield* typeParserIssue("Symbol has no declarations", void 0, void 0);
2524
2563
  for (const declaration of symbol3.declarations) {
2525
2564
  const sourceFile = tsUtils.getSourceFileOfNode(declaration);
@@ -2741,14 +2780,14 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
2741
2780
  );
2742
2781
  const importedContextModule = cachedBy(
2743
2782
  fn("TypeParser.importedContextModule")(function* (node) {
2783
+ if (!ts.isIdentifier(node)) {
2784
+ return yield* typeParserIssue("Node is not an identifier", void 0, node);
2785
+ }
2744
2786
  const type = typeChecker.getTypeAtLocation(node);
2745
2787
  const propertySymbol = typeChecker.getPropertyOfType(type, "Tag");
2746
2788
  if (!propertySymbol) {
2747
2789
  return yield* typeParserIssue("Type has no 'Tag' property", type, node);
2748
2790
  }
2749
- if (!ts.isIdentifier(node)) {
2750
- return yield* typeParserIssue("Node is not an identifier", type, node);
2751
- }
2752
2791
  const sourceFile = tsUtils.getSourceFileOfNode(node);
2753
2792
  if (!sourceFile) {
2754
2793
  return yield* typeParserIssue("Node is not in a source file", void 0, node);
@@ -2775,14 +2814,14 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
2775
2814
  );
2776
2815
  const importedDataModule = cachedBy(
2777
2816
  fn("TypeParser.importedDataModule")(function* (node) {
2817
+ if (!ts.isIdentifier(node)) {
2818
+ return yield* typeParserIssue("Node is not an expression", void 0, node);
2819
+ }
2778
2820
  const type = typeChecker.getTypeAtLocation(node);
2779
2821
  const propertySymbol = typeChecker.getPropertyOfType(type, "TaggedError");
2780
2822
  if (!propertySymbol) {
2781
2823
  return yield* typeParserIssue("Type has no 'TaggedError' property", type, node);
2782
2824
  }
2783
- if (!ts.isIdentifier(node)) {
2784
- return yield* typeParserIssue("Node is not an expression", type, node);
2785
- }
2786
2825
  const sourceFile = tsUtils.getSourceFileOfNode(node);
2787
2826
  if (!sourceFile) {
2788
2827
  return yield* typeParserIssue("Node is not in a source file", void 0, node);
@@ -3566,6 +3605,103 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
3566
3605
  };
3567
3606
  }
3568
3607
 
3608
+ // src/diagnostics/anyUnknownInErrorContext.ts
3609
+ var anyUnknownInErrorContext = createDiagnostic({
3610
+ name: "anyUnknownInErrorContext",
3611
+ code: 28,
3612
+ severity: "off",
3613
+ apply: fn("anyUnknownInErrorContext.apply")(function* (sourceFile, report) {
3614
+ const ts = yield* service(TypeScriptApi);
3615
+ const typeChecker = yield* service(TypeCheckerApi);
3616
+ const typeParser = yield* service(TypeParser);
3617
+ const isAnyOrUnknown = (type) => (type.flags & ts.TypeFlags.Any) > 0 || (type.flags & ts.TypeFlags.Unknown) > 0;
3618
+ const matchingNodes = [];
3619
+ const nodeToVisit = [sourceFile];
3620
+ const appendNodeToVisit = (node) => {
3621
+ nodeToVisit.push(node);
3622
+ return void 0;
3623
+ };
3624
+ while (nodeToVisit.length > 0) {
3625
+ const node = nodeToVisit.pop();
3626
+ if (ts.isTypeNode(node)) continue;
3627
+ if (ts.isTypeAliasDeclaration(node)) continue;
3628
+ if (ts.isInterfaceDeclaration(node)) continue;
3629
+ if (ts.isAsExpression(node) && node.type && node.type.kind === ts.SyntaxKind.AnyKeyword) {
3630
+ continue;
3631
+ }
3632
+ if (ts.isParameter(node) || ts.isPropertyDeclaration(node) || ts.isVariableDeclaration(node)) {
3633
+ if (node.type) {
3634
+ const type2 = typeChecker.getTypeAtLocation(node.type);
3635
+ const expectedEffect = yield* pipe(
3636
+ typeParser.strictEffectType(type2, node.type),
3637
+ orElse2(() => void_)
3638
+ );
3639
+ if (expectedEffect) continue;
3640
+ }
3641
+ }
3642
+ ts.forEachChild(node, appendNodeToVisit);
3643
+ if (!ts.isExpression(node)) continue;
3644
+ let type = typeChecker.getTypeAtLocation(node);
3645
+ if (ts.isCallExpression(node)) {
3646
+ const resolvedSignature = typeChecker.getResolvedSignature(node);
3647
+ if (resolvedSignature) {
3648
+ type = typeChecker.getReturnTypeOfSignature(resolvedSignature);
3649
+ }
3650
+ }
3651
+ if (!type) continue;
3652
+ yield* pipe(
3653
+ typeParser.strictEffectType(type, node),
3654
+ map4((effect) => {
3655
+ const { E, R } = effect;
3656
+ const hasAnyUnknownR = isAnyOrUnknown(R);
3657
+ const hasAnyUnknownE = isAnyOrUnknown(E);
3658
+ if (hasAnyUnknownR || hasAnyUnknownE) {
3659
+ const channels = [];
3660
+ if (hasAnyUnknownR) {
3661
+ const typeName = R.flags & ts.TypeFlags.Any ? "any" : "unknown";
3662
+ channels.push(`${typeName} in the requirements channel`);
3663
+ }
3664
+ if (hasAnyUnknownE) {
3665
+ const typeName = E.flags & ts.TypeFlags.Any ? "any" : "unknown";
3666
+ channels.push(`${typeName} in the error channel`);
3667
+ }
3668
+ const nodeStart = ts.getTokenPosOfNode(node, sourceFile);
3669
+ const nodeEnd = node.end;
3670
+ for (let i = matchingNodes.length - 1; i >= 0; i--) {
3671
+ const existing = matchingNodes[i];
3672
+ const existingStart = ts.getTokenPosOfNode(existing.node, sourceFile);
3673
+ const existingEnd = existing.node.end;
3674
+ if (existingStart <= nodeStart && existingEnd >= nodeEnd) {
3675
+ matchingNodes.splice(i, 1);
3676
+ }
3677
+ }
3678
+ const suggestions = [`This Effect has ${channels.join(" and ")} which is not recommended.`];
3679
+ if (hasAnyUnknownR) {
3680
+ suggestions.push(`Only service identifiers should appear in the requirements channel.`);
3681
+ }
3682
+ if (hasAnyUnknownE) {
3683
+ suggestions.push(
3684
+ `Having an unknown or any error type is not useful. Consider instead using specific error types baked by Data.TaggedError for example.`
3685
+ );
3686
+ }
3687
+ channels.push(`If you plan to later on manually cast the type, you can safely disable this diagnostic.`);
3688
+ const messageText = suggestions.join("\n");
3689
+ matchingNodes.push({ messageText, node, type });
3690
+ }
3691
+ }),
3692
+ ignore
3693
+ );
3694
+ }
3695
+ for (const { messageText, node } of matchingNodes) {
3696
+ report({
3697
+ location: node,
3698
+ messageText,
3699
+ fixes: []
3700
+ });
3701
+ }
3702
+ })
3703
+ });
3704
+
3569
3705
  // src/diagnostics/catchUnfailableEffect.ts
3570
3706
  var catchUnfailableEffect = createDiagnostic({
3571
3707
  name: "catchUnfailableEffect",
@@ -4327,9 +4463,10 @@ var leakingRequirements = createDiagnostic({
4327
4463
  (type) => {
4328
4464
  let symbol3 = type.symbol;
4329
4465
  if (symbol3 && symbol3.flags & ts.SymbolFlags.Alias) {
4330
- symbol3 = typeChecker.getAliasedSymbol(symbol3);
4466
+ symbol3 = typeChecker.getAliasedSymbol(symbol3) || symbol3;
4331
4467
  }
4332
- return !(symbol3.declarations || []).some((declaration) => {
4468
+ if (!symbol3) return false;
4469
+ return !(symbol3?.declarations || []).some((declaration) => {
4333
4470
  const declarationSource = tsUtils.getSourceFileOfNode(declaration);
4334
4471
  if (!declarationSource) return false;
4335
4472
  return declarationSource.text.substring(declaration.pos, declaration.end).toLowerCase().indexOf(
@@ -5424,6 +5561,26 @@ var overriddenSchemaConstructor = createDiagnostic({
5424
5561
  const ts = yield* service(TypeScriptApi);
5425
5562
  const typeParser = yield* service(TypeParser);
5426
5563
  const typeChecker = yield* service(TypeCheckerApi);
5564
+ function isAllowedConstructor(node) {
5565
+ if (node.body && node.body.statements.length === 1) {
5566
+ const expressionStatement = node.body.statements[0];
5567
+ if (ts.isExpressionStatement(expressionStatement)) {
5568
+ const maybeCallSuper = expressionStatement.expression;
5569
+ if (ts.isCallExpression(maybeCallSuper)) {
5570
+ if (maybeCallSuper.expression.kind === ts.SyntaxKind.SuperKeyword) {
5571
+ const expectedNames = node.parameters.map((_) => _.name).filter(ts.isIdentifier).map((_) => ts.idText(_));
5572
+ if (expectedNames.length === 2 && expectedNames.length === node.parameters.length) {
5573
+ const givenNames = maybeCallSuper.arguments.filter(ts.isIdentifier).map((_) => ts.idText(_));
5574
+ if (givenNames.length === expectedNames.length && givenNames.every((name, index) => name === expectedNames[index])) {
5575
+ return true;
5576
+ }
5577
+ }
5578
+ }
5579
+ }
5580
+ }
5581
+ }
5582
+ return false;
5583
+ }
5427
5584
  const nodeToVisit = [];
5428
5585
  const appendNodeToVisit = (node) => {
5429
5586
  nodeToVisit.push(node);
@@ -5455,6 +5612,9 @@ var overriddenSchemaConstructor = createDiagnostic({
5455
5612
  const members = node.members;
5456
5613
  for (const member of members) {
5457
5614
  if (ts.isConstructorDeclaration(member)) {
5615
+ if (isAllowedConstructor(member)) {
5616
+ continue;
5617
+ }
5458
5618
  const fixAsStaticNew = {
5459
5619
  fixName: "overriddenSchemaConstructor_static",
5460
5620
  description: "Rewrite using the static 'new' pattern",
@@ -5863,6 +6023,7 @@ var strictBooleanExpressions = createDiagnostic({
5863
6023
  for (const nodeToCheck of nodes) {
5864
6024
  if (!nodeToCheck) continue;
5865
6025
  if (!conditionChecks.has(nodeToCheck.parent)) continue;
6026
+ if (!ts.isExpression(nodeToCheck)) continue;
5866
6027
  const nodeType = typeChecker.getTypeAtLocation(nodeToCheck);
5867
6028
  const constrainedType = typeChecker.getBaseConstraintOfType(nodeType);
5868
6029
  let typesToCheck = [constrainedType || nodeType];
@@ -6252,6 +6413,7 @@ var unsupportedServiceAccessors = createDiagnostic({
6252
6413
 
6253
6414
  // src/diagnostics.ts
6254
6415
  var diagnostics = [
6416
+ anyUnknownInErrorContext,
6255
6417
  catchUnfailableEffect,
6256
6418
  classSelfMismatch,
6257
6419
  duplicatePackage,