Skip to content

Commit 82824ac

Browse files
committed
feat: add vite migration command
1 parent 77e867b commit 82824ac

53 files changed

Lines changed: 2275 additions & 591 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/vite_migration/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ ast-grep-config = { workspace = true }
1111
ast-grep-core = { workspace = true }
1212
ast-grep-language = { workspace = true }
1313
serde_json = { workspace = true, features = ["preserve_order"] }
14-
tokio = { workspace = true, features = ["fs"] }
1514
vite_error = { workspace = true }
1615

1716
[dev-dependencies]

crates/vite_migration/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
mod package;
22

3-
pub use package::rewrite_package_json_scripts;
3+
pub use package::rewrite_scripts;

crates/vite_migration/src/package.rs

Lines changed: 98 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
use std::path::Path;
2-
31
use ast_grep_config::{GlobalRules, RuleConfig, from_yaml_string};
42
use ast_grep_core::replacer::Replacer;
53
use ast_grep_language::{LanguageExt, SupportLang};
6-
use serde_json::Value;
7-
use tokio::fs;
4+
use serde_json::{Map, Value};
85
use vite_error::Error;
96

107
/// load script rules from yaml file
11-
async fn load_ast_grep_rules(yaml_path: &Path) -> Result<Vec<RuleConfig<SupportLang>>, Error> {
12-
let yaml = fs::read_to_string(yaml_path).await?;
8+
fn load_ast_grep_rules(yaml: &str) -> Result<Vec<RuleConfig<SupportLang>>, Error> {
139
let globals = GlobalRules::default();
1410
let rules: Vec<RuleConfig<SupportLang>> = from_yaml_string::<SupportLang>(&yaml, &globals)?;
1511
Ok(rules)
@@ -59,46 +55,55 @@ fn rewrite_script(script: &str, rules: &[RuleConfig<SupportLang>]) -> String {
5955
current
6056
}
6157

62-
/// rewrite scripts in package.json using rules from rules_yaml_path
63-
pub async fn rewrite_package_json_scripts(
64-
package_json_path: &Path,
65-
rules_yaml_path: &Path,
66-
) -> Result<bool, Error> {
67-
let content = fs::read_to_string(package_json_path).await?;
68-
let mut json: Value = serde_json::from_str(&content)?;
69-
let rules = load_ast_grep_rules(rules_yaml_path).await?;
58+
/// rewrite scripts json content using rules from rules_yaml
59+
pub fn rewrite_scripts(scripts_json: &str, rules_yaml: &str) -> Result<Option<String>, Error> {
60+
let mut scripts: Map<String, Value> = serde_json::from_str(scripts_json)?;
61+
let rules = load_ast_grep_rules(rules_yaml)?;
7062

7163
let mut updated = false;
7264
// get scripts field (object)
73-
if let Some(scripts) = json.get_mut("scripts").and_then(Value::as_object_mut) {
74-
let keys: Vec<String> = scripts.keys().cloned().collect();
75-
for key in keys {
76-
if let Some(Value::String(script)) = scripts.get(&key) {
77-
let new_script = rewrite_script(script, &rules);
78-
if new_script != *script {
65+
// let keys: Vec<String> = scripts.keys().cloned().collect();
66+
for value in scripts.values_mut() {
67+
if value.is_array() {
68+
// lint-staged scripts can be an array of strings
69+
// https://github.com/lint-staged/lint-staged?tab=readme-ov-file#packagejson-example
70+
if let Some(sub_scripts) = value.as_array_mut() {
71+
for sub_script in sub_scripts.iter_mut() {
72+
if sub_script.is_string()
73+
&& let Some(raw_script) = sub_script.as_str()
74+
{
75+
let new_script = rewrite_script(raw_script, &rules);
76+
if new_script != raw_script {
77+
updated = true;
78+
*sub_script = Value::String(new_script);
79+
}
80+
}
81+
}
82+
}
83+
} else if value.is_string() {
84+
if let Some(raw_script) = value.as_str() {
85+
let new_script = rewrite_script(raw_script, &rules);
86+
if new_script != raw_script {
7987
updated = true;
80-
scripts.insert(key.clone(), Value::String(new_script));
88+
*value = Value::String(new_script);
8189
}
8290
}
8391
}
8492
}
8593

8694
if updated {
87-
// write back to file
88-
let new_content = serde_json::to_string_pretty(&json)?;
89-
fs::write(package_json_path, new_content).await?;
95+
let new_content = serde_json::to_string_pretty(&scripts)?;
96+
Ok(Some(new_content))
97+
} else {
98+
Ok(None)
9099
}
91-
92-
Ok(updated)
93100
}
94101

95102
#[cfg(test)]
96103
mod tests {
97104
use super::*;
98105

99-
#[test]
100-
fn test_rewrite_script() {
101-
let yaml = r#"
106+
const RULES_YAML: &str = r#"
102107
# vite => vite dev
103108
---
104109
id: replace-vite-alone
@@ -148,10 +153,13 @@ language: bash
148153
rule:
149154
pattern: oxlint $$$ARGS
150155
fix: vite lint $$$ARGS
151-
"#;
156+
"#;
157+
158+
#[test]
159+
fn test_rewrite_script() {
152160
let globals = GlobalRules::default();
153161
let rules: Vec<RuleConfig<SupportLang>> =
154-
from_yaml_string::<SupportLang>(&yaml, &globals).unwrap();
162+
from_yaml_string::<SupportLang>(&RULES_YAML, &globals).unwrap();
155163
// vite commands
156164
assert_eq!(rewrite_script("vite", &rules), "vite dev");
157165
assert_eq!(rewrite_script("vite dev", &rules), "vite dev");
@@ -230,4 +238,63 @@ fix: vite lint $$$ARGS
230238
"npm run type-check && vite lint --type-aware"
231239
);
232240
}
241+
242+
#[test]
243+
fn test_rewrite_package_json_scripts_success() {
244+
let package_json_scripts = r#"
245+
{
246+
"dev": "vite"
247+
}
248+
"#;
249+
let updated = rewrite_scripts(package_json_scripts, &RULES_YAML)
250+
.expect("failed to rewrite package.json scripts");
251+
assert!(updated.is_some());
252+
assert_eq!(
253+
updated.unwrap(),
254+
r#"
255+
{
256+
"dev": "vite dev"
257+
}
258+
"#
259+
.trim()
260+
);
261+
}
262+
263+
#[test]
264+
fn test_rewrite_package_json_scripts_lint_staged() {
265+
let package_json_scripts = r#"
266+
{
267+
"*.js": ["oxlint --fix --type-aware", "oxfmt --fix"],
268+
"*.ts": "oxfmt --fix"
269+
}
270+
"#;
271+
let updated = rewrite_scripts(package_json_scripts, &RULES_YAML)
272+
.expect("failed to rewrite package.json scripts");
273+
assert!(updated.is_some());
274+
assert_eq!(
275+
updated.unwrap(),
276+
r#"
277+
{
278+
"*.js": [
279+
"vite lint --fix --type-aware",
280+
"oxfmt --fix"
281+
],
282+
"*.ts": "oxfmt --fix"
283+
}
284+
"#
285+
.trim()
286+
);
287+
}
288+
289+
#[test]
290+
fn test_rewrite_package_json_scripts_no_update() {
291+
let package_json_scripts = r#"
292+
{
293+
"foo": "bar"
294+
}
295+
"#;
296+
let updated = rewrite_scripts(package_json_scripts, &RULES_YAML)
297+
.expect("failed to rewrite package.json scripts");
298+
assert!(updated.is_none());
299+
}
233300
}

packages/cli/binding/index.d.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,28 +143,28 @@ export interface PathAccess {
143143
}
144144

145145
/**
146-
* Rewrite package.json scripts using rules from rules_yaml_path
146+
* Rewrite scripts json content using rules from rules_yaml
147147
*
148148
* # Arguments
149149
*
150-
* * `package_json_path` - The path to the package.json file
151-
* * `rules_yaml_path` - The path to the ast-grep rules.yaml file
150+
* * `scripts_json` - The scripts section of the package.json file as a JSON string
151+
* * `rules_yaml` - The ast-grep rules.yaml as a YAML string
152152
*
153153
* # Returns
154154
*
155-
* * `updated` - Whether the package.json scripts were updated
155+
* * `updated` - The updated scripts section of the package.json file as a JSON string, or `null` if no updates were made
156156
*
157157
* # Example
158158
*
159159
* ```javascript
160-
* const updated = await rewritePackageJsonScripts("package.json", "rules.yaml");
160+
* const updated = rewriteScripts("scripts section json content here", "ast-grep rules yaml content here");
161161
* console.log(`Updated: ${updated}`);
162162
* ```
163163
*/
164-
export declare function rewritePackageJsonScripts(
165-
packageJsonPath: string,
166-
rulesYamlPath: string,
167-
): Promise<boolean>;
164+
export declare function rewriteScripts(
165+
scriptsJson: string,
166+
rulesYaml: string,
167+
): string | null;
168168

169169
/**
170170
* Main entry point for the CLI, called from JavaScript.

packages/cli/binding/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -783,12 +783,12 @@ if (!nativeBinding) {
783783
const {
784784
detectWorkspace,
785785
downloadPackageManager,
786-
rewritePackageJsonScripts,
786+
rewriteScripts,
787787
run,
788788
runCommand,
789789
} = nativeBinding;
790790
export { detectWorkspace };
791791
export { downloadPackageManager };
792-
export { rewritePackageJsonScripts };
792+
export { rewriteScripts };
793793
export { run };
794794
export { runCommand };

packages/cli/binding/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use vite_task::ResolveCommandResult;
3131

3232
use crate::cli::{Args, CliOptions as ViteTaskCliOptions, Commands};
3333
pub use crate::{
34-
migration::rewrite_package_json_scripts,
34+
migration::rewrite_scripts,
3535
package_manager::{detect_workspace, download_package_manager},
3636
};
3737

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,26 @@
1-
use std::path::PathBuf;
2-
31
use napi::{anyhow, bindgen_prelude::*};
42
use napi_derive::napi;
53

6-
/// Rewrite package.json scripts using rules from rules_yaml_path
4+
/// Rewrite scripts json content using rules from rules_yaml
75
///
86
/// # Arguments
97
///
10-
/// * `package_json_path` - The path to the package.json file
11-
/// * `rules_yaml_path` - The path to the ast-grep rules.yaml file
8+
/// * `scripts_json` - The scripts section of the package.json file as a JSON string
9+
/// * `rules_yaml` - The ast-grep rules.yaml as a YAML string
1210
///
1311
/// # Returns
1412
///
15-
/// * `updated` - Whether the package.json scripts were updated
13+
/// * `updated` - The updated scripts section of the package.json file as a JSON string, or `null` if no updates were made
1614
///
1715
/// # Example
1816
///
1917
/// ```javascript
20-
/// const updated = await rewritePackageJsonScripts("package.json", "rules.yaml");
18+
/// const updated = rewriteScripts("scripts section json content here", "ast-grep rules yaml content here");
2119
/// console.log(`Updated: ${updated}`);
2220
/// ```
2321
#[napi]
24-
pub async fn rewrite_package_json_scripts(
25-
package_json_path: String,
26-
rules_yaml_path: String,
27-
) -> Result<bool> {
28-
let package_json_path = PathBuf::from(&package_json_path);
29-
let rules_yaml_path = PathBuf::from(&rules_yaml_path);
22+
pub fn rewrite_scripts(scripts_json: String, rules_yaml: String) -> Result<Option<String>> {
3023
let updated =
31-
vite_migration::rewrite_package_json_scripts(&package_json_path, &rules_yaml_path)
32-
.await
33-
.map_err(anyhow::Error::from)?;
24+
vite_migration::rewrite_scripts(&scripts_json, &rules_yaml).map_err(anyhow::Error::from)?;
3425
Ok(updated)
3526
}

packages/cli/binding/src/package_manager.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use napi::{Error, anyhow, bindgen_prelude::*};
22
use napi_derive::napi;
3-
use vite_error::Error::UnsupportedPackageManager;
3+
use vite_error::Error::{UnrecognizedPackageManager, UnsupportedPackageManager};
44
use vite_install::{PackageManagerType, get_package_manager_type_and_version};
55
use vite_path::AbsolutePathBuf;
66
use vite_workspace::{Error::PackageJsonNotFound, WorkspaceFile, find_workspace_root};
@@ -150,12 +150,14 @@ pub async fn detect_workspace(cwd: String) -> Result<DetectWorkspaceResult> {
150150
is_monorepo,
151151
root: Some(workspace_root_path),
152152
}),
153-
Err(UnsupportedPackageManager(_)) => Ok(DetectWorkspaceResult {
154-
package_manager_name: None,
155-
package_manager_version: None,
156-
is_monorepo,
157-
root: Some(workspace_root_path),
158-
}),
153+
Err(UnsupportedPackageManager(_) | UnrecognizedPackageManager) => {
154+
Ok(DetectWorkspaceResult {
155+
package_manager_name: None,
156+
package_manager_version: None,
157+
is_monorepo,
158+
root: Some(workspace_root_path),
159+
})
160+
}
159161
Err(e) => {
160162
return Err(anyhow::Error::from(e).into());
161163
}

packages/global/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,14 @@
3636
"@types/cross-spawn": "catalog:",
3737
"@types/validate-npm-package-name": "catalog:",
3838
"@voidzero-dev/vite-plus-tools": "workspace:",
39+
"detect-indent": "catalog:",
40+
"detect-newline": "catalog:",
3941
"glob": "catalog:",
4042
"minimatch": "catalog:",
4143
"mri": "catalog:",
4244
"picocolors": "catalog:",
43-
"rolldown": "workspace:*"
45+
"rolldown": "workspace:*",
46+
"yaml": "catalog:"
4447
},
4548
"engines": {
4649
"node": "^20.19.0 || >=22.12.0"
File renamed without changes.

0 commit comments

Comments
 (0)