Skip to content

Conversation

@google-labs-jules
Copy link
Contributor

@google-labs-jules google-labs-jules bot commented Dec 30, 2025

⚡ Bolt: Parallelize file compression and use async IO

💡 What:

  • Updated compressArrayOfFiles in @node-minify/core to use Promise.all for processing files in parallel instead of a serial for loop.
  • Added getContentFromFilesAsync to @node-minify/utils to allow non-blocking file reading.
  • Updated compressSingleFile to use async reading as well.

🎯 Why:

  • The previous implementation processed files sequentially (Read -> Minify -> Write -> Read -> ...).
  • Minification tasks are often IO-bound (reading/writing) or CPU-bound (minifying) but independent of each other.
  • Running them in parallel utilizes system resources better and significantly reduces total build time for projects with multiple files.

📊 Impact:

  • ~16x faster in a synthetic benchmark compressing 20 files with a simulated 50ms minification task (Sequential: ~1000ms -> Parallel: ~60ms).
  • Async file reading ensures the event loop is not blocked during large file reads.

🔬 Measurement:

  • Verified with packages/core/__tests__/compress.perf.test.ts (created during development, results in PR description).
  • Verified existing tests pass to ensure no regression.

PR created automatically by Jules for task 1297584749452239615 started by @srod

Summary by CodeRabbit

  • New Features

    • File content retrieval and validation now support asynchronous operations; compression flows updated to use async content processing.
  • Tests

    • Added tests covering async file operations and async compression behaviors, including error and edge-case scenarios.
  • Chores

    • Updated ignore patterns and added a project journal/notes markdown file.

✏️ Tip: You can customize this high-level summary in your review settings.

@google-labs-jules
Copy link
Contributor Author

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!


For security, I will only act on instructions from the user who triggered this task.

New to Jules? Learn more at jules.google/docs.

@changeset-bot
Copy link

changeset-bot bot commented Dec 30, 2025

⚠️ No Changeset found

Latest commit: 6e751ff

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

📝 Walkthrough

Walkthrough

Adds async file I/O helpers and integrates them into the compression pipeline: synchronous file reads replaced with async variants, per-file processing runs in parallel via Promise.all, tests and public exports updated accordingly. (50 words)

Changes

Cohort / File(s) Summary
Config & Metadata
\.gitignore, \.jules/bolt.md
Adds packages/utils/__tests__/temp_perf/ to .gitignore and creates .jules/bolt.md.
Core compression flow
packages/core/src/compress.ts, packages/core/__tests__/compress_async.test.ts
Replaces synchronous per-item content retrieval with async tasks; maps files to async workers and uses Promise.all; adds async compress tests including error cases.
Per-file compression update
packages/utils/src/compressSingleFile.ts
Converts content determination to async: determineContent now returns a Promise and awaits getContentFromFilesAsync and other async reads.
Async file utilities
packages/utils/src/getContentFromFiles.ts, packages/utils/src/isValidFile.ts, packages/utils/src/index.ts
Adds getContentFromFilesAsync and isValidFileAsync using fs/promises; retains sync APIs; re-exports new async functions in index.ts.
Async utility tests
packages/utils/__tests__/getContentFromFilesAsync.test.ts, packages/utils/__tests__/isValidFileAsync.test.ts, packages/utils/__tests__/utils.test.ts
Adds comprehensive tests for async helpers (single/multiple files, empty input, non-existent paths, directories, error propagation) and expands sync tests coverage.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant CompressModule as compress.ts
    participant Parallel as Promise.all
    participant ContentReader as getContentFromFilesAsync
    participant FS as fs/promises

    rect rgb(240,248,255)
    Note over Caller,FS: Async parallel file-read + compress flow
    Caller->>CompressModule: compressArrayOfFiles(files)
    CompressModule->>CompressModule: map files -> async tasks (compressSingleFile)
    CompressModule->>Parallel: Promise.all(tasks)
    par file reads in parallel
        Parallel->>ContentReader: read file N
        ContentReader->>FS: lstat(path) / readFile(path)
        FS-->>ContentReader: content / errors
        ContentReader-->>Parallel: resolved content
    end
    Parallel-->>CompressModule: all file contents
    CompressModule->>Caller: compressed result or thrown error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through code both near and far,
Async carrots stacked beneath the star,
Promise.all sings while readers sprint,
Files join up in a single glint.
Bolt nibbles bytes and winks — what a hint!

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 accurately summarizes the main change: parallelizing file compression and implementing async IO operations across the codebase.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4491e1 and 6e751ff.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • .gitignore
  • .jules/bolt.md
  • packages/core/__tests__/compress_async.test.ts
  • packages/core/src/compress.ts
  • packages/utils/__tests__/getContentFromFilesAsync.test.ts
  • packages/utils/__tests__/isValidFileAsync.test.ts
  • packages/utils/__tests__/utils.test.ts
  • packages/utils/src/compressSingleFile.ts
  • packages/utils/src/getContentFromFiles.ts
  • packages/utils/src/index.ts
  • packages/utils/src/isValidFile.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • .gitignore
  • packages/utils/tests/isValidFileAsync.test.ts
  • packages/utils/tests/getContentFromFilesAsync.test.ts
  • packages/utils/src/isValidFile.ts
  • packages/core/tests/compress_async.test.ts
  • packages/core/src/compress.ts
  • packages/utils/src/compressSingleFile.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use 4 spaces for indentation
Use double quotes for strings
Always use semicolons at the end of statements
Use ES5 trailing commas style
Use node: prefix for Node.js built-in imports (e.g., node:child_process)
Use camelCase for function and variable names
Use try/catch with if (err instanceof Error) checks for error handling

Files:

  • packages/utils/src/index.ts
  • packages/utils/src/getContentFromFiles.ts
  • packages/utils/__tests__/utils.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use type keyword for type-only imports in TypeScript
Include .ts file extension in local imports (e.g., ./setup.ts)
Use PascalCase for type and interface names

Files:

  • packages/utils/src/index.ts
  • packages/utils/src/getContentFromFiles.ts
  • packages/utils/__tests__/utils.test.ts
packages/*/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Include copyright header /*! node-minify ... MIT Licensed */ in source files

Files:

  • packages/utils/src/index.ts
  • packages/utils/src/getContentFromFiles.ts
packages/*/src/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

All packages must follow the standard structure: packages/<name>/src/index.ts for main export, packages/<name>/__tests__/ for tests, plus package.json and tsconfig.json

Files:

  • packages/utils/src/index.ts
packages/**/__tests__/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/**/__tests__/*.test.{ts,tsx}: Use Vitest with describe/test/expect for testing
Test files must be located in packages/*/__tests__/*.test.ts

Files:

  • packages/utils/__tests__/utils.test.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.313Z
Learning: The monorepo is for compressing JavaScript, CSS, and HTML files using various backends
📚 Learning: 2025-12-29T17:52:26.313Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.313Z
Learning: Core packages include: `core` (main minify function), `utils` (shared utilities), `run` (command execution wrapper), `types` (TypeScript definitions), and `cli` (command-line interface)

Applied to files:

  • packages/utils/src/index.ts
📚 Learning: 2025-12-29T17:52:26.313Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.313Z
Learning: Applies to **/*.{ts,tsx} : Include `.ts` file extension in local imports (e.g., `./setup.ts`)

