From fc0678bdbfbe8d3a492f8449f4ac6dc649c85dee Mon Sep 17 00:00:00 2001 From: Jiahan Chen Date: Thu, 6 Aug 2026 11:57:21 +0800 Subject: [PATCH 1/6] perf(fmt): share compile cache with workers (#213) --- packages/rstack/bin/rs.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/rstack/bin/rs.js b/packages/rstack/bin/rs.js index a29c927f..2dd9fc9f 100755 --- a/packages/rstack/bin/rs.js +++ b/packages/rstack/bin/rs.js @@ -1,12 +1,17 @@ #!/usr/bin/env node import nodeModule from 'node:module'; -// enable on-disk code caching of all modules loaded by Node.js +// enable on-disk code caching and share its directory with child workers // requires Nodejs >= 22.8.0 -const { enableCompileCache } = nodeModule; +const { enableCompileCache, constants } = nodeModule; if (enableCompileCache) { try { - enableCompileCache(); + const { directory, status } = enableCompileCache(); + // ALREADY_ENABLED returns the active version-specific cache directory. + // Passing it to workers would append another version directory. + if (directory && status === constants.compileCacheStatus.ENABLED) { + process.env.NODE_COMPILE_CACHE = directory; + } } catch { // ignore errors } From a5dbe91f4104ef533ae75f1ae1f941beb211d77a Mon Sep 17 00:00:00 2001 From: Jiahan Chen Date: Thu, 6 Aug 2026 12:55:41 +0800 Subject: [PATCH 2/6] perf(fmt): filter ignored files during traversal (#215) --- packages/rstack/src/fmt/discoverPaths.ts | 20 ++++++++----- packages/rstack/src/fmt/discovery.ts | 2 +- .../rstack/tests/fmt/discoverPaths.test.ts | 29 +++++++++++++------ 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/packages/rstack/src/fmt/discoverPaths.ts b/packages/rstack/src/fmt/discoverPaths.ts index d236bc6b..7ed432ae 100644 --- a/packages/rstack/src/fmt/discoverPaths.ts +++ b/packages/rstack/src/fmt/discoverPaths.ts @@ -18,8 +18,8 @@ interface DiscoverFmtPathsOptions { patterns?: string[]; /** Whether files inside node_modules may be discovered. */ withNodeModules?: boolean; - /** Returns whether a scanned directory can be pruned before traversal. */ - isDirectoryIgnored?: (directoryPath: string) => boolean; + /** Returns whether a scanned path can be excluded during traversal. */ + isIgnored?: (filePath: string, isDirectory: boolean) => boolean; } const isErrnoException = (error: unknown): error is NodeJS.ErrnoException => @@ -209,7 +209,7 @@ const createTraversalOptions = ( gitIgnore: GitIgnoreMatcher, ignoredDirNames: ReadonlySet, isIncluded?: (filePath: string) => boolean, - isDirectoryIgnored?: (directoryPath: string) => boolean, + isIgnored?: (filePath: string, isDirectory: boolean) => boolean, ) => { return { followSymlinks: false, @@ -221,12 +221,16 @@ const createTraversalOptions = ( } if (dirent.isDirectory()) { - return gitIgnore.isIgnored(targetPath, true) || isDirectoryIgnored?.(targetPath) === true; + return gitIgnore.isIgnored(targetPath, true) || isIgnored?.(targetPath, true) === true; + } + + if (isIncluded !== undefined && !isIncluded(targetPath)) { + return true; } return ( + isIgnored?.(targetPath, false) === true || isBinaryPath(targetPath) || - (isIncluded !== undefined && !isIncluded(targetPath)) || gitIgnore.isIgnored(targetPath, false) ); }, @@ -352,7 +356,7 @@ const discoverFmtPaths = async ({ cwd, patterns: inputPatterns, withNodeModules = false, - isDirectoryIgnored, + isIgnored, }: DiscoverFmtPathsOptions): Promise => { const patterns = inputPatterns?.length ? inputPatterns : ['.']; const resolveRelativePath = createRelativePathResolver(cwd); @@ -385,7 +389,7 @@ const discoverFmtPaths = async ({ } await gitIgnore.loadThrough(rootPath); - if (gitIgnore.isIgnored(rootPath, true) || isDirectoryIgnored?.(rootPath) === true) { + if (gitIgnore.isIgnored(rootPath, true) || isIgnored?.(rootPath, true) === true) { return []; } @@ -406,7 +410,7 @@ const discoverFmtPaths = async ({ return ( await readdir( rootPath, - createTraversalOptions(gitIgnore, ignoredDirNames, isIncluded, isDirectoryIgnored), + createTraversalOptions(gitIgnore, ignoredDirNames, isIncluded, isIgnored), ) ).files; }), diff --git a/packages/rstack/src/fmt/discovery.ts b/packages/rstack/src/fmt/discovery.ts index bacd7553..d4e012e8 100644 --- a/packages/rstack/src/fmt/discovery.ts +++ b/packages/rstack/src/fmt/discovery.ts @@ -24,7 +24,7 @@ const discoverFmtFiles = async ({ cwd, patterns, withNodeModules, - isDirectoryIgnored: (directoryPath) => isIgnored(directoryPath, true), + isIgnored, }); if (candidates.length === 0) { return []; diff --git a/packages/rstack/tests/fmt/discoverPaths.test.ts b/packages/rstack/tests/fmt/discoverPaths.test.ts index 02bfbd2d..5ad4aa0b 100644 --- a/packages/rstack/tests/fmt/discoverPaths.test.ts +++ b/packages/rstack/tests/fmt/discoverPaths.test.ts @@ -139,28 +139,39 @@ test('lets explicit files bypass gitignore', async () => { }); }); -test('prunes directories with an external ignore matcher', async () => { +test('applies an external ignore matcher during traversal', async () => { await withTempProject(async (rootPath) => { writeProjectFile(rootPath, 'generated/nested/output.ts'); + const ignoredFilePath = writeProjectFile(rootPath, 'src/ignored.ts'); writeProjectFile(rootPath, 'src/index.ts'); - const checkedDirectories: string[] = []; + const checkedPaths: { path: string; isDirectory: boolean }[] = []; const generatedPath = path.join(rootPath, 'generated'); - const isDirectoryIgnored = (directoryPath: string): boolean => { - checkedDirectories.push(path.relative(rootPath, directoryPath)); - return directoryPath === generatedPath; + const isIgnored = (filePath: string, isDirectory: boolean): boolean => { + checkedPaths.push({ + path: path.relative(rootPath, filePath), + isDirectory, + }); + return isDirectory ? filePath === generatedPath : filePath === ignoredFilePath; }; - const files = await discoverFmtPaths({ cwd: rootPath, isDirectoryIgnored }); + const files = await discoverFmtPaths({ cwd: rootPath, isIgnored }); const ignoredRoot = await discoverFmtPaths({ cwd: rootPath, patterns: ['generated'], - isDirectoryIgnored, + isIgnored, }); expect(relativePaths(rootPath, files)).toEqual([path.join('src', 'index.ts')]); expect(ignoredRoot).toEqual([]); - expect(checkedDirectories).toContain('generated'); - expect(checkedDirectories).not.toContain(path.join('generated', 'nested')); + expect(checkedPaths).toContainEqual({ path: 'generated', isDirectory: true }); + expect(checkedPaths).toContainEqual({ + path: path.join('src', 'ignored.ts'), + isDirectory: false, + }); + expect(checkedPaths).not.toContainEqual({ + path: path.join('generated', 'nested'), + isDirectory: true, + }); }); }); From 190a4e1b7b3dc4d59815dcc80735dc5627269914 Mon Sep 17 00:00:00 2001 From: Jiahan Chen Date: Thu, 6 Aug 2026 13:58:16 +0800 Subject: [PATCH 3/6] feat(config): expose config loader APIs (#216) --- packages/rstack/package.json | 4 ++++ packages/rstack/rslib.config.ts | 1 + packages/rstack/src/config.ts | 4 ++-- packages/rstack/src/configExports.ts | 7 +++++++ .../tests/exports/config-subpath/index.test.ts | 7 +++++++ .../rstack/tests/types/resolution-bundler/index.ts | 12 ++++++++++++ .../rstack/tests/types/resolution-nodenext/index.ts | 12 ++++++++++++ 7 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 packages/rstack/src/configExports.ts create mode 100644 packages/rstack/tests/exports/config-subpath/index.test.ts diff --git a/packages/rstack/package.json b/packages/rstack/package.json index 5638bdc0..87717d6c 100644 --- a/packages/rstack/package.json +++ b/packages/rstack/package.json @@ -18,6 +18,10 @@ "types": "./dist/index.d.ts", "default": "./dist/index.js" }, + "./config": { + "types": "./dist/configExports.d.ts", + "default": "./dist/configExports.js" + }, "./app": { "types": "./dist/app.d.ts", "default": "./dist/app.js" diff --git a/packages/rstack/rslib.config.ts b/packages/rstack/rslib.config.ts index 662a0fba..fdc4b9b5 100644 --- a/packages/rstack/rslib.config.ts +++ b/packages/rstack/rslib.config.ts @@ -8,6 +8,7 @@ export default defineConfig({ source: { entry: { index: './src/index.ts', + configExports: './src/configExports.ts', rsbuildConfig: './src/rsbuildConfig.ts', rslibConfig: './src/rslibConfig.ts', rslintConfig: './src/rslintConfig.ts', diff --git a/packages/rstack/src/config.ts b/packages/rstack/src/config.ts index 8ca7bc54..51a6c958 100644 --- a/packages/rstack/src/config.ts +++ b/packages/rstack/src/config.ts @@ -21,13 +21,13 @@ export type Configs = { staged?: StagedConfig; }; -type LoadedRstackConfig = { +export type LoadedRstackConfig = { configs: Configs; filePath: string | null; dependencies: string[]; }; -type LoadRstackConfigOptions = { +export type LoadRstackConfigOptions = { /** * The path to the Rstack config file, can be a relative or absolute path. * If `configFilePath` is not provided, the config path set by the CLI is used. diff --git a/packages/rstack/src/configExports.ts b/packages/rstack/src/configExports.ts new file mode 100644 index 00000000..7da0a774 --- /dev/null +++ b/packages/rstack/src/configExports.ts @@ -0,0 +1,7 @@ +/** Public configuration APIs exposed through `rstack/config`. */ +export { + loadRstackConfig, + type Configs, + type LoadedRstackConfig, + type LoadRstackConfigOptions, +} from './config.ts'; diff --git a/packages/rstack/tests/exports/config-subpath/index.test.ts b/packages/rstack/tests/exports/config-subpath/index.test.ts new file mode 100644 index 00000000..9786f55b --- /dev/null +++ b/packages/rstack/tests/exports/config-subpath/index.test.ts @@ -0,0 +1,7 @@ +import { expect, test } from 'rstack/test'; + +test('should expose only the config loader API from `rstack/config`', async () => { + const config = await import('rstack/config'); + + expect(Object.keys(config)).toEqual(['loadRstackConfig']); +}); diff --git a/packages/rstack/tests/types/resolution-bundler/index.ts b/packages/rstack/tests/types/resolution-bundler/index.ts index 25aedfa9..44477318 100644 --- a/packages/rstack/tests/types/resolution-bundler/index.ts +++ b/packages/rstack/tests/types/resolution-bundler/index.ts @@ -4,12 +4,24 @@ import 'rstack/test/importMeta'; import 'rstack/types'; import { define } from 'rstack'; import { createRsbuild, defineConfig as defineAppConfig } from 'rstack/app'; +import { + loadRstackConfig, + type Configs, + type LoadedRstackConfig, + type LoadRstackConfigOptions, +} from 'rstack/config'; import { defineConfig as defineLibConfig } from 'rstack/lib'; import { js, ts } from 'rstack/lint'; import { expect as importedExpect, test as importedTest } from 'rstack/test'; const appConfig = defineAppConfig({}); const libConfig = defineLibConfig({}); +const loadOptions: LoadRstackConfigOptions = { configFilePath: 'rstack.config.ts' }; +const loadedConfig: Promise = loadRstackConfig(loadOptions); +const configs: Configs = {}; + +void loadedConfig; +void configs; createRsbuild({ config: appConfig }); define.app(appConfig); diff --git a/packages/rstack/tests/types/resolution-nodenext/index.ts b/packages/rstack/tests/types/resolution-nodenext/index.ts index 37e7440d..95bf176a 100644 --- a/packages/rstack/tests/types/resolution-nodenext/index.ts +++ b/packages/rstack/tests/types/resolution-nodenext/index.ts @@ -4,12 +4,24 @@ import 'rstack/test/importMeta'; import 'rstack/types'; import { define } from 'rstack'; import { createRsbuild, defineConfig as defineAppConfig } from 'rstack/app'; +import { + loadRstackConfig, + type Configs, + type LoadedRstackConfig, + type LoadRstackConfigOptions, +} from 'rstack/config'; import { defineConfig as defineLibConfig } from 'rstack/lib'; import { js, ts } from 'rstack/lint'; import { expect as importedExpect, test as importedTest } from 'rstack/test'; const appConfig = defineAppConfig({}); const libConfig = defineLibConfig({}); +const loadOptions: LoadRstackConfigOptions = { configFilePath: 'rstack.config.ts' }; +const loadedConfig: Promise = loadRstackConfig(loadOptions); +const configs: Configs = {}; + +void loadedConfig; +void configs; createRsbuild({ config: appConfig }); define.app(appConfig); From 6db19b5379c9ed5681e11cf400fdc0bcc4edc144 Mon Sep 17 00:00:00 2001 From: Jiahan Chen Date: Thu, 6 Aug 2026 13:59:18 +0800 Subject: [PATCH 4/6] perf(fmt): prioritize Markdown worker tasks (#217) --- packages/rstack/src/fmt/runner.ts | 44 +++++++++++++++++-- packages/rstack/src/fmt/workerPool.ts | 2 + .../tests/fmt/runnerWriteFailure.test.ts | 1 + 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/packages/rstack/src/fmt/runner.ts b/packages/rstack/src/fmt/runner.ts index aa0cc5c7..0f9c92cf 100644 --- a/packages/rstack/src/fmt/runner.ts +++ b/packages/rstack/src/fmt/runner.ts @@ -16,6 +16,17 @@ interface FmtWorkerPoolResult { processedFileCount: number; } +/** Benchmarks show stable scheduling gains only when at least eight workers share the queue. */ +const minPriorityWorkers = 8; + +/** + * Markdown parsing is consistently slower in representative repositories. Keep this signal + * narrow: parser overrides and file size can outweigh the extension, and deferring every JS/TS + * file could turn a large source file into the final straggler. + */ +const isMarkdown = (file: FmtFileRequest): boolean => + file.path.endsWith('.md') || file.path.endsWith('.mdx'); + /** Converts a formatter outcome into the shared per-file result. */ const runFmtFile = async ( file: FmtFileRequest, @@ -41,6 +52,30 @@ const runFmtFile = async ( } }; +/** Starts slower Markdown parsers first while preserving order within both priority groups. */ +const runPriorityFmtFiles = async ( + files: FmtFileRequest[], + shouldWrite: boolean, + formatFile: FormatFile, +): Promise => { + const priority: number[] = []; + const rest: number[] = []; + + for (let index = 0; index < files.length; index++) { + (isMarkdown(files[index]) ? priority : rest).push(index); + } + + const order = priority.concat(rest); + const outcomes = await Promise.all( + order.map((index) => runFmtFile(files[index], shouldWrite, formatFile)), + ); + const results = new Array(files.length); + for (let index = 0; index < order.length; index++) { + results[order[index]] = outcomes[index]; + } + return results; +}; + /** Processes files in a worker pool while preserving input order. */ const runFmtFilesInWorkerPool = async ( files: FmtFileRequest[], @@ -51,9 +86,12 @@ const runFmtFilesInWorkerPool = async ( const workerPool = await createFmtWorkerPool(files.length, maxWorkers); try { - const results = await Promise.all( - files.map((file) => runFmtFile(file, shouldWrite, workerPool.formatFile)), - ); + const results = + workerPool.workerCount >= minPriorityWorkers + ? await runPriorityFmtFiles(files, shouldWrite, workerPool.formatFile) + : await Promise.all( + files.map((file) => runFmtFile(file, shouldWrite, workerPool.formatFile)), + ); const processedFiles: FmtFileResult[] = []; let processedFileCount = 0; diff --git a/packages/rstack/src/fmt/workerPool.ts b/packages/rstack/src/fmt/workerPool.ts index 974dd38d..1531a864 100644 --- a/packages/rstack/src/fmt/workerPool.ts +++ b/packages/rstack/src/fmt/workerPool.ts @@ -7,6 +7,7 @@ import type { FmtFileRequest } from './types.ts'; type FmtWorkerMethods = typeof import('./worker.ts'); interface FmtWorkerPool { + readonly workerCount: number; formatFile: ( file: FmtFileRequest, shouldWrite: boolean, @@ -55,6 +56,7 @@ const createFmtWorkerPool = async ( } return { + workerCount, formatFile: (file, shouldWrite) => pool.run({ file, shouldWrite }, { name: 'formatFile' }), terminate: () => pool.destroy(), }; diff --git a/packages/rstack/tests/fmt/runnerWriteFailure.test.ts b/packages/rstack/tests/fmt/runnerWriteFailure.test.ts index a7ea7b45..efb96dfd 100644 --- a/packages/rstack/tests/fmt/runnerWriteFailure.test.ts +++ b/packages/rstack/tests/fmt/runnerWriteFailure.test.ts @@ -8,6 +8,7 @@ const mocks = rs.hoisted(() => ({ rs.mock('../../src/fmt/workerPool.ts', () => ({ createFmtWorkerPool: () => Promise.resolve({ + workerCount: 1, formatFile: () => Promise.reject(new Error('file write failed')), terminate: () => { mocks.terminateCalls++; From 9782ba9c53808152eb3c0684c2603b480bfd4b8b Mon Sep 17 00:00:00 2001 From: Jiahan Chen Date: Thu, 6 Aug 2026 14:10:06 +0800 Subject: [PATCH 5/6] docs: add Rstack CLI migration guide (#218) --- website/docs/en/guide/_meta.json | 5 ++++ website/docs/en/guide/migration.mdx | 39 +++++++++++++++++++++++++++ website/docs/en/guide/quick-start.mdx | 2 ++ website/docs/zh/guide/_meta.json | 5 ++++ website/docs/zh/guide/migration.mdx | 39 +++++++++++++++++++++++++++ website/docs/zh/guide/quick-start.mdx | 2 ++ 6 files changed, 92 insertions(+) create mode 100644 website/docs/en/guide/migration.mdx create mode 100644 website/docs/zh/guide/migration.mdx diff --git a/website/docs/en/guide/_meta.json b/website/docs/en/guide/_meta.json index c637e781..775ed81b 100644 --- a/website/docs/en/guide/_meta.json +++ b/website/docs/en/guide/_meta.json @@ -22,6 +22,11 @@ "type": "section-header", "label": "Practices" }, + { + "type": "file", + "name": "migration", + "label": "Migration" + }, { "type": "file", "name": "testing", diff --git a/website/docs/en/guide/migration.mdx b/website/docs/en/guide/migration.mdx new file mode 100644 index 00000000..5a020498 --- /dev/null +++ b/website/docs/en/guide/migration.mdx @@ -0,0 +1,39 @@ +--- +description: 'Migrate an existing project to Rstack CLI with the recommended migration Skill.' +--- + +# Migrate to Rstack CLI + +To migrate an existing project, we recommend using the `migrate-to-rstack-cli` Skill. It inspects the project and automatically migrates supported tools used in the repository—including Rstack tools, Prettier, and Husky—to Rstack CLI. + +## Use the migration skill + +First, install the Skill: + +```bash +npx skills add rstackjs/rstack-cli --skill migrate-to-rstack-cli +``` + +Then ask your coding agent to perform the migration with this prompt: + +```text +Use the migrate-to-rstack-cli Skill to migrate this project to Rstack CLI. +``` + +## Supported tools + +The Skill can directly migrate the following standalone tools: + +- **Rstack toolchain:** [Rsbuild](https://rsbuild.rs/), [Rslib](https://rslib.rs/), [Rstest](https://rstest.rs/), [Rslint](https://rslint.rs/), and [Rspress](https://rspress.rs/) +- **Code formatting:** [Prettier](https://github.com/prettier/prettier) +- **Staged-file tasks:** [lint-staged](https://github.com/lint-staged/lint-staged) and [nano-staged](https://github.com/usmanyunusov/nano-staged) +- **Git hooks:** [Husky](https://github.com/typicode/husky) and [simple-git-hooks](https://github.com/toplenboren/simple-git-hooks) + +## Unsupported tools + +The Skill does not directly migrate tools outside the list above. If your project uses any of the following tools, migrate it to the corresponding Rstack tool first, then run the `migrate-to-rstack-cli` Skill: + +- **Application builds:** Follow the Rsbuild [webpack migration guide](https://rsbuild.rs/guide/migration/webpack), [Vite migration guide](https://rsbuild.rs/guide/migration/vite), [Create React App migration guide](https://rsbuild.rs/guide/migration/cra), or [Vue CLI migration guide](https://rsbuild.rs/guide/migration/vue-cli) to migrate the project to Rsbuild. +- **Library builds:** Follow the Rslib [tsup migration guide](https://rslib.rs/guide/migration/tsup) or [tsc migration guide](https://rslib.rs/guide/migration/tsc) to migrate the library to Rslib. +- **Testing:** Follow the Rstest [Jest migration guide](https://rstest.rs/guide/migration/jest) or [Vitest migration guide](https://rstest.rs/guide/migration/vitest) to migrate the project to Rstest. +- **Linting:** Follow the [Rslint getting started guide](https://rslint.rs/guide/) to migrate ESLint or other linters to Rslint. diff --git a/website/docs/en/guide/quick-start.mdx b/website/docs/en/guide/quick-start.mdx index 7c907890..bee941fa 100644 --- a/website/docs/en/guide/quick-start.mdx +++ b/website/docs/en/guide/quick-start.mdx @@ -105,3 +105,5 @@ Install the migration skill so the agent can migrate existing projects to Rstack ```bash npx skills add rstackjs/rstack-cli --skill migrate-to-rstack-cli ``` + +For supported tools and migration instructions, see [Migrate to Rstack CLI](./migration). diff --git a/website/docs/zh/guide/_meta.json b/website/docs/zh/guide/_meta.json index 5b1c69d4..1763df7c 100644 --- a/website/docs/zh/guide/_meta.json +++ b/website/docs/zh/guide/_meta.json @@ -22,6 +22,11 @@ "type": "section-header", "label": "实践" }, + { + "type": "file", + "name": "migration", + "label": "迁移" + }, { "type": "file", "name": "testing", diff --git a/website/docs/zh/guide/migration.mdx b/website/docs/zh/guide/migration.mdx new file mode 100644 index 00000000..41f7d94c --- /dev/null +++ b/website/docs/zh/guide/migration.mdx @@ -0,0 +1,39 @@ +--- +description: '使用推荐的迁移 Skill,将现有项目迁移到 Rstack CLI。' +--- + +# 迁移到 Rstack CLI \{#migrate-to-rstack-cli} + +迁移现有项目时,推荐使用 `migrate-to-rstack-cli` Skill。该 Skill 会分析项目,并自动将仓库中使用的 Rstack 工具及 Prettier、Husky 等受支持工具迁移到 Rstack CLI。 + +## 使用迁移 Skill \{#use-the-migration-skill} + +首先安装该 Skill: + +```bash +npx skills add rstackjs/rstack-cli --skill migrate-to-rstack-cli +``` + +安装完成后,向 Coding Agent 发送以下 Prompt: + +```text +使用 migrate-to-rstack-cli Skill 将当前项目迁移到 Rstack CLI。 +``` + +## 支持的工具 \{#supported-tools} + +该 Skill 可以直接迁移以下独立工具: + +- **Rstack 工具链:**[Rsbuild](https://rsbuild.rs/zh/)、[Rslib](https://rslib.rs/zh/)、[Rstest](https://rstest.rs/zh/)、[Rslint](https://rslint.rs/) 和 [Rspress](https://rspress.rs/zh/) +- **代码格式化:**[Prettier](https://github.com/prettier/prettier) +- **暂存文件处理:**[lint-staged](https://github.com/lint-staged/lint-staged) 和 [nano-staged](https://github.com/usmanyunusov/nano-staged) +- **Git hooks:**[Husky](https://github.com/typicode/husky) 和 [simple-git-hooks](https://github.com/toplenboren/simple-git-hooks) + +## 不支持的工具 \{#unsupported-tools} + +该 Skill 不会直接迁移上面列表之外的工具。如果项目使用以下工具,请先将其迁移到对应的 Rstack 工具,再运行 `migrate-to-rstack-cli` Skill: + +- **应用构建:**参考 Rsbuild 的 [webpack 迁移指南](https://rsbuild.rs/zh/guide/migration/webpack)、[Vite 迁移指南](https://rsbuild.rs/zh/guide/migration/vite)、[Create React App 迁移指南](https://rsbuild.rs/zh/guide/migration/cra) 或 [Vue CLI 迁移指南](https://rsbuild.rs/zh/guide/migration/vue-cli),将项目迁移到 Rsbuild。 +- **库构建:**参考 Rslib 的 [tsup 迁移指南](https://rslib.rs/zh/guide/migration/tsup) 或 [tsc 迁移指南](https://rslib.rs/zh/guide/migration/tsc),将库迁移到 Rslib。 +- **测试:**参考 Rstest 的 [Jest 迁移指南](https://rstest.rs/zh/guide/migration/jest) 或 [Vitest 迁移指南](https://rstest.rs/zh/guide/migration/vitest),将项目迁移到 Rstest。 +- **代码检查:**参考 [Rslint 入门指南](https://rslint.rs/guide/),将 ESLint 或其他代码检查工具迁移到 Rslint。 diff --git a/website/docs/zh/guide/quick-start.mdx b/website/docs/zh/guide/quick-start.mdx index 79bb999d..6927d123 100644 --- a/website/docs/zh/guide/quick-start.mdx +++ b/website/docs/zh/guide/quick-start.mdx @@ -105,3 +105,5 @@ npx skills add rstackjs/rstack-cli --skill rstack-cli-best-practices ```bash npx skills add rstackjs/rstack-cli --skill migrate-to-rstack-cli ``` + +支持的工具和迁移说明请参阅[迁移到 Rstack CLI](./migration)。 From 707c4edc1ae3dcce12bd82c1656210ac62abf47c Mon Sep 17 00:00:00 2001 From: Jiahan Chen Date: Thu, 6 Aug 2026 14:29:18 +0800 Subject: [PATCH 6/6] release: v0.3.5 (#219) --- packages/rstack/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rstack/package.json b/packages/rstack/package.json index 87717d6c..f16ec98b 100644 --- a/packages/rstack/package.json +++ b/packages/rstack/package.json @@ -1,6 +1,6 @@ { "name": "rstack", - "version": "0.3.4", + "version": "0.3.5", "description": "One CLI for JavaScript development, powered by Rstack.", "homepage": "https://rstack.rs", "bugs": {