- 
                Notifications
    You must be signed in to change notification settings 
- Fork 3.4k
[file_packager.py] Add --export-es6 to file_packager.py #24737
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
Changes from all commits
115e93d
              b62ddc7
              6707c83
              a25caf1
              697c9e6
              03095d0
              2874708
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -21,7 +21,7 @@ | |
|  | ||
| Usage: | ||
|  | ||
| file_packager TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--indexedDB-name=EM_PRELOAD_CACHE] [--separate-metadata] [--lz4] [--use-preload-plugins] [--no-node] [--help] | ||
| file_packager TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--indexedDB-name=EM_PRELOAD_CACHE] [--separate-metadata] [--lz4] [--use-preload-plugins] [--no-node] [--export-es6] [--help] | ||
|  | ||
| --preload , | ||
| --embed See emcc --help for more details on those options. | ||
|  | @@ -41,6 +41,8 @@ | |
|  | ||
| --export-name=EXPORT_NAME Use custom export name (default is `Module`) | ||
|  | ||
| --export-es6 Wrap generated code inside ES6 exported function | ||
|  | ||
| --no-force Don't create output if no valid input file is specified. | ||
|  | ||
| --use-preload-cache Stores package in IndexedDB so that subsequent loads don't need to do XHR. Checks package version. | ||
|  | @@ -129,6 +131,7 @@ def __init__(self): | |
| self.use_preload_plugins = False | ||
| self.support_node = True | ||
| self.wasm64 = False | ||
| self.export_es6 = False | ||
|  | ||
|  | ||
| class DataFile: | ||
|  | @@ -362,7 +365,7 @@ def main(): # noqa: C901, PLR0912, PLR0915 | |
| To revalidate these numbers, run `ruff check --select=C901,PLR091`. | ||
| """ | ||
| if len(sys.argv) == 1: | ||
| err('''Usage: file_packager TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--indexedDB-name=EM_PRELOAD_CACHE] [--separate-metadata] [--lz4] [--use-preload-plugins] [--no-node] [--help] | ||
| err('''Usage: file_packager TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--indexedDB-name=EM_PRELOAD_CACHE] [--separate-metadata] [--lz4] [--use-preload-plugins] [--no-node] [--export-es6] [--help] | ||
| Try 'file_packager --help' for more details.''') | ||
| return 1 | ||
|  | ||
|  | @@ -391,6 +394,9 @@ def main(): # noqa: C901, PLR0912, PLR0915 | |
| elif arg == '--no-force': | ||
| options.force = False | ||
| leading = '' | ||
| elif arg == '--export-es6': | ||
| options.export_es6 = True | ||
| leading = '' | ||
| elif arg == '--use-preload-cache': | ||
| options.use_preload_cache = True | ||
| leading = '' | ||
|  | @@ -485,6 +491,11 @@ def main(): # noqa: C901, PLR0912, PLR0915 | |
| diagnostics.error('TARGET should not be the same value of --js-output') | ||
| return 1 | ||
|  | ||
| if options.from_emcc and options.export_es6: | ||
| diagnostics.error('Can\'t use --export-es6 option together with --from-emcc since the code should be embedded ' | ||
| 'within emcc\'s code') | ||
| return 1 | ||
|  | ||
| walked.append(__file__) | ||
| for file_ in data_files: | ||
| if not should_ignore(file_.srcpath): | ||
|  | @@ -621,20 +632,38 @@ def generate_js(data_target, data_files, metadata): | |
| if options.from_emcc: | ||
| ret = '' | ||
| else: | ||
| ret = ''' | ||
| if options.export_es6: | ||
| ret = 'export default async function loadDataFile(Module) {\n' | ||
| else: | ||
| ret = ''' | ||
| var Module = typeof %(EXPORT_NAME)s != 'undefined' ? %(EXPORT_NAME)s : {};\n''' % {"EXPORT_NAME": options.export_name} | ||
|  | ||
| ret += ''' | ||
| Module['expectedDataFileDownloads'] ??= 0; | ||
| Module['expectedDataFileDownloads']++; | ||
| (() => { | ||
| Module['expectedDataFileDownloads']++;''' | ||
|  | ||
| if not options.export_es6: | ||
| ret += ''' | ||
| (() => {''' | ||
|  | ||
| ret += ''' | ||
| // Do not attempt to redownload the virtual filesystem data when in a pthread or a Wasm Worker context. | ||
| var isPthread = typeof ENVIRONMENT_IS_PTHREAD != 'undefined' && ENVIRONMENT_IS_PTHREAD; | ||
| var isWasmWorker = typeof ENVIRONMENT_IS_WASM_WORKER != 'undefined' && ENVIRONMENT_IS_WASM_WORKER; | ||
| if (isPthread || isWasmWorker) return;\n''' | ||
|  | ||
| if options.support_node: | ||
| ret += " var isNode = typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string';\n" | ||
|  | ||
| if options.support_node and options.export_es6: | ||
| ret += '''if (isNode) { | ||
| const { createRequire } = await import('module'); | ||
| /** @suppress{duplicate} */ | ||
| var require = createRequire(import.meta.url); | ||
| }\n''' | ||
|  | ||
| if options.export_es6: | ||
| ret += 'return new Promise((loadDataResolve, loadDataReject) => {\n' | ||
| ret += ' async function loadPackage(metadata) {\n' | ||
|  | ||
| code = ''' | ||
|  | @@ -688,6 +717,10 @@ def generate_js(data_target, data_files, metadata): | |
| Module['FS_createDataFile'](this.name, null, byteArray, true, true, true); | ||
| Module['removeRunDependency'](`fp ${that.name}`);''' | ||
|  | ||
| finish_handler = create_preloaded if options.use_preload_plugins else create_data | ||
| if options.export_es6: | ||
| finish_handler += '\nloadDataResolve();' | ||
|  | ||
| if not options.lz4: | ||
| # Data requests - for getting a block of data out of the big archive - have | ||
| # a similar API to XHRs | ||
|  | @@ -720,11 +753,18 @@ def generate_js(data_target, data_files, metadata): | |
| var files = metadata['files']; | ||
| for (var i = 0; i < files.length; ++i) { | ||
| new DataRequest(files[i]['start'], files[i]['end'], files[i]['audio'] || 0).open('GET', files[i]['filename']); | ||
| }\n''' % (create_preloaded if options.use_preload_plugins else create_data) | ||
| }\n''' % finish_handler | ||
|  | ||
| if options.has_embedded and not options.obj_output: | ||
| diagnostics.warn('--obj-output is recommended when using --embed. This outputs an object file for linking directly into your application is more efficient than JS encoding') | ||
|  | ||
| catch_handler = '' | ||
| if options.export_es6: | ||
| catch_handler += ''' | ||
| .catch((error) => { | ||
| loadDataReject(error); | ||
| })''' | ||
|  | ||
| for counter, file_ in enumerate(data_files): | ||
| filename = file_.dstpath | ||
| dirname = os.path.dirname(filename) | ||
|  | @@ -1049,10 +1089,10 @@ def generate_js(data_target, data_files, metadata): | |
| } | ||
| } | ||
| } catch(e) { | ||
| await preloadFallback(e); | ||
| await preloadFallback(e)%s; | ||
| } | ||
|  | ||
| Module['setStatus']?.('Downloading...');\n''' | ||
| Module['setStatus']?.('Downloading...');\n''' % catch_handler | ||
| else: | ||
| # Not using preload cache, so we might as well start the xhr ASAP, | ||
| # potentially before JS parsing of the main codebase if it's after us. | ||
|  | @@ -1065,15 +1105,16 @@ def generate_js(data_target, data_files, metadata): | |
| if (!fetched) { | ||
| // Note that we don't use await here because we want to execute the | ||
| // the rest of this function immediately. | ||
| fetchRemotePackage(REMOTE_PACKAGE_NAME, REMOTE_PACKAGE_SIZE).then((data) => { | ||
| if (fetchedCallback) { | ||
| fetchedCallback(data); | ||
| fetchedCallback = null; | ||
| } else { | ||
| fetched = data; | ||
| } | ||
| }) | ||
| }\n''' | ||
| fetchRemotePackage(REMOTE_PACKAGE_NAME, REMOTE_PACKAGE_SIZE) | ||
| .then((data) => { | ||
| if (fetchedCallback) { | ||
| fetchedCallback(data); | ||
| fetchedCallback = null; | ||
| } else { | ||
| fetched = data; | ||
| } | ||
| })%s; | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like there is no current error handling here, for this fetch. It looks like the code is trying to do a pre-fetch and maybe we can just ignore failures (like we currently do) for the pre-fetch? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In that case this block could be reverted There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Huh, without this  Fine: 
 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But isn't that just the existing behaviour? There is not catch mechanism for this particular fetch and so errors here will always go the to the global error handlers. I think that is the current/expected behaviour. We could change that perhaps? Given that there is not really any way to recover from such errors why not just let the global error handler handle these errors? Or the global unhandledRejection handler. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I meant that when the throw is two or more level deep I cant catch the error even with top level try catch block which is an issue - most likely es6 mode will be used together with some frontend and we don't want to crash whole JS but rather give some information and maybe possibility to retry There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And inside webworker we can't access window context to get access to  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can handle unhandled rejections using the worker global scope: But I understand that it would be nice to correctly reject the promise here, so this part lgtm. We can refactor later perhaps. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure | ||
| }\n''' % catch_handler | ||
|  | ||
| code += ''' | ||
| Module['preloadResults'][PACKAGE_NAME] = {fromCache: false}; | ||
|  | @@ -1090,10 +1131,10 @@ def generate_js(data_target, data_files, metadata): | |
| ret += ''' | ||
| } | ||
| if (Module['calledRun']) { | ||
| runWithFS(Module); | ||
| runWithFS(Module)%s; | ||
| } else { | ||
| (Module['preRun'] ??= []).push(runWithFS); // FS is not initialized yet, wait for it | ||
| }\n''' | ||
| }\n''' % catch_handler | ||
|  | ||
| if options.separate_metadata: | ||
| node_support_code = '' | ||
|  | @@ -1131,7 +1172,14 @@ def generate_js(data_target, data_files, metadata): | |
| } | ||
| loadPackage(%s);\n''' % json.dumps(metadata) | ||
|  | ||
| ret += ''' | ||
| if options.export_es6: | ||
| ret += ''' | ||
|         
                  lkwinta marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| }); | ||
| } | ||
| // END the loadDataFile function | ||
| ''' | ||
| else: | ||
| ret += ''' | ||
| })();\n''' | ||
|  | ||
| return ret | ||
|  | ||
Uh oh!
There was an error while loading. Please reload this page.