From 3abe7f6b787563e90c207c97c3035508830c1d60 Mon Sep 17 00:00:00 2001 From: Braeden Foster Date: Tue, 3 Feb 2026 11:54:16 +1300 Subject: [PATCH 1/4] fix(core): resolve builder preset path correctly in pnpm strict mode Use resolvePackageDir instead of dirname when resolving builder preset paths. This fixes preset resolution in pnpm monorepos with shamefully-hoist=false. The issue occurs because dirname('@storybook/builder-vite') returns '@storybook', which is not a valid package path. Using resolvePackageDir properly resolves the full package directory path, allowing the preset.js file to be found. Fixes #33748 --- code/core/src/core-server/load.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/core/src/core-server/load.ts b/code/core/src/core-server/load.ts index 0c0d716dce37..d098e1a10768 100644 --- a/code/core/src/core-server/load.ts +++ b/code/core/src/core-server/load.ts @@ -68,7 +68,7 @@ export async function loadStorybook( const builderName = typeof builder === 'string' ? builder : builder?.name; if (builderName) { - corePresets.push(join(dirname(builderName), 'preset.js')); + corePresets.push(join(resolvePackageDir(builderName), 'preset.js')); } // Load second pass: all presets are applied in order From d064811ce014d7f894ee4a5263281c311bb69bb7 Mon Sep 17 00:00:00 2001 From: Braeden Foster Date: Fri, 6 Mar 2026 07:37:39 +1300 Subject: [PATCH 2/4] fix(core): add require.resolve fallback and clean up unused import - Add require.resolve fallback in resolvePackageDir for strict pnpm environments where import.meta.resolve may fail - Remove unused dirname import from load.ts Co-Authored-By: Claude Opus 4.6 --- code/core/src/core-server/load.ts | 2 +- code/core/src/shared/utils/module.ts | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/code/core/src/core-server/load.ts b/code/core/src/core-server/load.ts index d098e1a10768..867cc0518a17 100644 --- a/code/core/src/core-server/load.ts +++ b/code/core/src/core-server/load.ts @@ -11,7 +11,7 @@ import type { BuilderOptions, CLIOptions, LoadOptions, Options } from 'storybook import { global } from '@storybook/global'; -import { dirname, join, relative, resolve } from 'pathe'; +import { join, relative, resolve } from 'pathe'; import { resolvePackageDir } from '../shared/utils/module'; diff --git a/code/core/src/shared/utils/module.ts b/code/core/src/shared/utils/module.ts index 7183241abdf0..ebe640d0e2c9 100644 --- a/code/core/src/shared/utils/module.ts +++ b/code/core/src/shared/utils/module.ts @@ -32,8 +32,14 @@ export const resolvePackageDir = ( try { return dirname(fileURLToPath(importMetaResolve(join(pkg, 'package.json'), parent))); } catch { - // Necessary fallback for Bun runtime - return dirname(fileURLToPath(importMetaResolve(join(pkg, 'package.json')))); + try { + // Necessary fallback for Bun runtime + return dirname(fileURLToPath(importMetaResolve(join(pkg, 'package.json')))); + } catch { + // Fallback using require.resolve for strict pnpm environments where import.meta.resolve may fail + const require = createRequire(import.meta.url); + return dirname(require.resolve(join(pkg, 'package.json'))); + } } }; From b8fb9c03c85c7105a586db0da3dedf11dab4b999 Mon Sep 17 00:00:00 2001 From: Braeden Foster Date: Fri, 6 Mar 2026 08:25:13 +1300 Subject: [PATCH 3/4] fix(core): handle resolved builder paths in preset resolution - Add support for file URLs and absolute paths in builder name resolution - Check if builder name is already resolved before calling resolvePackageDir - Use dirname for resolved paths, resolvePackageDir for bare package names - Import dirname and isAbsolute from pathe for path detection --- code/core/src/core-server/load.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/code/core/src/core-server/load.ts b/code/core/src/core-server/load.ts index 867cc0518a17..28b198bc04c4 100644 --- a/code/core/src/core-server/load.ts +++ b/code/core/src/core-server/load.ts @@ -11,7 +11,7 @@ import type { BuilderOptions, CLIOptions, LoadOptions, Options } from 'storybook import { global } from '@storybook/global'; -import { join, relative, resolve } from 'pathe'; +import { dirname, isAbsolute, join, relative, resolve } from 'pathe'; import { resolvePackageDir } from '../shared/utils/module'; @@ -68,7 +68,13 @@ export async function loadStorybook( const builderName = typeof builder === 'string' ? builder : builder?.name; if (builderName) { - corePresets.push(join(resolvePackageDir(builderName), 'preset.js')); + /* builderName can be a bare package name (e.g. '@storybook/builder-vite') or an already-resolved + file URL / absolute path (e.g. 'file:///.../.../dist/index.js'). For bare package names, we + need to resolve the package directory first; for already-resolved paths, dirname works directly. + */ + const isResolved = builderName.startsWith('file:') || isAbsolute(builderName); + const builderPresetDir = isResolved ? dirname(builderName) : resolvePackageDir(builderName); + corePresets.push(join(builderPresetDir, 'preset.js')); } // Load second pass: all presets are applied in order From 370e8d9a2520735e4886aa91a5673fd863379598 Mon Sep 17 00:00:00 2001 From: Braeden Foster Date: Fri, 6 Mar 2026 09:03:39 +1300 Subject: [PATCH 4/4] fix(core): use parent context in require.resolve fallback for pnpm environments - Use `parent` parameter in `createRequire()` to maintain correct module resolution context - Rename `require` to `req` to avoid shadowing the global require function --- code/core/src/shared/utils/module.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/core/src/shared/utils/module.ts b/code/core/src/shared/utils/module.ts index ebe640d0e2c9..960e3fcef645 100644 --- a/code/core/src/shared/utils/module.ts +++ b/code/core/src/shared/utils/module.ts @@ -37,8 +37,8 @@ export const resolvePackageDir = ( return dirname(fileURLToPath(importMetaResolve(join(pkg, 'package.json')))); } catch { // Fallback using require.resolve for strict pnpm environments where import.meta.resolve may fail - const require = createRequire(import.meta.url); - return dirname(require.resolve(join(pkg, 'package.json'))); + const req = createRequire(parent ?? import.meta.url); + return dirname(req.resolve(join(pkg, 'package.json'))); } } };