@@ -24,25 +24,38 @@ import { Logger } from '../utils/Logger.js';
2424 * URL detection patterns for various dev servers
2525 * These patterns extract URLs from different dev server outputs
2626 */
27- const URL_PATTERNS = [
28- // Vite with Unicode arrow: " ➜ Local: http://localhost:5173/"
29- / \s * [ ➜ > ] \s * L o c a l : \s + ( h t t p s ? : \/ \/ [ ^ \s ] + ) / i,
30- // Generic Local pattern with optional prefix: "Local: http://localhost:5173"
31- / \s * L o c a l : \s + ( h t t p s ? : \/ \/ [ ^ \s ] + ) / i,
32- // Create React App: "On Your Network: http://192.168.1.1:3000"
33- / O n Y o u r N e t w o r k : \s + ( h t t p s ? : \/ \/ [ ^ \s ] + ) / i,
34- // Next.js: "ready - started server on 0.0.0.0:3000, url: http://localhost:3000"
35- / u r l : \s + ( h t t p s ? : \/ \/ [ ^ \s , ] + ) / i,
36- // Next.js alternative: "- Local: http://localhost:3000"
37- / - \s * L o c a l : \s + ( h t t p s ? : \/ \/ [ ^ \s ] + ) / i,
38- // Generic: "Server running at http://localhost:8080"
27+ /**
28+ * URL detection patterns organized by dev server type
29+ * Add new server patterns here for easy maintenance
30+ */
31+
32+ // Vite dev server patterns
33+ // Example: " ➜ Local: http://localhost:5173/"
34+ const VITE_PATTERNS = [
35+ / ➜ \s * L o c a l : \s + ( h t t p s ? : \/ \/ [ ^ \s ] + ) / iu, // Unicode arrow (with colors)
36+ / > \s * L o c a l : \s + ( h t t p s ? : \/ \/ [ ^ \s ] + ) / i, // ASCII arrow fallback
37+ ] ;
38+
39+ // Create React App (Webpack) patterns
40+ // Example: "On Your Network: http://192.168.1.1:3000"
41+ const CRA_PATTERNS = [ / O n Y o u r N e t w o r k : \s + ( h t t p s ? : \/ \/ [ ^ \s ] + ) / i, / L o c a l : \s + ( h t t p s ? : \/ \/ [ ^ \s ] + ) / i] ;
42+
43+ // Next.js dev server patterns
44+ // Example: "ready - started server on 0.0.0.0:3000, url: http://localhost:3000"
45+ const NEXTJS_PATTERNS = [ / u r l : \s + ( h t t p s ? : \/ \/ [ ^ \s , ] + ) / i, / - \s * L o c a l : \s + ( h t t p s ? : \/ \/ [ ^ \s ] + ) / i] ;
46+
47+ // Generic patterns for custom/unknown servers
48+ // Example: "Server running at http://localhost:8080"
49+ const GENERIC_PATTERNS = [
3950 / (?: S e r v e r | s e r v e r | R u n n i n g | r u n n i n g | s t a r t e d | S t a r t e d ) .* ?( h t t p s ? : \/ \/ [ ^ \s ] + ) / i,
40- // Port-based: "localhost:3000" or "http://localhost:3000"
4151 / ( h t t p s ? : \/ \/ l o c a l h o s t : [ 0 - 9 ] + ) / i,
42- // Generic URL anywhere in the output
4352 / ( h t t p s ? : \/ \/ (?: l o c a l h o s t | 1 2 7 \. 0 \. 0 \. 1 | 0 \. 0 \. 0 \. 0 ) : [ 0 - 9 ] + ) / i,
4453] ;
4554
55+ // Combined patterns in priority order
56+ // Specific patterns first, generic fallbacks last
57+ const URL_PATTERNS = [ ...VITE_PATTERNS , ...CRA_PATTERNS , ...NEXTJS_PATTERNS , ...GENERIC_PATTERNS ] ;
58+
4659/**
4760 * Default configuration values for DevServerManager
4861 */
@@ -134,23 +147,47 @@ export class DevServerManager extends EventEmitter {
134147 }
135148
136149 /**
137- * Detects a dev server URL from process output
150+ * Strips ANSI color codes from a string
138151 *
139- * Tries multiple regex patterns to match various dev server formats
140- * (Vite, Create React App, Next.js, etc.)
152+ * @param text - Text with potential ANSI codes
153+ * @returns Clean text without ANSI codes
154+ */
155+ private static stripAnsiCodes ( text : string ) : string {
156+ // eslint-disable-next-line no-control-regex
157+ return text . replace ( / \x1B \[ [ 0 - 9 ; ] * [ a - z A - Z ] / g, '' ) ;
158+ }
159+
160+ /**
161+ * Detects dev server URL from process output
141162 *
142- * @param output The output string to analyze
143- * @returns Detected URL or null if not found
163+ * Attempts to match common dev server URL patterns like Vite,
164+ * Create React App, Next.js, etc. Processes line-by-line for robustness.
165+ *
166+ * @param output - The output string to search for URLs
167+ * @returns Detected URL or null if none found
144168 */
145169 private static detectUrlFromOutput ( output : string ) : string | null {
146- for ( const pattern of URL_PATTERNS ) {
147- const match = output . match ( pattern ) ;
148- if ( match ?. [ 1 ] ) {
149- const url = match [ 1 ] . trim ( ) ;
150- // Normalize 0.0.0.0 to localhost for better usability
151- return url . replace ( '0.0.0.0' , 'localhost' ) ;
170+ // Split by newlines and check each line separately
171+ // This is more robust against chunked output
172+ const lines = output . split ( '\n' ) ;
173+
174+ for ( const line of lines ) {
175+ // Strip ANSI color codes first (some tools ignore FORCE_COLOR=0)
176+ const cleanLine = DevServerManager . stripAnsiCodes ( line ) ;
177+ const trimmedLine = cleanLine . trim ( ) ;
178+ if ( ! trimmedLine ) continue ;
179+
180+ for ( const pattern of URL_PATTERNS ) {
181+ const match = trimmedLine . match ( pattern ) ;
182+
183+ if ( match ?. [ 1 ] ) {
184+ const url = match [ 1 ] . trim ( ) ;
185+ // Normalize 0.0.0.0 to localhost for better usability
186+ return url . replace ( '0.0.0.0' , 'localhost' ) ;
187+ }
152188 }
153189 }
190+
154191 return null ;
155192 }
156193
0 commit comments