@effect/language-service 0.15.0 → 0.16.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
@@ -26,12 +26,39 @@ This package implements a TypeScript language service plugin that allows additio
26
26
 
27
27
  And you're done! You'll now be able to use a set of refactor and diagnostics that targets Effect!
28
28
 
29
- ## Enable diagnostics at compile time
29
+ ## Provided functionalities
30
30
 
31
- TypeScript LSP are loaded only while editing your files. That means that if you run `tsc` in your project, the plugin won't be loaded and you'll miss out on the Effect diagnostics.
31
+ ### Quickinfo
32
32
 
33
- HOWEVER, if you use `ts-patch` you can enable the transform as well to get the diagnostics also at compile time.
34
- Your `tsconfig.json` should look like this:
33
+ - Show the extended type of the current Effect
34
+ - Hovering yield\* of Effect.gen will show the Effect type parameters
35
+
36
+ ### Diagnostics
37
+
38
+ - Better error readability when you're missing errors or service types in your Effect definitions
39
+ - Floating Effects that are not yielded or run
40
+ - Wrong usage of yield inside Effect.gen
41
+ - Unnecessary usages of Effect.gen
42
+ - Multiple versions of Effect in your project
43
+
44
+ ### Completions
45
+
46
+ - Autocomplete 'Self' in Effect.Service, Context.Tag, Schema.TaggedClass, Schema.TaggedRequest and family
47
+
48
+ ### Refactors
49
+
50
+ - Transform an async function definition, into an Effect by using Effect.gen.
51
+ - Transform an async function definition, into an Effect by using Effect.gen, and generating a tagged error for each promise call.
52
+ - Transform a function returning an Effect.gen into a Effect.fn
53
+ - Function calls to pipe: Transform a set of function calls to a pipe() call.
54
+ - Pipe to datafirst: Transform a pipe() call into a series of datafirst function calls (where available).
55
+ - Toggle return type signature: With a single refactor, adds or removes type annotations from the definition.
56
+ - Remove unnecessary `Effect.gen` definitions that contains a single `yield` statement.
57
+ - Wrap an `Effect` expression with `Effect.gen`
58
+
59
+ ## Options
60
+
61
+ Few options can be provided alongside the initialization of the Language Service Plugin.
35
62
 
36
63
  ```json
37
64
  {
@@ -39,30 +66,22 @@ Your `tsconfig.json` should look like this:
39
66
  "plugins": [
40
67
  {
41
68
  "name": "@effect/language-service",
42
- "transform": "@effect/language-service/transform" // enables diagnostics at compile time when using ts-patch
69
+ "diagnostics": true, // controls Effect diagnostics (default: true)
70
+ "quickinfo": true, // controls quickinfo over Effect (default: true)
71
+ "completions": true, // controls Effect completions (default: true)
72
+ "multipleEffectCheck": true // controls if multiple versions of Effect are referenced (default: true)
43
73
  }
44
74
  ]
45
75
  }
46
76
  }
47
77
  ```
48
78
 
49
- To get diagnostics you need to install `ts-patch` which will make it possible to run `tspc`.
50
-
51
- Running `tspc` in your project will now also run the plugin and give you the diagnostics at compile time.
52
-
53
- ```ts
54
- $ npx tspc
55
- index.ts:3:1 - error TS3: Effect must be yielded or assigned to a variable.
56
-
57
- 3 Effect.succeed(1)
58
- ~~~~~~~~~~~~~~~~~
59
-
60
- Found 1 error in index.ts:3 
61
- ```
79
+ ## Why diagnostics does not appear at compile time?
62
80
 
63
- ## Options
81
+ TypeScript LSP are loaded only while editing your files. That means that if you run `tsc` in your project, the plugin won't be loaded and you'll miss out on the Effect diagnostics.
64
82
 
65
- Few options can be provided alongside the initialization of the Language Service Plugin.
83
+ HOWEVER, if you use `ts-patch` you can enable the transform as well to get the diagnostics also at compile time.
84
+ Your `tsconfig.json` should look like this:
66
85
 
