Skip to content

Builder-Vite: Add plugin to enforce Storybook's output directory in Vite build configuration#33740

Merged
valentinpalkovic merged 2 commits into
nextfrom
valentin/ensure-consistent-build-output
Feb 2, 2026
Merged

Builder-Vite: Add plugin to enforce Storybook's output directory in Vite build configuration#33740
valentinpalkovic merged 2 commits into
nextfrom
valentin/ensure-consistent-build-output

Conversation

@valentinpalkovic
Copy link
Copy Markdown
Contributor

@valentinpalkovic valentinpalkovic commented Feb 2, 2026

Closes #33738

What I did

Fixes an issue where Storybook build artifacts are incorrectly redirected to Nitro's output directory (.output/public/) instead of the expected Storybook output directory (storybook-static) when using TanStack Start projects.

  • Adds a Vite plugin with enforce: 'post' to ensure Storybook's outDir takes precedence over framework plugins
  • Prevents frameworks like Nitro from hijacking the build output location

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

[ ] Create a new TanStack Start project
[ ] Add Storybook using npx storybook@latest init
[ ] Run npx storybook build
[ ] Verify that storybook-static contains the sb-preview folder
[ ] Verify that .output/public/ does NOT contain Storybook's preview assets
[ ] Run Chromatic CLI to verify the build is valid

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the @storybookjs/core team here.

core team members can create a canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=<PR_NUMBER>

Summary by CodeRabbit

  • Bug Fixes
    • Ensures the storybook build enforces the configured output directory so build artifacts are placed consistently and reliably across different build modes.

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Feb 2, 2026

View your CI Pipeline Execution ↗ for commit 4c506c2

Command Status Duration Result
nx run-many -t compile,check,knip,test,pretty-d... ✅ Succeeded 7m 36s View ↗

☁️ Nx Cloud last updated this comment at 2026-02-02 15:27:34 UTC

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 2, 2026

📝 Walkthrough

Walkthrough

Adds a Vite plugin named "storybook:enforce-output-dir" that runs with enforce: 'post' and sets build.outDir to options.outputDir via both a config hook and a configEnvironment hook (Vite 6) to enforce output directory in all config phases.

Changes

Cohort / File(s) Summary
Vite plugin change
code/builders/builder-vite/src/build.ts
Introduces the "storybook:enforce-output-dir" plugin. Plugin uses enforce: 'post', updates build.outDir to options.outputDir in a config hook and also in a configEnvironment hook for Vite 6, applied after existing presets.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@code/builders/builder-vite/src/build.ts`:
- Around line 56-63: The plugin object currently includes the Vite 6+ hook
configEnvironment which causes TypeScript errors under Vite 5; update the plugin
creation to be type-safe by either casting the plugin object to PluginOption
(import PluginOption from 'vite') or by only adding configEnvironment when Vite
>= 6 at runtime, or use the TypeScript satisfies PluginOption assertion; target
the object that defines configEnvironment (the anonymous plugin passed into
InlineConfig.plugins) and apply one of these approaches so TypeScript accepts
the plugin across the supported Vite versions.
🧹 Nitpick comments (1)
code/builders/builder-vite/src/build.ts (1)

43-64: Simplify the config hook to match the existing pattern.

The config hook unnecessarily spreads the entire config object. Vite's config hook deep-merges the returned value, so you only need to return the properties you want to override. This is inconsistent with the storybook:define-env plugin pattern used just below (lines 69-75), which correctly returns only the partial config.

♻️ Proposed simplification
   finalConfig.plugins?.push({
     name: 'storybook:enforce-output-dir',
     enforce: 'post',
-    config: (config) => ({
-      ...config,
+    config: () => ({
       build: {
         outDir: options.outputDir,
       },
     }),
     // configEnvironment is a new method in Vite 6
     // It is used to configure configs based on the environment
     // E.g. Nitro uses this method to set the output directory to .output/public/
     configEnvironment: () => ({
       build: {
         outDir: options.outputDir,
       },
     }),
   });

Comment on lines +56 to +63
// configEnvironment is a new method in Vite 6
// It is used to configure configs based on the environment
// E.g. Nitro uses this method to set the output directory to .output/public/
configEnvironment: () => ({
build: {
outDir: options.outputDir,
},
}),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's look at the actual code in the file
cat -n code/builders/builder-vite/src/build.ts | sed -n '50,70p'

Repository: storybookjs/storybook

Length of output: 783


🏁 Script executed:

#!/bin/bash
# Check the Vite version in package.json
find . -name "package.json" -path "*/builder-vite/*" | head -1 | xargs cat | grep -A2 -B2 '"vite"'

Repository: storybookjs/storybook

Length of output: 346


🏁 Script executed:

#!/bin/bash
# Search for how configEnvironment is used across the codebase
rg "configEnvironment" --type ts --type tsx -B2 -A2

Repository: storybookjs/storybook

Length of output: 92


🏁 Script executed:

#!/bin/bash
# Check if there are type definitions or interface declarations related to configEnvironment
rg "Plugin.*interface\|type.*Plugin\|configEnvironment" --type ts --type tsx -B3 -A3 | head -80

Repository: storybookjs/storybook

Length of output: 92


🏁 Script executed:

#!/bin/bash
# Search for configEnvironment without file type restrictions
rg "configEnvironment" -B2 -A2

Repository: storybookjs/storybook

Length of output: 680


🏁 Script executed:

#!/bin/bash
# Check if there's a tsconfig or any TypeScript configuration
find code/builders/builder-vite -name "tsconfig*.json" -o -name "*.d.ts" | head -10

Repository: storybookjs/storybook

Length of output: 105


🏁 Script executed:

#!/bin/bash
# Look at the actual Plugin type usage in the file
head -30 code/builders/builder-vite/src/build.ts

Repository: storybookjs/storybook

Length of output: 1218


🏁 Script executed:

#!/bin/bash
# Check Vite's plugin type definition from type stubs
find . -path "*/node_modules/vite/dist/node/*.d.ts" -o -path "*/vite/types/*" 2>/dev/null | head -5 | xargs grep -l "Plugin\|configEnvironment" 2>/dev/null | head -3

Repository: storybookjs/storybook

Length of output: 47


🏁 Script executed:

#!/bin/bash
# Look for any type casting or 'as' statements related to plugins
rg "as.*Plugin\|Plugin.*as\|satisfies.*Plugin" --type ts

Repository: storybookjs/storybook

Length of output: 47


🏁 Script executed:

#!/bin/bash
# Look at the full context where the plugin is created and returned
cat -n code/builders/builder-vite/src/build.ts | sed -n '40,90p'

Repository: storybookjs/storybook

Length of output: 2231


🏁 Script executed:

#!/bin/bash
# Check the commonConfig to see how plugins are handled
cat -n code/builders/builder-vite/src/vite-config.ts | head -100

Repository: storybookjs/storybook

Length of output: 4170


🏁 Script executed:

#!/bin/bash
# Check what version of Vite types are being used by looking at Vite's plugin definition
node -e "const vite = require('vite'); console.log(Object.keys(vite));" 2>/dev/null || echo "Node import failed"

Repository: storybookjs/storybook

Length of output: 83


🏁 Script executed:

#!/bin/bash
# Look for any type definitions or extends in the builder-vite directory
find code/builders/builder-vite -type f \( -name "*.ts" \) -exec grep -l "Plugin\|plugin\|configEnvironment" {} \; | head -5

Repository: storybookjs/storybook

Length of output: 830


🏁 Script executed:

#!/bin/bash
# Check the actual Vite package.json to see the types version
find . -path "*/node_modules/vite/package.json" | head -1 | xargs cat | grep -A5 -B5 "version\|types"

Repository: storybookjs/storybook

Length of output: 47


🏁 Script executed:

#!/bin/bash
# Search for how PluginOption type is used with plugins containing configEnvironment
rg "PluginOption\|Plugin\>" code/builders/builder-vite/src/build.ts -B5 -A5

Repository: storybookjs/storybook

Length of output: 173


🏁 Script executed:

#!/bin/bash
# Check if there's a types package or any type augmentation for Vite
find code/builders/builder-vite -name "*.d.ts" -o -name "types.ts" | xargs cat 2>/dev/null

Repository: storybookjs/storybook

Length of output: 653


🏁 Script executed:

#!/bin/bash
# Look for any TypeScript errors or build issues by checking if the project compiles
# Check the tsconfig for strict mode settings
cat code/builders/builder-vite/tsconfig.json

Repository: storybookjs/storybook

Length of output: 155


🏁 Script executed:

#!/bin/bash
# Check if configEnvironment is documented in Vite's types by checking the actual Plugin interface
python3 << 'EOF'
import json
import subprocess

# Try to find and read the Vite type definitions
try:
    result = subprocess.run(['find', '.', '-path', '*/node_modules/vite/dist/node/index.d.ts'], 
                          capture_output=True, text=True, timeout=5)
    if result.stdout.strip():
        print("Found Vite types at:")
        print(result.stdout)
        # Read the file
        with open(result.stdout.strip().split('\n')[0], 'r') as f:
            content = f.read()
            # Look for Plugin interface or configEnvironment
            if 'configEnvironment' in content:
                print("\nconfigEnvironment found in Vite types")
                # Print surrounding lines
                lines = content.split('\n')
                for i, line in enumerate(lines):
                    if 'configEnvironment' in line:
                        start = max(0, i-2)
                        end = min(len(lines), i+3)
                        print('\n'.join(lines[start:end]))
            else:
                print("\nconfigEnvironment NOT found in main Vite types")
except Exception as e:
    print(f"Error: {e}")
EOF

Repository: storybookjs/storybook

Length of output: 47


🏁 Script executed:

#!/bin/bash
# Look for any .d.ts files in vite module
find . -path "*/node_modules/vite/*" -name "*.d.ts" 2>/dev/null | head -20

Repository: storybookjs/storybook

Length of output: 47


🏁 Script executed:

#!/bin/bash
# Check if there are any build errors or TypeScript issues by looking at the actual return type
cat code/builders/builder-vite/src/build.ts | sed -n '47,77p'

Repository: storybookjs/storybook

Length of output: 861


🏁 Script executed:

#!/bin/bash
# Check if there's a satisfies clause or as cast for the plugin
rg "satisfies|as.*Plugin" code/builders/builder-vite/src/build.ts -B2 -A2

Repository: storybookjs/storybook

Length of output: 877


🏁 Script executed:

#!/bin/bash
# Look for any comment or TODO related to type issues with configEnvironment
rg "configEnvironment|TODO|FIXME|type" code/builders/builder-vite/src/build.ts

Repository: storybookjs/storybook

Length of output: 299


🏁 Script executed:

#!/bin/bash
# Check what PluginOption actually includes by looking at how it's imported and used
python3 << 'EOF'
import subprocess
import os

# Search for PluginOption type definition
result = subprocess.run(['rg', 'type PluginOption|interface PluginOption', '--type', 'ts'], 
                       capture_output=True, text=True, cwd='.')
print("PluginOption definition search:")
print(result.stdout if result.stdout else "Not found in codebase")
print(result.stderr if result.stderr else "")

# Try to find the Vite node types
result2 = subprocess.run(['find', '.', '-type', 'f', '-path', '*/vite/*', '-name', 'index.d.ts'], 
                        capture_output=True, text=True, timeout=10)
print("\nVite type files found:")
print(result2.stdout)
EOF

Repository: storybookjs/storybook

Length of output: 144


🏁 Script executed:

#!/bin/bash
# Let's check if there are any existing TypeScript errors by looking at the root tsconfig
cat tsconfig.json | head -50

Repository: storybookjs/storybook

Length of output: 110


🏁 Script executed:

#!/bin/bash
# Search in vite-config.ts for how PluginOption is used
cat code/builders/builder-vite/src/vite-config.ts | sed -n '1,30p'

Repository: storybookjs/storybook

Length of output: 767


🏁 Script executed:

#!/bin/bash
# Check the actual Plugin type being used - look at the imports more carefully
head -20 code/builders/builder-vite/src/build.ts

Repository: storybookjs/storybook

Length of output: 852


🏁 Script executed:

#!/bin/bash
# Let's examine what InlineConfig expects for plugins
python3 << 'EOF'
import subprocess

# Check if we can find any type information about configEnvironment in node_modules
result = subprocess.run(['find', '.', '-path', '*/node_modules/vite/dist/node/*.d.ts', '-type', 'f'], 
                       capture_output=True, text=True, timeout=10)
files = result.stdout.strip().split('\n')
print(f"Found {len([f for f in files if f])} Vite type definition files")

# Try to read the plugin definition if we can find it
for f in files[:5]:
    if f and 'plugin' in f.lower():
        print(f"\nReading {f}:")
        try:
            with open(f, 'r') as file:
                content = file.read(2000)
                if 'configEnvironment' in content:
                    print("Found configEnvironment!")
                print(content[:1000])
        except Exception as e:
            print(f"Error: {e}")
EOF

Repository: storybookjs/storybook

Length of output: 99


🏁 Script executed:

#!/bin/bash
# Check if this is actually compiled without errors by looking at build outputs
find code/builders/builder-vite -name "dist" -o -name "build" | head -5

Repository: storybookjs/storybook

Length of output: 47


🏁 Script executed:

#!/bin/bash
# Let's check the Vite release notes or see what version introduced configEnvironment
# by looking at comments in the code
rg "Vite [0-9]|configEnvironment" code/builders/builder-vite/src/ -B1 -A1

Repository: storybookjs/storybook

Length of output: 556


🏁 Script executed:

#!/bin/bash
# Check if there's any explicit type casting for the plugin object
cat code/builders/builder-vite/src/build.ts | sed -n '45,80p'

Repository: storybookjs/storybook

Length of output: 1089


🏁 Script executed:

#!/bin/bash
# Look for any type definitions or extensions related to Vite plugin compatibility
rg "declare|extend|augment" code/builders/builder-vite/src/ --type ts -B2 -A2

Repository: storybookjs/storybook

Length of output: 47


🏁 Script executed:

#!/bin/bash
# Check if there's a satisfies keyword or any inline type assertion
cat code/builders/builder-vite/src/build.ts | grep -E "satisfies|as |type |interface" | head -20

Repository: storybookjs/storybook

Length of output: 477


🏁 Script executed:

#!/bin/bash
# Let's check if Vite 5 is actually being used/tested in this codebase
rg "vite.*5\|version.*5" code/builders/builder-vite/ --type ts --type json

Repository: storybookjs/storybook

Length of output: 47


🏁 Script executed:

#!/antml/bash
# Check what the actual pushed plugin object looks like - if it has type issues it should show
python3 << 'EOF'
# Read the build.ts file and check if the plugin push statement is typed
with open('code/builders/builder-vite/src/build.ts', 'r') as f:
    lines = f.readlines()
    # Look at lines around 47-64 where plugin is pushed
    for i in range(46, min(65, len(lines))):
        print(f"{i+1:3d}: {lines[i]}", end='')
    
    # Check if there's a 'satisfies' or 'as' keyword
    content = ''.join(lines)
    if 'satisfies' in content:
        print("\n\nFound 'satisfies' keyword in file")
    else:
        print("\n\nNo 'satisfies' keyword found - object is untyped")
EOF

Repository: storybookjs/storybook

Length of output: 717


🏁 Script executed:

#!/bin/bash
# Check the package.json more carefully to understand version constraints
cat code/builders/builder-vite/package.json | grep -A5 -B5 "vite"

Repository: storybookjs/storybook

Length of output: 1130


🏁 Script executed:

#!/bin/bash
# Check if there's any version detection or conditional logic for Vite versions
rg "version|Vite 5|Vite 6|semver" code/builders/builder-vite/src/build.ts -B2 -A2

Repository: storybookjs/storybook

Length of output: 278


🏁 Script executed:

#!/bin/bash
# Check if the plugin object should have a type annotation or if other plugins use one
cat code/builders/builder-vite/src/build.ts | sed -n '66,77p'

Repository: storybookjs/storybook

Length of output: 354


🏁 Script executed:

#!/bin/bash
# See how other Storybook builders handle version-specific features
rg "configEnvironment" code/builders/ -B5 -A5 | head -40

Repository: storybookjs/storybook

Length of output: 1010


🏁 Script executed:

#!/bin/bash
# Check Vite 5 compatibility - does the code have any workarounds?
rg "TODO|FIXME|hack|workaround" code/builders/builder-vite/src/build.ts

Repository: storybookjs/storybook

Length of output: 47


🏁 Script executed:

#!/bin/bash
# Check what happens with the plugin object type - look at the complete function
tail -50 code/builders/builder-vite/src/build.ts

Repository: storybookjs/storybook

Length of output: 1679


🏁 Script executed:

#!/bin/bash
# Verify if the issue is actually causing TypeScript errors by looking at any build logs
find code/builders/builder-vite -name "*.log" -o -name "tsc-out*" -o -name "typescript*" 2>/dev/null

Repository: storybookjs/storybook

Length of output: 47


🏁 Script executed:

#!/bin/bash
# Check if the plugin object should be typed as PluginOption to catch the type issue
python3 << 'EOF'
import subprocess

# Search for how PluginOption is actually defined in vite-config.ts vs build.ts
result = subprocess.run(['rg', 'push.*plugin', 'code/builders/builder-vite/src/', '--type', 'ts', '-B2', '-A8'], 
                       capture_output=True, text=True)
print("Plugin push statements:")
print(result.stdout)
EOF

Repository: storybookjs/storybook

Length of output: 1399


🏁 Script executed:

#!/bin/bash
# Check what happens when we look at the types - does InlineConfig.plugins accept the plugin object?
python3 << 'EOF'
# The key question: is the plugin object properly typed?
# InlineConfig.plugins should be of type PluginOption[]
# configEnvironment is only in Vite 6+, so Vite 5 wouldn't have it

# Let's see what the actual issue would be
with open('code/builders/builder-vite/src/build.ts', 'r') as f:
    content = f.read()
    
    # Check if they import PluginOption
    if 'PluginOption' in content:
        print("PluginOption is imported in build.ts")
    else:
        print("PluginOption is NOT imported in build.ts - this is the issue!")
        
    # Check if they use 'as' or 'satisfies'
    lines = content.split('\n')
    for i, line in enumerate(lines[46:65], start=47):
        if 'as' in line or 'satisfies' in line:
            print(f"Line {i}: Found type assertion: {line.strip()}")
    
    print("\nThe plugin object is pushed without explicit typing,")
    print("but finalConfig.plugins is of type PluginOption[] (from InlineConfig)")
    print("configEnvironment doesn't exist in Vite 5, so TypeScript would error")
EOF

Repository: storybookjs/storybook

Length of output: 410


🏁 Script executed:

#!/bin/bash
# Confirm: does Vite 5 have configEnvironment hook?
web_search "Vite 5 configEnvironment hook plugin"

Repository: storybookjs/storybook

Length of output: 115


🌐 Web query:

Vite 5 vs Vite 6 configEnvironment plugin hook

💡 Result:

  • Vite 5: there is no configEnvironment plugin hook. Plugins can only influence configuration globally via hooks like config / configResolved, and users can branch config by command/mode and whether it’s an SSR build (e.g. isSsrBuild) in the config function. [3]

  • Vite 6: configEnvironment(name, options) is a new plugin hook added with the (experimental) Environment API. It’s called once per environment (e.g. client, ssr, and any custom environments) with that environment’s partially resolved options (including final defaults), and you can return a partial config to merge for just that environment. [1], [2]

    • Typical pattern in Vite 6: set broad defaults in config, then fine-tune per-environment in configEnvironment. [1]

Example (Vite 6):

export default function myPlugin() {
  return {
    name: 'my-plugin',
    configEnvironment(name, options) {
      if (name === 'rsc') {
        return { resolve: { conditions: ['workerd'] } }
      }
    },
  }
}

[1]

Practical takeaway: if you need “client vs ssr (or more) specific config” inside a plugin, that’s exactly what configEnvironment enables in Vite 6; in Vite 5 you had to approximate it with global config hooks and SSR/build branching rather than a per-environment config phase. [1], [3]

References:
[1] Vite “Environment API for Plugins” (configEnvironment) https://main.vitejs.dev/guide/api-environment-plugins
[2] Vite “Environment API” overview (Vite 6 environments) https://vite.dev/guide/api-environment
[3] Vite 5 “Configuring Vite” (conditional config incl. isSsrBuild) https://v5.vite.dev/config/


🏁 Script executed:

#!/bin/bash
# Verify if there are any tests or CI that would catch this TypeScript issue
find code/builders/builder-vite -name "*.test.ts" -o -name "*.spec.ts" | xargs grep -l "configEnvironment" 2>/dev/null

Repository: storybookjs/storybook

Length of output: 49


🏁 Script executed:

#!/bin/bash
# Check if there's a type guard or version check for this feature
rg "version|semver" code/builders/builder-vite/src/build.ts

Repository: storybookjs/storybook

Length of output: 47


🏁 Script executed:

#!/antml/bash
# Most importantly - check if this code actually compiles without errors
# by looking at what type the plugin object has when pushed
python3 << 'EOF'
# The issue is clear:
# 1. finalConfig is typed as InlineConfig (from Vite)
# 2. InlineConfig.plugins is of type PluginOption[] | undefined
# 3. PluginOption in Vite 5 doesn't include configEnvironment
# 4. PluginOption in Vite 6+ does include configEnvironment
# 5. The code pushes a plugin with configEnvironment without type annotation

# This would cause a TypeScript error in Vite 5
print("ANALYSIS:")
print("=========")
print("1. build.ts does NOT import PluginOption type")
print("2. Plugin object is pushed to finalConfig.plugins without type annotation")
print("3. The plugin object includes 'configEnvironment' property")
print("4. configEnvironment is Vite 6+ feature only")
print("5. Package.json supports Vite ^5.0.0 || ^6.0.0 || ^7.0.0")
print()
print("PROBLEM:")
print("When using Vite 5, TypeScript would report:")
print("  Property 'configEnvironment' does not exist on type 'Plugin'")
print()
print("SOLUTIONS:")
print("1. Add explicit type cast: (plugin as PluginOption)")
print("2. Use 'satisfies PluginOption' to validate")
print("3. Add version check to only include configEnvironment for Vite 6+")
print("4. Import and use PluginOption type for the object")
EOF

Repository: storybookjs/storybook

Length of output: 708


Add type safety to configEnvironment hook or implement version-aware inclusion.

The configEnvironment hook is a Vite 6+ feature that will cause TypeScript type errors when the code runs with Vite 5 (which is in peer dependencies: ^5.0.0 || ^6.0.0 || ^7.0.0). The plugin object at lines 47–64 includes configEnvironment without type annotation, and InlineConfig.plugins expects PluginOption[].

When Vite 5 is used, TypeScript will report: "Property 'configEnvironment' does not exist on type 'Plugin'".

Either:

  • Cast the plugin object: { ... } as PluginOption (import from 'vite')
  • Conditionally include configEnvironment only for Vite 6+ by checking the Vite version at runtime
  • Use satisfies PluginOption for type validation
🤖 Prompt for AI Agents
In `@code/builders/builder-vite/src/build.ts` around lines 56 - 63, The plugin
object currently includes the Vite 6+ hook configEnvironment which causes
TypeScript errors under Vite 5; update the plugin creation to be type-safe by
either casting the plugin object to PluginOption (import PluginOption from
'vite') or by only adding configEnvironment when Vite >= 6 at runtime, or use
the TypeScript satisfies PluginOption assertion; target the object that defines
configEnvironment (the anonymous plugin passed into InlineConfig.plugins) and
apply one of these approaches so TypeScript accepts the plugin across the
supported Vite versions.

@valentinpalkovic valentinpalkovic added the patch:yes Bugfix & documentation PR that need to be picked to main branch label Feb 2, 2026
@valentinpalkovic valentinpalkovic merged commit d349bf8 into next Feb 2, 2026
122 of 128 checks passed
@valentinpalkovic valentinpalkovic deleted the valentin/ensure-consistent-build-output branch February 2, 2026 15:18
valentinpalkovic added a commit that referenced this pull request Feb 4, 2026
…-build-output

Builder-Vite: Add plugin to enforce Storybook's output directory in Vite build configuration
(cherry picked from commit d349bf8)
@github-actions github-actions Bot added the patch:done Patch/release PRs already cherry-picked to main/release branch label Feb 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug ci:normal patch:done Patch/release PRs already cherry-picked to main/release branch patch:yes Bugfix & documentation PR that need to be picked to main branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Storybook 10 Build Artifacts Redirected to Nitro Output in TanStack Start Projects

2 participants