@effect/language-service 0.55.2 → 0.55.3

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/index.js CHANGED
@@ -909,8 +909,9 @@ var contAll = Symbol.for("Nano.contAll");
909
909
  var NanoYield = Symbol.for("Nano.yield");
910
910
  var args = Symbol.for("Nano.args");
911
911
  var NanoDefectException = class {
912
- constructor(message) {
912
+ constructor(message, lastSpan) {
913
913
  this.message = message;
914
+ this.lastSpan = lastSpan;
914
915
  }
915
916
  _tag = "@effect/language-service/NanoDefectException";
916
917
  };
@@ -968,6 +969,7 @@ var NanoFiber = class {
968
969
  _services = {};
969
970
  _cache = {};
970
971
  _perf = false;
972
+ _lastSpan = "";
971
973
  runLoop(nano) {
972
974
  let current = nano;
973
975
  while (true) {
@@ -998,17 +1000,21 @@ var WithSpanProto = {
998
1000
  [evaluate](fiber) {
999
1001
  const [fa, name] = this[args];
1000
1002
  if (!fiber._perf) return fa;
1003
+ const previousSpan = fiber._lastSpan;
1004
+ fiber._lastSpan = name;
1001
1005
  const start = performance.now();
1002
1006
  timingsCount[name] = (timingsCount[name] || 0) + 1;
1003
1007
  return match(fa, {
1004
1008
  onSuccess: (_) => {
1005
1009
  const end = performance.now();
1006
1010
  timings[name] = (timings[name] || 0) + (end - start);
1011
+ fiber._lastSpan = previousSpan;
1007
1012
  return succeed(_);
1008
1013
  },
1009
1014
  onFailure: (_) => {
1010
1015
  const end = performance.now();
1011
1016
  timings[name] = (timings[name] || 0) + (end - start);
1017
+ fiber._lastSpan = previousSpan;
1012
1018
  return fail(_);
1013
1019
  }
1014
1020
  });
@@ -1019,19 +1025,16 @@ var withSpan = (name) => (fa) => {
1019
1025
  nano[args] = [fa, name];
1020
1026
  return nano;
1021
1027
  };
1022
- var unsafeRun = (nano) => {
1023
- const fiber = new NanoFiber();
1024
- const result = fiber.runLoop(nano);
1025
- if (result._tag === "Success") {
1026
- return right2(result.value);
1027
- }
1028
- return left2(result.value);
1029
- };
1030
1028
  var run = (nano) => {
1029
+ const fiber = new NanoFiber();
1031
1030
  try {
1032
- return unsafeRun(nano);
1031
+ const result = fiber.runLoop(nano);
1032
+ if (result._tag === "Success") {
1033
+ return right2(result.value);
1034
+ }
1035
+ return left2(result.value);
1033
1036
  } catch (e) {
1034
- return left2(new NanoDefectException(e));
1037
+ return left2(new NanoDefectException(e, fiber._lastSpan));
1035
1038
  }
1036
1039
  };
1037
1040
  var OnSuccessProto = {
@@ -1138,7 +1141,7 @@ var ServiceProto = {
1138
1141
  return cont2 ? cont2[contA](value, fiber) : fiber.yieldWith(succeed(value));
1139
1142
  }
1140
1143
  const cont = fiber.getCont(contE);
1141
- return cont ? cont[contE](tag, fiber) : fiber.yieldWith(fail(new NanoDefectException(`Service ${tag.key} not found`)));
1144
+ return cont ? cont[contE](tag, fiber) : fiber.yieldWith(fail(new NanoDefectException(`Service ${tag.key} not found`, fiber._lastSpan)));
1142
1145
  }
1143
1146
  };
1144
1147
  var service = (tag) => {
@@ -1818,6 +1821,30 @@ function makeTypeScriptUtils(ts) {
1818
1821
  }
1819
1822
  return node;
1820
1823
  }
1824
+ function isOuterExpression(node, kinds = ts.OuterExpressionKinds.All) {
1825
+ switch (node.kind) {
1826
+ case ts.SyntaxKind.ParenthesizedExpression:
1827
+ return (kinds & ts.OuterExpressionKinds.Parentheses) !== 0;
1828
+ case ts.SyntaxKind.TypeAssertionExpression:
1829
+ case ts.SyntaxKind.AsExpression:
1830
+ return (kinds & ts.OuterExpressionKinds.TypeAssertions) !== 0;
1831
+ case ts.SyntaxKind.SatisfiesExpression:
1832
+ return (kinds & (ts.OuterExpressionKinds.TypeAssertions | ts.OuterExpressionKinds.Satisfies)) !== 0;
1833
+ case ts.SyntaxKind.ExpressionWithTypeArguments:
1834
+ return (kinds & ts.OuterExpressionKinds.ExpressionsWithTypeArguments) !== 0;
1835
+ case ts.SyntaxKind.NonNullExpression:
1836
+ return (kinds & ts.OuterExpressionKinds.NonNullAssertions) !== 0;
1837
+ case ts.SyntaxKind.PartiallyEmittedExpression:
1838
+ return (kinds & ts.OuterExpressionKinds.PartiallyEmittedExpressions) !== 0;
1839
+ }
1840
+ return false;
1841
+ }
1842
+ function skipOuterExpressions(node, kinds = ts.OuterExpressionKinds.All) {
1843
+ while (isOuterExpression(node, kinds)) {
1844
+ node = node.expression;
1845
+ }
1846
+ return node;
1847
+ }
1821
1848
  return {
1822
1849
  findNodeAtPositionIncludingTrivia,
1823
1850
  parsePackageContentNameAndVersionFromScope,
@@ -1838,7 +1865,9 @@ function makeTypeScriptUtils(ts) {
1838
1865
  createEffectGenCallExpressionWithBlock,
1839
1866
  createReturnYieldStarStatement,
1840
1867
  parseAccessedExpressionForCompletion,
1841
- getSourceFileOfNode
1868
+ getSourceFileOfNode,
1869
+ isOuterExpression,
1870
+ skipOuterExpressions
1842
1871
  };
1843
1872
  }
1844
1873
 
@@ -1938,6 +1967,14 @@ var createDiagnosticExecutor = fn("LSP.createCommentDirectivesProcessor")(
1938
1967
  result = node2;
1939
1968
  return;
1940
1969
  }
1970
+ if (ts.isPropertyAssignment(node2)) {
1971
+ const realStart = ts.getTokenPosOfNode(node2, sourceFile);
1972
+ const starts = sourceFile.getLineStarts().filter((start) => start >= node2.pos && start <= realStart);
1973
+ if (starts.length > 0) {
1974
+ result = node2;
1975
+ return;
1976
+ }
1977
+ }
1941
1978
  if (result) return;
1942
1979
  if (node2.parent) find(node2.parent);
1943
1980
  }
@@ -4122,6 +4159,103 @@ var effectDataClasses = createCompletion({
4122
4159
  })
4123
4160
  });
4124
4161
 
4162
+ // src/diagnostics/anyUnknownInErrorContext.ts
4163
+ var anyUnknownInErrorContext = createDiagnostic({
4164
+ name: "anyUnknownInErrorContext",
4165
+ code: 28,
4166
+ severity: "off",
4167
+ apply: fn("anyUnknownInErrorContext.apply")(function* (sourceFile, report) {
4168
+ const ts = yield* service(TypeScriptApi);
4169
+ const typeChecker = yield* service(TypeCheckerApi);
4170
+ const typeParser = yield* service(TypeParser);
4171
+ const isAnyOrUnknown = (type) => (type.flags & ts.TypeFlags.Any) > 0 || (type.flags & ts.TypeFlags.Unknown) > 0;
4172
+ const matchingNodes = [];
4173
+ const nodeToVisit = [sourceFile];
4174
+ const appendNodeToVisit = (node) => {
4175
+ nodeToVisit.push(node);
4176
+ return void 0;
4177
+ };
4178
+ while (nodeToVisit.length > 0) {
4179
+ const node = nodeToVisit.pop();
4180
+ if (ts.isTypeNode(node)) continue;
4181
+ if (ts.isTypeAliasDeclaration(node)) continue;
4182
+ if (ts.isInterfaceDeclaration(node)) continue;
4183
+ if (ts.isAsExpression(node) && node.type && node.type.kind === ts.SyntaxKind.AnyKeyword) {
4184
+ continue;
4185
+ }
4186
+ if (ts.isParameter(node) || ts.isPropertyDeclaration(node) || ts.isVariableDeclaration(node)) {
4187
+ if (node.type) {
4188
+ const type2 = typeChecker.getTypeAtLocation(node.type);
4189
+ const expectedEffect = yield* pipe(
4190
+ typeParser.strictEffectType(type2, node.type),
4191
+ orElse2(() => void_)
4192
+ );
4193
+ if (expectedEffect) continue;
4194
+ }
4195
+ }
4196
+ ts.forEachChild(node, appendNodeToVisit);
4197
+ if (!ts.isExpression(node)) continue;
4198
+ let type = typeChecker.getTypeAtLocation(node);
4199
+ if (ts.isCallExpression(node)) {
4200
+ const resolvedSignature = typeChecker.getResolvedSignature(node);
4201
+ if (resolvedSignature) {
4202
+ type = typeChecker.getReturnTypeOfSignature(resolvedSignature);
4203
+ }
4204
+ }
4205
+ if (!type) continue;
4206
+ yield* pipe(
4207
+ typeParser.strictEffectType(type, node),
4208
+ map5((effect) => {
4209
+ const { E, R } = effect;
4210
+ const hasAnyUnknownR = isAnyOrUnknown(R);
4211
+ const hasAnyUnknownE = isAnyOrUnknown(E);
4212
+ if (hasAnyUnknownR || hasAnyUnknownE) {
4213
+ const channels = [];
4214
+ if (hasAnyUnknownR) {
4215
+ const typeName = R.flags & ts.TypeFlags.Any ? "any" : "unknown";
4216
+ channels.push(`${typeName} in the requirements channel`);
4217
+ }
4218
+ if (hasAnyUnknownE) {
4219
+ const typeName = E.flags & ts.TypeFlags.Any ? "any" : "unknown";
4220
+ channels.push(`${typeName} in the error channel`);
4221
+ }
4222
+ const nodeStart = ts.getTokenPosOfNode(node, sourceFile);
4223
+ const nodeEnd = node.end;
4224
+ for (let i = matchingNodes.length - 1; i >= 0; i--) {
4225
+ const existing = matchingNodes[i];
4226
+ const existingStart = ts.getTokenPosOfNode(existing.node, sourceFile);
4227
+ const existingEnd = existing.node.end;
4228
+ if (existingStart <= nodeStart && existingEnd >= nodeEnd) {
4229
+ matchingNodes.splice(i, 1);
4230
+ }
4231
+ }
4232
+ const suggestions = [`This Effect has ${channels.join(" and ")} which is not recommended.`];
4233
+ if (hasAnyUnknownR) {
4234
+ suggestions.push(`Only service identifiers should appear in the requirements channel.`);
4235
+ }
4236
+ if (hasAnyUnknownE) {
4237
+ suggestions.push(
4238
+ `Having an unknown or any error type is not useful. Consider instead using specific error types baked by Data.TaggedError for example.`
4239
+ );
4240
+ }
4241
+ channels.push(`If you plan to later on manually cast the type, you can safely disable this diagnostic.`);
4242
+ const messageText = suggestions.join("\n");
4243
+ matchingNodes.push({ messageText, node, type });
4244
+ }
4245
+ }),
4246
+ ignore
4247
+ );
4248
+ }
4249
+ for (const { messageText, node } of matchingNodes) {
4250
+ report({
4251
+ location: node,
4252
+ messageText,
4253
+ fixes: []
4254
+ });
4255
+ }
4256
+ })
4257
+ });
4258
+
4125
4259
  // src/diagnostics/catchUnfailableEffect.ts
4126
4260
  var catchUnfailableEffect = createDiagnostic({
4127
4261
  name: "catchUnfailableEffect",
@@ -7885,6 +8019,26 @@ var overriddenSchemaConstructor = createDiagnostic({
7885
8019
  const ts = yield* service(TypeScriptApi);
7886
8020
  const typeParser = yield* service(TypeParser);
7887
8021
  const typeChecker = yield* service(TypeCheckerApi);
8022
+ function isAllowedConstructor(node) {
8023
+ if (node.body && node.body.statements.length === 1) {
8024
+ const expressionStatement = node.body.statements[0];
8025
+ if (ts.isExpressionStatement(expressionStatement)) {
8026
+ const maybeCallSuper = expressionStatement.expression;
8027
+ if (ts.isCallExpression(maybeCallSuper)) {
8028
+ if (maybeCallSuper.expression.kind === ts.SyntaxKind.SuperKeyword) {
8029
+ const expectedNames = node.parameters.map((_) => _.name).filter(ts.isIdentifier).map((_) => ts.idText(_));
8030
+ if (expectedNames.length === 2 && expectedNames.length === node.parameters.length) {
8031
+ const givenNames = maybeCallSuper.arguments.filter(ts.isIdentifier).map((_) => ts.idText(_));
8032
+ if (givenNames.length === expectedNames.length && givenNames.every((name, index) => name === expectedNames[index])) {
8033
+ return true;
8034
+ }
8035
+ }
8036
+ }
8037
+ }
8038
+ }
8039
+ }
8040
+ return false;
8041
+ }
7888
8042
  const nodeToVisit = [];
7889
8043
  const appendNodeToVisit = (node) => {
7890
8044
  nodeToVisit.push(node);
@@ -7916,6 +8070,9 @@ var overriddenSchemaConstructor = createDiagnostic({
7916
8070
  const members = node.members;
7917
8071
  for (const member of members) {
7918
8072
  if (ts.isConstructorDeclaration(member)) {
8073
+ if (isAllowedConstructor(member)) {
8074
+ continue;
8075
+ }
7919
8076
  const fixAsStaticNew = {
7920
8077
  fixName: "overriddenSchemaConstructor_static",
7921
8078
  description: "Rewrite using the static 'new' pattern",
@@ -8713,6 +8870,7 @@ var unsupportedServiceAccessors = createDiagnostic({
8713
8870
 
8714
8871
  // src/diagnostics.ts
8715
8872
  var diagnostics = [
8873
+ anyUnknownInErrorContext,
8716
8874
  catchUnfailableEffect,
8717
8875
  classSelfMismatch,
8718
8876
  duplicatePackage,
@@ -9828,6 +9986,7 @@ function effectTypeArgs(sourceFile, position, quickInfo2) {
9828
9986
  const ts = yield* service(TypeScriptApi);
9829
9987
  const typeChecker = yield* service(TypeCheckerApi);
9830
9988
  const typeParser = yield* service(TypeParser);
9989
+ const tsUtils = yield* service(TypeScriptUtils);
9831
9990
  const options = yield* service(LanguageServicePluginOptions);
9832
9991
  if (options.quickinfoEffectParameters === "never") return quickInfo2;
9833
9992
  function formatTypeForQuickInfo(channelType, channelName) {
@@ -9855,6 +10014,73 @@ function effectTypeArgs(sourceFile, position, quickInfo2) {
9855
10014
  text: "```ts\n/* " + title + " */\n" + formatTypeForQuickInfo(A, "Success") + "\n" + formatTypeForQuickInfo(E, "Failure") + "\n" + formatTypeForQuickInfo(R, "Requirements") + "\n```\n"
9856
10015
  }];
9857
10016
  }
10017
+ function isRightSideOfPropertyAccess(node2) {
10018
+ return node2.parent && ts.isPropertyAccessExpression(node2.parent) && node2.parent.name === node2;
10019
+ }
10020
+ function isArgumentExpressionOfElementAccess(node2) {
10021
+ return node2.parent && ts.isElementAccessExpression(node2.parent) && node2.parent.argumentExpression === node2;
10022
+ }
10023
+ function isCalleeWorker(node2, pred, calleeSelector, includeElementAccess, skipPastOuterExpressions) {
10024
+ let target = includeElementAccess ? climbPastPropertyOrElementAccess(node2) : climbPastPropertyAccess(node2);
10025
+ if (skipPastOuterExpressions) {
10026
+ target = tsUtils.skipOuterExpressions(target);
10027
+ }
10028
+ return !!target && !!target.parent && pred(target.parent) && calleeSelector(target.parent) === target;
10029
+ }
10030
+ function climbPastPropertyAccess(node2) {
10031
+ return isRightSideOfPropertyAccess(node2) ? node2.parent : node2;
10032
+ }
10033
+ function climbPastPropertyOrElementAccess(node2) {
10034
+ return isRightSideOfPropertyAccess(node2) || isArgumentExpressionOfElementAccess(node2) ? node2.parent : node2;
10035
+ }
10036
+ function selectExpressionOfCallOrNewExpressionOrDecorator(node2) {
10037
+ return node2.expression;
10038
+ }
10039
+ function isCallExpressionTarget(node2, includeElementAccess = false, skipPastOuterExpressions = false) {
10040
+ return isCalleeWorker(
10041
+ node2,
10042
+ ts.isCallExpression,
10043
+ selectExpressionOfCallOrNewExpressionOrDecorator,
10044
+ includeElementAccess,
10045
+ skipPastOuterExpressions
10046
+ );
10047
+ }
10048
+ function isNewExpressionTarget(node2, includeElementAccess = false, skipPastOuterExpressions = false) {
10049
+ return isCalleeWorker(
10050
+ node2,
10051
+ ts.isNewExpression,
10052
+ selectExpressionOfCallOrNewExpressionOrDecorator,
10053
+ includeElementAccess,
10054
+ skipPastOuterExpressions
10055
+ );
10056
+ }
10057
+ function getSignatureForQuickInfo(location) {
10058
+ if (location.parent && location.parent.kind === ts.SyntaxKind.PropertyAccessExpression) {
10059
+ const right3 = location.parent.name;
10060
+ if (right3 === location || right3 && right3.getFullWidth() === 0) {
10061
+ location = location.parent;
10062
+ }
10063
+ }
10064
+ let callExpressionLike;
10065
+ if (ts.isCallOrNewExpression(location)) {
10066
+ callExpressionLike = location;
10067
+ } else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) {
10068
+ callExpressionLike = location.parent;
10069
+ }
10070
+ if (callExpressionLike) {
10071
+ const signature = typeChecker.getResolvedSignature(callExpressionLike);
10072
+ if (signature) {
10073
+ const returnType = typeChecker.getReturnTypeOfSignature(signature);
10074
+ if (returnType) {
10075
+ return {
10076
+ callExpressionLike,
10077
+ location,
10078
+ returnType
10079
+ };
10080
+ }
10081
+ }
10082
+ }
10083
+ }
9858
10084
  function getNodeForQuickInfo(node2) {
9859
10085
  if (ts.isNewExpression(node2.parent) && node2.pos === node2.parent.pos) {
9860
10086
  return node2.parent.expression;
@@ -9875,6 +10101,7 @@ function effectTypeArgs(sourceFile, position, quickInfo2) {
9875
10101
  if (ts.isToken(adjustedNode) && adjustedNode.kind === ts.SyntaxKind.YieldKeyword) {
9876
10102
  if (ts.isYieldExpression(adjustedNode.parent) && adjustedNode.parent.asteriskToken && adjustedNode.parent.expression) {
9877
10103
  return {
10104
+ label: "Effect Type Parameters",
9878
10105
  type: typeChecker.getTypeAtLocation(adjustedNode.parent.expression),
9879
10106
  atLocation: adjustedNode.parent.expression,
9880
10107
  node: adjustedNode.parent,
@@ -9882,7 +10109,18 @@ function effectTypeArgs(sourceFile, position, quickInfo2) {
9882
10109
  };
9883
10110
  }
9884
10111
  }
10112
+ const nodeSignature = getSignatureForQuickInfo(adjustedNode);
10113
+ if (nodeSignature) {
10114
+ return {
10115
+ label: "Returned Effect Type Parameters",
10116
+ type: nodeSignature.returnType,
10117
+ atLocation: nodeSignature.location,
10118
+ node: nodeSignature.callExpressionLike,
10119
+ shouldTry: options.quickinfoEffectParameters === "always" && quickInfo2 ? true : quickInfo2 && ts.displayPartsToString(quickInfo2.displayParts).indexOf("...") > -1
10120
+ };
10121
+ }
9885
10122
  return {
10123
+ label: "Effect Type Parameters",
9886
10124
  type: typeChecker.getTypeAtLocation(adjustedNode),
9887
10125
  atLocation: adjustedNode,
9888
10126
  node: adjustedNode,
@@ -9891,25 +10129,13 @@ function effectTypeArgs(sourceFile, position, quickInfo2) {
9891
10129
  }
9892
10130
  const data = getDataForQuickInfo();
9893
10131
  if (!(data && data.shouldTry)) return quickInfo2;
9894
- const { atLocation, node, type } = data;
10132
+ const { atLocation, label, node, type } = data;
9895
10133
  const effectTypeArgsDocumentation = yield* pipe(
9896
10134
  typeParser.effectType(
9897
10135
  type,
9898
10136
  atLocation
9899
10137
  ),
9900
- map5((_) => makeSymbolDisplayParts("Effect Type Parameters", _.A, _.E, _.R)),
9901
- orElse2(() => {
9902
- const callSignatues = typeChecker.getSignaturesOfType(type, ts.SignatureKind.Call);
9903
- if (callSignatues.length !== 1) return succeed([]);
9904
- const returnType = typeChecker.getReturnTypeOfSignature(callSignatues[0]);
9905
- return pipe(
9906
- typeParser.effectType(
9907
- returnType,
9908
- atLocation
9909
- ),
9910
- map5((_) => makeSymbolDisplayParts("Returned Effect Type Parameters", _.A, _.E, _.R))
9911
- );
9912
- })
10138
+ map5((_) => makeSymbolDisplayParts(label, _.A, _.E, _.R))
9913
10139
  );
9914
10140
  if (!quickInfo2) {
9915
10141
  const start = ts.getTokenPosOfNode(node, sourceFile);