diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 48da391232..615d9486de 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -295,6 +295,7 @@ extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(c JSC::Options::useJITCage() = false; JSC::Options::useShadowRealm() = true; JSC::Options::useV8DateParser() = true; + JSC::Options::useMathSumPreciseMethod() = true; JSC::Options::evalMode() = evalMode; JSC::Options::heapGrowthSteepnessFactor() = 1.0; JSC::Options::heapGrowthMaxIncrease() = 2.0; diff --git a/src/bun.js/node/node_fs_stat_watcher.zig b/src/bun.js/node/node_fs_stat_watcher.zig index a58cb8aacf..8fcb3fe33a 100644 --- a/src/bun.js/node/node_fs_stat_watcher.zig +++ b/src/bun.js/node/node_fs_stat_watcher.zig @@ -430,7 +430,17 @@ pub const StatWatcher = struct { .err => std.mem.zeroes(bun.Stat), }; - if (std.mem.eql(u8, std.mem.asBytes(&res), std.mem.asBytes(&this.last_stat))) return; + var compare = res; + const StatT = @TypeOf(compare); + if (@hasField(StatT, "st_atim")) { + compare.st_atim = this.last_stat.st_atim; + } else if (@hasField(StatT, "st_atimespec")) { + compare.st_atimespec = this.last_stat.st_atimespec; + } else if (@hasField(StatT, "atim")) { + compare.atim = this.last_stat.atim; + } + + if (std.mem.eql(u8, std.mem.asBytes(&compare), std.mem.asBytes(&this.last_stat))) return; this.last_stat = res; this.enqueueTaskConcurrent(JSC.ConcurrentTask.fromCallback(this, swapAndCallListenerOnMainThread)); diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index b38b65b9c6..6766ad5552 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -206,7 +206,7 @@ pub const BundleV2 = struct { try client_transpiler.configureDefines(); client_transpiler.resolver.opts = client_transpiler.options; - + client_transpiler.resolver.env_loader = client_transpiler.env; this.client_transpiler = client_transpiler; return client_transpiler; } @@ -1752,7 +1752,7 @@ pub const BundleV2 = struct { if (!transpiler.options.production) { try transpiler.options.conditions.appendSlice(&.{"development"}); } - + transpiler.resolver.env_loader = transpiler.env; transpiler.resolver.opts = transpiler.options; } diff --git a/src/cli/build_command.zig b/src/cli/build_command.zig index 2a42e8898b..4258efc82d 100644 --- a/src/cli/build_command.zig +++ b/src/cli/build_command.zig @@ -225,6 +225,7 @@ pub const BuildCommand = struct { } this_transpiler.resolver.opts = this_transpiler.options; + this_transpiler.resolver.env_loader = this_transpiler.env; this_transpiler.options.jsx.development = !this_transpiler.options.production; this_transpiler.resolver.opts.jsx.development = this_transpiler.options.jsx.development; @@ -268,7 +269,9 @@ pub const BuildCommand = struct { try bun.bake.addImportMetaDefines(allocator, client_transpiler.options.define, .development, .client); this_transpiler.resolver.opts = this_transpiler.options; + this_transpiler.resolver.env_loader = this_transpiler.env; client_transpiler.resolver.opts = client_transpiler.options; + client_transpiler.resolver.env_loader = client_transpiler.env; } // var env_loader = this_transpiler.env; diff --git a/test/bundler/bun-build-api.test.ts b/test/bundler/bun-build-api.test.ts index 1292c07e59..b0ab58115f 100644 --- a/test/bundler/bun-build-api.test.ts +++ b/test/bundler/bun-build-api.test.ts @@ -663,3 +663,67 @@ export function testMacro(val: any) { `var t={borderRadius:{"1":"4px","2":"8px"}};export{t as testConfig};\n`, ); }); + +// Since NODE_PATH has to be set, we need to run this test outside the bundler tests. +test("regression/NODE_PATHBuild api", async () => { + const dir = tempDirWithFiles("node-path-build", { + "entry.js": ` + import MyClass from 'MyClass'; + console.log(new MyClass().constructor.name); + `, + "src/MyClass.js": ` + export default class MyClass {} + `, + "build.js": ` + import { join } from "path"; + + const build = await Bun.build({ + entrypoints: [join(import.meta.dir, "entry.js")], + outdir: join(import.meta.dir, "out"), + }); + + if (!build.success) { + console.error("Build failed:", build.logs); + process.exit(1); + } + + // Run the built file + const runProc = Bun.spawn({ + cmd: [process.argv[0], join(import.meta.dir, "out", "entry.js")], + stdout: "pipe", + stderr: "pipe", + }); + + await runProc.exited; + const runOutput = await new Response(runProc.stdout).text(); + const runError = await new Response(runProc.stderr).text(); + + if (runError) { + console.error("Run error:", runError); + process.exit(1); + } + + console.log(runOutput.trim()); + + `, + }); + + // Run the build script with NODE_PATH set + const proc = Bun.spawn({ + cmd: [bunExe(), join(dir, "build.js")], + env: { + ...bunEnv, + NODE_PATH: join(dir, "src"), + }, + stdout: "pipe", + stderr: "pipe", + cwd: dir, + }); + + await proc.exited; + const output = await new Response(proc.stdout).text(); + const error = await new Response(proc.stderr).text(); + + expect(error).toBe(""); + expect(output.trim()).toBe("MyClass"); +}); diff --git a/test/bundler/bundler_regressions.test.ts b/test/bundler/bundler_regressions.test.ts index 315515b0d9..e4efcf36f8 100644 --- a/test/bundler/bundler_regressions.test.ts +++ b/test/bundler/bundler_regressions.test.ts @@ -225,6 +225,26 @@ describe("bundler", () => { entryPointsRaw: ["test/entry.ts", "--external", "*"], }); + itBundled(`regression/NODE_PATHBuild cli`, { + files: { + "/entry.js": ` + import MyClass from 'MyClass'; + console.log(new MyClass().constructor.name); + `, + "/src/MyClass.js": ` + export default class MyClass {} + `, + }, + entryPoints: ["/entry.js"], + backend: "cli", + env: { + NODE_PATH: "{{root}}/src", + }, + run: { + stdout: "MyClass", + }, + }); + itBundled("regression/NamespaceTracking#12337", { files: { "/entry.ts": /* ts */ ` diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts index fb9d111679..c4126316e8 100644 --- a/test/bundler/expectBundled.ts +++ b/test/bundler/expectBundled.ts @@ -826,10 +826,13 @@ function expectBundled( } const bundlerEnv = { ...bunEnv, ...env }; - // remove undefined keys instead of passing "undefined" + // remove undefined keys instead of passing "undefined" and resolve {{root}} for (const key in bundlerEnv) { - if (bundlerEnv[key] === undefined) { + const value = bundlerEnv[key]; + if (value === undefined) { delete bundlerEnv[key]; + } else if (typeof value === "string") { + bundlerEnv[key] = value.replaceAll("{{root}}", root); } } diff --git a/test/js/node/watch/fs.watchFile.test.ts b/test/js/node/watch/fs.watchFile.test.ts index ec7166259c..dc84364d92 100644 --- a/test/js/node/watch/fs.watchFile.test.ts +++ b/test/js/node/watch/fs.watchFile.test.ts @@ -1,4 +1,4 @@ -import { tempDirWithFiles } from "harness"; +import { isWindows, tempDirWithFiles } from "harness"; import fs from "node:fs"; import path from "path"; @@ -113,6 +113,18 @@ describe("fs.watchFile", () => { expect(typeof entries[0][0].mtimeMs === "bigint").toBe(true); }); + test.if(isWindows)("does not fire on atime-only update", async () => { + let called = false; + const file = path.join(testDir, "watch.txt"); + fs.watchFile(file, { interval: 50 }, () => { + called = true; + }); + fs.readFileSync(file); + await Bun.sleep(100); + fs.unwatchFile(file); + expect(called).toBe(false); + }); + test("StatWatcherScheduler stress test (1000 watchers with random times)", async () => { const EventEmitter = require("events"); let defaultMaxListeners = EventEmitter.defaultMaxListeners; diff --git a/test/js/web/web-globals.test.js b/test/js/web/web-globals.test.js index 1270c0a810..551578d925 100644 --- a/test/js/web/web-globals.test.js +++ b/test/js/web/web-globals.test.js @@ -30,6 +30,7 @@ test("exists", () => { expect(typeof PerformanceResourceTiming !== "undefined").toBe(true); expect(typeof PerformanceServerTiming !== "undefined").toBe(true); expect(typeof PerformanceTiming !== "undefined").toBe(true); + expect(typeof Math.sumPrecise !== "undefined").toBe(true); }); const globalSetters = [