11use std:: {
22 fs:: File ,
33 io:: { BufReader , Seek , SeekFrom } ,
4- path:: Path ,
54 sync:: Arc ,
65} ;
76
87use vite_path:: { AbsolutePath , RelativePathBuf } ;
98
109use crate :: Error ;
1110
11+ /// A file handle bundled with its absolute path for error context.
12+ #[ derive( Debug ) ]
13+ pub struct FileWithPath {
14+ file : File ,
15+ path : Arc < AbsolutePath > ,
16+ }
17+
18+ impl FileWithPath {
19+ /// Open a file at the given path.
20+ pub fn open ( path : Arc < AbsolutePath > ) -> Result < Self , Error > {
21+ let file = File :: open ( & * path) ?;
22+ Ok ( Self { file, path } )
23+ }
24+
25+ /// Try to open a file, returning None if it doesn't exist.
26+ pub fn open_if_exists ( path : Arc < AbsolutePath > ) -> Result < Option < Self > , Error > {
27+ match File :: open ( & * path) {
28+ Ok ( file) => Ok ( Some ( Self { file, path } ) ) ,
29+ Err ( e) if e. kind ( ) == std:: io:: ErrorKind :: NotFound => Ok ( None ) ,
30+ Err ( e) => Err ( e. into ( ) ) ,
31+ }
32+ }
33+
34+ /// Get a reference to the file handle.
35+ pub fn file ( & self ) -> & File {
36+ & self . file
37+ }
38+
39+ /// Get a mutable reference to the file handle.
40+ pub fn file_mut ( & mut self ) -> & mut File {
41+ & mut self . file
42+ }
43+
44+ /// Get the file path.
45+ pub fn path ( & self ) -> & Arc < AbsolutePath > {
46+ & self . path
47+ }
48+ }
49+
1250/// The package root directory and its package.json file.
1351#[ derive( Debug ) ]
1452pub struct PackageRoot < ' a > {
1553 pub path : & ' a AbsolutePath ,
1654 pub cwd : RelativePathBuf ,
17- pub package_json : File ,
55+ pub package_json : FileWithPath ,
1856}
1957
2058/// Find the package root directory from the current working directory. `original_cwd` must be absolute.
@@ -24,11 +62,12 @@ pub fn find_package_root(original_cwd: &AbsolutePath) -> Result<PackageRoot<'_>,
2462 let mut cwd = original_cwd;
2563 loop {
2664 // Check for package.json
27- if let Some ( file) = open_exists_file ( cwd. join ( "package.json" ) ) ? {
65+ let package_json_path: Arc < AbsolutePath > = cwd. join ( "package.json" ) . into ( ) ;
66+ if let Some ( file_with_path) = FileWithPath :: open_if_exists ( package_json_path) ? {
2867 return Ok ( PackageRoot {
2968 path : cwd,
3069 cwd : original_cwd. strip_prefix ( cwd) ?. expect ( "cwd must be within the package root" ) ,
31- package_json : file ,
70+ package_json : file_with_path ,
3271 } ) ;
3372 }
3473
@@ -50,11 +89,11 @@ pub fn find_package_root(original_cwd: &AbsolutePath) -> Result<PackageRoot<'_>,
5089#[ derive( Debug ) ]
5190pub enum WorkspaceFile {
5291 /// The pnpm-workspace.yaml file of a pnpm workspace.
53- PnpmWorkspaceYaml ( File ) ,
92+ PnpmWorkspaceYaml ( FileWithPath ) ,
5493 /// The package.json file of a yarn/npm workspace.
55- NpmWorkspaceJson ( File ) ,
94+ NpmWorkspaceJson ( FileWithPath ) ,
5695 /// The package.json file of a non-workspace package.
57- NonWorkspacePackage ( File ) ,
96+ NonWorkspacePackage ( FileWithPath ) ,
5897}
5998
6099/// The workspace root directory and its workspace file.
@@ -82,31 +121,38 @@ pub fn find_workspace_root(
82121
83122 loop {
84123 // Check for pnpm-workspace.yaml for pnpm workspace
85- if let Some ( file) = open_exists_file ( cwd. join ( "pnpm-workspace.yaml" ) ) ? {
124+ let pnpm_workspace_path: Arc < AbsolutePath > = cwd. join ( "pnpm-workspace.yaml" ) . into ( ) ;
125+ if let Some ( file_with_path) = FileWithPath :: open_if_exists ( pnpm_workspace_path) ? {
86126 let relative_cwd =
87127 original_cwd. strip_prefix ( cwd) ?. expect ( "cwd must be within the pnpm workspace" ) ;
88128 return Ok ( (
89129 WorkspaceRoot {
90130 path : Arc :: from ( cwd) ,
91- workspace_file : WorkspaceFile :: PnpmWorkspaceYaml ( file ) ,
131+ workspace_file : WorkspaceFile :: PnpmWorkspaceYaml ( file_with_path ) ,
92132 } ,
93133 relative_cwd,
94134 ) ) ;
95135 }
96136
97137 // Check for package.json with workspaces field for npm/yarn workspace
98- let package_json_path = cwd. join ( "package.json" ) ;
99- if let Some ( mut file) = open_exists_file ( & package_json_path) ? {
100- let package_json: serde_json:: Value = serde_json:: from_reader ( BufReader :: new ( & file) ) ?;
138+ let package_json_path: Arc < AbsolutePath > = cwd. join ( "package.json" ) . into ( ) ;
139+ if let Some ( mut file_with_path) = FileWithPath :: open_if_exists ( package_json_path) ? {
140+ let package_json: serde_json:: Value =
141+ serde_json:: from_reader ( BufReader :: new ( file_with_path. file ( ) ) ) . map_err ( |e| {
142+ Error :: SerdeJson {
143+ file_path : Arc :: clone ( file_with_path. path ( ) ) ,
144+ serde_json_error : e,
145+ }
146+ } ) ?;
101147 if package_json. get ( "workspaces" ) . is_some ( ) {
102148 // Reset the file cursor since we consumed it reading
103- file . seek ( SeekFrom :: Start ( 0 ) ) ?;
149+ file_with_path . file_mut ( ) . seek ( SeekFrom :: Start ( 0 ) ) ?;
104150 let relative_cwd =
105151 original_cwd. strip_prefix ( cwd) ?. expect ( "cwd must be within the workspace" ) ;
106152 return Ok ( (
107153 WorkspaceRoot {
108154 path : Arc :: from ( cwd) ,
109- workspace_file : WorkspaceFile :: NpmWorkspaceJson ( file ) ,
155+ workspace_file : WorkspaceFile :: NpmWorkspaceJson ( file_with_path ) ,
110156 } ,
111157 relative_cwd,
112158 ) ) ;
@@ -129,12 +175,3 @@ pub fn find_workspace_root(
129175 }
130176 }
131177}
132-
133- fn open_exists_file ( path : impl AsRef < Path > ) -> Result < Option < File > , Error > {
134- match File :: open ( path) {
135- Ok ( file) => Ok ( Some ( file) ) ,
136- // if the file does not exist, return None
137- Err ( e) if e. kind ( ) == std:: io:: ErrorKind :: NotFound => Ok ( None ) ,
138- Err ( e) => Err ( e. into ( ) ) ,
139- }
140- }
0 commit comments