@@ -2,8 +2,13 @@ use std::collections::HashMap;
2
2
use std:: error:: Error ;
3
3
use std:: path:: { Path , PathBuf } ;
4
4
5
+ use pathdiff:: diff_paths;
6
+
7
+ use serde_json:: { json, Value } ;
8
+
5
9
use crate :: io:: {
6
- get_internal_package_manifest_files, read_lerna_manifest, write_project_references,
10
+ get_internal_package_manifest_files, read_internal_package_manifests, read_lerna_manifest,
11
+ read_tsconfig, write_project_references, write_tsconfig, PackageManifest ,
7
12
TypeScriptProjectReference , TypeScriptProjectReferences ,
8
13
} ;
9
14
@@ -81,9 +86,111 @@ fn link_children_packages(
81
86
. collect :: < Result < ( ) , Box < dyn Error > > > ( )
82
87
}
83
88
84
- // fn link_package_dependencies(root: &Path) -> Result<(), Box<dyn Error>> {
85
- // Ok(())
86
- // }
89
+ fn tsconfig_filename < P : AsRef < Path > > ( manifest_file : P ) -> Result < PathBuf , Box < dyn Error > > {
90
+ let tsconfig = manifest_file
91
+ . as_ref ( )
92
+ . parent ( )
93
+ . ok_or :: < Box < dyn Error > > (
94
+ String :: from ( "Unexpected internal package in monorepo root" ) . into ( ) ,
95
+ ) ?
96
+ . join ( "tsconfig.json" ) ;
97
+ Ok ( tsconfig)
98
+ }
99
+
100
+ // Map scoped internal-package name to relative path from monorepo root.
101
+ fn key_internal_package_directory_by_package_name < P : AsRef < Path > > (
102
+ root : P ,
103
+ internal_package_manifests : & HashMap < PathBuf , PackageManifest > ,
104
+ ) -> HashMap < String , PathBuf > {
105
+ internal_package_manifests. iter ( ) . fold (
106
+ HashMap :: with_capacity ( internal_package_manifests. len ( ) ) ,
107
+ |mut acc, ( manifest_file, manifest) | {
108
+ acc. insert (
109
+ manifest. name . clone ( ) ,
110
+ internal_package_relative_path ( & root, manifest_file)
111
+ . expect ( "Unable to create relative path to package from monorepo root" ) ,
112
+ ) ;
113
+ acc
114
+ } ,
115
+ )
116
+ }
117
+
118
+ fn link_package_dependencies (
119
+ opts : & crate :: opts:: Link ,
120
+ internal_package_manifest_files : & Vec < PathBuf > ,
121
+ ) -> Result < ( ) , Box < dyn Error > > {
122
+ let internal_manifests = read_internal_package_manifests ( internal_package_manifest_files) ?;
123
+ let package_directory_by_name =
124
+ key_internal_package_directory_by_package_name ( & opts. root , & internal_manifests) ;
125
+
126
+ let get_dependency_group = |package_manifest : & PackageManifest ,
127
+ dependency_group : & str |
128
+ -> serde_json:: Map < String , serde_json:: Value > {
129
+ package_manifest
130
+ . extra_fields
131
+ . get ( dependency_group)
132
+ . and_then ( |v| Value :: as_object ( v) . cloned ( ) )
133
+ . unwrap_or ( serde_json:: Map :: new ( ) )
134
+ } ;
135
+
136
+ internal_package_manifest_files
137
+ . iter ( )
138
+ . map ( |manifest_file| -> Result < ( ) , Box < dyn Error > > {
139
+ let package_directory = manifest_file. parent ( ) . ok_or :: < Box < dyn Error > > (
140
+ String :: from ( "Unexpected internal package in monorepo root" ) . into ( ) ,
141
+ ) ?;
142
+ let tsconfig_file = tsconfig_filename ( manifest_file) ?;
143
+ let mut tsconfig = read_tsconfig ( & tsconfig_file) ?;
144
+ let manifest = internal_manifests
145
+ . get ( manifest_file)
146
+ . ok_or :: < Box < dyn Error > > (
147
+ String :: from ( "Failed to lookup package by manifest path" ) . into ( ) ,
148
+ ) ?;
149
+
150
+ let desired_project_references: serde_json:: Value = {
151
+ let mut deps = get_dependency_group ( manifest, "dependencies" )
152
+ . iter ( )
153
+ . chain ( get_dependency_group ( manifest, "devDependencies" ) . iter ( ) )
154
+ . chain ( get_dependency_group ( manifest, "optionalDependencies" ) . iter ( ) )
155
+ . chain ( get_dependency_group ( manifest, "peerDependencies" ) . iter ( ) )
156
+ . filter_map ( |( name, _version) | package_directory_by_name. get ( name) . cloned ( ) )
157
+ . map ( |dependency_directory| {
158
+ diff_paths ( & opts. root . join ( dependency_directory) , package_directory)
159
+ . ok_or :: < Box < dyn Error > > ( String :: from ( "Unable to calculate relative path between consuming directory and internal dependency" ) . into ( ) )
160
+ } )
161
+ . collect :: < Result < Vec < _ > , _ > > ( ) ?;
162
+ deps. sort_unstable ( ) ;
163
+
164
+ let deps_to_write = serde_json:: Value :: Array ( deps
165
+ . iter ( )
166
+ . map ( |dep| {
167
+ json ! ( {
168
+ "path" : dep. to_str( ) . expect( "Path not valid UTF-8 encoded" ) . to_string( )
169
+ } )
170
+ } )
171
+ . collect :: < Vec < _ > > ( ) ) ;
172
+
173
+ deps_to_write
174
+ } ;
175
+
176
+ // Compare the current references against the desired references
177
+ let needs_update = !desired_project_references. eq (
178
+ tsconfig. get ( "references" ) . unwrap_or ( & serde_json:: Value :: Array ( vec ! [ ] ) )
179
+ ) ;
180
+ if !needs_update {
181
+ return Ok ( ( ) ) ;
182
+ }
183
+
184
+ let new_tsconfig = tsconfig
185
+ . as_object_mut ( )
186
+ . ok_or :: < Box < dyn Error > > ( String :: from ( "Expected tsconfig.json to contain an Object" ) . into ( ) ) ?;
187
+ new_tsconfig. insert ( String :: from ( "references" ) , desired_project_references) ;
188
+
189
+ write_tsconfig ( tsconfig_file, & tsconfig)
190
+ } )
191
+ . collect :: < Result < Vec < _ > , _ > > ( ) ?;
192
+ Ok ( ( ) )
193
+ }
87
194
88
195
pub fn link_typescript_project_references ( opts : crate :: opts:: Link ) -> Result < ( ) , Box < dyn Error > > {
89
196
let lerna_manifest = read_lerna_manifest ( & opts. root ) . expect ( "Unable to read lerna manifest" ) ;
@@ -93,6 +200,12 @@ pub fn link_typescript_project_references(opts: crate::opts::Link) -> Result<(),
93
200
94
201
link_children_packages ( & opts, & internal_package_manifest_files)
95
202
. expect ( "Unable to link children packages" ) ;
203
+ link_package_dependencies ( & opts, & internal_package_manifest_files)
204
+ . expect ( "Unable to link internal package dependencies" ) ;
205
+
206
+ // TODO: implement --check
207
+
208
+ // STRETCH TODO: create `tsconfig.settings.json` files
96
209
97
210
Ok ( ( ) )
98
211
}
0 commit comments