func findInvalidSegment(path string) string {
	slash := strings.IndexAny(path, "/\\")
	if slash == -1 {
		return ""
	}
	path = path[slash+1:]
	for path != "" {
		slash := strings.IndexAny(path, "/\\")
		segment := path
		if slash != -1 {
			segment = path[:slash]
			path = path[slash+1:]
		} else {
			path = ""
		}
		if segment == "." || segment == ".." || segment == "node_modules" {
			return segment
		}
	}
	return ""
}
If path split on "/" or "\" contains any ".", ".." or "node_modules"
segments after the first segment, throw an Invalid Package Target error.
package resolver
import (
	"fmt"
	"net/url"
	"path"
	"regexp"
	"sort"
	"strings"
	"github.com/evanw/esbuild/internal/config"
	"github.com/evanw/esbuild/internal/js_ast"
	"github.com/evanw/esbuild/internal/js_lexer"
	"github.com/evanw/esbuild/internal/js_parser"
	"github.com/evanw/esbuild/internal/logger"
)
type packageJSON struct {
	source     logger.Source
	mainFields map[string]string
	moduleType config.ModuleType
	browserMap map[string]*string
	sideEffectsMap     map[string]bool
	sideEffectsRegexps []*regexp.Regexp
	sideEffectsData    *SideEffectsData
	importsMap *pjMap
	exportsMap *pjMap
}
type browserPathKind uint8
const (
	absolutePathKind browserPathKind = iota
	packagePathKind
)
func (r resolverQuery) checkBrowserMap(resolveDirInfo *dirInfo, inputPath string, kind browserPathKind) (remapped *string, ok bool) {
	if r.options.Platform != config.PlatformBrowser {
		return nil, false
	}
	if resolveDirInfo.enclosingBrowserScope == nil {
		if r.debugLogs != nil {
			r.debugLogs.addNote(fmt.Sprintf("No \"browser\" map found in directory %q", resolveDirInfo.absPath))
		}
		return nil, false
	}
	packageJSON := resolveDirInfo.enclosingBrowserScope.packageJSON
	browserMap := packageJSON.browserMap
	checkPath := func(pathToCheck string) bool {
		if r.debugLogs != nil {
			r.debugLogs.addNote(fmt.Sprintf("Checking for %q in the \"browser\" map in %q",
				pathToCheck, packageJSON.source.KeyPath.Text))
		}
		if r.debugLogs != nil {
			r.debugLogs.addNote(fmt.Sprintf("  Checking for %q", pathToCheck))
		}
		remapped, ok = browserMap[pathToCheck]
		if ok {
			inputPath = pathToCheck
			return true
		}
		for _, ext := range r.options.ExtensionOrder {
			extPath := pathToCheck + ext
			if r.debugLogs != nil {
				r.debugLogs.addNote(fmt.Sprintf("  Checking for %q", extPath))
			}
			remapped, ok = browserMap[extPath]
			if ok {
				inputPath = extPath
				return true
			}
		}
		indexPath := path.Join(pathToCheck, "index")
		if IsPackagePath(indexPath) && !IsPackagePath(pathToCheck) {
			indexPath = "./" + indexPath
		}
		if r.debugLogs != nil {
			r.debugLogs.addNote(fmt.Sprintf("  Checking for %q", indexPath))
		}
		remapped, ok = browserMap[indexPath]
		if ok {
			inputPath = indexPath
			return true
		}
		for _, ext := range r.options.ExtensionOrder {
			extPath := indexPath + ext
			if r.debugLogs != nil {
				r.debugLogs.addNote(fmt.Sprintf("  Checking for %q", extPath))
			}
			remapped, ok = browserMap[extPath]
			if ok {
				inputPath = extPath
				return true
			}
		}
		return false
	}
	if kind == absolutePathKind {
		relPath, ok := r.fs.Rel(resolveDirInfo.enclosingBrowserScope.absPath, inputPath)
		if !ok {
			return nil, false
		}
		inputPath = strings.ReplaceAll(relPath, "\\", "/")
	}
	if inputPath == "." {
		return nil, false
	}
	if !checkPath(inputPath) && IsPackagePath(inputPath) {
		switch kind {
		case absolutePathKind:
			checkPath("./" + inputPath)
		case packagePathKind:
			isInSamePackage := true
			for info := resolveDirInfo; info != nil && info != resolveDirInfo.enclosingBrowserScope; info = info.parent {
				if info.isNodeModules {
					isInSamePackage = false
					break
				}
			}
			if isInSamePackage {
				checkPath("./" + inputPath)
			}
		}
	}
	if r.debugLogs != nil {
		if ok {
			if remapped == nil {
				r.debugLogs.addNote(fmt.Sprintf("Found %q marked as disabled", inputPath))
			} else {
				r.debugLogs.addNote(fmt.Sprintf("Found %q mapping to %q", inputPath, *remapped))
			}
		} else {
			r.debugLogs.addNote(fmt.Sprintf("Failed to find %q", inputPath))
		}
	}
	return
}
func (r resolverQuery) parsePackageJSON(inputPath string) *packageJSON {
	packageJSONPath := r.fs.Join(inputPath, "package.json")
	contents, err, originalError := r.caches.FSCache.ReadFile(r.fs, packageJSONPath)
	if r.debugLogs != nil && originalError != nil {
		r.debugLogs.addNote(fmt.Sprintf("Failed to read file %q: %s", packageJSONPath, originalError.Error()))
	}
	if err != nil {
		r.log.AddError(nil, logger.Loc{},
			fmt.Sprintf("Cannot read file %q: %s",
				r.PrettyPath(logger.Path{Text: packageJSONPath, Namespace: "file"}), err.Error()))
		return nil
	}
	if r.debugLogs != nil {
		r.debugLogs.addNote(fmt.Sprintf("The file %q exists", packageJSONPath))
	}
	keyPath := logger.Path{Text: packageJSONPath, Namespace: "file"}
	jsonSource := logger.Source{
		KeyPath:    keyPath,
		PrettyPath: r.PrettyPath(keyPath),
		Contents:   contents,
	}
	tracker := logger.MakeLineColumnTracker(&jsonSource)
	json, ok := r.caches.JSONCache.Parse(r.log, jsonSource, js_parser.JSONOptions{})
	if !ok {
		return nil
	}
	packageJSON := &packageJSON{source: jsonSource}
	if typeJSON, _, ok := getProperty(json, "type"); ok {
		if typeValue, ok := getString(typeJSON); ok {
			switch typeValue {
			case "commonjs":
				packageJSON.moduleType = config.ModuleCommonJS
			case "module":
				packageJSON.moduleType = config.ModuleESM
			default:
				r.log.AddRangeWarning(&tracker, jsonSource.RangeOfString(typeJSON.Loc),
					fmt.Sprintf("%q is not a valid value for the \"type\" field (must be either \"commonjs\" or \"module\")", typeValue))
			}
		} else {
			r.log.AddWarning(&tracker, typeJSON.Loc,
				"The value for \"type\" must be a string")
		}
	}
	mainFields := r.options.MainFields
	if mainFields == nil {
		mainFields = defaultMainFields[r.options.Platform]
	}
	for _, field := range mainFields {
		if mainJSON, _, ok := getProperty(json, field); ok {
			if main, ok := getString(mainJSON); ok && main != "" {
				if packageJSON.mainFields == nil {
					packageJSON.mainFields = make(map[string]string)
				}
				packageJSON.mainFields[field] = main
			}
		}
	}
	if browserJSON, _, ok := getProperty(json, "browser"); ok && r.options.Platform == config.PlatformBrowser {
		if browser, ok := browserJSON.Data.(*js_ast.EObject); ok {
			browserMap := make(map[string]*string)
			for _, prop := range browser.Properties {
				if key, ok := getString(prop.Key); ok && prop.ValueOrNil.Data != nil {
					if value, ok := getString(prop.ValueOrNil); ok {
						browserMap[key] = &value
					} else if value, ok := getBool(prop.ValueOrNil); ok {
						if !value {
							browserMap[key] = nil
						}
					} else {
						r.log.AddWarning(&tracker, prop.ValueOrNil.Loc,
							"Each \"browser\" mapping must be a string or a boolean")
					}
				}
			}
			packageJSON.browserMap = browserMap
		}
	}
	if sideEffectsJSON, sideEffectsLoc, ok := getProperty(json, "sideEffects"); ok {
		switch data := sideEffectsJSON.Data.(type) {
		case *js_ast.EBoolean:
			if !data.Value {
				packageJSON.sideEffectsMap = make(map[string]bool)
				packageJSON.sideEffectsData = &SideEffectsData{
					IsSideEffectsArrayInJSON: false,
					Source:                   &jsonSource,
					Range:                    jsonSource.RangeOfString(sideEffectsLoc),
				}
			}
		case *js_ast.EArray:
			packageJSON.sideEffectsMap = make(map[string]bool)
			packageJSON.sideEffectsData = &SideEffectsData{
				IsSideEffectsArrayInJSON: true,
				Source:                   &jsonSource,
				Range:                    jsonSource.RangeOfString(sideEffectsLoc),
			}
			for _, itemJSON := range data.Items {
				item, ok := itemJSON.Data.(*js_ast.EString)
				if !ok || item.Value == nil {
					r.log.AddWarning(&tracker, itemJSON.Loc,
						"Expected string in array for \"sideEffects\"")
					continue
				}
				pattern := js_lexer.UTF16ToString(item.Value)
				if !strings.ContainsRune(pattern, '/') {
					pattern = "**/" + pattern
				}
				absPattern := r.fs.Join(inputPath, pattern)
				re, hadWildcard := globstarToEscapedRegexp(absPattern)
				if hadWildcard {
					packageJSON.sideEffectsRegexps = append(packageJSON.sideEffectsRegexps, regexp.MustCompile(re))
					continue
				}
				packageJSON.sideEffectsMap[absPattern] = true
			}
		default:
			r.log.AddWarning(&tracker, sideEffectsJSON.Loc,
				"The value for \"sideEffects\" must be a boolean or an array")
		}
	}
	if importsJSON, _, ok := getProperty(json, "imports"); ok {
		if importsMap := parseImportsExportsMap(jsonSource, r.log, importsJSON); importsMap != nil {
			if importsMap.root.kind != pjObject {
				r.log.AddRangeWarning(&tracker, importsMap.root.firstToken,
					"The value for \"imports\" must be an object")
			}
			packageJSON.importsMap = importsMap
		}
	}
	if exportsJSON, _, ok := getProperty(json, "exports"); ok {
		if exportsMap := parseImportsExportsMap(jsonSource, r.log, exportsJSON); exportsMap != nil {
			packageJSON.exportsMap = exportsMap
		}
	}
	return packageJSON
}
func globstarToEscapedRegexp(glob string) (string, bool) {
	sb := strings.Builder{}
	sb.WriteByte('^')
	hadWildcard := false
	n := len(glob)
	for i := 0; i < n; i++ {
		c := glob[i]
		switch c {
		case '\\', '^', '$', '.', '+', '|', '(', ')', '[', ']', '{', '}':
			sb.WriteByte('\\')
			sb.WriteByte(c)
		case '?':
			sb.WriteByte('.')
			hadWildcard = true
		case '*':
			prevChar := -1
			if i > 0 {
				prevChar = int(glob[i-1])
			}
			starCount := 1
			for i+1 < n && glob[i+1] == '*' {
				starCount++
				i++
			}
			nextChar := -1
			if i+1 < n {
				nextChar = int(glob[i+1])
			}
			isGlobstar := starCount > 1 &&  
				(prevChar == '/' || prevChar == -1) &&  
				(nextChar == '/' || nextChar == -1)  
			if isGlobstar {
				sb.WriteString("(?:[^/]*(?:/|$))*")
				i++  
			} else {
				sb.WriteString("[^/]*")
			}
			hadWildcard = true
		default:
			sb.WriteByte(c)
		}
	}
	sb.WriteByte('$')
	return sb.String(), hadWildcard
}
type pjMap struct {
	root pjEntry
}
type pjKind uint8
const (
	pjNull pjKind = iota
	pjString
	pjArray
	pjObject
	pjInvalid
)
type pjEntry struct {
	strData       string
	arrData       []pjEntry
	mapData       []pjMapEntry  
	expansionKeys expansionKeysArray
	firstToken    logger.Range
	kind          pjKind
}
type pjMapEntry struct {
	key      string
	keyRange logger.Range
	value    pjEntry
}
type expansionKeysArray []pjMapEntry
func (a expansionKeysArray) Len() int          { return len(a) }
func (a expansionKeysArray) Swap(i int, j int) { a[i], a[j] = a[j], a[i] }
func (a expansionKeysArray) Less(i int, j int) bool {
	return len(a[i].key) > len(a[j].key)
}
func (entry pjEntry) valueForKey(key string) (pjEntry, bool) {
	for _, item := range entry.mapData {
		if item.key == key {
			return item.value, true
		}
	}
	return pjEntry{}, false
}
func parseImportsExportsMap(source logger.Source, log logger.Log, json js_ast.Expr) *pjMap {
	var visit func(expr js_ast.Expr) pjEntry
	tracker := logger.MakeLineColumnTracker(&source)
	visit = func(expr js_ast.Expr) pjEntry {
		var firstToken logger.Range
		switch e := expr.Data.(type) {
		case *js_ast.ENull:
			return pjEntry{
				kind:       pjNull,
				firstToken: js_lexer.RangeOfIdentifier(source, expr.Loc),
			}
		case *js_ast.EString:
			return pjEntry{
				kind:       pjString,
				firstToken: source.RangeOfString(expr.Loc),
				strData:    js_lexer.UTF16ToString(e.Value),
			}
		case *js_ast.EArray:
			arrData := make([]pjEntry, len(e.Items))
			for i, item := range e.Items {
				arrData[i] = visit(item)
			}
			return pjEntry{
				kind:       pjArray,
				firstToken: logger.Range{Loc: expr.Loc, Len: 1},
				arrData:    arrData,
			}
		case *js_ast.EObject:
			mapData := make([]pjMapEntry, len(e.Properties))
			expansionKeys := make(expansionKeysArray, 0, len(e.Properties))
			firstToken := logger.Range{Loc: expr.Loc, Len: 1}
			isConditionalSugar := false
			for i, property := range e.Properties {
				keyStr, _ := property.Key.Data.(*js_ast.EString)
				key := js_lexer.UTF16ToString(keyStr.Value)
				keyRange := source.RangeOfString(property.Key.Loc)
				curIsConditionalSugar := !strings.HasPrefix(key, ".")
				if i == 0 {
					isConditionalSugar = curIsConditionalSugar
				} else if isConditionalSugar != curIsConditionalSugar {
					prevEntry := mapData[i-1]
					log.AddRangeWarningWithNotes(&tracker, keyRange,
						"This object cannot contain keys that both start with \".\" and don't start with \".\"",
						[]logger.MsgData{logger.RangeData(&tracker, prevEntry.keyRange,
							fmt.Sprintf("The previous key %q is incompatible with the current key %q", prevEntry.key, key))})
					return pjEntry{
						kind:       pjInvalid,
						firstToken: firstToken,
					}
				}
				entry := pjMapEntry{
					key:      key,
					keyRange: keyRange,
					value:    visit(property.ValueOrNil),
				}
				if strings.HasSuffix(key, "/") || strings.HasSuffix(key, "*") {
					expansionKeys = append(expansionKeys, entry)
				}
				mapData[i] = entry
			}
			sort.Stable(expansionKeys)
			return pjEntry{
				kind:          pjObject,
				firstToken:    firstToken,
				mapData:       mapData,
				expansionKeys: expansionKeys,
			}
		case *js_ast.EBoolean:
			firstToken = js_lexer.RangeOfIdentifier(source, expr.Loc)
		case *js_ast.ENumber:
			firstToken = source.RangeOfNumber(expr.Loc)
		default:
			firstToken.Loc = expr.Loc
		}
		log.AddRangeWarning(&tracker, firstToken, "This value must be a string, an object, an array, or null")
		return pjEntry{
			kind:       pjInvalid,
			firstToken: firstToken,
		}
	}
	root := visit(json)
	if root.kind == pjNull {
		return nil
	}
	return &pjMap{root: root}
}
func (entry pjEntry) keysStartWithDot() bool {
	return len(entry.mapData) > 0 && strings.HasPrefix(entry.mapData[0].key, ".")
}
type pjStatus uint8
const (
	pjStatusUndefined                  pjStatus = iota
	pjStatusUndefinedNoConditionsMatch           
	pjStatusNull
	pjStatusExact
	pjStatusInexact         
	pjStatusPackageResolve  
	pjStatusInvalidModuleSpecifier
	pjStatusInvalidPackageConfiguration
	pjStatusInvalidPackageTarget
	pjStatusPackagePathNotExported
	pjStatusPackageImportNotDefined
	pjStatusModuleNotFound
	pjStatusUnsupportedDirectoryImport
)
func (status pjStatus) isUndefined() bool {
	return status == pjStatusUndefined || status == pjStatusUndefinedNoConditionsMatch
}
type pjDebug struct {
	token logger.Range
	unmatchedConditions []string
}
func (r resolverQuery) esmHandlePostConditions(
	resolved string,
	status pjStatus,
	debug pjDebug,
) (string, pjStatus, pjDebug) {
	if status != pjStatusExact && status != pjStatusInexact {
		return resolved, status, debug
	}
	resolvedPath, err := url.PathUnescape(resolved)
	if err != nil {
		if r.debugLogs != nil {
			r.debugLogs.addNote(fmt.Sprintf("The path %q contains invalid URL escapes: %s", resolved, err.Error()))
		}
		return resolved, pjStatusInvalidModuleSpecifier, debug
	}
	var found string
	if strings.Contains(resolved, "%2f") {
		found = "%2f"
	} else if strings.Contains(resolved, "%2F") {
		found = "%2F"
	} else if strings.Contains(resolved, "%5c") {
		found = "%5c"
	} else if strings.Contains(resolved, "%5C") {
		found = "%5C"
	}
	if found != "" {
		if r.debugLogs != nil {
			r.debugLogs.addNote(fmt.Sprintf("The path %q is not allowed to contain %q", resolved, found))
		}
		return resolved, pjStatusInvalidModuleSpecifier, debug
	}
	if strings.HasSuffix(resolvedPath, "/") || strings.HasSuffix(resolvedPath, "\\") {
		if r.debugLogs != nil {
			r.debugLogs.addNote(fmt.Sprintf("The path %q is not allowed to end with a slash", resolved))
		}
		return resolved, pjStatusUnsupportedDirectoryImport, debug
	}
	return resolvedPath, status, debug
}
func (r resolverQuery) esmPackageImportsResolve(
	specifier string,
	imports pjEntry,
	conditions map[string]bool,
) (string, pjStatus, pjDebug) {
	if imports.kind != pjObject {
		return "", pjStatusInvalidPackageConfiguration, pjDebug{token: imports.firstToken}
	}
	resolved, status, debug := r.esmPackageImportsExportsResolve(specifier, imports, "/", true, conditions)
	if status != pjStatusNull && status != pjStatusUndefined {
		return resolved, status, debug
	}
	if r.debugLogs != nil {
		r.debugLogs.addNote(fmt.Sprintf("The package import %q is not defined", specifier))
	}
	return specifier, pjStatusPackageImportNotDefined, pjDebug{token: imports.firstToken}
}
func (r resolverQuery) esmPackageExportsResolve(
	packageURL string,
	subpath string,
	exports pjEntry,
	conditions map[string]bool,
) (string, pjStatus, pjDebug) {
	if exports.kind == pjInvalid {
		if r.debugLogs != nil {
			r.debugLogs.addNote("Invalid package configuration")
		}
		return "", pjStatusInvalidPackageConfiguration, pjDebug{token: exports.firstToken}
	}
	if subpath == "." {
		mainExport := pjEntry{kind: pjNull}
		if exports.kind == pjString || exports.kind == pjArray || (exports.kind == pjObject && !exports.keysStartWithDot()) {
			mainExport = exports
		} else if exports.kind == pjObject {
			if dot, ok := exports.valueForKey("."); ok {
				if r.debugLogs != nil {
					r.debugLogs.addNote("Using the entry for \".\"")
				}
				mainExport = dot
			}
		}
		if mainExport.kind != pjNull {
			resolved, status, debug := r.esmPackageTargetResolve(packageURL, mainExport, "", false, false, conditions)
			if status != pjStatusNull && status != pjStatusUndefined {
				return resolved, status, debug
			}
		}
	} else if exports.kind == pjObject && exports.keysStartWithDot() {
		resolved, status, debug := r.esmPackageImportsExportsResolve(subpath, exports, packageURL, false, conditions)
		if status != pjStatusNull && status != pjStatusUndefined {
			return resolved, status, debug
		}
	}
	if r.debugLogs != nil {
		r.debugLogs.addNote(fmt.Sprintf("The path %q is not exported", subpath))
	}
	return "", pjStatusPackagePathNotExported, pjDebug{token: exports.firstToken}
}
func (r resolverQuery) esmPackageImportsExportsResolve(
	matchKey string,
	matchObj pjEntry,
	packageURL string,
	isImports bool,
	conditions map[string]bool,
) (string, pjStatus, pjDebug) {
	if r.debugLogs != nil {
		r.debugLogs.addNote(fmt.Sprintf("Checking object path map for %q", matchKey))
	}
	if !strings.HasSuffix(matchKey, "*") {
		if target, ok := matchObj.valueForKey(matchKey); ok {
			if r.debugLogs != nil {
				r.debugLogs.addNote(fmt.Sprintf("Found exact match for %q", matchKey))
			}
			return r.esmPackageTargetResolve(packageURL, target, "", false, isImports, conditions)
		}
	}
	for _, expansion := range matchObj.expansionKeys {
		if strings.HasSuffix(expansion.key, "*") {
			if substr := expansion.key[:len(expansion.key)-1]; strings.HasPrefix(matchKey, substr) && matchKey != substr {
				target := expansion.value
				subpath := matchKey[len(expansion.key)-1:]
				if r.debugLogs != nil {
					r.debugLogs.addNote(fmt.Sprintf("The key %q matched with %q left over", expansion.key, subpath))
				}
				return r.esmPackageTargetResolve(packageURL, target, subpath, true, isImports, conditions)
			}
		}
		if strings.HasPrefix(matchKey, expansion.key) {
			target := expansion.value
			subpath := matchKey[len(expansion.key):]
			if r.debugLogs != nil {
				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 {
				status = pjStatusInexact
			}
			return result, status, debug
		}
		if r.debugLogs != nil {
			r.debugLogs.addNote(fmt.Sprintf("The key %q did not match", expansion.key))
		}
	}
	if r.debugLogs != nil {
		r.debugLogs.addNote(fmt.Sprintf("No keys matched %q", matchKey))
	}
	return "", pjStatusNull, pjDebug{token: matchObj.firstToken}
}