From 35fe4670a49a72bd04144043d8a5aeb23adb9088 Mon Sep 17 00:00:00 2001 From: Hasegawa-Yukihiro Date: Sat, 16 Aug 2025 17:06:52 +0900 Subject: [PATCH 1/8] fix: preserve original package.json indent when upgrading --- .../core/src/common/js-package-manager/JsPackageManager.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/code/core/src/common/js-package-manager/JsPackageManager.ts b/code/core/src/common/js-package-manager/JsPackageManager.ts index 0bf7834ae79c..fc14a6695cdb 100644 --- a/code/core/src/common/js-package-manager/JsPackageManager.ts +++ b/code/core/src/common/js-package-manager/JsPackageManager.ts @@ -3,6 +3,7 @@ import { dirname, isAbsolute, join, resolve } from 'node:path'; import { logger, prompt } from 'storybook/internal/node-logger'; +import detectIndent from 'detect-indent'; // eslint-disable-next-line depend/ban-dependencies import { type CommonOptions, type ExecaChildProcess, execa, execaCommandSync } from 'execa'; import { findUpMultipleSync, findUpSync } from 'find-up'; @@ -184,8 +185,10 @@ export abstract class JsPackageManager { delete packageJsonToWrite[type]; } }); - - const content = `${JSON.stringify(packageJsonToWrite, null, 2)}\n`; + const filePath = join(directory, 'package.json'); + const contentJson = readFileSync(filePath, 'utf-8'); + const { indent } = detectIndent(contentJson); + const content = `${JSON.stringify(packageJsonToWrite, null, indent)}\n`; writeFileSync(resolve(directory, 'package.json'), content, 'utf8'); } From 42f1325bdc1390b20d938fc743605c4a5a6e7da7 Mon Sep 17 00:00:00 2001 From: Hasegawa-Yukihiro Date: Mon, 18 Aug 2025 23:47:10 +0900 Subject: [PATCH 2/8] feat: add getIndent helper to safely detect package.json indent with fallback to 2 --- .../common/js-package-manager/JsPackageManager.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/code/core/src/common/js-package-manager/JsPackageManager.ts b/code/core/src/common/js-package-manager/JsPackageManager.ts index fc14a6695cdb..e928a3bbfac9 100644 --- a/code/core/src/common/js-package-manager/JsPackageManager.ts +++ b/code/core/src/common/js-package-manager/JsPackageManager.ts @@ -175,6 +175,16 @@ export abstract class JsPackageManager { }; } + #getIndent(filePath: string) { + try { + const content = readFileSync(filePath, 'utf-8'); + const { indent } = detectIndent(content); + return indent; + } catch (e) { + return 2; + } + } + writePackageJson(packageJson: PackageJson, directory = this.cwd) { const packageJsonToWrite = { ...packageJson }; const dependencyTypes = ['dependencies', 'devDependencies', 'peerDependencies'] as const; @@ -186,8 +196,7 @@ export abstract class JsPackageManager { } }); const filePath = join(directory, 'package.json'); - const contentJson = readFileSync(filePath, 'utf-8'); - const { indent } = detectIndent(contentJson); + const indent = this.#getIndent(filePath); const content = `${JSON.stringify(packageJsonToWrite, null, indent)}\n`; writeFileSync(resolve(directory, 'package.json'), content, 'utf8'); } From dae42129053c19800be6d13db04b62cda283dcd3 Mon Sep 17 00:00:00 2001 From: Hasegawa-Yukihiro Date: Thu, 21 Aug 2025 20:52:58 +0900 Subject: [PATCH 3/8] feat: Add indent detection for package.json and update PackageJson type to allow symbol keys --- .../src/common/js-package-manager/JsPackageManager.ts | 8 ++++---- code/core/src/types/modules/core-common.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/core/src/common/js-package-manager/JsPackageManager.ts b/code/core/src/common/js-package-manager/JsPackageManager.ts index e928a3bbfac9..dc01af71fb6e 100644 --- a/code/core/src/common/js-package-manager/JsPackageManager.ts +++ b/code/core/src/common/js-package-manager/JsPackageManager.ts @@ -166,6 +166,7 @@ export abstract class JsPackageManager { static getPackageJson(packageJsonPath: string): PackageJsonWithDepsAndDevDeps { const jsonContent = readFileSync(packageJsonPath, 'utf8'); const packageJSON = JSON.parse(jsonContent); + packageJSON[Symbol.for('indent')] = detectIndent(jsonContent).indent ?? 2; return { ...packageJSON, @@ -175,11 +176,10 @@ export abstract class JsPackageManager { }; } - #getIndent(filePath: string) { + #getIndent(filePath: string): string | number { try { - const content = readFileSync(filePath, 'utf-8'); - const { indent } = detectIndent(content); - return indent; + const packageJson = JsPackageManager.getPackageJson(filePath); + return packageJson[Symbol.for('indent')]; } catch (e) { return 2; } diff --git a/code/core/src/types/modules/core-common.ts b/code/core/src/types/modules/core-common.ts index caff102504df..69e612210bf4 100644 --- a/code/core/src/types/modules/core-common.ts +++ b/code/core/src/types/modules/core-common.ts @@ -147,7 +147,7 @@ export interface BuilderResult { stats?: Stats; } -export type PackageJson = PackageJsonFromTypeFest & Record; +export type PackageJson = PackageJsonFromTypeFest & Record; // TODO: This could be exported to the outside world and used in `options.ts` file of each `@storybook/APP` // like it's described in docs/api/new-frameworks.md From e05cdf8df1694c42f43ec8d4f9745920eccda6ae Mon Sep 17 00:00:00 2001 From: Hasegawa-Yukihiro Date: Tue, 30 Sep 2025 21:24:18 +0900 Subject: [PATCH 4/8] fix: lint errors --- code/core/src/csf-tools/CsfFile.test.ts | 3 ++- code/core/src/outline/outlineCSS.ts | 2 +- code/renderers/vue3/src/extractArgTypes.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/code/core/src/csf-tools/CsfFile.test.ts b/code/core/src/csf-tools/CsfFile.test.ts index 3a2bfb94dcc2..db4cfcbb8583 100644 --- a/code/core/src/csf-tools/CsfFile.test.ts +++ b/code/core/src/csf-tools/CsfFile.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it, vi } from 'vitest'; import { logger } from 'storybook/internal/node-logger'; +// eslint-disable-next-line depend/ban-dependencies import yaml from 'js-yaml'; import { dedent } from 'ts-dedent'; @@ -2799,7 +2800,7 @@ describe('CsfFile', () => { ` ) ).toThrowErrorMatchingInlineSnapshot(` - [MultipleMetaError: CSF: multiple meta objects + [MultipleMetaError: CSF: multiple meta objects More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export] `); diff --git a/code/core/src/outline/outlineCSS.ts b/code/core/src/outline/outlineCSS.ts index 502ecd884b8c..e3737a3331d4 100644 --- a/code/core/src/outline/outlineCSS.ts +++ b/code/core/src/outline/outlineCSS.ts @@ -4,7 +4,7 @@ import { dedent } from 'ts-dedent'; From pesticide v1.3.0 . @mrmrs . MIT */ export default function outlineCSS(selector: string) { - return dedent/* css */ ` + return dedent /* css */ ` ${selector} body { outline: 1px solid #2980b9 !important; } diff --git a/code/renderers/vue3/src/extractArgTypes.ts b/code/renderers/vue3/src/extractArgTypes.ts index e261c8060731..fc71e747dee4 100644 --- a/code/renderers/vue3/src/extractArgTypes.ts +++ b/code/renderers/vue3/src/extractArgTypes.ts @@ -349,7 +349,7 @@ const isEnumSchema = (schemas: PropertyMetaSchema[]): schemas is string[] => { * @example * * ``` - * true, false; + * (true, false); * ``` */ const isBooleanSchema = ( From cd925dfc8341e85a65207a548c0702a9ad452142 Mon Sep 17 00:00:00 2001 From: Hasegawa-Yukihiro Date: Tue, 30 Sep 2025 22:21:44 +0900 Subject: [PATCH 5/8] test: resolve failing tests --- code/core/src/csf-tools/CsfFile.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/core/src/csf-tools/CsfFile.test.ts b/code/core/src/csf-tools/CsfFile.test.ts index db4cfcbb8583..17487fea5750 100644 --- a/code/core/src/csf-tools/CsfFile.test.ts +++ b/code/core/src/csf-tools/CsfFile.test.ts @@ -2800,7 +2800,7 @@ describe('CsfFile', () => { ` ) ).toThrowErrorMatchingInlineSnapshot(` - [MultipleMetaError: CSF: multiple meta objects + [MultipleMetaError: CSF: multiple meta objects More info: https://storybook.js.org/docs/writing-stories?ref=error#default-export] `); From 3830ed04284a8b5fb29579f3d3db257cffdca299 Mon Sep 17 00:00:00 2001 From: Hasegawa-Yukihiro Date: Wed, 10 Dec 2025 21:27:52 +0900 Subject: [PATCH 6/8] refactor: Use a local constant instead of Symbol.for for the shared identifier. --- code/core/src/common/js-package-manager/JsPackageManager.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/core/src/common/js-package-manager/JsPackageManager.ts b/code/core/src/common/js-package-manager/JsPackageManager.ts index 466eba09fcb4..d53b2517a71b 100644 --- a/code/core/src/common/js-package-manager/JsPackageManager.ts +++ b/code/core/src/common/js-package-manager/JsPackageManager.ts @@ -30,6 +30,8 @@ export enum PackageManagerName { type StorybookPackage = keyof typeof storybookPackagesVersions; +const indentSymbol = Symbol('indent'); + /** * Extract package name and version from input * @@ -182,7 +184,7 @@ export abstract class JsPackageManager { // Read from disk if not in cache const jsonContent = readFileSync(absolutePath, 'utf8'); const packageJSON = JSON.parse(jsonContent); - packageJSON[Symbol.for('indent')] = detectIndent(jsonContent).indent ?? 2; + packageJSON[indentSymbol] = detectIndent(jsonContent).indent ?? 2; const result: PackageJsonWithDepsAndDevDeps = { ...packageJSON, @@ -200,7 +202,7 @@ export abstract class JsPackageManager { #getIndent(filePath: string): string | number { try { const packageJson = JsPackageManager.getPackageJson(filePath); - return packageJson[Symbol.for('indent')]; + return packageJson[indentSymbol]; } catch (e) { return 2; } From dd0989b6a6a0bacc3f66e68d7e2df2097779873f Mon Sep 17 00:00:00 2001 From: Hasegawa-Yukihiro Date: Wed, 10 Dec 2025 21:30:00 +0900 Subject: [PATCH 7/8] chore: add comment --- code/core/src/common/js-package-manager/JsPackageManager.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/code/core/src/common/js-package-manager/JsPackageManager.ts b/code/core/src/common/js-package-manager/JsPackageManager.ts index d53b2517a71b..35350393e937 100644 --- a/code/core/src/common/js-package-manager/JsPackageManager.ts +++ b/code/core/src/common/js-package-manager/JsPackageManager.ts @@ -184,6 +184,7 @@ export abstract class JsPackageManager { // Read from disk if not in cache const jsonContent = readFileSync(absolutePath, 'utf8'); const packageJSON = JSON.parse(jsonContent); + // Symbol key keeps this metadata non-enumerable so JSON.stringify omits it packageJSON[indentSymbol] = detectIndent(jsonContent).indent ?? 2; const result: PackageJsonWithDepsAndDevDeps = { From a5da7084522ad4e1598865d5eb696c9e08c140e5 Mon Sep 17 00:00:00 2001 From: Hasegawa-Yukihiro Date: Wed, 10 Dec 2025 21:42:43 +0900 Subject: [PATCH 8/8] =?UTF-8?q?refactor:=20Use=20a=20local=20indent=20symb?= =?UTF-8?q?ol=20type=20while=20keeping=20PackageJson=20string=E2=80=91inde?= =?UTF-8?q?xed=20only.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/js-package-manager/JsPackageManager.ts | 13 +++++++++---- code/core/src/types/modules/core-common.ts | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/code/core/src/common/js-package-manager/JsPackageManager.ts b/code/core/src/common/js-package-manager/JsPackageManager.ts index 35350393e937..72e97894a055 100644 --- a/code/core/src/common/js-package-manager/JsPackageManager.ts +++ b/code/core/src/common/js-package-manager/JsPackageManager.ts @@ -32,6 +32,10 @@ type StorybookPackage = keyof typeof storybookPackagesVersions; const indentSymbol = Symbol('indent'); +type PackageJsonWithIndent = PackageJsonWithDepsAndDevDeps & { + [indentSymbol]?: any; +}; + /** * Extract package name and version from input * @@ -88,7 +92,7 @@ export abstract class JsPackageManager { static readonly installedVersionCache = new Map(); /** Cache for package.json files to avoid repeated file system calls. */ - static readonly packageJsonCache = new Map(); + static readonly packageJsonCache = new Map(); constructor(options?: JsPackageManagerOptions) { this.cwd = options?.cwd || process.cwd(); @@ -167,7 +171,7 @@ export abstract class JsPackageManager { } /** Read the `package.json` file available in the provided directory */ - static getPackageJson(packageJsonPath: string): PackageJsonWithDepsAndDevDeps { + static getPackageJson(packageJsonPath: string): PackageJsonWithIndent { // Normalize path to absolute for consistent cache keys // Always use resolve() to ensure consistent format on Windows // (handles drive letter casing and path separator differences) @@ -187,7 +191,7 @@ export abstract class JsPackageManager { // Symbol key keeps this metadata non-enumerable so JSON.stringify omits it packageJSON[indentSymbol] = detectIndent(jsonContent).indent ?? 2; - const result: PackageJsonWithDepsAndDevDeps = { + const result: PackageJsonWithIndent = { ...packageJSON, dependencies: { ...(packageJSON.dependencies || {}) }, devDependencies: { ...(packageJSON.devDependencies || {}) }, @@ -227,12 +231,13 @@ export abstract class JsPackageManager { // Update cache with the written content // Ensure dependencies and devDependencies exist (even if empty) to match PackageJsonWithDepsAndDevDeps type - const cachedPackageJson: PackageJsonWithDepsAndDevDeps = { + const cachedPackageJson: PackageJsonWithIndent = { ...packageJsonToWrite, dependencies: { ...(packageJsonToWrite.dependencies || {}) }, devDependencies: { ...(packageJsonToWrite.devDependencies || {}) }, peerDependencies: { ...(packageJsonToWrite.peerDependencies || {}) }, }; + cachedPackageJson[indentSymbol] = indent; JsPackageManager.packageJsonCache.set(packageJsonPath, cachedPackageJson); } diff --git a/code/core/src/types/modules/core-common.ts b/code/core/src/types/modules/core-common.ts index 17d982a08dc6..3523f60f7f0c 100644 --- a/code/core/src/types/modules/core-common.ts +++ b/code/core/src/types/modules/core-common.ts @@ -155,7 +155,7 @@ export interface BuilderResult { stats?: Stats; } -export type PackageJson = PackageJsonFromTypeFest & Record; +export type PackageJson = PackageJsonFromTypeFest & Record; // TODO: This could be exported to the outside world and used in `options.ts` file of each `@storybook/APP` // like it's described in docs/api/new-frameworks.md