| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
📝 Walkthrough
WalkthroughThis pull request adds a standardized this.fs context property to the unplugin build context across webpack, rspack, bun, esbuild, and farm bundlers. The implementation provides a shared filesystem abstraction with readFile, stat, and lstat methods, supporting both native bundler filesystem APIs and Node.js fallback. ChangesFile System Context Abstraction
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem🚥 Pre-merge checks | ✅ 4 | ❌ 1 ❌ Failed checks (1 warning)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches 🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. ❤️ ShareComment @coderabbitai help to get the list of available commands and usage tips. |
Sorry, something went wrong.
There was a problem hiding this comment.
src/utils/fs.ts (2)🤖 Prompt for all review comments with AI agents6-22: ⚡ Quick win
Verify the dynamic import pattern efficiency.
The fsPromiseModule promise is created once and awaited on every method call. While this works correctly (promises can be awaited multiple times), it adds overhead. Consider storing the resolved module instead.
♻️ Suggested optimizationfunction createNodeFs(): UnpluginContextFs { - const fsPromiseModule = import('node:fs/promises') + let fsPromise: Promise<typeof import('node:fs/promises')> | undefined return { readFile: async (path, options) => { - const fs = await fsPromiseModule + fsPromise ??= import('node:fs/promises') + const fs = await fsPromise return fs.readFile(path, options) }, stat: async (path, options) => { - const fs = await fsPromiseModule + fsPromise ??= import('node:fs/promises') + const fs = await fsPromise return fs.stat(path, options) }, lstat: async (path, options) => { - const fs = await fsPromiseModule + fsPromise ??= import('node:fs/promises') + const fs = await fsPromise return fs.lstat(path, options) }, } }Or better, cache the resolved module:
🤖 Prompt for AI Agentsfunction createNodeFs(): UnpluginContextFs { - const fsPromiseModule = import('node:fs/promises') + let fsModule: typeof import('node:fs/promises') | undefined + async function getFs() { + if (!fsModule) { + fsModule = await import('node:fs/promises') + } + return fsModule + } return { readFile: async (path, options) => { - const fs = await fsPromiseModule + const fs = await getFs() return fs.readFile(path, options) }, stat: async (path, options) => { - const fs = await fsPromiseModule + const fs = await getFs() return fs.stat(path, options) }, lstat: async (path, options) => { - const fs = await fsPromiseModule + const fs = await getFs() return fs.lstat(path, options) }, } }Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/utils/fs.ts` around lines 6 - 22, The dynamic import promise fsPromiseModule in createNodeFs is awaited on every call to readFile/stat/lstat which adds unnecessary overhead; change the implementation to cache the resolved module (e.g., keep a local variable like cachedFs or resolvedFs) and on the first call await fsPromiseModule and store the module, then use the cached module for subsequent calls in readFile, stat, and lstat to avoid repeated awaits.
28-30: ⚡ Quick win
Type assertions suppress important type checking.
The type assertions (as UnpluginContextFs['readFile']) bypass TypeScript's type checking and could hide incompatibilities between pify's output and the expected UnpluginContextFs interface.
Consider adding runtime validation or using more specific typing from pify.
💡 Alternative approachInstead of type assertions, explicitly type the pified object:
🤖 Prompt for AI Agentsexport function createBuildContextFs(inputFs?: InputFileSystem | null): UnpluginContextFs { - const fs = inputFs ? pify(inputFs) : createNodeFs() + const fs = inputFs ? pify(inputFs) as { + readFile: (path: PathLike, options?: any) => Promise<string | Uint8Array> + stat: (path: PathLike, options?: any) => Promise<Stats> + lstat: (path: PathLike, options?: any) => Promise<Stats> + } : createNodeFs() return { - readFile: fs.readFile as UnpluginContextFs['readFile'], - stat: fs.stat as UnpluginContextFs['stat'], - lstat: fs.lstat as UnpluginContextFs['lstat'], + readFile: fs.readFile, + stat: fs.stat, + lstat: fs.lstat, } }Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/utils/fs.ts` around lines 28 - 30, The current lines use type assertions (readFile: fs.readFile as UnpluginContextFs['readFile'], stat, lstat) which silence mismatches between pify(fs) output and the UnpluginContextFs shape; replace the assertions by explicitly typing the pify call (e.g. pify<UnpluginContextFs>(fs)) or by creating a typed wrapper that maps and validates each function (readFile, stat, lstat) to the expected signatures, and add minimal runtime checks (typeof fn === 'function' and argument/return shape checks or try/catch) to surface incompatible implementations rather than suppressing errors with "as". Ensure you update the assignment site where pify(fs) is produced so the resulting object already conforms to UnpluginContextFs instead of asserting per-property.
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. Nitpick comments: In `@src/utils/fs.ts`: - Around line 6-22: The dynamic import promise fsPromiseModule in createNodeFs is awaited on every call to readFile/stat/lstat which adds unnecessary overhead; change the implementation to cache the resolved module (e.g., keep a local variable like cachedFs or resolvedFs) and on the first call await fsPromiseModule and store the module, then use the cached module for subsequent calls in readFile, stat, and lstat to avoid repeated awaits. - Around line 28-30: The current lines use type assertions (readFile: fs.readFile as UnpluginContextFs['readFile'], stat, lstat) which silence mismatches between pify(fs) output and the UnpluginContextFs shape; replace the assertions by explicitly typing the pify call (e.g. pify<UnpluginContextFs>(fs)) or by creating a typed wrapper that maps and validates each function (readFile, stat, lstat) to the expected signatures, and add minimal runtime checks (typeof fn === 'function' and argument/return shape checks or try/catch) to surface incompatible implementations rather than suppressing errors with "as". Ensure you update the assignment site where pify(fs) is produced so the resulting object already conforms to UnpluginContextFs instead of asserting per-property.
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6b657dd7-39c0-455e-939a-11fe181bfc0c
📥 CommitsReviewing files that changed from the base of the PR and between 1623b46 and 953ab32.
⛔ Files ignored due to path filters (1)
Sorry, something went wrong.
There was a problem hiding this comment.
Adds a standardized this.fs interface to Unplugin’s unified build context so plugins can perform basic file operations across bundlers (and use bundler-provided virtual/input FS where available), addressing the portability gap described in #593.
Changes:
Copilot reviewed 16 out of 17 changed files in this pull request and generated 4 comments.
Show a summary per file| File | Description |
|---|---|
| test/unit-tests/webpack/context.test.ts | Asserts buildContext.fs exists and exposes readFile/stat/lstat. |
| test/unit-tests/rspack/context.test.ts | Asserts buildContext.fs exists and exposes readFile/stat/lstat. |
| test/unit-tests/resolve-id/resolve-id.test.ts | Adds fs to cross-bundler context property checks and updates assertion counting. |
| test/unit-tests/farm/context.test.ts | Asserts Farm context exposes fs methods. |
| test/unit-tests/esbuild/utils.test.ts | Asserts esbuild build context exposes fs methods. |
| test/unit-tests/bun/utils.test.ts | Asserts bun build context exposes fs methods. |
| src/webpack/context.ts | Adds fs to webpack build context using loader/compiler input FS. |
| src/rspack/context.ts | Adds fs to rspack build context using loader/compiler input FS. |
| src/farm/context.ts | Adds fs to Farm build context (Node fallback). |
| src/esbuild/utils.ts | Adds fs to esbuild build context (Node fallback). |
| src/bun/utils.ts | Adds fs to bun build context (Node fallback). |
| src/utils/fs.ts | New utility to create a unified FS wrapper (promisify input FS or Node fallback). |
| src/types.ts | Adds UnpluginContextFs and includes fs in UnpluginBuildContext. |
| docs/guide/index.md | Documents this.fs support and adds a note about fallback behavior. |
| pnpm-workspace.yaml | Adds pify to prod catalog. |
| package.json | Adds pify dependency. |
| pnpm-lock.yaml | Locks pify@6.1.0. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Sorry, something went wrong.
| export interface UnpluginContextFs { | ||
| readFile: (path: PathLike, options?: any) => Promise<string | Uint8Array> | ||
| stat: (path: PathLike, options?: any) => Promise<Stats> | ||
| lstat: (path: PathLike, options?: any) => Promise<Stats> | ||
| } |
| import type { InputFileSystem } from 'webpack' | ||
| import type { UnpluginContextFs } from '../types' | ||
| import pify from 'pify' |
| return { | ||
| readFile: async (path, options) => { | ||
| const fs = await fsPromiseModule | ||
| return fs.readFile(path, options) | ||
| }, | ||
| stat: async (path, options) => { | ||
| const fs = await fsPromiseModule | ||
| return fs.stat(path, options) | ||
| }, | ||
| lstat: async (path, options) => { | ||
| const fs = await fsPromiseModule |
|
|
||
| 1. For bundlers other than Rollup, Rolldown, or Vite, `setParseImpl` must be called to manually provide a parser implementation. Parsers such as [Acorn](https://github.com/acornjs/acorn), [Babel](https://babeljs.io/), or [Oxc](https://oxc.rs/) can be used. | ||
| 2. Currently, [`this.emitFile`](https://rollupjs.org/plugin-development/#thisemitfile) only supports the `EmittedAsset` variant. | ||
| 3. For bundlers without native plugin context file-system APIs, `this.fs` falls back to a Node-compatible implementation. |
| Back | FazBrowse Home | New Git URL |
resolves #593 .
Summary by CodeRabbit
New Features
Documentation