From 07e9faedf2b7b25d15bcbd71526317ece89d7894 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:43:43 +0000 Subject: [PATCH] fix(nitro): skip generated build artifacts (#2925) Signed-off-by: Nathan Colosimo <110621881+NathanColosimo@users.noreply.github.com> --- .changeset/silly-chairs-rest.md | 5 ++++ packages/nitro/src/index.test.ts | 51 ++++++++++++++++++++++++++++++++ packages/nitro/src/index.ts | 12 ++++---- packages/nitro/src/vite.ts | 13 ++++---- 4 files changed, 68 insertions(+), 13 deletions(-) create mode 100644 .changeset/silly-chairs-rest.md diff --git a/.changeset/silly-chairs-rest.md b/.changeset/silly-chairs-rest.md new file mode 100644 index 0000000000..b56276eaf8 --- /dev/null +++ b/.changeset/silly-chairs-rest.md @@ -0,0 +1,5 @@ +--- +'@workflow/nitro': patch +--- + +Skip Workflow transforms for generated Nitro build artifacts. diff --git a/packages/nitro/src/index.test.ts b/packages/nitro/src/index.test.ts index 7ebe569580..9e616b3152 100644 --- a/packages/nitro/src/index.test.ts +++ b/packages/nitro/src/index.test.ts @@ -13,6 +13,7 @@ import type { Nitro } from 'nitro/types'; import { describe, expect, it, onTestFinished } from 'vitest'; import { LocalBuilder, VercelBuilder } from './builders.js'; import nitroModule from './index.js'; +import { workflow as viteWorkflow } from './vite.js'; type StubOptions = { routing: boolean; @@ -180,3 +181,53 @@ describe('@workflow/nitro projectRoot', () => { }); } }); + +describe('@workflow/nitro transform boundaries', () => { + it('does not re-transform generated Nitro build artifacts', async () => { + const rollupBeforeHooks: Array<(nitro: any, config: any) => void> = []; + const nitro = createNitroStub({ routing: true }); + nitro.hooks.hook = ( + name: string, + hook: (nitro: any, config: any) => void + ) => { + if (name === 'rollup:before') rollupBeforeHooks.push(hook); + }; + + const plugins = viteWorkflow(); + const viteTransform = plugins.find( + (plugin) => plugin.name === 'workflow:transform' + ) as any; + const viteNitro = plugins.find( + (plugin) => plugin.name === 'workflow:nitro' + ) as any; + + await viteNitro.nitro.setup(nitro); + + const config: { plugins: any[] } = { plugins: [] }; + for (const hook of rollupBeforeHooks) { + hook(nitro, config); + } + const nitroTransform = config.plugins.find( + (plugin: { name?: string }) => plugin.name === 'workflow:transform' + ); + + const code = ` + import { WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from '@workflow/serde'; + export class Serializable { + static [WORKFLOW_SERIALIZE](value) { return value; } + static [WORKFLOW_DESERIALIZE]() { return new Serializable(); } + } + `; + const generatedId = '/tmp/.nitro/vite/services/ssr/assets/index.js'; + const siblingId = '/tmp/.nitro-source/index.js'; + + for (const transform of [viteTransform, nitroTransform]) { + await expect( + transform.transform.call({}, code, generatedId) + ).resolves.toBeNull(); + await expect( + transform.transform.call({}, code, siblingId) + ).resolves.not.toBeNull(); + } + }); +}); diff --git a/packages/nitro/src/index.ts b/packages/nitro/src/index.ts index 4e594a1352..2faaf04f16 100644 --- a/packages/nitro/src/index.ts +++ b/packages/nitro/src/index.ts @@ -15,18 +15,18 @@ export default { const isVercelDeploy = !nitro.options.dev && nitro.options.preset === 'vercel'; - // Pre-built workflow bundles directory - must be excluded from re-transformation - const workflowBuildDir = join(nitro.options.buildDir, 'workflow'); + // Nitro build artifacts have already been transformed; exclude the whole + // build directory from re-transformation. + const nitroBuildDir = `${nitro.options.buildDir.replace(/[\\/]+$/, '')}/`; // Add transform plugin at the BEGINNING to run before other transforms // (especially before class property transforms that rename classes like _ClassName) nitro.hooks.hook('rollup:before', (_nitro: Nitro, config: RollupConfig) => { (config.plugins as Array).unshift( workflowTransformPlugin({ - // Exclude pre-built workflow bundles from re-transformation - // These are already processed and re-processing causes issues like - // undefined class references when Nitro's bundler renames variables - exclude: [workflowBuildDir], + // Nitro build artifacts have already been transformed. + // Re-processing them can duplicate class registration. + exclude: [nitroBuildDir], }) ); }); diff --git a/packages/nitro/src/vite.ts b/packages/nitro/src/vite.ts index d80af7da80..c223cd3ac5 100644 --- a/packages/nitro/src/vite.ts +++ b/packages/nitro/src/vite.ts @@ -3,7 +3,6 @@ import { workflowTransformPlugin } from '@workflow/rollup'; import { workflowHotUpdatePlugin } from '@workflow/vite'; import type { Nitro } from 'nitro/types'; import type {} from 'nitro/vite'; -import { join } from 'pathe'; import type { Plugin } from 'vite'; import { LocalBuilder } from './builders.js'; import type { ModuleOptions } from './index.js'; @@ -11,18 +10,18 @@ import nitroModule from './index.js'; export function workflow(options?: ModuleOptions): Plugin[] { let builder: LocalBuilder; - let workflowBuildDir: string; + let nitroBuildDir: string; const enqueue = createBuildQueue(); - // Create a lazy transform plugin that excludes the workflow build directory + // Create a lazy transform plugin that excludes Nitro build artifacts. // The exclusion path is set during nitro setup, so we need to defer plugin creation const lazyTransformPlugin: Plugin = { name: 'workflow:transform', transform(code, id) { // Delegate to the actual transform plugin with exclusion - // workflowBuildDir is set during nitro setup before transforms run + // nitroBuildDir is set during nitro setup before transforms run const plugin = workflowTransformPlugin({ - exclude: workflowBuildDir ? [workflowBuildDir] : [], + exclude: nitroBuildDir ? [nitroBuildDir] : [], }); return (plugin.transform as Function)?.call(this, code, id); }, @@ -34,8 +33,8 @@ export function workflow(options?: ModuleOptions): Plugin[] { name: 'workflow:nitro', nitro: { setup: (nitro: Nitro) => { - // Capture the workflow build directory for exclusion - workflowBuildDir = join(nitro.options.buildDir, 'workflow'); + // Capture the Nitro build directory for exclusion + nitroBuildDir = `${nitro.options.buildDir.replace(/[\\/]+$/, '')}/`; nitro.options.workflow = { ...nitro.options.workflow, ...options,