fix(bundle): make the "dynamic require" hack work on minified output (#29997)
Some checks are pending
ci / pre-build (push) Waiting to run
ci / test debug linux-aarch64 (push) Blocked by required conditions
ci / test release linux-aarch64 (push) Blocked by required conditions
ci / test debug macos-aarch64 (push) Blocked by required conditions
ci / test release macos-aarch64 (push) Blocked by required conditions
ci / bench release linux-x86_64 (push) Blocked by required conditions
ci / lint debug linux-x86_64 (push) Blocked by required conditions
ci / lint debug macos-x86_64 (push) Blocked by required conditions
ci / lint debug windows-x86_64 (push) Blocked by required conditions
ci / test debug linux-x86_64 (push) Blocked by required conditions
ci / test release linux-x86_64 (push) Blocked by required conditions
ci / test debug macos-x86_64 (push) Blocked by required conditions
ci / test release macos-x86_64 (push) Blocked by required conditions
ci / test debug windows-x86_64 (push) Blocked by required conditions
ci / test release windows-x86_64 (push) Blocked by required conditions
ci / build libs (push) Blocked by required conditions
ci / publish canary (push) Blocked by required conditions

It's terrible and I hate it but it works
This commit is contained in:
Nathan Whitaker 2025-07-03 19:11:09 -07:00 committed by GitHub
parent b7cb8a7d97
commit 51c43ce8b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 31 additions and 6 deletions

View file

@ -154,14 +154,15 @@ pub async fn bundle(
let response = bundler.build().await?;
if bundle_flags.watch {
return bundle_watch(flags, bundler).await;
return bundle_watch(flags, bundler, bundle_flags.minify).await;
}
handle_esbuild_errors_and_warnings(&response, &init_cwd);
if response.errors.is_empty() {
let metafile = metafile_from_response(&response)?;
let output_infos = process_result(&response, &init_cwd, *DISABLE_HACK)?;
let output_infos =
process_result(&response, &init_cwd, *DISABLE_HACK, bundle_flags.minify)?;
if bundle_flags.output_dir.is_some() || bundle_flags.output_path.is_some() {
print_finished_message(&metafile, &output_infos, start.elapsed())?;
@ -188,6 +189,7 @@ fn metafile_from_response(
async fn bundle_watch(
flags: Arc<Flags>,
bundler: EsbuildBundler,
minified: bool,
) -> Result<(), AnyError> {
let initial_roots = bundler
.roots
@ -226,7 +228,7 @@ async fn bundle_watch(
if response.errors.is_empty() {
let metafile = metafile_from_response(&response)?;
let output_infos =
process_result(&response, &bundler.cwd, *DISABLE_HACK)?;
process_result(&response, &bundler.cwd, *DISABLE_HACK, minified)?;
print_finished_message(&metafile, &output_infos, start.elapsed())?;
let new_watched = get_input_paths_for_watch(&response);
@ -362,8 +364,17 @@ impl EsbuildBundler {
// TODO(nathanwhit): MASSIVE HACK
// See tests::specs::bundle::requires_node_builtin for why this is needed.
// Without this hack, that test would fail with "Dynamic require of "util" is not supported"
fn replace_require_shim(contents: &str) -> String {
contents.replace(
fn replace_require_shim(contents: &str, minified: bool) -> String {
if minified {
let re = lazy_regex::regex!(
r#"var (\w+)\s*=\((\w+)\s*=>typeof require<"u"\?require:typeof Proxy<"u"\?new Proxy\((\w+)\,\{get:\(\w+,\w+\)=>\(typeof require<"u"\?require:\w+\)\[l\]\}\):(\w+)\)\(function\(\w+\)\{if\(typeof require<"u"\)return require\.apply\(this\,arguments\);throw Error\('Dynamic require of "'\+\w+\+'" is not supported'\)\}\);"#
);
re.replace(contents, |c: &regex::Captures<'_>| {
let var_name = c.get(1).unwrap().as_str();
format!("import{{createRequire}} from \"node:module\";var {var_name}=createRequire(import.meta.url);")
}).into_owned()
} else {
contents.replace(
r#"var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
@ -374,6 +385,7 @@ fn replace_require_shim(contents: &str) -> String {
var __require = createRequire(import.meta.url);
"#,
)
}
}
fn format_message(
@ -1171,6 +1183,7 @@ fn process_result(
response: &BuildResponse,
cwd: &Path,
should_replace_require_shim: bool,
minified: bool,
) -> Result<Vec<OutputFileInfo>, AnyError> {
let mut exists_cache = std::collections::HashSet::new();
let output_files = response
@ -1187,7 +1200,7 @@ fn process_result(
let bytes = if is_js || file.path.ends_with("<stdout>") {
let string = String::from_utf8(file.contents.clone())?;
let string = if should_replace_require_shim {
replace_require_shim(&string)
replace_require_shim(&string, minified)
} else {
string
};

View file

@ -81,6 +81,18 @@
}
]
},
"requires_node_builtin_minified": {
"steps": [
{
"args": "bundle --output=out.js --minify uses_node_builtin.cjs",
"output": "requires_node_builtin.out"
},
{
"args": "run --no-lock --cached-only --no-config -A out.js",
"output": "{ a: 1, b: 'hello' }\n"
}
]
},
"json_import": {
"steps": [
{