From acb079919fcaf77db1de2e91c69a3f71b3155b39 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Tue, 21 Jul 2026 20:42:04 -0400 Subject: [PATCH 1/3] Modernize Babel build tooling and share preset config - Convert @alloy-js/babel-preset to TypeScript with typed exports and a new alloyBabelPresets() helper for the shared preset chain - Give @alloy-js/rollup-plugin a typed options API (addSourceInfo, sourceMaps) and auto-configure esbuild (jsx: preserve + source conditions) - Consume the shared helper from @alloy-js/cli and drop duplicated config - Remove now-redundant esbuild/resolve boilerplate from vitest configs --- .../feature-babel-preset-types-2026-7-21.md | 7 ++ .../feature-rollup-plugin-api-2026-7-21.md | 7 ++ .../internal-share-babel-config-2026-7-21.md | 17 ++++ packages/babel-preset-alloy/index.js | 38 -------- packages/babel-preset-alloy/package.json | 48 ++++++++-- packages/babel-preset-alloy/src/index.ts | 96 +++++++++++++++++++ .../src/vendored-plugins.d.ts | 7 ++ .../babel-preset-alloy/tsconfig.build.json | 10 ++ packages/babel-preset-alloy/tsconfig.json | 9 ++ packages/cli/package.json | 1 - packages/cli/src/babel.ts | 10 +- packages/core/test/babel-e2e.test.ts | 1 - packages/core/test/output-e2e.test.ts | 1 - packages/core/vitest.config.ts | 12 --- packages/csharp/vitest.config.ts | 12 --- packages/go/vitest.config.ts | 12 --- packages/java/vitest.config.ts | 12 --- packages/json/vitest.config.ts | 12 --- packages/markdown/vitest.config.ts | 12 --- packages/msbuild/vitest.config.ts | 12 --- packages/python/vitest.config.ts | 12 --- packages/rollup-plugin/package.json | 4 +- packages/rollup-plugin/src/index.ts | 51 +++++++--- packages/typescript/vitest.config.ts | 12 --- packages/typespec/vitest.config.ts | 12 --- pnpm-lock.yaml | 37 ++++--- samples/basic-project/vitest.config.ts | 4 - samples/client-emitter/vitest.config.ts | 4 - samples/go-example/vitest.config.ts | 4 - samples/python-example/vitest.config.ts | 4 - samples/scaffold-generator/vitest.config.ts | 4 - test/performance/vitest.config.ts | 4 - 32 files changed, 262 insertions(+), 226 deletions(-) create mode 100644 .chronus/changes/feature-babel-preset-types-2026-7-21.md create mode 100644 .chronus/changes/feature-rollup-plugin-api-2026-7-21.md create mode 100644 .chronus/changes/internal-share-babel-config-2026-7-21.md delete mode 100644 packages/babel-preset-alloy/index.js create mode 100644 packages/babel-preset-alloy/src/index.ts create mode 100644 packages/babel-preset-alloy/src/vendored-plugins.d.ts create mode 100644 packages/babel-preset-alloy/tsconfig.build.json create mode 100644 packages/babel-preset-alloy/tsconfig.json diff --git a/.chronus/changes/feature-babel-preset-types-2026-7-21.md b/.chronus/changes/feature-babel-preset-types-2026-7-21.md new file mode 100644 index 000000000..99b5dee78 --- /dev/null +++ b/.chronus/changes/feature-babel-preset-types-2026-7-21.md @@ -0,0 +1,7 @@ +--- +changeKind: feature +packages: + - "@alloy-js/babel-preset" +--- + +`@alloy-js/babel-preset` now ships with TypeScript type declarations and exports an `alloyBabelPresets()` helper providing the canonical `@babel/preset-typescript` + Alloy preset chain shared by the CLI and the Rollup/Vite plugin. diff --git a/.chronus/changes/feature-rollup-plugin-api-2026-7-21.md b/.chronus/changes/feature-rollup-plugin-api-2026-7-21.md new file mode 100644 index 000000000..7bde2b02f --- /dev/null +++ b/.chronus/changes/feature-rollup-plugin-api-2026-7-21.md @@ -0,0 +1,7 @@ +--- +changeKind: feature +packages: + - "@alloy-js/rollup-plugin" +--- + +The Rollup/Vite plugin now accepts `addSourceInfo` and `sourceMaps` options and automatically configures esbuild for Alloy (`jsx: "preserve"` plus the `source` resolve conditions). Consumers no longer need to set these by hand in their Vite/Vitest config. diff --git a/.chronus/changes/internal-share-babel-config-2026-7-21.md b/.chronus/changes/internal-share-babel-config-2026-7-21.md new file mode 100644 index 000000000..41d163881 --- /dev/null +++ b/.chronus/changes/internal-share-babel-config-2026-7-21.md @@ -0,0 +1,17 @@ +--- +changeKind: internal +packages: + - "@alloy-js/cli" + - "@alloy-js/core" + - "@alloy-js/csharp" + - "@alloy-js/go" + - "@alloy-js/java" + - "@alloy-js/json" + - "@alloy-js/markdown" + - "@alloy-js/msbuild" + - "@alloy-js/python" + - "@alloy-js/typescript" + - "@alloy-js/typespec" +--- + +Share the Babel preset configuration through `@alloy-js/babel-preset` and rely on the Rollup/Vite plugin to configure esbuild, removing the duplicated esbuild/resolve boilerplate from each package's `vitest.config.ts`. diff --git a/packages/babel-preset-alloy/index.js b/packages/babel-preset-alloy/index.js deleted file mode 100644 index a61d7cfba..000000000 --- a/packages/babel-preset-alloy/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import alloyTransform from "@alloy-js/babel-plugin"; -import jsxTransform from "@alloy-js/babel-plugin-jsx-dom-expressions"; - -export default function (context, options = {}) { - const envMode = process.env.BABEL_ENV ?? process.env.NODE_ENV; - const inferredDev = envMode === undefined ? true : envMode !== "production"; - const defaultOptions = { - alloyModuleName: "@alloy-js/core", - moduleName: "@alloy-js/core/jsx-runtime", - generate: "universal", - wrapConditionals: true, - preserveWhitespace: true, - }; - - const jsxOptions = Object.assign({}, defaultOptions, options); - if (options.addSourceInfo === undefined) { - if (options.dev !== undefined) { - jsxOptions.addSourceInfo = options.dev; - } else { - jsxOptions.addSourceInfo = inferredDev; - } - } - - const plugins = [ - [ - alloyTransform, - { - alloyModuleName: - options.alloyModuleName ?? defaultOptions.alloyModuleName, - legacyWhitespace: - options.legacyWhitespace ?? defaultOptions.legacyWhitespace, - }, - ], - [jsxTransform, jsxOptions], - ]; - - return { plugins }; -} diff --git a/packages/babel-preset-alloy/package.json b/packages/babel-preset-alloy/package.json index acb60938e..a4bb4d444 100644 --- a/packages/babel-preset-alloy/package.json +++ b/packages/babel-preset-alloy/package.json @@ -1,21 +1,53 @@ { "name": "@alloy-js/babel-preset", "version": "0.3.0", - "description": "", + "type": "module", + "description": "Babel preset for Alloy's JSX/TSX transformation", + "homepage": "https://github.com/alloy-framework/alloy", + "license": "MIT", + "author": "Microsoft", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "source": "./src/index.ts", + "default": "./dist/index.js" + } + }, + "publishConfig": { + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } + }, + "files": [ + "dist" + ], "repository": { "type": "git", "url": "git+https://github.com/alloy-framework/alloy.git" }, - "main": "index.js", + "engines": { + "node": ">=22.0.0" + }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "watch": "tsc -p ./tsconfig.build.json --watch", + "build": "tsc -p ./tsconfig.build.json", + "clean": "rimraf dist/ .temp/" }, "dependencies": { "@alloy-js/babel-plugin": "workspace:~", - "@alloy-js/babel-plugin-jsx-dom-expressions": "workspace:~" + "@alloy-js/babel-plugin-jsx-dom-expressions": "workspace:~", + "@babel/preset-typescript": "catalog:" }, - "keywords": [], - "author": "", - "license": "MIT", - "type": "module" + "devDependencies": { + "@babel/core": "catalog:", + "@types/node": "catalog:", + "rimraf": "catalog:", + "typescript": "catalog:" + }, + "bugs": "https://github.com/alloy-framework/alloy/issues" } diff --git a/packages/babel-preset-alloy/src/index.ts b/packages/babel-preset-alloy/src/index.ts new file mode 100644 index 000000000..4b94a8927 --- /dev/null +++ b/packages/babel-preset-alloy/src/index.ts @@ -0,0 +1,96 @@ +import alloyTransform from "@alloy-js/babel-plugin"; +import jsxTransform from "@alloy-js/babel-plugin-jsx-dom-expressions"; +import type { PluginItem, PresetItem } from "@babel/core"; +import typescriptPreset from "@babel/preset-typescript"; + +/** Options accepted by the Alloy Babel preset. */ +export interface AlloyPresetOptions { + /** Module name providing the Alloy runtime. @default "@alloy-js/core" */ + alloyModuleName?: string; + /** Module name providing the JSX runtime. @default "@alloy-js/core/jsx-runtime" */ + moduleName?: string; + /** Code generation mode passed to the JSX transform. @default "universal" */ + generate?: string; + /** Whether to wrap conditional expressions. @default true */ + wrapConditionals?: boolean; + /** Preserve JSX whitespace. @default true */ + preserveWhitespace?: boolean; + /** Opt into the legacy whitespace handling (not recommended). */ + legacyWhitespace?: boolean; + /** + * Emit source location information for components. When omitted, this is + * inferred from `dev` or from `BABEL_ENV`/`NODE_ENV`. + */ + addSourceInfo?: boolean; + /** Whether this is a development build. Used to infer `addSourceInfo`. */ + dev?: boolean; +} + +/** + * Alloy Babel preset. Transforms Alloy's JSX syntax into calls against the + * Alloy runtime. + */ +export default function alloyPreset( + _context: unknown, + options: AlloyPresetOptions = {}, +): { plugins: PluginItem[] } { + const envMode = process.env.BABEL_ENV ?? process.env.NODE_ENV; + const inferredDev = envMode === undefined ? true : envMode !== "production"; + const defaultOptions = { + alloyModuleName: "@alloy-js/core", + moduleName: "@alloy-js/core/jsx-runtime", + generate: "universal", + wrapConditionals: true, + preserveWhitespace: true, + }; + + const jsxOptions = { + ...defaultOptions, + ...options, + } as Record; + if (options.addSourceInfo === undefined) { + if (options.dev !== undefined) { + jsxOptions.addSourceInfo = options.dev; + } else { + jsxOptions.addSourceInfo = inferredDev; + } + } + + // `@babel/core`'s `PluginItem` type does not model the Alloy plugins' custom + // option objects, so assert comparability here. + const plugins = [ + [ + alloyTransform, + { + alloyModuleName: + options.alloyModuleName ?? defaultOptions.alloyModuleName, + legacyWhitespace: options.legacyWhitespace, + }, + ], + [jsxTransform, jsxOptions], + ] as PluginItem[]; + + return { plugins }; +} + +/** Options for {@link alloyBabelPresets}. */ +export interface AlloyBabelPresetsOptions { + /** Forwarded to the Alloy preset to emit component source locations. */ + addSourceInfo?: boolean; +} + +/** + * The canonical Babel preset chain used to compile Alloy `.ts`/`.tsx` sources: + * `@babel/preset-typescript` followed by the Alloy preset. + * + * Shared by `@alloy-js/cli` and `@alloy-js/rollup-plugin` so the configuration + * stays in a single place. + */ +export function alloyBabelPresets( + options: AlloyBabelPresetsOptions = {}, +): PresetItem[] { + return [ + typescriptPreset, + [alloyPreset, { addSourceInfo: options.addSourceInfo }], + ] as PresetItem[]; +} diff --git a/packages/babel-preset-alloy/src/vendored-plugins.d.ts b/packages/babel-preset-alloy/src/vendored-plugins.d.ts new file mode 100644 index 000000000..f82a5d3c5 --- /dev/null +++ b/packages/babel-preset-alloy/src/vendored-plugins.d.ts @@ -0,0 +1,7 @@ +// `@alloy-js/babel-plugin-jsx-dom-expressions` ships as plain JS without type +// declarations, so provide a minimal ambient declaration for it here. +declare module "@alloy-js/babel-plugin-jsx-dom-expressions" { + import type { PluginObj } from "@babel/core"; + const plugin: (...args: unknown[]) => PluginObj; + export default plugin; +} diff --git a/packages/babel-preset-alloy/tsconfig.build.json b/packages/babel-preset-alloy/tsconfig.build.json new file mode 100644 index 000000000..b1135e0c5 --- /dev/null +++ b/packages/babel-preset-alloy/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "references": [], + "include": ["src"], + "exclude": ["**/*.test.*", "test/**/*"] +} diff --git a/packages/babel-preset-alloy/tsconfig.json b/packages/babel-preset-alloy/tsconfig.json new file mode 100644 index 000000000..cfd2ed456 --- /dev/null +++ b/packages/babel-preset-alloy/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "outDir": "dist" + }, + "include": ["src/**/*.ts", "src/**/*.d.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/cli/package.json b/packages/cli/package.json index 1528eb295..d25237527 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -31,7 +31,6 @@ }, "dependencies": { "@alloy-js/babel-preset": "workspace:~", - "@babel/preset-typescript": "catalog:", "@babel/core": "catalog:", "picocolors": "catalog:", "pathe": "catalog:" diff --git a/packages/cli/src/babel.ts b/packages/cli/src/babel.ts index 86cd0c07f..6eec703cd 100644 --- a/packages/cli/src/babel.ts +++ b/packages/cli/src/babel.ts @@ -1,10 +1,7 @@ import { mkdir, writeFile } from "node:fs/promises"; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-expect-error -import alloyPreset from "@alloy-js/babel-preset"; +import { alloyBabelPresets } from "@alloy-js/babel-preset"; import * as babel from "@babel/core"; -import typescriptPreset from "@babel/preset-typescript"; import { basename, dirname, join, relative } from "pathe"; export interface BuildOptions { @@ -14,10 +11,7 @@ export interface BuildOptions { export async function buildFile(filename: string, options: BuildOptions) { return babel.transformFileAsync(filename, { sourceMaps: options.sourceMaps, - presets: [ - typescriptPreset, - [alloyPreset, { addSourceInfo: options.addSourceInfo }], - ], + presets: alloyBabelPresets({ addSourceInfo: options.addSourceInfo }), }); } diff --git a/packages/core/test/babel-e2e.test.ts b/packages/core/test/babel-e2e.test.ts index 118b662b6..0360ee762 100644 --- a/packages/core/test/babel-e2e.test.ts +++ b/packages/core/test/babel-e2e.test.ts @@ -10,7 +10,6 @@ * intrinsics returning AlloyNodes eagerly). */ -// @ts-expect-error — preset has no types import alloyPreset from "@alloy-js/babel-preset"; import { transformSync } from "@babel/core"; import typescriptPreset from "@babel/preset-typescript"; diff --git a/packages/core/test/output-e2e.test.ts b/packages/core/test/output-e2e.test.ts index 46fe90709..5af2ffdf9 100644 --- a/packages/core/test/output-e2e.test.ts +++ b/packages/core/test/output-e2e.test.ts @@ -10,7 +10,6 @@ * - `For` works under the runtime emitting multiple files. */ -// @ts-expect-error — preset has no types import alloyPreset from "@alloy-js/babel-preset"; import { transformSync } from "@babel/core"; import typescriptPreset from "@babel/preset-typescript"; diff --git a/packages/core/vitest.config.ts b/packages/core/vitest.config.ts index af9520a9f..05bb30644 100644 --- a/packages/core/vitest.config.ts +++ b/packages/core/vitest.config.ts @@ -2,18 +2,6 @@ import alloyPlugin from "@alloy-js/rollup-plugin"; import { defineConfig } from "vitest/config"; export default defineConfig({ - resolve: { - conditions: ["source"], - }, - ssr: { - resolve: { - conditions: ["source"], - }, - }, - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, test: { exclude: ["**/dist/**", "**/node_modules/**"], setupFiles: ["./test/vitest.setup.ts"], diff --git a/packages/csharp/vitest.config.ts b/packages/csharp/vitest.config.ts index af9520a9f..05bb30644 100644 --- a/packages/csharp/vitest.config.ts +++ b/packages/csharp/vitest.config.ts @@ -2,18 +2,6 @@ import alloyPlugin from "@alloy-js/rollup-plugin"; import { defineConfig } from "vitest/config"; export default defineConfig({ - resolve: { - conditions: ["source"], - }, - ssr: { - resolve: { - conditions: ["source"], - }, - }, - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, test: { exclude: ["**/dist/**", "**/node_modules/**"], setupFiles: ["./test/vitest.setup.ts"], diff --git a/packages/go/vitest.config.ts b/packages/go/vitest.config.ts index af9520a9f..05bb30644 100644 --- a/packages/go/vitest.config.ts +++ b/packages/go/vitest.config.ts @@ -2,18 +2,6 @@ import alloyPlugin from "@alloy-js/rollup-plugin"; import { defineConfig } from "vitest/config"; export default defineConfig({ - resolve: { - conditions: ["source"], - }, - ssr: { - resolve: { - conditions: ["source"], - }, - }, - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, test: { exclude: ["**/dist/**", "**/node_modules/**"], setupFiles: ["./test/vitest.setup.ts"], diff --git a/packages/java/vitest.config.ts b/packages/java/vitest.config.ts index af9520a9f..05bb30644 100644 --- a/packages/java/vitest.config.ts +++ b/packages/java/vitest.config.ts @@ -2,18 +2,6 @@ import alloyPlugin from "@alloy-js/rollup-plugin"; import { defineConfig } from "vitest/config"; export default defineConfig({ - resolve: { - conditions: ["source"], - }, - ssr: { - resolve: { - conditions: ["source"], - }, - }, - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, test: { exclude: ["**/dist/**", "**/node_modules/**"], setupFiles: ["./test/vitest.setup.ts"], diff --git a/packages/json/vitest.config.ts b/packages/json/vitest.config.ts index af9520a9f..05bb30644 100644 --- a/packages/json/vitest.config.ts +++ b/packages/json/vitest.config.ts @@ -2,18 +2,6 @@ import alloyPlugin from "@alloy-js/rollup-plugin"; import { defineConfig } from "vitest/config"; export default defineConfig({ - resolve: { - conditions: ["source"], - }, - ssr: { - resolve: { - conditions: ["source"], - }, - }, - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, test: { exclude: ["**/dist/**", "**/node_modules/**"], setupFiles: ["./test/vitest.setup.ts"], diff --git a/packages/markdown/vitest.config.ts b/packages/markdown/vitest.config.ts index af9520a9f..05bb30644 100644 --- a/packages/markdown/vitest.config.ts +++ b/packages/markdown/vitest.config.ts @@ -2,18 +2,6 @@ import alloyPlugin from "@alloy-js/rollup-plugin"; import { defineConfig } from "vitest/config"; export default defineConfig({ - resolve: { - conditions: ["source"], - }, - ssr: { - resolve: { - conditions: ["source"], - }, - }, - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, test: { exclude: ["**/dist/**", "**/node_modules/**"], setupFiles: ["./test/vitest.setup.ts"], diff --git a/packages/msbuild/vitest.config.ts b/packages/msbuild/vitest.config.ts index af9520a9f..05bb30644 100644 --- a/packages/msbuild/vitest.config.ts +++ b/packages/msbuild/vitest.config.ts @@ -2,18 +2,6 @@ import alloyPlugin from "@alloy-js/rollup-plugin"; import { defineConfig } from "vitest/config"; export default defineConfig({ - resolve: { - conditions: ["source"], - }, - ssr: { - resolve: { - conditions: ["source"], - }, - }, - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, test: { exclude: ["**/dist/**", "**/node_modules/**"], setupFiles: ["./test/vitest.setup.ts"], diff --git a/packages/python/vitest.config.ts b/packages/python/vitest.config.ts index af9520a9f..05bb30644 100644 --- a/packages/python/vitest.config.ts +++ b/packages/python/vitest.config.ts @@ -2,18 +2,6 @@ import alloyPlugin from "@alloy-js/rollup-plugin"; import { defineConfig } from "vitest/config"; export default defineConfig({ - resolve: { - conditions: ["source"], - }, - ssr: { - resolve: { - conditions: ["source"], - }, - }, - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, test: { exclude: ["**/dist/**", "**/node_modules/**"], setupFiles: ["./test/vitest.setup.ts"], diff --git a/packages/rollup-plugin/package.json b/packages/rollup-plugin/package.json index 4ece62297..074fdd8d5 100644 --- a/packages/rollup-plugin/package.json +++ b/packages/rollup-plugin/package.json @@ -28,7 +28,7 @@ "url": "git+https://github.com/alloy-framework/alloy.git" }, "engines": { - "node": ">=18.0.0" + "node": ">=22.0.0" }, "scripts": { "watch": "tsc -p ./tsconfig.build.json --watch", @@ -39,12 +39,12 @@ }, "dependencies": { "@alloy-js/babel-preset": "workspace:~", - "@babel/preset-typescript": "catalog:", "@rollup/plugin-babel": "catalog:" }, "devDependencies": { "@types/node": "catalog:", "typescript": "catalog:", + "vite": "catalog:", "vitest": "catalog:" }, "bugs": "https://github.com/alloy-framework/alloy/issues" diff --git a/packages/rollup-plugin/src/index.ts b/packages/rollup-plugin/src/index.ts index b5b89a8e8..31e0f883f 100644 --- a/packages/rollup-plugin/src/index.ts +++ b/packages/rollup-plugin/src/index.ts @@ -1,24 +1,53 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-expect-error -import alloyPreset from "@alloy-js/babel-preset"; -import typescriptPreset from "@babel/preset-typescript"; +import { alloyBabelPresets } from "@alloy-js/babel-preset"; import { babel } from "@rollup/plugin-babel"; +import type { Plugin, PluginOption } from "vite"; -export interface AlloyPluginOptions {} +export interface AlloyPluginOptions { + /** + * Emit source location information for components (used by the devtools). + * When omitted, the Alloy preset infers this from `BABEL_ENV`/`NODE_ENV`. + */ + addSourceInfo?: boolean; + /** + * How Babel should emit source maps for the transformed files. + * + * @default "both" + */ + sourceMaps?: boolean | "inline" | "both"; +} /** * Rollup/Vite plugin that handles Alloy's JSX syntax transformation. * * @remarks - * Do not set `esbuild.jsx: "automatic"` or `jsxImportSource` — esbuild must - * defer JSX processing to this plugin (set `esbuild.jsx: "preserve"`). + * When used with Vite (including Vitest) this plugin also configures esbuild to + * defer JSX processing to Babel (`esbuild.jsx: "preserve"`) and adds the + * `source` resolve condition, so consumers no longer need to set these by hand. + * Do not set `esbuild.jsx: "automatic"` or `jsxImportSource` — doing so + * prevents this plugin from transforming Alloy's JSX. */ -export default function alloyPlugin(options: AlloyPluginOptions = {}): any { - return babel({ +export default function alloyPlugin( + options: AlloyPluginOptions = {}, +): PluginOption { + const configPlugin: Plugin = { + name: "alloy:config", + // Vite-only hook; ignored by plain Rollup. + config() { + return { + esbuild: { jsx: "preserve", sourcemap: "both" }, + resolve: { conditions: ["source"] }, + ssr: { resolve: { conditions: ["source"] } }, + }; + }, + }; + + const transformPlugin = babel({ inputSourceMap: true as any, - sourceMaps: "both", + sourceMaps: options.sourceMaps ?? "both", babelHelpers: "bundled", extensions: [".ts", ".tsx"], - presets: [typescriptPreset, [alloyPreset]], + presets: alloyBabelPresets({ addSourceInfo: options.addSourceInfo }), }); + + return [configPlugin, transformPlugin as Plugin]; } diff --git a/packages/typescript/vitest.config.ts b/packages/typescript/vitest.config.ts index af9520a9f..05bb30644 100644 --- a/packages/typescript/vitest.config.ts +++ b/packages/typescript/vitest.config.ts @@ -2,18 +2,6 @@ import alloyPlugin from "@alloy-js/rollup-plugin"; import { defineConfig } from "vitest/config"; export default defineConfig({ - resolve: { - conditions: ["source"], - }, - ssr: { - resolve: { - conditions: ["source"], - }, - }, - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, test: { exclude: ["**/dist/**", "**/node_modules/**"], setupFiles: ["./test/vitest.setup.ts"], diff --git a/packages/typespec/vitest.config.ts b/packages/typespec/vitest.config.ts index b7862b4ee..cd14dbf09 100644 --- a/packages/typespec/vitest.config.ts +++ b/packages/typespec/vitest.config.ts @@ -2,18 +2,6 @@ import alloyPlugin from "@alloy-js/rollup-plugin"; import { defineConfig } from "vitest/config"; export default defineConfig({ - resolve: { - conditions: ["source"], - }, - ssr: { - resolve: { - conditions: ["source"], - }, - }, - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, plugins: [alloyPlugin()], test: { exclude: ["**/dist/**", "**/node_modules/**"], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 053dbfa22..38162db1f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -343,6 +343,22 @@ importers: '@alloy-js/babel-plugin-jsx-dom-expressions': specifier: workspace:~ version: link:../babel-plugin-jsx-dom-expressions + '@babel/preset-typescript': + specifier: 'catalog:' + version: 8.0.1(@babel/core@8.0.1) + devDependencies: + '@babel/core': + specifier: 'catalog:' + version: 8.0.1 + '@types/node': + specifier: 'catalog:' + version: 26.1.1 + rimraf: + specifier: 'catalog:' + version: 6.1.3 + typescript: + specifier: 'catalog:' + version: 7.0.2 packages/cli: dependencies: @@ -352,9 +368,6 @@ importers: '@babel/core': specifier: 'catalog:' version: 8.0.1 - '@babel/preset-typescript': - specifier: 'catalog:' - version: 8.0.1(@babel/core@8.0.1) pathe: specifier: 'catalog:' version: 2.0.3 @@ -907,12 +920,9 @@ importers: '@alloy-js/babel-preset': specifier: workspace:~ version: link:../babel-preset-alloy - '@babel/preset-typescript': - specifier: 'catalog:' - version: 8.0.1(@babel/core@8.0.1) '@rollup/plugin-babel': specifier: 'catalog:' - version: 7.1.0(@babel/core@8.0.1)(rollup@4.62.2) + version: 7.1.0(@babel/core@8.0.1)(rollup@4.62.2)(supports-color@8.1.1) devDependencies: '@types/node': specifier: 'catalog:' @@ -920,6 +930,9 @@ importers: typescript: specifier: 'catalog:' version: 7.0.2 + vite: + specifier: 'catalog:' + version: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0) vitest: specifier: 'catalog:' version: 4.1.10(@types/node@26.1.1)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)) @@ -6693,9 +6706,9 @@ snapshots: '@babel/traverse': 8.0.4 '@babel/types': 8.0.4 - '@babel/helper-module-imports@7.29.7': + '@babel/helper-module-imports@7.29.7(supports-color@8.1.1)': dependencies: - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@8.1.1) '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color @@ -6800,7 +6813,7 @@ snapshots: '@babel/parser': 8.0.4 '@babel/types': 8.0.4 - '@babel/traverse@7.29.7': + '@babel/traverse@7.29.7(supports-color@8.1.1)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 @@ -8151,10 +8164,10 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} - '@rollup/plugin-babel@7.1.0(@babel/core@8.0.1)(rollup@4.62.2)': + '@rollup/plugin-babel@7.1.0(@babel/core@8.0.1)(rollup@4.62.2)(supports-color@8.1.1)': dependencies: '@babel/core': 8.0.1 - '@babel/helper-module-imports': 7.29.7 + '@babel/helper-module-imports': 7.29.7(supports-color@8.1.1) '@rollup/pluginutils': 5.4.0(rollup@4.62.2) workerpool: 9.3.4 optionalDependencies: diff --git a/samples/basic-project/vitest.config.ts b/samples/basic-project/vitest.config.ts index f05f0ab40..4e1f52a87 100644 --- a/samples/basic-project/vitest.config.ts +++ b/samples/basic-project/vitest.config.ts @@ -6,9 +6,5 @@ export default defineConfig({ include: ["test/**/*.ts", "test/**/*.tsx"], exclude: ["test/**/*.util.ts", "test/**/*.d.ts"], }, - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, plugins: [alloyPlugin()], }); diff --git a/samples/client-emitter/vitest.config.ts b/samples/client-emitter/vitest.config.ts index f05f0ab40..4e1f52a87 100644 --- a/samples/client-emitter/vitest.config.ts +++ b/samples/client-emitter/vitest.config.ts @@ -6,9 +6,5 @@ export default defineConfig({ include: ["test/**/*.ts", "test/**/*.tsx"], exclude: ["test/**/*.util.ts", "test/**/*.d.ts"], }, - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, plugins: [alloyPlugin()], }); diff --git a/samples/go-example/vitest.config.ts b/samples/go-example/vitest.config.ts index ba661c0a9..01359c94a 100644 --- a/samples/go-example/vitest.config.ts +++ b/samples/go-example/vitest.config.ts @@ -2,9 +2,5 @@ import alloyPlugin from "@alloy-js/rollup-plugin"; import { defineConfig } from "vitest/config"; export default defineConfig({ - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, plugins: [alloyPlugin()], }); diff --git a/samples/python-example/vitest.config.ts b/samples/python-example/vitest.config.ts index ba661c0a9..01359c94a 100644 --- a/samples/python-example/vitest.config.ts +++ b/samples/python-example/vitest.config.ts @@ -2,9 +2,5 @@ import alloyPlugin from "@alloy-js/rollup-plugin"; import { defineConfig } from "vitest/config"; export default defineConfig({ - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, plugins: [alloyPlugin()], }); diff --git a/samples/scaffold-generator/vitest.config.ts b/samples/scaffold-generator/vitest.config.ts index f05f0ab40..4e1f52a87 100644 --- a/samples/scaffold-generator/vitest.config.ts +++ b/samples/scaffold-generator/vitest.config.ts @@ -6,9 +6,5 @@ export default defineConfig({ include: ["test/**/*.ts", "test/**/*.tsx"], exclude: ["test/**/*.util.ts", "test/**/*.d.ts"], }, - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, plugins: [alloyPlugin()], }); diff --git a/test/performance/vitest.config.ts b/test/performance/vitest.config.ts index ba661c0a9..01359c94a 100644 --- a/test/performance/vitest.config.ts +++ b/test/performance/vitest.config.ts @@ -2,9 +2,5 @@ import alloyPlugin from "@alloy-js/rollup-plugin"; import { defineConfig } from "vitest/config"; export default defineConfig({ - esbuild: { - jsx: "preserve", - sourcemap: "both", - }, plugins: [alloyPlugin()], }); From 1641a6785d38d8ea65190ac4bd3eb05691f10750 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Tue, 21 Jul 2026 20:53:13 -0400 Subject: [PATCH 2/3] Drop alloyBabelPresets helper, compose presets inline The shared helper was unnecessary indirection for such a small duplication. The CLI and Rollup/Vite plugin now compose the typed preset directly (still without @ts-expect-error thanks to the TS conversion of @alloy-js/babel-preset). --- .../feature-babel-preset-types-2026-7-21.md | 2 +- .../internal-share-babel-config-2026-7-21.md | 2 +- packages/babel-preset-alloy/package.json | 3 +-- packages/babel-preset-alloy/src/index.ts | 25 +------------------ packages/cli/package.json | 1 + packages/cli/src/babel.ts | 8 ++++-- packages/rollup-plugin/package.json | 1 + packages/rollup-plugin/src/index.ts | 8 ++++-- pnpm-lock.yaml | 9 ++++--- 9 files changed, 24 insertions(+), 35 deletions(-) diff --git a/.chronus/changes/feature-babel-preset-types-2026-7-21.md b/.chronus/changes/feature-babel-preset-types-2026-7-21.md index 99b5dee78..7bc33fe24 100644 --- a/.chronus/changes/feature-babel-preset-types-2026-7-21.md +++ b/.chronus/changes/feature-babel-preset-types-2026-7-21.md @@ -4,4 +4,4 @@ packages: - "@alloy-js/babel-preset" --- -`@alloy-js/babel-preset` now ships with TypeScript type declarations and exports an `alloyBabelPresets()` helper providing the canonical `@babel/preset-typescript` + Alloy preset chain shared by the CLI and the Rollup/Vite plugin. +`@alloy-js/babel-preset` now ships with TypeScript type declarations, so consumers can import the preset without `@ts-expect-error`. diff --git a/.chronus/changes/internal-share-babel-config-2026-7-21.md b/.chronus/changes/internal-share-babel-config-2026-7-21.md index 41d163881..303b79f3b 100644 --- a/.chronus/changes/internal-share-babel-config-2026-7-21.md +++ b/.chronus/changes/internal-share-babel-config-2026-7-21.md @@ -14,4 +14,4 @@ packages: - "@alloy-js/typespec" --- -Share the Babel preset configuration through `@alloy-js/babel-preset` and rely on the Rollup/Vite plugin to configure esbuild, removing the duplicated esbuild/resolve boilerplate from each package's `vitest.config.ts`. +Rely on the Rollup/Vite plugin to configure esbuild, removing the duplicated esbuild/resolve boilerplate from each package's `vitest.config.ts`. diff --git a/packages/babel-preset-alloy/package.json b/packages/babel-preset-alloy/package.json index a4bb4d444..08daff839 100644 --- a/packages/babel-preset-alloy/package.json +++ b/packages/babel-preset-alloy/package.json @@ -40,8 +40,7 @@ }, "dependencies": { "@alloy-js/babel-plugin": "workspace:~", - "@alloy-js/babel-plugin-jsx-dom-expressions": "workspace:~", - "@babel/preset-typescript": "catalog:" + "@alloy-js/babel-plugin-jsx-dom-expressions": "workspace:~" }, "devDependencies": { "@babel/core": "catalog:", diff --git a/packages/babel-preset-alloy/src/index.ts b/packages/babel-preset-alloy/src/index.ts index 4b94a8927..a99ef2d3b 100644 --- a/packages/babel-preset-alloy/src/index.ts +++ b/packages/babel-preset-alloy/src/index.ts @@ -1,7 +1,6 @@ import alloyTransform from "@alloy-js/babel-plugin"; import jsxTransform from "@alloy-js/babel-plugin-jsx-dom-expressions"; -import type { PluginItem, PresetItem } from "@babel/core"; -import typescriptPreset from "@babel/preset-typescript"; +import type { PluginItem } from "@babel/core"; /** Options accepted by the Alloy Babel preset. */ export interface AlloyPresetOptions { @@ -72,25 +71,3 @@ export default function alloyPreset( return { plugins }; } - -/** Options for {@link alloyBabelPresets}. */ -export interface AlloyBabelPresetsOptions { - /** Forwarded to the Alloy preset to emit component source locations. */ - addSourceInfo?: boolean; -} - -/** - * The canonical Babel preset chain used to compile Alloy `.ts`/`.tsx` sources: - * `@babel/preset-typescript` followed by the Alloy preset. - * - * Shared by `@alloy-js/cli` and `@alloy-js/rollup-plugin` so the configuration - * stays in a single place. - */ -export function alloyBabelPresets( - options: AlloyBabelPresetsOptions = {}, -): PresetItem[] { - return [ - typescriptPreset, - [alloyPreset, { addSourceInfo: options.addSourceInfo }], - ] as PresetItem[]; -} diff --git a/packages/cli/package.json b/packages/cli/package.json index d25237527..1528eb295 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -31,6 +31,7 @@ }, "dependencies": { "@alloy-js/babel-preset": "workspace:~", + "@babel/preset-typescript": "catalog:", "@babel/core": "catalog:", "picocolors": "catalog:", "pathe": "catalog:" diff --git a/packages/cli/src/babel.ts b/packages/cli/src/babel.ts index 6eec703cd..9cefafec1 100644 --- a/packages/cli/src/babel.ts +++ b/packages/cli/src/babel.ts @@ -1,7 +1,8 @@ import { mkdir, writeFile } from "node:fs/promises"; -import { alloyBabelPresets } from "@alloy-js/babel-preset"; +import alloyPreset from "@alloy-js/babel-preset"; import * as babel from "@babel/core"; +import typescriptPreset from "@babel/preset-typescript"; import { basename, dirname, join, relative } from "pathe"; export interface BuildOptions { @@ -11,7 +12,10 @@ export interface BuildOptions { export async function buildFile(filename: string, options: BuildOptions) { return babel.transformFileAsync(filename, { sourceMaps: options.sourceMaps, - presets: alloyBabelPresets({ addSourceInfo: options.addSourceInfo }), + presets: [ + typescriptPreset, + [alloyPreset, { addSourceInfo: options.addSourceInfo }], + ], }); } diff --git a/packages/rollup-plugin/package.json b/packages/rollup-plugin/package.json index 074fdd8d5..9f586e70f 100644 --- a/packages/rollup-plugin/package.json +++ b/packages/rollup-plugin/package.json @@ -39,6 +39,7 @@ }, "dependencies": { "@alloy-js/babel-preset": "workspace:~", + "@babel/preset-typescript": "catalog:", "@rollup/plugin-babel": "catalog:" }, "devDependencies": { diff --git a/packages/rollup-plugin/src/index.ts b/packages/rollup-plugin/src/index.ts index 31e0f883f..9a8c4ea89 100644 --- a/packages/rollup-plugin/src/index.ts +++ b/packages/rollup-plugin/src/index.ts @@ -1,4 +1,5 @@ -import { alloyBabelPresets } from "@alloy-js/babel-preset"; +import alloyPreset from "@alloy-js/babel-preset"; +import typescriptPreset from "@babel/preset-typescript"; import { babel } from "@rollup/plugin-babel"; import type { Plugin, PluginOption } from "vite"; @@ -46,7 +47,10 @@ export default function alloyPlugin( sourceMaps: options.sourceMaps ?? "both", babelHelpers: "bundled", extensions: [".ts", ".tsx"], - presets: alloyBabelPresets({ addSourceInfo: options.addSourceInfo }), + presets: [ + typescriptPreset, + [alloyPreset, { addSourceInfo: options.addSourceInfo }], + ], }); return [configPlugin, transformPlugin as Plugin]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 38162db1f..057588b9f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -343,9 +343,6 @@ importers: '@alloy-js/babel-plugin-jsx-dom-expressions': specifier: workspace:~ version: link:../babel-plugin-jsx-dom-expressions - '@babel/preset-typescript': - specifier: 'catalog:' - version: 8.0.1(@babel/core@8.0.1) devDependencies: '@babel/core': specifier: 'catalog:' @@ -368,6 +365,9 @@ importers: '@babel/core': specifier: 'catalog:' version: 8.0.1 + '@babel/preset-typescript': + specifier: 'catalog:' + version: 8.0.1(@babel/core@8.0.1) pathe: specifier: 'catalog:' version: 2.0.3 @@ -920,6 +920,9 @@ importers: '@alloy-js/babel-preset': specifier: workspace:~ version: link:../babel-preset-alloy + '@babel/preset-typescript': + specifier: 'catalog:' + version: 8.0.1(@babel/core@8.0.1) '@rollup/plugin-babel': specifier: 'catalog:' version: 7.1.0(@babel/core@8.0.1)(rollup@4.62.2)(supports-color@8.1.1) From f7899c19970e49d8195f47395f676f2a05abf3f9 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Mon, 27 Jul 2026 15:00:58 -0400 Subject: [PATCH 3/3] Use oxc transformer option instead of deprecated esbuild in Vite config --- packages/rollup-plugin/src/index.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/rollup-plugin/src/index.ts b/packages/rollup-plugin/src/index.ts index 9a8c4ea89..581d4c67e 100644 --- a/packages/rollup-plugin/src/index.ts +++ b/packages/rollup-plugin/src/index.ts @@ -21,11 +21,12 @@ export interface AlloyPluginOptions { * Rollup/Vite plugin that handles Alloy's JSX syntax transformation. * * @remarks - * When used with Vite (including Vitest) this plugin also configures esbuild to - * defer JSX processing to Babel (`esbuild.jsx: "preserve"`) and adds the - * `source` resolve condition, so consumers no longer need to set these by hand. - * Do not set `esbuild.jsx: "automatic"` or `jsxImportSource` — doing so - * prevents this plugin from transforming Alloy's JSX. + * When used with Vite (including Vitest) this plugin also configures the + * built-in transformer to defer JSX processing to Babel (`oxc.jsx: "preserve"`) + * and adds the `source` resolve condition, so consumers no longer need to set + * these by hand. Do not set the transformer's JSX handling to `"automatic"` or a + * `jsxImportSource` — doing so prevents this plugin from transforming Alloy's + * JSX. */ export default function alloyPlugin( options: AlloyPluginOptions = {}, @@ -35,7 +36,7 @@ export default function alloyPlugin( // Vite-only hook; ignored by plain Rollup. config() { return { - esbuild: { jsx: "preserve", sourcemap: "both" }, + oxc: { jsx: "preserve" }, resolve: { conditions: ["source"] }, ssr: { resolve: { conditions: ["source"] } }, };