@ripast/cli 0.0.0 → 0.1.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/bin/ripast.mjs CHANGED
@@ -45,7 +45,9 @@ function ensureAdapters(needed) {
45
45
  const require = createRequire(import.meta.url)
46
46
  const missing = []
47
47
  for (const name of needed) {
48
- try { require.resolve(`@ripast/${name}`) }
48
+ try {
49
+ require.resolve(`@ripast/${name}`)
50
+ }
49
51
  catch { missing.push(`@ripast/${name}`) }
50
52
  }
51
53
  if (!missing.length)
package/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { mkdirSync, readFileSync, renameSync } from "node:fs";
2
2
  import { dirname, resolve } from "node:path";
3
3
  import process from "node:process";
4
- import { buildDeclarationTree, buildScanGraph, formatAgentDeclarationTree, formatAgentHits, formatAgentScanHits, formatDeclarationTree, formatHits, formatRegressions, formatScanGraph, formatScanHits, printDiffs, resolveVerifyMode, runCssClassRename, runCssClassScan, runMove, runRename, runRenameFile, scan, summarize, writeChanges } from "@ripast/core";
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, 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 = {
@@ -69,7 +69,7 @@ function resolveCliVerifyMode(verify, verifyMode) {
69
69
  }
70
70
  function resolveExportFilter(raw) {
71
71
  if (raw !== "all" && raw !== "exported" && raw !== "local") {
72
- process.stderr.write(`ripast tree: --exports must be "all", "exported", or "local".\n`);
72
+ process.stderr.write(`ripast: --exports must be "all", "exported", or "local".\n`);
73
73
  process.exit(2);
74
74
  }
75
75
  return raw;
@@ -162,6 +162,42 @@ const renameCmd = defineCommand({
162
162
  }), !!args.apply, verifyMode !== "none", !!args.json, resolveProfile(args.profile).agentProfile);
163
163
  }
164
164
  });
165
+ const replaceCmd = defineCommand({
166
+ meta: {
167
+ name: "replace",
168
+ description: "Replace an imported symbol with another project export; updates imports and call sites."
169
+ },
170
+ args: {
171
+ "from": {
172
+ type: "positional",
173
+ required: true
174
+ },
175
+ "to": {
176
+ type: "positional",
177
+ required: true
178
+ },
179
+ "tsconfig": { type: "string" },
180
+ "glob": globArg,
181
+ "target-scope": {
182
+ type: "string",
183
+ description: "Restrict target symbol resolution to a single file when multiple files export the same name."
184
+ },
185
+ "apply": applyArg,
186
+ "verify": verifyArg,
187
+ "verifyMode": verifyModeArg,
188
+ "profile": profileArg,
189
+ "json": jsonArg
190
+ },
191
+ async run({ args }) {
192
+ const verifyMode = resolveCliVerifyMode(args.verify, args.verifyMode);
193
+ emitResult(await runReplace(args.from, args.to, {
194
+ tsconfig: args.tsconfig,
195
+ glob: args.glob ? args.glob.split(",") : void 0,
196
+ verify: verifyMode,
197
+ targetScope: args["target-scope"]
198
+ }), !!args.apply, verifyMode !== "none", !!args.json, resolveProfile(args.profile).agentProfile);
199
+ }
200
+ });
165
201
  const treeCmd = defineCommand({
166
202
  meta: {
167
203
  name: "tree",
@@ -191,6 +227,30 @@ const treeCmd = defineCommand({
191
227
  process.stdout.write(`${formatDeclarationTree(tree, !!args.json)}\n`);
192
228
  }
193
229
  });
230
+ const unusedCmd = defineCommand({
231
+ meta: {
232
+ name: "unused",
233
+ description: "Find unreferenced top-level declarations."
234
+ },
235
+ args: {
236
+ glob: globArg,
237
+ exports: {
238
+ type: "string",
239
+ description: "Declaration filter: all, exported, or local. Defaults to local."
240
+ },
241
+ tsconfig: { type: "string" },
242
+ json: jsonArg
243
+ },
244
+ run({ args }) {
245
+ const exportFilter = args.exports == null ? "local" : resolveExportFilter(args.exports);
246
+ const unused = buildUnusedDeclarations({
247
+ glob: args.glob ? args.glob.split(",") : void 0,
248
+ exports: exportFilter,
249
+ tsconfig: args.tsconfig
250
+ });
251
+ process.stdout.write(`${formatUnusedDeclarations(unused, !!args.json)}\n`);
252
+ }
253
+ });
194
254
  const moveCmd = defineCommand({
195
255
  meta: {
196
256
  name: "move",
@@ -228,6 +288,36 @@ const moveCmd = defineCommand({
228
288
  }), !!args.apply, verifyMode !== "none", !!args.json, resolveProfile(args.profile).agentProfile);
229
289
  }
230
290
  });
291
+ const deleteCmd = defineCommand({
292
+ meta: {
293
+ name: "delete",
294
+ description: "Delete an unused top-level declaration from a file; refuses if references remain."
295
+ },
296
+ args: {
297
+ symbol: {
298
+ type: "positional",
299
+ required: true
300
+ },
301
+ from: {
302
+ type: "string",
303
+ required: true,
304
+ description: "Source file path."
305
+ },
306
+ tsconfig: { type: "string" },
307
+ apply: applyArg,
308
+ verify: verifyArg,
309
+ verifyMode: verifyModeArg,
310
+ profile: profileArg,
311
+ json: jsonArg
312
+ },
313
+ async run({ args }) {
314
+ const verifyMode = resolveCliVerifyMode(args.verify, args.verifyMode);
315
+ emitResult(await runDelete(args.symbol, args.from, {
316
+ tsconfig: args.tsconfig,
317
+ verify: verifyMode
318
+ }), !!args.apply, verifyMode !== "none", !!args.json, resolveProfile(args.profile).agentProfile);
319
+ }
320
+ });
231
321
  function emitResult(r, apply, verify = false, json = false, agentProfile = false) {
232
322
  const s = summarize(r.changes);
233
323
  if (json) {
@@ -345,11 +435,13 @@ const renameFileCmd = defineCommand({
345
435
  before: c.before,
346
436
  after: c.after
347
437
  })),
348
- regressions: r.regressions
438
+ regressions: r.regressions,
439
+ warnings: r.warnings
349
440
  })}\n`);
350
441
  if (blockedByRegression) process.exit(1);
351
442
  return;
352
443
  }
444
+ for (const w of r.warnings) process.stderr.write(`warning: ${w}\n`);
353
445
  if (agentProfile) {
354
446
  process.stdout.write(`${profileHeader()}\n`);
355
447
  process.stdout.write(`rename-file: ${args.old} -> ${args.new}\n`);
@@ -458,6 +550,82 @@ function buildRenameMap(from, to, mapPath) {
458
550
  }
459
551
  return new Map(entries);
460
552
  }
553
+ const cssClassScanCmd = defineCommand({
554
+ meta: {
555
+ name: "css-class-scan",
556
+ description: "Tokenize every class site (strings, Vue class attrs, @apply) and emit sortable token or file frequency lists. Use --sort count-asc for rare tokens or --by file for files introducing the most unique classes."
557
+ },
558
+ args: {
559
+ pattern: {
560
+ type: "string",
561
+ description: "Comma-separated globs matched against the bare token (e.g. \"bg-*,text-*\"). Default: all tokens."
562
+ },
563
+ glob: globArg,
564
+ by: {
565
+ type: "string",
566
+ description: "Group by token or file. Default: token."
567
+ },
568
+ sort: {
569
+ type: "string",
570
+ description: "Sort order. Token: count-desc, count-asc, token. File: unique-desc, unique-asc, count-desc, count-asc, file."
571
+ },
572
+ profile: profileArg,
573
+ json: jsonArg
574
+ },
575
+ run({ args }) {
576
+ const { agentProfile } = resolveProfile(args.profile);
577
+ const by = resolveCssClassScanGroup(args.by);
578
+ const base = {
579
+ glob: args.glob ? args.glob.split(",") : void 0,
580
+ pattern: args.pattern ? args.pattern.split(",") : void 0
581
+ };
582
+ if (by === "file") {
583
+ const hits = runCssClassFileScan({
584
+ ...base,
585
+ sort: resolveCssClassFileScanSort(args.sort)
586
+ });
587
+ if (agentProfile && !args.json) {
588
+ process.stdout.write(`${profileHeader()}\n${formatAgentFileScanHits(hits)}\n`);
589
+ return;
590
+ }
591
+ process.stdout.write(`${formatFileScanHits(hits, !!args.json)}\n`);
592
+ return;
593
+ }
594
+ const hits = runCssClassScan({
595
+ ...base,
596
+ sort: resolveCssClassScanSort(args.sort)
597
+ });
598
+ if (agentProfile && !args.json) {
599
+ process.stdout.write(`${profileHeader()}\n${formatAgentScanHits(hits)}\n`);
600
+ return;
601
+ }
602
+ process.stdout.write(`${formatScanHits(hits, !!args.json)}\n`);
603
+ }
604
+ });
605
+ function resolveCssClassScanGroup(raw) {
606
+ if (raw == null) return "token";
607
+ if (raw !== "token" && raw !== "file") {
608
+ process.stderr.write(`ripast css-class-scan: --by must be "token" or "file".\n`);
609
+ process.exit(2);
610
+ }
611
+ return raw;
612
+ }
613
+ function resolveCssClassScanSort(raw) {
614
+ if (raw == null) return "count-desc";
615
+ if (raw !== "count-desc" && raw !== "count-asc" && raw !== "token") {
616
+ process.stderr.write(`ripast css-class-scan: --sort must be "count-desc", "count-asc", or "token".\n`);
617
+ process.exit(2);
618
+ }
619
+ return raw;
620
+ }
621
+ function resolveCssClassFileScanSort(raw) {
622
+ if (raw == null) return "unique-desc";
623
+ if (raw !== "unique-desc" && raw !== "unique-asc" && raw !== "count-desc" && raw !== "count-asc" && raw !== "file") {
624
+ process.stderr.write(`ripast css-class-scan: --sort with --by file must be "unique-desc", "unique-asc", "count-desc", "count-asc", or "file".\n`);
625
+ process.exit(2);
626
+ }
627
+ return raw;
628
+ }
461
629
  runMain(defineCommand({
462
630
  meta: {
463
631
  name: "ripast",
@@ -466,37 +634,14 @@ runMain(defineCommand({
466
634
  subCommands: {
467
635
  "scan": scanCmd,
468
636
  "tree": treeCmd,
637
+ "unused": unusedCmd,
469
638
  "rename": renameCmd,
639
+ "replace": replaceCmd,
470
640
  "rename-file": renameFileCmd,
471
641
  "move": moveCmd,
642
+ "delete": deleteCmd,
472
643
  "css-class-rename": cssClassRenameCmd,
473
- "css-class-scan": defineCommand({
474
- meta: {
475
- name: "css-class-scan",
476
- description: "Tokenize every class site (strings, Vue class attrs, @apply) and emit a frequency-sorted list. Feeds --map for css-class-rename."
477
- },
478
- args: {
479
- pattern: {
480
- type: "string",
481
- description: "Comma-separated globs matched against the bare token (e.g. \"bg-*,text-*\"). Default: all tokens."
482
- },
483
- glob: globArg,
484
- profile: profileArg,
485
- json: jsonArg
486
- },
487
- run({ args }) {
488
- const { agentProfile } = resolveProfile(args.profile);
489
- const hits = runCssClassScan({
490
- glob: args.glob ? args.glob.split(",") : void 0,
491
- pattern: args.pattern ? args.pattern.split(",") : void 0
492
- });
493
- if (agentProfile && !args.json) {
494
- process.stdout.write(`${profileHeader()}\n${formatAgentScanHits(hits)}\n`);
495
- return;
496
- }
497
- process.stdout.write(`${formatScanHits(hits, !!args.json)}\n`);
498
- }
499
- })
644
+ "css-class-scan": cssClassScanCmd
500
645
  }
501
646
  }));
502
647
  export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ripast/cli",
3
3
  "type": "module",
4
- "version": "0.0.0",
4
+ "version": "0.1.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.0.0"
53
+ "@ripast/core": "0.1.0"
54
54
  },
55
55
  "scripts": {
56
56
  "build": "obuild",