@effect/language-service 0.53.2 → 0.54.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
@@ -69,6 +69,7 @@ And you're done! You'll now be able to use a set of refactors and diagnostics th
69
69
  - Warn when `@effect-diagnostics-next-line` comments have no effect (i.e., they don't suppress any diagnostic)
70
70
  - Detect nested function calls that can be converted to pipeable style for better readability
71
71
  - Warn when using catch functions (`catchAll`, `catch`, `catchIf`, `catchSome`, `catchTag`, `catchTags`) on effects that never fail
72
+ - Warn when catch callbacks in `Effect.tryPromise`, `Effect.tryMap`, or `Effect.tryMapPromise` return `unknown` or `any` types
72
73
 
73
74
  ### Completions
74
75
 
package/cli.js CHANGED
@@ -35288,6 +35288,63 @@ var tryCatchInEffectGen = createDiagnostic({
35288
35288
  })
35289
35289
  });
35290
35290
 
35291
+ // src/diagnostics/unknownInEffectCatch.ts
35292
+ var unknownInEffectCatch = createDiagnostic({
35293
+ name: "unknownInEffectCatch",
35294
+ code: 31,
35295
+ severity: "warning",
35296
+ apply: fn2("unknownInEffectCatch.apply")(function* (sourceFile, report) {
35297
+ const ts = yield* service2(TypeScriptApi);
35298
+ const typeParser = yield* service2(TypeParser);
35299
+ const typeChecker = yield* service2(TypeCheckerApi);
35300
+ const nodeToVisit = [];
35301
+ const appendNodeToVisit = (node) => {
35302
+ nodeToVisit.push(node);
35303
+ return void 0;
35304
+ };
35305
+ ts.forEachChild(sourceFile, appendNodeToVisit);
35306
+ while (nodeToVisit.length > 0) {
35307
+ const node = nodeToVisit.shift();
35308
+ ts.forEachChild(node, appendNodeToVisit);
35309
+ if (ts.isCallExpression(node)) {
35310
+ const isEffectWithCatch = yield* pipe(
35311
+ typeParser.isNodeReferenceToEffectModuleApi("tryPromise")(node.expression),
35312
+ orElse14(() => typeParser.isNodeReferenceToEffectModuleApi("try")(node.expression)),
35313
+ orElse14(() => typeParser.isNodeReferenceToEffectModuleApi("tryMap")(node.expression)),
35314
+ orElse14(() => typeParser.isNodeReferenceToEffectModuleApi("tryMapPromise")(node.expression)),
35315
+ orElse14(() => void_8)
35316
+ );
35317
+ if (isEffectWithCatch) {
35318
+ const signature = typeChecker.getResolvedSignature(node);
35319
+ if (signature) {
35320
+ const objectType = typeChecker.getParameterType(signature, 0);
35321
+ const catchFunctionSymbol = typeChecker.getPropertyOfType(objectType, "catch");
35322
+ if (catchFunctionSymbol) {
35323
+ const catchFunctionType = typeChecker.getTypeOfSymbolAtLocation(catchFunctionSymbol, node);
35324
+ const signatures = typeChecker.getSignaturesOfType(catchFunctionType, ts.SignatureKind.Call);
35325
+ if (signatures.length > 0) {
35326
+ const returnType = typeChecker.getReturnTypeOfSignature(signatures[0]);
35327
+ if (returnType && (returnType.flags & ts.TypeFlags.Unknown || returnType.flags & ts.TypeFlags.Any)) {
35328
+ const nodeText = sourceFile.text.substring(
35329
+ ts.getTokenPosOfNode(node.expression, sourceFile),
35330
+ node.expression.end
35331
+ );
35332
+ report({
35333
+ location: node.expression,
35334
+ messageText: `The 'catch' callback in ${nodeText} returns 'unknown'. The catch callback should be used to provide typed errors.
35335
+ Consider wrapping unknown errors into Effect's Data.TaggedError for example, or narrow down the type to the specific error raised.`,
35336
+ fixes: []
35337
+ });
35338
+ }
35339
+ }
35340
+ }
35341
+ }
35342
+ }
35343
+ }
35344
+ }
35345
+ })
35346
+ });
35347
+
35291
35348
  // src/diagnostics/unnecessaryEffectGen.ts
