From b21ccfec92adef367d273e78fd04290d338b48a8 Mon Sep 17 00:00:00 2001 From: MK Date: Thu, 25 Dec 2025 11:55:08 +0800 Subject: [PATCH 1/2] feat(migration): skip rewriting vite imports when vite is in peerDependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a project has vite in peerDependencies (like a Vite plugin), the import rewriter now skips rewriting vite imports to avoid accidentally bundling vite-plus into the library. - Split AST-grep rules into separate VITE, VITEST, and TSDOWN rule sets - Added get_skip_packages_from_root() to check root package.json for peerDependencies - Apply rules conditionally based on which packages are in peerDependencies - Added comprehensive unit tests for peerDependencies scenarios - Added snap test: migration-skip-vite-peer-dependency - Updated CLAUDE.md with build and snap-test instructions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 26 + crates/vite_migration/src/import_rewriter.rs | 594 ++++++++++++++---- .../package.json | 6 + .../snap.txt | 56 ++ .../src/index.ts | 21 + .../steps.json | 10 + 6 files changed, 574 insertions(+), 139 deletions(-) create mode 100644 packages/global/snap-tests/migration-skip-vite-peer-dependency/package.json create mode 100644 packages/global/snap-tests/migration-skip-vite-peer-dependency/snap.txt create mode 100644 packages/global/snap-tests/migration-skip-vite-peer-dependency/src/index.ts create mode 100644 packages/global/snap-tests/migration-skip-vite-peer-dependency/steps.json diff --git a/CLAUDE.md b/CLAUDE.md index 0a409b3935..fd7f325236 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -79,3 +79,29 @@ vite dev # runs dev script from package.json - Run `cargo test` to execute all tests - You never need to run `pnpm install` in the test fixtures dir, vite-plus should able to load and parse the workspace without `pnpm install`. + +## Build + +- Run `pnpm bootstrap-cli` from the project root to build all packages and install the global CLI + - This builds all `@voidzero-dev/*` packages + - Compiles the Rust NAPI bindings + - Installs the CLI globally via npm + +## Snap Tests + +Snap tests are located in `packages/global/snap-tests/`. Each test case is a directory containing: + +- `package.json` - Package configuration for the test +- `steps.json` - Commands to run and environment variables +- `src/` - Source files for the test +- `snap.txt` - Expected output (generated/updated by running the test) + +```bash +# Run all snap tests +pnpm -F @voidzero-dev/global snap-test + +# Run a specific snap test by name filter +pnpm -F @voidzero-dev/global snap-test migration-skip-vite-peer-dependency +``` + +The snap test will automatically generate/update the `snap.txt` file with the command outputs. diff --git a/crates/vite_migration/src/import_rewriter.rs b/crates/vite_migration/src/import_rewriter.rs index 6a07524b28..de9042e47a 100644 --- a/crates/vite_migration/src/import_rewriter.rs +++ b/crates/vite_migration/src/import_rewriter.rs @@ -4,13 +4,82 @@ use vite_error::Error; use crate::{ast_grep, file_walker}; -/// ast-grep rules for rewriting imports and declare module statements to @voidzero-dev/vite-plus +/// ast-grep rules for rewriting vite imports and declare module statements /// /// This rewrites: -/// -/// **Import statements:** /// - `import { ... } from 'vite'` → `import { ... } from '@voidzero-dev/vite-plus'` /// - `import { ... } from 'vite/{name}'` → `import { ... } from '@voidzero-dev/vite-plus/{name}'` +/// - `declare module 'vite' { ... }` → `declare module '@voidzero-dev/vite-plus' { ... }` +/// - `declare module 'vite/{name}' { ... }` → `declare module '@voidzero-dev/vite-plus/{name}' { ... }` +const REWRITE_VITE_RULES: &str = r#"--- +id: rewrite-vite-import +language: TypeScript +rule: + pattern: $STR + kind: string + regex: ^['"]vite['"]$ + inside: + kind: import_statement +transform: + NEW_IMPORT: + replace: + source: $STR + replace: vite + by: "@voidzero-dev/vite-plus" +fix: $NEW_IMPORT +--- +id: rewrite-vite-subpath-import +language: TypeScript +rule: + pattern: $STR + kind: string + regex: ^['"]vite/.+['"]$ + inside: + kind: import_statement +transform: + NEW_IMPORT: + replace: + source: $STR + replace: vite/ + by: "@voidzero-dev/vite-plus/" +fix: $NEW_IMPORT +--- +id: rewrite-declare-module-vite +language: TypeScript +rule: + pattern: $STR + kind: string + regex: ^['\"]vite['\"]$ + inside: + kind: module +transform: + NEW_IMPORT: + replace: + source: $STR + replace: vite + by: "@voidzero-dev/vite-plus" +fix: $NEW_IMPORT +--- +id: rewrite-declare-module-vite-subpath +language: TypeScript +rule: + pattern: $STR + kind: string + regex: ^['\"]vite/.+['\"]$ + inside: + kind: module +transform: + NEW_IMPORT: + replace: + source: $STR + replace: vite/ + by: "@voidzero-dev/vite-plus/" +fix: $NEW_IMPORT +"#; + +/// ast-grep rules for rewriting vitest imports and declare module statements +/// +/// This rewrites: /// - `import { ... } from 'vitest'` → `import { ... } from '@voidzero-dev/vite-plus/test'` /// - `import { ... } from 'vitest/config'` → `import { ... } from '@voidzero-dev/vite-plus'` /// - `import { ... } from 'vitest/{name}'` → `import { ... } from '@voidzero-dev/vite-plus/test/{name}'` @@ -22,11 +91,6 @@ use crate::{ast_grep, file_walker}; /// - `import { ... } from '@vitest/browser-preview/{name}'` → `import { ... } from '@voidzero-dev/vite-plus/test/browser-preview/{name}'` /// - `import { ... } from '@vitest/browser-webdriverio'` → `import { ... } from '@voidzero-dev/vite-plus/test/browser-webdriverio'` /// - `import { ... } from '@vitest/browser-webdriverio/{name}'` → `import { ... } from '@voidzero-dev/vite-plus/test/browser-webdriverio/{name}'` -/// - `import { ... } from 'tsdown'` → `import { ... } from '@voidzero-dev/vite-plus/lib'` -/// -/// **Declare module statements:** -/// - `declare module 'vite' { ... }` → `declare module '@voidzero-dev/vite-plus' { ... }` -/// - `declare module 'vite/{name}' { ... }` → `declare module '@voidzero-dev/vite-plus/{name}' { ... }` /// - `declare module 'vitest' { ... }` → `declare module '@voidzero-dev/vite-plus/test' { ... }` /// - `declare module 'vitest/config' { ... }` → `declare module '@voidzero-dev/vite-plus' { ... }` /// - `declare module 'vitest/{name}' { ... }` → `declare module '@voidzero-dev/vite-plus/test/{name}' { ... }` @@ -38,8 +102,7 @@ use crate::{ast_grep, file_walker}; /// - `declare module '@vitest/browser-preview/{name}' { ... }` → `declare module '@voidzero-dev/vite-plus/test/browser-preview/{name}' { ... }` /// - `declare module '@vitest/browser-webdriverio' { ... }` → `declare module '@voidzero-dev/vite-plus/test/browser-webdriverio' { ... }` /// - `declare module '@vitest/browser-webdriverio/{name}' { ... }` → `declare module '@voidzero-dev/vite-plus/test/browser-webdriverio/{name}' { ... }` -/// - `declare module 'tsdown' { ... }` → `declare module '@voidzero-dev/vite-plus/lib' { ... }` -const REWRITE_IMPORT_RULES: &str = r#"--- +const REWRITE_VITEST_RULES: &str = r#"--- id: rewrite-vitest-config-import language: TypeScript rule: @@ -56,22 +119,6 @@ transform: by: "@voidzero-dev/vite-plus" fix: $NEW_IMPORT --- -id: rewrite-vite-import -language: TypeScript -rule: - pattern: $STR - kind: string - regex: ^['"]vite['"]$ - inside: - kind: import_statement -transform: - NEW_IMPORT: - replace: - source: $STR - replace: vite - by: "@voidzero-dev/vite-plus" -fix: $NEW_IMPORT ---- id: rewrite-vitest-import language: TypeScript rule: @@ -104,22 +151,6 @@ transform: by: "@voidzero-dev/vite-plus/test/" fix: $NEW_IMPORT --- -id: rewrite-vite-subpath-import -language: TypeScript -rule: - pattern: $STR - kind: string - regex: ^['"]vite/.+['"]$ - inside: - kind: import_statement -transform: - NEW_IMPORT: - replace: - source: $STR - replace: vite/ - by: "@voidzero-dev/vite-plus/" -fix: $NEW_IMPORT ---- id: rewrite-vitest-subpath-import language: TypeScript rule: @@ -152,22 +183,6 @@ transform: by: "@voidzero-dev/vite-plus" fix: $NEW_IMPORT --- -id: rewrite-declare-module-vite -language: TypeScript -rule: - pattern: $STR - kind: string - regex: ^['\"]vite['\"]$ - inside: - kind: module -transform: - NEW_IMPORT: - replace: - source: $STR - replace: vite - by: "@voidzero-dev/vite-plus" -fix: $NEW_IMPORT ---- id: rewrite-declare-module-vitest language: TypeScript rule: @@ -200,22 +215,6 @@ transform: by: "@voidzero-dev/vite-plus/test/" fix: $NEW_IMPORT --- -id: rewrite-declare-module-vite-subpath -language: TypeScript -rule: - pattern: $STR - kind: string - regex: ^['\"]vite/.+['\"]$ - inside: - kind: module -transform: - NEW_IMPORT: - replace: - source: $STR - replace: vite/ - by: "@voidzero-dev/vite-plus/" -fix: $NEW_IMPORT ---- id: rewrite-declare-module-vitest-subpath language: TypeScript rule: @@ -231,7 +230,14 @@ transform: replace: vitest/ by: "@voidzero-dev/vite-plus/test/" fix: $NEW_IMPORT ---- +"#; + +/// ast-grep rules for rewriting tsdown imports and declare module statements +/// +/// This rewrites: +/// - `import { ... } from 'tsdown'` → `import { ... } from '@voidzero-dev/vite-plus/lib'` +/// - `declare module 'tsdown' { ... }` → `declare module '@voidzero-dev/vite-plus/lib' { ... }` +const REWRITE_TSDOWN_RULES: &str = r#"--- id: rewrite-tsdown-import language: TypeScript rule: @@ -265,6 +271,51 @@ transform: fix: $NEW_IMPORT "#; +/// Packages to skip rewriting based on peerDependencies +#[derive(Debug, Clone, Default)] +struct SkipPackages { + /// Skip rewriting vite imports (vite is in peerDependencies) + skip_vite: bool, + /// Skip rewriting vitest imports (vitest is in peerDependencies) + skip_vitest: bool, + /// Skip rewriting tsdown imports (tsdown is in peerDependencies) + skip_tsdown: bool, +} + +impl SkipPackages { + /// Check if all packages should be skipped (file can be skipped entirely) + fn all_skipped(&self) -> bool { + self.skip_vite && self.skip_vitest && self.skip_tsdown + } +} + +/// Parse package.json at the root and check which packages are in peerDependencies. +/// Returns default (no skipping) if package.json doesn't exist or can't be parsed. +fn get_skip_packages_from_root(root: &Path) -> SkipPackages { + let package_json_path = root.join("package.json"); + + let content = match std::fs::read_to_string(&package_json_path) { + Ok(c) => c, + Err(_) => return SkipPackages::default(), + }; + + let pkg: serde_json::Value = match serde_json::from_str(&content) { + Ok(p) => p, + Err(_) => return SkipPackages::default(), + }; + + let peer_deps = match pkg.get("peerDependencies") { + Some(serde_json::Value::Object(deps)) => deps, + _ => return SkipPackages::default(), + }; + + SkipPackages { + skip_vite: peer_deps.contains_key("vite"), + skip_vitest: peer_deps.contains_key("vitest"), + skip_tsdown: peer_deps.contains_key("tsdown"), + } +} + /// Result of rewriting imports in a file #[derive(Debug)] struct RewriteResult { @@ -323,8 +374,17 @@ pub fn rewrite_imports_in_directory(root: &Path) -> Result { if rewrite_result.updated { // Write the modified content back @@ -350,41 +410,66 @@ pub fn rewrite_imports_in_directory(root: &Path) -> Result Result { +fn rewrite_import(file_path: &Path, skip_packages: &SkipPackages) -> Result { // Read the file let content = std::fs::read_to_string(file_path)?; // Rewrite the imports - rewrite_import_content(&content) + rewrite_import_content(&content, skip_packages) } /// Rewrite imports in content from vite/vitest to @voidzero-dev/vite-plus /// /// This is the internal function that performs the actual rewrite using ast-grep. -fn rewrite_import_content(content: &str) -> Result { - let (new_content, updated) = ast_grep::apply_rules(content, REWRITE_IMPORT_RULES)?; +/// Packages that are in peerDependencies will be skipped. +fn rewrite_import_content( + content: &str, + skip_packages: &SkipPackages, +) -> Result { + let mut new_content = content.to_string(); + let mut updated = false; + + // Apply vite rules if not skipped + if !skip_packages.skip_vite { + let (vite_content, vite_updated) = ast_grep::apply_rules(&new_content, REWRITE_VITE_RULES)?; + if vite_updated { + new_content = vite_content; + updated = true; + } + } + + // Apply vitest rules if not skipped + if !skip_packages.skip_vitest { + let (vitest_content, vitest_updated) = + ast_grep::apply_rules(&new_content, REWRITE_VITEST_RULES)?; + if vitest_updated { + new_content = vitest_content; + updated = true; + } + } + + // Apply tsdown rules if not skipped + if !skip_packages.skip_tsdown { + let (tsdown_content, tsdown_updated) = + ast_grep::apply_rules(&new_content, REWRITE_TSDOWN_RULES)?; + if tsdown_updated { + new_content = tsdown_content; + updated = true; + } + } + Ok(RewriteResult { content: new_content, updated }) } @@ -404,7 +489,7 @@ export default defineConfig({ plugins: [], });"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -424,7 +509,7 @@ export default defineConfig({ plugins: [], });"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -446,7 +531,7 @@ export default defineConfig({ }, });"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -469,7 +554,7 @@ export default defineConfig({ plugins: [react()], });"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -490,7 +575,7 @@ export default defineConfig({ plugins: [], });"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(!result.updated); assert_eq!(result.content, vite_config); } @@ -515,7 +600,7 @@ export default defineConfig({{ .unwrap(); // Run the rewrite - let result = rewrite_import(&vite_config_path).unwrap(); + let result = rewrite_import(&vite_config_path, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( @@ -538,7 +623,7 @@ describe('test', () => { }); });"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -558,7 +643,7 @@ describe('test', () => { describe('test', () => {});"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -574,7 +659,7 @@ describe('test', () => {});"# export default page;"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -590,7 +675,7 @@ export default page;"# export default page;"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -606,7 +691,7 @@ export default page;"# export default playwright;"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -622,7 +707,7 @@ export default playwright;"# export default playwright;"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -638,7 +723,7 @@ export default playwright;"# export default context;"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -654,7 +739,7 @@ export default context;"# export default something;"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -670,7 +755,7 @@ export default something;"# export default preview;"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -686,7 +771,7 @@ export default preview;"# export default something;"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -702,7 +787,7 @@ export default something;"# export default webdriverio;"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -718,7 +803,7 @@ export default webdriverio;"# export default something;"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -734,7 +819,7 @@ export default something;"# export default ModuleRunner;"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -750,7 +835,7 @@ export default ModuleRunner;"# export default ModuleRunner;"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -767,7 +852,7 @@ export default ModuleRunner;"# export default startVitest;"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -781,7 +866,7 @@ export default startVitest;"# export default somePlugin;"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -797,7 +882,7 @@ export default somePlugin;"# export default startVitest;"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -822,7 +907,7 @@ export default defineConfig({ plugins: [react()], });"#; - let result = rewrite_import_content(vite_config).unwrap(); + let result = rewrite_import_content(vite_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -984,7 +1069,7 @@ describe('app', () => { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1004,7 +1089,7 @@ describe('app', () => { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1024,7 +1109,7 @@ describe('app', () => { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1044,7 +1129,7 @@ describe('app', () => { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1064,7 +1149,7 @@ describe('app', () => { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1084,7 +1169,7 @@ describe('app', () => { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1104,7 +1189,7 @@ describe('app', () => { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1124,7 +1209,7 @@ describe('app', () => { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1144,7 +1229,7 @@ describe('app', () => { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1164,7 +1249,7 @@ describe('app', () => { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1184,7 +1269,7 @@ describe('app', () => { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1215,7 +1300,7 @@ declare module 'vitest' { export default defineConfig({});"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1246,7 +1331,7 @@ export default defineConfig({});"# } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(!result.updated); assert_eq!(result.content, content); } @@ -1277,7 +1362,7 @@ declare module '@vitest/browser' { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1315,7 +1400,7 @@ declare module '@voidzero-dev/vite-plus/test/browser' { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1335,7 +1420,7 @@ declare module '@voidzero-dev/vite-plus/test/browser' { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1355,7 +1440,7 @@ declare module '@voidzero-dev/vite-plus/test/browser' { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1375,7 +1460,7 @@ declare module '@voidzero-dev/vite-plus/test/browser' { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1407,7 +1492,7 @@ declare module '@voidzero-dev/vite-plus/test/browser' { } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1439,7 +1524,7 @@ export default defineConfig({ entry: 'src/index.ts', });"#; - let result = rewrite_import_content(tsdown_config).unwrap(); + let result = rewrite_import_content(tsdown_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1459,7 +1544,7 @@ export default defineConfig({ entry: "src/index.ts", });"#; - let result = rewrite_import_content(tsdown_config).unwrap(); + let result = rewrite_import_content(tsdown_config, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1479,7 +1564,7 @@ export default defineConfig({ } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1499,7 +1584,7 @@ export default defineConfig({ } }"#; - let result = rewrite_import_content(content).unwrap(); + let result = rewrite_import_content(content, &SkipPackages::default()).unwrap(); assert!(result.updated); assert_eq!( result.content, @@ -1510,4 +1595,235 @@ export default defineConfig({ }"# ); } + + // ======================== + // PeerDependencies Tests + // ======================== + + #[test] + fn test_skip_vite_when_peer_dependency() { + // When vite is a peerDependency, vite imports should NOT be rewritten + let content = r#"import { defineConfig } from 'vite'; +import { describe } from 'vitest'; + +export default defineConfig({});"#; + + let skip_packages = + SkipPackages { skip_vite: true, skip_vitest: false, skip_tsdown: false }; + + let result = rewrite_import_content(content, &skip_packages).unwrap(); + assert!(result.updated); + // vite import should NOT be rewritten, vitest import SHOULD be rewritten + assert_eq!( + result.content, + r#"import { defineConfig } from 'vite'; +import { describe } from '@voidzero-dev/vite-plus/test'; + +export default defineConfig({});"# + ); + } + + #[test] + fn test_skip_vitest_when_peer_dependency() { + // When vitest is a peerDependency, vitest imports should NOT be rewritten + let content = r#"import { defineConfig } from 'vite'; +import { describe } from 'vitest'; + +export default defineConfig({});"#; + + let skip_packages = + SkipPackages { skip_vite: false, skip_vitest: true, skip_tsdown: false }; + + let result = rewrite_import_content(content, &skip_packages).unwrap(); + assert!(result.updated); + // vite import SHOULD be rewritten, vitest import should NOT be rewritten + assert_eq!( + result.content, + r#"import { defineConfig } from '@voidzero-dev/vite-plus'; +import { describe } from 'vitest'; + +export default defineConfig({});"# + ); + } + + #[test] + fn test_skip_all_when_all_peer_dependencies() { + // When all packages are peerDependencies, nothing should be rewritten + let content = r#"import { defineConfig } from 'vite'; +import { describe } from 'vitest'; +import { build } from 'tsdown'; + +export default defineConfig({});"#; + + let skip_packages = SkipPackages { skip_vite: true, skip_vitest: true, skip_tsdown: true }; + + let result = rewrite_import_content(content, &skip_packages).unwrap(); + assert!(!result.updated); + assert_eq!(result.content, content); + } + + #[test] + fn test_skip_packages_all_skipped() { + let skip_all = SkipPackages { skip_vite: true, skip_vitest: true, skip_tsdown: true }; + assert!(skip_all.all_skipped()); + + let skip_some = SkipPackages { skip_vite: true, skip_vitest: false, skip_tsdown: true }; + assert!(!skip_some.all_skipped()); + + let skip_none = SkipPackages::default(); + assert!(!skip_none.all_skipped()); + } + + #[test] + fn test_get_skip_packages_from_root_with_vite_peer_dep() { + use std::fs; + + let temp = tempdir().unwrap(); + + // Create package.json with vite as peerDependency + let pkg_json = r#"{ + "name": "my-vite-plugin", + "peerDependencies": { + "vite": "^5.0.0" + } +}"#; + fs::write(temp.path().join("package.json"), pkg_json).unwrap(); + + let skip = get_skip_packages_from_root(temp.path()); + assert!(skip.skip_vite); + assert!(!skip.skip_vitest); + assert!(!skip.skip_tsdown); + } + + #[test] + fn test_get_skip_packages_from_root_with_all_peer_deps() { + use std::fs; + + let temp = tempdir().unwrap(); + + let pkg_json = r#"{ + "name": "my-plugin", + "peerDependencies": { + "vite": "^5.0.0", + "vitest": "^1.0.0", + "tsdown": "^1.0.0" + } +}"#; + fs::write(temp.path().join("package.json"), pkg_json).unwrap(); + + let skip = get_skip_packages_from_root(temp.path()); + assert!(skip.skip_vite); + assert!(skip.skip_vitest); + assert!(skip.skip_tsdown); + assert!(skip.all_skipped()); + } + + #[test] + fn test_get_skip_packages_from_root_no_peer_deps() { + use std::fs; + + let temp = tempdir().unwrap(); + + let pkg_json = r#"{ + "name": "my-app", + "dependencies": { + "vite": "^5.0.0" + } +}"#; + fs::write(temp.path().join("package.json"), pkg_json).unwrap(); + + let skip = get_skip_packages_from_root(temp.path()); + assert!(!skip.skip_vite); + assert!(!skip.skip_vitest); + assert!(!skip.skip_tsdown); + } + + #[test] + fn test_get_skip_packages_from_root_no_package_json() { + let temp = tempdir().unwrap(); + + // No package.json created - should return default (no skipping) + let skip = get_skip_packages_from_root(temp.path()); + assert!(!skip.skip_vite); + assert!(!skip.skip_vitest); + assert!(!skip.skip_tsdown); + } + + #[test] + fn test_rewrite_imports_in_directory_with_peer_deps() { + use std::fs; + + let temp = tempdir().unwrap(); + + // Create package.json with vite as peerDependency + let pkg_json = r#"{ + "name": "my-vite-plugin", + "peerDependencies": { + "vite": "^5.0.0" + } +}"#; + fs::write(temp.path().join("package.json"), pkg_json).unwrap(); + + // Create src directory + fs::create_dir(temp.path().join("src")).unwrap(); + + // Create source file with vite and vitest imports + let original_content = r#"import { defineConfig } from 'vite'; +import { describe } from 'vitest'; + +export default defineConfig({});"#; + fs::write(temp.path().join("src/index.ts"), original_content).unwrap(); + + // Run the batch rewrite + let result = rewrite_imports_in_directory(temp.path()).unwrap(); + + // File should be modified (vitest was rewritten) + assert_eq!(result.modified_files.len(), 1); + assert!(result.errors.is_empty()); + + // Verify vite import NOT rewritten, vitest IS rewritten + let content = fs::read_to_string(temp.path().join("src/index.ts")).unwrap(); + assert_eq!( + content, + r#"import { defineConfig } from 'vite'; +import { describe } from '@voidzero-dev/vite-plus/test'; + +export default defineConfig({});"# + ); + } + + #[test] + fn test_rewrite_imports_skips_file_when_all_peer_deps() { + use std::fs; + + let temp = tempdir().unwrap(); + + // Create package.json with all packages as peerDependencies + let pkg_json = r#"{ + "name": "my-plugin", + "peerDependencies": { + "vite": "^5.0.0", + "vitest": "^1.0.0", + "tsdown": "^1.0.0" + } +}"#; + fs::write(temp.path().join("package.json"), pkg_json).unwrap(); + + // Create source file + let original_content = r#"import { defineConfig } from 'vite'; +import { describe } from 'vitest'; +import { build } from 'tsdown';"#; + fs::write(temp.path().join("index.ts"), original_content).unwrap(); + + // Run the batch rewrite + let result = rewrite_imports_in_directory(temp.path()).unwrap(); + + // File should be unchanged (all skipped) + assert!(result.modified_files.is_empty()); + assert_eq!(result.unchanged_files.len(), 1); + + // Verify content unchanged + let content = fs::read_to_string(temp.path().join("index.ts")).unwrap(); + assert_eq!(content, original_content); + } } diff --git a/packages/global/snap-tests/migration-skip-vite-peer-dependency/package.json b/packages/global/snap-tests/migration-skip-vite-peer-dependency/package.json new file mode 100644 index 0000000000..13dc63d527 --- /dev/null +++ b/packages/global/snap-tests/migration-skip-vite-peer-dependency/package.json @@ -0,0 +1,6 @@ +{ + "name": "migration-skip-vite-peer-dependency", + "peerDependencies": { + "vite": "^6.0.0" + } +} diff --git a/packages/global/snap-tests/migration-skip-vite-peer-dependency/snap.txt b/packages/global/snap-tests/migration-skip-vite-peer-dependency/snap.txt new file mode 100644 index 0000000000..0e33b16e3f --- /dev/null +++ b/packages/global/snap-tests/migration-skip-vite-peer-dependency/snap.txt @@ -0,0 +1,56 @@ +> vp migration # migration should skip rewriting vite imports when vite is in peerDependencies +┌ Vite+ Migration +│ +● Using default package manager: pnpm +│ +● pnpm@latest installing... +│ +● pnpm@ installed +│ +◆ ✅ Rewrote imports in 1 file(s) +│ +● src/index.ts +│ +└ ✨ Migration completed! + + +> cat src/index.ts # vite imports should NOT be rewritten, vitest imports SHOULD be rewritten +import { defineConfig, type Plugin } from 'vite'; +import { describe, it, expect } from '@voidzero-dev/vite-plus/test'; + +export function myVitePlugin(): Plugin { + return { + name: 'my-vite-plugin', + configResolved(config) { + console.log(config); + }, + }; +} + +describe('myVitePlugin', () => { + it('should work', () => { + expect(myVitePlugin()).toBeDefined(); + }); +}); + +export default defineConfig({ + plugins: [myVitePlugin()], +}); + +> cat package.json # check package.json +{ + "name": "migration-skip-vite-peer-dependency", + "peerDependencies": { + "vite": "^6.0.0" + }, + "pnpm": { + "overrides": { + "vite": "npm:@voidzero-dev/vite-plus-core@latest", + "vitest": "npm:@voidzero-dev/vite-plus-test@latest" + } + }, + "devDependencies": { + "@voidzero-dev/vite-plus": "latest" + }, + "packageManager": "pnpm@" +} diff --git a/packages/global/snap-tests/migration-skip-vite-peer-dependency/src/index.ts b/packages/global/snap-tests/migration-skip-vite-peer-dependency/src/index.ts new file mode 100644 index 0000000000..aa72e9b40d --- /dev/null +++ b/packages/global/snap-tests/migration-skip-vite-peer-dependency/src/index.ts @@ -0,0 +1,21 @@ +import { defineConfig, type Plugin } from 'vite'; +import { describe, it, expect } from 'vitest'; + +export function myVitePlugin(): Plugin { + return { + name: 'my-vite-plugin', + configResolved(config) { + console.log(config); + }, + }; +} + +describe('myVitePlugin', () => { + it('should work', () => { + expect(myVitePlugin()).toBeDefined(); + }); +}); + +export default defineConfig({ + plugins: [myVitePlugin()], +}); diff --git a/packages/global/snap-tests/migration-skip-vite-peer-dependency/steps.json b/packages/global/snap-tests/migration-skip-vite-peer-dependency/steps.json new file mode 100644 index 0000000000..187b0b5c5a --- /dev/null +++ b/packages/global/snap-tests/migration-skip-vite-peer-dependency/steps.json @@ -0,0 +1,10 @@ +{ + "env": { + "VITE_DISABLE_AUTO_INSTALL": "1" + }, + "commands": [ + "vp migration # migration should skip rewriting vite imports when vite is in peerDependencies", + "cat src/index.ts # vite imports should NOT be rewritten, vitest imports SHOULD be rewritten", + "cat package.json # check package.json" + ] +} From 9a4de130f8dda90937038eec1d14623ddb54a266 Mon Sep 17 00:00:00 2001 From: MK Date: Thu, 25 Dec 2025 14:04:04 +0800 Subject: [PATCH 2/2] feat(migration): support monorepo peerDependencies detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Find the nearest package.json for each source file instead of only checking the root. This allows monorepos to have different packages with different peerDependencies - a vite plugin package can have vite in peerDependencies (imports preserved) while an app package without peerDependencies will have imports rewritten. - Add find_nearest_package_json() to walk up directories - Cache package.json lookups for performance - Add monorepo unit tests and snap-test 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/e2e-test.yml | 8 + crates/vite_migration/src/import_rewriter.rs | 318 +++++++++++++++--- ecosystem-ci/.gitignore | 1 + ecosystem-ci/repo.json | 5 + .../migration-monorepo-pnpm/package.json | 4 +- .../migration-monorepo-pnpm/snap.txt | 4 +- .../package.json | 3 + .../packages/vite-plugin/package.json | 6 + .../packages/vite-plugin/src/index.ts | 21 ++ .../pnpm-workspace.yaml | 2 + .../snap.txt | 53 +++ .../steps.json | 11 + .../migration-monorepo-yarn4/package.json | 4 +- .../migration-monorepo-yarn4/snap.txt | 4 +- .../package.json | 6 + .../migration-skip-vite-dependency/snap.txt | 56 +++ .../src/index.ts | 21 ++ .../migration-skip-vite-dependency/steps.json | 10 + packages/global/src/utils/workspace.ts | 1 + 19 files changed, 491 insertions(+), 47 deletions(-) create mode 100644 packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/package.json create mode 100644 packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/packages/vite-plugin/package.json create mode 100644 packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/packages/vite-plugin/src/index.ts create mode 100644 packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/pnpm-workspace.yaml create mode 100644 packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/snap.txt create mode 100644 packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/steps.json create mode 100644 packages/global/snap-tests/migration-skip-vite-dependency/package.json create mode 100644 packages/global/snap-tests/migration-skip-vite-dependency/snap.txt create mode 100644 packages/global/snap-tests/migration-skip-vite-dependency/src/index.ts create mode 100644 packages/global/snap-tests/migration-skip-vite-dependency/steps.json diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index bab6e6b6bf..8a9565eda7 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -96,6 +96,14 @@ jobs: vite run lint vite run type vite run test -- --coverage + - name: vite-plugin-react + node-version: 22 + command: | + vite run format + vite run lint -- --fix + # TODO(fengmk2): run all builds and tests after tsdown version upgrade + vite run @vitejs/plugin-rsc#build + vite run @vitejs/plugin-rsc#test steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 diff --git a/crates/vite_migration/src/import_rewriter.rs b/crates/vite_migration/src/import_rewriter.rs index de9042e47a..1677bec984 100644 --- a/crates/vite_migration/src/import_rewriter.rs +++ b/crates/vite_migration/src/import_rewriter.rs @@ -1,4 +1,7 @@ -use std::path::{Path, PathBuf}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; use vite_error::Error; @@ -271,14 +274,14 @@ transform: fix: $NEW_IMPORT "#; -/// Packages to skip rewriting based on peerDependencies +/// Packages to skip rewriting based on peerDependencies or dependencies #[derive(Debug, Clone, Default)] struct SkipPackages { - /// Skip rewriting vite imports (vite is in peerDependencies) + /// Skip rewriting vite imports (vite is in peerDependencies or dependencies) skip_vite: bool, - /// Skip rewriting vitest imports (vitest is in peerDependencies) + /// Skip rewriting vitest imports (vitest is in peerDependencies or dependencies) skip_vitest: bool, - /// Skip rewriting tsdown imports (tsdown is in peerDependencies) + /// Skip rewriting tsdown imports (tsdown is in peerDependencies or dependencies) skip_tsdown: bool, } @@ -289,12 +292,33 @@ impl SkipPackages { } } -/// Parse package.json at the root and check which packages are in peerDependencies. -/// Returns default (no skipping) if package.json doesn't exist or can't be parsed. -fn get_skip_packages_from_root(root: &Path) -> SkipPackages { - let package_json_path = root.join("package.json"); +/// Find the nearest package.json by walking up from the file's directory. +/// Stops at the root directory. +fn find_nearest_package_json(file_path: &Path, root: &Path) -> Option { + let mut current = file_path.parent()?; + + loop { + let package_json = current.join("package.json"); + if package_json.exists() { + return Some(package_json); + } + + // Stop if we've reached the root + if current == root { + break; + } - let content = match std::fs::read_to_string(&package_json_path) { + // Move to parent directory + current = current.parent()?; + } + + None +} + +/// Parse package.json and check which packages are in peerDependencies or dependencies. +/// Returns default (no skipping) if package.json doesn't exist or can't be parsed. +fn get_skip_packages_from_package_json(package_json_path: &Path) -> SkipPackages { + let content = match std::fs::read_to_string(package_json_path) { Ok(c) => c, Err(_) => return SkipPackages::default(), }; @@ -304,15 +328,21 @@ fn get_skip_packages_from_root(root: &Path) -> SkipPackages { Err(_) => return SkipPackages::default(), }; - let peer_deps = match pkg.get("peerDependencies") { - Some(serde_json::Value::Object(deps)) => deps, - _ => return SkipPackages::default(), + // Helper to check if a package exists in a dependencies object + let has_package = |deps_key: &str, package_name: &str| -> bool { + pkg.get(deps_key) + .and_then(|v| v.as_object()) + .map(|deps| deps.contains_key(package_name)) + .unwrap_or(false) }; + // Check both peerDependencies and dependencies SkipPackages { - skip_vite: peer_deps.contains_key("vite"), - skip_vitest: peer_deps.contains_key("vitest"), - skip_tsdown: peer_deps.contains_key("tsdown"), + skip_vite: has_package("peerDependencies", "vite") || has_package("dependencies", "vite"), + skip_vitest: has_package("peerDependencies", "vitest") + || has_package("dependencies", "vitest"), + skip_tsdown: has_package("peerDependencies", "tsdown") + || has_package("dependencies", "tsdown"), } } @@ -374,16 +404,27 @@ pub fn rewrite_imports_in_directory(root: &Path) -> Result = HashMap::new(); for file_path in walk_result.files { + // Find the nearest package.json for this file + let skip_packages = + if let Some(package_json_path) = find_nearest_package_json(&file_path, root) { + skip_packages_cache + .entry(package_json_path.clone()) + .or_insert_with(|| get_skip_packages_from_package_json(&package_json_path)) + .clone() + } else { + SkipPackages::default() + }; + + // If all packages are in peerDeps for this file's package, skip it + if skip_packages.all_skipped() { + result.unchanged_files.push(file_path); + continue; + } + match rewrite_import(&file_path, &skip_packages) { Ok(rewrite_result) => { if rewrite_result.updated { @@ -410,12 +451,12 @@ pub fn rewrite_imports_in_directory(root: &Path) -> Result Result", "dependencies": { - "testnpm2": "1.0.0", - "vite": "catalog:" + "testnpm2": "1.0.0" }, "resolutions": { "vue": "3.5.25" }, "devDependencies": { + "vite": "catalog:", "vitest": "catalog:", "@voidzero-dev/vite-plus": "catalog:" }, diff --git a/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/package.json b/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/package.json new file mode 100644 index 0000000000..e9aeb67550 --- /dev/null +++ b/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/package.json @@ -0,0 +1,3 @@ +{ + "name": "migration-monorepo-skip-vite-peer-dependency" +} diff --git a/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/packages/vite-plugin/package.json b/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/packages/vite-plugin/package.json new file mode 100644 index 0000000000..9613d932cf --- /dev/null +++ b/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/packages/vite-plugin/package.json @@ -0,0 +1,6 @@ +{ + "name": "my-vite-plugin", + "peerDependencies": { + "vite": "^6.0.0" + } +} diff --git a/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/packages/vite-plugin/src/index.ts b/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/packages/vite-plugin/src/index.ts new file mode 100644 index 0000000000..aa72e9b40d --- /dev/null +++ b/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/packages/vite-plugin/src/index.ts @@ -0,0 +1,21 @@ +import { defineConfig, type Plugin } from 'vite'; +import { describe, it, expect } from 'vitest'; + +export function myVitePlugin(): Plugin { + return { + name: 'my-vite-plugin', + configResolved(config) { + console.log(config); + }, + }; +} + +describe('myVitePlugin', () => { + it('should work', () => { + expect(myVitePlugin()).toBeDefined(); + }); +}); + +export default defineConfig({ + plugins: [myVitePlugin()], +}); diff --git a/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/pnpm-workspace.yaml b/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/pnpm-workspace.yaml new file mode 100644 index 0000000000..924b55f42e --- /dev/null +++ b/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - packages/* diff --git a/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/snap.txt b/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/snap.txt new file mode 100644 index 0000000000..c463a04291 --- /dev/null +++ b/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/snap.txt @@ -0,0 +1,53 @@ +> vp migration # migration should check each package's peerDependencies +┌ Vite+ Migration +│ +● pnpm@latest installing... +│ +● pnpm@ installed +│ +◆ ✅ Rewrote imports in 1 file(s) +│ +● packages/vite-plugin/src/index.ts +│ +└ ✨ Migration completed! + + +> cat packages/vite-plugin/src/index.ts # vite-plugin has vite in peerDeps: vite NOT rewritten, vitest rewritten +import { defineConfig, type Plugin } from 'vite'; +import { describe, it, expect } from '@voidzero-dev/vite-plus/test'; + +export function myVitePlugin(): Plugin { + return { + name: 'my-vite-plugin', + configResolved(config) { + console.log(config); + }, + }; +} + +describe('myVitePlugin', () => { + it('should work', () => { + expect(myVitePlugin()).toBeDefined(); + }); +}); + +export default defineConfig({ + plugins: [myVitePlugin()], +}); + +> cat package.json # check root package.json (no peerDependencies) +{ + "name": "migration-monorepo-skip-vite-peer-dependency", + "devDependencies": { + "@voidzero-dev/vite-plus": "catalog:" + }, + "packageManager": "pnpm@" +} + +> cat packages/vite-plugin/package.json # has vite in peerDependencies +{ + "name": "my-vite-plugin", + "peerDependencies": { + "vite": "^6.0.0" + } +} diff --git a/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/steps.json b/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/steps.json new file mode 100644 index 0000000000..579a5a0380 --- /dev/null +++ b/packages/global/snap-tests/migration-monorepo-skip-vite-peer-dependency/steps.json @@ -0,0 +1,11 @@ +{ + "env": { + "VITE_DISABLE_AUTO_INSTALL": "1" + }, + "commands": [ + "vp migration # migration should check each package's peerDependencies", + "cat packages/vite-plugin/src/index.ts # vite-plugin has vite in peerDeps: vite NOT rewritten, vitest rewritten", + "cat package.json # check root package.json (no peerDependencies)", + "cat packages/vite-plugin/package.json # has vite in peerDependencies" + ] +} diff --git a/packages/global/snap-tests/migration-monorepo-yarn4/package.json b/packages/global/snap-tests/migration-monorepo-yarn4/package.json index 98494d5553..c62f12cb89 100644 --- a/packages/global/snap-tests/migration-monorepo-yarn4/package.json +++ b/packages/global/snap-tests/migration-monorepo-yarn4/package.json @@ -3,10 +3,10 @@ "version": "1.0.0", "packageManager": "yarn@4.12.0", "dependencies": { - "testnpm2": "1.0.0", - "vite": "catalog:" + "testnpm2": "1.0.0" }, "devDependencies": { + "vite": "catalog:", "vitest": "catalog:", "oxlint": "catalog:", "oxfmt": "catalog:" diff --git a/packages/global/snap-tests/migration-monorepo-yarn4/snap.txt b/packages/global/snap-tests/migration-monorepo-yarn4/snap.txt index b5475a2ceb..0b117d3589 100644 --- a/packages/global/snap-tests/migration-monorepo-yarn4/snap.txt +++ b/packages/global/snap-tests/migration-monorepo-yarn4/snap.txt @@ -36,10 +36,10 @@ cat: .oxlintrc.json: No such file or directory "version": "1.0.0", "packageManager": "yarn@", "dependencies": { - "testnpm2": "1.0.0", - "vite": "catalog:" + "testnpm2": "1.0.0" }, "devDependencies": { + "vite": "catalog:", "vitest": "catalog:", "@voidzero-dev/vite-plus": "catalog:" }, diff --git a/packages/global/snap-tests/migration-skip-vite-dependency/package.json b/packages/global/snap-tests/migration-skip-vite-dependency/package.json new file mode 100644 index 0000000000..cc2ea31195 --- /dev/null +++ b/packages/global/snap-tests/migration-skip-vite-dependency/package.json @@ -0,0 +1,6 @@ +{ + "name": "migration-skip-vite-dependency", + "dependencies": { + "vite": "^6.0.0" + } +} diff --git a/packages/global/snap-tests/migration-skip-vite-dependency/snap.txt b/packages/global/snap-tests/migration-skip-vite-dependency/snap.txt new file mode 100644 index 0000000000..a1e661accd --- /dev/null +++ b/packages/global/snap-tests/migration-skip-vite-dependency/snap.txt @@ -0,0 +1,56 @@ +> vp migration # migration should skip rewriting vite imports when vite is in dependencies +┌ Vite+ Migration +│ +● Using default package manager: pnpm +│ +● pnpm@latest installing... +│ +● pnpm@ installed +│ +◆ ✅ Rewrote imports in 1 file(s) +│ +● src/index.ts +│ +└ ✨ Migration completed! + + +> cat src/index.ts # vite imports should NOT be rewritten, vitest imports SHOULD be rewritten +import { defineConfig, type Plugin } from 'vite'; +import { describe, it, expect } from '@voidzero-dev/vite-plus/test'; + +export function myApp(): Plugin { + return { + name: 'my-app', + configResolved(config) { + console.log(config); + }, + }; +} + +describe('myApp', () => { + it('should work', () => { + expect(myApp()).toBeDefined(); + }); +}); + +export default defineConfig({ + plugins: [myApp()], +}); + +> cat package.json # check package.json +{ + "name": "migration-skip-vite-dependency", + "dependencies": { + "vite": "npm:@voidzero-dev/vite-plus-core@latest" + }, + "pnpm": { + "overrides": { + "vite": "npm:@voidzero-dev/vite-plus-core@latest", + "vitest": "npm:@voidzero-dev/vite-plus-test@latest" + } + }, + "devDependencies": { + "@voidzero-dev/vite-plus": "latest" + }, + "packageManager": "pnpm@" +} diff --git a/packages/global/snap-tests/migration-skip-vite-dependency/src/index.ts b/packages/global/snap-tests/migration-skip-vite-dependency/src/index.ts new file mode 100644 index 0000000000..32e61aea0c --- /dev/null +++ b/packages/global/snap-tests/migration-skip-vite-dependency/src/index.ts @@ -0,0 +1,21 @@ +import { defineConfig, type Plugin } from 'vite'; +import { describe, it, expect } from 'vitest'; + +export function myApp(): Plugin { + return { + name: 'my-app', + configResolved(config) { + console.log(config); + }, + }; +} + +describe('myApp', () => { + it('should work', () => { + expect(myApp()).toBeDefined(); + }); +}); + +export default defineConfig({ + plugins: [myApp()], +}); diff --git a/packages/global/snap-tests/migration-skip-vite-dependency/steps.json b/packages/global/snap-tests/migration-skip-vite-dependency/steps.json new file mode 100644 index 0000000000..f6a840b91b --- /dev/null +++ b/packages/global/snap-tests/migration-skip-vite-dependency/steps.json @@ -0,0 +1,10 @@ +{ + "env": { + "VITE_DISABLE_AUTO_INSTALL": "1" + }, + "commands": [ + "vp migration # migration should skip rewriting vite imports when vite is in dependencies", + "cat src/index.ts # vite imports should NOT be rewritten, vitest imports SHOULD be rewritten", + "cat package.json # check package.json" + ] +} diff --git a/packages/global/src/utils/workspace.ts b/packages/global/src/utils/workspace.ts index aac837eba8..891877c1d3 100644 --- a/packages/global/src/utils/workspace.ts +++ b/packages/global/src/utils/workspace.ts @@ -113,6 +113,7 @@ export function discoverWorkspacePackages( { absolute: false, cwd: rootDir, + ignore: ['**/node_modules/**'], }, ); for (const packageJsonRelativePath of packageJsonRelativePaths) {