@effect/language-service 0.58.4 → 0.59.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
+ - Suggest using `Schema.TaggedStruct` instead of `Schema.Struct` when a `_tag` field with `Schema.Literal` is present to make the tag optional in the constructor
75
76
  - Warn when using `yield* Effect.fail()` with yieldable error types that can be yielded directly
76
77
 
77
78
  ### Completions
package/cli.js CHANGED
@@ -32329,67 +32329,69 @@ function make64(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
32329
32329
  "TypeParser.effectSubtype",
32330
32330
  (type2) => type2
32331
32331
  );
32332
- const importedContextModule = cachedBy(
32333
- fn2("TypeParser.importedContextModule")(function* (node) {
32334
- if (!ts.isIdentifier(node)) {
32335
- return yield* typeParserIssue("Node is not an identifier", void 0, node);
32336
- }
32337
- const type2 = typeChecker.getTypeAtLocation(node);
32338
- const propertySymbol = typeChecker.getPropertyOfType(type2, "Tag");
32339
- if (!propertySymbol) {
32340
- return yield* typeParserIssue("Type has no 'Tag' property", type2, node);
32341
- }
32342
- const sourceFile = tsUtils.getSourceFileOfNode(node);
32343
- if (!sourceFile) {
32344
- return yield* typeParserIssue("Node is not in a source file", void 0, node);
32345
- }
32346
- const contextIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(
32347
- sourceFile,
32348
- "effect",
32349
- "Context"
32350
- );
32351
- if (!contextIdentifier) {
32352
- return yield* typeParserIssue("Context module not found", void 0, node);
32353
- }
32354
- if (ts.idText(node) !== contextIdentifier) {
32355
- return yield* typeParserIssue("Node is not a context module reference", void 0, node);
32356
- }
32357
- return node;
32332
+ const isEffectContextSourceFile = cachedBy(
32333
+ fn2("TypeParser.isEffectContextSourceFile")(function* (sourceFile) {
32334
+ const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
32335
+ if (!moduleSymbol) return yield* typeParserIssue("Node has no symbol", void 0, sourceFile);
32336
+ const contextSymbol = typeChecker.tryGetMemberInModuleExports("Context", moduleSymbol);
32337
+ if (!contextSymbol) return yield* typeParserIssue("Context not found", void 0, sourceFile);
32338
+ const tagSymbol = typeChecker.tryGetMemberInModuleExports("Tag", moduleSymbol);
32339
+ if (!tagSymbol) return yield* typeParserIssue("Tag not found", void 0, sourceFile);
32340
+ const tagType = typeChecker.getDeclaredTypeOfSymbol(tagSymbol);
32341
+ yield* contextTag(tagType, sourceFile);
32342
+ return sourceFile;
32343
+ }),
32344
+ "TypeParser.isEffectContextSourceFile",
32345
+ (sourceFile) => sourceFile
32346
+ );
32347
+ const isNodeReferenceToEffectContextModuleApi = (memberName) => cachedBy(
32348
+ fn2("TypeParser.isNodeReferenceToEffectContextModuleApi")(function* (node) {
32349
+ return yield* isNodeReferenceToExportOfPackageModule(node, "effect", isEffectContextSourceFile, memberName);
32358
32350
  }),
32351
+ `TypeParser.isNodeReferenceToEffectContextModuleApi(${memberName})`,
32352
+ (node) => node
32353
+ );
32354
+ const importedContextModule = cachedBy(
32355
+ (node) => pipe(
32356
+ isNodeReferenceToPackageModule(node, "effect", isEffectContextSourceFile),
32357
+ map34(() => node)
32358
+ ),
32359
32359
  "TypeParser.importedContextModule",
32360
32360
  (node) => node
32361
32361
  );
32362
- const importedEffectModule = (node) => pipe(
32363
- isNodeReferenceToPackageModule(node, "effect", isEffectTypeSourceFile),
32364
- map34(() => node)
32362
+ const importedEffectModule = cachedBy(
32363
+ (node) => pipe(
32364
+ isNodeReferenceToPackageModule(node, "effect", isEffectTypeSourceFile),
32365
+ map34(() => node)
32366
+ ),
32367
+ "TypeParser.importedEffectModule",
32368
+ (node) => node
32369
+ );
32370
+ const isEffectDataSourceFile = cachedBy(
32371
+ fn2("TypeParser.isEffectDataSourceFile")(function* (sourceFile) {
32372
+ const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
32373
+ if (!moduleSymbol) return yield* typeParserIssue("Node has no symbol", void 0, sourceFile);
32374
+ const taggedEnumSymbol = typeChecker.tryGetMemberInModuleExports("TaggedEnum", moduleSymbol);
32375
+ if (!taggedEnumSymbol) return yield* typeParserIssue("TaggedEnum not found", void 0, sourceFile);
32376
+ const taggedErrorSymbol = typeChecker.tryGetMemberInModuleExports("TaggedError", moduleSymbol);
32377
+ if (!taggedErrorSymbol) return yield* typeParserIssue("TaggedError not found", void 0, sourceFile);
32378
+ return sourceFile;
32379
+ }),
32380
+ "TypeParser.isEffectDataSourceFile",
32381
+ (sourceFile) => sourceFile
32365
32382
  );
32366
- const importedDataModule = cachedBy(
32367
- fn2("TypeParser.importedDataModule")(function* (node) {
32368
- if (!ts.isIdentifier(node)) {
32369
- return yield* typeParserIssue("Node is not an expression", void 0, node);
32370
- }
32371
- const type2 = typeChecker.getTypeAtLocation(node);
32372
- const propertySymbol = typeChecker.getPropertyOfType(type2, "TaggedError");
32373
- if (!propertySymbol) {
32374
- return yield* typeParserIssue("Type has no 'TaggedError' property", type2, node);
32375
- }
32376
- const sourceFile = tsUtils.getSourceFileOfNode(node);
32377
- if (!sourceFile) {
32378
- return yield* typeParserIssue("Node is not in a source file", void 0, node);
32379
- }
32380
- const dataIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(
32381
- sourceFile,
32382
- "effect",
32383
- "Data"
32384
- );
32385
- if (!dataIdentifier) {
32386
- return yield* typeParserIssue("Data module not found", void 0, node);
32387
- }
32388
- if (ts.idText(node) !== dataIdentifier) {
32389
- return yield* typeParserIssue("Node is not a data module reference", void 0, node);
32390
- }
32391
- return node;
32383
+ const isNodeReferenceToEffectDataModuleApi = (memberName) => cachedBy(
32384
+ fn2("TypeParser.isNodeReferenceToEffectDataModuleApi")(function* (node) {
32385
+ return yield* isNodeReferenceToExportOfPackageModule(node, "effect", isEffectDataSourceFile, memberName);
32392
32386
  }),
32387
+ `TypeParser.isNodeReferenceToEffectDataModuleApi(${memberName})`,
32388
+ (node) => node
32389
+ );
32390
+ const importedDataModule = cachedBy(
32391
+ (node) => pipe(
32392
+ isNodeReferenceToPackageModule(node, "effect", isEffectDataSourceFile),
32393
+ map34(() => node)
32394
+ ),
32393
32395
  "TypeParser.importedDataModule",
32394
32396
  (node) => node
32395
32397
  );
@@ -33128,6 +33130,8 @@ function make64(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
33128
33130
  return {
33129
33131
  isNodeReferenceToEffectModuleApi,
33130
33132
  isNodeReferenceToEffectSchemaModuleApi,
33133
+ isNodeReferenceToEffectDataModuleApi,
33134
+ isNodeReferenceToEffectContextModuleApi,
33131
33135
  effectType,
33132
33136
  strictEffectType,
33133
33137
  layerType,
@@ -35680,6 +35684,85 @@ Consider extracting the Runtime by using for example Effect.runtime and then use
35680
35684
  })
35681
35685
  });
35682
35686
 
35687
+ // src/diagnostics/schemaStructWithTag.ts
35688
+ var schemaStructWithTag = createDiagnostic({
35689
+ name: "schemaStructWithTag",
35690
+ code: 34,
35691
+ severity: "suggestion",
35692
+ apply: fn2("schemaStructWithTag.apply")(function* (sourceFile, report) {
35693
+ const ts = yield* service2(TypeScriptApi);
35694
+ const typeParser = yield* service2(TypeParser);
35695
+ const nodeToVisit = [];
35696
+ const appendNodeToVisit = (node) => {
35697
+ nodeToVisit.push(node);
35698
+ return void 0;
35699
+ };
35700
+ ts.forEachChild(sourceFile, appendNodeToVisit);
35701
+ while (nodeToVisit.length > 0) {
35702
+ const node = nodeToVisit.shift();
35703
+ if (ts.isCallExpression(node)) {
35704
+ const isSchemaStructCall = yield* pipe(
35705
+ typeParser.isNodeReferenceToEffectSchemaModuleApi("Struct")(node.expression),
35706
+ orElse14(() => void_8)
35707
+ );
35708
+ if (isSchemaStructCall && node.arguments.length === 1) {
35709
+ const arg = node.arguments[0];
35710
+ if (ts.isObjectLiteralExpression(arg)) {
35711
+ const tagProperty = arg.properties.find(
35712
+ (prop) => ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && ts.idText(prop.name) === "_tag"
35713
+ );
35714
+ if (tagProperty && ts.isCallExpression(tagProperty.initializer)) {
35715
+ const isSchemaLiteralCall = yield* pipe(
35716
+ typeParser.isNodeReferenceToEffectSchemaModuleApi("Literal")(
35717
+ tagProperty.initializer.expression
35718
+ ),
35719
+ option4
35720
+ );
35721
+ if (isSchemaLiteralCall._tag === "Some") {
35722
+ const literalCall = tagProperty.initializer;
35723
+ const literalArgs = fromIterable(literalCall.arguments);
35724
+ if (literalArgs.length === 1 && ts.isStringLiteral(literalArgs[0])) {
35725
+ const tagValue = literalArgs[0].text;
35726
+ const otherProperties = arg.properties.filter((prop) => prop !== tagProperty);
35727
+ report({
35728
+ location: node,
35729
+ messageText: "Schema.Struct with a _tag field can be simplified to Schema.TaggedStruct to make the tag optional in the constructor.",
35730
+ fixes: [{
35731
+ fixName: "schemaStructWithTag_fix",
35732
+ description: "Replace with Schema.TaggedStruct",
35733
+ apply: gen3(function* () {
35734
+ const changeTracker = yield* service2(ChangeTracker);
35735
+ const newObjectLiteral = ts.factory.createObjectLiteralExpression(
35736
+ otherProperties,
35737
+ true
35738
+ );
35739
+ const newNode = ts.factory.createCallExpression(
35740
+ ts.factory.createPropertyAccessExpression(
35741
+ // Reuse the Schema identifier from the original expression
35742
+ ts.isPropertyAccessExpression(node.expression) ? node.expression.expression : ts.factory.createIdentifier("Schema"),
35743
+ "TaggedStruct"
35744
+ ),
35745
+ void 0,
35746
+ [
35747
+ ts.factory.createStringLiteral(tagValue),
35748
+ newObjectLiteral
35749
+ ]
35750
+ );
35751
+ changeTracker.replaceNode(sourceFile, node, newNode);
35752
+ })
35753
+ }]
35754
+ });
35755
+ }
35756
+ }
35757
+ }
35758
+ }
35759
+ }
35760
+ }
35761
+ ts.forEachChild(node, appendNodeToVisit);
35762
+ }
35763
+ })
35764
+ });
35765
+
35683
35766
  // src/diagnostics/schemaUnionOfLiterals.ts
35684
35767
  var schemaUnionOfLiterals = createDiagnostic({
35685
35768
  name: "schemaUnionOfLiterals",
@@ -36371,7 +36454,8 @@ var diagnostics = [
36371
36454
  strictEffectProvide,
36372
36455
  unknownInEffectCatch,
36373
36456
  runEffectInsideEffect,
36374
- schemaUnionOfLiterals
36457
+ schemaUnionOfLiterals,
36458
+ schemaStructWithTag
36375
36459
  ];
36376
36460
 
36377
36461
  // src/cli/diagnostics.ts