feat: warn about non-import references to moved files
The import graph only rewrites import/export-from/require. Files are also named from markdown links, package.json fields, jest.mock strings and tsconfig file lists, and a move that dangles those must not pass silently. mv now scans text formats plus source strings for the moved paths, module names and old specifier forms (word-boundary matched, lockfiles/hidden dirs skipped) and reports each occurrence once per line on stderr and as non_import_refs[] in --json (dry-run included). References are never edited; exit codes are unchanged. Also moves normalize_scope into Index (its only consumer) and the Change/ChangedFile JSON structs into output where they are built, to stay inside the 250-line-per-file budget.
This commit is contained in:
parent
b1af375763
commit
1ddf79b508
21 changed files with 557 additions and 81 deletions
68
tests/cli_refs.rs
Normal file
68
tests/cli_refs.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
//! `mv` warns (never edits) about non-import references: markdown links,
|
||||
//! `package.json` fields and `jest.mock` strings survive silent unless the
|
||||
//! user sees them. JSON payload carries the same list for agents.
|
||||
|
||||
mod common;
|
||||
|
||||
use common::{copy_fixture, in_root, jmove, read};
|
||||
use predicates::prelude::*;
|
||||
|
||||
fn dry_run_refs() -> String {
|
||||
let tmp = copy_fixture("typescript", "refs");
|
||||
let out = jmove(
|
||||
&tmp,
|
||||
&["mv", "lib/sum.ts", "lib/total.ts", "--dry-run", "--json"],
|
||||
)
|
||||
.success()
|
||||
.stdout(predicate::str::contains("\"non_import_refs\""))
|
||||
.get_output()
|
||||
.stdout
|
||||
.clone();
|
||||
String::from_utf8(out).expect("json is utf-8")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn json_dry_run_lists_hidden_references_and_skips_the_noise() {
|
||||
let out = dry_run_refs();
|
||||
for expected in [
|
||||
"\"file\": \"README.md\"",
|
||||
"\"file\": \"package.json\"",
|
||||
"\"file\": \"__tests__/sum.test.ts\"",
|
||||
] {
|
||||
assert!(out.contains(expected), "missing {expected} in {out}");
|
||||
}
|
||||
for absent in [
|
||||
"\"file\": \"app.ts\"",
|
||||
"\"file\": \"package-lock.json\"",
|
||||
"\"file\": \".notes/refs.md\"",
|
||||
"\"file\": \"lib/summary.ts\"",
|
||||
] {
|
||||
assert!(!out.contains(absent), "leaked {absent} in {out}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn human_dry_run_warns_on_stderr_and_exits_zero() {
|
||||
let tmp = copy_fixture("typescript", "refs");
|
||||
jmove(&tmp, &["mv", "lib/sum.ts", "lib/total.ts", "--dry-run"])
|
||||
.success()
|
||||
.stderr(
|
||||
predicate::str::contains("non-import reference")
|
||||
.and(predicate::str::contains("README.md:1"))
|
||||
.and(predicate::str::contains("__tests__/sum.test.ts:1")),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn real_move_still_succeeds_and_still_warns() {
|
||||
let tmp = copy_fixture("typescript", "refs");
|
||||
jmove(&tmp, &["mv", "lib/sum.ts", "lib/total.ts"])
|
||||
.success()
|
||||
.stderr(predicate::str::contains("may need manual fixing"));
|
||||
assert!(in_root(tmp.path(), "lib/total.ts").exists());
|
||||
let app = read(&in_root(tmp.path(), "app.ts"));
|
||||
assert!(app.contains("from './lib/total'"), "{app}");
|
||||
// The scanner warns; only the import graph is rewritten.
|
||||
let readme = read(&in_root(tmp.path(), "README.md"));
|
||||
assert!(readme.contains("./lib/sum.ts"), "{readme}");
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue