66 * found in the LICENSE file at https://angular.io/license
77 */
88
9+ import { readFileSync , existsSync } from 'fs' ;
910import * as browserSync from 'browser-sync' ;
1011import * as http from 'http' ;
1112import * as path from 'path' ;
@@ -17,6 +18,9 @@ import * as send from 'send';
1718 * environment and on Windows (with a runfile manifest file).
1819 */
1920export class DevServer {
21+ /** Cached content of the index.html. */
22+ private _index : string | null = null ;
23+
2024 /** Instance of the browser-sync server. */
2125 server = browserSync . create ( ) ;
2226
@@ -38,7 +42,7 @@ export class DevServer {
3842 private _historyApiFallback : boolean = false ) { }
3943
4044 /** Starts the server on the given port. */
41- async start ( ) {
45+ start ( ) {
4246 return new Promise < void > ( ( resolve , reject ) => {
4347 this . server . init ( this . options , ( err ) => {
4448 if ( err ) {
@@ -82,30 +86,51 @@ export class DevServer {
8286 // to the index: https://github.com/bripkens/connect-history-api-fallback#introduction
8387 if ( this . _historyApiFallback && req . method === 'GET' && ! req . url . includes ( '.' ) &&
8488 req . headers . accept && req . headers . accept . includes ( 'text/html' ) ) {
85- req . url = '/index.html' ;
86- }
89+ res . end ( this . _getIndex ( ) ) ;
90+ } else {
91+ const resolvedPath = this . _resolveUrlFromRunfiles ( req . url ) ;
8792
88- const resolvedPath = this . _resolveUrlFromRunfiles ( req . url ) ;
93+ if ( resolvedPath === null ) {
94+ res . statusCode = 404 ;
95+ res . end ( 'Page not found' ) ;
96+ return ;
97+ }
8998
90- if ( resolvedPath === null ) {
91- res . statusCode = 404 ;
92- res . end ( 'Page not found' ) ;
93- return ;
99+ send ( req , resolvedPath ) . pipe ( res ) ;
94100 }
95-
96- send ( req , resolvedPath ) . pipe ( res ) ;
97101 }
98102
99103 /** Resolves a given URL from the runfiles using the corresponding manifest path. */
100104 private _resolveUrlFromRunfiles ( url : string ) : string | null {
101105 for ( let rootPath of this . _rootPaths ) {
102106 try {
103107 return require . resolve ( path . posix . join ( rootPath , getManifestPath ( url ) ) ) ;
104- } catch {
105- }
108+ } catch { }
106109 }
107110 return null ;
108111 }
112+
113+ /** Gets the content of the index.html. */
114+ private _getIndex ( ) : string {
115+ if ( ! this . _index ) {
116+ const indexPath = this . _resolveUrlFromRunfiles ( '/index.html' ) ;
117+
118+ if ( ! indexPath ) {
119+ throw Error ( 'Could not resolve dev server index.html' ) ;
120+ }
121+
122+ // We support specifying a variables.json file next to the index.html which will be inlined
123+ // into the dev app as a `script` tag. It is used to pass in environment-specific variables.
124+ const varsPath = path . join ( path . dirname ( indexPath ) , 'variables.json' ) ;
125+ const scriptTag = '<script>window.DEV_APP_VARIABLES = ' +
126+ ( existsSync ( varsPath ) ? readFileSync ( varsPath , 'utf8' ) : '{}' ) + ';</script>' ;
127+ const content = readFileSync ( indexPath , 'utf8' ) ;
128+ const headIndex = content . indexOf ( '</head>' ) ;
129+ this . _index = content . slice ( 0 , headIndex ) + scriptTag + content . slice ( headIndex ) ;
130+ }
131+
132+ return this . _index ;
133+ }
109134}
110135
111136/** Gets the manifest path for a given url */
0 commit comments