Skip to content

Commit 53bb926

Browse files
committed
Use babel for transpiling rather than closure compiler
We recently switch from using closure-compiler with `WHITESPACE_ONLY` to closure-compiler with `SIMPLE_OPTIMIZATIONS`. However this had the negative side effect that all input need to be free of closure compiler warnings, and this turned out not to be practical for all users. See #20810 for more on this When selecting a transpilation tool use I also evaluated `swx` (written in rust) and `esbuild` (written in go). `esbuild` was rejected because the simply don't support ES5 (evanw/esbuild#297). `swx` was rejected because it almost doubled the side of our `node_modules` directory by adding two 50mb binary files.
1 parent c85f1b1 commit 53bb926

File tree

9 files changed

+6288
-2547
lines changed

9 files changed

+6288
-2547
lines changed

.flake8

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[flake8]
22
ignore = E111,E114,E501,E261,E266,E121,E402,E241,W504,E741,B011,B023,U101
33
exclude =
4+
./node_modules/, # third-party code
45
./third_party/, # third-party code
56
./tools/filelock.py, # third-party code
67
./tools/scons/, # third-party code

ChangeLog.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,20 @@ See docs/process.md for more on how version tagging works.
2929
ports of native GL renderers from later accidentally attempting to activate
3030
"dormant" features if web browser implementations gain new WebGL extensions in
3131
the future, which `*glGetProcAddress()` is not able to support. (#20802)
32-
- Added Hi DPI support to GLFW. When enabled, GLFW automatically accounts for the
33-
`devicePixelRatio` browser property and changes the size of the canvas accordingly
34-
(including dynamically if the canvas is moved from a 4k screen to a 2k screen and
35-
vice-versa). `glfwGetFramebufferSize` now properly returns the canvas size in pixels,
36-
while `glfwGetWindowSize` returns the canvas size is screen size. By default,
37-
this feature is disabled. You can enable it before creating a window by calling
38-
`glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE)`. You can also
39-
dynamically change it after the window has been created by calling
40-
`glfwSetWindowAttrib(window, GLFW_SCALE_TO_MONITOR, GLFW_TRUE)`. (#20584)
32+
- Added Hi DPI support to GLFW. When enabled, GLFW automatically accounts for
33+
the `devicePixelRatio` browser property and changes the size of the canvas
34+
accordingly (including dynamically if the canvas is moved from a 4K screen to
35+
a 2K screen and vice-versa). `glfwGetFramebufferSize` now properly returns the
36+
canvas size in pixels, while `glfwGetWindowSize` returns the canvas size is
37+
screen size. By default, this feature is disabled. You can enable it before
38+
creating a window by calling `glfwWindowHint(GLFW_SCALE_TO_MONITOR,
39+
GLFW_TRUE)`. You can also dynamically change it after the window has been
40+
created by calling `glfwSetWindowAttrib(window, GLFW_SCALE_TO_MONITOR,
41+
GLFW_TRUE)`. (#20584)
42+
- Transpilation to support older environments/browsers is now performed by babel
43+
rather than closure compiler. This means that folks targeting older browsers
44+
(e.g. `-sLEGACY_VM_SUPPORT`) do not need to ensure their code is closure
45+
compliant. (#20879)
4146

4247
3.1.50 - 11/29/23
4348
-----------------

package-lock.json

Lines changed: 6213 additions & 2498 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
"ws": "^8.14.2"
1212
},
1313
"dependencies": {
14+
"@babel/cli": "^7.23.4",
15+
"@babel/core": "^7.23.5",
16+
"@babel/preset-env": "^7.23.5",
1417
"acorn": "^8.11.2",
1518
"google-closure-compiler": "20230802.0.0",
1619
"html-minifier-terser": "7.2.0"

src/settings_internal.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,9 @@ var LINK_AS_CXX = false;
243243
// emitted in that case for closure compiler.
244244
var MAYBE_CLOSURE_COMPILER = false;
245245

246-
// Set when some minimum browser version triggers doesn't support the
247-
// minimum set of ES6 features. This triggers transpilation to ES5
248-
// using closure compiler.
249-
var TRANSPILE_TO_ES5 = false;
246+
// Set when some minimum browser version triggers doesn't support the minimum
247+
// set of JavaScript features. This triggers transpilation using babel.
248+
var TRANSPILE = false;
250249

251250
// A copy of the default the default INCOMING_MODULE_JS_API. (Soon to
252251
// include additional items).

test/test_other.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13130,7 +13130,6 @@ def check_for_es6(filename, expect):
1313013130
self.assertContained('foo(arg="hello")', js)
1313113131
self.assertContained(['() => 2', '()=>2'], js)
1313213132
self.assertContained('const ', js)
13133-
self.assertContained('let ', js)
1313413133
self.assertContained('?.[', js)
1313513134
self.assertContained('?.(', js)
1313613135
self.assertContained('??=', js)
@@ -13142,7 +13141,6 @@ def check_for_es6(filename, expect):
1314213141
self.assertNotContained('() => 2', js)
1314313142
self.assertNotContained('()=>2', js)
1314413143
self.assertNotContained('const ', js)
13145-
self.assertNotContained('let ', js)
1314613144
self.assertNotContained('??', js)
1314713145
self.assertNotContained('?.', js)
1314813146
self.assertNotContained('||=', js)
@@ -13162,13 +13160,12 @@ def check_for_es6(filename, expect):
1316213160
self.do_runf('test.c', expected, output_basename='test_old')
1316313161
check_for_es6('test_old.js', False)
1316413162

13165-
# If we add `--closure=0` that transpiler (closure) is not run at all
13166-
print('with old browser + --closure=0')
13167-
self.do_runf('test.c', expected, emcc_args=['--closure=0'], output_basename='test_no_closure')
13163+
# If we add `-sPOLYFILL=0` that transpiler is not run at all
13164+
print('with old browser + -sPOLYFILL=0')
13165+
self.do_runf('test.c', expected, emcc_args=['-sPOLYFILL=0'], output_basename='test_no_closure')
1316813166
check_for_es6('test_no_closure.js', True)
1316913167

13170-
# If we use `--closure=1` closure will run in full optimization mode
13171-
# and also transpile to ES5
13168+
# Test that transpiling is compatible with `--closure=1`
1317213169
print('with old browser + --closure=1')
1317313170
self.do_runf('test.c', expected, emcc_args=['--closure=1'], output_basename='test_closure')
1317413171
check_for_es6('test_closure.js', False)

tools/building.py

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from .shared import get_emscripten_temp_dir, exe_suffix, is_c_symbol
3434
from .utils import WINDOWS
3535
from .settings import settings, default_setting
36+
from .feature_matrix import UNSUPPORTED
3637

3738
logger = logging.getLogger('building')
3839

@@ -509,14 +510,39 @@ def add_to_path(dirname):
509510
return closure_cmd, env
510511

511512

513+
def version_split(v):
514+
"""Split version setting number (e.g. 162000) into versions string (e.g. "16.2.0")
515+
"""
516+
v = str(v).rjust(6, '0')
517+
assert len(v) == 6
518+
m = re.match(r'(\d{2})(\d{2})(\d{2})', v)
519+
major, minor, rev = m.group(1, 2, 3)
520+
return f'{int(major)}.{int(minor)}.{int(rev)}'
521+
522+
512523
@ToolchainProfiler.profile()
513-
def closure_transpile(filename):
514-
user_args = []
515-
closure_cmd, env = get_closure_compiler_and_env(user_args)
516-
closure_cmd += ['--language_out', 'ES5']
517-
closure_cmd += ['--compilation_level', 'SIMPLE_OPTIMIZATIONS']
518-
closure_cmd += ['--formatting', 'PRETTY_PRINT']
519-
return run_closure_cmd(closure_cmd, filename, env)
524+
def transpile(filename):
525+
config = {
526+
'sourceType': 'script',
527+
'targets': {}
528+
}
529+
if settings.MIN_CHROME_VERSION != UNSUPPORTED:
530+
config['targets']['chrome'] = str(settings.MIN_CHROME_VERSION)
531+
if settings.MIN_FIREFOX_VERSION != UNSUPPORTED:
532+
config['targets']['firefox'] = str(settings.MIN_FIREFOX_VERSION)
533+
if settings.MIN_IE_VERSION != UNSUPPORTED:
534+
config['targets']['ie'] = str(settings.MIN_IE_VERSION)
535+
if settings.MIN_SAFARI_VERSION != UNSUPPORTED:
536+
config['targets']['safari'] = version_split(settings.MIN_SAFARI_VERSION)
537+
if settings.MIN_NODE_VERSION != UNSUPPORTED:
538+
config['targets']['node'] = version_split(settings.MIN_NODE_VERSION)
539+
config_json = json.dumps(config, indent=2)
540+
outfile = shared.get_temp_files().get('babel.js').name
541+
config_file = shared.get_temp_files().get('babel_config.json').name
542+
utils.write_file(config_file, config_json)
543+
cmd = shared.get_npm_cmd('babel') + [filename, '-o', outfile, '--presets', '@babel/preset-env', '--config-file', config_file]
544+
check_call(cmd, cwd=path_from_root())
545+
return outfile
520546

521547

522548
@ToolchainProfiler.profile()
@@ -581,13 +607,8 @@ def closure_compiler(filename, advanced=True, extra_closure_args=None):
581607
args = ['--compilation_level', 'ADVANCED_OPTIMIZATIONS' if advanced else 'SIMPLE_OPTIMIZATIONS']
582608
# Keep in sync with ecmaVersion in tools/acorn-optimizer.mjs
583609
args += ['--language_in', 'ECMASCRIPT_2021']
584-
# Tell closure not to do any transpiling or inject any polyfills.
585-
# At some point we may want to look into using this as way to convert to ES5 but
586-
# babel is perhaps a better tool for that.
587-
if settings.TRANSPILE_TO_ES5:
588-
args += ['--language_out', 'ES5']
589-
else:
590-
args += ['--language_out', 'NO_TRANSPILE']
610+
# We do transpilation using babel
611+
args += ['--language_out', 'NO_TRANSPILE']
591612
# Tell closure never to inject the 'use strict' directive.
592613
args += ['--emit_use_strict=false']
593614

tools/feature_matrix.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
logger = logging.getLogger('feature_matrix')
1515

16+
UNSUPPORTED = 0x7FFFFFFF
17+
1618

1719
class Feature(IntEnum):
1820
NON_TRAPPING_FPTOINT = auto()
@@ -91,7 +93,7 @@ def report_missing(setting_name):
9193
report_missing('MIN_SAFARI_VERSION')
9294
return False
9395
# IE don't support any non-MVP features
94-
if settings.MIN_IE_VERSION != 0x7FFFFFFF:
96+
if settings.MIN_IE_VERSION != UNSUPPORTED:
9597
report_missing('MIN_IE_VERSION')
9698
return False
9799
if 'node' in min_versions and settings.MIN_NODE_VERSION < min_versions['node']:

tools/link.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,7 +1125,7 @@ def phase_linker_setup(options, state, newargs):
11251125

11261126
setup_environment_settings()
11271127

1128-
if options.use_closure_compiler != 0 and settings.POLYFILL:
1128+
if settings.POLYFILL:
11291129
# Emscripten requires certain ES6+ constructs by default in library code
11301130
# - (various ES6 operators available in all browsers listed below)
11311131
# - https://caniuse.com/mdn-javascript_operators_nullish_coalescing:
@@ -1137,14 +1137,11 @@ def phase_linker_setup(options, state, newargs):
11371137
# Taking the highest requirements gives is our minimum:
11381138
# Max Version: FF:79 CHROME:85 SAFARI:14 NODE:16
11391139
# TODO: replace this with feature matrix in the future.
1140-
settings.TRANSPILE_TO_ES5 = (settings.MIN_FIREFOX_VERSION < 79 or
1141-
settings.MIN_CHROME_VERSION < 85 or
1142-
settings.MIN_SAFARI_VERSION < 140000 or
1143-
settings.MIN_NODE_VERSION < 160000 or
1144-
settings.MIN_IE_VERSION != 0x7FFFFFFF)
1145-
1146-
if options.use_closure_compiler is None and settings.TRANSPILE_TO_ES5:
1147-
diagnostics.warning('transpile', 'enabling transpilation via closure due to browser version settings. This warning can be suppressed by passing `--closure=1` or `--closure=0` to opt into this explicitly.')
1140+
settings.TRANSPILE = (settings.MIN_FIREFOX_VERSION < 79 or
1141+
settings.MIN_CHROME_VERSION < 85 or
1142+
settings.MIN_SAFARI_VERSION < 140000 or
1143+
settings.MIN_NODE_VERSION < 160000 or
1144+
settings.MIN_IE_VERSION != 0x7FFFFFFF)
11481145

11491146
# https://caniuse.com/class: FF:45 CHROME:49 SAFARI:9
11501147
supports_es6_classes = (settings.MIN_FIREFOX_VERSION >= 45 and
@@ -2200,14 +2197,15 @@ def phase_binaryen(target, options, wasm_target, memfile):
22002197
with ToolchainProfiler.profile_block('asyncify_lazy_load_code'):
22012198
building.asyncify_lazy_load_code(wasm_target, debug=intermediate_debug_info)
22022199

2203-
if final_js and (options.use_closure_compiler or settings.TRANSPILE_TO_ES5):
2200+
if final_js:
22042201
if options.use_closure_compiler:
22052202
with ToolchainProfiler.profile_block('closure_compile'):
22062203
final_js = building.closure_compiler(final_js, extra_closure_args=options.closure_args)
2207-
else:
2208-
with ToolchainProfiler.profile_block('closure_transpile'):
2209-
final_js = building.closure_transpile(final_js)
2210-
save_intermediate_with_wasm('closure', wasm_target)
2204+
save_intermediate_with_wasm('closure', wasm_target)
2205+
if settings.TRANSPILE:
2206+
with ToolchainProfiler.profile_block('transpile'):
2207+
final_js = building.transpile(final_js)
2208+
save_intermediate_with_wasm('traspile', wasm_target)
22112209

22122210
symbols_file = None
22132211
if options.emit_symbol_map:

0 commit comments

Comments
 (0)