67
86
  ```json
68
87
  {
@@ -70,44 +89,27 @@ Few options can be provided alongside the initialization of the Language Service
70
89
  "plugins": [
71
90
  {
72
91
  "name": "@effect/language-service",
73
- "diagnostics": true, // controls Effect diagnostics
74
- "quickinfo": true, // controls quickinfo over Effect
75
- "completions": true, // controls Effect completions
76
- "multipleEffectCheck": true // controls if multiple versions of Effect are referenced
92
+ "transform": "@effect/language-service/transform" // enables diagnostics at compile time when using ts-patch
77
93
  }
78
94
  ]
79
95
  }
80
96
  }
81
97
  ```
82
98
 
83
- ## Provided functionalities
84
-
85
- ### Quickinfo
86
-
87
- - Show the extended type of the current Effect
88
-
89
- ### Diagnostics
90
-
91
- - Better error readability when you're missing errors or service types in your Effect definitions
92
- - Floating Effects that are not yielded or run
93
- - Wrong usage of yield inside Effect.gen
94
- - Unnecessary usages of Effect.gen
95
- - Multiple versions of Effect in your project
99
+ To get diagnostics you need to install `ts-patch` which will make it possible to run `tspc`.
96
100
 
97
- ### Completions
101
+ Running `tspc` in your project will now also run the plugin and give you the diagnostics at compile time.
102
+ Effect diagnostics will be shown only after standard TypeScript diagnostics has been satisfied.
98
103
 
99
- - Autocomplete 'Self' in Effect.Service, Context.Tag, Schema.TaggedClass, Schema.TaggedRequest and family
104
+ ```ts
105
+ $ npx tspc
106
+ index.ts:3:1 - error TS3: Effect must be yielded or assigned to a variable.
100
107
 
101
- ### Refactors
108
+ 3 Effect.succeed(1)
109
+ ~~~~~~~~~~~~~~~~~
102
110
 
103
- - Transform an async function definition, into an Effect by using Effect.gen.
104
- - Transform an async function definition, into an Effect by using Effect.gen, and generating a tagged error for each promise call.
105
- - Transform a function returning an Effect.gen into a Effect.fn
106
- - Function calls to pipe: Transform a set of function calls to a pipe() call.
107
- - Pipe to datafirst: Transform a pipe() call into a series of datafirst function calls (where available).
108
- - Toggle return type signature: With a single refactor, adds or removes type annotations from the definition.
109
- - Remove unnecessary `Effect.gen` definitions that contains a single `yield` statement.
110
- - Wrap an `Effect` expression with `Effect.gen`
111
+ Found 1 error in index.ts:3 
112
+ ```
111
113
 
112
114
  ## Configuring diagnostics
113
115
 
package/index.js CHANGED
@@ -1004,23 +1004,27 @@ var contextGet = (context, tag) => {
1004
1004
  }
1005
1005
  return none2();
1006
1006
  };
