1
1
use std:: collections:: { HashMap , HashSet } ;
2
2
3
- use tracing :: warn ;
3
+ use miette :: NamedSource ;
4
4
use turborepo_errors:: Spanned ;
5
- use turborepo_repository:: package_graph:: { PackageName , PackageNode } ;
5
+ use turborepo_repository:: {
6
+ package_graph:: { PackageName , PackageNode } ,
7
+ package_json:: PackageJson ,
8
+ } ;
6
9
7
10
use crate :: {
8
11
boundaries:: { config:: Rule , BoundariesDiagnostic , Error , Permissions , SecondaryDiagnostic } ,
9
12
run:: Run ,
10
- turbo_json:: TurboJson ,
11
13
} ;
12
14
13
15
pub type ProcessedRulesMap = HashMap < String , ProcessedRule > ;
14
16
15
17
pub struct ProcessedRule {
18
+ span : Spanned < ( ) > ,
16
19
dependencies : Option < ProcessedPermissions > ,
17
20
dependents : Option < ProcessedPermissions > ,
18
21
}
19
22
20
- impl From < Rule > for ProcessedRule {
21
- fn from ( rule : Rule ) -> Self {
23
+ impl From < Spanned < Rule > > for ProcessedRule {
24
+ fn from ( rule : Spanned < Rule > ) -> Self {
25
+ let ( rule, span) = rule. split ( ) ;
22
26
Self {
27
+ span,
23
28
dependencies : rule
24
29
. dependencies
25
30
. map ( |dependencies| dependencies. into_inner ( ) . into ( ) ) ,
@@ -56,30 +61,6 @@ impl Run {
56
61
. and_then ( |turbo_json| turbo_json. tags . as_ref ( ) )
57
62
}
58
63
59
- pub ( crate ) fn get_package_tags ( & self ) -> HashMap < PackageName , Spanned < Vec < Spanned < String > > > > {
60
- let mut package_tags = HashMap :: new ( ) ;
61
- for ( package, _) in self . pkg_dep_graph ( ) . packages ( ) {
62
- if let Ok ( TurboJson {
63
- tags : Some ( tags) ,
64
- boundaries,
65
- ..
66
- } ) = self . turbo_json_loader ( ) . load ( package)
67
- {
68
- if boundaries. as_ref ( ) . is_some_and ( |b| b. tags . is_some ( ) )
69
- && !matches ! ( package, PackageName :: Root )
70
- {
71
- warn ! (
72
- "Boundaries rules can only be defined in the root turbo.json. Any rules \
73
- defined in a package's turbo.json will be ignored."
74
- )
75
- }
76
- package_tags. insert ( package. clone ( ) , tags. clone ( ) ) ;
77
- }
78
- }
79
-
80
- package_tags
81
- }
82
-
83
64
pub ( crate ) fn get_processed_rules_map ( & self ) -> Option < ProcessedRulesMap > {
84
65
self . root_turbo_json ( )
85
66
. boundaries
@@ -88,7 +69,7 @@ impl Run {
88
69
. map ( |tags| {
89
70
tags. as_inner ( )
90
71
. iter ( )
91
- . map ( |( k, v) | ( k. clone ( ) , v. as_inner ( ) . clone ( ) . into ( ) ) )
72
+ . map ( |( k, v) | ( k. clone ( ) , v. clone ( ) . into ( ) ) )
92
73
. collect ( )
93
74
} )
94
75
}
@@ -99,14 +80,43 @@ impl Run {
99
80
fn validate_relation (
100
81
& self ,
101
82
package_name : & PackageName ,
83
+ package_json : & PackageJson ,
102
84
relation_package_name : & PackageName ,
103
85
tags : Option < & Spanned < Vec < Spanned < String > > > > ,
104
86
allow_list : Option < & Spanned < HashSet < String > > > ,
105
87
deny_list : Option < & Spanned < HashSet < String > > > ,
106
88
) -> Result < Option < BoundariesDiagnostic > , Error > {
107
- // If there is no allow list, then we vacuously have a tag in the allow list
108
- let mut has_tag_in_allowlist = allow_list. is_none ( ) ;
89
+ // We allow "punning" the package name as a tag, so if the allow list contains
90
+ // the package name, then we have a tag in the allow list
91
+ // Likewise, if the allow list is empty, then we vacuously have a tag in the
92
+ // allow list
93
+ let mut has_tag_in_allowlist =
94
+ allow_list. is_none_or ( |allow_list| allow_list. contains ( relation_package_name. as_str ( ) ) ) ;
109
95
let tags_span = tags. map ( |tags| tags. to ( ( ) ) ) . unwrap_or_default ( ) ;
96
+ if let Some ( deny_list) = deny_list {
97
+ if deny_list. contains ( relation_package_name. as_str ( ) ) {
98
+ let ( span, text) = package_json
99
+ . name
100
+ . as_ref ( )
101
+ . map ( |name| name. span_and_text ( "turbo.json" ) )
102
+ . unwrap_or_else ( || ( None , NamedSource :: new ( "package.json" , String :: new ( ) ) ) ) ;
103
+ let deny_list_spanned = deny_list. to ( ( ) ) ;
104
+ let ( deny_list_span, deny_list_text) =
105
+ deny_list_spanned. span_and_text ( "turbo.json" ) ;
106
+
107
+ return Ok ( Some ( BoundariesDiagnostic :: DeniedTag {
108
+ source_package_name : package_name. clone ( ) ,
109
+ package_name : relation_package_name. clone ( ) ,
110
+ tag : relation_package_name. to_string ( ) ,
111
+ span,
112
+ text,
113
+ secondary : [ SecondaryDiagnostic :: Denylist {
114
+ span : deny_list_span,
115
+ text : deny_list_text,
116
+ } ] ,
117
+ } ) ) ;
118
+ }
119
+ }
110
120
111
121
for tag in tags. into_iter ( ) . flatten ( ) . flatten ( ) {
112
122
if let Some ( allow_list) = allow_list {
@@ -170,10 +180,34 @@ impl Run {
170
180
pub ( crate ) fn check_package_tags (
171
181
& self ,
172
182
pkg : PackageNode ,
183
+ package_json : & PackageJson ,
173
184
current_package_tags : & Spanned < Vec < Spanned < String > > > ,
174
185
tags_rules : & ProcessedRulesMap ,
175
186
) -> Result < Vec < BoundariesDiagnostic > , Error > {
176
187
let mut diagnostics = Vec :: new ( ) ;
188
+
189
+ // We don't allow tags to share the same name as the package because
190
+ // we allow package names to be used as a tag
191
+ if let Some ( rule) = tags_rules. get ( pkg. as_package_name ( ) . as_str ( ) ) {
192
+ let ( tag_span, tag_text) = rule. span . span_and_text ( "turbo.json" ) ;
193
+ let ( package_span, package_text) = package_json
194
+ . name
195
+ . as_ref ( )
196
+ . map ( |name| name. span_and_text ( "package.json" ) )
197
+ . unwrap_or_else ( || ( None , NamedSource :: new ( "package.json" , "" . into ( ) ) ) ) ;
198
+ diagnostics. push ( BoundariesDiagnostic :: TagSharesPackageName {
199
+ tag : pkg. as_package_name ( ) . to_string ( ) ,
200
+ package : pkg. as_package_name ( ) . to_string ( ) ,
201
+ tag_span,
202
+ tag_text,
203
+ secondary : [ SecondaryDiagnostic :: PackageDefinedHere {
204
+ package : pkg. as_package_name ( ) . to_string ( ) ,
205
+ package_span,
206
+ package_text,
207
+ } ] ,
208
+ } ) ;
209
+ }
210
+
177
211
for tag in current_package_tags. iter ( ) {
178
212
if let Some ( rule) = tags_rules. get ( tag. as_inner ( ) ) {
179
213
if let Some ( dependency_permissions) = & rule. dependencies {
@@ -186,6 +220,7 @@ impl Run {
186
220
187
221
diagnostics. extend ( self . validate_relation (
188
222
pkg. as_package_name ( ) ,
223
+ package_json,
189
224
dependency. as_package_name ( ) ,
190
225
dependency_tags,
191
226
dependency_permissions. allow . as_ref ( ) ,
@@ -202,6 +237,7 @@ impl Run {
202
237
let dependent_tags = self . load_package_tags ( dependent. as_package_name ( ) ) ;
203
238
diagnostics. extend ( self . validate_relation (
204
239
pkg. as_package_name ( ) ,
240
+ package_json,
205
241
dependent. as_package_name ( ) ,
206
242
dependent_tags,
207
243
dependent_permissions. allow . as_ref ( ) ,
0 commit comments