@@ -14,10 +14,14 @@ internal sealed partial class BrowserConnector(DotNetWatchContext context) : IAs
14
14
// This needs to be in sync with the version BrowserRefreshMiddleware is compiled against.
15
15
private static readonly Version s_minimumSupportedVersion = Versions . Version6_0 ;
16
16
17
- private static readonly Regex s_nowListeningRegex = s_nowListeningOnRegex ( ) ;
17
+ private static readonly Regex s_nowListeningRegex = GetNowListeningOnRegex ( ) ;
18
+ private static readonly Regex s_aspireDashboardUrlRegex = GetAspireDashboardUrlRegex ( ) ;
18
19
19
20
[ GeneratedRegex ( @"Now listening on: (?<url>.*)\s*$" , RegexOptions . Compiled ) ]
20
- private static partial Regex s_nowListeningOnRegex ( ) ;
21
+ private static partial Regex GetNowListeningOnRegex ( ) ;
22
+
23
+ [ GeneratedRegex ( @"Login to the dashboard at (?<url>.*)\s*$" , RegexOptions . Compiled ) ]
24
+ private static partial Regex GetAspireDashboardUrlRegex ( ) ;
21
25
22
26
private readonly object _serversGuard = new ( ) ;
23
27
private readonly Dictionary < ProjectGraphNode , BrowserRefreshServer ? > _servers = [ ] ;
@@ -115,6 +119,10 @@ public bool TryGetRefreshServer(ProjectGraphNode projectNode, [NotNullWhen(true)
115
119
116
120
bool matchFound = false ;
117
121
122
+ // Workaround for Aspire dashboard launching: scan for "Login to the dashboard at " prefix in the output and use the URL.
123
+ // TODO: Share launch profile processing logic as implemented in VS with dotnet-run and implement browser launching there.
124
+ var isAspireHost = projectNode . GetCapabilities ( ) . Contains ( AspireServiceFactory . AppHostProjectCapability ) ;
125
+
118
126
return handler ;
119
127
120
128
void handler ( OutputLine line )
@@ -127,7 +135,7 @@ void handler(OutputLine line)
127
135
return ;
128
136
}
129
137
130
- var match = s_nowListeningRegex . Match ( line . Content ) ;
138
+ var match = ( isAspireHost ? s_aspireDashboardUrlRegex : s_nowListeningRegex ) . Match ( line . Content ) ;
131
139
if ( ! match . Success )
132
140
{
133
141
return ;
@@ -139,7 +147,8 @@ void handler(OutputLine line)
139
147
ImmutableInterlocked . Update ( ref _browserLaunchAttempted , static ( set , projectNode ) => set . Add ( projectNode ) , projectNode ) )
140
148
{
141
149
// first build iteration of a root project:
142
- LaunchBrowser ( launchProfile , match . Groups [ "url" ] . Value , server ) ;
150
+ var launchUrl = GetLaunchUrl ( launchProfile . LaunchUrl , match . Groups [ "url" ] . Value ) ;
151
+ LaunchBrowser ( launchUrl , server ) ;
143
152
}
144
153
else if ( server != null )
145
154
{
@@ -151,10 +160,15 @@ void handler(OutputLine line)
151
160
}
152
161
}
153
162
154
- private void LaunchBrowser ( LaunchSettingsProfile launchProfile , string launchUrl , BrowserRefreshServer ? server )
163
+ public static string GetLaunchUrl ( string ? profileLaunchUrl , string outputLaunchUrl )
164
+ => string . IsNullOrWhiteSpace ( profileLaunchUrl ) ? outputLaunchUrl :
165
+ Uri . TryCreate ( profileLaunchUrl , UriKind . Absolute , out _ ) ? profileLaunchUrl :
166
+ Uri . TryCreate ( outputLaunchUrl , UriKind . Absolute , out var launchUri ) ? new Uri ( launchUri , profileLaunchUrl ) . ToString ( ) :
167
+ outputLaunchUrl ;
168
+
169
+ private void LaunchBrowser ( string launchUrl , BrowserRefreshServer ? server )
155
170
{
156
- var launchPath = launchProfile . LaunchUrl ;
157
- var fileName = Uri . TryCreate ( launchPath , UriKind . Absolute , out _ ) ? launchPath : launchUrl + "/" + launchPath ;
171
+ var fileName = launchUrl ;
158
172
159
173
var args = string . Empty ;
160
174
if ( EnvironmentVariables . BrowserPath is { } browserPath )
0 commit comments