From 452335a78d98848f4d5602a2274cddd4659af346 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 11 Aug 2026 21:56:44 -0400 Subject: [PATCH] fix(tui): truncate fractional mtimes in fresh plugin specifiers (#41891) --- packages/tui/src/plugin/discovery.ts | 8 ++++++-- packages/tui/test/plugin-discovery.test.ts | 11 ++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/tui/src/plugin/discovery.ts b/packages/tui/src/plugin/discovery.ts index 1b1c2fc9fb4..45f5497074e 100644 --- a/packages/tui/src/plugin/discovery.ts +++ b/packages/tui/src/plugin/discovery.ts @@ -49,7 +49,11 @@ export function localSource(spec: string, directory: string) { // of hitting the ESM cache. Bun ignores query params when caching file:// URL // imports, so bust with a plain path there; Node keys its cache on the full // URL. Mirrors the core plugin supervisor's loader. +// The mtime is truncated to whole milliseconds: a fractional mtimeMs puts a +// dot in the query, and Bun's compiled binaries then skip runtime plugin +// hooks for the import, breaking JSX/solid rewriting for external plugins. export function freshSpecifier(entrypoint: string, mtime: number) { - if (typeof Bun !== "undefined") return `${fileURLToPath(entrypoint).replaceAll("\\", "/")}?mtime=${mtime}` - return `${entrypoint}?mtime=${mtime}` + const version = Math.trunc(mtime) + if (typeof Bun !== "undefined") return `${fileURLToPath(entrypoint).replaceAll("\\", "/")}?mtime=${version}` + return `${entrypoint}?mtime=${version}` } diff --git a/packages/tui/test/plugin-discovery.test.ts b/packages/tui/test/plugin-discovery.test.ts index ac52b3ccf23..4a8b60c74bb 100644 --- a/packages/tui/test/plugin-discovery.test.ts +++ b/packages/tui/test/plugin-discovery.test.ts @@ -1,7 +1,8 @@ import { mkdir, writeFile } from "node:fs/promises" import path from "node:path" +import { pathToFileURL } from "node:url" import { expect, test } from "bun:test" -import { discoverTuiPlugins, tuiPluginDirectories } from "../src/plugin/discovery" +import { discoverTuiPlugins, freshSpecifier, tuiPluginDirectories } from "../src/plugin/discovery" import { localProjectDirectory } from "../src/util/config-directories" import { tmpdir } from "./fixture/fixture" @@ -67,6 +68,14 @@ test("uses an Hg root for a missing project plugin directory", async () => { ) }) +test("truncates fractional mtimes in fresh specifiers", () => { + // A dot in the query makes Bun's compiled binaries skip runtime plugin + // hooks for the import, breaking JSX/solid rewriting for external plugins. + const entrypoint = pathToFileURL(path.resolve("example.tsx")).href + const specifier = freshSpecifier(entrypoint, 1786494961337.0317) + expect(specifier.endsWith("example.tsx?mtime=1786494961337")).toBe(true) +}) + test("propagates non-missing filesystem errors", async () => { await expect(localProjectDirectory("\0")).rejects.toBeInstanceOf(Error) await expect(discoverTuiPlugins(["\0"])).rejects.toBeInstanceOf(Error)