Skip to content

Commit 2c18c95

Browse files
authored
Generalize existing Node.js development-time version check (#25414)
Generalize existing Node.js development-time version check into common file that also checks for browser versions.
1 parent 11a55a5 commit 2c18c95

12 files changed

+128
-42
lines changed

eslint.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export default [{
2727
'**/test/',
2828
'src/polyfill/',
2929
'src/lib/',
30+
'src/minimum_runtime_check.js',
3031
'src/runtime_*.js',
3132
'src/shell*.js',
3233
'src/modularize.js',

src/minimum_runtime_check.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* @license
3+
* Copyright 2024 The Emscripten Authors
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#if ASSERTIONS
8+
9+
(function() {
10+
// "30.0.0" -> 300000
11+
function humanReadableVersionToPacked(str) {
12+
str = str.split('-')[0]; // Remove any trailing part from e.g. "12.53.3-alpha"
13+
var vers = str.split('.').slice(0, 3);
14+
while(vers.length < 3) vers.push('00');
15+
vers = vers.map((n, i, arr) => n.padStart(2, '0'));
16+
return vers.join('');
17+
}
18+
// 300000 -> "30.0.0"
19+
var packedVersionToHumanReadable = n => [n / 10000 | 0, (n / 100 | 0) % 100, n % 100].join('.');
20+
21+
var TARGET_NOT_SUPPORTED = {{{ TARGET_NOT_SUPPORTED }}};
22+
23+
var currentNodeVersion = typeof process !== 'undefined' && process?.versions?.node ? humanReadableVersionToPacked(process.versions.node) : TARGET_NOT_SUPPORTED;
24+
#if MIN_NODE_VERSION == TARGET_NOT_SUPPORTED
25+
if (currentNodeVersion < TARGET_NOT_SUPPORTED) {
26+
throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
27+
}
28+
#endif
29+
if (currentNodeVersion < {{{ MIN_NODE_VERSION }}}) {
30+
throw new Error(`This emscripten-generated code requires node v${ packedVersionToHumanReadable({{{ MIN_NODE_VERSION }}}) } (detected v${packedVersionToHumanReadable(currentNodeVersion)})`);
31+
}
32+
33+
var currentSafariVersion = typeof navigator !== 'undefined' && navigator?.userAgent?.includes("Safari/") && navigator.userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/) ? humanReadableVersionToPacked(navigator.userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/)[1]) : TARGET_NOT_SUPPORTED;
34+
#if MIN_SAFARI_VERSION == TARGET_NOT_SUPPORTED
35+
if (currentSafariVersion < TARGET_NOT_SUPPORTED) {
36+
throw new Error(`This page was compiled without support for Safari browser. Pass -sMIN_SAFARI_VERSION=${currentSafariVersion} or lower to enable support for this browser.`);
37+
}
38+
#endif
39+
if (currentSafariVersion < {{{ MIN_SAFARI_VERSION }}}) {
40+
throw new Error(`This emscripten-generated code requires Safari v${ packedVersionToHumanReadable({{{ MIN_SAFARI_VERSION }}}) } (detected v${currentSafariVersion})`);
41+
}
42+
43+
var currentFirefoxVersion = typeof navigator !== 'undefined' && navigator?.userAgent?.match(/Firefox\/(\d+(?:\.\d+)?)/) ? parseFloat(navigator.userAgent.match(/Firefox\/(\d+(?:\.\d+)?)/)[1]) : TARGET_NOT_SUPPORTED;
44+
#if MIN_FIREFOX_VERSION == TARGET_NOT_SUPPORTED
45+
if (currentFirefoxVersion < TARGET_NOT_SUPPORTED) {
46+
throw new Error(`This page was compiled without support for Firefox browser. Pass -sMIN_FIREFOX_VERSION=${currentFirefoxVersion} or lower to enable support for this browser.`);
47+
}
48+
#endif
49+
if (currentFirefoxVersion < {{{ MIN_FIREFOX_VERSION }}}) {
50+
throw new Error(`This emscripten-generated code requires Firefox v{{{ MIN_FIREFOX_VERSION }}} (detected v${currentFirefoxVersion})`);
51+
}
52+
53+
var currentChromeVersion = typeof navigator !== 'undefined' && navigator?.userAgent?.match(/Chrome\/(\d+(?:\.\d+)?)/) ? parseFloat(navigator.userAgent.match(/Chrome\/(\d+(?:\.\d+)?)/)[1]) : TARGET_NOT_SUPPORTED;
54+
#if MIN_CHROME_VERSION == TARGET_NOT_SUPPORTED
55+
if (currentChromeVersion < TARGET_NOT_SUPPORTED) {
56+
throw new Error(`This page was compiled without support for Chrome browser. Pass -sMIN_CHROME_VERSION=${currentChromeVersion} or lower to enable support for this browser.`);
57+
}
58+
#endif
59+
if (currentChromeVersion < {{{ MIN_CHROME_VERSION }}}) {
60+
throw new Error(`This emscripten-generated code requires Chrome v{{{ MIN_CHROME_VERSION }}} (detected v${currentChromeVersion})`);
61+
}
62+
})();
63+
64+
#endif

src/shell.js

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
"use strict";
88

99
#endif
10+
11+
#include "minimum_runtime_check.js"
12+
1013
// The Module object: Our interface to the outside world. We import
1114
// and export values on it. There are various ways Module can be used:
1215
// 1. Not defined. We create it here
@@ -192,15 +195,6 @@ if (ENVIRONMENT_IS_NODE) {
192195
if (!isNode) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
193196
#endif
194197

195-
#if ASSERTIONS
196-
var nodeVersion = process.versions.node;
197-
var numericVersion = nodeVersion.split('.').slice(0, 3);
198-
numericVersion = (numericVersion[0] * 10000) + (numericVersion[1] * 100) + (numericVersion[2].split('-')[0] * 1);
199-
if (numericVersion < {{{ MIN_NODE_VERSION }}}) {
200-
throw new Error('This emscripten-generated code requires node {{{ formattedMinNodeVersion() }}} (detected v' + nodeVersion + ')');
201-
}
202-
#endif
203-
204198
// These modules will usually be used on Node.js. Load them eagerly to avoid
205199
// the complexity of lazy-loading.
206200
var fs = require('fs');
@@ -269,11 +263,6 @@ if (ENVIRONMENT_IS_NODE) {
269263
#if ENVIRONMENT_MAY_BE_SHELL || ASSERTIONS
270264
if (ENVIRONMENT_IS_SHELL) {
271265

272-
#if ENVIRONMENT.length && ASSERTIONS
273-
const isNode = {{{ nodeDetectionCode() }}};
274-
if (isNode || typeof window == 'object' || typeof WorkerGlobalScope != 'undefined') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
275-
#endif
276-
277266
#if ENVIRONMENT_MAY_BE_SHELL
278267
readBinary = (f) => {
279268
if (typeof readbuffer == 'function') {

src/shell_minimal.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
* SPDX-License-Identifier: MIT
55
*/
66

7+
#include "minimum_runtime_check.js"
8+
79
#if MODULARIZE
810
var Module = moduleArg;
911
#elif USE_CLOSURE_COMPILER

src/shell_minimal_runtime.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<script>
55

66
#if !MODULARIZE
7-
var {{{ EXPORT_NAME }}} = {}
7+
var {{{ EXPORT_NAME }}} = {};
88
#endif
99

1010
#if WASM == 2

test/codesize/test_codesize_file_preload.expected.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// include: shell.js
22
"use strict";
33

4+
// include: minimum_runtime_check.js
5+
// end include: minimum_runtime_check.js
46
// The Module object: Our interface to the outside world. We import
57
// and export values on it. There are various ways Module can be used:
68
// 1. Not defined. We create it here

test/codesize/test_codesize_hello_O0.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
2-
"a.out.js": 22478,
3-
"a.out.js.gz": 8314,
2+
"a.out.js": 23348,
3+
"a.out.js.gz": 8559,
44
"a.out.nodebug.wasm": 15127,
55
"a.out.nodebug.wasm.gz": 7450,
6-
"total": 37605,
7-
"total_gz": 15764,
6+
"total": 38475,
7+
"total_gz": 16009,
88
"sent": [
99
"fd_write"
1010
],

test/codesize/test_codesize_minimal_O0.expected.js

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,43 @@
11
// include: shell.js
22
"use strict";
33

4+
// include: minimum_runtime_check.js
5+
(function() {
6+
// "30.0.0" -> 300000
7+
function humanReadableVersionToPacked(str) {
8+
str = str.split('-')[0]; // Remove any trailing part from e.g. "12.53.3-alpha"
9+
var vers = str.split('.').slice(0, 3);
10+
while(vers.length < 3) vers.push('00');
11+
vers = vers.map((n, i, arr) => n.padStart(2, '0'));
12+
return vers.join('');
13+
}
14+
// 300000 -> "30.0.0"
15+
var packedVersionToHumanReadable = n => [n / 10000 | 0, (n / 100 | 0) % 100, n % 100].join('.');
16+
17+
var TARGET_NOT_SUPPORTED = 2147483647;
18+
19+
var currentNodeVersion = typeof process !== 'undefined' && process?.versions?.node ? humanReadableVersionToPacked(process.versions.node) : TARGET_NOT_SUPPORTED;
20+
if (currentNodeVersion < 160000) {
21+
throw new Error(`This emscripten-generated code requires node v${ packedVersionToHumanReadable(160000) } (detected v${packedVersionToHumanReadable(currentNodeVersion)})`);
22+
}
23+
24+
var currentSafariVersion = typeof navigator !== 'undefined' && navigator?.userAgent?.includes("Safari/") && navigator.userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/) ? humanReadableVersionToPacked(navigator.userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/)[1]) : TARGET_NOT_SUPPORTED;
25+
if (currentSafariVersion < 150000) {
26+
throw new Error(`This emscripten-generated code requires Safari v${ packedVersionToHumanReadable(150000) } (detected v${currentSafariVersion})`);
27+
}
28+
29+
var currentFirefoxVersion = typeof navigator !== 'undefined' && navigator?.userAgent?.match(/Firefox\/(\d+(?:\.\d+)?)/) ? parseFloat(navigator.userAgent.match(/Firefox\/(\d+(?:\.\d+)?)/)[1]) : TARGET_NOT_SUPPORTED;
30+
if (currentFirefoxVersion < 79) {
31+
throw new Error(`This emscripten-generated code requires Firefox v79 (detected v${currentFirefoxVersion})`);
32+
}
33+
34+
var currentChromeVersion = typeof navigator !== 'undefined' && navigator?.userAgent?.match(/Chrome\/(\d+(?:\.\d+)?)/) ? parseFloat(navigator.userAgent.match(/Chrome\/(\d+(?:\.\d+)?)/)[1]) : TARGET_NOT_SUPPORTED;
35+
if (currentChromeVersion < 85) {
36+
throw new Error(`This emscripten-generated code requires Chrome v85 (detected v${currentChromeVersion})`);
37+
}
38+
})();
39+
40+
// end include: minimum_runtime_check.js
441
// The Module object: Our interface to the outside world. We import
542
// and export values on it. There are various ways Module can be used:
643
// 1. Not defined. We create it here
@@ -61,13 +98,6 @@ if (ENVIRONMENT_IS_NODE) {
6198
const isNode = typeof process == 'object' && process.versions?.node && process.type != 'renderer';
6299
if (!isNode) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
63100

64-
var nodeVersion = process.versions.node;
65-
var numericVersion = nodeVersion.split('.').slice(0, 3);
66-
numericVersion = (numericVersion[0] * 10000) + (numericVersion[1] * 100) + (numericVersion[2].split('-')[0] * 1);
67-
if (numericVersion < 160000) {
68-
throw new Error('This emscripten-generated code requires node v16.0.0 (detected v' + nodeVersion + ')');
69-
}
70-
71101
// These modules will usually be used on Node.js. Load them eagerly to avoid
72102
// the complexity of lazy-loading.
73103
var fs = require('fs');
@@ -110,9 +140,6 @@ readAsync = async (filename, binary = true) => {
110140
} else
111141
if (ENVIRONMENT_IS_SHELL) {
112142

113-
const isNode = typeof process == 'object' && process.versions?.node && process.type != 'renderer';
114-
if (isNode || typeof window == 'object' || typeof WorkerGlobalScope != 'undefined') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
115-
116143
} else
117144

118145
// Note that this includes Node.js workers when relevant (pthreads is enabled).

test/codesize/test_codesize_minimal_O0.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
2-
"a.out.js": 17754,
3-
"a.out.js.gz": 6617,
2+
"a.out.js": 18626,
3+
"a.out.js.gz": 6874,
44
"a.out.nodebug.wasm": 1136,
55
"a.out.nodebug.wasm.gz": 659,
6-
"total": 18890,
7-
"total_gz": 7276,
6+
"total": 19762,
7+
"total_gz": 7533,
88
"sent": [],
99
"imports": [],
1010
"exports": [
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
{
2-
"hello_world.js": 54133,
3-
"hello_world.js.gz": 17091,
2+
"hello_world.js": 55560,
3+
"hello_world.js.gz": 17522,
44
"hello_world.wasm": 15127,
55
"hello_world.wasm.gz": 7450,
6-
"no_asserts.js": 26513,
7-
"no_asserts.js.gz": 8864,
6+
"no_asserts.js": 26591,
7+
"no_asserts.js.gz": 8875,
88
"no_asserts.wasm": 12227,
99
"no_asserts.wasm.gz": 6010,
10-
"strict.js": 52171,
11-
"strict.js.gz": 16426,
10+
"strict.js": 53598,
11+
"strict.js.gz": 16856,
1212
"strict.wasm": 15127,
1313
"strict.wasm.gz": 7447,
14-
"total": 175298,
15-
"total_gz": 63288
14+
"total": 178230,
15+
"total_gz": 64160
1616
}

0 commit comments

Comments
 (0)