@effect/language-service 0.56.0 → 0.57.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/README.md CHANGED
@@ -72,6 +72,7 @@ And you're done! You'll now be able to use a set of refactors and diagnostics th
72
72
  - Warn when catch callbacks in `Effect.tryPromise`, `Effect.tryMap`, or `Effect.tryMapPromise` return `unknown` or `any` types
73
73
  - Warn when using `Effect.runSync`, `Effect.runPromise`, `Effect.runFork`, or `Effect.runCallback` inside an Effect
74
74
  - Warn when using `Schema.Union` with multiple `Schema.Literal` calls that can be simplified to a single `Schema.Literal` call
75
+ - Warn when using `yield* Effect.fail()` with yieldable error types that can be yielded directly
75
76
 
76
77
  ### Completions
77
78
 
@@ -99,6 +100,11 @@ And you're done! You'll now be able to use a set of refactors and diagnostics th
99
100
  - Toggle between pipe styles `X.pipe(Y)` and `pipe(X, Y)`
100
101
  - Layer Magic: Automatically compose and build layers based on service dependencies
101
102
 
103
+ ### Codegens
104
+
105
+ - Automatically adds type annotations to exported constants based on their initializer types using `// @effect-codegens annotate`
106
+ - Automatically implements service accessors in `Effect.Service`, `Context.Tag` or `Effect.Tag` declarations using `// @effect-codegens accessors`
107
+
102
108
  ### Miscellaneous
103
109
  - Renaming a class name, will rename the identifier as well for TaggedError, TaggedClass, etc...
104
110
  - "Go to definition" for RpcClient will resolve to the Rpc definition
package/cli.js CHANGED
@@ -32083,6 +32083,51 @@ function make64(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
32083
32083
  if (!symbol3) return typeParserIssue("Node has no symbol", void 0, givenNode);
32084
32084
  return isSymbolExportOfPackageModule(symbol3, packageName, memberName, isCorrectSourceFile);
32085
32085
  };
