@effect/language-service 0.29.0 → 0.30.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect/language-service",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
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
@@ -2266,6 +2266,36 @@ function make2(ts, tsUtils, typeChecker) {
2266
2266
  "TypeParser.effectSubtype",
2267
2267
  (type) => type
2268
2268
  );
2269
+ const importedSchemaModule = cachedBy(
2270
+ fn("TypeParser.importedSchemaModule")(function* (node) {
2271
+ const type = typeChecker.getTypeAtLocation(node);
2272
+ const propertySymbol = typeChecker.getPropertyOfType(type, "Class");
2273
+ if (!propertySymbol) {
2274
+ return yield* typeParserIssue("Type has no 'Class' property", type, node);
2275
+ }
2276
+ if (!ts.isExpression(node)) {
2277
+ return yield* typeParserIssue("Node is not an expression", type, node);
2278
+ }
2279
+ return node;
2280
+ }),
2281
+ "TypeParser.importedSchemaModule",
2282
+ (node) => node
2283
+ );
2284
+ const importedContextModule = cachedBy(
2285
+ fn("TypeParser.importedContextModule")(function* (node) {
2286
+ const type = typeChecker.getTypeAtLocation(node);
2287
+ const propertySymbol = typeChecker.getPropertyOfType(type, "Tag");
2288
+ if (!propertySymbol) {
2289
+ return yield* typeParserIssue("Type has no 'Tag' property", type, node);
2290
+ }
2291
+ if (!ts.isExpression(node)) {
2292
+ return yield* typeParserIssue("Node is not an expression", type, node);
2293
+ }
2294
+ return node;
2295
+ }),
2296
+ "TypeParser.importedContextModule",
2297
+ (node) => node
2298
+ );
2269
2299
  const importedEffectModule = cachedBy(
2270
2300
  fn("TypeParser.importedEffectModule")(function* (node) {
2271
2301
  const type = typeChecker.getTypeAtLocation(node);
@@ -2622,6 +2652,226 @@ function make2(ts, tsUtils, typeChecker) {
2622
2652
  "TypeParser.promiseLike",
2623
2653
  (type) => type
2624
2654
  );
2655
+ const extendsSchemaClass = cachedBy(
2656
+ fn("TypeParser.extendsSchemaClass")(function* (atLocation) {
2657
+ if (!atLocation.name) {
2658
+ return yield* typeParserIssue("Class has no name", void 0, atLocation);
2659
+ }
2660
+ const heritageClauses = atLocation.heritageClauses;
2661
+ if (!heritageClauses) {
2662
+ return yield* typeParserIssue("Class has no heritage clauses", void 0, atLocation);
2663
+ }
2664
+ for (const heritageClause of heritageClauses) {
2665
+ for (const typeX of heritageClause.types) {
2666
+ if (ts.isExpressionWithTypeArguments(typeX)) {
2667
+ const expression = typeX.expression;
2668
+ if (ts.isCallExpression(expression)) {
2669
+ const schemaCall = expression.expression;
2670
+ if (ts.isCallExpression(schemaCall) && schemaCall.typeArguments && schemaCall.typeArguments.length > 0) {
2671
+ const selfTypeNode = schemaCall.typeArguments[0];
2672
+ const schemaIdentifier = schemaCall.expression;
2673
+ if (ts.isPropertyAccessExpression(schemaIdentifier) && ts.isIdentifier(schemaIdentifier.name) && schemaIdentifier.name.text === "Class") {
2674
+ const parsedSchemaModule = yield* pipe(
2675
+ importedSchemaModule(schemaIdentifier.expression),
2676
+ option
2677
+ );
2678
+ if (isSome2(parsedSchemaModule)) {
2679
+ return {
2680
+ className: atLocation.name,
2681
+ selfTypeNode,
2682
+ Schema: parsedSchemaModule.value
2683
+ };
2684
+ }
2685
+ }
2686
+ }
2687
+ }
2688
+ }
2689
+ }
2690
+ }
2691
+ return yield* typeParserIssue("Class does not extend Schema.Class", void 0, atLocation);
2692
+ }),
2693
+ "TypeParser.extendsSchemaClass",
2694
+ (atLocation) => atLocation
2695
+ );
2696
+ const extendsSchemaTaggedClass = cachedBy(
2697
+ fn("TypeParser.extendsSchemaTaggedClass")(function* (atLocation) {
2698
+ if (!atLocation.name) {
2699
+ return yield* typeParserIssue("Class has no name", void 0, atLocation);
2700
+ }
2701
+ const heritageClauses = atLocation.heritageClauses;
2702
+ if (!heritageClauses) {
2703
+ return yield* typeParserIssue("Class has no heritage clauses", void 0, atLocation);
2704
+ }
2705
+ for (const heritageClause of heritageClauses) {
2706
+ for (const typeX of heritageClause.types) {
2707
+ if (ts.isExpressionWithTypeArguments(typeX)) {
2708
+ const expression = typeX.expression;
2709
+ if (ts.isCallExpression(expression)) {
2710
+ const tagCall = expression.expression;
2711
+ if (ts.isCallExpression(tagCall)) {
2712
+ const schemaCall = tagCall.expression;
2713
+ if (ts.isCallExpression(schemaCall) && schemaCall.typeArguments && schemaCall.typeArguments.length > 0) {
2714
+ const selfTypeNode = schemaCall.typeArguments[0];
2715
+ const schemaIdentifier = schemaCall.expression;
2716
+ if (ts.isPropertyAccessExpression(schemaIdentifier) && ts.isIdentifier(schemaIdentifier.name) && schemaIdentifier.name.text === "TaggedClass") {
2717
+ const parsedSchemaModule = yield* pipe(
2718
+ importedSchemaModule(schemaIdentifier.expression),
2719
+ option
2720
+ );
2721
+ if (isSome2(parsedSchemaModule)) {
2722
+ return {
2723
+ className: atLocation.name,
2724
+ selfTypeNode,
2725
+ Schema: parsedSchemaModule.value
2726
+ };
2727
+ }
2728
+ }
2729
+ }
2730
+ }
2731
+ }
2732
+ }
2733
+ }
2734
+ }
2735
+ return yield* typeParserIssue("Class does not extend Schema.TaggedClass", void 0, atLocation);
2736
+ }),
2737
+ "TypeParser.extendsSchemaTaggedClass",
2738
+ (atLocation) => atLocation
2739
+ );
2740
+ const extendsSchemaTaggedError = cachedBy(
2741
+ fn("TypeParser.extendsSchemaTaggedError")(function* (atLocation) {
2742
+ if (!atLocation.name) {
2743
+ return yield* typeParserIssue("Class has no name", void 0, atLocation);
2744
+ }
2745
+ const heritageClauses = atLocation.heritageClauses;
2746
+ if (!heritageClauses) {
2747
+ return yield* typeParserIssue("Class has no heritage clauses", void 0, atLocation);
2748
+ }
2749
+ for (const heritageClause of heritageClauses) {
2750
+ for (const typeX of heritageClause.types) {
2751
+ if (ts.isExpressionWithTypeArguments(typeX)) {
2752
+ const expression = typeX.expression;
2753
+ if (ts.isCallExpression(expression)) {
2754
+ const tagCall = expression.expression;
2755
+ if (ts.isCallExpression(tagCall)) {
2756
+ const schemaCall = tagCall.expression;
2757
+ if (ts.isCallExpression(schemaCall) && schemaCall.typeArguments && schemaCall.typeArguments.length > 0) {
2758
+ const selfTypeNode = schemaCall.typeArguments[0];
2759
+ const schemaIdentifier = schemaCall.expression;
2760
+ if (ts.isPropertyAccessExpression(schemaIdentifier) && ts.isIdentifier(schemaIdentifier.name) && schemaIdentifier.name.text === "TaggedError") {
2761
+ const parsedSchemaModule = yield* pipe(
2762
+ importedSchemaModule(schemaIdentifier.expression),
2763
+ option
2764
+ );
2765
+ if (isSome2(parsedSchemaModule)) {
2766
+ return {
2767
+ className: atLocation.name,
2768
+ selfTypeNode,
2769
+ Schema: parsedSchemaModule.value
2770
+ };
2771
+ }
2772
+ }
2773
+ }
2774
+ }
2775
+ }
2776
+ }
2777
+ }
2778
+ }
2779
+ return yield* typeParserIssue("Class does not extend Schema.TaggedError", void 0, atLocation);
2780
+ }),
2781
+ "TypeParser.extendsSchemaTaggedError",
2782
+ (atLocation) => atLocation
2783
+ );
2784
+ const extendsSchemaTaggedRequest = cachedBy(
2785
+ fn("TypeParser.extendsSchemaTaggedRequest")(function* (atLocation) {
2786
+ if (!atLocation.name) {
2787
+ return yield* typeParserIssue("Class has no name", void 0, atLocation);
2788
+ }
2789
+ const heritageClauses = atLocation.heritageClauses;
2790
+ if (!heritageClauses) {
2791
+ return yield* typeParserIssue("Class has no heritage clauses", void 0, atLocation);
2792
+ }
2793
+ for (const heritageClause of heritageClauses) {
2794
+ for (const typeX of heritageClause.types) {
2795
+ if (ts.isExpressionWithTypeArguments(typeX)) {
2796
+ const expression = typeX.expression;
2797
+ if (ts.isCallExpression(expression)) {
2798
+ const tagCall = expression.expression;
2799
+ if (ts.isCallExpression(tagCall)) {
2800
+ const schemaCall = tagCall.expression;
2801
+ if (ts.isCallExpression(schemaCall) && schemaCall.typeArguments && schemaCall.typeArguments.length > 0) {
2802
+ const selfTypeNode = schemaCall.typeArguments[0];
2803
+ const schemaIdentifier = schemaCall.expression;
2804
+ if (ts.isPropertyAccessExpression(schemaIdentifier) && ts.isIdentifier(schemaIdentifier.name) && schemaIdentifier.name.text === "TaggedRequest") {
2805
+ const parsedSchemaModule = yield* pipe(
2806
+ importedSchemaModule(schemaIdentifier.expression),
2807
+ option
2808
+ );
2809
+ if (isSome2(parsedSchemaModule)) {
2810
+ return {
2811
+ className: atLocation.name,
2812
+ selfTypeNode,
2813
+ Schema: parsedSchemaModule.value
2814
+ };
2815
+ }
2816
+ }
2817
+ }
2818
+ }
2819
+ }
2820
+ }
2821
+ }
2822
+ }
2823
+ return yield* typeParserIssue("Class does not extend Schema.TaggedRequest", void 0, atLocation);
2824
+ }),
2825
+ "TypeParser.extendsSchemaTaggedRequest",
2826
+ (atLocation) => atLocation
2827
+ );
2828
+ const extendsContextTag = cachedBy(
2829
+ fn("TypeParser.extendsContextTag")(function* (atLocation) {
2830
+ if (!atLocation.name) {
2831
+ return yield* typeParserIssue("Class has no name", void 0, atLocation);
2832
+ }
2833
+ const classSym = typeChecker.getSymbolAtLocation(atLocation.name);
2834
+ if (!classSym) return yield* typeParserIssue("Class has no symbol", void 0, atLocation);
2835
+ const type = typeChecker.getTypeOfSymbol(classSym);
2836
+ const heritageClauses = atLocation.heritageClauses;
2837
+ if (!heritageClauses) {
2838
+ return yield* typeParserIssue("Class has no heritage clauses", void 0, atLocation);
2839
+ }
2840
+ for (const heritageClause of heritageClauses) {
2841
+ for (const typeX of heritageClause.types) {
2842
+ if (ts.isExpressionWithTypeArguments(typeX)) {
2843
+ const wholeCall = typeX.expression;
2844
+ if (ts.isCallExpression(wholeCall)) {
2845
+ const contextTagCall = wholeCall.expression;
2846
+ if (ts.isCallExpression(contextTagCall) && wholeCall.typeArguments && wholeCall.typeArguments.length > 0) {
2847
+ const contextTagIdentifier = contextTagCall.expression;
2848
+ const selfTypeNode = wholeCall.typeArguments[0];
2849
+ if (ts.isPropertyAccessExpression(contextTagIdentifier) && ts.isIdentifier(contextTagIdentifier.name) && contextTagIdentifier.name.text === "Tag") {
2850
+ const parsedContextModule = yield* pipe(
2851
+ importedContextModule(contextTagIdentifier.expression),
2852
+ option
2853
+ );
2854
+ if (isSome2(parsedContextModule)) {
2855
+ const tagType = yield* contextTag(type, atLocation);
2856
+ return {
2857
+ className: atLocation.name,
2858
+ selfTypeNode,
2859
+ args: contextTagCall.arguments,
2860
+ Identifier: tagType.Identifier,
2861
+ Tag: parsedContextModule.value
2862
+ };
2863
+ }
2864
+ }
2865
+ }
2866
+ }
2867
+ }
2868
+ }
2869
+ }
2870
+ return yield* typeParserIssue("Class does not extend Context.Tag", void 0, atLocation);
2871
+ }),
2872
+ "TypeParser.extendsContextTag",
2873
+ (atLocation) => atLocation
2874
+ );
2625
2875
  const extendsEffectService = cachedBy(
2626
2876
  fn("TypeParser.extendsEffectService")(function* (atLocation) {
2627
2877
  if (!atLocation.name) {
@@ -2697,10 +2947,78 @@ function make2(ts, tsUtils, typeChecker) {
2697
2947
  pipeCall,
2698
2948
  scopeType,
2699
2949
  promiseLike,
2700
- extendsEffectService
2950
+ extendsEffectService,
2951
+ extendsContextTag,
2952
+ extendsSchemaClass,
2953
+ extendsSchemaTaggedClass,
2954
+ extendsSchemaTaggedError,
2955
+ extendsSchemaTaggedRequest
2701
2956
  };
2702
2957
  }
2703
2958
 
2959
+ // src/diagnostics/classSelfMismatch.ts
2960
+ var classSelfMismatch = createDiagnostic({
2961
+ name: "classSelfMismatch",
2962
+ code: 20,
2963
+ severity: "error",
2964
+ apply: fn("classSelfMismatch.apply")(function* (sourceFile, report) {
2965
+ const ts = yield* service(TypeScriptApi);
2966
+ const typeParser = yield* service(TypeParser);
2967
+ const nodeToVisit = [];
2968
+ const appendNodeToVisit = (node) => {
2969
+ nodeToVisit.push(node);
2970
+ return void 0;
2971
+ };
2972
+ ts.forEachChild(sourceFile, appendNodeToVisit);
2973
+ while (nodeToVisit.length > 0) {
2974
+ const node = nodeToVisit.shift();
2975
+ if (ts.isClassDeclaration(node) && node.name && node.heritageClauses) {
2976
+ const result = yield* pipe(
2977
+ typeParser.extendsEffectService(node),
2978
+ orElse2(() => typeParser.extendsContextTag(node)),
2979
+ orElse2(() => typeParser.extendsSchemaClass(node)),
2980
+ orElse2(() => typeParser.extendsSchemaTaggedClass(node)),
2981
+ orElse2(() => typeParser.extendsSchemaTaggedError(node)),
2982
+ orElse2(() => typeParser.extendsSchemaTaggedRequest(node)),
2983
+ orElse2(() => void_)
2984
+ );
2985
+ if (result) {
2986
+ const { className, selfTypeNode } = result;
2987
+ let actualName = "";
2988
+ if (ts.isTypeReferenceNode(selfTypeNode)) {
2989
+ if (ts.isIdentifier(selfTypeNode.typeName)) {
2990
+ actualName = selfTypeNode.typeName.text;
2991
+ } else if (ts.isQualifiedName(selfTypeNode.typeName)) {
2992
+ actualName = selfTypeNode.typeName.right.text;
2993
+ }
2994
+ }
2995
+ const expectedName = className.text;
2996
+ if (actualName !== expectedName) {
2997
+ report({
2998
+ location: selfTypeNode,
2999
+ messageText: `Self type parameter should be '${expectedName}'`,
3000
+ fixes: [{
3001
+ fixName: "classSelfMismatch_fix",
3002
+ description: `Replace '${actualName}' with '${expectedName}'`,
3003
+ apply: gen(function* () {
3004
+ const changeTracker = yield* service(ChangeTracker);
3005
+ const typeArgs = ts.isTypeReferenceNode(selfTypeNode) ? selfTypeNode.typeArguments : void 0;
3006
+ const newTypeReference = ts.factory.createTypeReferenceNode(
3007
+ ts.factory.createIdentifier(expectedName),
3008
+ typeArgs
3009
+ );
3010
+ changeTracker.replaceNode(sourceFile, selfTypeNode, newTypeReference);
3011
+ })
3012
+ }]
3013
+ });
3014
+ }
3015
+ }
3016
+ }
3017
+ ts.forEachChild(node, appendNodeToVisit);
3018
+ }
3019
+ })
3020
+ });
3021
+
2704
3022
  // src/diagnostics/duplicatePackage.ts
2705
3023
  var checkedPackagesCache = /* @__PURE__ */ new Map();
2706
3024
  var programResolvedCacheSize = /* @__PURE__ */ new Map();
@@ -4315,8 +4633,64 @@ var unnecessaryPipeChain = createDiagnostic({
4315
4633
  })
4316
4634
  });
4317
4635
 
4636
+ // src/diagnostics/unsupportedServiceAccessors.ts
4637
+ var unsupportedServiceAccessors = createDiagnostic({
4638
+ name: "unsupportedServiceAccessors",
4639
+ code: 21,
4640
+ severity: "warning",
4641
+ apply: fn("unsupportedServiceAccessors.apply")(function* (sourceFile, report) {
4642
+ const ts = yield* service(TypeScriptApi);
4643
+ const nodeToVisit = [];
4644
+ const appendNodeToVisit = (node) => {
4645
+ nodeToVisit.push(node);
4646
+ return void 0;
4647
+ };
4648
+ ts.forEachChild(sourceFile, appendNodeToVisit);
4649
+ while (nodeToVisit.length > 0) {
4650
+ const node = nodeToVisit.shift();
4651
+ ts.forEachChild(node, appendNodeToVisit);
4652
+ if (ts.isClassDeclaration(node)) {
4653
+ const parseResult = yield* pipe(
4654
+ parse2(node),
4655
+ orElse2(() => succeed(null))
4656
+ );
4657
+ if (parseResult && parseResult.involvedMembers.length > 0) {
4658
+ const existingStaticMembers = /* @__PURE__ */ new Set();
4659
+ node.members?.forEach((member) => {
4660
+ if (ts.isPropertyDeclaration(member) && member.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.StaticKeyword)) {
4661
+ if (member.name && ts.isIdentifier(member.name)) {
4662
+ existingStaticMembers.add(member.name.text);
4663
+ }
4664
+ }
4665
+ });
4666
+ const missingMembers = parseResult.involvedMembers.filter(
4667
+ ({ property }) => !existingStaticMembers.has(property.getName())
4668
+ );
4669
+ if (missingMembers.length > 0) {
4670
+ const memberNames = missingMembers.map(({ property }) => `'${property.getName()}'`).join(", ");
4671
+ report({
4672
+ location: parseResult.className,
4673
+ messageText: `Even if accessors are enabled, accessors for ${memberNames} won't be available because the signature have generic type parameters or multiple call signatures.`,
4674
+ fixes: [{
4675
+ fixName: "unsupportedServiceAccessors_enableCodegen",
4676
+ description: "Enable accessors codegen",
4677
+ apply: gen(function* () {
4678
+ const changeTracker = yield* service(ChangeTracker);
4679
+ const comment = "// @effect-codegens accessors\n";
4680
+ changeTracker.insertText(sourceFile, node.getStart(sourceFile), comment);
4681
+ })
4682
+ }]
4683
+ });
4684
+ }
4685
+ }
4686
+ }
4687
+ }
4688
+ })
4689
+ });
4690
+
4318
4691
  // src/diagnostics.ts
4319
4692
  var diagnostics = [
4693
+ classSelfMismatch,
4320
4694
  duplicatePackage,
4321
4695
  missingEffectContext,
4322
4696
  missingEffectError,
@@ -4335,7 +4709,8 @@ var diagnostics = [
4335
4709
  unnecessaryPipeChain,
4336
4710
  strictBooleanExpressions,
4337
4711
  multipleEffectProvide,
4338
- outdatedEffectCodegen
4712
+ outdatedEffectCodegen,
4713
+ unsupportedServiceAccessors
4339
4714
  ];
4340
4715
 
4341
4716
  // src/transform.ts