35292
35349
  var unnecessaryEffectGen = createDiagnostic({
35293
35350
  name: "unnecessaryEffectGen",
@@ -35535,7 +35592,8 @@ var diagnostics = [
35535
35592
  nonObjectEffectServiceType,
35536
35593
  deterministicKeys,
35537
35594
  missedPipeableOpportunity,
35538
- strictEffectProvide
35595
+ strictEffectProvide,
35596
+ unknownInEffectCatch
35539
35597
  ];
35540
35598
 
35541
35599
  // src/cli/diagnostics.ts
@@ -35596,6 +35654,13 @@ var diagnostics2 = make58(
35596
35654
  }
35597
35655
  const filesToCheckArray = fromIterable(filesToCheck);
35598
35656
  const batches = chunksOf(filesToCheckArray, BATCH_SIZE);
35657
+ let lastLanguageService;
35658
+ const disposeIfLanguageServiceChanged = (languageService) => {
35659
+ if (lastLanguageService !== languageService) {
35660
+ lastLanguageService?.dispose();
35661
+ lastLanguageService = languageService;
35662
+ }
35663
+ };
35599
35664
  for (const batch of batches) {
35600
35665
  const { service: service3 } = (0, import_project_service.createProjectService)({ options: { loadTypeScriptPlugins: false } });
35601
35666
  for (const filePath of batch) {
@@ -35605,6 +35670,7 @@ var diagnostics2 = make58(
35605
35670
  if (!scriptInfo) continue;
35606
35671
  const project4 = scriptInfo.getDefaultProject();
35607
35672
  const languageService = project4.getLanguageService(true);
35673
+ disposeIfLanguageServiceChanged(languageService);
35608
35674
  const program = languageService.getProgram();
35609
35675
  if (!program) continue;
35610
35676
  const sourceFile = program.getSourceFile(filePath);
@@ -35621,7 +35687,7 @@ var diagnostics2 = make58(
35621
35687
  provideService7(TypeScriptApi, tsInstance),
35622
35688
  provideService7(
35623
35689
  LanguageServicePluginOptions,
35624
- parse4(pluginConfig)
35690
+ { ...parse4(pluginConfig), diagnosticsName: false }
35625
35691
  ),
35626
35692
  run9,
35627
35693
  map((_) => _.diagnostics),
@@ -35637,11 +35703,14 @@ var diagnostics2 = make58(
35637
35703
  warningsCount += results.filter((_) => _.category === tsInstance.DiagnosticCategory.Warning).length;
35638
35704
  messagesCount += results.filter((_) => _.category === tsInstance.DiagnosticCategory.Message).length;
35639
35705
  if (results.length > 0) {
35640
- const formattedResults = tsInstance.formatDiagnosticsWithColorAndContext(results, {
35706
+ let formattedResults = tsInstance.formatDiagnosticsWithColorAndContext(results, {
35641
35707
  getCanonicalFileName: (fileName) => path2.resolve(fileName),
35642
35708
  getCurrentDirectory: () => path2.resolve("."),
35643
35709
  getNewLine: () => "\n"
35644
35710
  });
35711
+ Object.values(diagnostics).forEach(
35712
+ (_) => formattedResults = formattedResults.replace(new RegExp(`TS${_.code}:`, "g"), `effect(${_.name}):`)
35713
+ );
35645
35714
  console.log(formattedResults);
35646
35715
  }
35647
35716
  } finally {
@@ -35650,6 +35719,7 @@ var diagnostics2 = make58(
35650
35719
  }
35651
35720
  yield* yieldNow4();
35652
35721
  }
35722
+ disposeIfLanguageServiceChanged(void 0);
35653
35723
  console.log(
35654
35724
  `Checked ${checkedFilesCount} files out of ${filesToCheck.size} files.
35655
35725
  ${errorsCount} errors, ${warningsCount} warnings and ${messagesCount} messages.`
@@ -35782,7 +35852,7 @@ var getPatchesForModule = fn("getPatchesForModule")(
35782
35852
  sourceFile.text,
35783
35853
  insertCheckSourceFilePosition.value.position,
35784
35854
  insertCheckSourceFilePosition.value.position,
35785
- `effectLspPatchUtils().checkSourceFileWorker(${moduleName === "typescript" ? "module.exports" : "effectLspTypeScriptApis()"}, host, node, compilerOptions, diagnostics.add)
35855
+ `effectLspPatchUtils().checkSourceFileWorker(${moduleName === "typescript" ? "module.exports" : "effectLspTypeScriptApis()"}, host, node, compilerOptions, diagnostics.add, "${moduleName}")
35786
35856
  `,
35787
35857
  "\n",
35788
35858
  version