32086
+ const findSymbolsMatchingPackageAndExportedName = (packageName, exportedSymbolName) => cachedBy(
32087
+ fn2("TypeParser.findSymbolsMatchingPackageAndExportedName")(function* (_fromSourceFile) {
32088
+ const result = [];
32089
+ for (const sourceFile of program.getSourceFiles()) {
32090
+ const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
32091
+ if (!moduleSymbol) continue;
32092
+ const symbol3 = typeChecker.tryGetMemberInModuleExports(exportedSymbolName, moduleSymbol);
32093
+ if (!symbol3) continue;
32094
+ const packageInfo = yield* getSourceFilePackageInfo(sourceFile);
32095
+ if (!packageInfo || packageInfo.name.toLowerCase() !== packageName.toLowerCase()) continue;
32096
+ result.push([symbol3, sourceFile]);
32097
+ }
32098
+ return result;
32099
+ }),
32100
+ `TypeParser.findSymbolsMatchingPackageAndExportedName(${packageName}, ${exportedSymbolName})`,
32101
+ (sourceFile) => sourceFile
32102
+ );
32103
+ const isCauseTypeSourceFile = cachedBy(
32104
+ fn2("TypeParser.isCauseTypeSourceFile")(function* (sourceFile) {
32105
+ const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
32106
+ if (!moduleSymbol) return yield* typeParserIssue("Node has no symbol", void 0, sourceFile);
32107
+ const causeTypeSymbol = typeChecker.tryGetMemberInModuleExports("Cause", moduleSymbol);
32108
+ if (!causeTypeSymbol) return yield* typeParserIssue("Cause type not found", void 0, sourceFile);
32109
+ const type2 = typeChecker.getDeclaredTypeOfSymbol(causeTypeSymbol);
32110
+ yield* pipeableType(type2, sourceFile);
32111
+ return sourceFile;
32112
+ }),
32113
+ "TypeParser.isCauseTypeSourceFile",
32114
+ (sourceFile) => sourceFile
32115
+ );
32116
+ const effectCauseYieldableErrorTypes = cachedBy(
32117
+ fn2("TypeParser.effectCauseYieldableErrorTypes")(function* (fromSourceFile) {
32118
+ const symbols = yield* findSymbolsMatchingPackageAndExportedName("effect", "YieldableError")(fromSourceFile);
32119
+ const result = [];
32120
+ for (const [symbol3, sourceFile] of symbols) {
32121
+ const causeFile = yield* isCauseTypeSourceFile(sourceFile);
32122
+ if (!causeFile) continue;
32123
+ const type2 = typeChecker.getDeclaredTypeOfSymbol(symbol3);
32124
+ result.push(type2);
32125
+ }
32126
+ return result;
32127
+ }),
32128
+ "TypeParser.effectCauseYieldableErrorTypes",
32129
+ (fromSourceFile) => fromSourceFile
32130
+ );
32086
32131
  function covariantTypeArgument(type2) {
32087
32132
  const signatures = typeChecker.getSignaturesOfType(type2, ts.SignatureKind.Call);
32088
32133
  if (signatures.length !== 1) {
@@ -33072,6 +33117,7 @@ function make64(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
33072
33117
  effectGen,
33073
33118
  effectFnUntracedGen,
33074
33119
  effectFnGen,
33120
+ effectCauseYieldableErrorTypes,
33075
33121
  unnecessaryEffectGen: unnecessaryEffectGen2,
33076
33122
  effectSchemaType,
33077
33123
  contextTag,
@@ -33382,8 +33428,93 @@ var accessors = createCodegen({
33382
33428
  })
33383
33429
  });
33384
33430
 
33431
+ // src/codegens/annotate.ts
33432
+ var annotate3 = createCodegen({
33433
+ name: "annotate",
33434
+ apply: fn2("annotate.apply")(function* (sourceFile, textRange) {
33435
+ const ts = yield* service2(TypeScriptApi);
33436
+ const tsUtils = yield* service2(TypeScriptUtils);
33437
+ const typeChecker = yield* service2(TypeCheckerApi);
33438
+ const typeCheckerUtils = yield* service2(TypeCheckerUtils);
33439
+ const parse6 = (node) => gen3(function* () {
33440
+ let variableDeclarations = [];
33441
+ const result = [];
33442
+ if (ts.isVariableStatement(node)) {
33443
+ variableDeclarations = [...variableDeclarations, ...node.declarationList.declarations];
33444
+ } else if (ts.isVariableDeclarationList(node)) {
33445
+ variableDeclarations = [...variableDeclarations, ...node.declarations];
33446
+ } else if (ts.isVariableDeclaration(node)) {
33447
+ variableDeclarations = [...variableDeclarations, node];
33448
+ }
33449
+ if (variableDeclarations.length === 0) {
33450
+ return yield* fail18(new CodegenNotApplicableError("not a variable declaration"));
33451
+ }
33452
+ for (const variableDeclaration of variableDeclarations) {
33453
+ if (!variableDeclaration.initializer) continue;
33454
+ const initializerType = typeChecker.getTypeAtLocation(variableDeclaration.initializer);
33455
+ const initializerTypeNode = fromNullable(typeCheckerUtils.typeToSimplifiedTypeNode(
33456
+ initializerType,
33457
+ node,
33458
+ ts.NodeBuilderFlags.NoTruncation
33459
+ )).pipe(
33460
+ orElse(
33461
+ () => fromNullable(typeCheckerUtils.typeToSimplifiedTypeNode(
33462
+ initializerType,
33463
+ void 0,
33464
+ ts.NodeBuilderFlags.NoTruncation
33465
+ ))
33466
+ ),
33467
+ getOrUndefined
33468
+ );
33469
+ if (!initializerTypeNode) continue;
33470
+ const typeNodeString = typeChecker.typeToString(initializerType, void 0, ts.TypeFormatFlags.NoTruncation);
33471
+ const hash3 = cyrb53(typeNodeString);
33472
+ result.push({ variableDeclaration, initializerTypeNode, hash: hash3 });
33473
+ }
33474
+ if (result.length === 0) {
33475
+ return yield* fail18(new CodegenNotApplicableError("no variable declarations with initializers"));
33476
+ }
33477
+ const hash2 = cyrb53(result.map((_) => _.hash).join("/"));
33478
+ return {
33479
+ hash: hash2,
33480
+ result
33481
+ };
33482
+ });
33483
+ const nodeAndCommentRange = tsUtils.findNodeWithLeadingCommentAtPosition(sourceFile, textRange.pos);
33484
+ if (!nodeAndCommentRange) return yield* fail18(new CodegenNotApplicableError("no node and comment range"));
33485
+ return yield* pipe(
33486
+ parse6(nodeAndCommentRange.node),
33487
+ map34(
33488
+ (_) => ({
33489
+ hash: _.hash,
33490
+ description: "Annotate with type",
33491
+ apply: gen3(function* () {
33492
+ const changeTracker = yield* service2(ChangeTracker);
33493
+ for (const { initializerTypeNode, variableDeclaration } of _.result) {
33494
+ if (variableDeclaration.type) {
33495
+ changeTracker.deleteRange(sourceFile, {
33496
+ pos: variableDeclaration.name.end,
33497
+ end: variableDeclaration.type.end
33498
+ });
33499
+ }
33500
+ changeTracker.insertNodeAt(
33501
+ sourceFile,
33502
+ variableDeclaration.name.end,
33503
+ initializerTypeNode,
33504
+ {
33505
+ prefix: ": "
33506
+ }
33507
+ );
33508
+ }
33509
+ })
33510
+ })
33511
+ )
33512
+ );
33513
+ })
33514
+ });
33515
+
33385
33516
  // src/codegens.ts
33386
- var codegens = [accessors];
33517
+ var codegens = [accessors, annotate3];
33387
33518
 
33388
33519
  // src/cli/codegen.ts
33389
33520
  var NoFilesToCodegenError = class extends TaggedError("NoFilesToCodegenError") {
@@ -35872,6 +36003,66 @@ var unnecessaryEffectGen = createDiagnostic({
35872
36003
  })
35873
36004
  });
