@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/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
@@ -140,7 +141,8 @@ Few options can be provided alongside the initialization of the Language Service
140
141
  "importAliases": { "Array": "Arr" }, // allows to chose some different names for import name aliases (only when not chosing to import the whole module) (default: {})
141
142
  "noExternal": false, // disables features that provides links to external websites (such as links to mermaidchart.com) (default: false)
142
143
  "keyPatterns": [{ "target": "service", "pattern": "default", "skipLeadingPath": ["src/"] }], // configure the key patterns; recommended reading more on the section "Configuring Key Patterns"
143
- "layerGraphFollowDepth": 0 // controls the depth level that the layer graph will follow when resolving layer dependencies, depth is counted only when exiting the currently hovered/analyzed layer definition (default: 0)
144
+ "layerGraphFollowDepth": 0, // controls the depth level that the layer graph will follow when resolving layer dependencies, depth is counted only when exiting the currently hovered/analyzed layer definition (default: 0)
145
+ "mermaidProvider": "mermaid.live" // which provider to use for mermaid, can also be a uri like http://localhost:8080 if running mermaid-live-editor locally.
144
146
  }
145
147
  ]
146
148
  }
package/cli.js CHANGED
@@ -31275,7 +31275,8 @@ var defaults = {
31275
31275
  }],
31276
31276
  extendedKeyDetection: false,
31277
31277
  pipeableMinArgCount: 1,
31278
- layerGraphFollowDepth: 0
31278
+ layerGraphFollowDepth: 0,
31279
+ mermaidProvider: "mermaid.live"
31279
31280
  };
31280
31281
  function parseKeyPatterns(patterns) {
31281
31282
  const result = [];
@@ -31313,7 +31314,8 @@ function parse4(config2) {
31313
31314
  keyPatterns: isObject(config2) && hasProperty(config2, "keyPatterns") && isArray(config2.keyPatterns) ? parseKeyPatterns(config2.keyPatterns) : defaults.keyPatterns,
31314
31315
  extendedKeyDetection: isObject(config2) && hasProperty(config2, "extendedKeyDetection") && isBoolean(config2.extendedKeyDetection) ? config2.extendedKeyDetection : defaults.extendedKeyDetection,
31315
31316
  pipeableMinArgCount: isObject(config2) && hasProperty(config2, "pipeableMinArgCount") && isNumber(config2.pipeableMinArgCount) ? config2.pipeableMinArgCount : defaults.pipeableMinArgCount,
31316
- layerGraphFollowDepth: isObject(config2) && hasProperty(config2, "layerGraphFollowDepth") && isNumber(config2.layerGraphFollowDepth) ? config2.layerGraphFollowDepth : defaults.layerGraphFollowDepth
31317
+ layerGraphFollowDepth: isObject(config2) && hasProperty(config2, "layerGraphFollowDepth") && isNumber(config2.layerGraphFollowDepth) ? config2.layerGraphFollowDepth : defaults.layerGraphFollowDepth,
31318
+ mermaidProvider: isObject(config2) && hasProperty(config2, "mermaidProvider") && isString(config2.mermaidProvider) ? config2.mermaidProvider : defaults.mermaidProvider
31317
31319
  };
31318
31320
  }
31319
31321
 
