diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7b964ca..52d0802 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,7 +8,58 @@ env:
MIN_COVERAGE: ${{ vars.MIN_COVERAGE }}
jobs:
- ci:
+ website:
+ name: Website Tests
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup Dart
+ uses: dart-lang/setup-dart@v1
+ with:
+ sdk: ${{ vars.DART_VERSION }}
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+ cache: 'npm'
+ cache-dependency-path: website/package-lock.json
+
+ - name: Install dependencies
+ working-directory: website
+ run: npm ci
+
+ - name: Get Playwright version
+ id: playwright-version
+ working-directory: website
+ run: echo "version=$(npm ls @playwright/test --json | jq -r '.dependencies["@playwright/test"].version')" >> $GITHUB_OUTPUT
+
+ - name: Cache Playwright browsers
+ uses: actions/cache@v4
+ id: playwright-cache
+ with:
+ path: ~/.cache/ms-playwright
+ key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }}
+
+ - name: Install Playwright browsers
+ if: steps.playwright-cache.outputs.cache-hit != 'true'
+ working-directory: website
+ run: npx playwright install --with-deps chromium
+
+ - name: Install Playwright deps only (cached)
+ if: steps.playwright-cache.outputs.cache-hit == 'true'
+ run: npx playwright install-deps chromium
+
+ - name: Build website
+ working-directory: website
+ run: npm run build
+
+ - name: Run tests
+ working-directory: website
+ run: npm test
+
+ packages:
name: Lint, Test & Build
runs-on: ubuntu-latest
steps:
diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml
index 2bb6a6d..73f4b7c 100644
--- a/.github/workflows/deploy-website.yml
+++ b/.github/workflows/deploy-website.yml
@@ -39,13 +39,13 @@ jobs:
working-directory: website
run: npm ci
- - name: Generate API documentation
+ - name: Build website
working-directory: website
- run: ./scripts/generate-api-docs.sh
+ run: npm run build
- - name: Build website
+ - name: Run website tests
working-directory: website
- run: npx @11ty/eleventy
+ run: bash scripts/test.sh
- name: Setup Pages
uses: actions/configure-pages@v4
diff --git a/.gitignore b/.gitignore
index 88aef1d..f1ec095 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,15 @@ examples/mobile/rn/.expo/
# Website generated files
website/_site/
website/src/api/
+website/src/zh/api/dart_node_core/
+website/src/zh/api/dart_node_express/
+website/src/zh/api/dart_node_react/
+website/src/zh/api/dart_node_react_native/
+website/src/zh/api/dart_node_ws/
+website/src/zh/api/dart_node_better_sqlite3/
+website/src/zh/api/dart_node_mcp/
+website/src/zh/api/dart_logging/
+website/src/zh/api/reflux/
website/.dart-doc-temp/
examples/frontend/coverage/
@@ -39,4 +48,9 @@ examples/too_many_cooks_vscode_extension/.vscode-test/
examples/reflux_demo/flutter_counter/test/failures/
-mutation-reports
\ No newline at end of file
+mutation-reports
+.playwright-mcp/
+
+website/playwright-report/
+
+website/test-results/
diff --git a/CLAUDE.md b/CLAUDE.md
index 92e34d2..23414cd 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -34,6 +34,19 @@ Dart packages for building Node.js apps. Typed Dart layer over JS interop.
- All packages require: `austerity` (linting), `nadz` (Result types)
- `node_preamble` for dart2js Node.js compatibility
+# Web & Translation
+
+- Optimize for AI Search and SEO
+https://developers.google.com/search/blog/2025/05/succeeding-in-ai-search
+https://developers.google.com/search/docs/fundamentals/seo-starter-guide
+
+- Always translate the English version to the target language directly.
+- Be careful of cultural differences.
+- Avoid literal translations that may offend the reader.
+- Keep the code examples the same as the original but translate the comments to the target language
+- Minimize CSS.
+- Don't name CSS after sections. Name them after the HTML element
+
## Codebase Structure
```
diff --git a/README_zh.md b/README_zh.md
new file mode 100644
index 0000000..050901a
--- /dev/null
+++ b/README_zh.md
@@ -0,0 +1,48 @@
+# dart_node
+
+使用 Dart 编写完整技术栈:React Web 应用、基于 Expo 的 React Native 移动应用,以及 Node.js Express 后端。
+
+[文档](https://melbournedeveloper.github.io/dart_node/)
+
+
+
+## 包
+
+| 包 | 描述 |
+|---------|-------------|
+| [dart_node_core](packages/dart_node_core) | 核心 JS 互操作工具 |
+| [dart_node_express](packages/dart_node_express) | Express.js 绑定 |
+| [dart_node_ws](packages/dart_node_ws) | WebSocket 绑定 |
+| [dart_node_react](packages/dart_node_react) | React 绑定 |
+| [dart_node_react_native](packages/dart_node_react_native) | React Native 绑定 |
+| [dart_node_mcp](packages/dart_node_mcp) | MCP 服务器绑定 |
+| [dart_node_better_sqlite3](packages/dart_node_better_sqlite3) | SQLite3 绑定 |
+| [dart_jsx](packages/dart_jsx) | Dart JSX 转译器 |
+| [reflux](packages/reflux) | Redux 风格状态管理 |
+| [dart_logging](packages/dart_logging) | 结构化日志 |
+| [dart_node_coverage](packages/dart_node_coverage) | dart2js 代码覆盖率 |
+
+## 工具
+
+| 工具 | 描述 |
+|------|-------------|
+| [too-many-cooks](examples/too_many_cooks) | 多智能体协调 MCP 服务器 ([npm](https://www.npmjs.com/package/too-many-cooks)) |
+| [Too Many Cooks VSCode](examples/too_many_cooks_vscode_extension) | 智能体可视化 VSCode 扩展 |
+
+## 快速开始
+
+```bash
+# 切换到本地依赖
+dart tools/switch_deps.dart local
+
+# 运行全部
+sh run_dev.sh
+```
+
+打开 http://localhost:8080/web/
+
+**移动端:** 使用 VSCode 启动配置 `Mobile: Build & Run (Expo)`
+
+## 许可证
+
+BSD 3-Clause 许可证。版权所有 (c) 2025,Christian Findlay。
diff --git a/packages/dart_jsx/README.md b/packages/dart_jsx/README.md
index 4d94805..dd9685a 100644
--- a/packages/dart_jsx/README.md
+++ b/packages/dart_jsx/README.md
@@ -2,6 +2,13 @@
JSX transpiler for Dart - transforms JSX syntax to dart_node_react calls.
+## Installation
+
+```yaml
+dependencies:
+ dart_jsx: ^0.1.0
+```
+
## Usage
Write JSX inside `jsx()` calls in your Dart files:
diff --git a/packages/dart_jsx/README_zh.md b/packages/dart_jsx/README_zh.md
new file mode 100644
index 0000000..41d2a2b
--- /dev/null
+++ b/packages/dart_jsx/README_zh.md
@@ -0,0 +1,38 @@
+# dart_jsx
+
+Dart 的 JSX 转译器 - 将 JSX 语法转换为 dart_node_react 调用。
+
+## 安装
+
+```yaml
+dependencies:
+ dart_jsx: ^0.1.0
+```
+
+## 使用方法
+
+在 Dart 文件中的 `jsx()` 调用内编写 JSX:
+
+```dart
+final element = jsx(
+
Hello World
+
+);
+```
+
+转译器将其转换为:
+
+```dart
+final element = $div(className: 'app') >> [
+ $h1 >> 'Hello World',
+ $button(onClick: handleClick) >> 'Click me',
+];
+```
+
+## VSCode 扩展
+
+配套的 VSCode 扩展为 `.jsx` Dart 文件提供语法高亮。请参阅 [.vscode/extensions/dart-jsx](../../.vscode/extensions/dart-jsx)。
+
+## dart_node 的一部分
+
+[GitHub](https://github.com/MelbourneDeveloper/dart_node)
diff --git a/packages/dart_logging/README.md b/packages/dart_logging/README.md
index 4f8cae4..3022ee8 100644
--- a/packages/dart_logging/README.md
+++ b/packages/dart_logging/README.md
@@ -1,8 +1,14 @@
-# dart_logging
-Pino-style structured logging with child loggers.
+Pino-style structured logging with child loggers. Provides hierarchical logging with automatic context inheritance.
-## Getting Started
+## Installation
+
+```yaml
+dependencies:
+ dart_logging: ^0.11.0-beta
+```
+
+## Quick Start
```dart
import 'package:dart_logging/dart_logging.dart';
@@ -23,6 +29,94 @@ void main() {
}
```
-## Part of dart_node
+## Core Concepts
+
+### Logging Context
+
+Create a logging context with one or more transports:
+
+```dart
+final context = createLoggingContext(
+ transports: [logTransport(logToConsole)],
+);
+```
+
+### Log Levels
+
+Standard log levels are available (from lowest to highest severity):
+
+```dart
+logger.trace('Very detailed trace info');
+logger.debug('Debugging info');
+logger.info('Information');
+logger.warn('Warning');
+logger.error('Error occurred');
+logger.fatal('Fatal error');
+```
+
+### Structured Data
+
+Pass structured data with log messages:
+
+```dart
+logger.info('User logged in', structuredData: {'userId': 123, 'email': 'user@example.com'});
+```
+
+### Child Loggers
+
+Create child loggers that inherit and extend context:
+
+```dart
+final requestLogger = logger.child({'requestId': 'abc-123'});
+requestLogger.info('Start'); // Includes requestId
+
+final userLogger = requestLogger.child({'userId': 456});
+userLogger.info('Action'); // Includes both requestId and userId
+```
+
+This is useful for adding context that applies to a scope (like a request handler).
+
+### Custom Transports
+
+Create custom transports to send logs to different destinations:
+
+```dart
+void myTransport(LogEntry entry) {
+ // Send to external service, file, etc.
+ print('${entry.level}: ${entry.message}');
+}
+
+final context = createLoggingContext(
+ transports: [logTransport(myTransport)],
+);
+```
+
+## Example: Express Server Logging
+
+```dart
+import 'dart:js_interop';
+import 'package:dart_node_express/dart_node_express.dart';
+import 'package:dart_logging/dart_logging.dart';
+
+void main() {
+ final logger = createLoggerWithContext(
+ createLoggingContext(transports: [logTransport(logToConsole)]),
+ );
+
+ final app = express();
+
+ app.use(middleware((req, res, next) {
+ final reqLogger = logger.child({'path': req.path, 'method': req.method});
+ reqLogger.info('Request received');
+ next();
+ }));
+
+ app.listen(3000, () {
+ logger.info('Server started', structuredData: {'port': 3000});
+ }.toJS);
+}
+```
+
+## Source Code
-[GitHub](https://github.com/MelbourneDeveloper/dart_node)
+The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_logging).
diff --git a/packages/dart_logging/README_zh.md b/packages/dart_logging/README_zh.md
new file mode 100644
index 0000000..b692868
--- /dev/null
+++ b/packages/dart_logging/README_zh.md
@@ -0,0 +1,122 @@
+
+Pino 风格的结构化日志,支持子日志器。提供具有自动上下文继承的分层日志记录。
+
+## 安装
+
+```yaml
+dependencies:
+ dart_logging: ^0.11.0-beta
+```
+
+## 快速开始
+
+```dart
+import 'package:dart_logging/dart_logging.dart';
+
+void main() {
+ final context = createLoggingContext(
+ transports: [logTransport(logToConsole)],
+ );
+ final logger = createLoggerWithContext(context);
+
+ logger.info('Hello world');
+ logger.warn('Something might be wrong');
+ logger.error('Something went wrong');
+
+ // 具有继承上下文的子日志器
+ final childLogger = logger.child({'requestId': 'abc-123'});
+ childLogger.info('Processing request'); // requestId 自动包含
+}
+```
+
+## 核心概念
+
+### 日志上下文
+
+使用一个或多个传输创建日志上下文:
+
+```dart
+final context = createLoggingContext(
+ transports: [logTransport(logToConsole)],
+);
+```
+
+### 日志级别
+
+提供标准日志级别(从最低到最高严重性):
+
+```dart
+logger.trace('Very detailed trace info');
+logger.debug('Debugging info');
+logger.info('Information');
+logger.warn('Warning');
+logger.error('Error occurred');
+logger.fatal('Fatal error');
+```
+
+### 结构化数据
+
+在日志消息中传递结构化数据:
+
+```dart
+logger.info('User logged in', structuredData: {'userId': 123, 'email': 'user@example.com'});
+```
+
+### 子日志器
+
+创建继承并扩展上下文的子日志器:
+
+```dart
+final requestLogger = logger.child({'requestId': 'abc-123'});
+requestLogger.info('Start'); // 包含 requestId
+
+final userLogger = requestLogger.child({'userId': 456});
+userLogger.info('Action'); // 同时包含 requestId 和 userId
+```
+
+这对于添加适用于某个作用域(如请求处理程序)的上下文非常有用。
+
+### 自定义传输
+
+创建自定义传输以将日志发送到不同目的地:
+
+```dart
+void myTransport(LogEntry entry) {
+ // 发送到外部服务、文件等
+ print('${entry.level}: ${entry.message}');
+}
+
+final context = createLoggingContext(
+ transports: [logTransport(myTransport)],
+);
+```
+
+## 示例:Express 服务器日志
+
+```dart
+import 'dart:js_interop';
+import 'package:dart_node_express/dart_node_express.dart';
+import 'package:dart_logging/dart_logging.dart';
+
+void main() {
+ final logger = createLoggerWithContext(
+ createLoggingContext(transports: [logTransport(logToConsole)]),
+ );
+
+ final app = express();
+
+ app.use(middleware((req, res, next) {
+ final reqLogger = logger.child({'path': req.path, 'method': req.method});
+ reqLogger.info('Request received');
+ next();
+ }));
+
+ app.listen(3000, () {
+ logger.info('Server started', structuredData: {'port': 3000});
+ }.toJS);
+}
+```
+
+## 源代码
+
+源代码可在 [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_logging) 上获取。
diff --git a/packages/dart_node_better_sqlite3/README.md b/packages/dart_node_better_sqlite3/README.md
index efe21fe..1882ef8 100644
--- a/packages/dart_node_better_sqlite3/README.md
+++ b/packages/dart_node_better_sqlite3/README.md
@@ -1,8 +1,21 @@
-# dart_node_better_sqlite3
-Typed Dart bindings for better-sqlite3. Synchronous SQLite3 with WAL mode.
+Typed Dart bindings for [better-sqlite3](https://github.com/WiseLibs/better-sqlite3). Provides synchronous SQLite3 access with WAL mode support for Node.js applications.
-## Getting Started
+## Installation
+
+```yaml
+dependencies:
+ dart_node_better_sqlite3: ^0.11.0-beta
+ nadz: ^0.9.0
+```
+
+Also install the npm package:
+
+```bash
+npm install better-sqlite3
+```
+
+## Quick Start
```dart
import 'package:dart_node_better_sqlite3/dart_node_better_sqlite3.dart';
@@ -35,14 +48,80 @@ void main() {
}
```
-## Run
+## Core Concepts
+
+### Opening a Database
+
+```dart
+final db = switch (openDatabase('./my.db')) {
+ Success(:final value) => value,
+ Error(:final error) => throw Exception(error),
+};
+```
+
+Options can be passed for read-only mode, memory databases, etc.
+
+### Executing SQL
+
+For statements that don't return data:
+
+```dart
+db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
+db.exec('DROP TABLE IF EXISTS temp');
+```
+
+### Prepared Statements
+
+For parameterized queries:
+
+```dart
+final stmt = switch (db.prepare('INSERT INTO users (name, email) VALUES (?, ?)')) {
+ Success(:final value) => value,
+ Error(:final error) => throw Exception(error),
+};
+
+stmt.run(['Alice', 'alice@example.com']);
+stmt.run(['Bob', 'bob@example.com']);
+```
+
+### Querying Data
+
+```dart
+final query = switch (db.prepare('SELECT * FROM users WHERE id = ?')) {
+ Success(:final value) => value,
+ Error(:final error) => throw Exception(error),
+};
+
+// Get single row
+final row = query.get([1]);
+
+// Get all rows
+final allRows = query.all([]);
+```
+
+### Transactions
+
+```dart
+db.exec('BEGIN');
+try {
+ // Multiple operations...
+ db.exec('COMMIT');
+} catch (e) {
+ db.exec('ROLLBACK');
+ rethrow;
+}
+```
+
+## Compile and Run
```bash
-npm install better-sqlite3
+# Compile Dart to JavaScript
dart compile js -o app.js lib/main.dart
+
+# Run with Node.js
node app.js
```
-## Part of dart_node
+## Source Code
-[GitHub](https://github.com/MelbourneDeveloper/dart_node)
+The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_better_sqlite3).
diff --git a/website/src/docs/sqlite/index.md b/packages/dart_node_better_sqlite3/README_zh.md
similarity index 63%
rename from website/src/docs/sqlite/index.md
rename to packages/dart_node_better_sqlite3/README_zh.md
index 178411c..e67916b 100644
--- a/website/src/docs/sqlite/index.md
+++ b/packages/dart_node_better_sqlite3/README_zh.md
@@ -1,30 +1,21 @@
----
-layout: layouts/docs.njk
-title: dart_node_better_sqlite3
-description: Typed Dart bindings for better-sqlite3. Synchronous SQLite3 with WAL mode for Node.js.
-eleventyNavigation:
- key: dart_node_better_sqlite3
- parent: Packages
- order: 6
----
-Typed Dart bindings for [better-sqlite3](https://github.com/WiseLibs/better-sqlite3). Provides synchronous SQLite3 access with WAL mode support for Node.js applications.
+[better-sqlite3](https://github.com/WiseLibs/better-sqlite3) 的类型化 Dart 绑定。为 Node.js 应用程序提供支持 WAL 模式的同步 SQLite3 访问。
-## Installation
+## 安装
```yaml
dependencies:
- dart_node_better_sqlite3: ^0.2.0
+ dart_node_better_sqlite3: ^0.11.0-beta
nadz: ^0.9.0
```
-Also install the npm package:
+通过 npm 安装:
```bash
npm install better-sqlite3
```
-## Quick Start
+## 快速开始
```dart
import 'package:dart_node_better_sqlite3/dart_node_better_sqlite3.dart';
@@ -57,9 +48,9 @@ void main() {
}
```
-## Core Concepts
+## 核心概念
-### Opening a Database
+### 打开数据库
```dart
final db = switch (openDatabase('./my.db')) {
@@ -68,20 +59,20 @@ final db = switch (openDatabase('./my.db')) {
};
```
-Options can be passed for read-only mode, memory databases, etc.
+可以传递选项用于只读模式、内存数据库等。
-### Executing SQL
+### 执行 SQL
-For statements that don't return data:
+对于不返回数据的语句:
```dart
db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
db.exec('DROP TABLE IF EXISTS temp');
```
-### Prepared Statements
+### 预处理语句
-For parameterized queries:
+用于参数化查询:
```dart
final stmt = switch (db.prepare('INSERT INTO users (name, email) VALUES (?, ?)')) {
@@ -93,7 +84,7 @@ stmt.run(['Alice', 'alice@example.com']);
stmt.run(['Bob', 'bob@example.com']);
```
-### Querying Data
+### 查询数据
```dart
final query = switch (db.prepare('SELECT * FROM users WHERE id = ?')) {
@@ -101,19 +92,19 @@ final query = switch (db.prepare('SELECT * FROM users WHERE id = ?')) {
Error(:final error) => throw Exception(error),
};
-// Get single row
+// 获取单行
final row = query.get([1]);
-// Get all rows
+// 获取所有行
final allRows = query.all([]);
```
-### Transactions
+### 事务
```dart
db.exec('BEGIN');
try {
- // Multiple operations...
+ // 多个操作...
db.exec('COMMIT');
} catch (e) {
db.exec('ROLLBACK');
@@ -121,16 +112,16 @@ try {
}
```
-## Compile and Run
+## 编译和运行
```bash
-# Compile Dart to JavaScript
+# 将 Dart 编译为 JavaScript
dart compile js -o app.js lib/main.dart
-# Run with Node.js
+# 使用 Node.js 运行
node app.js
```
-## Source Code
+## 源代码
-The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_better_sqlite3).
+源代码可在 [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_better_sqlite3) 上获取。
diff --git a/packages/dart_node_better_sqlite3/pubspec.lock b/packages/dart_node_better_sqlite3/pubspec.lock
index 75b8a68..4ee964c 100644
--- a/packages/dart_node_better_sqlite3/pubspec.lock
+++ b/packages/dart_node_better_sqlite3/pubspec.lock
@@ -95,7 +95,7 @@ packages:
path: "../dart_node_core"
relative: true
source: path
- version: "0.9.0-beta"
+ version: "0.11.0-beta"
dart_node_coverage:
dependency: "direct dev"
description:
diff --git a/packages/dart_node_core/README.md b/packages/dart_node_core/README.md
index 81669c4..a7b4a82 100644
--- a/packages/dart_node_core/README.md
+++ b/packages/dart_node_core/README.md
@@ -1,24 +1,93 @@
-# dart_node_core
-Core JS interop utilities for Dart-to-JavaScript compilation.
+`dart_node_core` is the foundation layer that all other dart_node packages build upon. It provides low-level JavaScript interop utilities, Node.js bindings, and console helpers.
-## Getting Started
+## Installation
+
+```yaml
+dependencies:
+ dart_node_core: ^0.11.0-beta
+```
+
+## Core Utilities
+
+### Console Logging
+
+```dart
+import 'package:dart_node_core/dart_node_core.dart';
+
+void main() {
+ consoleLog('Hello, world!'); // stdout
+ consoleError('Something went wrong'); // stderr
+}
+```
+
+### Requiring Node.js Modules
```dart
import 'package:dart_node_core/dart_node_core.dart';
void main() {
- // Require a Node.js module
+ // Load a Node.js built-in module
final fs = requireModule('fs');
- // Convert Dart values to JS
+ // Load an npm package
+ final express = requireModule('express');
+}
+```
+
+### Accessing Global Objects
+
+```dart
+import 'package:dart_node_core/dart_node_core.dart';
+
+void main() {
+ // Access global JavaScript objects
+ final process = getGlobal('process');
+}
+```
+
+## Interop Helpers
+
+### Converting Between Dart and JavaScript
+
+Uses `dart:js_interop` for type-safe conversions:
+
+```dart
+import 'dart:js_interop';
+
+void main() {
+ // Dart to JS
final jsString = 'hello'.toJS;
+ final jsNumber = 42.toJS;
+ final jsList = [1, 2, 3].jsify();
+
+ // JS to Dart
+ final dartString = jsString.toDart;
+}
+```
+
+## FP Extensions
+
+Functional programming utilities:
+
+```dart
+import 'package:dart_node_core/dart_node_core.dart';
+
+String? getName() => 'World';
+
+void main() {
+ // Pattern match on nullable values
+ String? name = getName();
+ final result = name.match(
+ some: (n) => 'Hello, $n',
+ none: () => 'No name provided',
+ );
- // Work with JS objects
- final result = fs.callMethod('readFileSync'.toJS, ['./file.txt'.toJS]);
+ // Apply transformations
+ final length = 'hello'.let((s) => s.length);
}
```
-## Part of dart_node
+## Source Code
-[GitHub](https://github.com/MelbourneDeveloper/dart_node)
+The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_core).
diff --git a/packages/dart_node_core/README_zh.md b/packages/dart_node_core/README_zh.md
new file mode 100644
index 0000000..80e2821
--- /dev/null
+++ b/packages/dart_node_core/README_zh.md
@@ -0,0 +1,93 @@
+
+`dart_node_core` 是所有其他 dart_node 包的基础层。它提供底层 JavaScript 互操作工具、Node.js 绑定和控制台辅助功能。
+
+## 安装
+
+```yaml
+dependencies:
+ dart_node_core: ^0.11.0-beta
+```
+
+## 核心工具
+
+### 控制台日志
+
+```dart
+import 'package:dart_node_core/dart_node_core.dart';
+
+void main() {
+ consoleLog('Hello, world!'); // 标准输出
+ consoleError('Something went wrong'); // 标准错误输出
+}
+```
+
+### 加载 Node.js 模块
+
+```dart
+import 'package:dart_node_core/dart_node_core.dart';
+
+void main() {
+ // 加载 Node.js 内置模块
+ final fs = requireModule('fs');
+
+ // 加载 npm 包
+ final express = requireModule('express');
+}
+```
+
+### 访问全局对象
+
+```dart
+import 'package:dart_node_core/dart_node_core.dart';
+
+void main() {
+ // 访问全局 JavaScript 对象
+ final process = getGlobal('process');
+}
+```
+
+## 互操作辅助工具
+
+### Dart 和 JavaScript 之间的转换
+
+使用 `dart:js_interop` 进行类型安全转换:
+
+```dart
+import 'dart:js_interop';
+
+void main() {
+ // Dart 转 JS
+ final jsString = 'hello'.toJS;
+ final jsNumber = 42.toJS;
+ final jsList = [1, 2, 3].jsify();
+
+ // JS 转 Dart
+ final dartString = jsString.toDart;
+}
+```
+
+## 函数式编程扩展
+
+函数式编程工具:
+
+```dart
+import 'package:dart_node_core/dart_node_core.dart';
+
+String? getName() => 'World';
+
+void main() {
+ // 对可空值进行模式匹配
+ String? name = getName();
+ final result = name.match(
+ some: (n) => 'Hello, $n',
+ none: () => 'No name provided',
+ );
+
+ // 应用转换
+ final length = 'hello'.let((s) => s.length);
+}
+```
+
+## 源代码
+
+源代码可在 [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_core) 上获取。
diff --git a/packages/dart_node_coverage/README_zh.md b/packages/dart_node_coverage/README_zh.md
new file mode 100644
index 0000000..ae1b6f9
--- /dev/null
+++ b/packages/dart_node_coverage/README_zh.md
@@ -0,0 +1,28 @@
+# dart_node_coverage
+
+用于使用 dart2js 编译并在 Node.js 中执行的 Dart 代码的代码覆盖率收集工具。
+
+## 架构
+
+此包提供 Dart 源代码的编译时插桩功能,以便在通过 dart2js 在 Node.js 中运行测试时启用行覆盖率跟踪。
+
+详细架构文档请参阅 [lib/src/architecture.dart](lib/src/architecture.dart)。
+
+## 主要功能
+
+- **编译时插桩**:在 dart2js 编译前插入覆盖率探针
+- **LCOV 输出**:与 genhtml、coveralls 等兼容的标准格式
+- **与 dart test 集成**:与现有测试工作流程配合使用
+- **禁用时零运行时开销**:无插桩则无成本
+
+## 工作原理
+
+1. **分析** Dart 源代码以识别可执行行
+2. **插桩** 源代码,插入覆盖率探针调用
+3. **编译** 使用 dart2js 编译插桩后的源代码
+4. **执行** 在 Node.js 中运行测试(自动收集覆盖率)
+5. **生成** 从覆盖率数据生成 LCOV 报告
+
+## 状态
+
+此包处于早期开发阶段。架构已定义,实现正在进行中。
diff --git a/packages/dart_node_express/README.md b/packages/dart_node_express/README.md
index 333a839..d075b82 100644
--- a/packages/dart_node_express/README.md
+++ b/packages/dart_node_express/README.md
@@ -1,32 +1,326 @@
# dart_node_express
-Express.js bindings for Dart. Build Node.js HTTP servers entirely in Dart.
+Type-safe Express.js bindings for Dart. Build HTTP servers and REST APIs entirely in Dart.
-## Getting Started
+## Installation
+
+```yaml
+dependencies:
+ dart_node_express: ^0.11.0-beta
+```
+
+Also install Express via npm:
+
+```bash
+npm install express
+```
+
+## Quick Start
```dart
+import 'dart:js_interop';
import 'package:dart_node_express/dart_node_express.dart';
void main() {
final app = express();
- app.get('/', (req, res) {
- res.send('Hello from Dart!');
- });
+ app.get('/', handler((req, res) {
+ res.send('Hello, Dart!');
+ }));
app.listen(3000, () {
- print('Server running on http://localhost:3000');
+ print('Server running on port 3000');
+ }.toJS);
+}
+```
+
+## Routing
+
+### Basic Routes
+
+```dart
+app.get('/users', handler((req, res) {
+ res.jsonMap({'users': []});
+}));
+
+app.post('/users', handler((req, res) {
+ final body = req.body;
+ res.status(201);
+ res.jsonMap({'created': true});
+}));
+
+app.put('/users/:id', handler((req, res) {
+ final id = req.params['id'];
+ res.jsonMap({'updated': id});
+}));
+
+app.delete('/users/:id', handler((req, res) {
+ res.status(204);
+ res.end();
+}));
+```
+
+### Route Parameters
+
+```dart
+app.get('/users/:userId/posts/:postId', handler((req, res) {
+ final userId = req.params['userId'];
+ final postId = req.params['postId'];
+
+ res.jsonMap({
+ 'userId': userId,
+ 'postId': postId,
+ });
+}));
+```
+
+### Query Parameters
+
+```dart
+app.get('/search', handler((req, res) {
+ final query = req.query['q'];
+ final page = int.tryParse(req.query['page'] ?? '1') ?? 1;
+
+ res.jsonMap({
+ 'query': query,
+ 'page': page,
});
+}));
+```
+
+## Request Object
+
+The `Request` object provides access to incoming request data:
+
+```dart
+app.post('/api/data', handler((req, res) {
+ // Request body (requires body-parsing middleware)
+ final body = req.body;
+
+ // Headers
+ final contentType = req.headers['content-type'];
+
+ // URL path
+ final path = req.path;
+
+ // HTTP method
+ final method = req.method;
+
+ // Query string parameters
+ final params = req.query;
+
+ res.jsonMap({'received': body});
+}));
+```
+
+## Response Object
+
+The `Response` object provides methods for sending responses:
+
+```dart
+// Send text
+res.send('Hello!');
+
+// Send JSON (for Dart Maps, use jsonMap)
+res.jsonMap({'message': 'Hello!'});
+
+// Set status code (separate call from response)
+res.status(201);
+res.jsonMap({'created': true});
+
+// Set headers
+res.set('X-Custom-Header', 'value');
+
+// Redirect
+res.redirect('/new-location');
+
+// End response without body
+res.status(204);
+res.end();
+```
+
+## Middleware
+
+### Custom Middleware
+
+```dart
+app.use(middleware((req, res, next) {
+ print('${req.method} ${req.path}');
+ next();
+}));
+```
+
+### Chaining Middleware
+
+```dart
+app.use(chain([
+ middleware((req, res, next) {
+ print('First middleware');
+ next();
+ }),
+ middleware((req, res, next) {
+ print('Second middleware');
+ next();
+ }),
+]));
+```
+
+### Request Context
+
+Store and retrieve values in the request context:
+
+```dart
+// Set context in middleware
+app.use(middleware((req, res, next) {
+ setContext(req, 'userId', '123');
+ next();
+}));
+
+// Get context in handler
+app.get('/profile', handler((req, res) {
+ final userId = getContext(req, 'userId');
+ res.jsonMap({'userId': userId});
+}));
+```
+
+## Router
+
+Organize routes with the Router:
+
+```dart
+Router createUserRouter() {
+ final router = Router();
+
+ router.get('/', handler((req, res) {
+ res.jsonMap({'users': []});
+ }));
+
+ router.post('/', handler((req, res) {
+ res.status(201);
+ res.jsonMap({'created': true});
+ }));
+
+ router.get('/:id', handler((req, res) {
+ res.jsonMap({'user': req.params['id']});
+ }));
+
+ return router;
+}
+
+void main() {
+ final app = express();
+
+ // Mount the router
+ final router = createUserRouter();
+ app.use('/api/users', router);
+
+ app.listen(3000);
}
```
-## Run
+## Async Handlers
-```bash
-dart compile js -o server.js lib/main.dart
-node server.js
+Use async handlers for database calls and other async operations:
+
+```dart
+app.get('/users', asyncHandler((req, res) async {
+ final users = await database.fetchUsers();
+ res.jsonMap({'users': users});
+}));
+```
+
+The `asyncHandler` wrapper ensures errors are properly caught and passed to error middleware.
+
+## Validation
+
+Use the schema-based validation system:
+
+```dart
+// Define a validated data type
+typedef CreateUserData = ({String name, String email, int? age});
+
+// Create a schema
+final createUserSchema = schema(
+ {
+ 'name': string().minLength(2).maxLength(50),
+ 'email': string().email(),
+ 'age': optional(int_().positive()),
+ },
+ (data) => (
+ name: data['name'] as String,
+ email: data['email'] as String,
+ age: data['age'] as int?,
+ ),
+);
+
+// Use validation middleware
+app.post('/users', validateBody(createUserSchema));
+app.post('/users', handler((req, res) {
+ final result = getValidatedBody(req);
+ switch (result) {
+ case Success(:final value):
+ res.status(201);
+ res.jsonMap({'name': value.name, 'email': value.email});
+ case Error(:final error):
+ res.status(400);
+ res.jsonMap({'error': error});
+ }
+}));
+```
+
+### Available Validators
+
+```dart
+// String validators
+string().minLength(2).maxLength(100).notEmpty().email().alphanumeric()
+
+// Integer validators
+int_().min(0).max(100).positive().range(1, 10)
+
+// Boolean validators
+bool_()
+
+// Optional wrapper
+optional(string())
+```
+
+## Complete Example
+
+```dart
+import 'dart:js_interop';
+import 'package:dart_node_express/dart_node_express.dart';
+
+void main() {
+ final app = express();
+
+ // Logging middleware
+ app.use(middleware((req, res, next) {
+ print('[${DateTime.now()}] ${req.method} ${req.path}');
+ next();
+ }));
+
+ // Routes
+ app.get('/', handler((req, res) {
+ res.jsonMap({
+ 'name': 'My API',
+ 'version': '1.0.0',
+ });
+ }));
+
+ app.get('/health', handler((req, res) {
+ res.jsonMap({'status': 'ok'});
+ }));
+
+ // Mount routers
+ app.use('/api/users', createUserRouter());
+
+ // Start server
+ app.listen(3000, () {
+ print('Server running on port 3000');
+ }.toJS);
+}
```
-## Part of dart_node
+## Source Code
-[GitHub](https://github.com/MelbourneDeveloper/dart_node)
+The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_express).
diff --git a/packages/dart_node_express/README_zh.md b/packages/dart_node_express/README_zh.md
new file mode 100644
index 0000000..5f89bf0
--- /dev/null
+++ b/packages/dart_node_express/README_zh.md
@@ -0,0 +1,326 @@
+# dart_node_express
+
+类型安全的 Express.js 绑定。完全使用 Dart 构建 HTTP 服务器和 REST API。
+
+## 安装
+
+```yaml
+dependencies:
+ dart_node_express: ^0.11.0-beta
+```
+
+通过 npm 安装 Express:
+
+```bash
+npm install express
+```
+
+## 快速开始
+
+```dart
+import 'dart:js_interop';
+import 'package:dart_node_express/dart_node_express.dart';
+
+void main() {
+ final app = express();
+
+ app.get('/', handler((req, res) {
+ res.send('Hello, Dart!');
+ }));
+
+ app.listen(3000, () {
+ print('Server running on port 3000');
+ }.toJS);
+}
+```
+
+## 路由
+
+### 基本路由
+
+```dart
+app.get('/users', handler((req, res) {
+ res.jsonMap({'users': []});
+}));
+
+app.post('/users', handler((req, res) {
+ final body = req.body;
+ res.status(201);
+ res.jsonMap({'created': true});
+}));
+
+app.put('/users/:id', handler((req, res) {
+ final id = req.params['id'];
+ res.jsonMap({'updated': id});
+}));
+
+app.delete('/users/:id', handler((req, res) {
+ res.status(204);
+ res.end();
+}));
+```
+
+### 路由参数
+
+```dart
+app.get('/users/:userId/posts/:postId', handler((req, res) {
+ final userId = req.params['userId'];
+ final postId = req.params['postId'];
+
+ res.jsonMap({
+ 'userId': userId,
+ 'postId': postId,
+ });
+}));
+```
+
+### 查询参数
+
+```dart
+app.get('/search', handler((req, res) {
+ final query = req.query['q'];
+ final page = int.tryParse(req.query['page'] ?? '1') ?? 1;
+
+ res.jsonMap({
+ 'query': query,
+ 'page': page,
+ });
+}));
+```
+
+## 请求对象
+
+`Request` 对象提供对传入请求数据的访问:
+
+```dart
+app.post('/api/data', handler((req, res) {
+ // 请求体(需要 body-parsing 中间件)
+ final body = req.body;
+
+ // 请求头
+ final contentType = req.headers['content-type'];
+
+ // URL 路径
+ final path = req.path;
+
+ // HTTP 方法
+ final method = req.method;
+
+ // 查询字符串参数
+ final params = req.query;
+
+ res.jsonMap({'received': body});
+}));
+```
+
+## 响应对象
+
+`Response` 对象提供发送响应的方法:
+
+```dart
+// 发送文本
+res.send('Hello!');
+
+// 发送 JSON(对于 Dart Map,使用 jsonMap)
+res.jsonMap({'message': 'Hello!'});
+
+// 设置状态码(与响应分开调用)
+res.status(201);
+res.jsonMap({'created': true});
+
+// 设置响应头
+res.set('X-Custom-Header', 'value');
+
+// 重定向
+res.redirect('/new-location');
+
+// 结束响应(无响应体)
+res.status(204);
+res.end();
+```
+
+## 中间件
+
+### 自定义中间件
+
+```dart
+app.use(middleware((req, res, next) {
+ print('${req.method} ${req.path}');
+ next();
+}));
+```
+
+### 链式中间件
+
+```dart
+app.use(chain([
+ middleware((req, res, next) {
+ print('First middleware');
+ next();
+ }),
+ middleware((req, res, next) {
+ print('Second middleware');
+ next();
+ }),
+]));
+```
+
+### 请求上下文
+
+在请求上下文中存储和检索值:
+
+```dart
+// 在中间件中设置上下文
+app.use(middleware((req, res, next) {
+ setContext(req, 'userId', '123');
+ next();
+}));
+
+// 在处理程序中获取上下文
+app.get('/profile', handler((req, res) {
+ final userId = getContext(req, 'userId');
+ res.jsonMap({'userId': userId});
+}));
+```
+
+## 路由器
+
+使用路由器组织路由:
+
+```dart
+Router createUserRouter() {
+ final router = Router();
+
+ router.get('/', handler((req, res) {
+ res.jsonMap({'users': []});
+ }));
+
+ router.post('/', handler((req, res) {
+ res.status(201);
+ res.jsonMap({'created': true});
+ }));
+
+ router.get('/:id', handler((req, res) {
+ res.jsonMap({'user': req.params['id']});
+ }));
+
+ return router;
+}
+
+void main() {
+ final app = express();
+
+ // 挂载路由器
+ final router = createUserRouter();
+ app.use('/api/users', router);
+
+ app.listen(3000);
+}
+```
+
+## 异步处理程序
+
+使用异步处理程序进行数据库调用和其他异步操作:
+
+```dart
+app.get('/users', asyncHandler((req, res) async {
+ final users = await database.fetchUsers();
+ res.jsonMap({'users': users});
+}));
+```
+
+`asyncHandler` 包装器确保错误被正确捕获并传递给错误中间件。
+
+## 验证
+
+使用基于 Schema 的验证系统:
+
+```dart
+// 定义验证数据类型
+typedef CreateUserData = ({String name, String email, int? age});
+
+// 创建 Schema
+final createUserSchema = schema(
+ {
+ 'name': string().minLength(2).maxLength(50),
+ 'email': string().email(),
+ 'age': optional(int_().positive()),
+ },
+ (data) => (
+ name: data['name'] as String,
+ email: data['email'] as String,
+ age: data['age'] as int?,
+ ),
+);
+
+// 使用验证中间件
+app.post('/users', validateBody(createUserSchema));
+app.post('/users', handler((req, res) {
+ final result = getValidatedBody(req);
+ switch (result) {
+ case Success(:final value):
+ res.status(201);
+ res.jsonMap({'name': value.name, 'email': value.email});
+ case Error(:final error):
+ res.status(400);
+ res.jsonMap({'error': error});
+ }
+}));
+```
+
+### 可用验证器
+
+```dart
+// 字符串验证器
+string().minLength(2).maxLength(100).notEmpty().email().alphanumeric()
+
+// 整数验证器
+int_().min(0).max(100).positive().range(1, 10)
+
+// 布尔验证器
+bool_()
+
+// 可选包装器
+optional(string())
+```
+
+## 完整示例
+
+```dart
+import 'dart:js_interop';
+import 'package:dart_node_express/dart_node_express.dart';
+
+void main() {
+ final app = express();
+
+ // 日志中间件
+ app.use(middleware((req, res, next) {
+ print('[${DateTime.now()}] ${req.method} ${req.path}');
+ next();
+ }));
+
+ // 路由
+ app.get('/', handler((req, res) {
+ res.jsonMap({
+ 'name': 'My API',
+ 'version': '1.0.0',
+ });
+ }));
+
+ app.get('/health', handler((req, res) {
+ res.jsonMap({'status': 'ok'});
+ }));
+
+ // 挂载路由器
+ app.use('/api/users', createUserRouter());
+
+ // 启动服务器
+ app.listen(3000, () {
+ print('Server running on port 3000');
+ }.toJS);
+}
+```
+
+## 源代码
+
+源代码可在 [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_express) 上获取。
diff --git a/packages/dart_node_express/pubspec.lock b/packages/dart_node_express/pubspec.lock
index 75b8a68..4ee964c 100644
--- a/packages/dart_node_express/pubspec.lock
+++ b/packages/dart_node_express/pubspec.lock
@@ -95,7 +95,7 @@ packages:
path: "../dart_node_core"
relative: true
source: path
- version: "0.9.0-beta"
+ version: "0.11.0-beta"
dart_node_coverage:
dependency: "direct dev"
description:
diff --git a/packages/dart_node_mcp/README.md b/packages/dart_node_mcp/README.md
index 366d2e0..3363d2d 100644
--- a/packages/dart_node_mcp/README.md
+++ b/packages/dart_node_mcp/README.md
@@ -1,8 +1,21 @@
-# dart_node_mcp
-MCP (Model Context Protocol) server bindings for Dart on Node.js.
+MCP (Model Context Protocol) server bindings for Dart on Node.js. Build AI tool servers that can be used by Claude, GPT, and other AI assistants.
-## Getting Started
+## Installation
+
+```yaml
+dependencies:
+ dart_node_mcp: ^0.11.0-beta
+ nadz: ^0.9.0
+```
+
+Also install the npm package:
+
+```bash
+npm install @modelcontextprotocol/sdk
+```
+
+## Quick Start
```dart
import 'package:dart_node_mcp/dart_node_mcp.dart';
@@ -34,11 +47,78 @@ Future main() async {
}
```
-## Run
+## Core Concepts
+
+### Server Creation
+
+Create an MCP server with a name and version:
+
+```dart
+final serverResult = McpServer.create((name: 'my-server', version: '1.0.0'));
+```
+
+### Registering Tools
+
+Tools are functions that AI assistants can call. Register them with a name, description, and handler:
+
+```dart
+server.registerTool(
+ 'greet',
+ (
+ description: 'Greet a user by name',
+ inputSchema: {
+ 'type': 'object',
+ 'properties': {
+ 'name': {'type': 'string', 'description': 'Name to greet'},
+ },
+ 'required': ['name'],
+ },
+ ),
+ (args, meta) async {
+ final name = args['name'] as String;
+ return (
+ content: [(type: 'text', text: 'Hello, $name!')],
+ isError: false,
+ );
+ },
+);
+```
+
+### Transport
+
+Connect to clients using stdio transport (standard for MCP):
+
+```dart
+final transport = switch (createStdioServerTransport()) {
+ Success(:final value) => value,
+ Error(:final error) => throw Exception(error),
+};
+
+await server.connect(transport);
+```
+
+## Compile and Run
```bash
+# Compile Dart to JavaScript
dart compile js -o server.js lib/main.dart
+
+# Run with Node.js
node server.js
```
-## Part of [dart_node](https://github.com/MelbourneDeveloper/dart_node)
+## Use with Claude Code
+
+Add your MCP server to Claude Code:
+
+```bash
+claude mcp add --transport stdio my-server -- node /path/to/server.js
+```
+
+## Example: Too Many Cooks
+
+The [Too Many Cooks](/docs/too-many-cooks/) MCP server is built with dart_node_mcp. It provides multi-agent coordination for AI assistants editing the same codebase.
+
+## Source Code
+
+The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_mcp).
diff --git a/website/src/docs/mcp/index.md b/packages/dart_node_mcp/README_zh.md
similarity index 59%
rename from website/src/docs/mcp/index.md
rename to packages/dart_node_mcp/README_zh.md
index 1d551fe..3a9f4a3 100644
--- a/website/src/docs/mcp/index.md
+++ b/packages/dart_node_mcp/README_zh.md
@@ -1,30 +1,21 @@
----
-layout: layouts/docs.njk
-title: dart_node_mcp
-description: MCP (Model Context Protocol) server bindings for Dart on Node.js. Build AI tool servers in Dart.
-eleventyNavigation:
- key: dart_node_mcp
- parent: Packages
- order: 7
----
-MCP (Model Context Protocol) server bindings for Dart on Node.js. Build AI tool servers that can be used by Claude, GPT, and other AI assistants.
+适用于 Node.js 上 Dart 的 MCP(模型上下文协议)服务器绑定。构建可供 Claude、GPT 和其他 AI 助手使用的 AI 工具服务器。
-## Installation
+## 安装
```yaml
dependencies:
- dart_node_mcp: ^0.2.0
+ dart_node_mcp: ^0.11.0-beta
nadz: ^0.9.0
```
-Also install the npm package:
+通过 npm 安装:
```bash
npm install @modelcontextprotocol/sdk
```
-## Quick Start
+## 快速开始
```dart
import 'package:dart_node_mcp/dart_node_mcp.dart';
@@ -56,19 +47,19 @@ Future main() async {
}
```
-## Core Concepts
+## 核心概念
-### Server Creation
+### 创建服务器
-Create an MCP server with a name and version:
+使用名称和版本创建 MCP 服务器:
```dart
final serverResult = McpServer.create((name: 'my-server', version: '1.0.0'));
```
-### Registering Tools
+### 注册工具
-Tools are functions that AI assistants can call. Register them with a name, description, and handler:
+工具是 AI 助手可以调用的函数。使用名称、描述和处理程序注册它们:
```dart
server.registerTool(
@@ -93,9 +84,9 @@ server.registerTool(
);
```
-### Transport
+### 传输
-Connect to clients using stdio transport (standard for MCP):
+使用标准输入输出传输连接到客户端(MCP 标准方式):
```dart
final transport = switch (createStdioServerTransport()) {
@@ -106,28 +97,28 @@ final transport = switch (createStdioServerTransport()) {
await server.connect(transport);
```
-## Compile and Run
+## 编译和运行
```bash
-# Compile Dart to JavaScript
+# 将 Dart 编译为 JavaScript
dart compile js -o server.js lib/main.dart
-# Run with Node.js
+# 使用 Node.js 运行
node server.js
```
-## Use with Claude Code
+## 与 Claude Code 一起使用
-Add your MCP server to Claude Code:
+将您的 MCP 服务器添加到 Claude Code:
```bash
claude mcp add --transport stdio my-server -- node /path/to/server.js
```
-## Example: Too Many Cooks
+## 示例:Too Many Cooks
-The [Too Many Cooks](/docs/too-many-cooks/) MCP server is built with dart_node_mcp. It provides multi-agent coordination for AI assistants editing the same codebase.
+[Too Many Cooks](/zh/docs/too-many-cooks/) MCP 服务器是使用 dart_node_mcp 构建的。它为编辑同一代码库的 AI 助手提供多智能体协调功能。
-## Source Code
+## 源代码
-The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_mcp).
+源代码可在 [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_mcp) 上获取。
diff --git a/packages/dart_node_mcp/pubspec.lock b/packages/dart_node_mcp/pubspec.lock
index f14fa43..7d44754 100644
--- a/packages/dart_node_mcp/pubspec.lock
+++ b/packages/dart_node_mcp/pubspec.lock
@@ -95,7 +95,7 @@ packages:
path: "../dart_node_core"
relative: true
source: path
- version: "0.9.0-beta"
+ version: "0.11.0-beta"
dart_node_coverage:
dependency: "direct dev"
description:
diff --git a/packages/dart_node_react/README.md b/packages/dart_node_react/README.md
index 11ed784..4c3ca6e 100644
--- a/packages/dart_node_react/README.md
+++ b/packages/dart_node_react/README.md
@@ -1,35 +1,436 @@
# dart_node_react
-React bindings for Dart. Build React web apps entirely in Dart.
+Type-safe React bindings for building web applications in Dart. If you know React, you'll feel right at home.
-## Getting Started
+## Installation
+
+```yaml
+dependencies:
+ dart_node_react: ^0.11.0-beta
+```
+
+Also install React via npm:
+
+```bash
+npm install react react-dom
+```
+
+## Quick Start
```dart
import 'package:dart_node_react/dart_node_react.dart';
+ReactElement app() {
+ return div(
+ className: 'app',
+ children: [
+ h1(children: [text('Hello, Dart!')]),
+ p(children: [text('Welcome to React with Dart.')]),
+ ],
+ );
+}
+
void main() {
- final app = div(
- props: {'className': 'app'},
+ final container = document.getElementById('root');
+ final root = ReactDOM.createRoot(container);
+ root.render(app());
+}
+```
+
+## Components
+
+### Functional Components
+
+```dart
+ReactElement greeting({required String name}) {
+ return div(
+ className: 'greeting',
+ children: [
+ text('Hello, $name!'),
+ ],
+ );
+}
+
+// Usage
+greeting(name: 'World');
+```
+
+### Components with Props
+
+```dart
+ReactElement userCard({
+ required String name,
+ required String email,
+ String? avatarUrl,
+}) {
+ return div(
+ className: 'user-card',
+ children: [
+ avatarUrl != null
+ ? img(src: avatarUrl, alt: name)
+ : div(className: 'avatar-placeholder'),
+ h2(children: [text(name)]),
+ p(children: [text(email)]),
+ ],
+ );
+}
+```
+
+## Hooks
+
+### useState
+
+Returns a `StateHook` with `.value`, `.set()`, and `.setWithUpdater()`:
+
+```dart
+ReactElement counter() {
+ final count = useState(0);
+
+ return div(children: [
+ p(children: [text('Count: ${count.value}')]),
+ button(
+ onClick: (_) => count.setWithUpdater((c) => c + 1),
+ children: [text('Increment')],
+ ),
+ button(
+ onClick: (_) => count.setWithUpdater((c) => c - 1),
+ children: [text('Decrement')],
+ ),
+ ]);
+}
+```
+
+### useStateLazy
+
+For expensive initial state computation:
+
+```dart
+final data = useStateLazy(() => expensiveComputation());
+```
+
+### useEffect
+
+```dart
+ReactElement timer() {
+ final seconds = useState(0);
+
+ useEffect(() {
+ final timer = Timer.periodic(Duration(seconds: 1), (_) {
+ seconds.setWithUpdater((s) => s + 1);
+ });
+
+ // Cleanup function
+ return () => timer.cancel();
+ }, []); // Empty deps = run once on mount
+
+ return p(children: [text('Seconds: ${seconds.value}')]);
+}
+```
+
+### useLayoutEffect
+
+Synchronous version of useEffect that runs before screen updates:
+
+```dart
+useLayoutEffect(() {
+ // DOM measurements
+ return () { /* cleanup */ };
+}, [dependency]);
+```
+
+### useRef
+
+```dart
+ReactElement focusInput() {
+ final inputRef = useRef(null);
+
+ void handleClick() {
+ inputRef.current?.focus();
+ }
+
+ return div(children: [
+ input(ref: inputRef, type: 'text'),
+ button(
+ onClick: (_) => handleClick(),
+ children: [text('Focus Input')],
+ ),
+ ]);
+}
+```
+
+### useMemo
+
+```dart
+ReactElement expensiveList({required List numbers}) {
+ final count = useState(0);
+
+ // Only recalculate when count.value changes
+ final fib = useMemo(
+ () => fibonacci(count.value),
+ [count.value],
+ );
+
+ return div(children: [
+ p(children: [text('Fibonacci of ${count.value} is $fib')]),
+ ]);
+}
+```
+
+### useCallback
+
+```dart
+ReactElement searchBox({required void Function(String) onSearch}) {
+ final query = useState('');
+
+ // Memoize the callback
+ final handleSubmit = useCallback(
+ () => onSearch(query.value),
+ [query.value, onSearch],
+ );
+
+ return form(
+ onSubmit: (_) => handleSubmit(),
children: [
- h1(children: ['Hello from Dart!']),
- button(
- props: {'onClick': () => print('Clicked!')},
- children: ['Click me'],
+ input(
+ value: query.value,
+ onChange: (e) => query.set(e.target.value),
),
+ button(type: 'submit', children: [text('Search')]),
],
);
+}
+```
+
+### useDebugValue
+
+Display custom labels in React DevTools:
+
+```dart
+useDebugValue(
+ isOnline.value,
+ (isOnline) => isOnline ? 'Online' : 'Not Online',
+);
+```
+
+## Elements
+
+### HTML Elements
- render(app, querySelector('#root'));
+```dart
+// Divs and spans
+div(className: 'container', children: [...])
+span(className: 'highlight', children: [...])
+
+// Headings
+h1(children: [text('Title')])
+h2(children: [text('Subtitle')])
+
+// Paragraphs and text
+p(children: [text('Some text')])
+text('Raw text content')
+
+// Links
+a(href: 'https://example.com', children: [text('Click me')])
+
+// Images
+img(src: '/image.png', alt: 'Description')
+
+// Forms
+form(onSubmit: handleSubmit, children: [...])
+input(type: 'text', value: value, onChange: handleChange)
+button(type: 'submit', children: [text('Submit')])
+```
+
+### Lists
+
+```dart
+ReactElement todoList({required List todos}) {
+ return ul(
+ className: 'todo-list',
+ children: todos.map((todo) =>
+ li(
+ key: todo.id,
+ children: [
+ input(
+ type: 'checkbox',
+ checked: todo.completed,
+ ),
+ text(todo.title),
+ ],
+ )
+ ).toList(),
+ );
}
```
-## Run
+### Conditional Rendering
-```bash
-dart compile js -o app.js lib/main.dart
-# Serve with your preferred static server
+```dart
+ReactElement userStatus({required User? user}) {
+ return div(children: [
+ user != null
+ ? span(children: [text('Welcome, ${user.name}!')])
+ : span(children: [text('Please log in')]),
+ ]);
+}
+```
+
+## Event Handling
+
+```dart
+ReactElement interactiveButton() {
+ void handleClick(MouseEvent e) {
+ print('Button clicked at (${e.clientX}, ${e.clientY})');
+ }
+
+ void handleMouseEnter(MouseEvent e) {
+ print('Mouse entered');
+ }
+
+ return button(
+ onClick: handleClick,
+ onMouseEnter: handleMouseEnter,
+ children: [text('Hover and Click Me')],
+ );
+}
+```
+
+### Form Events
+
+```dart
+ReactElement loginForm() {
+ final email = useState('');
+ final password = useState('');
+
+ void handleSubmit(Event e) {
+ e.preventDefault();
+ print('Login: ${email.value} / ${password.value}');
+ }
+
+ return form(
+ onSubmit: handleSubmit,
+ children: [
+ input(
+ type: 'email',
+ value: email.value,
+ onChange: (e) => email.set(e.target.value),
+ placeholder: 'Email',
+ ),
+ input(
+ type: 'password',
+ value: password.value,
+ onChange: (e) => password.set(e.target.value),
+ placeholder: 'Password',
+ ),
+ button(type: 'submit', children: [text('Log In')]),
+ ],
+ );
+}
+```
+
+## Styling
+
+### Inline Styles
+
+```dart
+div(
+ style: {
+ 'backgroundColor': '#f0f0f0',
+ 'padding': '1rem',
+ 'borderRadius': '8px',
+ },
+ children: [...],
+)
+```
+
+### CSS Classes
+
+```dart
+div(
+ className: 'card card-primary',
+ children: [...],
+)
+```
+
+## Complete Example
+
+```dart
+import 'package:dart_node_react/dart_node_react.dart';
+
+ReactElement todoApp() {
+ final todos = useState>([]);
+ final input = useState('');
+
+ void addTodo() {
+ if (input.value.trim().isEmpty) return;
+
+ todos.setWithUpdater((prev) => [
+ ...prev,
+ Todo(id: DateTime.now().toString(), title: input.value, completed: false),
+ ]);
+ input.set('');
+ }
+
+ void toggleTodo(String id) {
+ todos.setWithUpdater((prev) => prev.map((todo) =>
+ todo.id == id
+ ? Todo(id: todo.id, title: todo.title, completed: !todo.completed)
+ : todo
+ ).toList());
+ }
+
+ return div(
+ className: 'todo-app',
+ children: [
+ h1(children: [text('Todo List')]),
+
+ form(
+ onSubmit: (e) {
+ e.preventDefault();
+ addTodo();
+ },
+ children: [
+ input(
+ value: input.value,
+ onChange: (e) => input.set(e.target.value),
+ placeholder: 'What needs to be done?',
+ ),
+ button(type: 'submit', children: [text('Add')]),
+ ],
+ ),
+
+ ul(
+ children: todos.value.map((todo) =>
+ li(
+ key: todo.id,
+ className: todo.completed ? 'completed' : '',
+ onClick: (_) => toggleTodo(todo.id),
+ children: [text(todo.title)],
+ )
+ ).toList(),
+ ),
+
+ p(children: [
+ text('${todos.value.where((t) => !t.completed).length} items left'),
+ ]),
+ ],
+ );
+}
+
+class Todo {
+ final String id;
+ final String title;
+ final bool completed;
+
+ Todo({required this.id, required this.title, required this.completed});
+}
+
+void main() {
+ final root = ReactDOM.createRoot(document.getElementById('root'));
+ root.render(todoApp());
+}
```
-## Part of dart_node
+## Source Code
-[GitHub](https://github.com/MelbourneDeveloper/dart_node)
+The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_react).
diff --git a/website/src/docs/react/index.md b/packages/dart_node_react/README_zh.md
similarity index 65%
rename from website/src/docs/react/index.md
rename to packages/dart_node_react/README_zh.md
index 320f487..b5ec51a 100644
--- a/website/src/docs/react/index.md
+++ b/packages/dart_node_react/README_zh.md
@@ -1,29 +1,21 @@
----
-layout: layouts/docs.njk
-title: dart_node_react
-description: React bindings for building web applications in Dart with hooks, components, and JSX-like syntax.
-eleventyNavigation:
- key: dart_node_react
- parent: Packages
- order: 3
----
+# dart_node_react
-`dart_node_react` provides type-safe React bindings for building web applications in Dart. If you know React, you'll feel right at home.
+类型安全的 React 绑定,用于在 Dart 中构建 Web 应用程序。如果您熟悉 React,您会感到非常亲切。
-## Installation
+## 安装
```yaml
dependencies:
- dart_node_react: ^0.2.0
+ dart_node_react: ^0.11.0-beta
```
-Also install React via npm:
+通过 npm 安装 React:
```bash
npm install react react-dom
```
-## Quick Start
+## 快速开始
```dart
import 'package:dart_node_react/dart_node_react.dart';
@@ -45,9 +37,9 @@ void main() {
}
```
-## Components
+## 组件
-### Functional Components
+### 函数组件
```dart
ReactElement greeting({required String name}) {
@@ -59,11 +51,11 @@ ReactElement greeting({required String name}) {
);
}
-// Usage
+// 使用方式
greeting(name: 'World');
```
-### Components with Props
+### 带 Props 的组件
```dart
ReactElement userCard({
@@ -88,43 +80,64 @@ ReactElement userCard({
### useState
+返回包含 `.value`、`.set()` 和 `.setWithUpdater()` 的 `StateHook`:
+
```dart
ReactElement counter() {
- final (count, setCount) = useState(0);
+ final count = useState(0);
return div(children: [
- p(children: [text('Count: $count')]),
+ p(children: [text('Count: ${count.value}')]),
button(
- onClick: (_) => setCount((c) => c + 1),
+ onClick: (_) => count.setWithUpdater((c) => c + 1),
children: [text('Increment')],
),
button(
- onClick: (_) => setCount((c) => c - 1),
+ onClick: (_) => count.setWithUpdater((c) => c - 1),
children: [text('Decrement')],
),
]);
}
```
+### useStateLazy
+
+用于昂贵的初始状态计算:
+
+```dart
+final data = useStateLazy(() => expensiveComputation());
+```
+
### useEffect
```dart
ReactElement timer() {
- final (seconds, setSeconds) = useState(0);
+ final seconds = useState(0);
useEffect(() {
final timer = Timer.periodic(Duration(seconds: 1), (_) {
- setSeconds((s) => s + 1);
+ seconds.setWithUpdater((s) => s + 1);
});
- // Cleanup function
+ // 清理函数
return () => timer.cancel();
- }, []); // Empty deps = run once on mount
+ }, []); // 空依赖数组 = 仅在挂载时运行一次
- return p(children: [text('Seconds: $seconds')]);
+ return p(children: [text('Seconds: ${seconds.value}')]);
}
```
+### useLayoutEffect
+
+useEffect 的同步版本,在屏幕更新前运行:
+
+```dart
+useLayoutEffect(() {
+ // DOM 测量
+ return () { /* 清理 */ };
+}, [dependency]);
+```
+
### useRef
```dart
@@ -149,15 +162,17 @@ ReactElement focusInput() {
```dart
ReactElement expensiveList({required List numbers}) {
- // Only recalculate when numbers changes
- final sorted = useMemo(
- () => numbers.toList()..sort(),
- [numbers],
- );
+ final count = useState(0);
- return ul(
- children: sorted.map((n) => li(children: [text('$n')])).toList(),
+ // 仅当 count.value 变化时重新计算
+ final fib = useMemo(
+ () => fibonacci(count.value),
+ [count.value],
);
+
+ return div(children: [
+ p(children: [text('Fibonacci of ${count.value} is $fib')]),
+ ]);
}
```
@@ -165,20 +180,20 @@ ReactElement expensiveList({required List numbers}) {
```dart
ReactElement searchBox({required void Function(String) onSearch}) {
- final (query, setQuery) = useState('');
+ final query = useState('');
- // Memoize the callback
+ // 记忆化回调
final handleSubmit = useCallback(
- () => onSearch(query),
- [query, onSearch],
+ () => onSearch(query.value),
+ [query.value, onSearch],
);
return form(
onSubmit: (_) => handleSubmit(),
children: [
input(
- value: query,
- onChange: (e) => setQuery(e.target.value),
+ value: query.value,
+ onChange: (e) => query.set(e.target.value),
),
button(type: 'submit', children: [text('Search')]),
],
@@ -186,36 +201,47 @@ ReactElement searchBox({required void Function(String) onSearch}) {
}
```
-## Elements
+### useDebugValue
+
+在 React DevTools 中显示自定义标签:
+
+```dart
+useDebugValue(
+ isOnline.value,
+ (isOnline) => isOnline ? 'Online' : 'Not Online',
+);
+```
+
+## 元素
-### HTML Elements
+### HTML 元素
```dart
-// Divs and spans
+// Div 和 span
div(className: 'container', children: [...])
span(className: 'highlight', children: [...])
-// Headings
+// 标题
h1(children: [text('Title')])
h2(children: [text('Subtitle')])
-// Paragraphs and text
+// 段落和文本
p(children: [text('Some text')])
text('Raw text content')
-// Links
+// 链接
a(href: 'https://example.com', children: [text('Click me')])
-// Images
+// 图片
img(src: '/image.png', alt: 'Description')
-// Forms
+// 表单
form(onSubmit: handleSubmit, children: [...])
input(type: 'text', value: value, onChange: handleChange)
button(type: 'submit', children: [text('Submit')])
```
-### Lists
+### 列表
```dart
ReactElement todoList({required List todos}) {
@@ -237,7 +263,7 @@ ReactElement todoList({required List todos}) {
}
```
-### Conditional Rendering
+### 条件渲染
```dart
ReactElement userStatus({required User? user}) {
@@ -249,7 +275,7 @@ ReactElement userStatus({required User? user}) {
}
```
-## Event Handling
+## 事件处理
```dart
ReactElement interactiveButton() {
@@ -269,16 +295,16 @@ ReactElement interactiveButton() {
}
```
-### Form Events
+### 表单事件
```dart
ReactElement loginForm() {
- final (email, setEmail) = useState('');
- final (password, setPassword) = useState('');
+ final email = useState('');
+ final password = useState('');
void handleSubmit(Event e) {
e.preventDefault();
- print('Login: $email / $password');
+ print('Login: ${email.value} / ${password.value}');
}
return form(
@@ -286,14 +312,14 @@ ReactElement loginForm() {
children: [
input(
type: 'email',
- value: email,
- onChange: (e) => setEmail(e.target.value),
+ value: email.value,
+ onChange: (e) => email.set(e.target.value),
placeholder: 'Email',
),
input(
type: 'password',
- value: password,
- onChange: (e) => setPassword(e.target.value),
+ value: password.value,
+ onChange: (e) => password.set(e.target.value),
placeholder: 'Password',
),
button(type: 'submit', children: [text('Log In')]),
@@ -302,9 +328,9 @@ ReactElement loginForm() {
}
```
-## Styling
+## 样式
-### Inline Styles
+### 内联样式
```dart
div(
@@ -317,7 +343,7 @@ div(
)
```
-### CSS Classes
+### CSS 类
```dart
div(
@@ -326,27 +352,27 @@ div(
)
```
-## Complete Example
+## 完整示例
```dart
import 'package:dart_node_react/dart_node_react.dart';
ReactElement todoApp() {
- final (todos, setTodos) = useState>([]);
- final (input, setInput) = useState('');
+ final todos = useState>([]);
+ final input = useState('');
void addTodo() {
- if (input.trim().isEmpty) return;
+ if (input.value.trim().isEmpty) return;
- setTodos((prev) => [
+ todos.setWithUpdater((prev) => [
...prev,
- Todo(id: DateTime.now().toString(), title: input, completed: false),
+ Todo(id: DateTime.now().toString(), title: input.value, completed: false),
]);
- setInput('');
+ input.set('');
}
void toggleTodo(String id) {
- setTodos((prev) => prev.map((todo) =>
+ todos.setWithUpdater((prev) => prev.map((todo) =>
todo.id == id
? Todo(id: todo.id, title: todo.title, completed: !todo.completed)
: todo
@@ -365,8 +391,8 @@ ReactElement todoApp() {
},
children: [
input(
- value: input,
- onChange: (e) => setInput(e.target.value),
+ value: input.value,
+ onChange: (e) => input.set(e.target.value),
placeholder: 'What needs to be done?',
),
button(type: 'submit', children: [text('Add')]),
@@ -374,7 +400,7 @@ ReactElement todoApp() {
),
ul(
- children: todos.map((todo) =>
+ children: todos.value.map((todo) =>
li(
key: todo.id,
className: todo.completed ? 'completed' : '',
@@ -385,7 +411,7 @@ ReactElement todoApp() {
),
p(children: [
- text('${todos.where((t) => !t.completed).length} items left'),
+ text('${todos.value.where((t) => !t.completed).length} items left'),
]),
],
);
@@ -400,11 +426,11 @@ class Todo {
}
void main() {
- final root = ReactDOM.createRoot(document.getElementById('root')!);
+ final root = ReactDOM.createRoot(document.getElementById('root'));
root.render(todoApp());
}
```
-## API Reference
+## 源代码
-See the [full API documentation](/api/dart_node_react/) for all available functions and types.
+源代码可在 [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_react) 上获取。
diff --git a/packages/dart_node_react/lib/src/children.dart b/packages/dart_node_react/lib/src/children.dart
index e4766c9..13a38ef 100644
--- a/packages/dart_node_react/lib/src/children.dart
+++ b/packages/dart_node_react/lib/src/children.dart
@@ -47,7 +47,8 @@ external JSArray _childrenToArray(JSAny? children);
/// });
/// ```
///
-/// See: https://react.dev/reference/react/Children
+/// - [React.Children documentation](https://react.dev/reference/react/Children)
+/// - [Children – React 中文文档](https://zh-hans.react.dev/reference/react/Children)
// This class mirrors React's Children API which is a namespace with static
// methods, so the lint is intentionally ignored.
@@ -65,7 +66,8 @@ abstract final class Children {
/// });
/// ```
///
- /// See: https://react.dev/reference/react/Children#children-map
+ /// - [Children.map documentation](https://react.dev/reference/react/Children#children-map)
+ /// - [Children.map – React 中文文档](https://zh-hans.react.dev/reference/react/Children#children-map)
static List? map(
JSAny? children,
ReactElement Function(ReactElement child, int index) fn,
@@ -95,7 +97,8 @@ abstract final class Children {
/// });
/// ```
///
- /// See: https://react.dev/reference/react/Children#children-foreach
+ /// - [Children.forEach documentation](https://react.dev/reference/react/Children#children-foreach)
+ /// - [Children.forEach – React 中文文档](https://zh-hans.react.dev/reference/react/Children#children-foreach)
static void forEach(
JSAny? children,
void Function(ReactElement child, int index) fn,
@@ -117,7 +120,8 @@ abstract final class Children {
/// final childCount = Children.count(props['children']);
/// ```
///
- /// See: https://react.dev/reference/react/Children#children-count
+ /// - [Children.count documentation](https://react.dev/reference/react/Children#children-count)
+ /// - [Children.count – React 中文文档](https://zh-hans.react.dev/reference/react/Children#children-count)
static int count(JSAny? children) => _childrenCount(children);
/// Verifies that children has only one child and returns it.
@@ -129,7 +133,8 @@ abstract final class Children {
/// final onlyChild = Children.only(props['children']);
/// ```
///
- /// See: https://react.dev/reference/react/Children#children-only
+ /// - [Children.only documentation](https://react.dev/reference/react/Children#children-only)
+ /// - [Children.only – React 中文文档](https://zh-hans.react.dev/reference/react/Children#children-only)
static ReactElement only(JSAny? children) =>
ReactElement.fromJS(_childrenOnly(children));
@@ -144,7 +149,8 @@ abstract final class Children {
/// final reversed = childArray.reversed.toList();
/// ```
///
- /// See: https://react.dev/reference/react/Children#children-toarray
+ /// - [Children.toArray documentation](https://react.dev/reference/react/Children#children-toarray)
+ /// - [Children.toArray – React 中文文档](https://zh-hans.react.dev/reference/react/Children#children-toarray)
static List toArray(JSAny? children) =>
_childrenToArray(children).toDart
.map(
diff --git a/packages/dart_node_react/lib/src/context.dart b/packages/dart_node_react/lib/src/context.dart
index 0b2bdb3..4990b35 100644
--- a/packages/dart_node_react/lib/src/context.dart
+++ b/packages/dart_node_react/lib/src/context.dart
@@ -29,7 +29,8 @@ extension type JsContext._(JSObject _) implements JSObject {
/// Every Context object comes with a Provider React component that allows
/// consuming components to subscribe to context changes.
///
-/// See: https://reactjs.org/docs/context.html
+/// - [createContext documentation](https://react.dev/reference/react/createContext)
+/// - [使用 createContext 创建组件能够提供与读取的上下文(context)](https://zh-hans.react.dev/reference/react/createContext)
final class Context {
Context._(this._jsContext, this._defaultValue);
@@ -97,7 +98,8 @@ final class Context {
/// });
/// ```
///
-/// See: https://reactjs.org/docs/context.html#reactcreatecontext
+/// - [createContext documentation](https://react.dev/reference/react/createContext)
+/// - [使用 createContext 创建组件能够提供与读取的上下文(context)](https://zh-hans.react.dev/reference/react/createContext)
Context createContext(T defaultValue) {
final jsDefault = switch (defaultValue) {
null => null,
@@ -134,7 +136,8 @@ Context createContext(T defaultValue) {
/// });
/// ```
///
-/// See: https://reactjs.org/docs/hooks-reference.html#usecontext
+/// - [useContext documentation](https://react.dev/reference/react/useContext)
+/// - [useContext 是一个 React Hook,可以让你读取和订阅组件中的 context](https://zh-hans.react.dev/reference/react/useContext)
T useContext(Context context) {
final jsValue = _reactUseContext(context.jsContext);
return switch (jsValue) {
diff --git a/packages/dart_node_react/lib/src/hooks.dart b/packages/dart_node_react/lib/src/hooks.dart
index 8b4f206..b83c5db 100644
--- a/packages/dart_node_react/lib/src/hooks.dart
+++ b/packages/dart_node_react/lib/src/hooks.dart
@@ -71,7 +71,8 @@ JSAny? _toJsAny(Object? value) => (value == null) ? null : value.jsify();
/// });
/// ```
///
-/// Learn more: https://reactjs.org/docs/hooks-effect.html
+/// - [useEffect documentation](https://react.dev/reference/react/useEffect)
+/// - [useEffect 是一个 React Hook,它允许你将组件与外部系统同步](https://zh-hans.react.dev/reference/react/useEffect)
void useEffect(Object? Function() sideEffect, [List