From 92df01f551a27ef153ae7b751db2fd2f440acbc3 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 29 Apr 2026 16:54:59 +0800 Subject: [PATCH] Skip Effect diagnostics for external libraries --- .changeset/calm-cloudflare-types.md | 5 + _patches/002-checker-checker.patch | 8 ++ _patches/018-ls-autoimport-stylepolicy.patch | 15 +++ etscheckerhooks/external_library_test.go | 98 ++++++++++++++++++++ etscheckerhooks/init.go | 9 +- flake.lock | 16 ++-- 6 files changed, 138 insertions(+), 13 deletions(-) create mode 100644 .changeset/calm-cloudflare-types.md create mode 100644 etscheckerhooks/external_library_test.go diff --git a/.changeset/calm-cloudflare-types.md b/.changeset/calm-cloudflare-types.md new file mode 100644 index 00000000..39d2bcb4 --- /dev/null +++ b/.changeset/calm-cloudflare-types.md @@ -0,0 +1,5 @@ +--- +"@effect/tsgo": patch +--- + +Skip Effect diagnostics for source files resolved from external libraries. diff --git a/_patches/002-checker-checker.patch b/_patches/002-checker-checker.patch index f3332caa..eb3bb47c 100644 --- a/_patches/002-checker-checker.patch +++ b/_patches/002-checker-checker.patch @@ -43,6 +43,14 @@ index 2e1622418..66059cab4 100644 // CheckMode type CheckMode uint32 +@@ -559,6 +593,7 @@ type Program interface { + GetImportHelpersImportSpecifier(path tspath.Path) *ast.Node + SourceFileMayBeEmitted(sourceFile *ast.SourceFile, forceDtsEmit bool) bool + IsSourceFileDefaultLibrary(path tspath.Path) bool ++ IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool + GetProjectReferenceFromOutputDts(path tspath.Path) *tsoptions.SourceOutputAndProjectReference + GetRedirectForResolution(file ast.HasFileName) *tsoptions.ParsedCommandLine + CommonSourceDirectory() string @@ -2145,6 +2179,8 @@ func (c *Checker) checkSourceFile(ctx context.Context, sourceFile *ast.SourceFil links := c.sourceFileLinks.Get(sourceFile) if !links.typeChecked { diff --git a/_patches/018-ls-autoimport-stylepolicy.patch b/_patches/018-ls-autoimport-stylepolicy.patch index b2a7c833..d1c921a1 100644 --- a/_patches/018-ls-autoimport-stylepolicy.patch +++ b/_patches/018-ls-autoimport-stylepolicy.patch @@ -126,3 +126,18 @@ index b2107443f..bf5161047 100644 } // getAddAsTypeOnly determines if an import should be type-only based on usage context +diff --git a/internal/ls/autoimport/aliasresolver.go b/internal/ls/autoimport/aliasresolver.go +--- a/internal/ls/autoimport/aliasresolver.go ++++ b/internal/ls/autoimport/aliasresolver.go +@@ -220,6 +220,11 @@ func (r *aliasResolver) IsSourceFileDefaultLibrary(path tspath.Path) bool { + panic("unimplemented") + } + ++// IsSourceFileFromExternalLibrary implements checker.Program. ++func (r *aliasResolver) IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool { ++ return false ++} ++ + // IsSourceFromProjectReference implements checker.Program. + func (r *aliasResolver) IsSourceFromProjectReference(path tspath.Path) bool { + panic("unimplemented") diff --git a/etscheckerhooks/external_library_test.go b/etscheckerhooks/external_library_test.go new file mode 100644 index 00000000..db9cee37 --- /dev/null +++ b/etscheckerhooks/external_library_test.go @@ -0,0 +1,98 @@ +package etscheckerhooks_test + +import ( + "context" + "strings" + "testing" + "testing/fstest" + + _ "github.com/effect-ts/tsgo/etscheckerhooks" + "github.com/effect-ts/tsgo/etscore" + "github.com/microsoft/typescript-go/shim/ast" + "github.com/microsoft/typescript-go/shim/bundled" + "github.com/microsoft/typescript-go/shim/compiler" + "github.com/microsoft/typescript-go/shim/core" + "github.com/microsoft/typescript-go/shim/tsoptions" + "github.com/microsoft/typescript-go/shim/vfs/vfstest" +) + +func TestEffectDiagnosticsSkipExternalLibrarySourceFiles(t *testing.T) { + t.Parallel() + + testfs := map[string]any{ + "/src/main.ts": &fstest.MapFile{ + Data: []byte(`import { ExternalProblem } from "problem" + +export class LocalProblem extends Error {} +export const value = ExternalProblem +`), + }, + "/node_modules/problem/package.json": &fstest.MapFile{ + Data: []byte(`{"name":"problem","version":"1.0.0","types":"index.ts"}`), + }, + "/node_modules/problem/index.ts": &fstest.MapFile{ + Data: []byte(`export class ExternalProblem extends Error {}`), + }, + } + + fs := bundled.WrapFS(vfstest.FromMap(testfs, true)) + options := &core.CompilerOptions{ + NewLine: core.NewLineKindLF, + NoErrorTruncation: core.TSTrue, + Target: core.ScriptTargetESNext, + Module: core.ModuleKindCommonJS, + ModuleResolution: core.ModuleResolutionKindNode10, + Effect: &etscore.EffectPluginOptions{ + Diagnostics: true, + DiagnosticSeverity: map[string]etscore.Severity{ + "extendsNativeError": etscore.SeverityError, + }, + }, + } + program := compiler.NewProgram(compiler.ProgramOptions{ + Config: &tsoptions.ParsedCommandLine{ + ParsedConfig: &core.ParsedOptions{ + CompilerOptions: options, + FileNames: []string{"/src/main.ts"}, + }, + }, + Host: compiler.NewCompilerHost("/", fs, bundled.LibPath(), nil, nil), + SingleThreaded: core.TSTrue, + }) + + dependencySource := program.GetSourceFile("/node_modules/problem/index.ts") + if dependencySource == nil { + t.Fatal("expected dependency source file to be resolved") + } + if !program.IsSourceFileFromExternalLibrary(dependencySource) { + t.Fatal("expected dependency source file to be marked as external library") + } + + diagnostics := program.GetSemanticDiagnostics(context.Background(), nil) + localEffectDiagnostics := 0 + externalEffectDiagnostics := 0 + for _, diag := range diagnostics { + if !isEffectDiagnostic(diag) || diag.File() == nil { + continue + } + fileName := diag.File().FileName() + switch { + case strings.Contains(fileName, "/node_modules/"): + externalEffectDiagnostics++ + case fileName == "/src/main.ts": + localEffectDiagnostics++ + } + } + + if externalEffectDiagnostics != 0 { + t.Fatalf("expected no Effect diagnostics from external libraries, got %d", externalEffectDiagnostics) + } + if localEffectDiagnostics == 0 { + t.Fatal("expected Effect diagnostics for project source file") + } +} + +func isEffectDiagnostic(diag *ast.Diagnostic) bool { + code := diag.Code() + return code >= 377000 && code < 378000 +} diff --git a/etscheckerhooks/init.go b/etscheckerhooks/init.go index 44727349..63acc149 100644 --- a/etscheckerhooks/init.go +++ b/etscheckerhooks/init.go @@ -45,7 +45,9 @@ type RuleDiagnostic struct { // afterCheckSourceFile is called after type checking each source file. // It runs Effect diagnostics if the plugin is enabled. func afterCheckSourceFile(ctx context.Context, program checker.Program, c *checker.Checker, sf *ast.SourceFile) { - tp := typeparser.NewTypeParser(program, c) + if sf.IsDeclarationFile || program.IsSourceFileFromExternalLibrary(sf) { + return + } // Get Effect config from program options (parsed during config loading) effectConfig := getEffectConfig(program) @@ -71,10 +73,7 @@ func afterCheckSourceFile(ctx context.Context, program checker.Program, c *check return } - // Skip declaration files - if sf.IsDeclarationFile { - return - } + tp := typeparser.NewTypeParser(program, c) // Collect directives from source file for suppression support sourceText := sf.Text() diff --git a/flake.lock b/flake.lock index 9a0dae38..bda2ac45 100644 --- a/flake.lock +++ b/flake.lock @@ -43,34 +43,34 @@ "typescript-go-src": { "flake": false, "locked": { - "lastModified": 1773275612, - "narHash": "sha256-S5TnTCE3/e7ZyQgsBKYzBp5aqCvecflo1JAEL0LDv4Q=", + "lastModified": 1777424922, + "narHash": "sha256-ebjX+1+9axEqsnZXCRZSumA38gPNdmoz98tMt2oftN4=", "owner": "microsoft", "repo": "typescript-go", - "rev": "8b515c6ce5f821ed382cdb540c6df488738bb515", + "rev": "56ab4af421576f5326e0c08be5ac67149c053618", "type": "github" }, "original": { "owner": "microsoft", "repo": "typescript-go", - "rev": "8b515c6ce5f821ed382cdb540c6df488738bb515", + "rev": "56ab4af421576f5326e0c08be5ac67149c053618", "type": "github" } }, "typescript-src": { "flake": false, "locked": { - "lastModified": 1772506970, - "narHash": "sha256-qxZXsu81U3iZguCpHDCN47oghOE9LnAXkVxz3FNrTqY=", + "lastModified": 1775154140, + "narHash": "sha256-3WkY0R9tt5jdI2cOemHm91KztsNJYP/uuOE0SYn0SvM=", "owner": "microsoft", "repo": "TypeScript", - "rev": "2a3bed2b4265fa1173c88771a21ce044e6480f75", + "rev": "c3bd12d888b86f676718b16e64d7d2abcb423514", "type": "github" }, "original": { "owner": "microsoft", "repo": "TypeScript", - "rev": "2a3bed2b4265fa1173c88771a21ce044e6480f75", + "rev": "c3bd12d888b86f676718b16e64d7d2abcb423514", "type": "github" } }