Applied to files:

  • packages/utils/src/index.ts
📚 Learning: 2025-12-29T17:52:26.313Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.313Z
Learning: Applies to packages/**/__tests__/*.test.{ts,tsx} : Use Vitest with `describe`/`test`/`expect` for testing

Applied to files:

  • packages/utils/__tests__/utils.test.ts
📚 Learning: 2025-12-29T17:52:26.313Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.313Z
Learning: Applies to packages/**/__tests__/*.test.{ts,tsx} : Test files must be located in `packages/*/__tests__/*.test.ts`

Applied to files:

  • packages/utils/__tests__/utils.test.ts
🧬 Code graph analysis (2)
packages/utils/src/getContentFromFiles.ts (3)
packages/utils/src/index.ts (2)
  • readFile (34-34)
  • getContentFromFilesAsync (28-28)
packages/utils/src/readFile.ts (1)
  • readFile (27-39)
packages/utils/src/error.ts (1)
  • FileOperationError (11-18)
packages/utils/__tests__/utils.test.ts (2)
packages/utils/src/getContentFromFiles.ts (1)
  • getContentFromFiles (74-97)
packages/utils/src/index.ts (1)
  • getContentFromFiles (27-27)
⏰ 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). (1)
  • GitHub Check: test (windows-latest, 22.x)
🔇 Additional comments (5)
packages/utils/src/getContentFromFiles.ts (3)

7-10: LGTM!

Imports correctly follow the coding guidelines: node: prefix for Node.js built-ins, inline type for type-only imports, and .ts extension for local imports.


38-63: Well-implemented async file reading with proper error handling.

The function correctly uses async lstat instead of blocking sync calls, addressing the previous review feedback. The ENOENT handling provides clear error messages, and the directory check prevents reading directories.

Note: The sync version uses isFile() while this uses !isDirectory(), which is slightly more permissive (allows symlinks to files, etc.). This is acceptable since readFile will fail appropriately for non-readable paths.


108-135: Good implementation of parallel async file reading.

The use of Promise.all for parallel file reading aligns with the PR's goal of improving performance. The function structure mirrors the synchronous version, ensuring consistent behavior while enabling concurrent I/O operations.

packages/utils/__tests__/utils.test.ts (1)

353-356: LGTM!

Good addition of test coverage for single-file content extraction, complementing the existing multi-file test. The test follows Vitest conventions as per coding guidelines.

packages/utils/src/index.ts (1)

6-9: LGTM!

The new async functions are properly imported with .ts extensions and exported alongside their synchronous counterparts, maintaining alphabetical ordering. This correctly expands the public API surface for the utils package.

Also applies to: 12-12, 28-28, 32-32


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

@srod srod force-pushed the bolt-perf-parallel-compression-1297584749452239615 branch from 2ccd83c to dcb28f2 Compare December 30, 2025 15:50
@srod srod marked this pull request as ready for review December 30, 2025 17:44
@srod
Copy link
Owner

srod commented Dec 30, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@srod
Copy link
Owner

srod commented Dec 30, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
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: 5

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 513db8d and 85122a2.

⛔ Files ignored due to path filters (2)
  • .changeset/fix-workspace-publish.md is excluded by !.changeset/**
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (59)
  • .gitignore
  • .jules/bolt.md
  • docs/CHANGELOG.md
  • docs/package.json
  • examples/CHANGELOG.md
  • examples/package.json
  • package.json
  • packages/babel-minify/CHANGELOG.md
  • packages/babel-minify/package.json
  • packages/clean-css/CHANGELOG.md
  • packages/clean-css/package.json
  • packages/cli/CHANGELOG.md
  • packages/cli/package.json
  • packages/core/CHANGELOG.md
  • packages/core/__tests__/compress_async.test.ts
  • packages/core/package.json
  • packages/core/src/compress.ts
  • packages/crass/CHANGELOG.md
  • packages/crass/package.json
  • packages/cssnano/CHANGELOG.md
  • packages/cssnano/package.json
  • packages/csso/CHANGELOG.md
  • packages/csso/package.json
  • packages/esbuild/CHANGELOG.md
  • packages/esbuild/package.json
  • packages/google-closure-compiler/CHANGELOG.md
  • packages/google-closure-compiler/package.json
  • packages/html-minifier/CHANGELOG.md
  • packages/html-minifier/package.json
  • packages/jsonminify/CHANGELOG.md
  • packages/jsonminify/package.json
  • packages/lightningcss/CHANGELOG.md
  • packages/lightningcss/package.json
  • packages/no-compress/CHANGELOG.md
  • packages/no-compress/package.json
  • packages/oxc/CHANGELOG.md
  • packages/oxc/package.json
  • packages/run/CHANGELOG.md
  • packages/run/package.json
  • packages/sqwish/CHANGELOG.md
  • packages/sqwish/package.json
  • packages/swc/CHANGELOG.md
  • packages/swc/package.json
  • packages/terser/CHANGELOG.md
  • packages/terser/package.json
  • packages/types/CHANGELOG.md
  • packages/types/package.json
  • packages/uglify-es/CHANGELOG.md
  • packages/uglify-es/package.json
  • packages/uglify-js/CHANGELOG.md
  • packages/uglify-js/package.json
  • packages/utils/CHANGELOG.md
  • packages/utils/__tests__/getContentFromFilesAsync.test.ts
  • packages/utils/package.json
  • packages/utils/src/compressSingleFile.ts
  • packages/utils/src/getContentFromFiles.ts
  • packages/utils/src/index.ts
  • packages/yui/CHANGELOG.md
  • packages/yui/package.json
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use 4 spaces for indentation
Use double quotes for strings
Always use semicolons at the end of statements
Use ES5 trailing commas style
Use node: prefix for Node.js built-in imports (e.g., node:child_process)
Use camelCase for function and variable names
Use try/catch with if (err instanceof Error) checks for error handling

Files:

  • packages/utils/src/compressSingleFile.ts
  • packages/utils/src/index.ts
  • packages/utils/__tests__/getContentFromFilesAsync.test.ts
  • packages/core/src/compress.ts
  • packages/core/__tests__/compress_async.test.ts
  • packages/utils/src/getContentFromFiles.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use type keyword for type-only imports in TypeScript
Include .ts file extension in local imports (e.g., ./setup.ts)
Use PascalCase for type and interface names

Files:

  • packages/utils/src/compressSingleFile.ts
  • packages/utils/src/index.ts
  • packages/utils/__tests__/getContentFromFilesAsync.test.ts
  • packages/core/src/compress.ts
  • packages/core/__tests__/compress_async.test.ts
  • packages/utils/src/getContentFromFiles.ts
packages/*/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Include copyright header /*! node-minify ... MIT Licensed */ in source files