35874
36005
 
36006
+ // src/diagnostics/unnecessaryFailYieldableError.ts
36007
+ var unnecessaryFailYieldableError = createDiagnostic({
36008
+ name: "unnecessaryFailYieldableError",
36009
+ code: 29,
36010
+ severity: "suggestion",
36011
+ apply: fn2("unnecessaryFailYieldableError.apply")(function* (sourceFile, report) {
36012
+ const ts = yield* service2(TypeScriptApi);
36013
+ const typeParser = yield* service2(TypeParser);
36014
+ const typeChecker = yield* service2(TypeCheckerApi);
36015
+ const yieldableErrorTypes = yield* pipe(
36016
+ typeParser.effectCauseYieldableErrorTypes(sourceFile),
36017
+ orElse14(() => succeed17([]))
36018
+ );
36019
+ const nodeToVisit = [];
36020
+ const appendNodeToVisit = (node) => {
36021
+ nodeToVisit.push(node);
36022
+ return void 0;
36023
+ };
36024
+ ts.forEachChild(sourceFile, appendNodeToVisit);
36025
+ while (nodeToVisit.length > 0) {
36026
+ const node = nodeToVisit.shift();
36027
+ ts.forEachChild(node, appendNodeToVisit);
36028
+ if (ts.isYieldExpression(node) && node.asteriskToken && node.expression && ts.isCallExpression(node.expression)) {
36029
+ const callExpression = node.expression;
36030
+ yield* pipe(
36031
+ typeParser.isNodeReferenceToEffectModuleApi("fail")(callExpression.expression),
36032
+ map34(() => {
36033
+ if (callExpression.arguments.length > 0) {
36034
+ const failArgument = callExpression.arguments[0];
36035
+ const argumentType = typeChecker.getTypeAtLocation(failArgument);
36036
+ const isYieldableError = yieldableErrorTypes.some(
36037
+ (yieldableType) => typeChecker.isTypeAssignableTo(argumentType, yieldableType)
36038
+ );
36039
+ if (isYieldableError) {
36040
+ report({
36041
+ location: node,
36042
+ messageText: `This Effect.fail call uses a yieldable error type as argument. You can yield* the error directly instead.`,
36043
+ fixes: [{
36044
+ fixName: "unnecessaryFailYieldableError_fix",
36045
+ description: "Replace yield* Effect.fail with yield*",
36046
+ apply: gen3(function* () {
36047
+ const changeTracker = yield* service2(ChangeTracker);
36048
+ changeTracker.replaceNode(
36049
+ sourceFile,
36050
+ callExpression,
36051
+ failArgument
36052
+ );
36053
+ })
36054
+ }]
36055
+ });
36056
+ }
36057
+ }
36058
+ }),
36059
+ ignore3
36060
+ );
36061
+ }
36062
+ }
36063
+ })
36064
+ });
36065
+
35875
36066
  // src/diagnostics/unnecessaryPipe.ts
35876
36067
  var unnecessaryPipe = createDiagnostic({
35877
36068
  name: "unnecessaryPipe",
@@ -36059,6 +36250,7 @@ var diagnostics = [
36059
36250
  floatingEffect,
36060
36251
  missingStarInYieldEffectGen,
36061
36252
  unnecessaryEffectGen,
36253
+ unnecessaryFailYieldableError,
36062
36254
  missingReturnYieldStar,
36063
36255
  leakingRequirements,
36064
36256
  unnecessaryPipe,