@effect/language-service 0.55.1 → 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",
@@ -7169,8 +7303,20 @@ var missedPipeableOpportunity = createDiagnostic({
7169
7303
  while (nodeToVisit.length > 0) {
7170
7304
  const node = nodeToVisit.shift();
7171
7305
  if (ts.isCallExpression(node) && node.arguments.length === 1) {
7172
- const parentChain = callChainNodes.get(node) || [];
7173
- callChainNodes.set(node.arguments[0], parentChain.concat(node));
7306
+ const isPipeCall = yield* pipe(typeParser.pipeCall(node), orElse2(() => void_));
7307
+ if (!isPipeCall) {
7308
+ const resolvedSignature = typeChecker.getResolvedSignature(node);
7309
+ if (resolvedSignature) {
7310
+ const returnType = typeChecker.getReturnTypeOfSignature(resolvedSignature);
7311
+ if (returnType) {
7312
+ const callSignatures = typeChecker.getSignaturesOfType(returnType, ts.SignatureKind.Call);
7313
+ if (callSignatures.length === 0) {
7314
+ const parentChain = callChainNodes.get(node) || [];
7315
+ callChainNodes.set(node.arguments[0], parentChain.concat(node));
7316
+ }
7317
+ }
7318
+ }
7319
+ }
7174
7320
  } else if (callChainNodes.has(node) && ts.isExpression(node)) {
7175
7321
  const parentChain = (callChainNodes.get(node) || []).slice();
7176
7322
  const originalParentChain = parentChain.slice();
@@ -7873,6 +8019,26 @@ var overriddenSchemaConstructor = createDiagnostic({
7873
8019
  const ts = yield* service(TypeScriptApi);
7874
8020
  const typeParser = yield* service(TypeParser);
7875
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
+ }
7876
8042
  const nodeToVisit = [];
7877
8043
  const appendNodeToVisit = (node) => {
7878
8044
  nodeToVisit.push(node);
@@ -7904,6 +8070,9 @@ var overriddenSchemaConstructor = createDiagnostic({
7904
8070
  const members = node.members;
7905
8071
  for (const member of members) {
7906
8072
  if (ts.isConstructorDeclaration(member)) {
8073
+ if (isAllowedConstructor(member)) {
8074
+ continue;
8075
+ }
7907
8076
  const fixAsStaticNew = {
7908
8077
  fixName: "overriddenSchemaConstructor_static",
7909
8078
  description: "Rewrite using the static 'new' pattern",
@@ -8701,6 +8870,7 @@ var unsupportedServiceAccessors = createDiagnostic({
8701
8870
 
8702
8871
  // src/diagnostics.ts
8703
8872
  var diagnostics = [
8873
+ anyUnknownInErrorContext,
8704
8874
  catchUnfailableEffect,
8705
8875
  classSelfMismatch,
8706
8876
  duplicatePackage,
@@ -9816,6 +9986,7 @@ function effectTypeArgs(sourceFile, position, quickInfo2) {
9816
9986
  const ts = yield* service(TypeScriptApi);
9817
9987
  const typeChecker = yield* service(TypeCheckerApi);
9818
9988
  const typeParser = yield* service(TypeParser);
9989
+ const tsUtils = yield* service(TypeScriptUtils);
9819
9990
  const options = yield* service(LanguageServicePluginOptions);
9820
9991
  if (options.quickinfoEffectParameters === "never") return quickInfo2;
9821
9992
  function formatTypeForQuickInfo(channelType, channelName) {
@@ -9843,6 +10014,73 @@ function effectTypeArgs(sourceFile, position, quickInfo2) {
9843
10014
  text: "```ts\n/* " + title + " */\n" + formatTypeForQuickInfo(A, "Success") + "\n" + formatTypeForQuickInfo(E, "Failure") + "\n" + formatTypeForQuickInfo(R, "Requirements") + "\n```\n"
9844
10015
  }];
9845
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
+ }
9846
10084
  function getNodeForQuickInfo(node2) {
9847
10085
  if (ts.isNewExpression(node2.parent) && node2.pos === node2.parent.pos) {
9848
10086
  return node2.parent.expression;
@@ -9863,6 +10101,7 @@ function effectTypeArgs(sourceFile, position, quickInfo2) {
9863
10101
  if (ts.isToken(adjustedNode) && adjustedNode.kind === ts.SyntaxKind.YieldKeyword) {
9864
10102
  if (ts.isYieldExpression(adjustedNode.parent) && adjustedNode.parent.asteriskToken && adjustedNode.parent.expression) {
9865
10103
  return {
10104
+ label: "Effect Type Parameters",
9866
10105
  type: typeChecker.getTypeAtLocation(adjustedNode.parent.expression),
9867
10106
  atLocation: adjustedNode.parent.expression,
9868
10107
  node: adjustedNode.parent,
@@ -9870,7 +10109,18 @@ function effectTypeArgs(sourceFile, position, quickInfo2) {
9870
10109
  };
9871
10110
  }
9872
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
+ }
9873
10122
  return {
10123
+ label: "Effect Type Parameters",
9874
10124
  type: typeChecker.getTypeAtLocation(adjustedNode),
9875
10125
  atLocation: adjustedNode,
9876
10126
  node: adjustedNode,
@@ -9879,25 +10129,13 @@ function effectTypeArgs(sourceFile, position, quickInfo2) {
9879
10129
  }
9880
10130
  const data = getDataForQuickInfo();
9881
10131
  if (!(data && data.shouldTry)) return quickInfo2;
9882
- const { atLocation, node, type } = data;
10132
+ const { atLocation, label, node, type } = data;
9883
10133
  const effectTypeArgsDocumentation = yield* pipe(
9884
10134
  typeParser.effectType(
9885
10135
  type,
9886
10136
  atLocation
9887
10137
  ),
9888
- map5((_) => makeSymbolDisplayParts("Effect Type Parameters", _.A, _.E, _.R)),
9889
- orElse2(() => {
9890
- const callSignatues = typeChecker.getSignaturesOfType(type, ts.SignatureKind.Call);
9891
- if (callSignatues.length !== 1) return succeed([]);
9892
- const returnType = typeChecker.getReturnTypeOfSignature(callSignatues[0]);
9893
- return pipe(
9894
- typeParser.effectType(
9895
- returnType,
9896
- atLocation
9897
- ),
9898
- map5((_) => makeSymbolDisplayParts("Returned Effect Type Parameters", _.A, _.E, _.R))
9899
- );
9900
- })
10138
+ map5((_) => makeSymbolDisplayParts(label, _.A, _.E, _.R))
9901
10139
  );
9902
10140
  if (!quickInfo2) {
9903
10141
  const start = ts.getTokenPosOfNode(node, sourceFile);