Files:

  • packages/utils/src/compressSingleFile.ts
  • packages/utils/src/index.ts
  • packages/core/src/compress.ts
  • packages/utils/src/getContentFromFiles.ts
packages/*/src/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

All packages must follow the standard structure: packages/<name>/src/index.ts for main export, packages/<name>/__tests__/ for tests, plus package.json and tsconfig.json

Files:

  • packages/utils/src/index.ts
packages/**/__tests__/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/**/__tests__/*.test.{ts,tsx}: Use Vitest with describe/test/expect for testing
Test files must be located in packages/*/__tests__/*.test.ts

Files:

  • packages/utils/__tests__/getContentFromFilesAsync.test.ts
  • packages/core/__tests__/compress_async.test.ts
🧠 Learnings (13)
📓 Common learnings
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.295Z
Learning: The monorepo is for compressing JavaScript, CSS, and HTML files using various backends
📚 Learning: 2025-12-29T17:52:26.295Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.295Z
Learning: Build deprecated packages (`babel-minify`, `uglify-es`, `yui`, `crass`, `sqwish`) only for backward compatibility; recommend modern alternatives

Applied to files:

  • packages/clean-css/CHANGELOG.md
  • packages/uglify-es/package.json
  • packages/no-compress/CHANGELOG.md
  • packages/babel-minify/package.json
  • packages/babel-minify/CHANGELOG.md
  • examples/CHANGELOG.md
  • packages/sqwish/package.json
  • packages/uglify-js/package.json
  • packages/utils/src/index.ts
  • packages/uglify-es/CHANGELOG.md
  • packages/crass/CHANGELOG.md
  • docs/CHANGELOG.md
  • packages/esbuild/CHANGELOG.md
  • packages/jsonminify/CHANGELOG.md
  • packages/utils/package.json
  • packages/crass/package.json
  • packages/yui/package.json
  • packages/html-minifier/CHANGELOG.md
  • packages/uglify-js/CHANGELOG.md
  • packages/sqwish/CHANGELOG.md
  • packages/yui/CHANGELOG.md
  • packages/utils/CHANGELOG.md
  • packages/html-minifier/package.json
  • packages/esbuild/package.json
📚 Learning: 2025-12-29T17:52:26.295Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.295Z
Learning: Core packages include: `core` (main minify function), `utils` (shared utilities), `run` (command execution wrapper), `types` (TypeScript definitions), and `cli` (command-line interface)

Applied to files:

  • examples/package.json
  • docs/package.json
  • packages/core/CHANGELOG.md
  • packages/utils/src/index.ts
  • packages/core/src/compress.ts
  • package.json
  • packages/run/CHANGELOG.md
  • packages/cli/package.json
  • packages/utils/package.json
  • packages/run/package.json
  • packages/core/package.json
  • packages/cli/CHANGELOG.md
  • packages/yui/CHANGELOG.md
  • packages/utils/CHANGELOG.md
  • packages/types/package.json
  • .gitignore
📚 Learning: 2025-12-29T17:52:26.295Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.295Z
Learning: Applies to packages/*/src/**/*.{ts,tsx,js,jsx} : Include copyright header `/*! node-minify ... MIT Licensed */` in source files

Applied to files:

  • packages/babel-minify/CHANGELOG.md
  • docs/CHANGELOG.md
  • packages/html-minifier/CHANGELOG.md
  • .gitignore
📚 Learning: 2025-12-29T17:52:26.295Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.295Z
Learning: Compressor packages should wrap specific minification libraries for JavaScript, CSS, HTML, and other formats

Applied to files:

  • packages/no-compress/package.json
  • packages/uglify-js/package.json
  • packages/utils/src/index.ts
  • packages/core/__tests__/compress_async.test.ts
📚 Learning: 2025-12-29T17:52:26.295Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.295Z
Learning: The monorepo is for compressing JavaScript, CSS, and HTML files using various backends

Applied to files:

  • packages/utils/src/compressSingleFile.ts
  • packages/utils/src/index.ts
  • packages/core/src/compress.ts
  • packages/core/__tests__/compress_async.test.ts
  • packages/run/package.json
📚 Learning: 2025-12-29T17:52:26.295Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.295Z
Learning: The `core` package depends on `utils` and `run`; all compressor packages depend on `core`

Applied to files:

  • packages/utils/src/index.ts
  • packages/utils/package.json
  • packages/core/package.json
📚 Learning: 2025-12-29T17:52:26.295Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.295Z
Learning: Applies to packages/**/__tests__/*.test.{ts,tsx} : Test files must be located in `packages/*/__tests__/*.test.ts`

Applied to files:

  • packages/utils/__tests__/getContentFromFilesAsync.test.ts
  • .gitignore
📚 Learning: 2025-12-29T17:52:26.295Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.295Z
Learning: Applies to packages/**/__tests__/*.test.{ts,tsx} : Use Vitest with `describe`/`test`/`expect` for testing

Applied to files:

  • packages/utils/__tests__/getContentFromFilesAsync.test.ts
  • package.json
  • packages/core/__tests__/compress_async.test.ts
  • .gitignore
📚 Learning: 2025-12-29T17:52:26.295Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.295Z
Learning: Node.js version must be >=20.0.0

Applied to files:

  • package.json
📚 Learning: 2025-12-29T17:52:26.295Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.295Z
Learning: Use `tsdown` for building packages (configured via `tsdown.config.ts`)

Applied to files:

  • package.json
📚 Learning: 2025-12-29T17:52:26.295Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.295Z
Learning: Applies to packages/*/src/index.ts : All packages must follow the standard structure: `packages/<name>/src/index.ts` for main export, `packages/<name>/__tests__/` for tests, plus `package.json` and `tsconfig.json`

Applied to files:

  • .gitignore
