Skip to content

Commit c7db803

Browse files
authored
build: set up generic way of passing in environment variables to dev app (#21825)
Currently we have a one-off way of passing the Google Maps API key to the dev app. These changes rework the approach so that we have a single `variables.json` file which can be used for the Google Maps key, as well as any other variables in the future.
1 parent 0484310 commit c7db803

File tree

4 files changed

+52
-35
lines changed

4 files changed

+52
-35
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,6 @@ testem.log
4343
*.log
4444
.ng-dev.user*
4545
.husky/_
46-
/src/dev-app/google-maps-api-key.txt
46+
47+
# Variables that are inlined into the dev app index.html
48+
/src/dev-app/variables.json

src/dev-app/BUILD.bazel

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,12 @@ create_system_config(
122122
output_name = "system-config.js",
123123
)
124124

125-
# File group for static files that are listed in the gitignore file that contain
126-
# secrets like API keys.
125+
# Variables that are going to be inlined into the dev app index.html.
127126
filegroup(
128-
name = "environment-secret-assets",
129-
srcs = glob(["*-api-key.txt"]),
127+
name = "variables",
128+
# Note that we need the * in the pattern, because there's a lint rule which
129+
# doesn't allow single files in a `glob`. We have to use a glob, because the file is optional.
130+
srcs = glob(["*variables.json"]),
130131
)
131132

132133
# File group for all static files which are needed to serve the dev-app. These files are
@@ -137,9 +138,9 @@ filegroup(
137138
srcs = [
138139
"favicon.ico",
139140
"index.html",
140-
":environment-secret-assets",
141141
":system-config",
142142
":theme",
143+
":variables",
143144
"//src/dev-app/icon:icon_demo_assets",
144145
"//tools:system-rxjs-operators.js",
145146
"@npm//:node_modules/@material/animation/dist/mdc.animation.js",

src/dev-app/index.html

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,30 +25,19 @@
2525
<body>
2626
<dev-app>Loading...</dev-app>
2727

28-
<!-- This iframe loads the hidden Google Maps API Key. -->
29-
<iframe id="google-maps-api-key"
30-
src="google-maps-api-key.txt"
31-
style="display:none;"
32-
onload="loadGoogleMapsScript()"></iframe>
33-
3428
<script src="core-js-bundle/index.js"></script>
3529
<script src="zone.js/dist/zone.js"></script>
3630
<script src="systemjs/dist/system.js"></script>
3731
<script src="system-config.js"></script>
3832
<script src="https://www.youtube.com/iframe_api"></script>
3933
<script src="https://unpkg.com/@googlemaps/markerclustererplus/dist/index.min.js"></script>
4034
<script>
41-
function loadGoogleMapsScript() {
42-
var iframe = document.getElementById('google-maps-api-key');
43-
var googleMapsScript = document.createElement('script');
44-
var googleMapsApiKey = iframe.contentDocument.body.textContent;
45-
var googleMapsUrl = 'https://maps.googleapis.com/maps/api/js?libraries=visualization';
46-
if (googleMapsApiKey !== 'Page not found') {
47-
googleMapsUrl = googleMapsUrl + '&key=' + googleMapsApiKey;
48-
}
49-
googleMapsScript.src = googleMapsUrl;
50-
document.body.appendChild(googleMapsScript);
51-
}
35+
(function loadGoogleMaps(key) {
36+
var script = document.createElement('script');
37+
script.src = 'https://maps.googleapis.com/maps/api/js?libraries=visualization' +
38+
(key ? '&key=' + key : '');
39+
document.body.appendChild(script);
40+
})(window.DEV_APP_VARIABLES.GOOGLE_MAPS_KEY);
5241
</script>
5342
<script>
5443
System.config({

tools/dev-server/dev-server.ts

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {readFileSync, existsSync} from 'fs';
910
import * as browserSync from 'browser-sync';
1011
import * as http from 'http';
1112
import * as path from 'path';
@@ -17,6 +18,9 @@ import * as send from 'send';
1718
* environment and on Windows (with a runfile manifest file).
1819
*/
1920
export 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

Comments
 (0)