@@ -32329,67 +32331,69 @@ function make64(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
32329
32331
  "TypeParser.effectSubtype",
32330
32332
  (type2) => type2
32331
32333
  );
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;
32334
+ const isEffectContextSourceFile = cachedBy(
32335
+ fn2("TypeParser.isEffectContextSourceFile")(function* (sourceFile) {
32336
+ const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
32337
+ if (!moduleSymbol) return yield* typeParserIssue("Node has no symbol", void 0, sourceFile);
32338
+ const contextSymbol = typeChecker.tryGetMemberInModuleExports("Context", moduleSymbol);
32339
+ if (!contextSymbol) return yield* typeParserIssue("Context not found", void 0, sourceFile);
32340
+ const tagSymbol = typeChecker.tryGetMemberInModuleExports("Tag", moduleSymbol);
32341
+ if (!tagSymbol) return yield* typeParserIssue("Tag not found", void 0, sourceFile);
32342
+ const tagType = typeChecker.getDeclaredTypeOfSymbol(tagSymbol);
32343
+ yield* contextTag(tagType, sourceFile);
32344
+ return sourceFile;
32345
+ }),
32346
+ "TypeParser.isEffectContextSourceFile",
32347
+ (sourceFile) => sourceFile
32348
+ );
32349
+ const isNodeReferenceToEffectContextModuleApi = (memberName) => cachedBy(
32350
+ fn2("TypeParser.isNodeReferenceToEffectContextModuleApi")(function* (node) {
32351
+ return yield* isNodeReferenceToExportOfPackageModule(node, "effect", isEffectContextSourceFile, memberName);
32358
32352
  }),
32353
+ `TypeParser.isNodeReferenceToEffectContextModuleApi(${memberName})`,
32354
+ (node) => node
32355
+ );
32356
+ const importedContextModule = cachedBy(
32357
+ (node) => pipe(
32358
+ isNodeReferenceToPackageModule(node, "effect", isEffectContextSourceFile),
32359
+ map34(() => node)
32360
+ ),
32359
32361
  "TypeParser.importedContextModule",
32360
32362
  (node) => node
32361
32363
  );
32362
- const importedEffectModule = (node) => pipe(
32363
- isNodeReferenceToPackageModule(node, "effect", isEffectTypeSourceFile),
32364
- map34(() => node)
32364
+ const importedEffectModule = cachedBy(
32365
+ (node) => pipe(
32366
+ isNodeReferenceToPackageModule(node, "effect", isEffectTypeSourceFile),
32367
+ map34(() => node)
32368
+ ),
32369
+ "TypeParser.importedEffectModule",
32370
+ (node) => node
32371
+ );
32372
+ const isEffectDataSourceFile = cachedBy(
32373
+ fn2("TypeParser.isEffectDataSourceFile")(function* (sourceFile) {
32374
+ const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
32375
+ if (!moduleSymbol) return yield* typeParserIssue("Node has no symbol", void 0, sourceFile);
32376
+ const taggedEnumSymbol = typeChecker.tryGetMemberInModuleExports("TaggedEnum", moduleSymbol);
32377
+ if (!taggedEnumSymbol) return yield* typeParserIssue("TaggedEnum not found", void 0, sourceFile);
32378
+ const taggedErrorSymbol = typeChecker.tryGetMemberInModuleExports("TaggedError", moduleSymbol);
32379
+ if (!taggedErrorSymbol) return yield* typeParserIssue("TaggedError not found", void 0, sourceFile);
32380
+ return sourceFile;
32381
+ }),
32382
+ "TypeParser.isEffectDataSourceFile",
32383
+ (sourceFile) => sourceFile
32365
32384
  );
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;
32385
+ const isNodeReferenceToEffectDataModuleApi = (memberName) => cachedBy(
32386
+ fn2("TypeParser.isNodeReferenceToEffectDataModuleApi")(function* (node) {
32387
+ return yield* isNodeReferenceToExportOfPackageModule(node, "effect", isEffectDataSourceFile, memberName);
32392
32388
  }),
32389
+ `TypeParser.isNodeReferenceToEffectDataModuleApi(${memberName})`,
32390
+ (node) => node
32391
+ );
32392
+ const importedDataModule = cachedBy(
32393
+ (node) => pipe(
32394
+ isNodeReferenceToPackageModule(node, "effect", isEffectDataSourceFile),
32395
+ map34(() => node)
32396
+ ),
32393
32397
  "TypeParser.importedDataModule",
32394
32398
  (node) => node
32395
32399
  );
@@ -33128,6 +33132,8 @@ function make64(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
33128
33132
  return {
33129
33133
  isNodeReferenceToEffectModuleApi,
33130
33134
  isNodeReferenceToEffectSchemaModuleApi,
33135
+ isNodeReferenceToEffectDataModuleApi,
33136
+ isNodeReferenceToEffectContextModuleApi,
33131
33137
  effectType,
33132
33138
  strictEffectType,
33133
33139
  layerType,
@@ -35680,6 +35686,85 @@ Consider extracting the Runtime by using for example Effect.runtime and then use
35680
35686
  })
35681
35687
  });
35682
35688
 