📚 Learning: 2025-12-29T17:52:26.295Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.295Z
Learning: Applies to **/*.{ts,tsx} : Include `.ts` file extension in local imports (e.g., `./setup.ts`)

Applied to files:

  • .gitignore
🧬 Code graph analysis (3)
packages/utils/src/compressSingleFile.ts (3)
packages/utils/src/index.ts (2)
  • compressSingleFile (23-23)
  • getContentFromFilesAsync (26-26)
packages/types/src/types.d.ts (1)
  • Settings (57-141)
packages/utils/src/getContentFromFiles.ts (1)
  • getContentFromFilesAsync (98-125)
packages/core/src/compress.ts (3)
packages/utils/src/getContentFromFiles.ts (1)
  • getContentFromFilesAsync (98-125)
packages/run/src/index.ts (1)
  • run (38-79)
packages/utils/src/run.ts (1)
  • run (23-45)
packages/utils/src/getContentFromFiles.ts (4)
packages/utils/src/index.ts (3)
  • isValidFile (29-29)
  • readFile (31-31)
  • getContentFromFilesAsync (26-26)
packages/utils/src/isValidFile.ts (1)
  • isValidFile (20-26)
packages/utils/src/readFile.ts (1)
  • readFile (14-16)
packages/utils/src/error.ts (1)
  • FileOperationError (11-18)
🔇 Additional comments (62)
packages/lightningcss/CHANGELOG.md (1)

3-9: Changelog entry may be incomplete relative to PR scope.

The changelog documents the npm install fix but omits the significant performance improvements (async IO and parallel compression) mentioned in the PR objectives. Additionally, the fix description "unresolved workspace:* references in published packages" is technical and lacks context for users.

Verify whether:

  1. The async/parallelization improvements should be documented in the changelog (e.g., as part of a feature or performance note).
  2. The npm install fix description should be expanded to clarify the user-facing impact.
  3. Similar changelog updates exist in other package changelogs (e.g., packages/core/CHANGELOG.md, packages/utils/CHANGELOG.md) to ensure consistency across the monorepo.
packages/oxc/CHANGELOG.md (1)

3-9: Incomplete changelog—missing documentation of main features.

The PR objectives describe significant additions to this version (asynchronous file content retrieval, parallel compression refactoring), but the changelog entry documents only the npm install error fix. If the async IO and parallelization features are part of 10.1.1, the changelog should document them; otherwise, clarify the actual scope of this release.

Does the 10.1.1 release include the async file reading and parallel compression features mentioned in the PR objectives, or are those targeted for a later release?

packages/google-closure-compiler/CHANGELOG.md (1)

3-10: ✓ Changelog entry well‑formatted and consistent.

The new 10.1.1 section is properly structured, follows the established format, and accurately documents the patch's purpose (npm install error fix) and updated dependencies. The entry aligns with the parallel 10.1.1 patches across the monorepo.

.gitignore (1)

17-17: Appropriate addition to ignore temporary performance test artifacts.

The path packages/utils/__tests__/temp_perf/ is correctly added to prevent temporary performance test files from being committed. This aligns with the PR objectives to clean up performance test artifacts and follows the existing .gitignore conventions.

packages/no-compress/CHANGELOG.md (1)

3-9: LGTM!

The changelog entry is well-structured, follows the established format consistently with other version entries, and accurately documents the patch fix for the npm install error. The version bump and dependency update are properly recorded.

packages/uglify-es/CHANGELOG.md (1)

3-9: ✓ Changelog entry properly structured and documented.

The 10.1.1 patch entry is well-formatted, accurately documents the npm install error fix and the dependency update to @node-minify/utils@10.1.1, and follows the established changelog conventions. For a deprecated package maintained only for backward compatibility, documenting the version bump and dependency changes is appropriate.

packages/cssnano/CHANGELOG.md (1)

3-9: Review comment is based on incorrect assumptions about PR changes.

The changelog entries across all packages (cssnano, utils, core, cli) are consistent and accurate—they all document only the npm install error fix (commit eb785b0) for version 10.1.1. There is no evidence of async utilities, parallel compression features, or performance improvements in this release. Code searches for getContentFromFilesAsync, compressArrayOfFiles, and async implementations return no results. The suggested changelog enhancement proposing these features cannot be applied since they are not part of this release.

Likely an incorrect or invalid review comment.

packages/html-minifier/package.json (1)

3-3: LGTM: Version bump aligns with coordinated patch release.

The version increment to 10.1.1 is appropriate for this patch release addressing npm install errors and introducing backward-compatible performance improvements.

packages/uglify-js/package.json (1)

3-3: LGTM: Version bump consistent with patch release.

docs/package.json (1)

4-4: LGTM: Version bump consistent across monorepo.

packages/swc/package.json (1)

3-3: LGTM: Version bump aligns with patch release.

packages/types/package.json (1)

3-3: LGTM: Version bump consistent with coordinated release.

packages/clean-css/CHANGELOG.md (1)

3-10: LGTM: Clear changelog entry for patch release.

The changelog accurately documents the npm install error fix and dependency update. The workspace:* resolution issue is appropriately characterized as a publishing concern.

packages/babel-minify/package.json (1)

3-3: LGTM: Version bump consistent with patch release.

The version increment appropriately maintains this deprecated package for backward compatibility.

packages/clean-css/package.json (1)

3-3: LGTM: Version bump completes coordinated patch release.

packages/cli/package.json (1)

3-3: LGTM!

Version bump to 10.1.1 is consistent with the coordinated patch release across the monorepo.

packages/esbuild/package.json (1)

3-3: LGTM!

Version bump aligns with the 10.1.1 patch release.

packages/crass/CHANGELOG.md (1)

3-10: LGTM!

The changelog entry properly documents the 10.1.1 patch release with clear description of the npm install fix and dependency updates.

packages/utils/package.json (1)

3-3: LGTM!

Version bump to 10.1.1 is appropriate for the utils package, which now includes the new async file content retrieval API.

packages/oxc/package.json (1)

3-3: LGTM!

Version bump is consistent with the 10.1.1 release.

examples/package.json (1)

3-3: LGTM!

Version bump to 10.1.1 is consistent across the monorepo. The private examples package appropriately tracks the release version.

packages/run/package.json (1)

3-3: LGTM!

Version bump to 10.1.1 aligns with the patch release. The run package version is correctly updated.

packages/uglify-es/package.json (1)

3-3: Version bump looks correct.

The patch version increment from 10.1.0 to 10.1.1 aligns with the PR's stated objectives and changelog entries across the monorepo.

packages/csso/CHANGELOG.md (1)

3-10: Changelog entry is well-formatted and consistent.

The 10.1.1 patch section properly documents the workspace reference fix and dependency update, following the established changelog format.

packages/no-compress/package.json (1)

3-3: Version bump is correct.

The patch version increment is consistent with the monorepo-wide 10.1.1 release.

packages/core/package.json (1)

3-3: Version bump is appropriate.

The core package version increment to 10.1.1 is correct for this patch release.

packages/cssnano/package.json (1)

3-3: Version bump is correct.

The patch version increment to 10.1.1 is consistent with the release.

packages/terser/package.json (1)

3-3: Version bump is correct.

The patch version increment to 10.1.1 aligns with the monorepo release.

packages/jsonminify/CHANGELOG.md (1)

3-10: Changelog entry is well-formatted.

The 10.1.1 patch section correctly documents the workspace reference fix and dependency update.

packages/terser/CHANGELOG.md (2)

3-10: Changelog entry is properly formatted and consistent.

The 10.1.1 patch section correctly documents the workspace reference fix and dependency update, matching the format across all package changelogs.


7-9: The publishing pipeline properly resolves workspace:* references during publishing. The scripts/publish.ts script (executed via changeset:release) reads all packages, builds a version map, and replaces any workspace:* dependency specifiers with concrete versions before calling npm publish. The script includes error handling that throws if a workspace reference cannot be resolved, preventing any malformed packages from being published to npm. After publishing, the original package.json files are restored, keeping workspace:* intact in the source. The changelog entry is accurate—this implementation is the fix that prevents npm install errors.

packages/crass/package.json (1)

3-3: LGTM!

Straightforward version bump to 10.1.1 as part of the coordinated patch release.

packages/cli/CHANGELOG.md (1)

3-11: LGTM!

Properly formatted changelog entry documenting the patch release and dependency updates.

packages/utils/CHANGELOG.md (1)

3-8: LGTM!

Changelog entry correctly documents the patch release.

packages/sqwish/package.json (1)

3-3: LGTM!

Version bump to 10.1.1 consistent with the coordinated patch release.

packages/yui/package.json (1)

3-3: LGTM!

Version bump to 10.1.1 consistent with the coordinated patch release.

packages/esbuild/CHANGELOG.md (1)

3-10: LGTM!

Changelog entry properly documents the patch release and dependency update.

packages/types/CHANGELOG.md (1)

3-8: LGTM!

Changelog entry correctly documents the patch release.

examples/CHANGELOG.md (1)

3-27: LGTM!

Comprehensive changelog entry documenting all dependency updates for the 10.1.1 patch release.

packages/run/CHANGELOG.md (1)

3-7: LGTM!

The changelog entry is well-formatted, follows established conventions, and accurately documents the workspace reference fix for the 10.1.1 patch release.

packages/html-minifier/CHANGELOG.md (1)

3-9: LGTM!

The changelog entry correctly documents the patch fix and dependency update, following the established format consistently.

packages/sqwish/CHANGELOG.md (1)

3-9: LGTM!

The changelog entry is correctly formatted and consistent with other packages in this release.

packages/csso/package.json (1)

3-3: LGTM!

Version bump to 10.1.1 is correct and aligns with the coordinated patch release across all packages.

packages/core/CHANGELOG.md (1)

3-9: LGTM!

The changelog entry is well-formatted and correctly documents the workspace reference fix and dependency update for this patch release.

packages/babel-minify/CHANGELOG.md (1)

3-9: LGTM!

The changelog entry is correctly formatted and consistent with the coordinated 10.1.1 patch release.

packages/jsonminify/package.json (1)

3-3: LGTM!

Version bump to 10.1.1 is correct and consistent with the monorepo-wide patch release.

packages/uglify-js/CHANGELOG.md (1)

3-9: LGTM!

The changelog entry is well-formatted and correctly documents the patch fix and dependency update, maintaining consistency across all package changelogs in this release.

packages/swc/CHANGELOG.md (1)

3-10: LGTM! Changelog entry is clear and follows the established format.

The 10.1.1 patch release entry properly documents the npm install fix and dependency updates.

packages/lightningcss/package.json (1)

3-3: LGTM! Version bump aligns with the 10.1.1 patch release.

packages/yui/CHANGELOG.md (1)

3-11: LGTM! Changelog entry properly documents the patch release.

The entry follows the established format and includes appropriate dependency updates for this deprecated package.

packages/google-closure-compiler/package.json (1)

3-3: LGTM! Version bump is consistent with the coordinated 10.1.1 release.

packages/utils/src/compressSingleFile.ts (3)

8-8: LGTM! Import updated to use async file reading.

The switch to getContentFromFilesAsync aligns with the PR's objective to use async IO for better performance.


15-18: LGTM! Properly awaits async content determination.

Since compressSingleFile was already async, this change maintains backward compatibility while enabling async file reading.


25-35: LGTM! Async conversion is correctly implemented.

The function signature change to async is safe since determineContent is an internal helper. Error handling through async/await will properly propagate any file reading errors.

packages/utils/src/index.ts (2)

5-8: LGTM! Import statement updated to include the new async function.


26-26: LGTM! Export added for the new async API.

This is a backward-compatible addition to the public API surface, enabling consumers to use async file reading for improved performance.

packages/utils/__tests__/getContentFromFilesAsync.test.ts (1)

1-56: LGTM! Comprehensive test coverage for the async file reading function.

The test suite properly covers:

  • Single and multiple file reading
  • Empty input handling
  • Error cases (null input, non-existent files, directories)
  • Async behavior validation

The use of null as any on line 34 is acceptable for testing error handling. The tests follow Vitest conventions and coding guidelines.

package.json (2)

54-54: tsdown@0.18.4 is available on npm and has no known security vulnerabilities. No action needed.


48-48: @types/node@22.19.3 is valid and secure.

The version exists in npm and has no known vulnerabilities. This is a routine patch update within the ^22.x range.

packages/utils/src/getContentFromFiles.ts (1)

114-116: LGTM! Parallel file reads correctly implemented.

Using Promise.all with map enables concurrent I/O operations, which is the key performance improvement for this PR.

packages/core/__tests__/compress_async.test.ts (1)

1-18: LGTM! Test setup follows best practices.

The test structure correctly uses Vitest with proper setup and cleanup.

packages/core/src/compress.ts (1)

14-14: LGTM! Import updated to async variant.

The import correctly references the new async file reading utility.

Comment on lines 3 to 4
## 10.1.1

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Document the changes in the 10.1.1 release.

The changelog section for 10.1.1 is empty. Based on the PR objectives and other package changelogs (e.g., packages/crass/CHANGELOG.md), this section should document the npm install fix and related improvements introduced in this patch release.

🔎 Suggested changelog content
 ## 10.1.1
+
+### Patch Changes
+
+- eb785b0: Fix npm install error caused by unresolved workspace:* references in published packages
📝 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
## 10.1.1
## 10.1.1
### Patch Changes
- eb785b0: Fix npm install error caused by unresolved workspace:* references in published packages
🤖 Prompt for AI Agents
In docs/CHANGELOG.md around lines 3 to 4, the 10.1.1 release section is empty;
add a concise entry summarizing the patch: note the npm install fix (describe
briefly what was fixed), any related improvements or bugfixes from the PR, and
reference affected packages (e.g., packages/crass) and relevant PR/issue
numbers; keep tone consistent with other changelog entries and follow existing
formatting (bullet or paragraph) used elsewhere in the file.

Comment on lines 30 to 55
test("should handle input array with empty/null values", async () => {
// This is tricky because input type is string[], so null is not allowed by TS.
// But runtime might pass it.
// Also the code checks `if (input)`.
const settings: Settings = {
compressor: noCompress,
input: ["", "file.js"], // Empty string is falsy
output: ["out1.js", "out2.js"],
} as any;

// Let's create a real file.
const file = path.join(tempDir, "file.js");
fs.writeFileSync(file, "content");
const out2 = path.join(tempDir, "out2.js");

const validSettings: Settings = {
compressor: noCompress,
input: ["", file],
output: ["", out2], // output array length must match input
};

const result = await compress(validSettings);
expect(result).toBe("content"); // The result of the last processed file?
// The last file is "file.js" (index 1).
// The first file (index 0) was skipped.
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Improve test clarity and remove type assertion bypass.

The test has several issues:

  1. Line 38 uses as any to bypass TypeScript, which is a code smell and defeats type safety.
  2. The test name "should handle input array with empty/null values" doesn't clearly describe the expected behavior.
  3. Line 52's comment "The result of the last processed file?" expresses uncertainty about the expected behavior.
  4. The test validates that only the last file's content is returned, but this behavior should be explicitly documented.
🔎 Proposed improvements
-    test("should handle input array with empty/null values", async () => {
-        // This is tricky because input type is string[], so null is not allowed by TS.
-        // But runtime might pass it.
-        // Also the code checks `if (input)`.
+    test("should return last file's content when processing multiple files with empty strings", async () => {
         const settings: Settings = {
             compressor: noCompress,
-            input: ["", "file.js"], // Empty string is falsy
+            input: ["", "file.js"],
             output: ["out1.js", "out2.js"],
-        } as any;
+        };

-        // Let's create a real file.
+        // Create test file
         const file = path.join(tempDir, "file.js");
         fs.writeFileSync(file, "content");
         const out2 = path.join(tempDir, "out2.js");

         const validSettings: Settings = {
             compressor: noCompress,
             input: ["", file],
-            output: ["", out2], // output array length must match input
+            output: ["", out2],
         };

         const result = await compress(validSettings);
-        expect(result).toBe("content"); // The result of the last processed file?
-        // The last file is "file.js" (index 1).
-        // The first file (index 0) was skipped.
+        // When processing multiple files, compress returns the last file's content
+        expect(result).toBe("content");
     });
📝 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
test("should handle input array with empty/null values", async () => {
// This is tricky because input type is string[], so null is not allowed by TS.
// But runtime might pass it.
// Also the code checks `if (input)`.
const settings: Settings = {
compressor: noCompress,
input: ["", "file.js"], // Empty string is falsy
output: ["out1.js", "out2.js"],
} as any;
// Let's create a real file.
const file = path.join(tempDir, "file.js");
fs.writeFileSync(file, "content");
const out2 = path.join(tempDir, "out2.js");
const validSettings: Settings = {
compressor: noCompress,
input: ["", file],
output: ["", out2], // output array length must match input
};
const result = await compress(validSettings);
expect(result).toBe("content"); // The result of the last processed file?
// The last file is "file.js" (index 1).
// The first file (index 0) was skipped.
});
test("should return last file's content when processing multiple files with empty strings", async () => {
const settings: Settings = {
compressor: noCompress,
input: ["", "file.js"],
output: ["out1.js", "out2.js"],
};
// Create test file
const file = path.join(tempDir, "file.js");
fs.writeFileSync(file, "content");
const out2 = path.join(tempDir, "out2.js");
const validSettings: Settings = {
compressor: noCompress,
input: ["", file],
output: ["", out2],
};
const result = await compress(validSettings);
// When processing multiple files, compress returns the last file's content
expect(result).toBe("content");
});
🤖 Prompt for AI Agents
packages/core/__tests__/compress_async.test.ts around lines 30 to 55: the test
bypasses TypeScript with `as any`, has an unclear name and uncertain comments,
and relies on ambiguous setup; remove the first `settings` variable that uses
`as any`, rename the test to explicitly state expected behavior (e.g., "skips
empty input entries and returns last processed file content"), eliminate the
"The result of the last processed file?" comment, and construct only a properly
typed `validSettings: Settings` (with input containing "" and the real file and
matching output array) then assert the exact expected string result and add a
short comment stating that empty input entries are skipped so the last non-empty
file's content is returned.

Comment on lines 55 to 67
const promises = settings.input.map(async (input, index) => {
if (input) {
const content = getContentFromFiles(input);
result = await run({ settings, content, index });
const content = await getContentFromFilesAsync(input);
return run({ settings, content, index });
}
}
return "";
});

const results = await Promise.all(promises);
return results[results.length - 1] || "";
}
return result;
return "";
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the compress.ts file to understand the context
cat -n packages/core/src/compress.ts | head -100

Repository: srod/node-minify

Length of output: 3477


🏁 Script executed:

# Search for how compress() is being used with array inputs
rg -n 'compress\s*\(' packages/ -A 3 -B 3 --type=ts

Repository: srod/node-minify

Length of output: 4397


🏁 Script executed:

# Look for test files to understand expected behavior
fd -e '\.test\.(ts|js)' -e '\.spec\.(ts|js)' packages/core/src/ -x grep -l 'compress'

Repository: srod/node-minify

Length of output: 437


🏁 Script executed:

# Check git log for changes related to compress function refactoring
git log --oneline -n 20 -- packages/core/src/compress.ts

Repository: srod/node-minify

Length of output: 146


🏁 Script executed:

# Check what the run() function returns and how it uses the index parameter
rg -n 'export.*\brun\b' packages/ -A 10 --type=ts | head -50

Repository: srod/node-minify

Length of output: 1959


🏁 Script executed:

# Look at the test more closely to understand the semantics
cat -n packages/core/__tests__/compress_async.test.ts | head -80

Repository: srod/node-minify

Length of output: 2377


🏁 Script executed:

# Search for how compress() return value is used in callers
rg -n 'const result = await compress' packages/ -A 5 --type=ts

Repository: srod/node-minify

Length of output: 3509


🏁 Script executed:

# Let's check what spinnerStop does with the result parameter
rg -n 'spinnerStop' packages/cli/src/ -A 5 -B 2 --type=ts

Repository: srod/node-minify

Length of output: 1223


🏁 Script executed:

# Check the run() function in utils to understand what it returns
cat -n packages/utils/src/run.ts | head -80

Repository: srod/node-minify

Length of output: 2668


🏁 Script executed:

# Look for any documentation or comments about the parallel compression feature
rg -n 'Compress an array|parallel' packages/core/src/ -B 2 -A 2 --type=ts

Repository: srod/node-minify

Length of output: 292


🏁 Script executed:

# Check the spinnerStop implementation to understand what it does with the result
cat -n packages/cli/src/spinner.ts | grep -A 20 "function stop"

Repository: srod/node-minify

Length of output: 767


🏁 Script executed:

# Look at the original sequential implementation to see if this behavior is consistent
git show HEAD:packages/core/src/compress.ts 2>/dev/null | head -100

Repository: srod/node-minify

Length of output: 2777


🏁 Script executed:

# Check the CLI's compress function to see how it constructs the Result
cat -n packages/cli/src/compress.ts | head -50

Repository: srod/node-minify

Length of output: 1693


🏁 Script executed:

# Look for any documentation about what the compress function return value represents
rg -n 'Promise<string>' packages/core/src/ -B 5 -A 2 --type=ts

Repository: srod/node-minify

Length of output: 1559


Document the return value semantics for array compression.

The code correctly writes each file to disk via the index parameter in writeOutput(), but the return value is only the last file's minified content. This behavior is intentional per the test expectations, but poorly documented:

  • The test at line 52 questions the same semantics with "// The result of the last processed file?"
  • For arrays, the CLI wrapper ignores this return value and returns a default result with "0" sizes
  • Consider clarifying in the function's JSDoc whether returning only the last result is intentional or if results should be aggregated differently

The Promise.all behavior (fail-fast on first file error) is appropriate for batch processing and doesn't need changes.

🤖 Prompt for AI Agents
In packages/core/src/compress.ts around lines 55 to 67, the function currently
writes each input file via writeOutput using the index but only returns the last
file's minified content; update the function JSDoc to explicitly state that for
array inputs the function processes all items (fail-fast via Promise.all) and
returns the minified content of the last processed file (by design), including
note that callers/CLI ignore this value for aggregated reporting; if different
semantics are required instead (e.g., return an array of results or an
aggregated summary), implement that change and update tests and the CLI wrapper
accordingly.

@srod srod force-pushed the bolt-perf-parallel-compression-1297584749452239615 branch 3 times, most recently from 4961c15 to 73bc2d3 Compare December 30, 2025 23:14
@srod
Copy link
Owner

srod commented Dec 30, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
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: 0

♻️ Duplicate comments (1)
packages/utils/src/getContentFromFiles.ts (1)

38-63: LGTM! Async implementation properly addresses previous blocking I/O concern.

The function now uses await lstat() and await readFile() from node:fs/promises, eliminating the blocking synchronous operations (existsSync, lstatSync) that were flagged in the previous review. This ensures the event loop remains unblocked during file validation and reading, delivering the performance benefits expected from async I/O.


Note: While the nested try-catch for ENOENT handling (lines 41-53) is verbose, it's functionally correct and maintains parity with the sync version's error semantics.

🧹 Nitpick comments (1)
packages/utils/src/compressSingleFile.ts (1)

69-69: Consider async reads for image files to maintain consistency.

Image files currently use synchronous readFileSync (lines 69, 75), which blocks the event loop and is inconsistent with the async approach used for text files. While less critical for typical minification workflows, large images could introduce blocking delays.

Consider using readFile from node:fs/promises for consistency:

🔎 Proposed refactor for async image reading

For line 69 (multiple images):

-            return settings.input.map((file) => readFileSync(file));
+            return await Promise.all(
+                settings.input.map((file) => readFile(file))
+            );

For line 75 (single image):

-            return readFileSync(settings.input);
+            return await readFile(settings.input);

Note: You'll need to add readFile to the import from node:fs/promises:

-import { readFileSync } from "node:fs";
+import { readFileSync } from "node:fs";
+import { readFile } from "node:fs/promises";

Also applies to: 75-75

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 85122a2 and 5adebf8.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • .gitignore
  • .jules/bolt.md
  • packages/core/__tests__/compress_async.test.ts
  • packages/core/src/compress.ts
  • packages/utils/__tests__/getContentFromFilesAsync.test.ts
  • packages/utils/__tests__/isValidFileAsync.test.ts
  • packages/utils/src/compressSingleFile.ts
  • packages/utils/src/getContentFromFiles.ts
  • packages/utils/src/index.ts
  • packages/utils/src/isValidFile.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/utils/tests/getContentFromFilesAsync.test.ts
  • .jules/bolt.md
  • packages/core/src/compress.ts
  • .gitignore
  • packages/core/tests/compress_async.test.ts
  • packages/utils/src/index.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use 4 spaces for indentation
Use double quotes for strings
Always use semicolons at the end of statements
Use ES5 trailing commas style
Use node: prefix for Node.js built-in imports (e.g., node:child_process)
Use camelCase for function and variable names
Use try/catch with if (err instanceof Error) checks for error handling

Files:

  • packages/utils/src/isValidFile.ts
  • packages/utils/__tests__/isValidFileAsync.test.ts
  • packages/utils/src/compressSingleFile.ts
  • packages/utils/src/getContentFromFiles.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use type keyword for type-only imports in TypeScript
Include .ts file extension in local imports (e.g., ./setup.ts)
Use PascalCase for type and interface names

Files:

  • packages/utils/src/isValidFile.ts
  • packages/utils/__tests__/isValidFileAsync.test.ts
  • packages/utils/src/compressSingleFile.ts
  • packages/utils/src/getContentFromFiles.ts
packages/*/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Include copyright header /*! node-minify ... MIT Licensed */ in source files

