Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support cjs and esm both by tshy #34

Merged
merged 9 commits into from
Jan 11, 2025
Merged

feat: support cjs and esm both by tshy #34

merged 9 commits into from
Jan 11, 2025

Conversation

fengmk2
Copy link
Member

@fengmk2 fengmk2 commented Jan 3, 2025

BREAKING CHANGE: drop Node.js < 18.19.0 support

part of eggjs/egg#3644

eggjs/egg#5257

Summary by CodeRabbit

Based on the comprehensive changes, here are the release notes:

Release Notes for @eggjs/development

  • New Features

    • Added TypeScript support for development environment
    • Enhanced file watching and reloading mechanisms
    • Improved configuration options for development mode
  • Breaking Changes

    • Migrated from egg-development to @eggjs/development
    • Requires Node.js version >=18.19.0
    • Dropped support for Node.js 14 and 16
  • Improvements

    • Refined ESLint configuration
    • Updated GitHub Actions workflow to focus on macOS testing
    • Added more robust file change detection
    • Enhanced type safety across the project
  • Bug Fixes

    • Improved handling of file reloading in different scenarios
    • Fixed potential issues with asset and service file monitoring
  • Chores

    • Updated dependencies
    • Migrated test suite to TypeScript
    • Restructured project configuration

Copy link

coderabbitai bot commented Jan 3, 2025

Walkthrough

This pull request represents a comprehensive modernization and refactoring of the Egg.js development package. The changes involve transitioning from a JavaScript-based implementation to a TypeScript-based one, updating the project structure, enhancing type safety, and modernizing the development workflow. Key modifications include updating the ESLint configuration, revising GitHub Actions workflow, improving gitignore, updating package metadata, and restructuring the project's source and test files to use TypeScript and ES modules.

Changes

File Change Summary
.eslintrc Extended ESLint configuration to include TypeScript and node prefix rules
.github/workflows/nodejs.yml Updated CI to use only macOS and Node.js versions 18.19.0, 20, and 22
.gitignore Added entries for .tshy*, .eslintcache, dist, package-lock.json, and .package-lock.json
README.md Updated package name, added PR welcome badge, modified configuration examples
package.json Renamed to @eggjs/development, added module export configurations, updated dependencies
src/ Converted core functionality to TypeScript with new AgentBoot and AppBoot classes
test/ Migrated tests to TypeScript, updated test utilities and fixtures

Sequence Diagram

sequenceDiagram
    participant App as Application
    participant Agent as Agent
    participant FileWatcher as File Watcher
    participant Master as Master Process

    App->>Agent: Initialize
    Agent->>FileWatcher: Setup Watch Directories
    FileWatcher-->>Agent: Monitor Changes
    alt File Change Detected
        FileWatcher->>Agent: Notify File Change
        Agent->>Master: Request Worker Reload
        Master->>App: Reload Worker
    end
Loading

Poem

🐰 A Rabbit's Ode to Code Renewal 🌱

TypeScript whispers, JavaScript fades,
Egg development gets its upgrades
Workflows dance, configs align
Modernization's design so fine!

Hop, hop, hooray! 🎉

Finishing Touches

  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

socket-security bot commented Jan 3, 2025

BREAKING CHANGE: drop Node.js < 18.19.0 support

part of eggjs/egg#3644

eggjs/egg#5257
Copy link

pkg-pr-new bot commented Jan 11, 2025

Open in Stackblitz

npm i https://pkg.pr.new/eggjs/development/@eggjs/development@34

commit: 02c3c5f

@fengmk2 fengmk2 marked this pull request as ready for review January 11, 2025 06:41
@fengmk2 fengmk2 merged commit 7a63cd6 into master Jan 11, 2025
9 of 10 checks passed
@fengmk2 fengmk2 deleted the egg-v4 branch January 11, 2025 06:43
fengmk2 pushed a commit that referenced this pull request Jan 11, 2025
[skip ci]

## [4.0.0](v3.0.2...v4.0.0) (2025-01-11)

### ⚠ BREAKING CHANGES

* drop Node.js < 18.19.0 support

part of eggjs/egg#3644

eggjs/egg#5257

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

Based on the comprehensive changes, here are the release notes:

## Release Notes for @eggjs/development

- **New Features**
	- Added TypeScript support for development environment
	- Enhanced file watching and reloading mechanisms
	- Improved configuration options for development mode

- **Breaking Changes**
	- Migrated from `egg-development` to `@eggjs/development`
	- Requires Node.js version >=18.19.0
	- Dropped support for Node.js 14 and 16

- **Improvements**
	- Refined ESLint configuration
	- Updated GitHub Actions workflow to focus on macOS testing
	- Added more robust file change detection
	- Enhanced type safety across the project

- **Bug Fixes**
	- Improved handling of file reloading in different scenarios
	- Fixed potential issues with asset and service file monitoring

- **Chores**
	- Updated dependencies
	- Migrated test suite to TypeScript
	- Restructured project configuration

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

### Features

* support cjs and esm both by tshy ([#34](#34)) ([7a63cd6](7a63cd6))
Copy link

@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: 8

🔭 Outside diff range comments (1)
README.md (1)

Line range hint 11-19: Update badge URLs to match new package name

The badge URLs still reference the old package name egg-development instead of @eggjs/development.

-[npm-image]: https://img.shields.io/npm/v/egg-development.svg?style=flat-square
-[npm-url]: https://npmjs.org/package/egg-development
+[npm-image]: https://img.shields.io/npm/v/@eggjs/development.svg?style=flat-square
+[npm-url]: https://npmjs.org/package/@eggjs/development
🧰 Tools
🪛 LanguageTool

[style] ~3-~3: Using many exclamation marks might seem excessive (in this case: 5 exclamation marks for a text that’s 2027 characters long)
Context: ...t [![NPM version][npm-image]][npm-url] Node.js CI [![Test coverage][codecov-image]][codecov-url] [![Known Vulnerabilities][snyk-image]][snyk-url] [![npm download][download-image]][download-url] [![Node.js Version](https://img.shields.io...

(EN_EXCESSIVE_EXCLAMATION)

🧹 Nitpick comments (24)
test/fixtures/timing/app.js (1)

7-8: Consider using async operations for real-world scenarios.

While synchronous operations are acceptable in tests, for real-world implementations consider using async alternatives to avoid blocking the event loop.

Here's how it could be implemented asynchronously:

-  app.checkFile = () => {
-    return {
-      timing: fs.existsSync(path.join(__dirname, 'run/agent_timing_11111.json')),
-      config: fs.existsSync(path.join(__dirname, 'run/application_config.json')),
-    };
+  app.checkFile = async () => {
+    const [timing, config] = await Promise.all([
+      fs.promises.access(path.join(__dirname, 'run/agent_timing_11111.json'))
+        .then(() => true)
+        .catch(() => false),
+      fs.promises.access(path.join(__dirname, 'run/application_config.json'))
+        .then(() => true)
+        .catch(() => false),
+    ]);
+    return { timing, config };
   };
src/agent.ts (2)

5-5: Use standard Node.js methods instead of the custom exists function

You are importing exists from the 'utility' package, but it might be better to use the standard fs.access() method from the fs/promises module to check if the directory exists. This reduces external dependencies and utilizes built-in Node.js functionality.

Apply this diff to use fs.access():

-import { exists } from 'utility';
...
-const stat = await exists(rundir);
-if (!stat) return;
+try {
+  await fs.access(rundir);
+} catch {
+  return;
+}

Also applies to: 18-20


31-31: Simplify property access by removing Reflect.get

Instead of using Reflect.get(agent.options, 'mode'), you can access the mode property directly. This simplifies the code and improves readability.

Apply this diff:

-if (agent.options && Reflect.get(agent.options, 'mode') === 'single') {
+if (agent.options?.mode === 'single') {
src/utils.ts (2)

8-10: Consider addressing the ts-ignore technical debt

The ts-ignore comment suggests potential type-safety issues that should be addressed.

Consider using proper type assertions or fixing the underlying type issue instead of suppressing it.


17-19: Enhance file pattern matching robustness

The current regex pattern could be more explicit and maintainable.

Consider this improvement:

-export function isTimingFile(file: string) {
-  return /^(agent|application)_timing/.test(file);
+export function isTimingFile(file: string) {
+  const TIMING_FILE_PATTERN = /^(agent|application)_timing[^/]*$/;
+  return TIMING_FILE_PATTERN.test(file);
}

This change:

  • Makes the pattern more explicit with a named constant
  • Ensures full string match with $
  • Prevents matching paths with / using [^/]*
test/not-reload.test.ts (2)

4-4: Avoid shadowing built-in globals.

The escape import shadows the global escape function. Consider renaming it to be more specific, e.g., escapeRegExp.

-import { escape, getFilepath, DELAY } from './utils.js';
+import { escape as escapeRegExp, getFilepath, DELAY } from './utils.js';

// Update usage
-app.notExpect('stdout', new RegExp(escape(`reload worker because ${filepath} change`)));
+app.notExpect('stdout', new RegExp(escapeRegExp(`reload worker because ${filepath} change`)));
🧰 Tools
🪛 Biome (1.9.4)

[error] 4-4: Do not shadow the global "escape" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)


Line range hint 10-22: Consolidate duplicate afterEach hooks.

There are multiple afterEach hooks that can be consolidated into a single hook for better maintainability.

-  afterEach(() => app.close());
-  afterEach(mm.restore);
-  // for debounce
-  afterEach(() => scheduler.wait(500));
+  afterEach(async () => {
+    await app.close();
+    mm.restore();
+    // for debounce
+    await scheduler.wait(500);
+  });
🧰 Tools
🪛 Biome (1.9.4)

[error] 22-22: Disallow duplicate setup and teardown hooks.

Disallow afterEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

src/types.ts (1)

27-29: Enhance reloadPattern documentation.

The documentation for reloadPattern could be more descriptive about the pattern format and provide examples.

   /**
-   * whether to reload, use https://github.com/sindresorhus/multimatch
+   * Pattern(s) to determine whether to reload when files change.
+   * Uses https://github.com/sindresorhus/multimatch for pattern matching.
+   * @example ['src/**/*.ts', '!src/**/*.spec.ts'] - Watch all TypeScript files except tests
+   * @example '*.{js,ts}' - Watch all JavaScript and TypeScript files
    */
src/config/config.default.ts (1)

13-22: Consider using the satisfies operator for better type safety.

Instead of using type assertion with as, consider using the satisfies operator for better type checking while maintaining type inference.

-  } as DevelopmentConfig,
+  } satisfies DevelopmentConfig,

The satisfies operator ensures the object matches the type without widening the type information, making it safer than type assertion.

test/fast_ready_false.test.ts (2)

9-12: Consolidate duplicate afterEach hooks.

Similar to the previous test file, there are multiple afterEach hooks that can be consolidated.

-  afterEach(() => app.close());
-  afterEach(mm.restore);
-  // for debounce
-  afterEach(() => scheduler.wait(500));
+  afterEach(async () => {
+    await app.close();
+    mm.restore();
+    // for debounce
+    await scheduler.wait(500);
+  });
🧰 Tools
🪛 Biome (1.9.4)

[error] 10-10: Disallow duplicate setup and teardown hooks.

Disallow afterEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)


[error] 12-12: Disallow duplicate setup and teardown hooks.

Disallow afterEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)


20-20: Extract wait times to named constants.

The different wait times (100ms and 300ms) should be extracted to named constants for better maintainability and clarity of intent.

+const LOG_WRITE_DELAY = 100;
+const READY_DELAY = 300;

-    await scheduler.wait(100);
+    await scheduler.wait(LOG_WRITE_DELAY);

-    await scheduler.wait(300);
+    await scheduler.wait(READY_DELAY);

Also applies to: 31-31

test/custom.test.ts (1)

17-19: Consider combining afterEach hooks

The duplicate afterEach hooks can be combined into a single hook for better maintainability:

-  afterEach(mm.restore);
-  // for debounce
-  afterEach(() => scheduler.wait(500));
+  afterEach(async () => {
+    mm.restore();
+    // for debounce
+    await scheduler.wait(500);
+  });
🧰 Tools
🪛 Biome (1.9.4)

[error] 19-19: Disallow duplicate setup and teardown hooks.

Disallow afterEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

src/app/middleware/egg_loader_trace.ts (1)

12-14: Add error handling and caching for template loading

The template loading could benefit from error handling and caching for better performance:

+  let templateCache: string | undefined;
+
   return async (ctx, next) => {
     if (ctx.path !== '/__loader_trace__') {
       return await next();
     }
-    const template = await fs.readFile(getSourceFile('config/loader_trace.html'), 'utf8');
+    try {
+      if (!templateCache) {
+        templateCache = await fs.readFile(getSourceFile('config/loader_trace.html'), 'utf8');
+      }
+      const data = await loadTimingData(app);
+      ctx.body = templateCache.replace('{{placeholder}}', JSON.stringify(data));
+    } catch (err) {
+      ctx.status = 500;
+      ctx.body = { error: 'Failed to load timing data' };
+      app.logger.error(err);
+    }
   };
test/absolute.test.ts (1)

19-21: Clarify the commented debug option

The commented debug option needs clarification. Either remove it or document why it's commented out:

-      // debug: true,
+      // debug: false, // Enable only when debugging test failures
test/timing.test.ts (2)

24-24: Extract magic numbers into named constants

The magic number 3000 should be extracted into a named constant for better maintainability:

+    const MIN_EXPECTED_JSON_LENGTH = 3000;
-    assert(jsonString[1].length > 3000);
+    assert(jsonString[1].length > MIN_EXPECTED_JSON_LENGTH);

35-35: Remove commented console.log

Remove the commented console.log statement as it adds noise to the code:

-    // console.log(last);
test/process_mode_single.test.ts (2)

21-21: Consider removing the type assertion.

The type assertion as any suggests incomplete typing. Consider defining a proper interface for the configuration object.

interface AppConfig {
  env: string;
  baseDir: string;
  plugins: {
    [key: string]: {
      enable: boolean;
      path: string;
    };
  };
}

39-39: Replace hard-coded wait time with a constant.

Hard-coded wait times can make tests flaky. Consider using a shared constant from utils.

-    await scheduler.wait(1000);
+    await scheduler.wait(DELAY);
test/development.test.ts (1)

17-19: Consolidate afterEach hooks.

Multiple afterEach hooks can be confusing. Consider combining them into a single hook.

-  afterEach(mm.restore);
-  // for debounce
-  afterEach(() => scheduler.wait(500));
+  afterEach(async () => {
+    mm.restore();
+    // for debounce
+    await scheduler.wait(500);
+  });
🧰 Tools
🪛 Biome (1.9.4)

[error] 19-19: Disallow duplicate setup and teardown hooks.

Disallow afterEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

test/development-ts.test.ts (1)

9-15: Consider using before instead of beforeEach.

The test setup uses beforeEach, while similar tests use before. This could impact performance as the app is recreated for each test.

-  beforeEach(() => {
+  before(() => {
     mm.env('local');
     app = mm.cluster({
       baseDir: 'development-ts',
     });
     return app.ready();
   });
test/override.test.ts (3)

8-8: Use optional chaining in after hook.

The current check app && app.close() can be simplified using optional chaining.

-  after(() => app && app.close());
+  after(() => app?.close());

29-29: Standardize RegExp usage.

The file uses different styles for RegExp creation: direct literals (/a\.js/) and constructor (new RegExp(escape(...))). Consider standardizing the approach.

-  app.expect('stdout', /a\.js/);
+  app.expect('stdout', new RegExp(escape('a.js')));

Also applies to: 39-39, 58-58, 68-68


32-33: Remove redundant debug calls.

The app.debug() calls appear inconsistently before some tests. Either add them consistently or remove them if not necessary.

-    app.debug();
     const filepath = getFilepath('override/app/no-trigger/index.js');

Also applies to: 62-63

.github/workflows/nodejs.yml (1)

14-15: Consider testing on multiple platforms

Currently, the CI is limited to macOS. Consider adding Ubuntu to ensure cross-platform compatibility, especially since Node.js native ESM behavior can vary between platforms.

-      os: 'macos-latest'
+      os: 'ubuntu-latest, macos-latest'
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 571b851 and 02c3c5f.

📒 Files selected for processing (40)
  • .eslintrc (1 hunks)
  • .github/workflows/nodejs.yml (1 hunks)
  • .gitignore (1 hunks)
  • README.md (4 hunks)
  • agent.js (0 hunks)
  • app.js (0 hunks)
  • package.json (3 hunks)
  • src/agent.ts (1 hunks)
  • src/app.ts (1 hunks)
  • src/app/middleware/egg_loader_trace.ts (1 hunks)
  • src/config/config.default.ts (2 hunks)
  • src/index.ts (1 hunks)
  • src/types.ts (1 hunks)
  • src/typings/index.d.ts (1 hunks)
  • src/utils.ts (1 hunks)
  • test/absolute.test.ts (1 hunks)
  • test/custom.test.js (0 hunks)
  • test/custom.test.ts (1 hunks)
  • test/development-ts.test.ts (1 hunks)
  • test/development.test.js (0 hunks)
  • test/development.test.ts (1 hunks)
  • test/fast_ready_false.test.ts (1 hunks)
  • test/fixtures/development-ts/app/public/foo.js (1 hunks)
  • test/fixtures/development-ts/app/router.ts (1 hunks)
  • test/fixtures/development-ts/config/config.default.ts (1 hunks)
  • test/fixtures/development-ts/package.json (1 hunks)
  • test/fixtures/development-ts/tsconfig.json (1 hunks)
  • test/fixtures/development/app/router.js (1 hunks)
  • test/fixtures/development/config/config.default.js (0 hunks)
  • test/fixtures/timing/app.js (1 hunks)
  • test/fixtures/timing/app/router.js (1 hunks)
  • test/not-reload.test.ts (2 hunks)
  • test/override.test.ts (3 hunks)
  • test/process_mode_single.test.js (0 hunks)
  • test/process_mode_single.test.ts (1 hunks)
  • test/timing.test.js (0 hunks)
  • test/timing.test.ts (1 hunks)
  • test/utils.js (0 hunks)
  • test/utils.ts (1 hunks)
  • tsconfig.json (1 hunks)
💤 Files with no reviewable changes (8)
  • test/fixtures/development/config/config.default.js
  • app.js
  • test/utils.js
  • test/custom.test.js
  • agent.js
  • test/process_mode_single.test.js
  • test/timing.test.js
  • test/development.test.js
✅ Files skipped from review due to trivial changes (6)
  • test/fixtures/development-ts/package.json
  • src/index.ts
  • test/fixtures/development-ts/app/public/foo.js
  • src/typings/index.d.ts
  • .gitignore
  • tsconfig.json
🧰 Additional context used
🪛 Biome (1.9.4)
test/utils.ts

[error] 11-11: Do not shadow the global "escape" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

test/custom.test.ts

[error] 19-19: Disallow duplicate setup and teardown hooks.

Disallow afterEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

test/override.test.ts

[error] 4-4: Do not shadow the global "escape" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)


[error] 9-9: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 12-12: Disallow duplicate setup and teardown hooks.

Disallow afterEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

test/not-reload.test.ts

[error] 4-4: Do not shadow the global "escape" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)


[error] 22-22: Disallow duplicate setup and teardown hooks.

Disallow afterEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

test/development.test.ts

[error] 5-5: Do not shadow the global "escape" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)


[error] 19-19: Disallow duplicate setup and teardown hooks.

Disallow afterEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

test/absolute.test.ts

[error] 27-27: Disallow duplicate setup and teardown hooks.

Disallow afterEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

test/fast_ready_false.test.ts

[error] 10-10: Disallow duplicate setup and teardown hooks.

Disallow afterEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)


[error] 12-12: Disallow duplicate setup and teardown hooks.

Disallow afterEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

test/development-ts.test.ts

[error] 5-5: Do not shadow the global "escape" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)


