package fs

import (
	
	
	
	
	
	
	
)

Stores the file entries for directories we've listed before
If true, do not use the "entries" cache
This stores data that will end up being returned by "WatchData()"
When building with WebAssembly, the Go compiler doesn't correctly handle platform-specific path behavior. Hack around these bugs by compiling support for both Unix and Windows paths into all executables and switch between them at run-time instead.
	fp goFilepath
}

type entriesOrErr struct {
	entries DirEntries
	err     error
}

type watchState uint8

const (
	stateNone               watchState = iota
	stateDirHasEntries                 // Compare "dirEntries"
	stateDirMissing                    // Compare directory presence
	stateFileHasModKey                 // Compare "modKey"
	stateFileNeedModKey                // Need to transition to "stateFileHasModKey" or "stateFileUnusableModKey" before "WatchData()" returns
	stateFileMissing                   // Compare file presence
	stateFileUnusableModKey            // Compare "fileContents"
)

type privateWatchData struct {
	dirEntries   []string
	fileContents string
	modKey       ModKey
	state        watchState
}

type RealFSOptions struct {
	WantWatchData bool
	AbsWorkingDir string
	DoNotCache    bool
}

func ( RealFSOptions) (FS, error) {
	var  goFilepath
	if CheckIfWindows() {
		.isWindows = true
		.pathSeparator = '\\'
	} else {
		.isWindows = false
		.pathSeparator = '/'
	}
Come up with a default working directory if one was not specified
	.cwd = .AbsWorkingDir
	if .cwd == "" {
		if ,  := os.Getwd();  == nil {
			.cwd = 
		} else if .isWindows {
			.cwd = "C:\\"
		} else {
			.cwd = "/"
		}
	} else if !.isAbs(.cwd) {
		return nil, fmt.Errorf("The working directory %q is not an absolute path", .cwd)
	}
Resolve symlinks in the current working directory. Symlinks are resolved when input file paths are converted to absolute paths because we need to recognize an input file as unique even if it has multiple symlinks pointing to it. The build will generate relative paths from the current working directory to the absolute input file paths for error messages, so the current working directory should be processed the same way. Not doing this causes test failures with esbuild when run from inside a symlinked directory. This deliberately ignores errors due to e.g. infinite loops. If there is an error, we will just use the original working directory and likely encounter an error later anyway. And if we don't encounter an error later, then the current working directory didn't even matter and the error is unimportant.
	if ,  := .evalSymlinks(.cwd);  == nil {
		.cwd = 
	}
Only allocate memory for watch data if necessary
	var  map[string]privateWatchData
	if .WantWatchData {
		 = make(map[string]privateWatchData)
	}

	return &realFS{
		entries:           make(map[string]entriesOrErr),
		fp:                ,
		watchData:         ,
		doNotCacheEntries: .DoNotCache,
	}, nil
}

func ( *realFS) ( string) (DirEntries, error) {
First, check the cache
		,  := func() ( entriesOrErr,  bool) {
			.entriesMutex.Lock()
			defer .entriesMutex.Unlock()
			,  = .entries[]
			return
		}()
Cache hit: stop now
			return .entries, .err
		}
	}
Cache miss: read the directory entries
	,  := readdir()
	 := DirEntries{, make(map[string]*Entry)}
Unwrap to get the underlying error
	if ,  := .(*os.PathError);  {
		 = .Unwrap()
	}

	if  == nil {
Call "stat" lazily for performance. The "@material-ui/icons" package contains a directory with over 11,000 entries in it and running "stat" for each entry was a big performance issue for that package.
			.data[strings.ToLower()] = &Entry{
				dir:      ,
				base:     ,
				needStat: true,
			}
		}
	}
Store data for watch mode
	if .watchData != nil {
		defer .watchMutex.Unlock()
		.watchMutex.Lock()
		 := stateDirHasEntries
		if  != nil {
			 = stateDirMissing
		}
		sort.Strings()
		.watchData[] = privateWatchData{
			dirEntries: ,
			state:      ,
		}
	}
Update the cache unconditionally. Even if the read failed, we don't want to retry again later. The directory is inaccessible so trying again is wasted.
	if  != nil {
		.data = nil
	}
	if !.doNotCacheEntries {
		.entriesMutex.Lock()
		defer .entriesMutex.Unlock()
		.entries[] = entriesOrErr{entries: , err: }
	}
	return , 
}

func ( *realFS) ( string) (string, error) {
	BeforeFileOpen()
	defer AfterFileClose()
	,  := ioutil.ReadFile()
Unwrap to get the underlying error
	if ,  := .(*os.PathError);  {
		 = .Unwrap()
	}
Windows returns ENOTDIR here even though nothing we've done yet has asked for a directory. This really means ENOENT on Windows. Return ENOENT here so callers that check for ENOENT will successfully detect this file as missing.
	if  == syscall.ENOTDIR {
		 = syscall.ENOENT
	}
Allocate the string once
	 := string()
Store data for watch mode
	if .watchData != nil {
		defer .watchMutex.Unlock()
		.watchMutex.Lock()
		,  := .watchData[]
		if  != nil {
			.state = stateFileMissing
		} else if ! {
			.state = stateFileNeedModKey
		}
		.fileContents = 
		.watchData[] = 
	}

	return , 
}