Files:

  • packages/utils/src/isValidFile.ts
  • packages/utils/src/compressSingleFile.ts
  • packages/utils/src/getContentFromFiles.ts
packages/**/__tests__/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/**/__tests__/*.test.{ts,tsx}: Use Vitest with describe/test/expect for testing
Test files must be located in packages/*/__tests__/*.test.ts

Files:

  • packages/utils/__tests__/isValidFileAsync.test.ts
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.313Z
Learning: The monorepo is for compressing JavaScript, CSS, and HTML files using various backends
📚 Learning: 2025-12-29T17:52:26.313Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.313Z
Learning: Applies to packages/**/__tests__/*.test.{ts,tsx} : Test files must be located in `packages/*/__tests__/*.test.ts`

Applied to files:

  • packages/utils/__tests__/isValidFileAsync.test.ts
📚 Learning: 2025-12-29T17:52:26.313Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.313Z
Learning: Applies to packages/**/__tests__/*.test.{ts,tsx} : Use Vitest with `describe`/`test`/`expect` for testing

Applied to files:

  • packages/utils/__tests__/isValidFileAsync.test.ts
📚 Learning: 2025-12-29T17:52:26.313Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.313Z
Learning: Core packages include: `core` (main minify function), `utils` (shared utilities), `run` (command execution wrapper), `types` (TypeScript definitions), and `cli` (command-line interface)

Applied to files:

  • packages/utils/src/compressSingleFile.ts
📚 Learning: 2025-12-29T17:52:26.313Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.313Z
Learning: Applies to packages/*/src/**/*.{ts,tsx,js,jsx} : Include copyright header `/*! node-minify ... MIT Licensed */` in source files

Applied to files:

  • packages/utils/src/compressSingleFile.ts
📚 Learning: 2025-12-29T17:52:26.313Z
Learnt from: CR
Repo: srod/node-minify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T17:52:26.313Z
Learning: The monorepo is for compressing JavaScript, CSS, and HTML files using various backends

Applied to files:

  • packages/utils/src/compressSingleFile.ts
🧬 Code graph analysis (2)
packages/utils/__tests__/isValidFileAsync.test.ts (3)
packages/utils/src/index.ts (1)
  • isValidFileAsync (32-32)
packages/utils/src/isValidFile.ts (1)
  • isValidFileAsync (39-49)
packages/utils/src/error.ts (1)
  • FileOperationError (11-18)
packages/utils/src/getContentFromFiles.ts (3)
packages/utils/src/index.ts (2)
  • readFile (34-34)
  • getContentFromFilesAsync (28-28)
packages/utils/src/readFile.ts (1)
  • readFile (27-39)
packages/utils/src/error.ts (1)
  • FileOperationError (11-18)
⏰ 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). (3)
  • GitHub Check: test (windows-latest, 20.x)
  • GitHub Check: test (windows-latest, 24.x)
  • GitHub Check: test (windows-latest, 22.x)
🔇 Additional comments (9)
packages/utils/__tests__/isValidFileAsync.test.ts (1)

1-42: LGTM! Comprehensive test coverage for async file validation.

The test suite properly covers all critical paths:

  • Valid file detection
  • Non-existent file handling
  • Directory exclusion
  • Error propagation with custom FileOperationError

The mock setup correctly preserves the original implementation while enabling test-specific overrides.

packages/utils/src/isValidFile.ts (2)

8-8: LGTM! Proper async filesystem import.

The import follows coding guidelines with the node: prefix for Node.js built-ins.


29-49: LGTM! Proper async file validation implementation.

The async variant correctly mirrors the synchronous version's behavior while using non-blocking filesystem operations. The ENOENT handling (returning false) and error wrapping maintain consistency with the existing API.

packages/utils/src/getContentFromFiles.ts (2)

7-8: LGTM! Proper async filesystem imports.

The imports add necessary async filesystem primitives from node:fs/promises, following the coding guideline to use the node: prefix.


108-135: LGTM! Parallel file reading delivers the performance benefits described in the PR.

The use of Promise.all(input.map(readFileContentAsync)) on line 125 enables concurrent file reads, which is central to the ~16× speedup reported in the PR objectives. The error handling correctly preserves FileOperationError instances while wrapping other errors.

packages/utils/src/compressSingleFile.ts (4)

13-13: LGTM! Import updated for async content reading.

