diff --git a/bun.lock b/bun.lock index f0902813a..3dfb6f902 100644 --- a/bun.lock +++ b/bun.lock @@ -90,6 +90,7 @@ "tscircuit": "0.0.2547-libonly", "tsx": "^4.7.1", "typed-ky": "^0.0.4", + "typescript": "5.9.3", "zod": "^3.23.8", }, "peerDependencies": { diff --git a/cli/build/transpile/index.ts b/cli/build/transpile/index.ts index f4994561d..fdbdfcdcd 100644 --- a/cli/build/transpile/index.ts +++ b/cli/build/transpile/index.ts @@ -1,32 +1,70 @@ import path from "node:path" import fs from "node:fs" -import { rollup } from "rollup" -import typescript from "@rollup/plugin-typescript" -import resolve from "@rollup/plugin-node-resolve" -import commonjs from "@rollup/plugin-commonjs" -import json from "@rollup/plugin-json" -import dts from "rollup-plugin-dts" import kleur from "kleur" -import ts from "typescript" import { createStaticAssetPlugin, STATIC_ASSET_EXTENSIONS, } from "./static-asset-plugin" -const [typescriptMajor, typescriptMinor] = ts.versionMajorMinor - .split(".") - .map(Number) -// TypeScript 5.7+ can emit projects that import .ts/.tsx paths by rewriting -// those specifiers, instead of reporting TS5097 or requiring noEmit. -const supportsRewriteRelativeImportExtensions = - typescriptMajor > 5 || (typescriptMajor === 5 && typescriptMinor >= 7) -const typescriptExtensionEmitOptions = supportsRewriteRelativeImportExtensions - ? { - allowImportingTsExtensions: true, - rewriteRelativeImportExtensions: true, - } - : { allowImportingTsExtensions: false } +// The bundler stack (rollup plugins + the user's typescript install) is only +// needed when transpiling. Loading it lazily keeps `tsci` usable in projects +// whose typescript version cannot even be parsed by @rollup/plugin-typescript +// (e.g. the TypeScript 7 native port, which dropped ts.ModuleKind). +const loadBundlerDeps = async () => { + const ts = (await import("typescript")).default + const [typescriptMajor, typescriptMinor] = ts.versionMajorMinor + .split(".") + .map(Number) + if ( + !Number.isFinite(typescriptMajor) || + typescriptMajor >= 6 || + ts.ModuleKind?.ES2015 === undefined + ) { + throw new Error( + `Installed TypeScript ${ts.version} is not supported by the ` + + `transpiler (@rollup/plugin-typescript requires TypeScript 5.x). ` + + `Pin typescript@^5 in your project, e.g. \`bun add -D typescript@5\`.`, + ) + } + + const [ + { rollup }, + { default: typescript }, + { default: resolve }, + { default: commonjs }, + { default: json }, + { default: dts }, + ] = await Promise.all([ + import("rollup"), + import("@rollup/plugin-typescript"), + import("@rollup/plugin-node-resolve"), + import("@rollup/plugin-commonjs"), + import("@rollup/plugin-json"), + import("rollup-plugin-dts"), + ]) + + // TypeScript 5.7+ can emit projects that import .ts/.tsx paths by rewriting + // those specifiers, instead of reporting TS5097 or requiring noEmit. + const supportsRewriteRelativeImportExtensions = + typescriptMajor > 5 || (typescriptMajor === 5 && typescriptMinor >= 7) + const typescriptExtensionEmitOptions = supportsRewriteRelativeImportExtensions + ? { + allowImportingTsExtensions: true, + rewriteRelativeImportExtensions: true, + } + : { allowImportingTsExtensions: false } + + return { + rollup, + typescript, + resolve, + commonjs, + json, + dts, + typescriptExtensionEmitOptions, + } +} const createExternalFunction = (projectDir: string, tsconfigPath?: string) => @@ -102,6 +140,16 @@ export const transpileFile = async ({ try { fs.mkdirSync(outputDir, { recursive: true }) + const { + rollup, + typescript, + resolve, + commonjs, + json, + dts, + typescriptExtensionEmitOptions, + } = await loadBundlerDeps() + // Check if user has a tsconfig.json const tsconfigPath = path.join(projectDir, "tsconfig.json") const hasTsConfig = fs.existsSync(tsconfigPath) diff --git a/package.json b/package.json index 35055685c..c2b39584c 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "tscircuit": "0.0.2547-libonly", "tsx": "^4.7.1", "typed-ky": "^0.0.4", + "typescript": "5.9.3", "zod": "^3.23.8" }, "peerDependencies": {