func ( *realFS) ( string) (ModKey, error) {
	BeforeFileOpen()
	defer AfterFileClose()
	,  := modKey()
Store data for watch mode
	if .watchData != nil {
		defer .watchMutex.Unlock()
		.watchMutex.Lock()
		,  := .watchData[]
		if ! {
			if  == modKeyUnusable {
				.state = stateFileUnusableModKey
			} else if  != nil {
				.state = stateFileMissing
			} else {
				.state = stateFileHasModKey
			}
		} else if .state == stateFileNeedModKey {
			.state = stateFileHasModKey
		}
		.modKey = 
		.watchData[] = 
	}

	return , 
}

func ( *realFS) ( string) bool {
	return .fp.isAbs()
}

func ( *realFS) ( string) (string, bool) {
	,  := .fp.abs()
	return ,  == nil
}

func ( *realFS) ( string) string {
	return .fp.dir()
}

func ( *realFS) ( string) string {
	return .fp.base()
}

func ( *realFS) ( string) string {
	return .fp.ext()
}

func ( *realFS) ( ...string) string {
	return .fp.clean(.fp.join())
}

func ( *realFS) () string {
	return .fp.cwd
}

func ( *realFS) ( string,  string) (string, bool) {
	if ,  := .fp.rel(, );  == nil {
		return , true
	}
	return "", false
}

func ( string) ([]string, error) {
	BeforeFileOpen()
	defer AfterFileClose()
	,  := os.Open()
Unwrap to get the underlying error
	if ,  := .(*os.PathError);  {
		 = .Unwrap()
	}
Windows returns ENOTDIR here even though nothing we've done yet has asked for a directory. This really means ENOENT on Windows. Return ENOENT here so callers that check for ENOENT will successfully detect this directory as missing.
	if  == syscall.ENOTDIR {
		return nil, syscall.ENOENT
	}
Stop now if there was an error
	if  != nil {
		return nil, 
	}

	defer .Close()
	,  := .Readdirnames(-1)
Unwrap to get the underlying error
	if ,  := .(*os.SyscallError);  {
		 = .Unwrap()
	}
Don't convert ENOTDIR to ENOENT here. ENOTDIR is a legitimate error condition for Readdirnames() on non-Windows platforms.

	return , 
}

func ( *realFS) ( string,  string) ( string,  EntryKind) {
	 := .fp.join([]string{, })
Use "lstat" since we want information about symbolic links
	BeforeFileOpen()
	defer AfterFileClose()
	,  := os.Lstat()
	if  != nil {
		return
	}
	 := .Mode()
Follow symlinks now so the cache contains the translation
	if ( & os.ModeSymlink) != 0 {
		 = 
		 := 0
		for {
			++
			if  > 255 {
				return // Error: too many links
			}
			,  := os.Readlink()
			if  != nil {
				return // Skip over this entry
			}
			if !.fp.isAbs() {
				 = .fp.join([]string{, })
			}
			 = .fp.clean()
Re-run "lstat" on the symlink target
			,  := os.Lstat()
			if  != nil {
				return // Skip over this entry
			}
			 = .Mode()
			if ( & os.ModeSymlink) == 0 {
				break
			}
			 = .fp.dir()
		}
	}
We consider the entry either a directory or a file
	if ( & os.ModeDir) != 0 {
		 = DirEntry
	} else {
		 = FileEntry
	}
	return
}

func ( *realFS) () WatchData {
	 := make(map[string]func() bool)

Each closure below needs its own copy of these loop variables
		 := 
		 := 
Each function should return true if the state has been changed
		if .state == stateFileNeedModKey {
			,  := modKey()
			if  == modKeyUnusable {
				.state = stateFileUnusableModKey
			} else if  != nil {
				.state = stateFileMissing
			} else {
				.state = stateFileHasModKey
				.modKey = 
			}
		}

		switch .state {
		case stateDirMissing:
			[] = func() bool {
				,  := os.Stat()
				return  == nil && .IsDir()
			}

		case stateDirHasEntries:
			[] = func() bool {
				,  := readdir()
				if  != nil || len() != len(.dirEntries) {
					return true
				}
				sort.Strings()
				for ,  := range  {
					if  != .dirEntries[] {
						return true
					}
				}
				return false
			}

		case stateFileMissing:
			[] = func() bool {
				,  := os.Stat()
				return  == nil && !.IsDir()
			}

		case stateFileHasModKey:
			[] = func() bool {
				,  := modKey()
				return  != nil ||  != .modKey
			}

		case stateFileUnusableModKey:
			[] = func() bool {
				,  := ioutil.ReadFile()
				return  != nil || string() != .fileContents
			}
		}
	}

	return WatchData{
		Paths: ,
	}