Skip to content

CLI: Drop contextual dependencies in create-storybook#32134

Merged
ndelangen merged 8 commits into
storybookjs:nextfrom
43081j:config-files-context
Sep 18, 2025
Merged

CLI: Drop contextual dependencies in create-storybook#32134
ndelangen merged 8 commits into
storybookjs:nextfrom
43081j:config-files-context

Conversation

@43081j
Copy link
Copy Markdown
Contributor

@43081j 43081j commented Jul 27, 2025

We've had this TODO for a while. Basically, we no longer pull
dependencies from the context, so we don't need to mock it or populate
it anymore.

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

Ensure the config resolution/loading still works in a created project.

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.

Greptile Summary

This PR refactors the dependency management system in Storybook's create-storybook module by removing contextual dependencies. The main change is in the vitestConfigFiles check, where the code previously validated that required dependencies (babel, findUp, fs) were present in a runtime context object before proceeding with configuration validation.

The refactored implementation directly imports and uses these dependencies at the module level, eliminating the need for runtime context validation. This simplifies the code by removing 13 lines of dependency validation logic and associated error handling. The core functionality for validating Vitest workspace and configuration files remains unchanged - the system still parses configuration files using Babel, searches for workspace files using findUp, and reads file contents using the fs module.

The corresponding test file has been updated to use proper Vitest mocking (vi.mock()) instead of the previous context-based mocking approach. The mockContext is now empty since dependencies are no longer passed through it. This change aligns with modern JavaScript/TypeScript practices where dependencies are resolved at import time rather than being injected through context objects.

Confidence score: 2/5

  • This PR has a significant issue that could cause runtime failures in production

  • The babel dependency is imported in the implementation but may not be properly mocked in tests, potentially causing test failures

  • The test file removes babel from the context but doesn't ensure babel parsing functionality is properly tested

  • Files that need more attention: code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts

Summary by CodeRabbit

  • Refactor

    • Simplified Vitest config/workspace validation into a single, consistent flow with clearer compatibility outcomes.
    • Removed dependency-based gating so checks always run, improving reliability.
    • Consolidated result handling to report all reasons when incompatible.
  • Tests

    • Migrated to fully mocked tests, removing reliance on live environments and real file I/O.
    • Expanded coverage across multiple workspace and config scenarios (valid/invalid JSON, defineWorkspace syntax, config formats, and options).
    • Standardized the test suite for clearer, more maintainable testing.

@43081j 43081j mentioned this pull request Jul 27, 2025
8 tasks
@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Jul 27, 2025

View your CI Pipeline Execution ↗ for commit 12ded7f

Command Status Duration Result
nx run-many -t build --parallel=3 ✅ Succeeded 48s View ↗

☁️ Nx Cloud last updated this comment at 2025-09-17 14:41:30 UTC

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, no comments

Edit Code Review Bot Settings | Greptile

@valentinpalkovic valentinpalkovic added addon: vitest cleanup Minor cleanup style change that won't show up in release changelog ci:normal labels Jul 28, 2025
@43081j 43081j force-pushed the config-files-context branch from 2a0b22f to 520b09c Compare July 28, 2025 14:55
@storybook-app-bot
Copy link
Copy Markdown

storybook-app-bot Bot commented Jul 28, 2025

Package Benchmarks

Commit: 12ded7f, ran on 17 September 2025 at 14:26:07 UTC

No significant changes detected, all good. 👏

@43081j
Copy link
Copy Markdown
Contributor Author

43081j commented Jul 28, 2025

the bundle analysis says we somehow gained 29KB 🤔

but clicking through to the treeemap - both look identical (and should be) 😅

@github-actions github-actions Bot added the Stale label Aug 10, 2025
We've had this TODO for a while. Basically, we no longer pull
dependencies from the context, so we don't need to mock it or populate
it anymore.
@43081j 43081j force-pushed the config-files-context branch from 520b09c to 5d00cfd Compare September 2, 2025 21:51
@43081j
Copy link
Copy Markdown
Contributor Author

43081j commented Sep 2, 2025

have caught this up, let me know if we can land this

its already dead code in that the condition is always truthy, so it should only be a patch change

@ndelangen ndelangen changed the title fix(create-storybook): drop contextual dependencies CLI: Drop contextual dependencies in create-storybook Sep 15, 2025
@ndelangen
Copy link
Copy Markdown
Member

@43081j Sorry for the disruption in the next branch.

Things should be stable now.
Would it be possible for you to look at the Ci failure?

@43081j
Copy link
Copy Markdown
Contributor Author

43081j commented Sep 16, 2025

no worries, this should pass now 👍

it does locally but will keep an eye on CI

…iles.test.ts`

- Adjusted the formatting of the `coerce` function to enhance clarity and maintainability.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 17, 2025

Walkthrough

Refactors vitestConfigFiles tests to a fully mock-driven suite and consolidates the implementation’s validation flow. The condition now unconditionally checks workspace and config files, aggregates incompatibility reasons, and returns a single compatibility result. Tests mock file discovery and reads, expanding scenario coverage without live filesystem or dependency gating.

Changes

Cohort / File(s) Summary
Vitest config check implementation
code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx
Removed dependency-gating; unified validation for workspace and config files; consolidated reason aggregation and single return; minor param rename context_context; retained babel-based parsing for config validation.
Vitest config tests (mock-driven)
code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
Rewrote tests to use vi mocks for file discovery and reads; removed live-context setup; added synchronous coerce; expanded scenarios (valid/invalid workspace, defineWorkspace, disallowed CJS, config/testConfig variations); centralized into one suite.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Runner as StepRunner
  participant Check as vitestConfigFiles.condition
  participant Finder as find.any
  participant FS as readFile
  participant Babel as Babel Parser

  Runner->>Check: invoke(state)
  Note over Check: Start unified validation flow

  Check->>Finder: locate vitest workspace file
  alt Workspace file found
    Check->>FS: read workspace file
    Check->>Check: validate workspace syntax/options
  else No workspace file
    Note over Check: Skip workspace checks
  end

  Check->>Finder: locate vitest.config.* file
  alt Config file found
    Check->>FS: read config file
    Check->>Babel: parse and inspect for safe DefineConfig
    Check->>Check: validate config/testConfig usage
  else No config file
    Note over Check: Skip config checks
  end

  Check->>Check: Aggregate reasons (if any)
  Check-->>Runner: COMPATIBLE or INCOMPATIBLE (+ reasons)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

In burrows of code, I twitch my nose,
Mocked files appear where the real one goes.
I nibble on reasons, compile them neat,
One flow to rule the checks complete.
Thump-thump—tests hop light and quick,
Configs aligned, compatibility slick.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly and accurately summarizes the primary change—dropping contextual dependency usage in create-storybook—which aligns with the PR diff and objectives describing the vitestConfigFiles refactor and test updates that remove context-based dependency resolution.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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: 3

🧹 Nitpick comments (5)
code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx (1)

91-99: Type the reasons array to avoid implicit any[]

Prevents accidental non-string pushes and improves type safety.

-    const reasons = [];
+    const reasons: string[] = [];
code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts (4)

7-9: Use spy: true and mock all required exports from empathic/find

Aligns with repo mocking rules and avoids accidental partial-module shape mismatches (e.g., find.up).

-vi.mock('empathic/find', () => ({
-  any: vi.fn(),
-}));
+vi.mock(
+  'empathic/find',
+  () => ({
+    any: vi.fn(),
+    up: vi.fn(),
+  }),
+  { spy: true }
+);

83-86: Make coerce return undefined when there is no match

Better models find.any semantics and avoids accidental “found” files in unrelated checks.

-const coerce =
-  (from: string, to: string) =>
-  ([name]: string[]) =>
-    name.includes(from) ? to : name;
+const coerce =
+  (from: string, to: string) =>
+  (names: string[]) =>
+    names.some((n) => n.includes(from)) ? to : undefined;

92-97: Reset mocks between tests

Prevents cross-test leakage and adheres to our mocking rules.

 describe('vitestConfigFiles', () => {
+  beforeEach(() => {
+    vi.mocked(find.any).mockReset();
+  });
+  afterEach(() => {
+    vi.resetAllMocks();
+  });
   it('should run properly with mock dependencies', async () => {

100-101: Inline mock implementations in it-blocks

Prefer setting per-case implementations in a scoped beforeEach for each describe to comply with our rules; keeping as-is is functionally fine but deviates from the guideline.

Would you like me to push a small refactor that moves these mockImplementation calls into per-scope beforeEach blocks?

Also applies to: 118-119, 126-127, 137-138, 146-147, 155-156, 163-164, 172-173, 180-181

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5528525 and 12ded7f.

📒 Files selected for processing (2)
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts (3 hunks)
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
code/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

code/**/*.{test,spec}.{ts,tsx}: Place all test files under the code/ directory
Name test files as *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx

Files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/spy-mocking.mdc)

**/*.test.{ts,tsx,js,jsx}: Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests
Place all mocks at the top of the test file before any test cases
Use vi.mocked() to type and access mocked functions
Implement mock behaviors in beforeEach blocks
Mock all required dependencies that the test subject uses
Mock implementations should be placed in beforeEach blocks
Each mock implementation should return a Promise for async functions
Mock implementations should match the expected return type of the original function
Use vi.mocked() to access and implement mock behaviors
Mock all required properties and methods that the test subject uses
Avoid direct function mocking without vi.mocked()
Avoid mock implementations outside of beforeEach blocks
Avoid mocking without the spy: true option
Avoid inline mock implementations within test cases
Avoid mocking only a subset of required dependencies
Mock at the highest level of abstraction needed
Keep mock implementations simple and focused
Use type-safe mocking with vi.mocked()
Document complex mock behaviors
Group related mocks together

Files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
🧠 Learnings (15)
📓 Common learnings
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.184Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursorrules:0-0
Timestamp: 2025-09-17T08:11:04.275Z
Learning: Applies to code/vitest.workspace.ts : Keep and use the Vitest workspace configuration at code/vitest.workspace.ts
📚 Learning: 2025-09-17T08:11:47.184Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.184Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests

Applied to files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
📚 Learning: 2025-09-17T08:11:47.184Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.184Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mocked() to access and implement mock behaviors

Applied to files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
📚 Learning: 2025-09-17T08:11:47.184Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.184Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid inline mock implementations within test cases

Applied to files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
📚 Learning: 2025-09-17T08:11:04.275Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursorrules:0-0
Timestamp: 2025-09-17T08:11:04.275Z
Learning: Applies to code/vitest.workspace.ts : Keep and use the Vitest workspace configuration at code/vitest.workspace.ts

Applied to files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx
📚 Learning: 2025-09-17T08:11:47.184Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.184Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking only a subset of required dependencies

Applied to files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
📚 Learning: 2025-09-17T08:11:47.184Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.184Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid direct function mocking without vi.mocked()

Applied to files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
📚 Learning: 2025-09-17T08:11:47.184Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.184Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Keep mock implementations simple and focused

Applied to files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
📚 Learning: 2025-09-17T08:11:47.184Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.184Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mocked() to type and access mocked functions

Applied to files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
📚 Learning: 2025-09-17T08:11:47.184Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.184Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Group related mocks together

Applied to files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
📚 Learning: 2025-09-17T08:11:47.184Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.184Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Document complex mock behaviors

Applied to files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
📚 Learning: 2025-09-17T08:11:47.184Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.184Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use type-safe mocking with vi.mocked()

Applied to files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
📚 Learning: 2025-09-17T08:11:47.184Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.184Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Implement mock behaviors in beforeEach blocks

Applied to files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
📚 Learning: 2025-09-17T08:11:47.184Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.184Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required properties and methods that the test subject uses

Applied to files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
📚 Learning: 2025-09-17T08:11:47.184Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.184Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required dependencies that the test subject uses

Applied to files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
🧬 Code graph analysis (2)
code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts (1)
code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx (1)
  • vitestConfigFiles (90-138)
code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx (1)
code/core/src/common/utils/paths.ts (1)
  • getProjectRoot (11-57)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (1)
code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx (1)

134-137: Return path simplification LGTM

Clear, single decision point; reads well.

Comment on lines +71 to +79
vi.mock(import('node:fs/promises'), async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
readFile: vi
.fn()
.mockImplementation((filePath) => fileMocks[filePath as keyof typeof fileMocks]),
};
});
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

Invalid vi.mock target and non-async readFile mock

First arg must be the module id string. Also return a Promise for async APIs and use spy: true per rules.

-vi.mock(import('node:fs/promises'), async (importOriginal) => {
-  const actual = await importOriginal();
-  return {
-    ...actual,
-    readFile: vi
-      .fn()
-      .mockImplementation((filePath) => fileMocks[filePath as keyof typeof fileMocks]),
-  };
-});
+vi.mock(
+  'node:fs/promises',
+  async () => {
+    const actual = await vi.importActual<typeof import('node:fs/promises')>('node:fs/promises');
+    return {
+      ...actual,
+      readFile: vi.fn(async (filePath: string) =>
+        Promise.resolve(fileMocks[filePath as keyof typeof fileMocks])
+      ),
+    };
+  },
+  { spy: true }
+);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
vi.mock(import('node:fs/promises'), async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
readFile: vi
.fn()
.mockImplementation((filePath) => fileMocks[filePath as keyof typeof fileMocks]),
};
});
vi.mock(
'node:fs/promises',
async () => {
const actual = await vi.importActual<typeof import('node:fs/promises')>('node:fs/promises');
return {
...actual,
readFile: vi.fn(async (filePath: string) =>
Promise.resolve(fileMocks[filePath as keyof typeof fileMocks])
),
};
},
{ spy: true }
);
🤖 Prompt for AI Agents
In code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
around lines 71-79, the vi.mock call is using import(...) as the first arg and
returning a non-async readFile mock; change it to mock the module by its string
id ('node:fs/promises'), use vi.importActual to get the real implementation,
return an object shaped like the module (exporting the actual members) and
ensure readFile is mocked as an async function (use
vi.fn().mockResolvedValue(...) or mockImplementation returning a Promise) and
pass { spy: true } in the vi.mock options so spies are allowed; ensure the mock
factory returns the module object (or Promise of it) as required by Vitest.

Comment on lines +103 to +107
const fileContents = await fs.readFile(vitestWorkspaceFile, 'utf8');
if (!isValidWorkspaceConfigFile(fileContents, babel)) {
reasons.push(`Found an invalid workspace config file: ${vitestWorkspaceFile}`);
}
}
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

Harden workspace parsing with try/catch; drop unused babel arg

Avoids unhandled exceptions on unreadable/invalid files and removes the unused parameter.

-      const fileContents = await fs.readFile(vitestWorkspaceFile, 'utf8');
-      if (!isValidWorkspaceConfigFile(fileContents, babel)) {
-        reasons.push(`Found an invalid workspace config file: ${vitestWorkspaceFile}`);
-      }
+      try {
+        const fileContents = await fs.readFile(vitestWorkspaceFile, 'utf8');
+        if (!isValidWorkspaceConfigFile(fileContents)) {
+          reasons.push(`Found an invalid workspace config file: ${vitestWorkspaceFile}`);
+        }
+      } catch {
+        reasons.push(`Error reading/parsing workspace config file: ${vitestWorkspaceFile}`);
+      }

Outside this hunk, update the helper’s signature to match usage:

-export const isValidWorkspaceConfigFile: (fileContents: string, babel: any) => boolean = (
-  fileContents
-) => {
+export const isValidWorkspaceConfigFile = (fileContents: string): boolean => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const fileContents = await fs.readFile(vitestWorkspaceFile, 'utf8');
if (!isValidWorkspaceConfigFile(fileContents, babel)) {
reasons.push(`Found an invalid workspace config file: ${vitestWorkspaceFile}`);
}
}
try {
const fileContents = await fs.readFile(vitestWorkspaceFile, 'utf8');
if (!isValidWorkspaceConfigFile(fileContents)) {
reasons.push(`Found an invalid workspace config file: ${vitestWorkspaceFile}`);
}
} catch {
reasons.push(`Error reading/parsing workspace config file: ${vitestWorkspaceFile}`);
}
}
Suggested change
const fileContents = await fs.readFile(vitestWorkspaceFile, 'utf8');
if (!isValidWorkspaceConfigFile(fileContents, babel)) {
reasons.push(`Found an invalid workspace config file: ${vitestWorkspaceFile}`);
}
}
export const isValidWorkspaceConfigFile = (fileContents: string): boolean => {
🤖 Prompt for AI Agents
In code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx around
lines 103 to 107, wrap the fs.readFile + isValidWorkspaceConfigFile call in a
try/catch so unreadable or invalid files don't throw (on error push a
descriptive reason like `Unable to read/parse workspace config:
${vitestWorkspaceFile}`); remove the unused `babel` argument from the
isValidWorkspaceConfigFile call here and update the helper function's signature
and all its call sites elsewhere to drop that parameter so signatures and usages
remain consistent.

Comment on lines +109 to 132
const vitestConfigFile = find.any(
['ts', 'js', 'tsx', 'jsx', 'cts', 'cjs', 'mts', 'mjs'].map((ex) => `vitest.config.${ex}`),
{ cwd: state.directory, last: projectRoot }
);
if (vitestConfigFile?.endsWith('.cts') || vitestConfigFile?.endsWith('.cjs')) {
reasons.push(`Cannot auto-update CommonJS config file: ${vitestConfigFile}`);
} else if (vitestConfigFile) {
let isValidVitestConfig = false;
const configContent = await fs.readFile(vitestConfigFile, 'utf8');
const parsedConfig = babel.babelParse(configContent);
babel.traverse(parsedConfig, {
ExportDefaultDeclaration(path) {
if (
isDefineConfigExpression(path.node.declaration) &&
isSafeToExtendWorkspace(path.node.declaration as CallExpression)
) {
isValidVitestConfig = true;
}
},
});
if (!isValidVitestConfig) {
reasons.push(`Found an invalid Vitest config file: ${vitestConfigFile}`);
}

return reasons.length
? { type: CompatibilityType.INCOMPATIBLE, reasons }
: { type: CompatibilityType.COMPATIBLE };
}
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

Guard Vitest config read/parse with try/catch

Prevents crashes on IO/parse errors and surfaces actionable reasons.

-    } else if (vitestConfigFile) {
-      let isValidVitestConfig = false;
-      const configContent = await fs.readFile(vitestConfigFile, 'utf8');
-      const parsedConfig = babel.babelParse(configContent);
-      babel.traverse(parsedConfig, {
-        ExportDefaultDeclaration(path) {
-          if (
-            isDefineConfigExpression(path.node.declaration) &&
-            isSafeToExtendWorkspace(path.node.declaration as CallExpression)
-          ) {
-            isValidVitestConfig = true;
-          }
-        },
-      });
-      if (!isValidVitestConfig) {
-        reasons.push(`Found an invalid Vitest config file: ${vitestConfigFile}`);
-      }
-    }
+    } else if (vitestConfigFile) {
+      try {
+        let isValidVitestConfig = false;
+        const configContent = await fs.readFile(vitestConfigFile, 'utf8');
+        const parsedConfig = babel.babelParse(configContent);
+        babel.traverse(parsedConfig, {
+          ExportDefaultDeclaration(path) {
+            if (
+              isDefineConfigExpression(path.node.declaration) &&
+              isSafeToExtendWorkspace(path.node.declaration as CallExpression)
+            ) {
+              isValidVitestConfig = true;
+            }
+          },
+        });
+        if (!isValidVitestConfig) {
+          reasons.push(`Found an invalid Vitest config file: ${vitestConfigFile}`);
+        }
+      } catch {
+        reasons.push(`Error reading/parsing Vitest config file: ${vitestConfigFile}`);
+      }
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const vitestConfigFile = find.any(
['ts', 'js', 'tsx', 'jsx', 'cts', 'cjs', 'mts', 'mjs'].map((ex) => `vitest.config.${ex}`),
{ cwd: state.directory, last: projectRoot }
);
if (vitestConfigFile?.endsWith('.cts') || vitestConfigFile?.endsWith('.cjs')) {
reasons.push(`Cannot auto-update CommonJS config file: ${vitestConfigFile}`);
} else if (vitestConfigFile) {
let isValidVitestConfig = false;
const configContent = await fs.readFile(vitestConfigFile, 'utf8');
const parsedConfig = babel.babelParse(configContent);
babel.traverse(parsedConfig, {
ExportDefaultDeclaration(path) {
if (
isDefineConfigExpression(path.node.declaration) &&
isSafeToExtendWorkspace(path.node.declaration as CallExpression)
) {
isValidVitestConfig = true;
}
},
});
if (!isValidVitestConfig) {
reasons.push(`Found an invalid Vitest config file: ${vitestConfigFile}`);
}
return reasons.length
? { type: CompatibilityType.INCOMPATIBLE, reasons }
: { type: CompatibilityType.COMPATIBLE };
}
const vitestConfigFile = find.any(
['ts', 'js', 'tsx', 'jsx', 'cts', 'cjs', 'mts', 'mjs'].map((ex) => `vitest.config.${ex}`),
{ cwd: state.directory, last: projectRoot }
);
if (vitestConfigFile?.endsWith('.cts') || vitestConfigFile?.endsWith('.cjs')) {
reasons.push(`Cannot auto-update CommonJS config file: ${vitestConfigFile}`);
} else if (vitestConfigFile) {
try {
let isValidVitestConfig = false;
const configContent = await fs.readFile(vitestConfigFile, 'utf8');
const parsedConfig = babel.babelParse(configContent);
babel.traverse(parsedConfig, {
ExportDefaultDeclaration(path) {
if (
isDefineConfigExpression(path.node.declaration) &&
isSafeToExtendWorkspace(path.node.declaration as CallExpression)
) {
isValidVitestConfig = true;
}
},
});
if (!isValidVitestConfig) {
reasons.push(`Found an invalid Vitest config file: ${vitestConfigFile}`);
}
} catch {
reasons.push(`Error reading/parsing Vitest config file: ${vitestConfigFile}`);
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

addon: vitest ci:normal cleanup Minor cleanup style change that won't show up in release changelog needs triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants