@effect/language-service 0.58.4 → 0.60.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.58.4",
3
+ "version": "0.60.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
@@ -1213,7 +1213,8 @@ var defaults = {
1213
1213
  }],
1214
1214
  extendedKeyDetection: false,
1215
1215
  pipeableMinArgCount: 1,
1216
- layerGraphFollowDepth: 0
1216
+ layerGraphFollowDepth: 0,
1217
+ mermaidProvider: "mermaid.live"
1217
1218
  };
1218
1219
  function parseKeyPatterns(patterns) {
1219
1220
  const result = [];
@@ -1251,7 +1252,8 @@ function parse(config) {
1251
1252
  keyPatterns: isObject(config) && hasProperty(config, "keyPatterns") && isArray(config.keyPatterns) ? parseKeyPatterns(config.keyPatterns) : defaults.keyPatterns,
1252
1253
  extendedKeyDetection: isObject(config) && hasProperty(config, "extendedKeyDetection") && isBoolean(config.extendedKeyDetection) ? config.extendedKeyDetection : defaults.extendedKeyDetection,
1253
1254
  pipeableMinArgCount: isObject(config) && hasProperty(config, "pipeableMinArgCount") && isNumber(config.pipeableMinArgCount) ? config.pipeableMinArgCount : defaults.pipeableMinArgCount,
1254
- layerGraphFollowDepth: isObject(config) && hasProperty(config, "layerGraphFollowDepth") && isNumber(config.layerGraphFollowDepth) ? config.layerGraphFollowDepth : defaults.layerGraphFollowDepth
1255
+ layerGraphFollowDepth: isObject(config) && hasProperty(config, "layerGraphFollowDepth") && isNumber(config.layerGraphFollowDepth) ? config.layerGraphFollowDepth : defaults.layerGraphFollowDepth,
1256
+ mermaidProvider: isObject(config) && hasProperty(config, "mermaidProvider") && isString(config.mermaidProvider) ? config.mermaidProvider : defaults.mermaidProvider
1255
1257
  };
1256
1258
  }
1257
1259
 
@@ -2840,67 +2842,69 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
2840
2842
  "TypeParser.effectSubtype",
2841
2843
  (type) => type
2842
2844
  );