[error] 17-17: Disallow duplicate setup and teardown hooks.

Disallow afterEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)


[error] 19-19: Disallow duplicate setup and teardown hooks.

Disallow afterEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

🪛 LanguageTool
README.md

[style] ~3-~3: Using many exclamation marks might seem excessive (in this case: 5 exclamation marks for a text that’s 2027 characters long)
Context: ...t [![NPM version][npm-image]][npm-url] Node.js CI [![Test coverage][codecov-image]][codecov-url] [![Known Vulnerabilities][snyk-image]][snyk-url] [![npm download][download-image]][download-url] [![Node.js Version](https://img.shields.io...

(EN_EXCESSIVE_EXCLAMATION)

🔇 Additional comments (18)
test/fixtures/timing/app/router.js (1)

3-3: LGTM! Good improvement in the test fixture.

The change to invoke checkFile() as a function ensures that each request gets the current file system state, making the test more accurate for timing-related scenarios.

test/fixtures/timing/app.js (1)

5-10: Good conversion to a function, but note about synchronous operations.

Converting checkFile to a function ensures fresh file system state on each call, which is good. However, be aware that fs.existsSync is being used, which is a synchronous operation.

Since this is a test fixture, the synchronous operation might be acceptable, but let's verify if this pattern is used elsewhere in the codebase:

✅ Verification successful

Synchronous fs operations safely isolated to test fixtures

Verification confirms that fs.existsSync usage is contained within test fixtures only, with no occurrences in production code. The synchronous file system operation is acceptable in this context.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for existsSync usage in production code
rg "fs\.existsSync" --type js --type ts --glob '!test/**'

Length of output: 59


Script:

#!/bin/bash
# First, verify our pattern works in test files
rg "fs\.existsSync" --type js --type ts

# Then check different fs import/require patterns
rg "(?:import.*from ['|\"]fs['|\"]|require\(['|\"]fs['\"])" --type js --type ts --glob '!test/**'

Length of output: 369

test/fixtures/development-ts/config/config.default.ts (2)

1-1: Ensure correct module resolution for TypeScript

Importing a JavaScript file using a relative path in a TypeScript file may cause module resolution issues, especially with ESM modules. Verify that the TypeScript compiler options (tsconfig.json) are configured to handle this import correctly.

Please check if the compilerOptions in your tsconfig.json include the correct settings, such as allowJs: true and esModuleInterop: true.


5-10: Configuration file looks good

The configuration is properly defined with type casting to EggAppConfig, ensuring type safety.

test/fixtures/development-ts/app/router.ts (1)

1-11: Implementation aligns well with PR objectives!

The router implementation correctly uses:

  • ESM import syntax
  • TypeScript types from egg
  • Modern async/await pattern for route handlers
test/utils.ts (1)

1-9: Well-implemented path handling for dual ESM/CJS support!

The implementation correctly:

  • Uses node: protocol imports for better compatibility
  • Handles both ESM and CJS dirname resolution
src/app.ts (1)

1-17: Excellent TypeScript implementation with modern features!

The implementation demonstrates good practices:

  • Proper interface implementation
  • Private class fields for encapsulation
  • Type-safe configuration access
test/not-reload.test.ts (1)

1-4: LGTM! Clean ES modules migration.

The migration to ES modules is well-executed, using the recommended patterns:

  • Node.js built-in modules use the node: protocol
  • Type imports are properly separated
  • Local imports use the .js extension
🧰 Tools
🪛 Biome (1.9.4)

[error] 4-4: Do not shadow the global "escape" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

src/types.ts (1)

1-30: LGTM! Well-structured type definitions.

The DevelopmentConfig interface is well-documented and properly typed. The optional reloadPattern property allows for flexible configuration.

test/custom.test.ts (1)

21-24: Reconsider skipping tests in CI environment

The test is being skipped in CI, which could hide potential issues. Consider implementing a more robust solution that works in both CI and local environments.

test/absolute.test.ts (1)

12-13: Address the FIXME comment

The comment "FIXME: ONLY WATCH EXIST DIR" needs to be addressed:

Would you like me to help implement proper directory watching logic or create an issue to track this?

test/development-ts.test.ts (1)

54-54: Verify the expected reload count.

There's an inconsistency in the expected reload count between this test (2) and the similar test in development.test.ts (4).

✅ Verification successful

The reload count difference appears to be intentional

The tests are identical in structure but operate in different environments:

  • development.test.ts tests JavaScript files (.js) in the 'development' directory
  • development-ts.test.ts tests TypeScript files (.ts) in the 'development-ts' directory

The different reload counts (4 vs 2) likely reflect the intended behavior for their respective environments.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other reload count assertions
rg -A 1 "assert\.equal\(count\(.*stdout.*reload worker" --type ts

Length of output: 302


Script:

#!/bin/bash
# Compare the two test files to understand context
diff test/development.test.ts test/development-ts.test.ts

# Search for reload-related code and setup
rg -B 5 -A 5 "reload worker" test/development*.test.ts

Length of output: 4829

package.json (4)

78-90: LGTM! Proper dual module format configuration

The exports configuration correctly defines both ESM and CommonJS entry points with their corresponding type definitions. The tshy configuration is properly set up for building both formats.

Also applies to: 71-77


54-61: LGTM! Comprehensive build and test scripts

The scripts section is well organized with proper lifecycle hooks:

  • Linting and cleaning before tests
  • Type checking and dual format building before publish
  • Security checks with attw

16-20: ⚠️ Potential issue

Remove duplicate exports field in eggPlugin section

The exports field in the eggPlugin section appears to be misplaced. This configuration should only be at the root level where it's already correctly defined (lines 78-90).

  "eggPlugin": {
    "name": "development",
    "env": [
      "local"
    ],
    "dependencies": [
      "watcher"
-    ],
-    "exports": {
-      "import": "./dist/esm",
-      "require": "./dist/commonjs",
-      "typescript": "./src"
-    }
+    ]
  },

Likely invalid or redundant comment.


29-48: Verify compatibility with updated dependencies

Several major version updates are included:

  • @eggjs/core: ^6.2.11
  • debounce: ^2.2.0
  • New dev dependencies for TypeScript support

Let's verify the compatibility of these versions:

✅ Verification successful

Dependencies and TypeScript setup verified successfully

The updated dependencies are compatible and properly integrated:

  • No security vulnerabilities found
  • TypeScript configuration is complete and correct
  • Project structure supports all dependency updates
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any known compatibility issues with the new versions
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "debounce") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
    }
  }
}'

