Skip to content

Commit

Permalink
Use babel for traspiling rather than closure compiler
Browse files Browse the repository at this point in the history
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 emscripten-core#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.
  • Loading branch information
sbc100 committed Dec 8, 2023
1 parent dabbde6 commit 7b1da18
Show file tree
Hide file tree
Showing 7 changed files with 6,262 additions and 2,532 deletions.
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[flake8]
ignore = E111,E114,E501,E261,E266,E121,E402,E241,W504,E741,B011,B023,U101
exclude =
./node_modules/, # third-party code
./third_party/, # third-party code
./tools/filelock.py, # third-party code
./tools/scons/, # third-party code
Expand Down
8,711 changes: 6,213 additions & 2,498 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
"ws": "^8.14.2"
},
"dependencies": {
"@babel/cli": "^7.23.4",
"@babel/core": "^7.23.5",
"@babel/preset-env": "^7.23.5",
"acorn": "^8.11.2",
"google-closure-compiler": "20230802.0.0",
"html-minifier-terser": "7.2.0"
Expand Down
7 changes: 3 additions & 4 deletions src/settings_internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,9 @@ var LINK_AS_CXX = false;
// emitted in that case for closure compiler.
var MAYBE_CLOSURE_COMPILER = false;

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

// A copy of the default the default INCOMING_MODULE_JS_API. (Soon to
// include additional items).
Expand Down
2 changes: 0 additions & 2 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -13130,7 +13130,6 @@ def check_for_es6(filename, expect):
self.assertContained('foo(arg="hello")', js)
self.assertContained(['() => 2', '()=>2'], js)
self.assertContained('const ', js)
self.assertContained('let ', js)
self.assertContained('?.[', js)
self.assertContained('?.(', js)
self.assertContained('??=', js)
Expand All @@ -13142,7 +13141,6 @@ def check_for_es6(filename, expect):
self.assertNotContained('() => 2', js)
self.assertNotContained('()=>2', js)
self.assertNotContained('const ', js)
self.assertNotContained('let ', js)
self.assertNotContained('??', js)
self.assertNotContained('?.', js)
self.assertNotContained('||=', js)
Expand Down
44 changes: 30 additions & 14 deletions tools/building.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,14 +509,35 @@ def add_to_path(dirname):
return closure_cmd, env


def version_split(v):
v = str(v)
assert len(v) == 6
rev = int(v[4:6])
minor = int(v[2:4])
major = int(v[0:2])
return f'{major}.{minor}.{rev}'


@ToolchainProfiler.profile()
def closure_transpile(filename):
user_args = []
closure_cmd, env = get_closure_compiler_and_env(user_args)
closure_cmd += ['--language_out', 'ES5']
closure_cmd += ['--compilation_level', 'SIMPLE_OPTIMIZATIONS']
closure_cmd += ['--formatting', 'PRETTY_PRINT']
return run_closure_cmd(closure_cmd, filename, env)
def transpile(filename):
outfile = shared.get_temp_files().get('babel.js').name
config_file = shared.get_temp_files().get('babel_config.json').name
config = '{\n "targets": {'
if settings.MIN_CHROME_VERSION:
config += f'\n "chrome": "{settings.MIN_CHROME_VERSION}",'
if settings.MIN_FIREFOX_VERSION:
config += f'\n "firefox": "{settings.MIN_FIREFOX_VERSION}",'
if settings.MIN_SAFARI_VERSION:
min_safari_version = version_split(settings.MIN_SAFARI_VERSION)
config += f'\n "safari": "{min_safari_version}",'
if settings.MIN_NODE_VERSION:
min_node_version = version_split(settings.MIN_NODE_VERSION)
config += f'\n "node": "{min_node_version}",'
config += '\n }\n}'
utils.write_file(config_file, config)
cmd = shared.get_npm_cmd('babel') + [filename, '-o', outfile, '--presets', '@babel/preset-env', '--config-file', config_file]
check_call(cmd, cwd=path_from_root())
return outfile


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

Expand Down
26 changes: 12 additions & 14 deletions tools/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -1138,15 +1138,12 @@ def phase_linker_setup(options, state, newargs):
# Taking the highest requirements gives is our minimum:
# Max Version: EDGE:85 FF:79 CHROME:85 SAFARI:14 NODE:16
# TODO: replace this with feature matrix in the future.
settings.TRANSPILE_TO_ES5 = (settings.MIN_EDGE_VERSION < 85 or
settings.MIN_FIREFOX_VERSION < 79 or
settings.MIN_CHROME_VERSION < 85 or
settings.MIN_SAFARI_VERSION < 140000 or
settings.MIN_NODE_VERSION < 160000 or
settings.MIN_IE_VERSION != 0x7FFFFFFF)

if options.use_closure_compiler is None and settings.TRANSPILE_TO_ES5:
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.')
settings.TRANSPILE = (settings.MIN_EDGE_VERSION < 85 or
settings.MIN_FIREFOX_VERSION < 79 or
settings.MIN_CHROME_VERSION < 85 or
settings.MIN_SAFARI_VERSION < 140000 or
settings.MIN_NODE_VERSION < 160000 or
settings.MIN_IE_VERSION != 0x7FFFFFFF)

# https://caniuse.com/class: EDGE:13 FF:45 CHROME:49 SAFARI:9
supports_es6_classes = (settings.MIN_EDGE_VERSION >= 13 and
Expand Down Expand Up @@ -2203,14 +2200,15 @@ def phase_binaryen(target, options, wasm_target, memfile):
with ToolchainProfiler.profile_block('asyncify_lazy_load_code'):
building.asyncify_lazy_load_code(wasm_target, debug=intermediate_debug_info)

if final_js and (options.use_closure_compiler or settings.TRANSPILE_TO_ES5):
if final_js:
if options.use_closure_compiler:
with ToolchainProfiler.profile_block('closure_compile'):
final_js = building.closure_compiler(final_js, extra_closure_args=options.closure_args)
else:
with ToolchainProfiler.profile_block('closure_transpile'):
final_js = building.closure_transpile(final_js)
save_intermediate_with_wasm('closure', wasm_target)
save_intermediate_with_wasm('closure', wasm_target)
if settings.TRANSPILE:
with ToolchainProfiler.profile_block('transpile'):
final_js = building.transpile(final_js)
save_intermediate_with_wasm('traspile', wasm_target)

symbols_file = None
if options.emit_symbol_map:
Expand Down

0 comments on commit 7b1da18

Please sign in to comment.