Skip to content

Commit

Permalink
extra log info for missing file extension (#2518)
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Sep 3, 2022
1 parent 77194c8 commit ccd8cd4
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 10 deletions.
1 change: 1 addition & 0 deletions internal/bundler/bundler_packagejson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1936,6 +1936,7 @@ func TestPackageJsonExportsNotExactMissingExtensionPattern(t *testing.T) {
},
expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1/foo/bar"
Users/user/project/node_modules/pkg1/package.json: NOTE: The module "./dir/bar" was not found on the file system:
Users/user/project/src/entry.js: NOTE: Import from "pkg1/foo/bar.js" to get the file "Users/user/project/node_modules/pkg1/dir/bar.js":
NOTE: You can mark the path "pkg1/foo/bar" as external to exclude it from the bundle, which will remove this error.
`,
})
Expand Down
12 changes: 9 additions & 3 deletions internal/resolver/package_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,7 @@ const (
pjStatusUndefinedNoConditionsMatch // A more friendly error message for when no conditions are matched
pjStatusNull
pjStatusExact
pjStatusExactEndsWithStar
pjStatusInexact // This means we may need to try CommonJS-style extension suffixes
pjStatusPackageResolve // Need to re-run package resolution on the result

Expand All @@ -713,6 +714,7 @@ const (

// The package or module requested does not exist.
pjStatusModuleNotFound
pjStatusModuleNotFoundMissingExtension // The user just needs to add the missing extension

// The resolved path corresponds to a directory, which is not a supported target for module imports.
pjStatusUnsupportedDirectoryImport
Expand All @@ -737,7 +739,7 @@ func (r resolverQuery) esmHandlePostConditions(
status pjStatus,
debug pjDebug,
) (string, pjStatus, pjDebug) {
if status != pjStatusExact && status != pjStatusInexact {
if status != pjStatusExact && status != pjStatusExactEndsWithStar && status != pjStatusInexact {
return resolved, status, debug
}

Expand Down Expand Up @@ -886,7 +888,7 @@ func (r resolverQuery) esmPackageImportsExportsResolve(
r.debugLogs.addNote(fmt.Sprintf("The key %q matched with %q left over", expansion.key, subpath))
}
result, status, debug := r.esmPackageTargetResolve(packageURL, target, subpath, false, isImports, conditions)
if status == pjStatusExact {
if status == pjStatusExact || status == pjStatusExactEndsWithStar {
// Return the object { resolved, exact: false }.
status = pjStatusInexact
}
Expand Down Expand Up @@ -1002,7 +1004,11 @@ func (r resolverQuery) esmPackageTargetResolve(
if r.debugLogs != nil {
r.debugLogs.addNote(fmt.Sprintf("Substituted %q for \"*\" in %q to get %q", subpath, "."+resolvedTarget, "."+result))
}
return result, pjStatusExact, pjDebug{token: target.firstToken}
status := pjStatusExact
if strings.HasSuffix(resolvedTarget, "*") && strings.IndexByte(resolvedTarget, '*') == len(resolvedTarget)-1 {
status = pjStatusExactEndsWithStar
}
return result, status, pjDebug{token: target.firstToken}
} else {
// Return the URL resolution of the concatenation of subpath and resolvedTarget.
result := path.Join(resolvedTarget, subpath)
Expand Down
57 changes: 50 additions & 7 deletions internal/resolver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,17 +128,29 @@ type ResolveResult struct {
UnusedImportFlagsTS config.UnusedImportFlagsTS
}

type suggestionRange uint8

const (
suggestionRangeFull suggestionRange = iota
suggestionRangeEnd
)

type DebugMeta struct {
notes []logger.MsgData
suggestionText string
suggestionMessage string
notes []logger.MsgData
suggestionRange suggestionRange
}

func (dm DebugMeta) LogErrorMsg(log logger.Log, source *logger.Source, r logger.Range, text string, suggestion string, notes []logger.MsgData) {
tracker := logger.MakeLineColumnTracker(source)

if source != nil && dm.suggestionMessage != "" {
data := tracker.MsgData(r, dm.suggestionMessage)
suggestionRange := r
if dm.suggestionRange == suggestionRangeEnd {
suggestionRange = logger.Range{Loc: logger.Loc{Start: r.End() - 1}}
}
data := tracker.MsgData(suggestionRange, dm.suggestionMessage)
data.Location.Suggestion = dm.suggestionText
dm.notes = append(dm.notes, data)
}
Expand Down Expand Up @@ -2133,19 +2145,41 @@ func (r resolverQuery) finalizeImportsExportsResult(
esmPackageSubpath string,
absImportPath string,
) (PathPair, bool, *fs.DifferentCase) {
if (status == pjStatusExact || status == pjStatusInexact) && strings.HasPrefix(resolvedPath, "/") {
absResolvedPath := r.fs.Join(absDirPath, resolvedPath[1:])
missingExtension := ""

if (status == pjStatusExact || status == pjStatusExactEndsWithStar || status == pjStatusInexact) && strings.HasPrefix(resolvedPath, "/") {
absResolvedPath := r.fs.Join(absDirPath, resolvedPath)

switch status {
case pjStatusExact:
case pjStatusExact, pjStatusExactEndsWithStar:
if r.debugLogs != nil {
r.debugLogs.addNote(fmt.Sprintf("The resolved path %q is exact", absResolvedPath))
}
resolvedDirInfo := r.dirInfoCached(r.fs.Dir(absResolvedPath))
base := r.fs.Base(absResolvedPath)
if resolvedDirInfo == nil {
status = pjStatusModuleNotFound
} else if entry, diffCase := resolvedDirInfo.entries.Get(r.fs.Base(absResolvedPath)); entry == nil {
} else if entry, diffCase := resolvedDirInfo.entries.Get(base); entry == nil {
endsWithStar := status == pjStatusExactEndsWithStar
status = pjStatusModuleNotFound

// Try to have a friendly error message if people forget the extension
if endsWithStar {
extensionOrder := r.options.ExtensionOrder
if r.kind == ast.ImportAt || r.kind == ast.ImportAtConditional {
extensionOrder = r.atImportExtensionOrder
}
for _, ext := range extensionOrder {
if entry, _ := resolvedDirInfo.entries.Get(base + ext); entry != nil {
if r.debugLogs != nil {
r.debugLogs.addNote(fmt.Sprintf("The import %q is missing the extension %q", path.Join(esmPackageName, esmPackageSubpath), ext))
}
status = pjStatusModuleNotFoundMissingExtension
missingExtension = ext
break
}
}
}
} else if kind := entry.Kind(r.fs); kind == fs.DirEntry {
if r.debugLogs != nil {
r.debugLogs.addNote(fmt.Sprintf("The path %q is a directory, which is not allowed", absResolvedPath))
Expand Down Expand Up @@ -2228,10 +2262,19 @@ func (r resolverQuery) finalizeImportsExportsResult(
r.debugMeta.notes = []logger.MsgData{tracker.MsgData(debug.token,
fmt.Sprintf("The package import %q is not defined in this \"imports\" map:", resolvedPath))}

case pjStatusModuleNotFound:
case pjStatusModuleNotFound, pjStatusModuleNotFoundMissingExtension:
r.debugMeta.notes = []logger.MsgData{tracker.MsgData(debug.token,
fmt.Sprintf("The module %q was not found on the file system:", resolvedPath))}

// Provide an inline suggestion message with the correct import path
if status == pjStatusModuleNotFoundMissingExtension {
actualImportPath := path.Join(esmPackageName, esmPackageSubpath+missingExtension)
r.debugMeta.suggestionRange = suggestionRangeEnd
r.debugMeta.suggestionText = missingExtension
r.debugMeta.suggestionMessage = fmt.Sprintf("Import from %q to get the file %q:",
actualImportPath, r.PrettyPath(logger.Path{Text: r.fs.Join(absDirPath, resolvedPath+missingExtension), Namespace: "file"}))
}

case pjStatusUnsupportedDirectoryImport:
r.debugMeta.notes = []logger.MsgData{tracker.MsgData(debug.token,
fmt.Sprintf("Importing the directory %q is not supported:", resolvedPath))}
Expand Down

0 comments on commit ccd8cd4

Please sign in to comment.