feat(watcher): merge @eggjs/watcher plugin into monorepo#5497
Conversation
Move @eggjs/watcher plugin from separate repository to plugins/watcher. Configure for monorepo with workspace dependencies and standard tsdown build. - Add watcher plugin to plugins directory - Configure package.json with workspace and catalog dependencies - Add standard tsdown.config.ts following plugin conventions - Update test files to use vitest imports - Add vitest.config.ts for test configuration - Update root tsconfig.json references - Add camelcase to pnpm catalog dependencies - Document watcher plugin in CLAUDE.md 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
WalkthroughIntroduces a new @eggjs/watcher plugin with boot lifecycle, event sources (default and development), configuration, packaging, and tests. Integrates typed watcher access into development and mock agents. Adjusts Egg typings to make watcher config optional. Updates scripts to run clean before typecheck and workspace/catalog settings. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Dev as Developer
participant App as Egg App
participant Boot as Watcher Boot
participant Watcher as Watcher
participant ES as EventSource (development)
participant Router as App Router
Dev->>App: start (env=local/unittest)
App->>Boot: construct
Boot->>Watcher: new Watcher(config)
Note over Watcher,ES: Select event source by watcher.type
Watcher->>ES: instantiate + subscribe(events)
Boot->>App: attach app.watcher
Boot->>App: didLoad() -> watcher.ready()
Dev->>Router: GET /app-watch
Router->>Watcher: watch([paths], listener)
ES-->>Watcher: change/info/warn/error events (async)
Watcher-->>Router: listener(ChangeInfo)
Note over Router: Updates counters, logs
Dev->>Router: GET /app-msg
Router-->>Dev: count
sequenceDiagram
autonumber
actor Dev as Developer
participant NPM as npm/pnpm
Dev->>NPM: pnpm run typecheck
NPM->>NPM: run "clean"
alt clean succeeds
NPM->>NPM: run "-r typecheck"
else clean fails
NPM-->>Dev: exit (typecheck not executed)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
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. Comment |
|
All alerts resolved. Learn more about Socket for GitHub. This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## next #5497 +/- ##
==========================================
+ Coverage 76.20% 76.34% +0.14%
==========================================
Files 108 113 +5
Lines 5774 5885 +111
Branches 1215 1233 +18
==========================================
+ Hits 4400 4493 +93
- Misses 1199 1215 +16
- Partials 175 177 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull Request Overview
This PR merges the @eggjs/watcher plugin from a separate repository into the main Eggjs monorepo under plugins/watcher. The watcher plugin provides file system watching capabilities and is a core component used by the development plugin for auto-reload functionality.
- Move complete watcher plugin source code, tests, and documentation from external repo
- Configure plugin for monorepo with workspace dependencies and standardized build tooling
- Update dependent packages to use workspace reference instead of catalog reference
Reviewed Changes
Copilot reviewed 57 out of 60 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-workspace.yaml | Remove external @eggjs/watcher from catalog and add camelcase dependency |
| plugins/watcher/* | Complete watcher plugin implementation with TypeScript source, tests, configs |
| packages/egg/package.json | Switch @eggjs/watcher dependency from catalog to workspace reference |
| plugins/development/src/agent.ts | Remove FIXME comment and type casting after watcher merge |
| packages/mock/src/lib/parallel/agent.ts | Remove unnecessary type casting |
| packages/egg/test/index.test-d.ts | Update type expectations to handle optional watcher config |
| package.json | Update typecheck script to clean before running |
| CLAUDE.md | Document the merged watcher plugin |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
There was a problem hiding this comment.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
plugins/development/src/agent.ts (1)
80-99: Guard against missing watcher to avoid runtime crash.If the watcher plugin isn’t enabled/booted,
agent.watcherwill be undefined and this throws at runtime. Add a null‑guard and bail early.Apply this diff:
- agent.watcher.watch(watchDirs, (info: any) => { + if (!agent.watcher) { + logger.warn('[agent:development] watcher plugin not enabled; skip reload watching'); + return; + } + agent.watcher.watch(watchDirs, (info: any) => {
🧹 Nitpick comments (22)
packages/mock/src/lib/parallel/agent.ts (2)
71-75: Tiny typing improvement: givemsgan explicit typeHelps catch action/data shape drift at compile time.
- const msg = { + const msg: { action: 'egg-ready'; data: MockApplicationOptions } = { action: 'egg-ready', data: this.options, };
79-91: Clear cached listener sets after binding to free memoryAfter re-binding, the sets aren’t used again; clearing avoids holding unnecessary array refs.
#bindEvents() { debug('bind cache events to agent'); for (const args of this.#initOnListeners) { debug('on(%s), use cache and pass to agent', args); this._instance.on(args[0], args[1]); this.removeListener(args[0], args[1]); } for (const args of this.#initOnceListeners) { debug('once(%s), use cache and pass to agent', args); this._instance.once(args[0], args[1]); this.removeListener(args[0], args[1]); } + this.#initOnListeners.clear(); + this.#initOnceListeners.clear(); }plugins/development/src/agent.ts (1)
41-56: Optional: skip setting up watch when reload is disabled.You check
reloadOnDebuginside the callback; we can avoid attaching watchers at all for lower overhead.Apply this diff:
- // watch dirs to reload worker, will debounce 200ms + // watch dirs to reload worker, will debounce 200ms + if (!config.reloadOnDebug) return;Also applies to: 80-99
package.json (1)
33-33: Pre‑clean before typecheck is fine; expand clean to cover plugins’ dists.Your root clean removes only
packages/**/dist. Since watcher lives underplugins/, include that too.Outside this hunk, update scripts.clean to:
"clean": "pnpm -r run clean && rimraf packages/**/dist plugins/**/dist"plugins/watcher/test/fixtures/apps/watcher-type-default/config/config.unittest.js (1)
1-5: Fixture looks correct; consider adding 'use strict' for consistency.Matches the default watcher type expected by tests. Optional: add 'use strict' to align with other fixtures (e.g., fuzzy custom).
+'use strict'; + exports.watcher = { type: 'default', }; exports.keys = 'testkey';plugins/watcher/tsdown.config.ts (1)
1-10: Tsdown config aligns with the standard; verify parity with plugins/development template.Looks good: unbundled ESM, DTS, devExports. Please double‑check against plugins/development/tsdown.config.ts to keep all knobs in sync (platform/target/sourcemap, if the template has them).
export default defineConfig({ entry: 'src/**/*.ts', unbundle: true, dts: true, exports: { devExports: true, }, + // Optional: keep parity with template if used there + // platform: 'node', + // target: 'node18', + // sourcemap: true, });plugins/watcher/test/fixtures/apps/watcher-custom-event-source/plugins/egg-watcher-custom/package.json (1)
1-9: Minimal test plugin manifest is fine; mark private to avoid accidental publish.Add
"private": trueto prevent accidental release from fixtures.{ "name": "egg-watcher-custom", + "private": true, "eggPlugin": { "name": "watcherCustom", "dep": [ "watcher" ] } }packages/egg/test/index.test-d.ts (1)
47-48: Optional chaining on nested field: consider guarding eventSources as well (depends on typings).If
eventSourcesitself can be absent, prefer chaining there too; otherwise current assertion is fine. Please confirm with packages/egg/src/index.d.ts.-expectType<string | undefined>(app.config.watcher?.eventSources.default); +expectType<string | undefined>(app.config.watcher?.eventSources?.default);plugins/watcher/test/fixtures/apps/watcher-custom-event-source/plugins/egg-watcher-custom/custom.js (2)
10-19: Make watch() idempotent to avoid timer leaks if called multiple timesIf watch() is called repeatedly without an intervening unwatch(), multiple intervals will accumulate and emit duplicate change events.
Apply this minimal guard:
watch(path) { this.emit('info', 'info12345'); this.emit('warn', 'warn12345'); - this._h = setInterval(() => { + if (this._h) clearInterval(this._h); + this._h = setInterval(() => { this.emit('change', { path, foo: this._options.foo, }); }, 1000); }
21-25: Reset handle after clearInterval for correctnessAfter clearing, null out the handle so subsequent watch() calls can reliably detect prior state.
unwatch() { if (this._h) { clearInterval(this._h); + this._h = null; } }plugins/watcher/CHANGELOG.md (1)
1-8: Silence markdownlint for imported historical changelog formattingThis file preserves upstream history and intentionally mixes styles. Add a file-local disable to keep CI green without rewriting history.
# Changelog + +<!-- markdownlint-disable MD003 MD004 MD007 MD010 MD034 MD036 -->plugins/watcher/README.md (2)
55-55: Update the development mode link to the monorepo pathThe current link points to the old repository. Use a relative link so it stays valid after moves/renames.
-There's a built-in [development mode](https://github.com/eggjs/watcher/blob/master/src/lib/event-sources/development.ts) which works in local(env is `local`). Once files on disk is modified it will emit a `change` event immediately. +There's a built-in [development mode](./src/lib/event-sources/development.ts) which works in local (env is `local`). Once files on disk are modified it will emit a `change` event immediately.
76-83: Document optional isDirectory flag in change payloadRecent fixes added isDirectory; reflecting this improves DX and test expectations.
// when aware of any change, emit a `change` event - // with an info object containing `path` property - // specifying the changed directory or file. + // with an info object containing `path` and optionally + // `isDirectory: boolean`, specifying the changed entry. this._h = setInterval(() => { this.emit('change', { path }); }, 1000);plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/package.json (1)
1-9: Mark fixture package as private to avoid accidental publishFile: plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/package.json
{ "name": "egg-watcher-custom", + "private": true, "eggPlugin": { "name": "watcherCustom", "dep": [ "watcher" ] } }plugins/watcher/src/config/config.default.ts (1)
1-23: Optional: avoid repeatedgetSourceDirname()calls.Minor readability win; not required.
export default { /** * watcher options */ - watcher: { + watcher: { + // const base = path.join(getSourceDirname(), 'lib', 'event-sources'); + // eventSources: { default: path.join(base, 'default.js'), development: path.join(base, 'development.js') } ...plugins/watcher/src/lib/event-sources/default.ts (1)
7-13: Minor: preferqueueMicrotaskoversetImmediatefor same‑turn deferral.Not required, but slightly lighter when only ordering is needed.
- setImmediate(() => + queueMicrotask(() => this.emit( 'info', '[@eggjs/watcher] defaultEventSource watcher will NOT take effect' ) );plugins/watcher/test/development.test.ts (1)
56-57: Replace deprecated rmdirSync + redundant rmSync with a single recursive rmSync; avoid hard-coded waitsFile: plugins/watcher/test/development.test.ts (lines 56–57)
- fs.rmdirSync(file_path4); - fs.rmSync(file_path4, { force: true }); + fs.rmSync(file_path4, { recursive: true, force: true });
- For watcher flakiness: do not blindly increase 100ms→200ms; Vitest docs/community don’t recommend hard waits — prefer proper async synchronization (await file ops, use wait-for/poll helpers) or Vitest watch options (forceRerunTriggers / watchTriggerPatterns).
plugins/watcher/test/fixtures/apps/watcher-development-app/agent.js (1)
16-24: Guard unwatch: Watcher.unwatch is not implemented yetCalling
agent.watcher.unwatchwill throw if exposed watcher lacks this API (currently commented out).- agent.messenger.on('agent-unwatch', function () { - agent.watcher.unwatch([file_path1, dir_path], listener); - agent.messenger.broadcast('agent-unwatch-success', 'agent unwatch success'); - }); + agent.messenger.on('agent-unwatch', function () { + if (typeof agent.watcher.unwatch === 'function') { + agent.watcher.unwatch([file_path1, dir_path], listener); + agent.messenger.broadcast('agent-unwatch-success', 'agent unwatch success'); + } else { + agent.messenger.broadcast('agent-unwatch-success', 'agent unwatch skipped (not supported)'); + } + });plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/custom.js (1)
13-19: Avoid multiple intervals if watch() is called more than onceClear any existing timer before creating a new one to prevent leaks.
watch(path) { this.emit('info', 'info12345'); this.emit('warn', 'warn12345'); + if (this._h) clearInterval(this._h); this._h = setInterval(() => { this.emit('fuzzy-change', { path: '/home/admin', foo: this._options.foo, }); }, 1000); }plugins/watcher/test/development_cluster.test.ts (2)
53-58: Remove redundant rmdir; use rmSync recursive onceSimpler and avoids deprecated rmdir usage.
fs.writeFileSync(file_path2, 'aaa'); fs.writeFileSync(file_path3, 'aaa'); - fs.mkdirSync(file_path4, { recursive: true }); - fs.rmdirSync(file_path4); - fs.rmSync(file_path4, { force: true }); + fs.mkdirSync(file_path4, { recursive: true }); + fs.rmSync(file_path4, { recursive: true, force: true });
43-46: Reduce flakiness: increase waits slightly100ms is tight on CI; consider 200–300ms.
Also applies to: 58-58, 97-99
plugins/watcher/src/lib/event-sources/development.ts (1)
54-56: Propagate fs.watch errors via warn eventsSurface watcher errors instead of failing silently.
- // 保存 handler,用于解除监听 + // 监听底层错误并透出 + handler.on('error', err => { + this.emit( + 'warn', + '[@eggjs/watcher:DevelopmentEventSource] fs.watch error on %o: %s', + file, + err + ); + }); + // 保存 handler,用于解除监听 this.#fileWatching.set(file, handler);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
plugins/watcher/test/__snapshots__/index.test.ts.snapis excluded by!**/*.snappnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (57)
CLAUDE.md(1 hunks)package.json(1 hunks)packages/egg/package.json(1 hunks)packages/egg/test/index.test-d.ts(1 hunks)packages/mock/src/lib/parallel/agent.ts(1 hunks)plugins/development/src/agent.ts(1 hunks)plugins/watcher/CHANGELOG.md(1 hunks)plugins/watcher/LICENSE(1 hunks)plugins/watcher/README.md(1 hunks)plugins/watcher/package.json(1 hunks)plugins/watcher/src/agent.ts(1 hunks)plugins/watcher/src/app.ts(1 hunks)plugins/watcher/src/config/config.default.ts(1 hunks)plugins/watcher/src/config/config.local.ts(1 hunks)plugins/watcher/src/config/config.unittest.ts(1 hunks)plugins/watcher/src/index.ts(1 hunks)plugins/watcher/src/lib/boot.ts(1 hunks)plugins/watcher/src/lib/event-sources/base.ts(1 hunks)plugins/watcher/src/lib/event-sources/default.ts(1 hunks)plugins/watcher/src/lib/event-sources/development.ts(1 hunks)plugins/watcher/src/lib/event-sources/index.ts(1 hunks)plugins/watcher/src/lib/types.ts(1 hunks)plugins/watcher/src/lib/utils.ts(1 hunks)plugins/watcher/src/lib/watcher.ts(1 hunks)plugins/watcher/test/development.test.ts(1 hunks)plugins/watcher/test/development_cluster.test.ts(1 hunks)plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/config/config.unittest.js(1 hunks)plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/config/plugin.js(1 hunks)plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/package.json(1 hunks)plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/config/config.default.js(1 hunks)plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/custom.js(1 hunks)plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/package.json(1 hunks)plugins/watcher/test/fixtures/apps/watcher-custom-event-source/config/config.unittest.js(1 hunks)plugins/watcher/test/fixtures/apps/watcher-custom-event-source/config/plugin.js(1 hunks)plugins/watcher/test/fixtures/apps/watcher-custom-event-source/package.json(1 hunks)plugins/watcher/test/fixtures/apps/watcher-custom-event-source/plugins/egg-watcher-custom/config/config.default.js(1 hunks)plugins/watcher/test/fixtures/apps/watcher-custom-event-source/plugins/egg-watcher-custom/custom.js(1 hunks)plugins/watcher/test/fixtures/apps/watcher-custom-event-source/plugins/egg-watcher-custom/package.json(1 hunks)plugins/watcher/test/fixtures/apps/watcher-development-app/agent.js(1 hunks)plugins/watcher/test/fixtures/apps/watcher-development-app/app/router.js(1 hunks)plugins/watcher/test/fixtures/apps/watcher-development-app/config/config.unittest.js(1 hunks)plugins/watcher/test/fixtures/apps/watcher-development-app/package.json(1 hunks)plugins/watcher/test/fixtures/apps/watcher-development-app/tmp-agent.txt(1 hunks)plugins/watcher/test/fixtures/apps/watcher-development-app/tmp.txt(1 hunks)plugins/watcher/test/fixtures/apps/watcher-development-app/tmp/t1/t2/t3/t4/tmp.txt(1 hunks)plugins/watcher/test/fixtures/apps/watcher-development-app/tmp/tmp.txt(1 hunks)plugins/watcher/test/fixtures/apps/watcher-type-default/app.js(1 hunks)plugins/watcher/test/fixtures/apps/watcher-type-default/config/config.unittest.js(1 hunks)plugins/watcher/test/fixtures/apps/watcher-type-default/package.json(1 hunks)plugins/watcher/test/index.test.ts(1 hunks)plugins/watcher/test/utils.ts(1 hunks)plugins/watcher/test/watcher.test.ts(1 hunks)plugins/watcher/tsconfig.json(1 hunks)plugins/watcher/tsdown.config.ts(1 hunks)plugins/watcher/vitest.config.ts(1 hunks)pnpm-workspace.yaml(1 hunks)tsconfig.json(1 hunks)
🧰 Additional context used
📓 Path-based instructions (13)
plugins/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place all Egg framework plugins under the plugins/ directory
Files:
plugins/watcher/test/utils.tsplugins/watcher/test/fixtures/apps/watcher-development-app/tmp/t1/t2/t3/t4/tmp.txtplugins/watcher/test/fixtures/apps/watcher-development-app/tmp/tmp.txtplugins/watcher/LICENSEplugins/watcher/test/index.test.tsplugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/config/config.default.jsplugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/config/plugin.jsplugins/watcher/test/fixtures/apps/watcher-custom-event-source/plugins/egg-watcher-custom/config/config.default.jsplugins/watcher/test/fixtures/apps/watcher-custom-event-source/plugins/egg-watcher-custom/custom.jsplugins/watcher/test/fixtures/apps/watcher-development-app/tmp-agent.txtplugins/watcher/test/fixtures/apps/watcher-custom-event-source/package.jsonplugins/watcher/test/fixtures/apps/watcher-development-app/package.jsonplugins/watcher/test/fixtures/apps/watcher-type-default/app.jsplugins/watcher/tsconfig.jsonplugins/watcher/test/fixtures/apps/watcher-type-default/package.jsonplugins/watcher/test/fixtures/apps/watcher-custom-event-source/plugins/egg-watcher-custom/package.jsonplugins/watcher/test/fixtures/apps/watcher-development-app/config/config.unittest.jsplugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/custom.jsplugins/watcher/test/fixtures/apps/watcher-development-app/app/router.jsplugins/watcher/test/fixtures/apps/watcher-custom-event-source/config/plugin.jsplugins/watcher/test/fixtures/apps/watcher-custom-event-source/config/config.unittest.jsplugins/watcher/src/lib/event-sources/base.tsplugins/watcher/test/development.test.tsplugins/watcher/src/agent.tsplugins/watcher/src/index.tsplugins/watcher/src/lib/event-sources/index.tsplugins/watcher/README.mdplugins/watcher/test/fixtures/apps/watcher-development-app/agent.jsplugins/watcher/src/app.tsplugins/watcher/test/watcher.test.tsplugins/watcher/src/config/config.unittest.tsplugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/config/config.unittest.jsplugins/watcher/src/lib/event-sources/default.tsplugins/watcher/test/fixtures/apps/watcher-development-app/tmp.txtplugins/watcher/src/lib/boot.tsplugins/watcher/test/fixtures/apps/watcher-type-default/config/config.unittest.jsplugins/watcher/src/lib/utils.tsplugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/package.jsonplugins/watcher/test/development_cluster.test.tsplugins/watcher/src/lib/types.tsplugins/watcher/src/config/config.default.tsplugins/watcher/src/config/config.local.tsplugins/watcher/tsdown.config.tsplugins/development/src/agent.tsplugins/watcher/vitest.config.tsplugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/package.jsonplugins/watcher/src/lib/watcher.tsplugins/watcher/package.jsonplugins/watcher/src/lib/event-sources/development.tsplugins/watcher/CHANGELOG.md
packages/**/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use TypeScript for all source files in packages (no JavaScript source files)
Files:
packages/mock/src/lib/parallel/agent.ts
{packages,plugins}/**/test/**/*.test.ts
📄 CodeRabbit inference engine (CLAUDE.md)
{packages,plugins}/**/test/**/*.test.ts: Vitest test files should follow the naming pattern test/**/*.test.ts
In Vitest tests, import test functions from vitest and use Node.js built-in assert for assertions
Files:
plugins/watcher/test/index.test.tsplugins/watcher/test/development.test.tsplugins/watcher/test/watcher.test.tsplugins/watcher/test/development_cluster.test.ts
plugins/**/package.json
📄 CodeRabbit inference engine (CLAUDE.md)
plugins/**/package.json: Plugin package.json must include eggPlugin configuration
Plugin package.json must set type: module and define exports that point to TypeScript sources (e.g., ./src/index.ts, ./src/agent.ts, ./src/app.ts)
Plugin package.json must define publishConfig.exports mirroring exports to compiled dist/*.js and include files: ["dist"]
Plugin package.json must include scripts: build (tsdown), clean (rimraf dist), and prepublishOnly (npm run build)
Files:
plugins/watcher/test/fixtures/apps/watcher-custom-event-source/package.jsonplugins/watcher/test/fixtures/apps/watcher-development-app/package.jsonplugins/watcher/test/fixtures/apps/watcher-type-default/package.jsonplugins/watcher/test/fixtures/apps/watcher-custom-event-source/plugins/egg-watcher-custom/package.jsonplugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/package.jsonplugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/package.jsonplugins/watcher/package.json
{packages,plugins,tools}/**/package.json
📄 CodeRabbit inference engine (CLAUDE.md)
{packages,plugins,tools}/**/package.json: Use workspace:* for internal workspace dependencies
Use catalog: for external dependencies defined in pnpm-workspace.yaml
Files:
plugins/watcher/test/fixtures/apps/watcher-custom-event-source/package.jsonplugins/watcher/test/fixtures/apps/watcher-development-app/package.jsonplugins/watcher/test/fixtures/apps/watcher-type-default/package.jsonplugins/watcher/test/fixtures/apps/watcher-custom-event-source/plugins/egg-watcher-custom/package.jsonpackages/egg/package.jsonplugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/package.jsonplugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/package.jsonplugins/watcher/package.json
{packages,plugins,tools}/**/tsconfig.json
📄 CodeRabbit inference engine (CLAUDE.md)
All sub-project tsconfig.json must extend the root tsconfig.json via "extends": "../../tsconfig.json" and include "baseUrl": "./"
Files:
plugins/watcher/tsconfig.json
packages/*/package.json
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use "workspace:*" for internal workspace dependencies in package.json
Files:
packages/egg/package.json
pnpm-workspace.yaml
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Define dependency versions via the catalog section in pnpm-workspace.yaml and reference them with "catalog:"
Add and manage external dependency versions in the catalog section of pnpm-workspace.yaml
Files:
pnpm-workspace.yaml
plugins/**/tsdown.config.ts
📄 CodeRabbit inference engine (CLAUDE.md)
All future plugins must use the standard tsdown configuration template (based on plugins/development/tsdown.config.ts)
Files:
plugins/watcher/tsdown.config.ts
{packages,plugins,tools}/**/tsdown.config.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Each sub-package should use tsdown for TypeScript builds with a tsdown.config.ts file
Files:
plugins/watcher/tsdown.config.ts
{packages,plugins}/**/vitest.config.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Each package (except egg-bin) must include a vitest.config.ts for testing
Files:
plugins/watcher/vitest.config.ts
{tsconfig.json,packages/*/tsconfig.json}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Enable strict TypeScript mode across all packages
Files:
tsconfig.json
tsconfig.json
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Root tsconfig.json must include all packages in the references array
Files:
tsconfig.json
🧠 Learnings (22)
📚 Learning: 2025-09-14T14:28:36.647Z
Learnt from: CR
PR: eggjs/egg#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-14T14:28:36.647Z
Learning: Applies to {packages,plugins}/**/test/**/*.test.ts : Vitest test files should follow the naming pattern test/**/*.test.ts
Applied to files:
plugins/watcher/test/index.test.tsplugins/watcher/test/development.test.tsplugins/watcher/test/watcher.test.tsplugins/watcher/test/development_cluster.test.tsplugins/watcher/vitest.config.ts
📚 Learning: 2025-09-14T14:28:36.647Z
Learnt from: CR
PR: eggjs/egg#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-14T14:28:36.647Z
Learning: Applies to {packages,plugins}/**/test/**/*.test.ts : In Vitest tests, import test functions from vitest and use Node.js built-in assert for assertions
Applied to files:
plugins/watcher/test/index.test.tsplugins/watcher/test/development.test.tsplugins/watcher/test/watcher.test.tsplugins/watcher/test/development_cluster.test.ts
📚 Learning: 2025-09-14T08:41:30.606Z
Learnt from: CR
PR: eggjs/egg#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-14T08:41:30.606Z
Learning: Applies to packages/**/test/**/*.test.ts : Name test files as test/**/*.test.ts and run them with Vitest
Applied to files:
plugins/watcher/test/index.test.tsplugins/watcher/test/development.test.tsplugins/watcher/test/watcher.test.tsplugins/watcher/test/development_cluster.test.tsplugins/watcher/vitest.config.ts
📚 Learning: 2025-09-14T14:28:36.647Z
Learnt from: CR
PR: eggjs/egg#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-14T14:28:36.647Z
Learning: Applies to {packages,plugins}/**/vitest.config.ts : Each package (except egg-bin) must include a vitest.config.ts for testing
Applied to files:
plugins/watcher/test/index.test.tsplugins/watcher/test/development.test.tsplugins/watcher/test/watcher.test.tsplugins/watcher/test/development_cluster.test.tsplugins/watcher/vitest.config.tspackages/egg/test/index.test-d.ts
📚 Learning: 2025-09-14T08:41:30.606Z
Learnt from: CR
PR: eggjs/egg#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-14T08:41:30.606Z
Learning: Applies to packages/**/test/**/*.test.ts : Use import { describe, it } from 'vitest' in tests
Applied to files:
plugins/watcher/test/index.test.tsplugins/watcher/test/watcher.test.tsplugins/watcher/vitest.config.ts
📚 Learning: 2025-09-14T08:41:30.606Z
Learnt from: CR
PR: eggjs/egg#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-14T08:41:30.606Z
Learning: Applies to packages/**/test/**/*.test.ts : Use Node.js built-in assert module for test assertions
Applied to files:
plugins/watcher/test/index.test.tsplugins/watcher/test/watcher.test.ts
📚 Learning: 2025-09-14T14:28:36.647Z
Learnt from: CR
PR: eggjs/egg#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-14T14:28:36.647Z
Learning: Applies to {packages,plugins,tools}/**/tsconfig.json : All sub-project tsconfig.json must extend the root tsconfig.json via "extends": "../../tsconfig.json" and include "baseUrl": "./"
Applied to files:
plugins/watcher/tsconfig.json
📚 Learning: 2025-09-14T14:28:36.647Z
Learnt from: CR
PR: eggjs/egg#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-14T14:28:36.647Z
Learning: Applies to plugins/**/tsdown.config.ts : All future plugins must use the standard tsdown configuration template (based on plugins/development/tsdown.config.ts)
Applied to files:
plugins/watcher/tsconfig.jsonplugins/watcher/tsdown.config.ts
📚 Learning: 2025-09-14T08:41:30.606Z
Learnt from: CR
PR: eggjs/egg#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-14T08:41:30.606Z
Learning: Applies to packages/*/tsconfig.json : All sub-project tsconfig.json files must extend from the root via "extends": "../../tsconfig.json"
Applied to files:
plugins/watcher/tsconfig.json
📚 Learning: 2025-09-14T14:28:36.647Z
Learnt from: CR
PR: eggjs/egg#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-14T14:28:36.647Z
Learning: Applies to plugins/**/package.json : Plugin package.json must include eggPlugin configuration
Applied to files:
plugins/watcher/test/fixtures/apps/watcher-custom-event-source/plugins/egg-watcher-custom/package.jsonpackages/egg/package.jsonplugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/package.jsonplugins/watcher/package.json
📚 Learning: 2025-09-14T14:28:36.647Z
Learnt from: CR
PR: eggjs/egg#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-14T14:28:36.647Z
Learning: Applies to {packages,plugins,tools}/**/package.json : Use catalog: for external dependencies defined in pnpm-workspace.yaml
Applied to files:
packages/egg/package.jsonpnpm-workspace.yaml
📚 Learning: 2025-09-14T08:41:30.606Z
Learnt from: CR
PR: eggjs/egg#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-14T08:41:30.606Z
Learning: Applies to packages/*/package.json : Use "workspace:*" for internal workspace dependencies in package.json
Applied to files:
packages/egg/package.json
📚 Learning: 2025-09-14T14:28:36.647Z
Learnt from: CR
PR: eggjs/egg#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-14T14:28:36.647Z
Learning: Applies to {packages,plugins,tools}/**/package.json : Use workspace:* for internal workspace dependencies
Applied to files:
packages/egg/package.json
📚 Learning: 2025-09-14T08:41:30.606Z
Learnt from: CR
PR: eggjs/egg#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-14T08:41:30.606Z
Learning: Applies to pnpm-workspace.yaml : Define dependency versions via the catalog section in pnpm-workspace.yaml and reference them with "catalog:"
Applied to files:
pnpm-workspace.yaml
📚 Learning: 2025-09-14T14:28:36.647Z
Learnt from: CR
PR: eggjs/egg#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-14T14:28:36.647Z
Learning: Applies to pnpm-workspace.yaml : Add and manage external dependency versions in the catalog section of pnpm-workspace.yaml
Applied to files:
pnpm-workspace.yaml
📚 Learning: 2025-09-14T14:28:36.647Z
Learnt from: CR
PR: eggjs/egg#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-14T14:28:36.647Z
Learning: Applies to tools/egg-bin/test/**/*.test.ts : The egg-bin tool uses Mocha with test files named test/**/*.test.ts
Applied to files:
plugins/watcher/test/development.test.tsplugins/watcher/test/watcher.test.tsplugins/watcher/test/development_cluster.test.tspackages/egg/test/index.test-d.ts
📚 Learning: 2025-09-14T14:28:36.647Z
Learnt from: CR
PR: eggjs/egg#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-14T14:28:36.647Z
Learning: Applies to plugins/**/package.json : Plugin package.json must include scripts: build (tsdown), clean (rimraf dist), and prepublishOnly (npm run build)
Applied to files:
package.json
📚 Learning: 2025-09-14T14:28:36.647Z
Learnt from: CR
PR: eggjs/egg#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-14T14:28:36.647Z
Learning: Applies to {packages,plugins,tools}/**/tsdown.config.ts : Each sub-package should use tsdown for TypeScript builds with a tsdown.config.ts file
Applied to files:
plugins/watcher/tsdown.config.ts
📚 Learning: 2025-09-14T08:41:30.606Z
Learnt from: CR
PR: eggjs/egg#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-14T08:41:30.606Z
Learning: Applies to packages/*/tsdown.config.ts : Each package must provide a tsdown.config.ts for unbundled ESM builds
Applied to files:
plugins/watcher/tsdown.config.ts
📚 Learning: 2025-09-14T08:41:30.606Z
Learnt from: CR
PR: eggjs/egg#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-14T08:41:30.606Z
Learning: Applies to tsconfig.json : Root tsconfig.json must include all packages in the references array
Applied to files:
tsconfig.json
📚 Learning: 2025-09-14T14:28:36.647Z
Learnt from: CR
PR: eggjs/egg#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-14T14:28:36.647Z
Learning: Applies to plugins/**/package.json : Plugin package.json must define publishConfig.exports mirroring exports to compiled dist/*.js and include files: ["dist"]
Applied to files:
plugins/watcher/package.json
📚 Learning: 2025-09-14T14:28:36.647Z
Learnt from: CR
PR: eggjs/egg#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-14T14:28:36.647Z
Learning: Applies to plugins/**/package.json : Plugin package.json must set type: module and define exports that point to TypeScript sources (e.g., ./src/index.ts, ./src/agent.ts, ./src/app.ts)
Applied to files:
plugins/watcher/package.json
🧬 Code graph analysis (23)
plugins/watcher/test/utils.ts (3)
scripts/version.js (1)
__filename(9-9)plugins/watcher/test/fixtures/apps/watcher-development-app/agent.js (1)
path(1-1)plugins/watcher/test/fixtures/apps/watcher-development-app/app/router.js (1)
path(1-1)
plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/config/config.default.js (1)
plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/custom.js (1)
require(1-1)
plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/config/plugin.js (1)
plugins/watcher/test/fixtures/apps/watcher-custom-event-source/config/plugin.js (1)
path(1-1)
plugins/watcher/test/fixtures/apps/watcher-custom-event-source/plugins/egg-watcher-custom/config/config.default.js (1)
plugins/watcher/test/fixtures/apps/watcher-custom-event-source/plugins/egg-watcher-custom/custom.js (1)
require(1-1)
plugins/watcher/test/fixtures/apps/watcher-custom-event-source/plugins/egg-watcher-custom/custom.js (1)
plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/custom.js (1)
require(1-1)
plugins/watcher/test/fixtures/apps/watcher-development-app/app/router.js (2)
plugins/watcher/test/fixtures/apps/watcher-development-app/agent.js (3)
path(1-1)file_path1(3-3)dir_path(4-4)plugins/watcher/src/lib/watcher.ts (2)
info(101-117)info(119-136)
plugins/watcher/test/fixtures/apps/watcher-custom-event-source/config/plugin.js (1)
plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/config/plugin.js (1)
path(3-3)
plugins/watcher/src/lib/event-sources/base.ts (1)
plugins/watcher/src/lib/event-sources/index.ts (1)
BaseEventSource(5-5)
plugins/watcher/test/development.test.ts (3)
plugins/watcher/test/fixtures/apps/watcher-development-app/agent.js (2)
file_path1(3-3)count(7-7)plugins/watcher/test/fixtures/apps/watcher-development-app/app/router.js (1)
file_path1(3-3)plugins/watcher/test/utils.ts (1)
getFilePath(8-10)
plugins/watcher/src/agent.ts (1)
plugins/watcher/src/lib/boot.ts (1)
Boot(5-34)
plugins/watcher/test/fixtures/apps/watcher-development-app/agent.js (1)
plugins/watcher/test/fixtures/apps/watcher-development-app/app/router.js (3)
path(1-1)file_path1(3-3)dir_path(4-4)
plugins/watcher/src/app.ts (1)
plugins/watcher/src/lib/boot.ts (1)
Boot(5-34)
plugins/watcher/test/watcher.test.ts (3)
plugins/watcher/test/utils.ts (1)
getFilePath(8-10)plugins/watcher/src/lib/watcher.ts (2)
info(101-117)info(119-136)plugins/watcher/src/lib/types.ts (1)
ChangeInfo(18-26)
plugins/watcher/src/config/config.unittest.ts (1)
plugins/watcher/src/lib/types.ts (1)
WatcherConfig(5-16)
plugins/watcher/src/lib/boot.ts (1)
plugins/watcher/src/lib/watcher.ts (1)
Watcher(16-137)
plugins/watcher/src/lib/utils.ts (1)
scripts/version.js (1)
__filename(9-9)
plugins/watcher/test/development_cluster.test.ts (3)
plugins/watcher/test/fixtures/apps/watcher-development-app/agent.js (1)
file_path1(3-3)plugins/watcher/test/fixtures/apps/watcher-development-app/app/router.js (1)
file_path1(3-3)plugins/watcher/test/utils.ts (1)
getFilePath(8-10)
plugins/watcher/src/lib/types.ts (2)
plugins/watcher/src/lib/watcher.ts (1)
Watcher(16-137)packages/egg/src/lib/types.ts (1)
EggAppConfig(97-271)
plugins/watcher/src/config/config.default.ts (2)
plugins/watcher/src/lib/utils.ts (1)
getSourceDirname(11-20)plugins/watcher/src/lib/types.ts (1)
WatcherConfig(5-16)
plugins/watcher/src/config/config.local.ts (1)
plugins/watcher/src/lib/types.ts (1)
WatcherConfig(5-16)
plugins/development/src/agent.ts (2)
packages/egg/src/lib/core/base_hook_class.ts (1)
agent(31-37)plugins/watcher/src/lib/watcher.ts (2)
info(101-117)info(119-136)
plugins/watcher/src/lib/watcher.ts (4)
plugins/watcher/src/lib/types.ts (1)
ChangeInfo(18-26)packages/egg/src/lib/types.ts (1)
EggAppConfig(97-271)packages/utils/src/import.ts (1)
importModule(450-522)plugins/watcher/src/lib/utils.ts (1)
isEqualOrParentPath(7-9)
plugins/watcher/src/lib/event-sources/development.ts (2)
plugins/watcher/src/lib/watcher.ts (2)
info(101-117)info(119-136)plugins/watcher/src/lib/types.ts (1)
ChangeInfo(18-26)
🪛 markdownlint-cli2 (0.17.2)
plugins/watcher/CHANGELOG.md
43-43: Bare URL used
(MD034, no-bare-urls)
45-45: Bare URL used
(MD034, no-bare-urls)
52-52: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
53-53: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
55-55: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
57-57: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
59-59: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
62-62: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
63-63: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
66-66: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
67-67: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
67-67: Unordered list indentation
Expected: 2; Actual: 1
(MD007, ul-indent)
67-67: Hard tabs
Column: 1
(MD010, no-hard-tabs)
69-69: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
70-70: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
73-73: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
74-74: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
76-76: Unordered list style
Expected: asterisk; Actual: dash
(MD004, ul-style)
76-76: Unordered list indentation
Expected: 2; Actual: 1
(MD007, ul-indent)
76-76: Hard tabs
Column: 1
(MD010, no-hard-tabs)
84-84: Heading style
Expected: atx; Actual: setext
(MD003, heading-style)
87-87: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
88-88: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
90-90: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
91-91: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
93-93: Heading style
Expected: atx; Actual: setext
(MD003, heading-style)
96-96: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
97-97: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
99-99: Heading style
Expected: atx; Actual: setext
(MD003, heading-style)
102-102: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
103-103: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
105-105: Heading style
Expected: atx; Actual: setext
(MD003, heading-style)
108-108: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
110-110: Heading style
Expected: atx; Actual: setext
(MD003, heading-style)
113-113: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
115-115: Heading style
Expected: atx; Actual: setext
(MD003, heading-style)
118-118: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
120-120: Heading style
Expected: atx; Actual: setext
(MD003, heading-style)
123-123: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
124-124: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
126-126: Heading style
Expected: atx; Actual: setext
(MD003, heading-style)
129-129: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
130-130: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
132-132: Heading style
Expected: atx; Actual: setext
(MD003, heading-style)
135-135: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
137-137: Heading style
Expected: atx; Actual: setext
(MD003, heading-style)
140-140: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
⏰ 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). (6)
- GitHub Check: Test egg-bin (windows-latest, 24)
- GitHub Check: Test egg-bin (windows-latest, 22)
- GitHub Check: Test egg-bin (macos-latest, 24)
- GitHub Check: Test egg-bin (ubuntu-latest, 22)
- GitHub Check: Test egg-bin (ubuntu-latest, 24)
- GitHub Check: Test egg-bin (macos-latest, 22)
🔇 Additional comments (30)
packages/mock/src/lib/parallel/agent.ts (1)
75-75: Good: removed the unsafe cast; please confirmonMessageis public/stableDirectly using
agent.messenger.onMessage(msg)improves type-safety. Verify thatonMessageis part of the public Messenger typings across the repo’s pinned @eggjs/core version(s) to avoid relying on internals.plugins/watcher/test/fixtures/apps/watcher-development-app/tmp.txt (1)
1-1: LGTM: fixture content is minimal and explicit.Exact bytes ("aaa", no trailing newline) are fine for change-detection tests.
plugins/watcher/test/fixtures/apps/watcher-development-app/package.json (1)
1-3: LGTM: minimal package manifest for fixture app.No additional fields are necessary for test resolution; default CommonJS semantics apply without
"type".plugins/watcher/test/fixtures/apps/watcher-development-app/tmp-agent.txt (1)
1-1: LGTM: agent-side fixture content is correct.Matches tests that watch this file specifically.
plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/package.json (1)
1-3: LGTM: minimal manifest for fuzzy custom event-source fixture.Sufficient for test isolation.
plugins/watcher/LICENSE (1)
1-21: LGTM: MIT license added for plugin.Matches project licensing; no action needed.
plugins/watcher/test/fixtures/apps/watcher-custom-event-source/package.json (1)
1-3: LGTM: minimal manifest for custom event-source fixture.Consistent with other test apps.
CLAUDE.md (1)
43-46: plugins/watcher/package.json verified — no changes required.
plugins/watcher/package.json has type: "module", an eggPlugin entry, exports -> ./src/.ts, publishConfig.exports -> ./dist/.js, files includes "dist", and scripts build (tsdown), clean (rimraf dist), prepublishOnly (npm run build).pnpm-workspace.yaml (1)
62-62: Catalog entry added correctly — verification passed.
- All consumer packages reference camelcase via "catalog:" (no semver usages found).
- All workspace packages reference "@eggjs/watcher" with "workspace:*" and "@eggjs/watcher" is not present in pnpm-workspace.yaml.
rg printed PCRE2 compilation errors during the run; they do not change the OK results above.
plugins/watcher/test/fixtures/apps/watcher-development-app/tmp/tmp.txt (1)
1-1: Fixture OK.Looks fine for exercising file change detection.
plugins/watcher/vitest.config.ts (1)
1-7: Vitest project config looks good.20s timeout is reasonable for FS‑watch tests.
plugins/watcher/test/fixtures/apps/watcher-type-default/package.json (1)
1-3: Fixture metadata OK.Sufficient for tests.
plugins/watcher/test/fixtures/apps/watcher-custom-event-source/config/plugin.js (1)
1-6: Custom plugin enablement looks correct.Path resolution is correct relative to fixture.
plugins/watcher/test/fixtures/apps/watcher-custom-event-source/plugins/egg-watcher-custom/config/config.default.js (1)
1-7: Custom event source wiring LGTM.Exports shape matches expected watcher config.
plugins/watcher/test/fixtures/apps/watcher-type-default/app.js (1)
1-3: Default‑type watcher fixture is fine.No-Op handler is adequate for log‑based assertions.
plugins/watcher/test/fixtures/apps/watcher-development-app/config/config.unittest.js (1)
1-7: LGTM: correct dev-mode fixture.env = 'local' with watcher.type = 'development' matches the development event source tests.
plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/config/config.unittest.js (1)
1-11: LGTM: custom event source options wired correctly.watcher.type = 'custom' and watcherCustom.foo = 'bar' match the fuzzy-path test expectations.
plugins/watcher/src/app.ts (1)
1-3: LGTM: app entry re-exports Boot correctlyNamed import + default re-export matches ./lib/boot.ts. No issues.
plugins/watcher/test/fixtures/apps/watcher-development-app/tmp/t1/t2/t3/t4/tmp.txt (1)
1-1: No review needed for binary/text fixtureFixture content is fine as-is.
tsconfig.json (1)
38-42: Add watcher project reference — looks correctRoot references now include ./plugins/watcher per repo policy.
plugins/watcher/tsconfig.json (1)
1-6: LGTM: extends root config and sets baseUrlMatches monorepo TypeScript conventions.
plugins/watcher/test/utils.ts (1)
1-10: Utility is correct and ESM-safePath derivation via
import.meta.urlis correct; function is minimal and clear.plugins/watcher/src/lib/event-sources/base.ts (1)
3-6: Abstract contract looks goodClear minimal interface for event sources; no issues.
plugins/watcher/src/agent.ts (1)
1-3: Consistent default export for agent entry — verified. package exports include "./agent": "./src/agent.ts" and publishConfig.exports include "./agent": "./dist/agent.js" in plugins/watcher/package.json.plugins/watcher/src/index.ts (1)
1-3: Public surface re-exports verifiedpackage.json "exports" includes "./lib/types", "./lib/watcher", and "./lib/event-sources" mapped to the corresponding src files — no changes required.
plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/plugins/egg-watcher-custom/config/config.default.js (1)
1-5: No change required — loader accepts module/class valuesWatcher._init (src/lib/watcher.ts:33–40) accepts either a string (loaded via importModule) or a module/class (constructed directly), so require('../custom') is valid. The TS declaration at src/lib/types.ts:15 is too narrow — update to Record<string, string | typeof BaseEventSource> if you want type accuracy.
Likely an incorrect or invalid review comment.
plugins/watcher/test/fixtures/apps/watcher-custom-event-source/config/config.unittest.js (1)
1-9: LGTM – fixture config is minimal and correct.
watcher.type = 'custom'andwatcherCustomare set as expected for the custom source;keyspresent.plugins/watcher/test/fixtures/apps/watcher-custom-event-source-fuzzy/config/plugin.js (1)
5-8: LGTM – plugin enablement/path looks right.plugins/watcher/src/lib/types.ts (1)
28-36: Typings look goodAugmentations and public types align with usage across watcher and tests.
plugins/watcher/package.json (1)
24-36: Package manifest meets plugin guidelines
- eggPlugin present
- ESM type with TS-source exports and publishConfig.exports to dist
- scripts clean/build/prepublishOnly included
- workspace/catalog usage correct
Also applies to: 72-96
| "@eggjs/watcher": "workspace:*", | ||
| "circular-json-for-egg": "catalog:", |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Switch @eggjs/watcher to workspace: — correct for internal dependency*
This aligns with workspace/catalog rules.
Run this to verify workspace wiring and plugin metadata:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Check pnpm-workspace includes plugins/* …"
rg -nP '(?s)^packages:\s*(?:- .*\n)*\s*-+\s*plugins/\*' pnpm-workspace.yaml || { echo "Missing plugins/* in pnpm-workspace.yaml"; exit 1; }
echo "Check watcher plugin has package.json and eggPlugin metadata …"
test -f plugins/watcher/package.json
rg -n '"eggPlugin"' plugins/watcher/package.json || { echo "Missing eggPlugin field in plugins/watcher/package.json"; exit 1; }
echo "Check egg depends on @eggjs/watcher via workspace:* …"
rg -nP '"@eggjs/watcher"\s*:\s*"workspace:\*"' packages/egg/package.jsonLength of output: 82
pnpm-workspace.yaml missing plugins/ — workspace: dependency won’t resolve**
- Add "- plugins/" under packages: in pnpm-workspace.yaml so "@eggjs/watcher": "workspace:" in packages/egg/package.json (lines 133–134) can be resolved.
- Verify plugins/watcher/package.json exists and contains an "eggPlugin" field.
🤖 Prompt for AI Agents
In packages/egg/package.json around lines 133–134, the dependency
"@eggjs/watcher": "workspace:*" will not resolve because pnpm-workspace.yaml
does not include the plugins/* glob; update pnpm-workspace.yaml by adding "-
plugins/*" under the packages: array so pnpm can find workspace packages, and
then verify that plugins/watcher/package.json exists and declares the required
"eggPlugin" field (add it if missing).
| import { getSourceDirname } from '../lib/utils.ts'; | ||
| import type { WatcherConfig } from '../lib/types.ts'; |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
ESM import specifiers and dynamic import targets need .js extensions.
Using .ts in import specifiers will fail at runtime under NodeNext; also ensure eventSources point to concrete .js files for dynamic import.
This is a merge blocker.
-import { getSourceDirname } from '../lib/utils.ts';
-import type { WatcherConfig } from '../lib/types.ts';
+import { getSourceDirname } from '../lib/utils.js';
+import type { WatcherConfig } from '../lib/types.js';
watcher: {
type: 'default', // default event source
eventSources: {
- default: path.join(getSourceDirname(), 'lib', 'event-sources', 'default'),
- development: path.join(
+ default: path.join(getSourceDirname(), 'lib', 'event-sources', 'default.js'),
+ development: path.join(
getSourceDirname(),
'lib',
'event-sources',
- 'development'
+ 'development.js'
),
},
- } as WatcherConfig,
+ } satisfies WatcherConfig,Run to find any lingering .ts specifiers in source imports:
Also applies to: 14-21, 22-22
🏁 Script executed:
#!/bin/bash
rg -nP --type=ts -C1 "from\s+['\"].*\.ts['\"]" plugins/watcher/srcLength of output: 2200
ESM import specifiers and dynamic import targets must use .js — merge blocker.
Replace all .ts import specifiers with .js and ensure dynamic eventSources point to concrete .js files; update the config object to use satisfies WatcherConfig instead of as WatcherConfig. Affected locations (from ripgrep):
- plugins/watcher/src/config/config.default.ts (lines 2–3)
- plugins/watcher/src/lib/watcher.ts (lines 8–10)
- plugins/watcher/src/lib/types.ts (line 3)
- plugins/watcher/src/index.ts (lines 1–3)
- plugins/watcher/src/lib/boot.ts (line 3)
- plugins/watcher/src/app.ts (line 1)
- plugins/watcher/src/agent.ts (line 1)
- plugins/watcher/src/lib/event-sources/index.ts (lines 1–3)
- plugins/watcher/src/lib/event-sources/development.ts (lines 5–6)
- plugins/watcher/src/lib/event-sources/default.ts (line 1)
Fix all matches (change .ts → .js, make eventSource paths concrete .js, and update the config typing), then verify under Node (NodeNext) and run the build/tests.
🤖 Prompt for AI Agents
In plugins/watcher/src/config/config.default.ts around lines 2–3, update ESM
import specifiers from .ts to .js and change the config typing from using "as
WatcherConfig" to "satisfies WatcherConfig"; then scan and fix the other listed
files (plugins/watcher/src/lib/watcher.ts lines 8–10, src/lib/types.ts line 3,
src/index.ts lines 1–3, src/lib/boot.ts line 3, src/app.ts line 1, src/agent.ts
line 1, src/lib/event-sources/index.ts lines 1–3,
src/lib/event-sources/development.ts lines 5–6, src/lib/event-sources/default.ts
line 1) replacing all .ts import specifiers with .js, ensure any dynamic
eventSources point to concrete .js file paths (no runtime .ts targets), and
update any config object assertions to use "satisfies WatcherConfig"; after
making these changes, run the project under Node (NodeNext) and execute the
build and tests to verify no import/runtime errors remain.
| type: 'development', | ||
| } as WatcherConfig, |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Use satisfies Partial<WatcherConfig> instead of a type assertion.
Avoids lying to the type system about missing eventSources while still letting default config provide it via deep‑merge.
- } as WatcherConfig,
+ } satisfies Partial<WatcherConfig>,📝 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.
| type: 'development', | |
| } as WatcherConfig, | |
| type: 'development', | |
| } satisfies Partial<WatcherConfig>, |
🤖 Prompt for AI Agents
In plugins/watcher/src/config/config.local.ts around lines 5–6, the file
currently uses a type assertion ("as WatcherConfig") for the exported config
which falsely tells TypeScript the object satisfies the full interface; replace
that assertion with the TypeScript 4.9+ "satisfies Partial<WatcherConfig>"
operator so the object is checked as a partial (allowing missing eventSources)
while preserving the full WatcherConfig type for downstream use — update the
export to use "satisfies Partial<WatcherConfig>" instead of the "as" cast.
| import type { WatcherConfig } from '../lib/types.js'; | ||
|
|
||
| export default { | ||
| watcher: { | ||
| type: 'development', | ||
| } as WatcherConfig, | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Avoid type-assertion by satisfying the type; also standardize import extension
Current code casts to WatcherConfig while omitting eventSources. Provide an empty map and use .ts for consistency with other files.
-import type { WatcherConfig } from '../lib/types.js';
+import type { WatcherConfig } from '../lib/types.ts';
export default {
watcher: {
type: 'development',
- } as WatcherConfig,
+ eventSources: {},
+ } as WatcherConfig,
};📝 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.
| import type { WatcherConfig } from '../lib/types.js'; | |
| export default { | |
| watcher: { | |
| type: 'development', | |
| } as WatcherConfig, | |
| }; | |
| import type { WatcherConfig } from '../lib/types.ts'; | |
| export default { | |
| watcher: { | |
| type: 'development', | |
| eventSources: {}, | |
| } as WatcherConfig, | |
| }; |
🤖 Prompt for AI Agents
In plugins/watcher/src/config/config.unittest.ts lines 1-7, avoid the type
assertion and standardize the import extension: change the import to use
'../lib/types.ts' and make the exported object satisfy WatcherConfig by adding
an explicit empty eventSources map (e.g., eventSources: {}) under watcher, and
remove the trailing "as WatcherConfig" cast so the literal structurally matches
the type.
| import { Watcher } from './watcher.ts'; | ||
|
|
There was a problem hiding this comment.
Fix ESM import extension.
.ts here will break at runtime.
-import { Watcher } from './watcher.ts';
+import { Watcher } from './watcher.js';📝 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.
| import { Watcher } from './watcher.ts'; | |
| import { Watcher } from './watcher.js'; |
🤖 Prompt for AI Agents
In plugins/watcher/src/lib/boot.ts around lines 3-4, the import uses a .ts
extension which will fail at runtime for ESM; remove the extension so the module
specifier is extensionless (import { Watcher } from './watcher') or, if your
runtime requires explicit JS specifiers, change it to the emitted .js file
(import { Watcher } from './watcher.js'); update the import accordingly to match
your build/runtime resolution.
| app.get('/agent-watch', async ctx => { | ||
| app.messenger.broadcast('agent-watch'); | ||
| ctx.body = await new Promise(function (resolve) { | ||
| app.messenger.on('agent-watch-success', function (msg) { | ||
| resolve(msg); | ||
| }); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Avoid race by registering the listener before broadcasting; use once to prevent leaks.
Current order risks missing an immediate response and accumulates listeners across calls.
- app.get('/agent-watch', async ctx => {
- app.messenger.broadcast('agent-watch');
- ctx.body = await new Promise(function (resolve) {
- app.messenger.on('agent-watch-success', function (msg) {
- resolve(msg);
- });
- });
- });
+ app.get('/agent-watch', async ctx => {
+ const p = new Promise(resolve => {
+ app.messenger.once('agent-watch-success', msg => resolve(msg));
+ });
+ app.messenger.broadcast('agent-watch');
+ ctx.body = await p;
+ });📝 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.
| app.get('/agent-watch', async ctx => { | |
| app.messenger.broadcast('agent-watch'); | |
| ctx.body = await new Promise(function (resolve) { | |
| app.messenger.on('agent-watch-success', function (msg) { | |
| resolve(msg); | |
| }); | |
| }); | |
| }); | |
| app.get('/agent-watch', async ctx => { | |
| const p = new Promise(resolve => { | |
| app.messenger.once('agent-watch-success', msg => resolve(msg)); | |
| }); | |
| app.messenger.broadcast('agent-watch'); | |
| ctx.body = await p; | |
| }); |
🤖 Prompt for AI Agents
In plugins/watcher/test/fixtures/apps/watcher-development-app/app/router.js
around lines 38 to 45, the code broadcasts 'agent-watch' before attaching a
listener which can miss immediate responses and accumulates listeners; change
the Promise to attach a one-time listener (use app.messenger.once or equivalent)
before calling app.messenger.broadcast('agent-watch') so the handler is
guaranteed to receive the next response and is automatically removed to prevent
listener leaks; keep the Promise to resolve with the message when the once
handler fires (optionally add a timeout inside the Promise if desired).
| app.get('/agent-unwatch', async ctx => { | ||
| app.messenger.broadcast('agent-unwatch'); | ||
| ctx.body = await new Promise(function (resolve) { | ||
| app.messenger.on('agent-unwatch-success', function (msg) { | ||
| resolve(msg); | ||
| }); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Repeat the safer pattern for /agent-unwatch.
- app.get('/agent-unwatch', async ctx => {
- app.messenger.broadcast('agent-unwatch');
- ctx.body = await new Promise(function (resolve) {
- app.messenger.on('agent-unwatch-success', function (msg) {
- resolve(msg);
- });
- });
- });
+ app.get('/agent-unwatch', async ctx => {
+ const p = new Promise(resolve => {
+ app.messenger.once('agent-unwatch-success', msg => resolve(msg));
+ });
+ app.messenger.broadcast('agent-unwatch');
+ ctx.body = await p;
+ });📝 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.
| app.get('/agent-unwatch', async ctx => { | |
| app.messenger.broadcast('agent-unwatch'); | |
| ctx.body = await new Promise(function (resolve) { | |
| app.messenger.on('agent-unwatch-success', function (msg) { | |
| resolve(msg); | |
| }); | |
| }); | |
| }); | |
| app.get('/agent-unwatch', async ctx => { | |
| const p = new Promise(resolve => { | |
| app.messenger.once('agent-unwatch-success', msg => resolve(msg)); | |
| }); | |
| app.messenger.broadcast('agent-unwatch'); | |
| ctx.body = await p; | |
| }); |
🤖 Prompt for AI Agents
In plugins/watcher/test/fixtures/apps/watcher-development-app/app/router.js
around lines 47–54, the /agent-unwatch handler uses app.messenger.on and never
removes the listener (risking leaks/race conditions); change to use a one-time
listener (app.messenger.once) and add a short timeout fallback so the promise
resolves or rejects if no 'agent-unwatch-success' arrives (ensure the timeout
clears the listener and the listener clears the timeout), and surface any error
instead of hanging.
| app.get('/agent-msg', async ctx => { | ||
| app.messenger.broadcast('i-want-agent-file-changed-count'); | ||
| ctx.body = await new Promise(function (resolve) { | ||
| app.messenger.on('agent-file-changed-count', function (msg) { | ||
| resolve(msg); | ||
| }); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Repeat the safer pattern for /agent-msg.
- app.get('/agent-msg', async ctx => {
- app.messenger.broadcast('i-want-agent-file-changed-count');
- ctx.body = await new Promise(function (resolve) {
- app.messenger.on('agent-file-changed-count', function (msg) {
- resolve(msg);
- });
- });
- });
+ app.get('/agent-msg', async ctx => {
+ const p = new Promise(resolve => {
+ app.messenger.once('agent-file-changed-count', msg => resolve(msg));
+ });
+ app.messenger.broadcast('i-want-agent-file-changed-count');
+ ctx.body = await p;
+ });📝 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.
| app.get('/agent-msg', async ctx => { | |
| app.messenger.broadcast('i-want-agent-file-changed-count'); | |
| ctx.body = await new Promise(function (resolve) { | |
| app.messenger.on('agent-file-changed-count', function (msg) { | |
| resolve(msg); | |
| }); | |
| }); | |
| }); | |
| app.get('/agent-msg', async ctx => { | |
| const p = new Promise(resolve => { | |
| app.messenger.once('agent-file-changed-count', msg => resolve(msg)); | |
| }); | |
| app.messenger.broadcast('i-want-agent-file-changed-count'); | |
| ctx.body = await p; | |
| }); |
🤖 Prompt for AI Agents
In plugins/watcher/test/fixtures/apps/watcher-development-app/app/router.js
around lines 56-63, the /agent-msg handler uses app.messenger.on and broadcasts
before attaching the listener which can leak listeners or miss messages; change
it to attach a one-time listener (app.messenger.once) before broadcasting and
wrap in a Promise that rejects/ resolves on a short timeout so the request
doesn't hang forever, ensuring the listener is removed automatically and the
broadcast happens after the listener is registered.
| import { test, expect } from 'vitest'; | ||
|
|
||
| import * as watcher from '../src/index.ts'; | ||
|
|
||
| test('should exports work', async () => { | ||
| expect(Object.keys(watcher).sort()).toMatchSnapshot(); | ||
| }); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Use Node.js assert instead of Vitest expect per repo testing guideline
Replace Vitest's expect(...).toMatchSnapshot() with Node's built-in assert and verify a stable subset of exports.
-import { test, expect } from 'vitest';
+import { test } from 'vitest';
+import assert from 'node:assert/strict';
import * as watcher from '../src/index.ts';
test('should exports work', async () => {
- expect(Object.keys(watcher).sort()).toMatchSnapshot();
+ const keys = Object.keys(watcher).sort();
+ // Assert a stable subset to avoid snapshot churn
+ for (const k of ['Watcher', 'BaseEventSource', 'DefaultEventSource', 'DevelopmentEventSource']) {
+ assert.ok(keys.includes(k), `missing export: ${k}`);
+ }
});📝 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.
| import { test, expect } from 'vitest'; | |
| import * as watcher from '../src/index.ts'; | |
| test('should exports work', async () => { | |
| expect(Object.keys(watcher).sort()).toMatchSnapshot(); | |
| }); | |
| import { test } from 'vitest'; | |
| import assert from 'node:assert/strict'; | |
| import * as watcher from '../src/index.ts'; | |
| test('should exports work', async () => { | |
| const keys = Object.keys(watcher).sort(); | |
| // Assert a stable subset to avoid snapshot churn | |
| for (const k of ['Watcher', 'BaseEventSource', 'DefaultEventSource', 'DevelopmentEventSource']) { | |
| assert.ok(keys.includes(k), `missing export: ${k}`); | |
| } | |
| }); |
🤖 Prompt for AI Agents
In plugins/watcher/test/index.test.ts around lines 1 to 7, replace the Vitest
snapshot assertion with Node's built-in assert and check a stable subset of
exports: import/require assert, keep the test() from Vitest, compute
Object.keys(watcher).sort(), filter or pick the specific expected export names
(the stable subset) and use assert.deepStrictEqual(actualSubset, expectedArray)
to verify equality instead of expect(...).toMatchSnapshot().
|
|
||
| describe('test/watcher.test.ts', () => { | ||
| let app: MockApplication; | ||
| afterEach(() => app && app.close()); |
There was a problem hiding this comment.
Await app.close() in afterEach to avoid resource leaks
Not awaiting close can leave handles open between tests.
- afterEach(() => app && app.close());
+ afterEach(async () => {
+ if (app) await app.close();
+ });📝 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.
| afterEach(() => app && app.close()); | |
| afterEach(async () => { | |
| if (app) await app.close(); | |
| }); |
🤖 Prompt for AI Agents
In plugins/watcher/test/watcher.test.ts around line 11, the afterEach hook calls
app.close() without awaiting it which can leave handles open between tests;
change the hook to be async and await app.close() (guarding for app being
defined) so teardown completes before the next test runs.

Move @eggjs/watcher plugin from separate repository to plugins/watcher. Configure for monorepo with workspace dependencies and standard tsdown build.
🤖 Generated with Claude Code
Summary by CodeRabbit