# Check if @eggjs/core ^6.2.11 is the latest compatible version
npm view @eggjs/core versions --json | jq 'map(select(startswith("6.")))'

Length of output: 667


Script:

#!/bin/bash
# Check TypeScript configuration and egg compatibility
cat package.json | jq '.scripts + .tshy'

# Check if there are any TypeScript configuration files
fd -e json -e jsonc tsconfig

# Look for egg configuration files
fd -g 'config.*' -t f

Length of output: 1046

test/fixtures/development-ts/tsconfig.json (1)

1-10: LGTM! TypeScript configuration optimized for modern Node.js

The TypeScript configuration is well-structured:

  • Extends the standard @eggjs/tsconfig
  • Uses modern module resolution (NodeNext)
  • Enables strict type checking
  • Targets ES2022 which is compatible with Node.js >= 18.19.0
.eslintrc (1)

2-5: LGTM! ESLint configuration updated for TypeScript

The ESLint configuration properly extends:

  • TypeScript-specific rules from eslint-config-egg
  • Node prefix enforcement rules

Comment on lines +99 to +100
if (filepath.startsWith(ignorePath)) {
return true;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ensure cross-platform path compatibility

Using filepath.startsWith(ignorePath) may not work correctly on all platforms due to differences in path separators ('/' vs. '\\'). To ensure cross-platform compatibility, consider using path.relative() or path.normalize().

Update the condition to:

-if (filepath.startsWith(ignorePath)) {
+if (!path.relative(ignorePath, filepath).startsWith('..')) {

This checks if filepath is within ignorePath regardless of the operating system.

Don't forget to import path if not already imported:

+import path from 'node:path';

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +2 to 4
app.get('/foo.js', async function() {
this.body = 'foo.js';
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix this binding in asynchronous route handlers

When using async function as route handlers, the this context is not bound as it is in generator functions. To access the context, you should use an arrow function with the ctx parameter instead of this.

Apply this diff to fix the context binding:

-app.get('/foo.js', async function() {
-  this.body = 'foo.js';
+app.get('/foo.js', async (ctx) => {
+  ctx.body = 'foo.js';
});

...

-app.get('/foo', async function() {
-  this.body = 'foo';
+app.get('/foo', async (ctx) => {
+  ctx.body = 'foo';
});

Also applies to: 6-8

Comment on lines +11 to +15
export function escape(str: string) {
return str
.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
.replace(/-/g, '\\x2d');
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Rename the escape function to avoid shadowing global property

The function name shadows the global escape property which could lead to confusion.

Consider renaming to something more specific like escapeRegExp:

-export function escape(str: string) {
+export function escapeRegExp(str: string) {
   return str
     .replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
     .replace(/-/g, '\\x2d');
 }
📝 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
export function escape(str: string) {
return str
.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
.replace(/-/g, '\\x2d');
}
export function escapeRegExp(str: string) {
return str
.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
.replace(/-/g, '\\x2d');
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 11-11: Do not shadow the global "escape" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

Comment on lines +25 to +31
let filepath;
filepath = getFilepath('custom/app/service/a.js');
await fs.writeFile(filepath, '');
await scheduler.wait(DELAY);

await fs.unlink(filepath);
app.expect('stdout', /a\.js/);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add cleanup for created files

File operations should be cleaned up after each test to prevent test pollution:

+  afterEach(async () => {
+    const filepath = getFilepath('custom/app/service/a.js');
+    await fs.rm(filepath, { force: true });
+    const filepathTs = getFilepath('custom/app/service/b.ts');
+    await fs.rm(filepathTs, { force: true });
+  });

Committable suggestion skipped: line range outside the PR's diff.

const rundir = app.config.rundir;
const files = await fs.readdir(rundir);
const data = [];
const data: any[] = [];
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Define proper types instead of using 'any'

Consider creating an interface for the timing data structure instead of using any[]:

-  const data: any[] = [];
+  interface TimingData {
+    type: string;
+    pid: string;
+    start: number;
+    end: number;
+    index: number;
+    range: [number, number];
+    title: string;
+  }
+  const data: TimingData[] = [];

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +22 to +25
const jsonString = res.text.match(/data = (.*?);/);
assert(jsonString);
assert(jsonString[1].length > 3000);
const json = JSON.parse(jsonString[1]);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve JSON extraction robustness

The current regex-based JSON extraction could be more robust:

-    const jsonString = res.text.match(/data = (.*?);/);
-    assert(jsonString);
-    assert(jsonString[1].length > 3000);
-    const json = JSON.parse(jsonString[1]);
+    const match = res.text.match(/data = ({.*?});/s);
+    assert(match, 'Failed to extract JSON data from response');
+    const jsonString = match[1];
+    assert(jsonString.length > 3000, 'JSON data is unexpectedly small');
+    try {
+      const json = JSON.parse(jsonString);
+      assert(Array.isArray(json), 'Parsed data is not an array');
📝 Committable suggestion

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

Suggested change
const jsonString = res.text.match(/data = (.*?);/);
assert(jsonString);
assert(jsonString[1].length > 3000);
const json = JSON.parse(jsonString[1]);
const match = res.text.match(/data = ({.*?});/s);
assert(match, 'Failed to extract JSON data from response');
const jsonString = match[1];
assert(jsonString.length > 3000, 'JSON data is unexpectedly small');
try {
const json = JSON.parse(jsonString);
assert(Array.isArray(json), 'Parsed data is not an array');

Comment on lines +58 to +61
function count(str: string, match: string) {
const m = str.match(new RegExp(match, 'g'));
return m ? m.length : 0;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Move count utility function to utils.js.

The count function is duplicated across test files. Consider moving it to the shared utils file.

// In utils.js
+ export function count(str: string, match: string) {
+   const m = str.match(new RegExp(match, 'g'));
+   return m ? m.length : 0;
+ }

// In this file
- function count(str: string, match: string) {
-   const m = str.match(new RegExp(match, 'g'));
-   return m ? m.length : 0;
- }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +24 to +26
## Requirements

- egg >= 4.x
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add dual module format support to requirements

The requirements section should mention the dual module format support (ESM/CommonJS).

 ## Requirements

 - egg >= 4.x
+- Supports both ESM and CommonJS module formats
+- Node.js >= 18.19.0
📝 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
## Requirements
- egg >= 4.x
## Requirements
- egg >= 4.x
- Supports both ESM and CommonJS module formats
- Node.js >= 18.19.0

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.

1 participant