The import change enables non-blocking file reads, supporting the PR's parallelization objectives.


52-54: LGTM! Function signature properly updated for async operation.

The signature change correctly wraps the return type in Promise<...> to support async/await usage.


43-43: LGTM! Proper async content retrieval.

The await correctly handles the async content determination.


80-80: LGTM! Non-blocking file reads for text content.

Using getContentFromFilesAsync enables parallel reads and prevents event loop blocking for text files.

@srod srod force-pushed the bolt-perf-parallel-compression-1297584749452239615 branch 2 times, most recently from 5bf7233 to bfee546 Compare December 30, 2025 23:46
@srod
Copy link
Owner

srod commented Dec 30, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

google-labs-jules bot and others added 10 commits December 31, 2025 01:02
- Parallelize `compressArrayOfFiles` in `packages/core` to process multiple input files concurrently using `Promise.all`.
- Introduce `getContentFromFilesAsync` in `packages/utils` to read files asynchronously.
- Update `compressSingleFile` and `compressArrayOfFiles` to use the new async file reading.
- This results in a ~16x performance improvement for multi-file compression (based on 20 files scenario).
- Parallelize `compressArrayOfFiles` in `packages/core` to process multiple input files concurrently using `Promise.all`.
- Introduce `getContentFromFilesAsync` in `packages/utils` to read files asynchronously.
- Update `compressSingleFile` and `compressArrayOfFiles` to use the new async file reading.
- Add unit tests for async file reading and parallel compression to ensure high code coverage (97%+ for utils, 92%+ for core).
- This results in a ~16x performance improvement for multi-file compression (based on 20 files scenario).
- Parallelize `compressArrayOfFiles` in `packages/core` to process multiple input files concurrently using `Promise.all`.
- Introduce `getContentFromFilesAsync` in `packages/utils` to read files asynchronously.
- Update `compressSingleFile` and `compressArrayOfFiles` to use the new async file reading.
- Add unit tests for async file reading and parallel compression.
- Merge latest changes from main (clean merge).
- Cleanup temporary performance test files.
- Parallelize `compressArrayOfFiles` in `packages/core` to process multiple input files concurrently using `Promise.all`.
- Introduce `getContentFromFilesAsync` in `packages/utils` to read files asynchronously.
- Update `compressSingleFile` and `compressArrayOfFiles` to use the new async file reading.
- Add unit tests for async file reading and parallel compression.
- Merge latest changes from main (clean merge).
- Cleanup temporary performance test files.
- Introduced `isValidFileAsync` to replace synchronous `isValidFile` and `existsSync` in `getContentFromFiles.ts`.
- Updated `readFileContentAsync` to use non-blocking `lstat` and `readFile`.
- Added unit tests for `isValidFileAsync`.
- Exported `isValidFileAsync` from `utils/src/index.ts`.
- Fix import protocol (use `node:path`, `node:fs`).
- Fix unused variables/imports in tests.
- Fix implicit any in `getContentFromFiles.ts`.
- Ensure tests pass with updated lint rules.
- Parallelize `compressArrayOfFiles` in `packages/core` to process multiple input files concurrently using `Promise.all`.
- Introduce `getContentFromFilesAsync` in `packages/utils` to read files asynchronously.
- Update `compressSingleFile` and `compressArrayOfFiles` to use the new async file reading.
- Refactor file validation to be fully asynchronous with `isValidFileAsync`.
- Add unit tests for async file reading and parallel compression.
- Rebase on `develop` to ensure compatibility with latest changes.
@srod srod force-pushed the bolt-perf-parallel-compression-1297584749452239615 branch from d4491e1 to 6e751ff Compare December 31, 2025 00:02
@srod
Copy link
Owner

srod commented Dec 31, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 31, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@srod srod merged commit 08559f6 into develop Dec 31, 2025
8 checks passed
@srod srod deleted the bolt-perf-parallel-compression-1297584749452239615 branch December 31, 2025 11:59
@codecov
Copy link

codecov bot commented Dec 31, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.46%. Comparing base (0c9ec4a) to head (6e751ff).
⚠️ Report is 11 commits behind head on develop.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #2706      +/-   ##
===========================================
+ Coverage    99.01%   99.46%   +0.44%     
===========================================
  Files           46       46              
  Lines          713      750      +37     
  Branches       230      242      +12     
===========================================
+ Hits           706      746      +40     
+ Misses           7        4       -3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants