@ripast/cli 0.2.2 → 0.4.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 +228 -53
- 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",
|
|
@@ -265,13 +265,13 @@ const unusedCmd = defineCommand({
|
|
|
265
265
|
glob: globArg,
|
|
266
266
|
exports: {
|
|
267
267
|
type: "string",
|
|
268
|
-
description: "Declaration filter: all, exported, or local. Defaults to local."
|
|
268
|
+
description: "Declaration filter: all, exported, or local. Defaults to exported (unused local declarations are already caught by tsc noUnusedLocals)."
|
|
269
269
|
},
|
|
270
270
|
tsconfig: { type: "string" },
|
|
271
271
|
json: jsonArg
|
|
272
272
|
},
|
|
273
273
|
run({ args }) {
|
|
274
|
-
const exportFilter = args.exports == null ? "
|
|
274
|
+
const exportFilter = args.exports == null ? "exported" : resolveExportFilter(args.exports);
|
|
275
275
|
const unused = buildUnusedDeclarations({
|
|
276
276
|
glob: args.glob ? args.glob.split(",") : void 0,
|
|
277
277
|
exports: exportFilter,
|
|
@@ -349,6 +349,7 @@ const deleteCmd = defineCommand({
|
|
|
349
349
|
});
|
|
350
350
|
function emitResult(r, apply, verify = false, json = false, agentProfile = false) {
|
|
351
351
|
const s = summarize(r.changes);
|
|
352
|
+
const warnings = r.warnings ?? [];
|
|
352
353
|
if (json) {
|
|
353
354
|
const blockedByRegression = verify && apply && r.regressions.length > 0;
|
|
354
355
|
const wrote = apply && !blockedByRegression;
|
|
@@ -365,12 +366,14 @@ function emitResult(r, apply, verify = false, json = false, agentProfile = false
|
|
|
365
366
|
before: c.before,
|
|
366
367
|
after: c.after
|
|
367
368
|
})),
|
|
368
|
-
regressions: r.regressions
|
|
369
|
+
regressions: r.regressions,
|
|
370
|
+
warnings
|
|
369
371
|
};
|
|
370
372
|
process.stdout.write(`${JSON.stringify(payload)}\n`);
|
|
371
373
|
if (blockedByRegression) process.exit(1);
|
|
372
374
|
return;
|
|
373
375
|
}
|
|
376
|
+
for (const w of warnings) process.stderr.write(`warning: ${w}\n`);
|
|
374
377
|
if (agentProfile) {
|
|
375
378
|
const blockedByRegression = verify && apply && r.regressions.length > 0;
|
|
376
379
|
process.stdout.write(`${profileHeader()}\n`);
|
|
@@ -681,6 +684,136 @@ const rootOnlyArg = {
|
|
|
681
684
|
default: false,
|
|
682
685
|
description: "Match only template-root elements (direct children of <template>); ignores nested matches."
|
|
683
686
|
};
|
|
687
|
+
const vueTemplateWrapCmd = defineCommand({
|
|
688
|
+
meta: {
|
|
689
|
+
name: "vue-template-wrap",
|
|
690
|
+
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\\\"\")."
|
|
691
|
+
},
|
|
692
|
+
args: {
|
|
693
|
+
selector: {
|
|
694
|
+
type: "positional",
|
|
695
|
+
required: true
|
|
696
|
+
},
|
|
697
|
+
wrapper: {
|
|
698
|
+
type: "positional",
|
|
699
|
+
required: true
|
|
700
|
+
},
|
|
701
|
+
glob: globArg,
|
|
702
|
+
scope: scopeArg,
|
|
703
|
+
rootOnly: rootOnlyArg,
|
|
704
|
+
apply: applyArg,
|
|
705
|
+
profile: profileArg,
|
|
706
|
+
json: jsonArg
|
|
707
|
+
},
|
|
708
|
+
async run({ args }) {
|
|
709
|
+
emitResult(await runVueTemplateWrap(args.selector, args.wrapper, {
|
|
710
|
+
glob: args.glob ? args.glob.split(",") : void 0,
|
|
711
|
+
scope: args.scope,
|
|
712
|
+
rootOnly: args.rootOnly
|
|
713
|
+
}), !!args.apply, false, !!args.json, resolveProfile(args.profile).agentProfile);
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
const vueTemplateUnwrapCmd = defineCommand({
|
|
717
|
+
meta: {
|
|
718
|
+
name: "vue-template-unwrap",
|
|
719
|
+
description: "Remove every matching element in a Vue <template>, hoisting its children up one level. Inverse of vue-template-wrap."
|
|
720
|
+
},
|
|
721
|
+
args: {
|
|
722
|
+
selector: {
|
|
723
|
+
type: "positional",
|
|
724
|
+
required: true
|
|
725
|
+
},
|
|
726
|
+
glob: globArg,
|
|
727
|
+
scope: scopeArg,
|
|
728
|
+
rootOnly: rootOnlyArg,
|
|
729
|
+
apply: applyArg,
|
|
730
|
+
profile: profileArg,
|
|
731
|
+
json: jsonArg
|
|
732
|
+
},
|
|
733
|
+
async run({ args }) {
|
|
734
|
+
emitResult(await runVueTemplateUnwrap(args.selector, {
|
|
735
|
+
glob: args.glob ? args.glob.split(",") : void 0,
|
|
736
|
+
scope: args.scope,
|
|
737
|
+
rootOnly: args.rootOnly
|
|
738
|
+
}), !!args.apply, false, !!args.json, resolveProfile(args.profile).agentProfile);
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
const componentsCmd = defineCommand({
|
|
742
|
+
meta: {
|
|
743
|
+
name: "components",
|
|
744
|
+
description: "Inventory Vue/Nuxt components (manifest-first, glob fallback); flag shadowed entries and duplicate-name groups. Pass a name positional for the focused view."
|
|
745
|
+
},
|
|
746
|
+
args: {
|
|
747
|
+
name: {
|
|
748
|
+
type: "positional",
|
|
749
|
+
required: false,
|
|
750
|
+
description: "Component name. If given, prints file, aliases, usages, and same-name candidates."
|
|
751
|
+
},
|
|
752
|
+
glob: globArg,
|
|
753
|
+
source: {
|
|
754
|
+
type: "string",
|
|
755
|
+
description: "Discovery source: auto, manifest, or filesystem. Default: auto."
|
|
756
|
+
},
|
|
757
|
+
dups: {
|
|
758
|
+
type: "boolean",
|
|
759
|
+
default: false,
|
|
760
|
+
description: "Print only duplicate-name groups."
|
|
761
|
+
},
|
|
762
|
+
profile: profileArg,
|
|
763
|
+
json: jsonArg
|
|
764
|
+
},
|
|
765
|
+
async run({ args }) {
|
|
766
|
+
const { agentProfile } = resolveProfile(args.profile);
|
|
767
|
+
const opts = {
|
|
768
|
+
glob: args.glob ? args.glob.split(",") : void 0,
|
|
769
|
+
source: resolveComponentsSource(args.source)
|
|
770
|
+
};
|
|
771
|
+
if (args.name) {
|
|
772
|
+
const detail = await buildComponentDetail(args.name, opts);
|
|
773
|
+
if (!detail) {
|
|
774
|
+
process.stderr.write(`ripast components: no component named "${args.name}".\n`);
|
|
775
|
+
process.exit(1);
|
|
776
|
+
}
|
|
777
|
+
if (args.json) {
|
|
778
|
+
process.stdout.write(`${JSON.stringify(detail, null, 2)}\n`);
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
process.stdout.write(`${formatDetail(detail)}\n`);
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
const inv = await buildComponentInventory(opts);
|
|
785
|
+
if (args.json) {
|
|
786
|
+
process.stdout.write(`${JSON.stringify(args.dups ? inv.duplicates : inv, null, 2)}\n`);
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if (args.dups) {
|
|
790
|
+
if (!inv.duplicates.length) {
|
|
791
|
+
process.stdout.write("no duplicate-name groups\n");
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
const lines = [];
|
|
795
|
+
for (const dup of inv.duplicates) {
|
|
796
|
+
lines.push(dup.name);
|
|
797
|
+
for (const e of dup.entries) lines.push(` ${e.rel}${e.shadowed ? " (shadowed)" : ""}`);
|
|
798
|
+
}
|
|
799
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
if (agentProfile) {
|
|
803
|
+
process.stdout.write(`${profileHeader()}\n${formatAgentInventory(inv)}\n`);
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
process.stdout.write(`${formatInventory(inv)}\n`);
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
function resolveComponentsSource(raw) {
|
|
810
|
+
if (raw == null) return void 0;
|
|
811
|
+
if (raw !== "auto" && raw !== "manifest" && raw !== "filesystem") {
|
|
812
|
+
process.stderr.write(`ripast components: --source must be "auto", "manifest", or "filesystem".\n`);
|
|
813
|
+
process.exit(2);
|
|
814
|
+
}
|
|
815
|
+
return raw;
|
|
816
|
+
}
|
|
684
817
|
runMain(defineCommand({
|
|
685
818
|
meta: {
|
|
686
819
|
name: "ripast",
|
|
@@ -690,67 +823,109 @@ runMain(defineCommand({
|
|
|
690
823
|
"scan": scanCmd,
|
|
691
824
|
"tree": treeCmd,
|
|
692
825
|
"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({
|
|
826
|
+
"doctor": defineCommand({
|
|
701
827
|
meta: {
|
|
702
|
-
name: "
|
|
703
|
-
description: "
|
|
828
|
+
name: "doctor",
|
|
829
|
+
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
830
|
},
|
|
705
831
|
args: {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
832
|
+
glob: globArg,
|
|
833
|
+
checks: {
|
|
834
|
+
type: "string",
|
|
835
|
+
description: "Comma-separated subset: dangling-reexport, stale-reexport, stale-import, duplicate-export, orphan-file, orphan-test, inconsistent-import-path, circular-dep, phantom-component, shadowed-component, cross-realm-import, stale-nuxt-config-ref. Default: all (adapter checks run when their framework is detected)."
|
|
709
836
|
},
|
|
710
|
-
|
|
711
|
-
type: "
|
|
712
|
-
|
|
837
|
+
entry: {
|
|
838
|
+
type: "string",
|
|
839
|
+
description: "Comma-separated entry files exempt from orphan check (relative to cwd)."
|
|
713
840
|
},
|
|
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
|
|
841
|
+
fix: {
|
|
842
|
+
type: "boolean",
|
|
843
|
+
default: false,
|
|
844
|
+
description: "Compute safe fixes for inconsistent-import-path and dangling-reexport findings."
|
|
738
845
|
},
|
|
739
|
-
glob: globArg,
|
|
740
|
-
scope: scopeArg,
|
|
741
|
-
rootOnly: rootOnlyArg,
|
|
742
846
|
apply: applyArg,
|
|
847
|
+
changed: {
|
|
848
|
+
type: "string",
|
|
849
|
+
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."
|
|
850
|
+
},
|
|
743
851
|
profile: profileArg,
|
|
744
852
|
json: jsonArg
|
|
745
853
|
},
|
|
746
854
|
async run({ args }) {
|
|
747
|
-
|
|
855
|
+
const { agentProfile } = resolveProfile(args.profile);
|
|
856
|
+
const checks = args.checks ? args.checks.split(",") : void 0;
|
|
857
|
+
const entry = args.entry ? args.entry.split(",") : void 0;
|
|
858
|
+
let changedFiles;
|
|
859
|
+
if (args.changed != null) {
|
|
860
|
+
const ref = typeof args.changed === "string" && args.changed !== "" && args.changed !== "true" ? args.changed : void 0;
|
|
861
|
+
changedFiles = getChangedFiles({
|
|
862
|
+
cwd: process.cwd(),
|
|
863
|
+
ref
|
|
864
|
+
});
|
|
865
|
+
if (!changedFiles.length) {
|
|
866
|
+
process.stderr.write(`ripast doctor: --changed${ref ? ` ${ref}` : ""} matched no files; nothing to report.\n`);
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
const report = await runDoctor({
|
|
748
871
|
glob: args.glob ? args.glob.split(",") : void 0,
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
872
|
+
checks,
|
|
873
|
+
entry,
|
|
874
|
+
changedFiles
|
|
875
|
+
});
|
|
876
|
+
if (args.fix) {
|
|
877
|
+
const fix = buildDoctorFixes(report, process.cwd());
|
|
878
|
+
if (args.json) {
|
|
879
|
+
process.stdout.write(`${JSON.stringify({
|
|
880
|
+
findings: report.findings,
|
|
881
|
+
filesScanned: report.filesScanned,
|
|
882
|
+
fix: {
|
|
883
|
+
applied: !!args.apply,
|
|
884
|
+
files: fix.changes.length,
|
|
885
|
+
fixed: fix.fixed,
|
|
886
|
+
skipped: fix.skipped.length
|
|
887
|
+
},
|
|
888
|
+
changes: args.apply ? void 0 : fix.changes.map((c) => ({
|
|
889
|
+
path: c.rel,
|
|
890
|
+
before: c.before,
|
|
891
|
+
after: c.after
|
|
892
|
+
}))
|
|
893
|
+
})}\n`);
|
|
894
|
+
if (args.apply && fix.changes.length) writeChanges(fix.changes);
|
|
895
|
+
if (fix.skipped.length) process.exit(1);
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
const s = summarize(fix.changes);
|
|
899
|
+
process.stdout.write(`doctor --fix: ${fix.fixed.length} fixable / ${fix.skipped.length} non-fixable findings\n`);
|
|
900
|
+
process.stdout.write(`${fix.changes.length} file${fix.changes.length === 1 ? "" : "s"}, +${s.linesAdded} -${s.linesRemoved} lines\n\n`);
|
|
901
|
+
if (!args.apply) {
|
|
902
|
+
printDiffs(fix.changes);
|
|
903
|
+
process.stdout.write(`\n(dry run, pass --apply to write)\n`);
|
|
904
|
+
} else {
|
|
905
|
+
writeChanges(fix.changes);
|
|
906
|
+
for (const c of fix.changes) process.stdout.write(`wrote ${c.rel}\n`);
|
|
907
|
+
}
|
|
908
|
+
if (fix.skipped.length) process.exit(1);
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
if (agentProfile && !args.json) {
|
|
912
|
+
process.stdout.write(`${profileHeader()}\n${formatAgentDoctorReport(report)}\n`);
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
process.stdout.write(formatDoctorReport(report, !!args.json));
|
|
916
|
+
if (report.findings.length) process.exit(1);
|
|
752
917
|
}
|
|
753
|
-
})
|
|
918
|
+
}),
|
|
919
|
+
"rename": renameCmd,
|
|
920
|
+
"replace": replaceCmd,
|
|
921
|
+
"rename-file": renameFileCmd,
|
|
922
|
+
"move": moveCmd,
|
|
923
|
+
"delete": deleteCmd,
|
|
924
|
+
"components": componentsCmd,
|
|
925
|
+
"css-class-rename": cssClassRenameCmd,
|
|
926
|
+
"css-class-scan": cssClassScanCmd,
|
|
927
|
+
"vue-template-wrap": vueTemplateWrapCmd,
|
|
928
|
+
"vue-template-unwrap": vueTemplateUnwrapCmd
|
|
754
929
|
}
|
|
755
930
|
}));
|
|
756
931
|
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.4.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.4.0"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"build": "obuild",
|