From 81fa2ca2e71a0518fe1e411276593ef6ea21a380 Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Fri, 30 Sep 2022 21:04:54 -0400 Subject: [PATCH] more info in yarn pnp error messages (#2585) --- internal/js_parser/js_parser.go | 27 +++- internal/js_parser/json_parser.go | 12 +- internal/logger/logger.go | 209 ++++++++++++++++-------------- internal/resolver/resolver.go | 36 +++-- internal/resolver/yarnpnp.go | 65 +++++++--- internal/resolver/yarnpnp_test.go | 6 +- 6 files changed, 220 insertions(+), 135 deletions(-) diff --git a/internal/js_parser/js_parser.go b/internal/js_parser/js_parser.go index 6dddc9e651a..ee63c90477f 100644 --- a/internal/js_parser/js_parser.go +++ b/internal/js_parser/js_parser.go @@ -14150,15 +14150,19 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO case *js_ast.EString: // "hydrateRuntimeState(JSON.parse())" source := logger.Source{KeyPath: p.source.KeyPath, Contents: helpers.UTF16ToString(a.Value)} - log := logger.NewStringInJSLog(p.log, &p.tracker, arg.Loc, source.Contents) + stringInJSTable := logger.GenerateStringInJSTable(p.source.Contents, arg.Loc, source.Contents) + log := logger.NewStringInJSLog(p.log, &p.tracker, stringInJSTable) p.manifestForYarnPnP, _ = ParseJSON(log, source, JSONOptions{}) + remapExprLocsInJSON(&p.manifestForYarnPnP, stringInJSTable) case *js_ast.EIdentifier: // "hydrateRuntimeState(JSON.parse())" if data, ok := p.stringLocalsForYarnPnP[a.Ref]; ok { source := logger.Source{KeyPath: p.source.KeyPath, Contents: helpers.UTF16ToString(data.value)} - log := logger.NewStringInJSLog(p.log, &p.tracker, data.loc, source.Contents) + stringInJSTable := logger.GenerateStringInJSTable(p.source.Contents, data.loc, source.Contents) + log := logger.NewStringInJSLog(p.log, &p.tracker, stringInJSTable) p.manifestForYarnPnP, _ = ParseJSON(log, source, JSONOptions{}) + remapExprLocsInJSON(&p.manifestForYarnPnP, stringInJSTable) } } } @@ -14521,6 +14525,25 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO return expr, exprOut{} } +func remapExprLocsInJSON(expr *js_ast.Expr, table []logger.StringInJSTableEntry) { + expr.Loc = logger.RemapStringInJSLoc(table, expr.Loc) + + switch e := expr.Data.(type) { + case *js_ast.EArray: + e.CloseBracketLoc = logger.RemapStringInJSLoc(table, e.CloseBracketLoc) + for i := range e.Items { + remapExprLocsInJSON(&e.Items[i], table) + } + + case *js_ast.EObject: + e.CloseBraceLoc = logger.RemapStringInJSLoc(table, e.CloseBraceLoc) + for i := range e.Properties { + remapExprLocsInJSON(&e.Properties[i].Key, table) + remapExprLocsInJSON(&e.Properties[i].ValueOrNil, table) + } + } +} + func (p *parser) convertSymbolUseToCall(ref js_ast.Ref, isSingleNonSpreadArgCall bool) { // Remove the normal symbol use use := p.symbolUses[ref] diff --git a/internal/js_parser/json_parser.go b/internal/js_parser/json_parser.go index f2dabe99b69..f5befe81d55 100644 --- a/internal/js_parser/json_parser.go +++ b/internal/js_parser/json_parser.go @@ -89,10 +89,12 @@ func (p *jsonParser) parseExpr() js_ast.Expr { if p.lexer.HasNewlineBefore { isSingleLine = false } + closeBracketLoc := p.lexer.Loc() p.lexer.Expect(js_lexer.TCloseBracket) return js_ast.Expr{Loc: loc, Data: &js_ast.EArray{ - Items: items, - IsSingleLine: isSingleLine, + Items: items, + IsSingleLine: isSingleLine, + CloseBracketLoc: closeBracketLoc, }} case js_lexer.TOpenBrace: @@ -146,10 +148,12 @@ func (p *jsonParser) parseExpr() js_ast.Expr { if p.lexer.HasNewlineBefore { isSingleLine = false } + closeBraceLoc := p.lexer.Loc() p.lexer.Expect(js_lexer.TCloseBrace) return js_ast.Expr{Loc: loc, Data: &js_ast.EObject{ - Properties: properties, - IsSingleLine: isSingleLine, + Properties: properties, + IsSingleLine: isSingleLine, + CloseBraceLoc: closeBraceLoc, }} default: diff --git a/internal/logger/logger.go b/internal/logger/logger.go index db4ce764650..2121e8424f9 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -1701,111 +1701,135 @@ func allowOverride(overrides map[MsgID]LogLevel, id MsgID, kind MsgKind) (MsgKin return kind, true } -// For Yarn PnP we sometimes parse JSON embedded in a JS string. This is a shim -// that remaps log message locations inside the embedded string literal into -// log messages in the actual JS file, which makes them easier to understand. -func NewStringInJSLog(log Log, outerTracker *LineColumnTracker, outerStringLiteralLoc Loc, innerContents string) Log { - type entry struct { - line int32 - column int32 - loc Loc - } - - var table []entry - oldAddMsg := log.AddMsg +type StringInJSTableEntry struct { + innerLine int32 + innerColumn int32 + innerLoc Loc + outerLoc Loc +} + +// For Yarn PnP we sometimes parse JSON embedded in a JS string. This generates +// a table that remaps locations inside the embedded JSON string literal into +// locations in the actual JS file, which makes them easier to understand. +func GenerateStringInJSTable(outerContents string, outerStringLiteralLoc Loc, innerContents string) (table []StringInJSTableEntry) { + i := int32(0) + n := int32(len(innerContents)) + line := int32(1) + column := int32(0) + loc := Loc{Start: outerStringLiteralLoc.Start + 1} + + for i < n { + // Ignore line continuations. A line continuation is not an escaped newline. + for { + if c, _ := utf8.DecodeRuneInString(outerContents[loc.Start:]); c != '\\' { + break + } + c, width := utf8.DecodeRuneInString(outerContents[loc.Start+1:]) + switch c { + case '\n', '\r', '\u2028', '\u2029': + loc.Start += 1 + int32(width) + if c == '\r' && outerContents[loc.Start] == '\n' { + // Make sure Windows CRLF counts as a single newline + loc.Start++ + } + continue + } + break + } - generateTable := func() { - i := 0 - n := len(innerContents) - line := int32(1) - column := int32(0) - loc := Loc{Start: outerStringLiteralLoc.Start + 1} - outerContents := outerTracker.contents + c, width := utf8.DecodeRuneInString(innerContents[i:]) - for i < n { - // Ignore line continuations. A line continuation is not an escaped newline. - for { - if c, _ := utf8.DecodeRuneInString(outerContents[loc.Start:]); c != '\\' { - break - } - c, width := utf8.DecodeRuneInString(outerContents[loc.Start+1:]) - switch c { - case '\n', '\r', '\u2028', '\u2029': - loc.Start += 1 + int32(width) - if c == '\r' && outerContents[loc.Start] == '\n' { - // Make sure Windows CRLF counts as a single newline - loc.Start++ - } - continue - } - break + // Compress the table using run-length encoding + table = append(table, StringInJSTableEntry{innerLine: line, innerColumn: column, innerLoc: Loc{Start: i}, outerLoc: loc}) + if len(table) > 1 { + if last := table[len(table)-2]; line == last.innerLine && loc.Start-column == last.outerLoc.Start-last.innerColumn { + table = table[:len(table)-1] } + } - c, width := utf8.DecodeRuneInString(innerContents[i:]) + // Advance the inner line/column + switch c { + case '\n', '\r', '\u2028', '\u2029': + line++ + column = 0 - // Compress the table using run-length encoding - table = append(table, entry{line: line, column: column, loc: loc}) - if len(table) > 1 { - if last := table[len(table)-2]; line == last.line && loc.Start-column == last.loc.Start-last.column { - table = table[:len(table)-1] - } + // Handle newlines on Windows + if c == '\r' && i+1 < n && innerContents[i+1] == '\n' { + i++ } - // Advance the inner line/column + default: + column += int32(width) + } + i += int32(width) + + // Advance the outer loc, assuming the string syntax is already valid + c, width = utf8.DecodeRuneInString(outerContents[loc.Start:]) + if c == '\r' && outerContents[loc.Start+1] == '\n' { + // Handle newlines on Windows in template literal strings + loc.Start += 2 + } else if c != '\\' { + loc.Start += int32(width) + } else { + // Handle an escape sequence + c, width = utf8.DecodeRuneInString(outerContents[loc.Start+1:]) switch c { - case '\n', '\r', '\u2028', '\u2029': - line++ - column = 0 - - // Handle newlines on Windows - if c == '\r' && i+1 < n && innerContents[i+1] == '\n' { - i++ + case 'x': + // 2-digit hexadecimal + loc.Start += 1 + 2 + + case 'u': + loc.Start++ + if outerContents[loc.Start] == '{' { + // Variable-length + for outerContents[loc.Start] != '}' { + loc.Start++ + } + loc.Start++ + } else { + // Fixed-length + loc.Start += 4 } + case '\n', '\r', '\u2028', '\u2029': + // This will be handled by the next iteration + break + default: - column += int32(width) + loc.Start += 1 + int32(width) } - i += width - - // Advance the outer loc, assuming the string syntax is already valid - c, width = utf8.DecodeRuneInString(outerContents[loc.Start:]) - if c == '\r' && outerContents[loc.Start+1] == '\n' { - // Handle newlines on Windows in template literal strings - loc.Start += 2 - } else if c != '\\' { - loc.Start += int32(width) - } else { - // Handle an escape sequence - c, width = utf8.DecodeRuneInString(outerContents[loc.Start+1:]) - switch c { - case 'x': - // 2-digit hexadecimal - loc.Start += 1 + 2 - - case 'u': - loc.Start++ - if outerContents[loc.Start] == '{' { - // Variable-length - for outerContents[loc.Start] != '}' { - loc.Start++ - } - loc.Start++ - } else { - // Fixed-length - loc.Start += 4 - } + } + } - case '\n', '\r', '\u2028', '\u2029': - // This will be handled by the next iteration - break + return +} - default: - loc.Start += 1 + int32(width) - } +func RemapStringInJSLoc(table []StringInJSTableEntry, innerLoc Loc) Loc { + count := len(table) + index := 0 + + // Binary search to find the previous entry + for count > 0 { + step := count / 2 + i := index + step + if i+1 < len(table) { + if entry := table[i+1]; entry.innerLoc.Start < innerLoc.Start { + index = i + 1 + count -= step + 1 + continue } } + count = step } + entry := table[index] + entry.outerLoc.Start += innerLoc.Start - entry.innerLoc.Start // Undo run-length compression + return entry.outerLoc +} + +func NewStringInJSLog(log Log, outerTracker *LineColumnTracker, table []StringInJSTableEntry) Log { + oldAddMsg := log.AddMsg + remapLineAndColumnToLoc := func(line int32, column int32) Loc { count := len(table) index := 0 @@ -1815,7 +1839,7 @@ func NewStringInJSLog(log Log, outerTracker *LineColumnTracker, outerStringLiter step := count / 2 i := index + step if i+1 < len(table) { - if entry := table[i+1]; entry.line < line || (entry.line == line && entry.column < column) { + if entry := table[i+1]; entry.innerLine < line || (entry.innerLine == line && entry.innerColumn < column) { index = i + 1 count -= step + 1 continue @@ -1825,8 +1849,8 @@ func NewStringInJSLog(log Log, outerTracker *LineColumnTracker, outerStringLiter } entry := table[index] - entry.loc.Start += column - entry.column // Undo run-length compression - return entry.loc + entry.outerLoc.Start += column - entry.innerColumn // Undo run-length compression + return entry.outerLoc } remapData := func(data MsgData) MsgData { @@ -1834,11 +1858,6 @@ func NewStringInJSLog(log Log, outerTracker *LineColumnTracker, outerStringLiter return data } - // Generate and cache a lookup table to accelerate remappings - if table == nil { - generateTable() - } - // Generate a range in the outer source using the line/column/length in the inner source r := Range{Loc: remapLineAndColumnToLoc(int32(data.Location.Line), int32(data.Location.Column))} if data.Location.Length != 0 { diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go index 6814e7b2897..ae7b879d5d3 100644 --- a/internal/resolver/resolver.go +++ b/internal/resolver/resolver.go @@ -437,12 +437,12 @@ func (rr *resolver) Resolve(sourceDir string, importPath string, kind ast.Import for dirInfo := r.dirInfoCached(r.fs.Cwd()); dirInfo != nil; dirInfo = dirInfo.parent { if absPath := dirInfo.pnpManifestAbsPath; absPath != "" { if strings.HasSuffix(absPath, ".json") { - if json := r.extractYarnPnPDataFromJSON(absPath, pnpReportErrorsAboutMissingFiles); json.Data != nil { - r.pnpManifest = compileYarnPnPData(absPath, r.fs.Dir(absPath), json) + if json, source := r.extractYarnPnPDataFromJSON(absPath, pnpReportErrorsAboutMissingFiles); json.Data != nil { + r.pnpManifest = compileYarnPnPData(absPath, r.fs.Dir(absPath), json, source) } } else { - if json := r.tryToExtractYarnPnPDataFromJS(absPath, pnpReportErrorsAboutMissingFiles); json.Data != nil { - r.pnpManifest = compileYarnPnPData(absPath, r.fs.Dir(absPath), json) + if json, source := r.tryToExtractYarnPnPDataFromJS(absPath, pnpReportErrorsAboutMissingFiles); json.Data != nil { + r.pnpManifest = compileYarnPnPData(absPath, r.fs.Dir(absPath), json, source) } } if r.debugLogs != nil && r.pnpManifest != nil && r.pnpManifest.invalidIgnorePatternData != "" { @@ -979,20 +979,20 @@ func (r resolverQuery) parseTSConfig(file string, visited map[string]bool) (*TSC for { if _, _, ok := fs.ParseYarnPnPVirtualPath(current); !ok { absPath := r.fs.Join(current, ".pnp.data.json") - if json := r.extractYarnPnPDataFromJSON(absPath, pnpIgnoreErrorsAboutMissingFiles); json.Data != nil { - pnpData = compileYarnPnPData(absPath, current, json) + if json, source := r.extractYarnPnPDataFromJSON(absPath, pnpIgnoreErrorsAboutMissingFiles); json.Data != nil { + pnpData = compileYarnPnPData(absPath, current, json, source) break } absPath = r.fs.Join(current, ".pnp.cjs") - if json := r.tryToExtractYarnPnPDataFromJS(absPath, pnpIgnoreErrorsAboutMissingFiles); json.Data != nil { - pnpData = compileYarnPnPData(absPath, current, json) + if json, source := r.tryToExtractYarnPnPDataFromJS(absPath, pnpIgnoreErrorsAboutMissingFiles); json.Data != nil { + pnpData = compileYarnPnPData(absPath, current, json, source) break } absPath = r.fs.Join(current, ".pnp.js") - if json := r.tryToExtractYarnPnPDataFromJS(absPath, pnpIgnoreErrorsAboutMissingFiles); json.Data != nil { - pnpData = compileYarnPnPData(absPath, current, json) + if json, source := r.tryToExtractYarnPnPDataFromJS(absPath, pnpIgnoreErrorsAboutMissingFiles); json.Data != nil { + pnpData = compileYarnPnPData(absPath, current, json, source) break } } @@ -1007,7 +1007,7 @@ func (r resolverQuery) parseTSConfig(file string, visited map[string]bool) (*TSC } if pnpData != nil { - if result := r.resolveToUnqualified(extends, fileDir, pnpData); result.status == pnpError { + if result := r.resolveToUnqualified(extends, fileDir, pnpData); result.status == pnpErrorGeneric { if r.debugLogs != nil { r.debugLogs.addNote("The Yarn PnP path resolution algorithm returned an error") } @@ -2012,10 +2012,22 @@ func (r resolverQuery) loadNodeModules(importPath string, dirInfo *dirInfo, forb // If Yarn PnP is active, use it to find the package if r.pnpManifest != nil { - if result := r.resolveToUnqualified(importPath, dirInfo.absPath, r.pnpManifest); result.status == pnpError { + if result := r.resolveToUnqualified(importPath, dirInfo.absPath, r.pnpManifest); result.status.isError() { if r.debugLogs != nil { r.debugLogs.addNote("The Yarn PnP path resolution algorithm returned an error") } + + // Try to provide more information about this error if it's available + switch result.status { + case pnpErrorDependencyNotFound: + r.debugMeta.notes = []logger.MsgData{r.pnpManifest.tracker.MsgData(result.errorRange, + fmt.Sprintf("The Yarn Plug'n'Play manifest forbids importing %q here because it's not listed as a dependency of this package:", result.errorIdent))} + + case pnpErrorUnfulfilledPeerDependency: + r.debugMeta.notes = []logger.MsgData{r.pnpManifest.tracker.MsgData(result.errorRange, + fmt.Sprintf("The Yarn Plug'n'Play manifest says this package has a peer dependency on %q, but the package %q has not been installed:", result.errorIdent, result.errorIdent))} + } + return PathPair{}, false, nil } else if result.status == pnpSuccess { absPath := r.fs.Join(result.pkgDirPath, result.pkgSubpath) diff --git a/internal/resolver/yarnpnp.go b/internal/resolver/yarnpnp.go index b8fa98c1821..ab2e1849cb7 100644 --- a/internal/resolver/yarnpnp.go +++ b/internal/resolver/yarnpnp.go @@ -47,6 +47,7 @@ type pnpData struct { // the fallback pool, even if not listed here. enableTopLevelFallback bool + tracker logger.LineColumnTracker absPath string absDirPath string } @@ -65,12 +66,14 @@ type pnpData struct { type pnpIdentAndReference struct { ident string // Empty if null reference string // Empty if null + span logger.Range } type pnpPackage struct { - packageDependencies map[string]pnpIdentAndReference - packageLocation string - discardFromLookup bool + packageDependencies map[string]pnpIdentAndReference + packageLocation string + packageDependenciesRange logger.Range + discardFromLookup bool } type pnpPackageLocatorByLocation struct { @@ -117,16 +120,26 @@ func parseBareIdentifier(specifier string) (ident string, modulePath string, ok type pnpStatus uint8 const ( - pnpError pnpStatus = iota + pnpErrorGeneric pnpStatus = iota + pnpErrorDependencyNotFound + pnpErrorUnfulfilledPeerDependency pnpSuccess pnpSkipped ) +func (status pnpStatus) isError() bool { + return status < pnpSuccess +} + type pnpResult struct { status pnpStatus pkgDirPath string pkgIdent string pkgSubpath string + + // This is for error messages + errorIdent string + errorRange logger.Range } // Note: If this returns successfully then the node module resolution algorithm @@ -147,7 +160,7 @@ func (r resolverQuery) resolveToUnqualified(specifier string, parentURL string, if r.debugLogs != nil { r.debugLogs.addNote(fmt.Sprintf(" Failed to parse specifier %q into a bare identifier", specifier)) } - return pnpResult{status: pnpError} + return pnpResult{status: pnpErrorGeneric} } if r.debugLogs != nil { r.debugLogs.addNote(fmt.Sprintf(" Parsed bare identifier %q and module path %q", ident, modulePath)) @@ -169,7 +182,7 @@ func (r resolverQuery) resolveToUnqualified(specifier string, parentURL string, parentPkg, ok := r.getPackage(manifest, parentLocator.ident, parentLocator.reference) if !ok { // We aren't supposed to get here according to the Yarn PnP specification - return pnpResult{status: pnpError} + return pnpResult{status: pnpErrorGeneric} } if r.debugLogs != nil { r.debugLogs.addNote(fmt.Sprintf(" Found parent package at %q", parentPkg.packageLocation)) @@ -211,14 +224,22 @@ func (r resolverQuery) resolveToUnqualified(specifier string, parentURL string, // If referenceOrAlias is still undefined, then if !ok { // Throw a resolution error - return pnpResult{status: pnpError} + return pnpResult{ + status: pnpErrorDependencyNotFound, + errorIdent: ident, + errorRange: parentPkg.packageDependenciesRange, + } } // If referenceOrAlias is still null, then if referenceOrAlias.reference == "" { // Note: It means that parentPkg has an unfulfilled peer dependency on ident // Throw a resolution error - return pnpResult{status: pnpError} + return pnpResult{ + status: pnpErrorUnfulfilledPeerDependency, + errorIdent: ident, + errorRange: referenceOrAlias.span, + } } if r.debugLogs != nil { @@ -241,7 +262,7 @@ func (r resolverQuery) resolveToUnqualified(specifier string, parentURL string, dependencyPkg, ok = r.getPackage(manifest, alias.ident, alias.reference) if !ok { // We aren't supposed to get here according to the Yarn PnP specification - return pnpResult{status: pnpError} + return pnpResult{status: pnpErrorGeneric} } } else { // Otherwise, @@ -249,7 +270,7 @@ func (r resolverQuery) resolveToUnqualified(specifier string, parentURL string, dependencyPkg, ok = r.getPackage(manifest, ident, referenceOrAlias.reference) if !ok { // We aren't supposed to get here according to the Yarn PnP specification - return pnpResult{status: pnpError} + return pnpResult{status: pnpErrorGeneric} } } if r.debugLogs != nil { @@ -385,10 +406,11 @@ func quoteOrNullIfEmpty(str string) string { return "null" } -func compileYarnPnPData(absPath string, absDirPath string, json js_ast.Expr) *pnpData { +func compileYarnPnPData(absPath string, absDirPath string, json js_ast.Expr, source logger.Source) *pnpData { data := pnpData{ absPath: absPath, absDirPath: absDirPath, + tracker: logger.MakeLineColumnTracker(&source), } if value, _, ok := getProperty(json, "enableTopLevelFallback"); ok { @@ -505,7 +527,11 @@ func compileYarnPnPData(absPath string, absDirPath string, json js_ast.Expr) *pn references[packageReference] = pnpPackage{ packageLocation: packageLocation, packageDependencies: deps, - discardFromLookup: discardFromLookup, + packageDependenciesRange: logger.Range{ + Loc: packageDependencies.Loc, + Len: array3.CloseBracketLoc.Start + 1 - packageDependencies.Loc.Start, + }, + discardFromLookup: discardFromLookup, } // This is what Yarn's PnP implementation does (specifically in @@ -554,10 +580,10 @@ func getStringOrNull(json js_ast.Expr) (string, bool) { func getDependencyTarget(json js_ast.Expr) (pnpIdentAndReference, bool) { switch d := json.Data.(type) { case *js_ast.ENull: - return pnpIdentAndReference{}, true + return pnpIdentAndReference{span: logger.Range{Loc: json.Loc, Len: 4}}, true case *js_ast.EString: - return pnpIdentAndReference{reference: helpers.UTF16ToString(d.Value)}, true + return pnpIdentAndReference{reference: helpers.UTF16ToString(d.Value), span: logger.Range{Loc: json.Loc}}, true case *js_ast.EArray: if len(d.Items) == 2 { @@ -566,6 +592,7 @@ func getDependencyTarget(json js_ast.Expr) (pnpIdentAndReference, bool) { return pnpIdentAndReference{ ident: name, reference: reference, + span: logger.Range{Loc: json.Loc, Len: d.CloseBracketLoc.Start + 1 - json.Loc.Start}, }, true } } @@ -582,7 +609,7 @@ const ( pnpReportErrorsAboutMissingFiles ) -func (r resolverQuery) extractYarnPnPDataFromJSON(pnpDataPath string, mode pnpDataMode) (result js_ast.Expr) { +func (r resolverQuery) extractYarnPnPDataFromJSON(pnpDataPath string, mode pnpDataMode) (result js_ast.Expr, source logger.Source) { contents, err, originalError := r.caches.FSCache.ReadFile(r.fs, pnpDataPath) if r.debugLogs != nil && originalError != nil { r.debugLogs.addNote(fmt.Sprintf("Failed to read file %q: %s", pnpDataPath, originalError.Error())) @@ -599,7 +626,7 @@ func (r resolverQuery) extractYarnPnPDataFromJSON(pnpDataPath string, mode pnpDa r.debugLogs.addNote(fmt.Sprintf("The file %q exists", pnpDataPath)) } keyPath := logger.Path{Text: pnpDataPath, Namespace: "file"} - source := logger.Source{ + source = logger.Source{ KeyPath: keyPath, PrettyPath: r.PrettyPath(keyPath), Contents: contents, @@ -608,7 +635,7 @@ func (r resolverQuery) extractYarnPnPDataFromJSON(pnpDataPath string, mode pnpDa return } -func (r resolverQuery) tryToExtractYarnPnPDataFromJS(pnpDataPath string, mode pnpDataMode) (result js_ast.Expr) { +func (r resolverQuery) tryToExtractYarnPnPDataFromJS(pnpDataPath string, mode pnpDataMode) (result js_ast.Expr, source logger.Source) { contents, err, originalError := r.caches.FSCache.ReadFile(r.fs, pnpDataPath) if r.debugLogs != nil && originalError != nil { r.debugLogs.addNote(fmt.Sprintf("Failed to read file %q: %s", pnpDataPath, originalError.Error())) @@ -626,7 +653,7 @@ func (r resolverQuery) tryToExtractYarnPnPDataFromJS(pnpDataPath string, mode pn } keyPath := logger.Path{Text: pnpDataPath, Namespace: "file"} - source := logger.Source{ + source = logger.Source{ KeyPath: keyPath, PrettyPath: r.PrettyPath(keyPath), Contents: contents, @@ -636,5 +663,5 @@ func (r resolverQuery) tryToExtractYarnPnPDataFromJS(pnpDataPath string, mode pn if r.debugLogs != nil && ast.ManifestForYarnPnP.Data != nil { r.debugLogs.addNote(fmt.Sprintf(" Extracted JSON data from %q", pnpDataPath)) } - return ast.ManifestForYarnPnP + return ast.ManifestForYarnPnP, source } diff --git a/internal/resolver/yarnpnp_test.go b/internal/resolver/yarnpnp_test.go index 6f487d0c17d..3a033bc9946 100644 --- a/internal/resolver/yarnpnp_test.go +++ b/internal/resolver/yarnpnp_test.go @@ -62,7 +62,7 @@ func TestYarnPnP(t *testing.T) { t.Fatalf("Log not empty after re-parsing JSON: %s", path) } - manifest := compileYarnPnPData(path, "/path/to/project/", expr) + manifest := compileYarnPnPData(path, "/path/to/project/", expr, source) for _, current := range expectation.Tests { func(current pnpTest) { @@ -74,12 +74,12 @@ func TestYarnPnP(t *testing.T) { var observed string switch result.status { - case pnpError: - observed = "error!" case pnpSuccess: observed = fs.Join(result.pkgDirPath, result.pkgSubpath) case pnpSkipped: observed = current.Imported + default: + observed = "error!" } // If a we aren't going through PnP, then we should just run the