1007
- var Nano = class {
1008
- constructor(run2) {
1009
- this.run = run2;
1010
- }
1007
+ var Proto = {
1008
+ run: () => {
1009
+ },
1011
1010
  [Symbol.iterator]() {
1012
1011
  return new SingleShotGen(new YieldWrap(this));
1013
1012
  }
1014
1013
  };
1014
+ function make3(run2) {
1015
+ const result = Object.create(Proto);
1016
+ result.run = run2;
1017
+ return result;
1018
+ }
1015
1019
  var unsafeRun = (fa) => {
1016
1020
  const result = fa.run(contextEmpty);
1017
1021
  switch (result._tag) {
1018
1022
  case "Left":
1019
- return left2(result.left);
1023
+ return left2(result.value);
1020
1024
  case "Defect":
1021
- return left2(new NanoDefectException(result.defect));
1025
+ return left2(new NanoDefectException(result.value));
1022
1026
  case "Right":
1023
- return right2(result.right);
1027
+ return right2(result.value);
1024
1028
  }
1025
1029
  };
1026
1030
  var run = (fa) => {
@@ -1030,35 +1034,35 @@ var run = (fa) => {
1030
1034
  return left2(new NanoDefectException(e));
1031
1035
  }
1032
1036
  };
1033
- var succeed = (value) => new Nano(() => ({ _tag: "Right", right: value }));
1034
- var fail = (value) => new Nano(() => ({ _tag: "Left", left: value }));
1035
- var sync = (value) => new Nano(() => ({ _tag: "Right", right: value() }));
1036
- var flatMap3 = dual(2, (fa, f) => new Nano((ctx) => {
1037
+ var succeed = (value) => make3(() => ({ _tag: "Right", value }));
1038
+ var fail = (value) => make3(() => ({ _tag: "Left", value }));
1039
+ var sync = (value) => make3(() => ({ _tag: "Right", value: value() }));
1040
+ var flatMap3 = dual(2, (fa, f) => make3((ctx) => {
1037
1041
  const result = fa.run(ctx);
1038
1042
  if (result._tag !== "Right") return result;
1039
- return f(result.right).run(ctx);
1043
+ return f(result.value).run(ctx);
1040
1044
  }));
1041
- var map3 = dual(2, (fa, f) => new Nano((ctx) => {
1045
+ var map3 = dual(2, (fa, f) => make3((ctx) => {
1042
1046
  const result = fa.run(ctx);
1043
1047
  if (result._tag !== "Right") return result;
1044
- return { _tag: "Right", right: f(result.right) };
1048
+ return { _tag: "Right", value: f(result.value) };
1045
1049
  }));
1046
- var orElse3 = (f) => (fa) => new Nano((ctx) => {
1050
+ var orElse3 = (f) => (fa) => make3((ctx) => {
1047
1051
  const result = fa.run(ctx);
1048
- if (result._tag === "Left") return f(result.left).run(ctx);
1052
+ if (result._tag === "Left") return f(result.value).run(ctx);
1049
1053
  return result;
1050
1054
  });
1051
1055
  var firstSuccessOf = (arr) => reduce(arr.slice(1), arr[0], (arr2, fa) => orElse3(() => fa)(arr2));
1052
- var service = (tag) => new Nano(
1056
+ var service = (tag) => make3(
1053
1057
  (ctx) => contextGet(ctx, tag).pipe(match2({
1054
- onNone: () => ({ _tag: "Defect", defect: `Cannot find service ${tag.key}` }),
1055
- onSome: (value) => ({ _tag: "Right", right: value })
1058
+ onNone: () => ({ _tag: "Defect", value: `Cannot find service ${tag.key}` }),
1059
+ onSome: (value) => ({ _tag: "Right", value })
1056
1060
  }))
1057
1061
  );
1058
- var provideService = (tag, value) => (fa) => new Nano((ctx) => {
1062
+ var provideService = (tag, value) => (fa) => make3((ctx) => {
1059
1063
  return fa.run(contextAdd(ctx, tag, value));
1060
1064
  });
1061
- var gen2 = (...args) => new Nano((ctx) => {
1065
+ var gen2 = (...args) => make3((ctx) => {
1062
1066
  const iterator = args[0]();
1063
1067
  let state = iterator.next();
1064
1068
  while (!state.done) {
@@ -1067,11 +1071,11 @@ var gen2 = (...args) => new Nano((ctx) => {
1067
1071
  if (result._tag !== "Right") {
1068
1072
  return result;
1069
1073
  }
1070
- state = iterator.next(result.right);
1074
+ state = iterator.next(result.value);
1071
1075
  }
1072
- return { _tag: "Right", right: state.value };
1076
+ return { _tag: "Right", value: state.value };
1073
1077
  });
1074
- var fn = (_) => (body) => (...args) => new Nano((ctx) => {
1078
+ var fn = (_) => (body) => (...args) => make3((ctx) => {
1075
1079
  const iterator = body(...args);
1076
1080
  let state = iterator.next();
1077
1081
  while (!state.done) {
@@ -1080,17 +1084,17 @@ var fn = (_) => (body) => (...args) => new Nano((ctx) => {
1080
1084
  if (result._tag !== "Right") {
1081
1085
  return result;
1082
1086
  }
1083
- state = iterator.next(result.right);
1087
+ state = iterator.next(result.value);
1084
1088
  }
1085
- return { _tag: "Right", right: state.value };
1089
+ return { _tag: "Right", value: state.value };
1086
1090
  });
1087
- var option = (fa) => new Nano((ctx) => {
1091
+ var option = (fa) => make3((ctx) => {
1088
1092
  const result = fa.run(ctx);
1089
1093
  switch (result._tag) {
1090
1094
  case "Right":
1091
- return { _tag: "Right", right: some2(result.right) };
1095
+ return { _tag: "Right", value: some2(result.value) };
1092
1096
  case "Left":
1093
- return { _tag: "Right", right: none2() };
1097
+ return { _tag: "Right", value: none2() };
1094
1098
  case "Defect":
1095
1099
  return result;
1096
1100
  }
@@ -2610,6 +2614,7 @@ var JSDocTagInfoEq = make(
2610
2614
  (fa, fb) => fa.name === fb.name && typeof fa.text === typeof fb.text && (typeof fa.text !== "undefined" ? array(SymbolDisplayPartEq)(fa.text, fb.text) : true)
2611
2615
  );
2612
2616
  function dedupeJsDocTags(quickInfo) {
2617
+ if (!quickInfo) return quickInfo;
2613
2618
  if (quickInfo.tags) {
2614
2619
  return {
2615
2620
  ...quickInfo,
@@ -2635,21 +2640,33 @@ function prependEffectTypeArguments(sourceFile, position, quickInfo) {
2635
2640
  gen2(function* () {
2636
2641
  const ts = yield* service(TypeScriptApi);
2637
2642
  const typeChecker = yield* service(TypeCheckerApi);
2638
- const hasTruncationHappened = ts.displayPartsToString(quickInfo.displayParts).indexOf("...") > -1;
2639
- if (!hasTruncationHappened) return quickInfo;
2640
2643
  const maybeNode = pipe(
2641
2644
  yield* getAncestorNodesInRange(sourceFile, toTextRange(position)),
2642
2645
  head
2643
2646
  );
2644
2647
  if (isNone2(maybeNode)) return quickInfo;
2648
+ const node = maybeNode.value;
2649
+ const hasTruncationHappened = quickInfo && ts.displayPartsToString(quickInfo.displayParts).indexOf("...") > -1;
2650
+ const nodeForType = !quickInfo && ts.isYieldExpression(node) && node.asteriskToken && node.expression ? node.expression : hasTruncationHappened ? node : void 0;
2651
+ if (!nodeForType) return quickInfo;
2645
2652
  const effectType2 = yield* effectType(
2646
- typeChecker.getTypeAtLocation(maybeNode.value),
2647
- maybeNode.value
2653
+ typeChecker.getTypeAtLocation(nodeForType),
2654
+ nodeForType
2648
2655
  );
2649
2656
  const effectTypeArgsDocumentation = [{
2650
2657
  kind: "text",
2651
2658
  text: "```ts\n/* Effect Type Parameters */\n" + (yield* formatTypeForQuickInfo(effectType2.A, "Success")) + "\n" + (yield* formatTypeForQuickInfo(effectType2.E, "Failure")) + "\n" + (yield* formatTypeForQuickInfo(effectType2.R, "Requirements")) + "\n```\n"
2652
2659
  }];
2660
+ if (!quickInfo) {
2661
+ const start = node.getStart();
2662
+ const end = node.getEnd();
2663
+ return {
2664
+ kind: ts.ScriptElementKind.callSignatureElement,
2665
+ kindModifiers: "",
2666
+ textSpan: { start, length: end - start },
2667
+ documentation: effectTypeArgsDocumentation
2668
+ };
2669
+ }
2653
2670
  if (quickInfo.documentation) {
2654
2671
  return {
2655
2672
  ...quickInfo,
@@ -3595,6 +3612,7 @@ var makeSchemaGenContext = fn("SchemaGen.makeSchemaGenContext")(function* (sourc
3595
3612
  case "Date":
3596
3613
  case "Pick":
3597
3614
  case "Omit":
3615
+ case "Record":
3598
3616
  return some2(name.text);
3599
3617
  case "ReadonlyArray":
3600
3618
  case "Array":
@@ -3631,8 +3649,17 @@ var parseAllLiterals = fn(
3631
3649
  )(
3632
3650
  function* (node) {
3633
3651
  const { ts } = yield* service(SchemaGenContext);
3634
- if (ts.isLiteralTypeNode(node) && node.literal.kind === ts.SyntaxKind.StringLiteral) {
3635
- return [ts.factory.createStringLiteral(node.literal.text)];
3652
+ if (ts.isLiteralTypeNode(node)) {
3653
+ switch (node.literal.kind) {
3654
+ case ts.SyntaxKind.StringLiteral:
3655
+ return [ts.factory.createStringLiteral(node.literal.text)];
3656
+ case ts.SyntaxKind.NumericLiteral:
3657
+ return [ts.factory.createNumericLiteral(node.literal.text)];
3658
+ case ts.SyntaxKind.TrueKeyword:
3659
+ return [ts.factory.createTrue()];
3660
+ case ts.SyntaxKind.FalseKeyword:
3661
+ return [ts.factory.createFalse()];
3662
+ }
3636
3663
  }
3637
3664
  if (ts.isUnionTypeNode(node)) {
3638
3665
  return flatten(yield* all2(...node.types.map((_) => parseAllLiterals(_))));
@@ -3675,23 +3702,30 @@ var processNode = (node) => gen2(function* () {
3675
3702
  return createApiPropertyAccess("BigInt");
3676
3703
  }
3677
3704
  if (ts.isLiteralTypeNode(node)) {
3678
- switch (node.literal.kind) {
3679
- case ts.SyntaxKind.NullKeyword:
3680
- return createApiPropertyAccess("Null");
3681
- case ts.SyntaxKind.TrueKeyword:
3682
- return createApiCall("Literal", [ts.factory.createTrue()]);
3683
- case ts.SyntaxKind.FalseKeyword:
3684
- return createApiCall("Literal", [ts.factory.createFalse()]);
3685
- case ts.SyntaxKind.StringLiteral:
3686
- return createApiCall("Literal", [ts.factory.createStringLiteral(node.literal.text)]);
3687
- case ts.SyntaxKind.NumericLiteral:
3688
- return createApiCall("Literal", [ts.factory.createNumericLiteral(node.literal.text)]);
3689
- }
3705
+ if (node.literal.kind === ts.SyntaxKind.NullKeyword) return createApiPropertyAccess("Null");
3706
+ const literalMembers = yield* option(parseAllLiterals(node));
3707
+ if (isSome2(literalMembers)) return createApiCall("Literal", literalMembers.value);
3690
3708
  }
3691
3709
  if (ts.isUnionTypeNode(node)) {
3710
+ const allLiterals = yield* option(parseAllLiterals(node));
3711
+ if (isSome2(allLiterals)) return createApiCall("Literal", allLiterals.value);
3692
3712
  const members = yield* all2(...node.types.map((_) => processNode(_)));
3693
3713
  return createApiCall("Union", members);
3694
3714
  }
3715
+ if (ts.isIntersectionTypeNode(node)) {
3716
+ const [firstSchema, ...otherSchemas] = yield* all2(
3717
+ ...node.types.map((_) => processNode(_))
3718
+ );
3719
+ if (otherSchemas.length === 0) return firstSchema;
3720
+ return ts.factory.createCallExpression(
3721
+ ts.factory.createPropertyAccessExpression(
3722
+ firstSchema,
3723
+ "pipe"
3724
+ ),
3725
+ [],
3726
+ otherSchemas.map((_) => createApiCall("extend", [_]))
3727
+ );
3728
+ }
3695
3729
  if (ts.isArrayTypeNode(node)) {
3696
3730
  const typeSchema = yield* processNode(node.elementType);
3697
3731
  return createApiCall("Array", [typeSchema]);
@@ -3718,6 +3752,20 @@ var processNode = (node) => gen2(function* () {
3718
3752
  );
3719
3753
  return createApiCall(parsedName.value, elements);
3720
3754
  }
3755
+ case "Record": {
3756
+ const elements = yield* all2(
3757
+ ...node.typeArguments ? node.typeArguments.map(processNode) : []
3758
+ );
3759
+ if (elements.length >= 2) {
3760
+ return createApiCall(parsedName.value, [
3761
+ ts.factory.createObjectLiteralExpression([
3762
+ ts.factory.createPropertyAssignment("key", elements[0]),
3763
+ ts.factory.createPropertyAssignment("value", elements[1])
3764
+ ])
3765
+ ]);
3766
+ }
3767
+ return createUnsupportedNodeComment(ts, sourceFile, node);
3768
+ }
3721
3769
  case "Either": {
3722
3770
  const elements = yield* all2(
3723
3771
  ...node.typeArguments ? node.typeArguments.map(processNode) : []
@@ -4252,7 +4300,7 @@ var init = (modules) => {
4252
4300
  };
4253
4301
  proxy.getQuickInfoAtPosition = (fileName, position, ...args) => {
4254
4302
  const quickInfo = languageService.getQuickInfoAtPosition(fileName, position, ...args);
4255
- if (pluginOptions.quickinfo && quickInfo) {
4303
+ if (pluginOptions.quickinfo) {
4256
4304
  const dedupedTagsQuickInfo = dedupeJsDocTags(quickInfo);
4257
4305
  const program = languageService.getProgram();
4258
4306
  if (program) {