From 37e84fa487f033ca81cd3bdd9367535550fc21f4 Mon Sep 17 00:00:00 2001 From: Severin Ibarluzea Date: Mon, 17 Nov 2025 19:33:49 -0800 Subject: [PATCH] Handle export type star reexports --- .../export-type-star-reexports.test.tsx | 38 +++++++++++++++++++ webworker/transform-with-sucrase.ts | 8 +++- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/features/export-type-star-reexports.test.tsx diff --git a/tests/features/export-type-star-reexports.test.tsx b/tests/features/export-type-star-reexports.test.tsx new file mode 100644 index 00000000..8fa3a043 --- /dev/null +++ b/tests/features/export-type-star-reexports.test.tsx @@ -0,0 +1,38 @@ +import { expect, test } from "bun:test" +import { createCircuitWebWorker } from "lib" + +const workerUrl = new URL("../../webworker/entrypoint.ts", import.meta.url) + +test("export type * re-exports do not break evaluation", async () => { + const worker = await createCircuitWebWorker({ + webWorkerUrl: workerUrl, + }) + + const execution = worker.executeWithFsMap({ + fsMap: { + "entrypoint.tsx": ` + import { ComponentA } from "./ComponentA" + + circuit.add() + `, + "ComponentA.tsx": ` + import type { ComponentProps } from "./component-types" + + export type * from "./component-types" + + export const ComponentA = ({ message }: ComponentProps) => { + void message + return null + } + `, + "component-types.ts": ` + export type ComponentProps = { message: string } + `, + }, + entrypoint: "entrypoint.tsx", + }) + + await expect(execution).resolves.toBeUndefined() + + await worker.kill() +}) diff --git a/webworker/transform-with-sucrase.ts b/webworker/transform-with-sucrase.ts index 934e9528..0ab7dfcb 100644 --- a/webworker/transform-with-sucrase.ts +++ b/webworker/transform-with-sucrase.ts @@ -2,6 +2,11 @@ import { transform, type Transform as SucraseTransform } from "sucrase" const TS_EXTENSIONS = new Set([".ts", ".tsx", ".mts", ".cts"]) const JSX_EXTENSIONS = new Set([".tsx", ".jsx", ".ts"]) +const TYPE_STAR_EXPORT_REGEX = + /^\s*export\s+type\s+\*\s+(?:as\s+[\w$]+\s+)?from\s+['"][^'"]+['"]\s*;?\s*$/gim + +const stripTypeStarExports = (code: string) => + code.replace(TYPE_STAR_EXPORT_REGEX, "") const stripQueryAndHash = (filePath: string) => { const queryIndex = filePath.indexOf("?") @@ -58,7 +63,8 @@ const getTransformsForFilePath = (filePath: string) => { export const transformWithSucrase = (code: string, filePath: string) => { const transforms = getTransformsForFilePath(filePath) - const { code: transformedCode } = transform(code, { + const sanitizedCode = stripTypeStarExports(code) + const { code: transformedCode } = transform(sanitizedCode, { filePath, production: true, transforms,