35689
+ // src/diagnostics/schemaStructWithTag.ts
35690
+ var schemaStructWithTag = createDiagnostic({
35691
+ name: "schemaStructWithTag",
35692
+ code: 34,
35693
+ severity: "suggestion",
35694
+ apply: fn2("schemaStructWithTag.apply")(function* (sourceFile, report) {
35695
+ const ts = yield* service2(TypeScriptApi);
35696
+ const typeParser = yield* service2(TypeParser);
35697
+ const nodeToVisit = [];
35698
+ const appendNodeToVisit = (node) => {
35699
+ nodeToVisit.push(node);
35700
+ return void 0;
35701
+ };
35702
+ ts.forEachChild(sourceFile, appendNodeToVisit);
35703
+ while (nodeToVisit.length > 0) {
35704
+ const node = nodeToVisit.shift();
35705
+ if (ts.isCallExpression(node)) {
35706
+ const isSchemaStructCall = yield* pipe(
35707
+ typeParser.isNodeReferenceToEffectSchemaModuleApi("Struct")(node.expression),
35708
+ orElse14(() => void_8)
35709
+ );
35710
+ if (isSchemaStructCall && node.arguments.length === 1) {
35711
+ const arg = node.arguments[0];
35712
+ if (ts.isObjectLiteralExpression(arg)) {
35713
+ const tagProperty = arg.properties.find(
35714
+ (prop) => ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && ts.idText(prop.name) === "_tag"
35715
+ );
35716
+ if (tagProperty && ts.isCallExpression(tagProperty.initializer)) {
35717
+ const isSchemaLiteralCall = yield* pipe(
35718
+ typeParser.isNodeReferenceToEffectSchemaModuleApi("Literal")(
35719
+ tagProperty.initializer.expression
35720
+ ),
35721
+ option4
35722
+ );
35723
+ if (isSchemaLiteralCall._tag === "Some") {
35724
+ const literalCall = tagProperty.initializer;
35725
+ const literalArgs = fromIterable(literalCall.arguments);
35726
+ if (literalArgs.length === 1 && ts.isStringLiteral(literalArgs[0])) {
35727
+ const tagValue = literalArgs[0].text;
35728
+ const otherProperties = arg.properties.filter((prop) => prop !== tagProperty);
35729
+ report({
35730
+ location: node,
35731
+ messageText: "Schema.Struct with a _tag field can be simplified to Schema.TaggedStruct to make the tag optional in the constructor.",
35732
+ fixes: [{
35733
+ fixName: "schemaStructWithTag_fix",
35734
+ description: "Replace with Schema.TaggedStruct",
35735
+ apply: gen3(function* () {
35736
+ const changeTracker = yield* service2(ChangeTracker);
35737
+ const newObjectLiteral = ts.factory.createObjectLiteralExpression(
35738
+ otherProperties,
35739
+ true
35740
+ );
35741
+ const newNode = ts.factory.createCallExpression(
35742
+ ts.factory.createPropertyAccessExpression(
35743
+ // Reuse the Schema identifier from the original expression
35744
+ ts.isPropertyAccessExpression(node.expression) ? node.expression.expression : ts.factory.createIdentifier("Schema"),
35745
+ "TaggedStruct"
35746
+ ),
35747
+ void 0,
35748
+ [
35749
+ ts.factory.createStringLiteral(tagValue),
35750
+ newObjectLiteral
35751
+ ]
35752
+ );
35753
+ changeTracker.replaceNode(sourceFile, node, newNode);
35754
+ })
35755
+ }]
35756
+ });
35757
+ }
35758
+ }
35759
+ }
35760
+ }
35761
+ }
35762
+ }
35763
+ ts.forEachChild(node, appendNodeToVisit);
35764
+ }
35765
+ })
35766
+ });
35767
+
35683
35768
  // src/diagnostics/schemaUnionOfLiterals.ts
35684
35769
  var schemaUnionOfLiterals = createDiagnostic({
35685
35770
  name: "schemaUnionOfLiterals",
@@ -36371,7 +36456,8 @@ var diagnostics = [
36371
36456
  strictEffectProvide,
36372
36457
  unknownInEffectCatch,
36373
36458
  runEffectInsideEffect,
36374
- schemaUnionOfLiterals
36459
+ schemaUnionOfLiterals,
36460
+ schemaStructWithTag
36375
36461
  ];
36376
36462
 
36377
36463
  // src/cli/diagnostics.ts