diff --git a/emcc.py b/emcc.py index 6cc5dd59b9758..4eb747f806950 100755 --- a/emcc.py +++ b/emcc.py @@ -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 @@ -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: @@ -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')) + 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') @@ -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) diff --git a/src/closure-externs/closure-externs.js b/src/closure-externs/closure-externs.js index 5e81717de3ab3..37e1a4a0e5d54 100644 --- a/src/closure-externs/closure-externs.js +++ b/src/closure-externs/closure-externs.js @@ -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 /** diff --git a/src/library_pthread.js b/src/library_pthread.js index 470f8325ac1b2..4bc001b758b7a 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -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 @@ -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. diff --git a/src/parseTools.js b/src/parseTools.js index e2ff8f927f089..15e00bd285428 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -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 . 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. diff --git a/src/preamble.js b/src/preamble.js index 8c5c422ebb410..35b52331c89bd 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -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 { @@ -809,7 +814,7 @@ function getBinaryPromise() { } #endif } - + // Otherwise, getBinary should be able to get it synchronously return Promise.resolve().then(function() { return getBinary(wasmBinaryFile); }); } diff --git a/src/settings_internal.js b/src/settings_internal.js index 8ff5ef7e678f9..959827d55a172 100644 --- a/src/settings_internal.js +++ b/src/settings_internal.js @@ -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 diff --git a/src/worker.js b/src/worker.js index 4aea79943c87a..31528497a808e 100644 --- a/src/worker.js +++ b/src/worker.js @@ -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;