Source File
resolver.go
Belonging Package
github.com/evanw/esbuild/internal/resolver
package resolver
import (
)
config.PlatformBrowser: {"browser", "module", "main"},
config.PlatformNode: {"main", "module"},
config.PlatformNeutral: {},
}
IsSideEffectsArrayInJSON bool
}
type ResolveResult struct {
PathPair PathPair
PluginData interface{}
JSXFactory []string // Default if empty: "React.createElement"
JSXFragment []string // Default if empty: "React.Fragment"
IsExternal bool
DifferentCase *fs.DifferentCase
PreserveUnusedImportsTS bool
}
type Resolver interface {
Resolve(sourceDir string, importPath string, kind ast.ImportKind) *ResolveResult
ResolveAbs(absPath string) *ResolveResult
PrettyPath(path logger.Path) string
if .Platform == config.PlatformNode {
:= make(map[string]bool)
if .ExternalModules.NodeModules != nil {
for := range .ExternalModules.NodeModules {
[] = true
}
}
for := range BuiltInNodeModules {
[] = true
}
.ExternalModules.NodeModules =
}
:= make([]string, 0, len(.ExtensionOrder))
for , := range .ExtensionOrder {
if , := .ExtensionToLoader[]; && != config.LoaderCSS {
continue
}
= append(, )
}
return &resolver{
fs: ,
log: ,
options: ,
caches: ,
dirCache: make(map[string]*dirInfo),
atImportExtensionOrder: ,
}
}
if .isExternalPattern() ||
if .DecodeMIMEType() != MIMETypeUnsupported {
return &ResolveResult{
PathPair: PathPair{Primary: logger.Path{Text: , Namespace: "dataurl"}},
}
}
return &ResolveResult{
PathPair: PathPair{Primary: logger.Path{Text: }},
IsExternal: true,
}
}
:= strings.IndexAny(, "?#")
if < 1 {
return nil
}
= .resolveWithoutSymlinks(, [:], )
if == nil {
return nil
}
.PathPair.Primary.IgnoredSuffix = [:]
if .PathPair.HasSecondary() {
.PathPair.Secondary.IgnoredSuffix = [:]
}
}
.finalizeResolve()
return
}
func ( *resolver) ( string) bool {
for , := range .options.ExternalModules.Patterns {
if len() >= len(.Prefix)+len(.Suffix) &&
strings.HasPrefix(, .Prefix) &&
strings.HasSuffix(, .Suffix) {
return true
}
}
return false
}
func ( *resolver) ( string) *ResolveResult {
.mutex.Lock()
defer .mutex.Unlock()
:= &ResolveResult{PathPair: PathPair{Primary: logger.Path{Text: , Namespace: "file"}}}
.finalizeResolve()
return
}
func ( *resolver) ( string, string, ast.ImportKind) *ResolveResult {
:= .fs.Join(, )
.mutex.Lock()
defer .mutex.Unlock()
if , , := .loadAsFileOrDirectory(, ); {
:= &ResolveResult{PathPair: , DifferentCase: }
.finalizeResolve()
return
}
return nil
}
func ( string) bool {
if * == .PathPair.Primary {
for := ; != nil; = .parent {
if .packageJSON != nil {
if .packageJSON.sideEffectsMap != nil {
:= false
= true
for , := range .packageJSON.sideEffectsRegexps {
if .MatchString(.Text) {
= true
break
}
}
}
if ! {
.IgnorePrimaryIfUnused = .packageJSON.ignoreIfUnusedData
}
}
break
}
}
}
if == &.PathPair.Primary && .tsConfigJSON != nil {
.JSXFactory = .tsConfigJSON.JSXFactory
.JSXFragment = .tsConfigJSON.JSXFragmentFactory
.UseDefineForClassFieldsTS = .tsConfigJSON.UseDefineForClassFields
.PreserveUnusedImportsTS = .tsConfigJSON.PreserveImportsNotUsedAsValues
}
if !.options.PreserveSymlinks {
if , := .entries.Get(); != nil {
.Text =
.Text = .fs.Join(.absRealPath, )
}
}
}
}
}
}
}
var ResolveResult
if := .dirInfoCached(); != nil && .tsConfigJSON != nil && .tsConfigJSON.Paths != nil {
if , , := .matchTSConfigPaths(.tsConfigJSON, , ); {
return &ResolveResult{PathPair: , DifferentCase: }
}
}
return &ResolveResult{PathPair: PathPair{Primary: logger.Path{Text: }}, IsExternal: true}
}
if , , := .loadAsFileOrDirectory(, ); {
return &ResolveResult{PathPair: , DifferentCase: }
}
return nil
}
:= IsPackagePath()
:= ! || == ast.ImportURL
:=
if {
:= .fs.Join(, )
if .options.ExternalModules.AbsPaths != nil && .options.ExternalModules.AbsPaths[] {
return &ResolveResult{PathPair: PathPair{Primary: logger.Path{Text: , Namespace: "file"}}, IsExternal: true}
}
:= .dirInfoCached(.fs.Dir())
if != nil && .enclosingBrowserScope != nil {
if := .enclosingBrowserScope.packageJSON; .browserNonPackageMap != nil {
if , := .browserNonPackageMap[]; {
if == nil {
return &ResolveResult{PathPair: PathPair{Primary: logger.Path{Text: , Namespace: "file", Flags: logger.PathDisabled}}}
} else if , , := .resolveWithoutRemapping(.enclosingBrowserScope, *, ); {
= ResolveResult{PathPair: , DifferentCase: }
= false
= false
}
}
}
}
if {
if , , := .loadAsFileOrDirectory(, ); {
= false
= ResolveResult{PathPair: , DifferentCase: }
} else if ! {
return nil
}
}
}
if .options.ExternalModules.NodeModules != nil {
:=
for {
if .options.ExternalModules.NodeModules[] {
return &ResolveResult{PathPair: PathPair{Primary: logger.Path{Text: }}, IsExternal: true}
}
:= strings.LastIndexByte(, '/')
if == -1 {
break
}
= [:]
}
}
:= .dirInfoCached()
return nil
}
if .enclosingBrowserScope != nil {
:= .enclosingBrowserScope.packageJSON
if .browserPackageMap != nil {
if , := .browserPackageMap[]; {
if , , := .loadNodeModules(, , ); {
.Primary = logger.Path{Text: .Primary.Text, Namespace: "file", Flags: logger.PathDisabled}
if .HasSecondary() {
.Secondary = logger.Path{Text: .Secondary.Text, Namespace: "file", Flags: logger.PathDisabled}
}
return &ResolveResult{PathPair: , DifferentCase: }
} else {
return &ResolveResult{PathPair: PathPair{Primary: logger.Path{Text: , Flags: logger.PathDisabled}}, DifferentCase: }
}
= *
= .enclosingBrowserScope
}
}
}
}
if , , := .resolveWithoutRemapping(, , ); {
= ResolveResult{PathPair: , DifferentCase: }
return nil
}
}
if != nil && .enclosingBrowserScope != nil {
:= .enclosingBrowserScope.packageJSON
if .browserNonPackageMap != nil {
if , := .browserNonPackageMap[.Text]; {
if == nil {
.Flags |= logger.PathDisabled
} else if , , := .resolveWithoutRemapping(.enclosingBrowserScope, *, ); {
* = .Primary
} else {
return nil
}
}
}
}
}
return &
}
func ( *resolver) ( *dirInfo, string, ast.ImportKind) (PathPair, bool, *fs.DifferentCase) {
if IsPackagePath() {
return .loadNodeModules(, , )
} else {
return .loadAsFileOrDirectory(.fs.Join(.absPath, ), )
}
}
func ( *resolver) ( logger.Path) string {
if .Namespace == "file" {
if , := .fs.Rel(.fs.Cwd(), .Text); {
.Text =
}
.Text = strings.ReplaceAll(.Text, "\\", "/")
} else if .Namespace != "" {
.Text = fmt.Sprintf("%s:%s", .Namespace, .Text)
}
if .IsDisabled() {
.Text = "(disabled):" + .Text
}
return .Text + .IgnoredSuffix
}
type packageJSON struct {
absMainFields map[string]string
absPath string
entries fs.DirEntries
hasNodeModules bool // Is there a "node_modules" subdirectory?
absPathIndex *string // Is there an "index.js" file?
packageJSON *packageJSON // Is there a "package.json" file?
tsConfigJSON *TSConfigJSON // Is there a "tsconfig.json" file in this directory or a parent directory?
absRealPath string // If non-empty, this is the real absolute path resolving any symlinks
}
, := .dirCache[]
if {
return
}
:= .dirInfoUncached()
.dirCache[] =
return
}
var parseErrorImportCycle = errors.New("(import cycle)")
var parseErrorAlreadyLogged = errors.New("(error already logged)")
if [] {
return nil, parseErrorImportCycle
}
[] = true
, := .caches.FSCache.ReadFile(.fs, )
if != nil {
return nil,
}
:= logger.Path{Text: , Namespace: "file"}
:= logger.Source{
KeyPath: ,
PrettyPath: .PrettyPath(),
Contents: ,
}
:= .fs.Dir()
:= ParseTSConfigJSON(.log, , &.caches.JSONCache, func( string, logger.Range) *TSConfigJSON {
:=
if .fs.Base() != "node_modules" {
:= .fs.Join(, "node_modules", )
:= []string{.fs.Join(, "tsconfig.json"), , + ".json"}
for , := range {
, := .(, )
if == nil {
return
} else if == syscall.ENOENT {
continue
} else if == parseErrorImportCycle {
.log.AddRangeWarning(&, ,
fmt.Sprintf("Base config file %q forms cycle", ))
} else if != parseErrorAlreadyLogged {
.log.AddRangeError(&, ,
fmt.Sprintf("Cannot read file %q: %s",
.PrettyPath(logger.Path{Text: , Namespace: "file"}), .Error()))
}
return nil
}
}
:=
if !.fs.IsAbs() {
= .fs.Join(, )
}
for , := range []string{, + ".json"} {
, := .(, )
if == nil {
return
} else if == syscall.ENOENT {
continue
} else if == parseErrorImportCycle {
.log.AddRangeWarning(&, ,
fmt.Sprintf("Base config file %q forms cycle", ))
} else if != parseErrorAlreadyLogged {
.log.AddRangeError(&, ,
fmt.Sprintf("Cannot read file %q: %s",
.PrettyPath(logger.Path{Text: , Namespace: "file"}), .Error()))
}
return nil
}
}
if !IsInsideNodeModules() {
.log.AddRangeWarning(&, ,
fmt.Sprintf("Cannot find base config file %q", ))
}
return nil
})
if == nil {
return nil, parseErrorAlreadyLogged
}
if .BaseURL != nil && !.fs.IsAbs(*.BaseURL) {
*.BaseURL = .fs.Join(, *.BaseURL)
}
if .Paths != nil && !.fs.IsAbs(.BaseURLForPaths) {
.BaseURLForPaths = .fs.Join(, .BaseURLForPaths)
}
return , nil
}
var *dirInfo
:= .fs.Dir()
if != {
= .dirInfoCached()
, := .fs.ReadDirectory()
if != nil {
.enclosingBrowserScope = .enclosingBrowserScope
if !.options.PreserveSymlinks {
if , := .entries.Get(); != nil {
if := .Symlink(.fs); != "" {
.absRealPath =
} else if .absRealPath != "" {
.absRealPath = .fs.Join(.absRealPath, )
}
}
}
}
if , := .Get("package.json"); != nil && .Kind(.fs) == fs.FileEntry {
.packageJSON = .parsePackageJSON()
if .packageJSON != nil && (.packageJSON.browserPackageMap != nil || .packageJSON.browserNonPackageMap != nil) {
.enclosingBrowserScope =
}
}
=
}
if != "" {
var error
.tsConfigJSON, = .parseTSConfig(, make(map[string]bool))
if != nil {
if == syscall.ENOENT {
.log.AddError(nil, logger.Loc{}, fmt.Sprintf("Cannot find tsconfig file %q",
.PrettyPath(logger.Path{Text: , Namespace: "file"})))
} else if != parseErrorAlreadyLogged {
.log.AddError(nil, logger.Loc{},
fmt.Sprintf("Cannot read file %q: %s",
.PrettyPath(logger.Path{Text: , Namespace: "file"}), .Error()))
}
}
}
}
if .tsConfigJSON == nil && != nil {
.tsConfigJSON = .tsConfigJSON
}
if , , := .loadAsIndex(, ); {
.absPathIndex = &
}
return
}
func ( *resolver) ( string) *packageJSON {
:= .fs.Join(, "package.json")
, := .caches.FSCache.ReadFile(.fs, )
if != nil {
.log.AddError(nil, logger.Loc{},
fmt.Sprintf("Cannot read file %q: %s",
.PrettyPath(logger.Path{Text: , Namespace: "file"}), .Error()))
return nil
}
:= logger.Path{Text: , Namespace: "file"}
:= logger.Source{
KeyPath: ,
PrettyPath: .PrettyPath(),
Contents: ,
}
, := .caches.JSONCache.Parse(.log, , js_parser.JSONOptions{})
if ! {
return nil
}
if , , := .loadAsFile(, .options.ExtensionOrder); {
return &
}
if , , := .loadAsIndex(, ); {
return &
}
} else if != syscall.ENOENT {
.log.AddRangeError(&, ,
fmt.Sprintf("Cannot read directory %q: %s",
.PrettyPath(logger.Path{Text: , Namespace: "file"}), .Error()))
}
return nil
}
:= &packageJSON{}
:= .options.MainFields
if == nil {
= defaultMainFields[.options.Platform]
}
for , := range {
if , , := getProperty(, ); {
if , := getString(); {
if .absMainFields == nil {
.absMainFields = make(map[string]string)
}
if := (.fs.Join(, ), .RangeOfString(.Loc)); != nil {
.absMainFields[] = *
}
}
}
}
for , := range .Properties {
if , := getString(.Key); && .Value != nil {
:= IsPackagePath()
if {
[] = &
} else {
[] = &
}
if {
[] = nil
} else {
[] = nil
}
}
}
}
.browserPackageMap =
.browserNonPackageMap =
}
}
if , , := getProperty(, "sideEffects"); {
switch data := .Data.(type) {
case *js_ast.EBoolean:
.sideEffectsMap = make(map[string]bool)
.ignoreIfUnusedData = &IgnoreIfUnusedData{
IsSideEffectsArrayInJSON: false,
Source: &,
Range: .RangeOfString(),
}
}
.sideEffectsMap = make(map[string]bool)
.ignoreIfUnusedData = &IgnoreIfUnusedData{
IsSideEffectsArrayInJSON: true,
Source: &,
Range: .RangeOfString(),
}
for , := range .Items {
, := .Data.(*js_ast.EString)
if ! || .Value == nil {
.log.AddWarning(&, .Loc,
"Expected string in array for \"sideEffects\"")
continue
}
:= .fs.Join(, js_lexer.UTF16ToString(.Value))
, := globToEscapedRegexp()
if {
.sideEffectsRegexps = append(.sideEffectsRegexps, regexp.MustCompile())
continue
}
.sideEffectsMap[] = true
}
default:
.log.AddWarning(&, .Loc,
"The value for \"sideEffects\" must be a boolean or an array")
}
}
return
}
func ( string) (string, bool) {
:= strings.Builder{}
.WriteByte('^')
:= false
for , := range {
switch {
case '\\', '^', '$', '.', '+', '|', '(', ')', '[', ']', '{', '}':
.WriteByte('\\')
.WriteRune()
case '*':
.WriteString(".*")
= true
case '?':
.WriteByte('.')
= true
default:
.WriteRune()
}
}
.WriteByte('$')
return .String(),
}
for , := range .options.ExtensionOrder {
:= "index" +
if , := .Get(); != nil && .Kind(.fs) == fs.FileEntry {
return .fs.Join(, ), true,
}
}
return "", false, nil
}
func ( js_ast.Expr, string) (js_ast.Expr, logger.Loc, bool) {
if , := .Data.(*js_ast.EObject); {
for , := range .Properties {
if , := .Key.Data.(*js_ast.EString); && .Value != nil &&
len(.Value) == len() && js_lexer.UTF16ToString(.Value) == {
return *.Value, .Key.Loc, true
}
}
}
return js_ast.Expr{}, logger.Loc{}, false
}
func ( js_ast.Expr) (string, bool) {
if , := .Data.(*js_ast.EString); {
return js_lexer.UTF16ToString(.Value), true
}
return "", false
}
func ( js_ast.Expr) (bool, bool) {
if , := .Data.(*js_ast.EBoolean); {
return .Value, true
}
return false, false
}
:= .options.ExtensionOrder
if == ast.ImportAt {
= .atImportExtensionOrder
}
:= .dirInfoCached()
if == nil {
return PathPair{}, false, nil
}
if .packageJSON != nil && .packageJSON.absMainFields != nil {
:= .packageJSON.absMainFields
:= .options.MainFields
:= false
if == nil {
= defaultMainFields[.options.Platform]
= true
}
for , := range {
if && == "module" {
, := ["main"]
if ! && .absPathIndex != nil {
= *.absPathIndex
= true
}
if != ast.ImportRequire {
func ( *resolver) ( *TSConfigJSON, string, ast.ImportKind) (PathPair, bool, *fs.DifferentCase) {
:= .BaseURLForPaths
for , := range .Paths {
if == {
if != -1 {
if .tsConfigJSON.Paths != nil {
if , , := .matchTSConfigPaths(.tsConfigJSON, , ); {
return , true,
}
}
if .tsConfigJSON.BaseURL != nil {
:= .fs.Join(*.tsConfigJSON.BaseURL, )
if , , := .loadAsFileOrDirectory(, ); {
return , true,
}
}
}
for , := range .options.AbsNodePaths {
:= .fs.Join(, )
if , , := .loadAsFileOrDirectory(, ); {
return , true,
}
}
if .hasNodeModules {
:= .fs.Join(.absPath, "node_modules", )
:= .dirInfoCached(.fs.Dir())
if != nil && .enclosingBrowserScope != nil {
if := .enclosingBrowserScope.packageJSON; .browserNonPackageMap != nil {
if , := .browserNonPackageMap[]; {
if == nil {
return PathPair{Primary: logger.Path{Text: , Namespace: "file", Flags: logger.PathDisabled}}, true, nil
} else if , , := .resolveWithoutRemapping(.enclosingBrowserScope, *, ); {
return , true,
}
}
}
}
if , , := .loadAsFileOrDirectory(, ); {
return , true,
}
}
func ( string) bool {
return !strings.HasPrefix(, "/") && !strings.HasPrefix(, "./") &&
!strings.HasPrefix(, "../") && != "." && != ".."
}
var BuiltInNodeModules = map[string]bool{
"assert": true,
"async_hooks": true,
"buffer": true,
"child_process": true,
"cluster": true,
"console": true,
"constants": true,
"crypto": true,
"dgram": true,
"dns": true,
"domain": true,
"events": true,
"fs": true,
"http": true,
"http2": true,
"https": true,
"inspector": true,
"module": true,
"net": true,
"os": true,
"path": true,
"perf_hooks": true,
"process": true,
"punycode": true,
"querystring": true,
"readline": true,
"repl": true,
"stream": true,
"string_decoder": true,
"sys": true,
"timers": true,
"tls": true,
"trace_events": true,
"tty": true,
"url": true,
"util": true,
"v8": true,
"vm": true,
"worker_threads": true,
"zlib": true,
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |