Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 26 additions & 6 deletions code/core/src/common/js-package-manager/JsPackageManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { dirname, isAbsolute, join, normalize, resolve } from 'node:path';

import { logger, prompt } from 'storybook/internal/node-logger';

import detectIndent from 'detect-indent';
Comment thread
y-hsgw marked this conversation as resolved.
import * as find from 'empathic/find';
// eslint-disable-next-line depend/ban-dependencies
import { type ExecaChildProcess } from 'execa';
Expand All @@ -29,6 +30,12 @@ export enum PackageManagerName {

type StorybookPackage = keyof typeof storybookPackagesVersions;

const indentSymbol = Symbol('indent');

type PackageJsonWithIndent = PackageJsonWithDepsAndDevDeps & {
[indentSymbol]?: any;
};

/**
* Extract package name and version from input
*
Expand Down Expand Up @@ -85,7 +92,7 @@ export abstract class JsPackageManager {
static readonly installedVersionCache = new Map<string, string | null>();

/** Cache for package.json files to avoid repeated file system calls. */
static readonly packageJsonCache = new Map<string, PackageJsonWithDepsAndDevDeps>();
static readonly packageJsonCache = new Map<string, PackageJsonWithIndent>();

constructor(options?: JsPackageManagerOptions) {
this.cwd = options?.cwd || process.cwd();
Expand Down Expand Up @@ -164,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)
Expand All @@ -181,8 +188,10 @@ 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 = {
const result: PackageJsonWithIndent = {
...packageJSON,
dependencies: { ...(packageJSON.dependencies || {}) },
devDependencies: { ...(packageJSON.devDependencies || {}) },
Expand All @@ -195,6 +204,15 @@ export abstract class JsPackageManager {
return result;
}

#getIndent(filePath: string): string | number {
try {
const packageJson = JsPackageManager.getPackageJson(filePath);
return packageJson[indentSymbol];
} catch (e) {
return 2;
}
}

writePackageJson(packageJson: PackageJson, directory = this.cwd) {
const packageJsonToWrite = { ...packageJson };
const dependencyTypes = ['dependencies', 'devDependencies', 'peerDependencies'] as const;
Expand All @@ -205,19 +223,21 @@ export abstract class JsPackageManager {
delete packageJsonToWrite[type];
}
});

const filePath = join(directory, 'package.json');
Comment thread
Sidnioulz marked this conversation as resolved.
const indent = this.#getIndent(filePath);
const packageJsonPath = normalize(resolve(directory, 'package.json'));
const content = `${JSON.stringify(packageJsonToWrite, null, 2)}\n`;
const content = `${JSON.stringify(packageJsonToWrite, null, indent)}\n`;
writeFileSync(packageJsonPath, content, 'utf8');

// 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);
}

Expand Down
Loading