Skip to content

Commit 6e2eaff

Browse files
authored
add pnp error handling and test (#3)
* add pnpapi test * remove cycle dependency * add error handling * add error message * apply error message * add error bubbling * code clean up * remove useless test * add empty findBrokenPeerDependencies * early return * change function name * apply code review * merge duplicate logic * apply collect usage * apply codereview * apply fromConfig
1 parent 9aa7f3e commit 6e2eaff

File tree

11 files changed

+18952
-65
lines changed

11 files changed

+18952
-65
lines changed

internal/core/compileroptions.go

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"sync"
77

88
"github.com/microsoft/typescript-go/internal/collections"
9-
"github.com/microsoft/typescript-go/internal/pnp"
109
"github.com/microsoft/typescript-go/internal/tspath"
1110
)
1211

@@ -301,10 +300,17 @@ func (options *CompilerOptions) GetStrictOptionValue(value Tristate) bool {
301300
return options.Strict == TSTrue
302301
}
303302

304-
func (options *CompilerOptions) GetEffectiveTypeRoots(currentDirectory string, pnpApi *pnp.PnpApi) (result []string, fromConfig bool) {
303+
func (options *CompilerOptions) GetEffectiveTypeRoots(currentDirectory string) (result []string, fromConfig bool) {
305304
if options.TypeRoots != nil {
306305
return options.TypeRoots, true
307306
}
307+
baseDir := options.GetBaseDirFromOptions(currentDirectory)
308+
309+
nmTypes, nmFromConfig := options.GetNodeModulesTypeRoots(baseDir)
310+
return nmTypes, nmFromConfig
311+
}
312+
313+
func (options *CompilerOptions) GetBaseDirFromOptions(currentDirectory string) string {
308314
var baseDir string
309315
if options.ConfigFilePath != "" {
310316
baseDir = tspath.GetDirectoryPath(options.ConfigFilePath)
@@ -316,15 +322,7 @@ func (options *CompilerOptions) GetEffectiveTypeRoots(currentDirectory string, p
316322
panic("cannot get effective type roots without a config file path or current directory")
317323
}
318324
}
319-
320-
nmTypes, nmFromConfig := options.GetNodeModulesTypeRoots(baseDir)
321-
322-
if pnpApi != nil {
323-
typeRoots, fromConfig := pnpApi.AppendPnpTypeRoots(nmTypes, baseDir, nmFromConfig)
324-
return typeRoots, fromConfig
325-
}
326-
327-
return nmTypes, nmFromConfig
325+
return baseDir
328326
}
329327

330328
func (options *CompilerOptions) GetNodeModulesTypeRoots(baseDir string) (result []string, fromConfig bool) {

internal/diagnostics/diagnostics_generated.go

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/diagnostics/extraDiagnosticMessages.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,41 @@
3434
"Project '{0}' is out of date because it has errors.": {
3535
"category": "Message",
3636
"code": 6423
37+
},
38+
"Your application tried to access '{0}'. While this module is usually interpreted as a Node builtin, your resolver is running inside a non-Node resolution context where such builtins are ignored. Since '{0}' isn't otherwise declared in your dependencies, this makes the require call ambiguous and unsound.\n\nRequired package: {0}{1}\nRequired by: {2}": {
39+
"category": "Error",
40+
"code": 100003
41+
},
42+
"{0} tried to access '{1}'. While this module is usually interpreted as a Node builtin, your resolver is running inside a non-Node resolution context where such builtins are ignored. Since '{1}' isn't otherwise declared in {0}'s dependencies, this makes the require call ambiguous and unsound.\n\nRequired package: {1}{2}\nRequired by: {3}": {
43+
"category": "Error",
44+
"code": 100004
45+
},
46+
"Your application tried to access '{0}', but it isn't declared in your dependencies; this makes the require call ambiguous and unsound.\n\nRequired package: {0}{1}\nRequired by: {2}": {
47+
"category": "Error",
48+
"code": 100005
49+
},
50+
"Your application tried to access '{0}' (a peer dependency); this isn't allowed as there is no ancestor to satisfy the requirement. Use a devDependency if needed.\n\nRequired package: {0}\nRequired by: {1}": {
51+
"category": "Error",
52+
"code": 100006
53+
},
54+
"{0} tried to access '{1}' (a peer dependency) but it isn't provided by its ancestors/your application; this makes the require call ambiguous and unsound.\n\nRequired package: {1}\nRequired by: {2}": {
55+
"category": "Error",
56+
"code": 100007
57+
},
58+
"no PnP manifest found": {
59+
"category": "Error",
60+
"code": 100008
61+
},
62+
"no package found for path '{0}'": {
63+
"category": "Error",
64+
"code": 100009
65+
},
66+
"Empty specifier: '{0}'": {
67+
"category": "Error",
68+
"code": 100010
69+
},
70+
"Invalid specifier: '{0}'": {
71+
"category": "Error",
72+
"code": 100011
3773
}
3874
}

internal/module/resolver.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,10 @@ func (r *Resolver) ResolveTypeReferenceDirective(
207207
compilerOptions := GetCompilerOptionsWithRedirect(r.compilerOptions, redirectedReference)
208208
containingDirectory := tspath.GetDirectoryPath(containingFile)
209209

210-
typeRoots, fromConfig := compilerOptions.GetEffectiveTypeRoots(r.host.GetCurrentDirectory(), r.host.PnpApi())
210+
typeRoots, fromConfig := compilerOptions.GetEffectiveTypeRoots(r.host.GetCurrentDirectory())
211+
if pnpApi := r.host.PnpApi(); pnpApi != nil {
212+
typeRoots, fromConfig = pnpApi.AppendPnpTypeRoots(typeRoots, r.host.GetCurrentDirectory(), compilerOptions, fromConfig)
213+
}
211214
if traceBuilder != nil {
212215
traceBuilder.write(diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_2.Format(typeReferenceDirectiveName, containingFile, strings.Join(typeRoots, ",")))
213216
traceBuilder.traceResolutionUsingProjectReference(redirectedReference)
@@ -983,8 +986,13 @@ func (r *resolutionState) loadModuleFromPnpResolution(ext extensions, moduleName
983986

984987
if pnpApi != nil {
985988
packageName, rest := ParsePackageName(moduleName)
986-
// TODO: bubble up yarn resolution errors, instead of _
987-
packageDirectory, _ := pnpApi.ResolveToUnqualified(packageName, issuer)
989+
packageDirectory, err := pnpApi.ResolveToUnqualified(packageName, issuer)
990+
if err != nil {
991+
if r.tracer != nil {
992+
r.tracer.write(err.Error())
993+
}
994+
return nil
995+
}
988996
if packageDirectory != "" {
989997
candidate := tspath.NormalizePath(tspath.CombinePaths(packageDirectory, rest))
990998
return r.loadModuleFromSpecificNodeModulesDirectoryImpl(ext, true /* nodeModulesDirectoryExists */, candidate, rest, packageDirectory)
@@ -1790,7 +1798,14 @@ func (r *resolutionState) readPackageJsonPeerDependencies(packageJsonInfo *packa
17901798
var peerDependencyPath string
17911799

17921800
if pnpApi != nil {
1793-
peerDependencyPath, _ = pnpApi.ResolveToUnqualified(name, packageDirectory)
1801+
var err error
1802+
peerDependencyPath, err = pnpApi.ResolveToUnqualified(name, packageDirectory)
1803+
if err != nil {
1804+
if r.tracer != nil {
1805+
r.tracer.write(err.Error())
1806+
}
1807+
continue
1808+
}
17941809
}
17951810

17961811
if peerDependencyPath == "" {
@@ -2040,7 +2055,10 @@ func GetAutomaticTypeDirectiveNames(options *core.CompilerOptions, host Resoluti
20402055
}
20412056

20422057
var result []string
2043-
typeRoots, _ := options.GetEffectiveTypeRoots(host.GetCurrentDirectory(), host.PnpApi())
2058+
typeRoots, fromConfig := options.GetEffectiveTypeRoots(host.GetCurrentDirectory())
2059+
if pnpApi := host.PnpApi(); pnpApi != nil {
2060+
typeRoots, fromConfig = pnpApi.AppendPnpTypeRoots(typeRoots, host.GetCurrentDirectory(), options, fromConfig)
2061+
}
20442062
for _, root := range typeRoots {
20452063
if host.FS().DirectoryExists(root) {
20462064
for _, typeDirectivePath := range host.FS().GetAccessibleEntries(root).Directories {

internal/pnp/manifestparser.go

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/dlclark/regexp2"
1111
"github.com/microsoft/typescript-go/internal/tspath"
12+
"github.com/microsoft/typescript-go/internal/vfs"
1213
)
1314

1415
type LinkType string
@@ -74,40 +75,47 @@ type PnpManifestData struct {
7475
packageRegistryTrie *PackageRegistryTrie
7576
}
7677

77-
func parseManifestFromPath(fs PnpApiFS, manifestDir string) (*PnpManifestData, error) {
78+
func parseManifestFromPath(fs vfs.FS, manifestDir string) (*PnpManifestData, error) {
7879
pnpDataString := ""
7980

8081
data, ok := fs.ReadFile(tspath.CombinePaths(manifestDir, ".pnp.data.json"))
8182
if ok {
8283
pnpDataString = data
8384
} else {
84-
pnpScriptString, ok := fs.ReadFile(tspath.CombinePaths(manifestDir, ".pnp.cjs"))
85-
if !ok {
86-
return nil, errors.New("failed to read .pnp.cjs file")
85+
dataString, err := extractPnpDataStringFromPath(fs, tspath.CombinePaths(manifestDir, ".pnp.cjs"))
86+
if err != nil {
87+
return nil, err
8788
}
89+
pnpDataString = dataString
90+
}
8891

89-
manifestRegex := regexp2.MustCompile(`(const[ \r\n]+RAW_RUNTIME_STATE[ \r\n]*=[ \r\n]*|hydrateRuntimeState\(JSON\.parse\()'`, regexp2.None)
90-
matches, err := manifestRegex.FindStringMatch(pnpScriptString)
91-
if err != nil || matches == nil {
92-
return nil, errors.New("We failed to locate the PnP data payload inside its manifest file. Did you manually edit the file?")
93-
}
92+
return parseManifestFromData(pnpDataString, manifestDir)
93+
}
9494

95-
start := matches.Index + matches.Length
96-
var b strings.Builder
97-
b.Grow(len(pnpScriptString))
98-
for i := start; i < len(pnpScriptString); i++ {
99-
if pnpScriptString[i] == '\'' {
100-
break
101-
}
95+
func extractPnpDataStringFromPath(fs vfs.FS, path string) (string, error) {
96+
pnpScriptString, ok := fs.ReadFile(path)
97+
if !ok {
98+
return "", errors.New("failed to read file: " + path)
99+
}
100+
manifestRegex := regexp2.MustCompile(`(const[ \r\n]+RAW_RUNTIME_STATE[ \r\n]*=[ \r\n]*|hydrateRuntimeState\(JSON\.parse\()'`, regexp2.None)
101+
matches, err := manifestRegex.FindStringMatch(pnpScriptString)
102+
if err != nil || matches == nil {
103+
return "", errors.New("we failed to locate the PnP data payload inside its manifest file. Did you manually edit the file?")
104+
}
102105

103-
if pnpScriptString[i] != '\\' {
104-
b.WriteByte(pnpScriptString[i])
105-
}
106+
start := matches.Index + matches.Length
107+
var b strings.Builder
108+
b.Grow(len(pnpScriptString))
109+
for i := start; i < len(pnpScriptString); i++ {
110+
if pnpScriptString[i] == '\'' {
111+
break
106112
}
107-
pnpDataString = b.String()
108-
}
109113

110-
return parseManifestFromData(pnpDataString, manifestDir)
114+
if pnpScriptString[i] != '\\' {
115+
b.WriteByte(pnpScriptString[i])
116+
}
117+
}
118+
return b.String(), nil
111119
}
112120

113121
func parseManifestFromData(pnpDataString string, manifestDir string) (*PnpManifestData, error) {

internal/pnp/pnp.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package pnp
22

3-
import "strings"
3+
import (
4+
"strings"
45

5-
func InitPnpApi(fs PnpApiFS, filePath string) *PnpApi {
6+
"github.com/microsoft/typescript-go/internal/vfs"
7+
)
8+
9+
func InitPnpApi(fs vfs.FS, filePath string) *PnpApi {
610
pnpApi := &PnpApi{fs: fs, url: filePath}
711

812
manifestData, err := pnpApi.findClosestPnpManifest()

0 commit comments

Comments
 (0)