2843
- const importedContextModule = cachedBy(
2844
- fn("TypeParser.importedContextModule")(function* (node) {
2845
- if (!ts.isIdentifier(node)) {
2846
- return yield* typeParserIssue("Node is not an identifier", void 0, node);
2847
- }
2848
- const type = typeChecker.getTypeAtLocation(node);
2849
- const propertySymbol = typeChecker.getPropertyOfType(type, "Tag");
2850
- if (!propertySymbol) {
2851
- return yield* typeParserIssue("Type has no 'Tag' property", type, node);
2852
- }
2853
- const sourceFile = tsUtils.getSourceFileOfNode(node);
2854
- if (!sourceFile) {
2855
- return yield* typeParserIssue("Node is not in a source file", void 0, node);
2856
- }
2857
- const contextIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(
2858
- sourceFile,
2859
- "effect",
2860
- "Context"
2861
- );
2862
- if (!contextIdentifier) {
2863
- return yield* typeParserIssue("Context module not found", void 0, node);
2864
- }
2865
- if (ts.idText(node) !== contextIdentifier) {
2866
- return yield* typeParserIssue("Node is not a context module reference", void 0, node);
2867
- }
2868
- return node;
2845
+ const isEffectContextSourceFile = cachedBy(
2846
+ fn("TypeParser.isEffectContextSourceFile")(function* (sourceFile) {
2847
+ const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
2848
+ if (!moduleSymbol) return yield* typeParserIssue("Node has no symbol", void 0, sourceFile);
2849
+ const contextSymbol = typeChecker.tryGetMemberInModuleExports("Context", moduleSymbol);
2850
+ if (!contextSymbol) return yield* typeParserIssue("Context not found", void 0, sourceFile);
2851
+ const tagSymbol = typeChecker.tryGetMemberInModuleExports("Tag", moduleSymbol);
2852
+ if (!tagSymbol) return yield* typeParserIssue("Tag not found", void 0, sourceFile);
2853
+ const tagType = typeChecker.getDeclaredTypeOfSymbol(tagSymbol);
2854
+ yield* contextTag(tagType, sourceFile);
2855
+ return sourceFile;
2856
+ }),
2857
+ "TypeParser.isEffectContextSourceFile",
2858
+ (sourceFile) => sourceFile
2859
+ );
2860
+ const isNodeReferenceToEffectContextModuleApi = (memberName) => cachedBy(
2861
+ fn("TypeParser.isNodeReferenceToEffectContextModuleApi")(function* (node) {
2862
+ return yield* isNodeReferenceToExportOfPackageModule(node, "effect", isEffectContextSourceFile, memberName);
2869
2863
  }),
2864
+ `TypeParser.isNodeReferenceToEffectContextModuleApi(${memberName})`,
2865
+ (node) => node
2866
+ );
2867
+ const importedContextModule = cachedBy(
2868
+ (node) => pipe(
2869
+ isNodeReferenceToPackageModule(node, "effect", isEffectContextSourceFile),
2870
+ map4(() => node)
2871
+ ),
2870
2872
  "TypeParser.importedContextModule",
2871
2873
  (node) => node
2872
2874
  );
2873
- const importedEffectModule = (node) => pipe(
2874
- isNodeReferenceToPackageModule(node, "effect", isEffectTypeSourceFile),
2875
- map4(() => node)
2875
+ const importedEffectModule = cachedBy(
2876
+ (node) => pipe(
2877
+ isNodeReferenceToPackageModule(node, "effect", isEffectTypeSourceFile),
2878
+ map4(() => node)
2879
+ ),
2880
+ "TypeParser.importedEffectModule",
2881
+ (node) => node
2882
+ );
2883
+ const isEffectDataSourceFile = cachedBy(
2884
+ fn("TypeParser.isEffectDataSourceFile")(function* (sourceFile) {
2885
+ const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
2886
+ if (!moduleSymbol) return yield* typeParserIssue("Node has no symbol", void 0, sourceFile);
2887
+ const taggedEnumSymbol = typeChecker.tryGetMemberInModuleExports("TaggedEnum", moduleSymbol);
2888
+ if (!taggedEnumSymbol) return yield* typeParserIssue("TaggedEnum not found", void 0, sourceFile);
2889
+ const taggedErrorSymbol = typeChecker.tryGetMemberInModuleExports("TaggedError", moduleSymbol);
2890
+ if (!taggedErrorSymbol) return yield* typeParserIssue("TaggedError not found", void 0, sourceFile);
2891
+ return sourceFile;
2892
+ }),
2893
+ "TypeParser.isEffectDataSourceFile",
2894
+ (sourceFile) => sourceFile
2876
2895
  );
2877
- const importedDataModule = cachedBy(
2878
- fn("TypeParser.importedDataModule")(function* (node) {
2879
- if (!ts.isIdentifier(node)) {
2880
- return yield* typeParserIssue("Node is not an expression", void 0, node);
2881
- }
2882
- const type = typeChecker.getTypeAtLocation(node);
2883
- const propertySymbol = typeChecker.getPropertyOfType(type, "TaggedError");
2884
- if (!propertySymbol) {
2885
- return yield* typeParserIssue("Type has no 'TaggedError' property", type, node);
2886
- }
2887
- const sourceFile = tsUtils.getSourceFileOfNode(node);
2888
- if (!sourceFile) {
2889
- return yield* typeParserIssue("Node is not in a source file", void 0, node);
2890
- }
2891
- const dataIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(
2892
- sourceFile,
2893
- "effect",
2894
- "Data"
2895
- );
2896
- if (!dataIdentifier) {
2897
- return yield* typeParserIssue("Data module not found", void 0, node);
2898
- }
2899
- if (ts.idText(node) !== dataIdentifier) {
2900
- return yield* typeParserIssue("Node is not a data module reference", void 0, node);
2901
- }
2902
- return node;
2896
+ const isNodeReferenceToEffectDataModuleApi = (memberName) => cachedBy(
2897
+ fn("TypeParser.isNodeReferenceToEffectDataModuleApi")(function* (node) {
2898
+ return yield* isNodeReferenceToExportOfPackageModule(node, "effect", isEffectDataSourceFile, memberName);
2903
2899
  }),
2900
+ `TypeParser.isNodeReferenceToEffectDataModuleApi(${memberName})`,
2901
+ (node) => node
2902
+ );
2903
+ const importedDataModule = cachedBy(
2904
+ (node) => pipe(
2905
+ isNodeReferenceToPackageModule(node, "effect", isEffectDataSourceFile),
2906
+ map4(() => node)
2907
+ ),
2904
2908
  "TypeParser.importedDataModule",
2905
2909
  (node) => node
2906
2910
  );
@@ -3639,6 +3643,8 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
3639
3643
  return {
3640
3644
  isNodeReferenceToEffectModuleApi,
3641
3645
  isNodeReferenceToEffectSchemaModuleApi,
3646
+ isNodeReferenceToEffectDataModuleApi,
3647
+ isNodeReferenceToEffectContextModuleApi,
3642
3648
  effectType,
3643
3649
  strictEffectType,
3644
3650
  layerType,
@@ -6042,6 +6048,85 @@ Consider extracting the Runtime by using for example Effect.runtime and then use
6042
6048
  })
6043
6049
  });
6044
6050
 
6051
+ // src/diagnostics/schemaStructWithTag.ts
6052
+ var schemaStructWithTag = createDiagnostic({
6053
+ name: "schemaStructWithTag",
6054
+ code: 34,
6055
+ severity: "suggestion",
6056
+ apply: fn("schemaStructWithTag.apply")(function* (sourceFile, report) {
6057
+ const ts = yield* service(TypeScriptApi);
6058
+ const typeParser = yield* service(TypeParser);
6059
+ const nodeToVisit = [];
6060
+ const appendNodeToVisit = (node) => {
6061
+ nodeToVisit.push(node);
6062
+ return void 0;
6063
+ };
6064
+ ts.forEachChild(sourceFile, appendNodeToVisit);
6065
+ while (nodeToVisit.length > 0) {
6066
+ const node = nodeToVisit.shift();
6067
+ if (ts.isCallExpression(node)) {
6068
+ const isSchemaStructCall = yield* pipe(
6069
+ typeParser.isNodeReferenceToEffectSchemaModuleApi("Struct")(node.expression),
6070
+ orElse2(() => void_)
6071
+ );
6072
+ if (isSchemaStructCall && node.arguments.length === 1) {
6073
+ const arg = node.arguments[0];
6074
+ if (ts.isObjectLiteralExpression(arg)) {
6075
+ const tagProperty = arg.properties.find(
6076
+ (prop) => ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && ts.idText(prop.name) === "_tag"
6077
+ );
6078
+ if (tagProperty && ts.isCallExpression(tagProperty.initializer)) {
6079
+ const isSchemaLiteralCall = yield* pipe(
6080
+ typeParser.isNodeReferenceToEffectSchemaModuleApi("Literal")(
6081
+ tagProperty.initializer.expression
6082
+ ),
6083
+ option
6084
+ );
6085
+ if (isSchemaLiteralCall._tag === "Some") {
6086
+ const literalCall = tagProperty.initializer;
6087
+ const literalArgs = fromIterable(literalCall.arguments);
6088
+ if (literalArgs.length === 1 && ts.isStringLiteral(literalArgs[0])) {
6089
+ const tagValue = literalArgs[0].text;
6090
+ const otherProperties = arg.properties.filter((prop) => prop !== tagProperty);
6091
+ report({
6092
+ location: node,
6093
+ messageText: "Schema.Struct with a _tag field can be simplified to Schema.TaggedStruct to make the tag optional in the constructor.",
6094
+ fixes: [{
6095
+ fixName: "schemaStructWithTag_fix",
6096
+ description: "Replace with Schema.TaggedStruct",
6097
+ apply: gen(function* () {
6098
+ const changeTracker = yield* service(ChangeTracker);
6099
+ const newObjectLiteral = ts.factory.createObjectLiteralExpression(
6100
+ otherProperties,
6101
+ true
6102
+ );
6103
+ const newNode = ts.factory.createCallExpression(
6104
+ ts.factory.createPropertyAccessExpression(
6105
+ // Reuse the Schema identifier from the original expression
6106
+ ts.isPropertyAccessExpression(node.expression) ? node.expression.expression : ts.factory.createIdentifier("Schema"),
6107
+ "TaggedStruct"
6108
+ ),
6109
+ void 0,
6110
+ [
6111
+ ts.factory.createStringLiteral(tagValue),
6112
+ newObjectLiteral
6113
+ ]
6114
+ );
6115
+ changeTracker.replaceNode(sourceFile, node, newNode);
6116
+ })
6117
+ }]
6118
+ });
6119
+ }
6120
+ }
6121
+ }
6122
+ }
6123
+ }
6124
+ }
6125
+ ts.forEachChild(node, appendNodeToVisit);
6126
+ }
6127
+ })
6128
+ });
6129
+
6045
6130
  // src/diagnostics/schemaUnionOfLiterals.ts
6046
6131
  var schemaUnionOfLiterals = createDiagnostic({
6047
6132
  name: "schemaUnionOfLiterals",
@@ -6733,7 +6818,8 @@ var diagnostics = [
6733
6818
  strictEffectProvide,
6734
6819
  unknownInEffectCatch,
6735
6820
  runEffectInsideEffect,
6736
- schemaUnionOfLiterals
6821
+ schemaUnionOfLiterals,
6822
+ schemaStructWithTag
6737
6823
  ];
6738
6824
 
6739
6825
  // src/transform.ts