Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emit bundler-friendly URL locators #14135

Merged
merged 5 commits into from
May 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1206,8 +1206,6 @@ def phase_setup(state):
else:
target = 'a.out.js'

settings.TARGET_BASENAME = unsuffixed_basename(target)

if settings.EXTRA_EXPORTED_RUNTIME_METHODS:
diagnostics.warning('deprecated', 'EXTRA_EXPORTED_RUNTIME_METHODS is deprecated, please use EXPORTED_RUNTIME_METHODS instead')
settings.EXPORTED_RUNTIME_METHODS += settings.EXTRA_EXPORTED_RUNTIME_METHODS
Expand Down Expand Up @@ -2399,6 +2397,13 @@ def phase_post_link(options, in_wasm, wasm_target, target):
if options.oformat != OFormat.WASM:
final_js = in_temp(target_basename + '.js')

settings.TARGET_BASENAME = unsuffixed_basename(target)

if options.oformat in (OFormat.JS, OFormat.MJS):
settings.TARGET_JS_NAME = target
else:
settings.TARGET_JS_NAME = get_secondary_target(target, '.js')

if settings.MEM_INIT_IN_WASM:
memfile = None
else:
Expand Down Expand Up @@ -2534,6 +2539,15 @@ def phase_final_emitting(options, target, wasm_target, memfile):
shared.JS.handle_license(final_js)
shared.run_process([shared.PYTHON, shared.path_from_root('tools', 'hacky_postprocess_around_closure_limitations.py'), final_js])

# Unmangle previously mangled `import.meta` references in both main code and libraries.
# See also: `preprocess` in parseTools.js.
if settings.EXPORT_ES6 and settings.USE_ES6_IMPORT_META:
src = open(final_js).read()
final_js += '.esmeta.js'
with open(final_js, 'w') as f:
f.write(src.replace('EMSCRIPTEN$IMPORT$META', 'import.meta'))
sbc100 marked this conversation as resolved.
Show resolved Hide resolved
save_intermediate('es6-import-meta')

# Apply pre and postjs files
if options.extern_pre_js or options.extern_post_js:
logger.debug('applying extern pre/postjses')
Expand All @@ -2547,10 +2561,7 @@ def phase_final_emitting(options, target, wasm_target, memfile):

shared.JS.handle_license(final_js)

if options.oformat in (OFormat.JS, OFormat.MJS):
js_target = target
else:
js_target = get_secondary_target(target, '.js')
js_target = settings.TARGET_JS_NAME

# The JS is now final. Move it to its final location
move_file(final_js, js_target)
Expand Down
3 changes: 3 additions & 0 deletions src/closure-externs/closure-externs.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
* The closure_compiler() method in tools/shared.py refers to this file when calling closure.
*/

// Special placeholder for `import.meta`.
var EMSCRIPTEN$IMPORT$META;

// Closure externs used by library_sockfs.js

/**
Expand Down
17 changes: 16 additions & 1 deletion src/library_pthread.js
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,11 @@ var LibraryPThread = {
// it could load up the same file. In that case, developer must either deliver the Blob
// object in Module['mainScriptUrlOrBlob'], or a URL to it, so that pthread Workers can
// independently load up the same main application file.
'urlOrBlob': Module['mainScriptUrlOrBlob'] || _scriptDir,
'urlOrBlob': Module['mainScriptUrlOrBlob']
#if !EXPORT_ES6
|| _scriptDir
#endif
,
#if WASM2JS
// the polyfill WebAssembly.Memory instance has function properties,
// which will fail in postMessage, so just send a custom object with the
Expand All @@ -469,6 +473,17 @@ var LibraryPThread = {
#if MINIMAL_RUNTIME
var pthreadMainJs = Module['worker'];
#else
#if EXPORT_ES6 && USE_ES6_IMPORT_META
// If we're using module output and there's no explicit override, use bundler-friendly pattern.
if (!Module['locateFile']) {
#if PTHREADS_DEBUG
out('Allocating a new web worker from ' + new URL('{{{ PTHREAD_WORKER_FILE }}}', import.meta.url));
#endif
// Use bundler-friendly `new Worker(new URL(..., import.meta.url))` pattern; works in browsers too.
PThread.unusedWorkers.push(new Worker(new URL('{{{ PTHREAD_WORKER_FILE }}}', import.meta.url)));
return;
}
#endif
// Allow HTML module to configure the location where the 'worker.js' file will be loaded from,
// via Module.locateFile() function. If not specified, then the default URL 'worker.js' relative
// to the main html file is loaded.
Expand Down
8 changes: 8 additions & 0 deletions src/parseTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ function processMacros(text) {
// Param filenameHint can be passed as a description to identify the file that is being processed, used
// to locate errors for reporting and for html files to stop expansion between <style> and </style>.
function preprocess(text, filenameHint) {
if (EXPORT_ES6 && USE_ES6_IMPORT_META) {
// `eval`, Terser and Closure don't support module syntax; to allow it,
// we need to temporarily replace `import.meta` usages with placeholders
// during preprocess phase, and back after all the other ops.
// See also: `phase_final_emitting` in emcc.py.
text = text.replace(/\bimport\.meta\b/g, 'EMSCRIPTEN$IMPORT$META');
}

const IGNORE = 0;
const SHOW = 1;
// This state is entered after we have shown one of the block of an if/elif/else sequence.
Expand Down
7 changes: 6 additions & 1 deletion src/preamble.js
Original file line number Diff line number Diff line change
Expand Up @@ -746,10 +746,15 @@ function instrumentWasmTableWithAbort() {
}
#endif

#if EXPORT_ES6
// Use bundler-friendly `new URL(..., import.meta.url)` pattern; works in browsers too.
var wasmBinaryFile = new URL('{{{ WASM_BINARY_FILE }}}', import.meta.url).toString();
#else
var wasmBinaryFile = '{{{ WASM_BINARY_FILE }}}';
if (!isDataURI(wasmBinaryFile)) {
wasmBinaryFile = locateFile(wasmBinaryFile);
}
#endif

function getBinary(file) {
try {
Expand Down Expand Up @@ -809,7 +814,7 @@ function getBinaryPromise() {
}
#endif
}

// Otherwise, getBinary should be able to get it synchronously
return Promise.resolve().then(function() { return getBinary(wasmBinaryFile); });
}
Expand Down
3 changes: 3 additions & 0 deletions src/settings_internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ var SIDE_MODULE_IMPORTS = [];
// stores the base name of the output file (-o TARGET_BASENAME.js)
var TARGET_BASENAME = '';

// stores the base name (with extension) of the output JS file
var TARGET_JS_NAME = '';

// Indicates that the syscalls (which we see statically) indicate that they need
// full filesystem support. Otherwise, when just a small subset are used, we can
// get away without including the full filesystem - in particular, if open() is
Expand Down
2 changes: 1 addition & 1 deletion src/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ self.onmessage = function(e) {
#endif

#if MODULARIZE && EXPORT_ES6
import(e.data.urlOrBlob).then(function({{{ EXPORT_NAME }}}) {
(e.data.urlOrBlob ? import(e.data.urlOrBlob) : import('./{{{ TARGET_JS_NAME }}}')).then(function({{{ EXPORT_NAME }}}) {
return {{{ EXPORT_NAME }}}.default(Module);
}).then(function(instance) {
Module = instance;
Expand Down