@ripast/cli 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +222 -50
- package/package.json +2 -2
package/dist/cli.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, renameSync } from "node:fs";
|
|
2
2
|
import { dirname, relative, resolve } from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
|
-
import { buildDeclarationTree, buildScanGraph, buildUnusedDeclarations, formatAgentDeclarationTree, formatAgentFileScanHits, formatAgentHits, formatAgentScanHits, formatDeclarationTree, formatFileScanHits, formatHits, formatRegressions, formatScanGraph, formatScanHits, formatUnusedDeclarations, printDiffs, resolveVerifyMode, runCssClassFileScan, runCssClassRename, runCssClassScan, runDelete, runMove, runRename, runRenameFile, runReplace, runVueTemplateUnwrap, runVueTemplateWrap, scan, summarize, writeChanges } from "@ripast/core";
|
|
4
|
+
import { buildComponentDetail, buildComponentInventory, buildDeclarationTree, buildDoctorFixes, buildScanGraph, buildUnusedDeclarations, formatAgentDeclarationTree, formatAgentDoctorReport, formatAgentFileScanHits, formatAgentHits, formatAgentInventory, formatAgentScanHits, formatDeclarationTree, formatDetail, formatDoctorReport, formatFileScanHits, formatHits, formatInventory, formatRegressions, formatScanGraph, formatScanHits, formatUnusedDeclarations, getChangedFiles, printDiffs, resolveVerifyMode, runCssClassFileScan, runCssClassRename, runCssClassScan, runDelete, runDoctor, runMove, runRename, runRenameFile, runReplace, runVueTemplateUnwrap, runVueTemplateWrap, scan, summarize, writeChanges } from "@ripast/core";
|
|
5
5
|
import { defineCommand, runMain } from "citty";
|
|
6
6
|
import { agent, isAgent } from "std-env";
|
|
7
7
|
const globArg = {
|
|
8
8
|
type: "string",
|
|
9
|
-
description: "File glob(s), comma-separated. Defaults to *.ts,*.tsx,*.vue,... Respects .gitignore."
|
|
9
|
+
description: "File glob(s), comma-separated. Prefix with ! to exclude (e.g. \"*.ts,!.nuxt/**,!**/*.d.ts\"). Defaults to *.ts,*.tsx,*.vue,... Respects .gitignore."
|
|
10
10
|
};
|
|
11
11
|
const applyArg = {
|
|
12
12
|
type: "boolean",
|
|
@@ -681,6 +681,136 @@ const rootOnlyArg = {
|
|
|
681
681
|
default: false,
|
|
682
682
|
description: "Match only template-root elements (direct children of <template>); ignores nested matches."
|
|
683
683
|
};
|
|
684
|
+
const vueTemplateWrapCmd = defineCommand({
|
|
685
|
+
meta: {
|
|
686
|
+
name: "vue-template-wrap",
|
|
687
|
+
description: "Wrap every matching element in a Vue <template> with a parent component. Selector: \"Tag\" or \"Tag[attr]\" / \"Tag[attr=value]\". Wrapper: tag name with optional inline attributes (e.g. \"ProPageStates name=\\\"lh\\\"\")."
|
|
688
|
+
},
|
|
689
|
+
args: {
|
|
690
|
+
selector: {
|
|
691
|
+
type: "positional",
|
|
692
|
+
required: true
|
|
693
|
+
},
|
|
694
|
+
wrapper: {
|
|
695
|
+
type: "positional",
|
|
696
|
+
required: true
|
|
697
|
+
},
|
|
698
|
+
glob: globArg,
|
|
699
|
+
scope: scopeArg,
|
|
700
|
+
rootOnly: rootOnlyArg,
|
|
701
|
+
apply: applyArg,
|
|
702
|
+
profile: profileArg,
|
|
703
|
+
json: jsonArg
|
|
704
|
+
},
|
|
705
|
+
async run({ args }) {
|
|
706
|
+
emitResult(await runVueTemplateWrap(args.selector, args.wrapper, {
|
|
707
|
+
glob: args.glob ? args.glob.split(",") : void 0,
|
|
708
|
+
scope: args.scope,
|
|
709
|
+
rootOnly: args.rootOnly
|
|
710
|
+
}), !!args.apply, false, !!args.json, resolveProfile(args.profile).agentProfile);
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
const vueTemplateUnwrapCmd = defineCommand({
|
|
714
|
+
meta: {
|
|
715
|
+
name: "vue-template-unwrap",
|
|
716
|
+
description: "Remove every matching element in a Vue <template>, hoisting its children up one level. Inverse of vue-template-wrap."
|
|
717
|
+
},
|
|
718
|
+
args: {
|
|
719
|
+
selector: {
|
|
720
|
+
type: "positional",
|
|
721
|
+
required: true
|
|
722
|
+
},
|
|
723
|
+
glob: globArg,
|
|
724
|
+
scope: scopeArg,
|
|
725
|
+
rootOnly: rootOnlyArg,
|
|
726
|
+
apply: applyArg,
|
|
727
|
+
profile: profileArg,
|
|
728
|
+
json: jsonArg
|
|
729
|
+
},
|
|
730
|
+
async run({ args }) {
|
|
731
|
+
emitResult(await runVueTemplateUnwrap(args.selector, {
|
|
732
|
+
glob: args.glob ? args.glob.split(",") : void 0,
|
|
733
|
+
scope: args.scope,
|
|
734
|
+
rootOnly: args.rootOnly
|
|
735
|
+
}), !!args.apply, false, !!args.json, resolveProfile(args.profile).agentProfile);
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
const componentsCmd = defineCommand({
|
|
739
|
+
meta: {
|
|
740
|
+
name: "components",
|
|
741
|
+
description: "Inventory Vue/Nuxt components (manifest-first, glob fallback); flag shadowed entries and duplicate-name groups. Pass a name positional for the focused view."
|
|
742
|
+
},
|
|
743
|
+
args: {
|
|
744
|
+
name: {
|
|
745
|
+
type: "positional",
|
|
746
|
+
required: false,
|
|
747
|
+
description: "Component name. If given, prints file, aliases, usages, and same-name candidates."
|
|
748
|
+
},
|
|
749
|
+
glob: globArg,
|
|
750
|
+
source: {
|
|
751
|
+
type: "string",
|
|
752
|
+
description: "Discovery source: auto, manifest, or filesystem. Default: auto."
|
|
753
|
+
},
|
|
754
|
+
dups: {
|
|
755
|
+
type: "boolean",
|
|
756
|
+
default: false,
|
|
757
|
+
description: "Print only duplicate-name groups."
|
|
758
|
+
},
|
|
759
|
+
profile: profileArg,
|
|
760
|
+
json: jsonArg
|
|
761
|
+
},
|
|
762
|
+
async run({ args }) {
|
|
763
|
+
const { agentProfile } = resolveProfile(args.profile);
|
|
764
|
+
const opts = {
|
|
765
|
+
glob: args.glob ? args.glob.split(",") : void 0,
|
|
766
|
+
source: resolveComponentsSource(args.source)
|
|
767
|
+
};
|
|
768
|
+
if (args.name) {
|
|
769
|
+
const detail = await buildComponentDetail(args.name, opts);
|
|
770
|
+
if (!detail) {
|
|
771
|
+
process.stderr.write(`ripast components: no component named "${args.name}".\n`);
|
|
772
|
+
process.exit(1);
|
|
773
|
+
}
|
|
774
|
+
if (args.json) {
|
|
775
|
+
process.stdout.write(`${JSON.stringify(detail, null, 2)}\n`);
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
process.stdout.write(`${formatDetail(detail)}\n`);
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
const inv = await buildComponentInventory(opts);
|
|
782
|
+
if (args.json) {
|
|
783
|
+
process.stdout.write(`${JSON.stringify(args.dups ? inv.duplicates : inv, null, 2)}\n`);
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
if (args.dups) {
|
|
787
|
+
if (!inv.duplicates.length) {
|
|
788
|
+
process.stdout.write("no duplicate-name groups\n");
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
const lines = [];
|
|
792
|
+
for (const dup of inv.duplicates) {
|
|
793
|
+
lines.push(dup.name);
|
|
794
|
+
for (const e of dup.entries) lines.push(` ${e.rel}${e.shadowed ? " (shadowed)" : ""}`);
|
|
795
|
+
}
|
|
796
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
if (agentProfile) {
|
|
800
|
+
process.stdout.write(`${profileHeader()}\n${formatAgentInventory(inv)}\n`);
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
process.stdout.write(`${formatInventory(inv)}\n`);
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
function resolveComponentsSource(raw) {
|
|
807
|
+
if (raw == null) return void 0;
|
|
808
|
+
if (raw !== "auto" && raw !== "manifest" && raw !== "filesystem") {
|
|
809
|
+
process.stderr.write(`ripast components: --source must be "auto", "manifest", or "filesystem".\n`);
|
|
810
|
+
process.exit(2);
|
|
811
|
+
}
|
|
812
|
+
return raw;
|
|
813
|
+
}
|
|
684
814
|
runMain(defineCommand({
|
|
685
815
|
meta: {
|
|
686
816
|
name: "ripast",
|
|
@@ -690,67 +820,109 @@ runMain(defineCommand({
|
|
|
690
820
|
"scan": scanCmd,
|
|
691
821
|
"tree": treeCmd,
|
|
692
822
|
"unused": unusedCmd,
|
|
693
|
-
"
|
|
694
|
-
"replace": replaceCmd,
|
|
695
|
-
"rename-file": renameFileCmd,
|
|
696
|
-
"move": moveCmd,
|
|
697
|
-
"delete": deleteCmd,
|
|
698
|
-
"css-class-rename": cssClassRenameCmd,
|
|
699
|
-
"css-class-scan": cssClassScanCmd,
|
|
700
|
-
"vue-template-wrap": defineCommand({
|
|
823
|
+
"doctor": defineCommand({
|
|
701
824
|
meta: {
|
|
702
|
-
name: "
|
|
703
|
-
description: "
|
|
825
|
+
name: "doctor",
|
|
826
|
+
description: "Health checks over the AST graph. Suppress per-file with `// ripast-doctor-ignore-file[: c1,c2]` or per-line with `// ripast-doctor-ignore-next-line[: c1,c2]`."
|
|
704
827
|
},
|
|
705
828
|
args: {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
829
|
+
glob: globArg,
|
|
830
|
+
checks: {
|
|
831
|
+
type: "string",
|
|
832
|
+
description: "Comma-separated subset: dangling-reexport, stale-reexport, stale-import, duplicate-export, orphan-file, orphan-test, inconsistent-import-path, circular-dep. Default: all."
|
|
709
833
|
},
|
|
710
|
-
|
|
711
|
-
type: "
|
|
712
|
-
|
|
834
|
+
entry: {
|
|
835
|
+
type: "string",
|
|
836
|
+
description: "Comma-separated entry files exempt from orphan check (relative to cwd)."
|
|
713
837
|
},
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
profile: profileArg,
|
|
719
|
-
json: jsonArg
|
|
720
|
-
},
|
|
721
|
-
async run({ args }) {
|
|
722
|
-
emitResult(await runVueTemplateWrap(args.selector, args.wrapper, {
|
|
723
|
-
glob: args.glob ? args.glob.split(",") : void 0,
|
|
724
|
-
scope: args.scope,
|
|
725
|
-
rootOnly: args.rootOnly
|
|
726
|
-
}), !!args.apply, false, !!args.json, resolveProfile(args.profile).agentProfile);
|
|
727
|
-
}
|
|
728
|
-
}),
|
|
729
|
-
"vue-template-unwrap": defineCommand({
|
|
730
|
-
meta: {
|
|
731
|
-
name: "vue-template-unwrap",
|
|
732
|
-
description: "Remove every matching element in a Vue <template>, hoisting its children up one level. Inverse of vue-template-wrap."
|
|
733
|
-
},
|
|
734
|
-
args: {
|
|
735
|
-
selector: {
|
|
736
|
-
type: "positional",
|
|
737
|
-
required: true
|
|
838
|
+
fix: {
|
|
839
|
+
type: "boolean",
|
|
840
|
+
default: false,
|
|
841
|
+
description: "Compute safe fixes for inconsistent-import-path and dangling-reexport findings."
|
|
738
842
|
},
|
|
739
|
-
glob: globArg,
|
|
740
|
-
scope: scopeArg,
|
|
741
|
-
rootOnly: rootOnlyArg,
|
|
742
843
|
apply: applyArg,
|
|
844
|
+
changed: {
|
|
845
|
+
type: "string",
|
|
846
|
+
description: "Report only findings on files changed vs git. No value = dirty working tree (staged+unstaged+untracked). With value (e.g. main, HEAD~1) = diff against that ref. Full project is still scanned so cross-file checks stay accurate."
|
|
847
|
+
},
|
|
743
848
|
profile: profileArg,
|
|
744
849
|
json: jsonArg
|
|
745
850
|
},
|
|
746
851
|
async run({ args }) {
|
|
747
|
-
|
|
852
|
+
const { agentProfile } = resolveProfile(args.profile);
|
|
853
|
+
const checks = args.checks ? args.checks.split(",") : void 0;
|
|
854
|
+
const entry = args.entry ? args.entry.split(",") : void 0;
|
|
855
|
+
let changedFiles;
|
|
856
|
+
if (args.changed != null) {
|
|
857
|
+
const ref = typeof args.changed === "string" && args.changed !== "" && args.changed !== "true" ? args.changed : void 0;
|
|
858
|
+
changedFiles = getChangedFiles({
|
|
859
|
+
cwd: process.cwd(),
|
|
860
|
+
ref
|
|
861
|
+
});
|
|
862
|
+
if (!changedFiles.length) {
|
|
863
|
+
process.stderr.write(`ripast doctor: --changed${ref ? ` ${ref}` : ""} matched no files; nothing to report.\n`);
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
const report = await runDoctor({
|
|
748
868
|
glob: args.glob ? args.glob.split(",") : void 0,
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
869
|
+
checks,
|
|
870
|
+
entry,
|
|
871
|
+
changedFiles
|
|
872
|
+
});
|
|
873
|
+
if (args.fix) {
|
|
874
|
+
const fix = buildDoctorFixes(report, process.cwd());
|
|
875
|
+
if (args.json) {
|
|
876
|
+
process.stdout.write(`${JSON.stringify({
|
|
877
|
+
findings: report.findings,
|
|
878
|
+
filesScanned: report.filesScanned,
|
|
879
|
+
fix: {
|
|
880
|
+
applied: !!args.apply,
|
|
881
|
+
files: fix.changes.length,
|
|
882
|
+
fixed: fix.fixed,
|
|
883
|
+
skipped: fix.skipped.length
|
|
884
|
+
},
|
|
885
|
+
changes: args.apply ? void 0 : fix.changes.map((c) => ({
|
|
886
|
+
path: c.rel,
|
|
887
|
+
before: c.before,
|
|
888
|
+
after: c.after
|
|
889
|
+
}))
|
|
890
|
+
})}\n`);
|
|
891
|
+
if (args.apply && fix.changes.length) writeChanges(fix.changes);
|
|
892
|
+
if (fix.skipped.length) process.exit(1);
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
const s = summarize(fix.changes);
|
|
896
|
+
process.stdout.write(`doctor --fix: ${fix.fixed.length} fixable / ${fix.skipped.length} non-fixable findings\n`);
|
|
897
|
+
process.stdout.write(`${fix.changes.length} file${fix.changes.length === 1 ? "" : "s"}, +${s.linesAdded} -${s.linesRemoved} lines\n\n`);
|
|
898
|
+
if (!args.apply) {
|
|
899
|
+
printDiffs(fix.changes);
|
|
900
|
+
process.stdout.write(`\n(dry run, pass --apply to write)\n`);
|
|
901
|
+
} else {
|
|
902
|
+
writeChanges(fix.changes);
|
|
903
|
+
for (const c of fix.changes) process.stdout.write(`wrote ${c.rel}\n`);
|
|
904
|
+
}
|
|
905
|
+
if (fix.skipped.length) process.exit(1);
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
if (agentProfile && !args.json) {
|
|
909
|
+
process.stdout.write(`${profileHeader()}\n${formatAgentDoctorReport(report)}\n`);
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
process.stdout.write(formatDoctorReport(report, !!args.json));
|
|
913
|
+
if (report.findings.length) process.exit(1);
|
|
752
914
|
}
|
|
753
|
-
})
|
|
915
|
+
}),
|
|
916
|
+
"rename": renameCmd,
|
|
917
|
+
"replace": replaceCmd,
|
|
918
|
+
"rename-file": renameFileCmd,
|
|
919
|
+
"move": moveCmd,
|
|
920
|
+
"delete": deleteCmd,
|
|
921
|
+
"components": componentsCmd,
|
|
922
|
+
"css-class-rename": cssClassRenameCmd,
|
|
923
|
+
"css-class-scan": cssClassScanCmd,
|
|
924
|
+
"vue-template-wrap": vueTemplateWrapCmd,
|
|
925
|
+
"vue-template-unwrap": vueTemplateUnwrapCmd
|
|
754
926
|
}
|
|
755
927
|
}));
|
|
756
928
|
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ripast/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "CLI for ripast. Deterministic AST refactors (rename, move, find-usages, tailwind/CSS class migration) for TypeScript and frontend frameworks. Auto-installs framework adapters as needed.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"citty": "^0.2.2",
|
|
52
52
|
"std-env": "^4.1.0",
|
|
53
|
-
"@ripast/core": "0.
|
|
53
|
+
"@ripast/core": "0.3.0"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"build": "obuild",
|