Skip to content

Commit 964892d

Browse files
committed
refactor: separate cli and commands
1 parent f7a9716 commit 964892d

20 files changed

+571
-568
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ jobs:
9999
run: npm ci
100100

101101
- name: Generate shell completions
102-
run: npm run generate:completion
102+
run: npm run generate:shell-completion
103103

104104
- name: Run ShellCheck
105105
uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 #v2.0.0

package-lock.json

Lines changed: 233 additions & 233 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525
"scripts": {
2626
"build": "MINIFY=true vite build",
2727
"check:typescript": "tsc --noEmit",
28-
"check:all": "npm run generate:completion && npm run shellcheck && npm run check:typescript && npm run test && npm run format && npm run lint",
28+
"check:all": "npm run generate:shell-completion && npm run shellcheck && npm run check:typescript && npm run test && npm run format && npm run lint",
2929
"ff-diff": "vite build -l warn && USE_CURRENT_DIR=true node dist/index.js",
3030
"format": "prettier --check .",
3131
"format:fix": "prettier --write .",
32-
"generate:completion": "COMPLETION=true vite build && node dist/shell-completion.js --path ./completions",
32+
"generate:shell-completion": "COMPLETION=true vite build && node dist/shell-completion.js --path ./completions",
3333
"generate:usage": "USAGE=true vite build && node dist/usage.js",
3434
"lint": "oxlint",
3535
"lint:fix": "oxlint --fix --fix-suggestions",
@@ -42,7 +42,7 @@
4242
"@types/node": "24.1.0",
4343
"@types/selenium-webdriver": "4.1.28",
4444
"@vitest/coverage-v8": "3.2.4",
45-
"oxlint": "1.8.0",
45+
"oxlint": "1.9.0",
4646
"prettier": "3.6.2",
4747
"typescript": "5.8.3",
4848
"vite": "7.0.6",

src/commands/cli.ts renamed to src/cli/index.ts

Lines changed: 129 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import { getDefaultPrefs } from "@commands/default-prefs";
33
import { defaultPrefsUserJS } from "@commands/default-prefs-userjs";
44
import { diff } from "@commands/diff";
55
import { unusedPrefs } from "@commands/unused-prefs";
6+
import { getArgumentValue, parseKeepArgument } from "@lib/cli";
7+
import { getInstalledFirefoxPath } from "@lib/firefox";
8+
import { startsWithNumberDotNumber } from "@lib/helpers";
69

710
const CONSOLE_COLORS = {
811
GREEN: "\u001B[32m",
@@ -15,7 +18,7 @@ interface SourceCleanupOptions {
1518
sources: boolean;
1619
}
1720

18-
export interface OutputOptions {
21+
interface OutputOptions {
1922
doNotPrintConsole: boolean;
2023
saveOutput: boolean;
2124
}
@@ -31,7 +34,7 @@ export const HELP_ARGS = {
3134
longOption: "--help",
3235
doc: "Print help",
3336
};
34-
export const HELP_ARGS_VALUES = Object.values(VERSION_ARGS);
37+
export const HELP_ARGS_VALUES = Object.values(HELP_ARGS);
3538

3639
export const CLI_ARGS = {
3740
CLEAN_ARCHIVES: "--clean-archives",
@@ -81,6 +84,16 @@ const pathToFirefox: CliOption = {
8184
longOption: `${CLI_ARGS.FIREFOX_PATH} ${CLI_VALUES.PATH_USAGE}`,
8285
doc: "Path to the firefox binary",
8386
};
87+
88+
export const hasAnyArg = (args: readonly string[]): boolean => {
89+
return process.argv.some((arg) => args.includes(arg));
90+
};
91+
92+
const createPrintOptions = (): OutputOptions => ({
93+
doNotPrintConsole: process.argv.includes(CLI_ARGS.DO_NOT_PRINT_IN_CONSOLE),
94+
saveOutput: process.argv.includes(CLI_ARGS.SAVE_OUTPUT),
95+
});
96+
8497
const createCleanOptions = (): SourceCleanupOptions => ({
8598
archives: process.argv.includes(CLI_ARGS.CLEAN_ARCHIVES),
8699
sources: process.argv.includes(CLI_ARGS.CLEAN_SOURCES),
@@ -91,15 +104,6 @@ const createKeepOptions = (): SourceCleanupOptions => ({
91104
sources: process.argv.includes(CLI_ARGS.KEEP_SOURCES),
92105
});
93106

94-
export const hasAnyArg = (args: readonly string[]): boolean => {
95-
return process.argv.some((arg) => args.includes(arg));
96-
};
97-
98-
export const createPrintOptions = (): OutputOptions => ({
99-
doNotPrintConsole: process.argv.includes(CLI_ARGS.DO_NOT_PRINT_IN_CONSOLE),
100-
saveOutput: process.argv.includes(CLI_ARGS.SAVE_OUTPUT),
101-
});
102-
103107
export const cleanOptions: SourceCleanupOptions = createCleanOptions();
104108
export const keepOptions: SourceCleanupOptions = createKeepOptions();
105109
export const printOptions: OutputOptions = createPrintOptions();
@@ -118,7 +122,7 @@ interface CliOption extends CliDoc {
118122
readonly shortOption?: string;
119123
}
120124

121-
export abstract class BaseCli {
125+
abstract class BaseCli {
122126
public readonly command?: string;
123127
public readonly description: string;
124128
public readonly commands: readonly CliCommand[];
@@ -211,163 +215,192 @@ export abstract class BaseCli {
211215
}
212216
}
213217

214-
export class DiffCommand extends BaseCli {
215-
public static readonly COMMAND = "diff";
216-
public static readonly SMALL_DESCRIPTION =
217-
"Compare the default preferences of two firefox versions";
218-
public static readonly DESCRIPTION =
219-
DiffCommand.SMALL_DESCRIPTION + " and highlight differences";
220-
221-
public static readonly COMMANDS: readonly CliCommand[] = [
222-
{
223-
values: [`<${CLI_VALUES.OLD_VERSION}> <${CLI_VALUES.NEW_VERSION}>`],
224-
doc: [
225-
"First arg is the old version, second arg is the new version",
226-
` Example: diff ${EXAMPLE_VERSION.OLD_VERSION} ${EXAMPLE_VERSION.NEW_VERSION}`,
227-
].join("\n"),
228-
},
229-
];
230-
218+
class CleanCommand extends BaseCli {
219+
public static readonly COMMAND = "clean";
220+
public static readonly SMALL_DESCRIPTION = undefined;
221+
public static readonly DESCRIPTION = `Remove files generated by ${APP_NAME}`;
222+
public static readonly COMMANDS: readonly CliCommand[] = [];
231223
public static readonly OPTIONS: readonly CliOption[] = [
232224
{
233-
longOption: CLI_ARGS.CLEAN_ARCHIVES,
225+
longOption: `${CLI_ARGS.KEEP} ${CLI_VALUES.VERSION1},${CLI_VALUES.VERSION2}`,
234226

235-
doc: "Remove archives after retrieving preferences",
227+
doc: [
228+
"Specify one or more versions whose archives and binaries should be preserved during cleanup",
229+
"Provide a comma-separated list of versions to keep",
230+
` Example: ${CLI_ARGS.KEEP} ${EXAMPLE_VERSION.OLD_VERSION},${EXAMPLE_VERSION.NEW_VERSION}`,
231+
].join("\n"),
232+
smallDoc: "Version to keep",
236233
},
237234
{
238-
longOption: CLI_ARGS.CLEAN_SOURCES,
239-
240-
doc: "Remove binaries after retrieving preferences",
235+
longOption: CLI_ARGS.KEEP_ARCHIVES,
236+
doc: "Keep all archives",
241237
},
242-
...printAndSave,
243238
{
244-
longOption: `${CLI_ARGS.COMPARE_USERJS} ${CLI_VALUES.PATH_USAGE}`,
245-
doc: "Check for removed or changed keys in the specified user.js file",
239+
longOption: CLI_ARGS.KEEP_SOURCES,
240+
doc: "Keep all binaries",
246241
},
247242
];
248243

249244
constructor(fail: boolean = true) {
250245
super(
251246
fail,
252-
DiffCommand.DESCRIPTION,
253-
DiffCommand.COMMANDS,
254-
DiffCommand.OPTIONS,
247+
CleanCommand.DESCRIPTION,
248+
CleanCommand.COMMANDS,
249+
CleanCommand.OPTIONS,
255250
);
256251
}
257252

258253
public async entrypoint(): Promise<void> {
259-
await diff();
254+
const keptVersions = parseKeepArgument();
255+
await clean(keptVersions);
260256
}
261257
}
262258

263-
export class UnusedPrefCommand extends BaseCli {
264-
public static readonly COMMAND = "unused-prefs-userjs";
259+
class DefaultPrefsUserJSCommand extends BaseCli {
260+
public static readonly COMMAND = "default-prefs-userjs";
265261
public static readonly SMALL_DESCRIPTION = undefined;
266262
public static readonly DESCRIPTION =
267-
"Identify unused preferences from your user.js file";
263+
"Identify default preferences from your user.js file";
268264
public static readonly COMMANDS: readonly CliCommand[] = [pathUsageUserJS];
269-
public static readonly OPTIONS: readonly CliOption[] = [pathToFirefox];
265+
public static readonly OPTIONS: readonly CliOption[] = [
266+
pathToFirefox,
267+
...printAndSave,
268+
];
270269

271270
constructor(fail: boolean = true) {
272271
super(
273272
fail,
274-
UnusedPrefCommand.DESCRIPTION,
275-
UnusedPrefCommand.COMMANDS,
276-
UnusedPrefCommand.OPTIONS,
273+
DefaultPrefsUserJSCommand.DESCRIPTION,
274+
DefaultPrefsUserJSCommand.COMMANDS,
275+
DefaultPrefsUserJSCommand.OPTIONS,
277276
);
278277
}
279278

280279
public async entrypoint(): Promise<void> {
281-
await unusedPrefs();
280+
const [, , , compareUserjs] = process.argv;
281+
if (compareUserjs === undefined) {
282+
this.usage();
283+
}
284+
await defaultPrefsUserJS(compareUserjs);
282285
}
283286
}
284287

285-
export class CleanCommand extends BaseCli {
286-
public static readonly COMMAND = "clean";
288+
class DefaultPrefsCommand extends BaseCli {
289+
public static readonly COMMAND = "default-prefs";
287290
public static readonly SMALL_DESCRIPTION = undefined;
288-
public static readonly DESCRIPTION = `Remove files generated by ${APP_NAME}`;
291+
public static readonly DESCRIPTION = `Get a list of all default prefs`;
289292
public static readonly COMMANDS: readonly CliCommand[] = [];
290293
public static readonly OPTIONS: readonly CliOption[] = [
291-
{
292-
longOption: `${CLI_ARGS.KEEP} ${CLI_VALUES.VERSION1},${CLI_VALUES.VERSION2}`,
293-
294-
doc: [
295-
"Specify one or more versions whose archives and binaries should be preserved during cleanup",
296-
"Provide a comma-separated list of versions to keep",
297-
` Example: ${CLI_ARGS.KEEP} ${EXAMPLE_VERSION.OLD_VERSION},${EXAMPLE_VERSION.NEW_VERSION}`,
298-
].join("\n"),
299-
smallDoc: "Version to keep",
300-
},
301-
{
302-
longOption: CLI_ARGS.KEEP_ARCHIVES,
303-
doc: "Keep all archives",
304-
},
305-
{
306-
longOption: CLI_ARGS.KEEP_SOURCES,
307-
doc: "Keep all binaries",
308-
},
294+
...printAndSave,
295+
pathToFirefox,
309296
];
310297

311298
constructor(fail: boolean = true) {
312299
super(
313300
fail,
314-
CleanCommand.DESCRIPTION,
315-
CleanCommand.COMMANDS,
316-
CleanCommand.OPTIONS,
301+
DefaultPrefsCommand.DESCRIPTION,
302+
DefaultPrefsCommand.COMMANDS,
303+
DefaultPrefsCommand.OPTIONS,
317304
);
318305
}
319306

320307
public async entrypoint(): Promise<void> {
321-
await clean();
308+
const { path } = getInstalledFirefoxPath();
309+
await getDefaultPrefs(path);
322310
}
323311
}
324312

325-
export class DefaultPrefsCommand extends BaseCli {
326-
public static readonly COMMAND = "default-prefs";
327-
public static readonly SMALL_DESCRIPTION = undefined;
328-
public static readonly DESCRIPTION = `Get a list of all default prefs`;
329-
public static readonly COMMANDS: readonly CliCommand[] = [];
313+
export interface Diff {
314+
compareUserJS?: string;
315+
oldVersion: string;
316+
newVersion: string;
317+
}
318+
319+
class DiffCommand extends BaseCli {
320+
public static readonly COMMAND = "diff";
321+
public static readonly SMALL_DESCRIPTION =
322+
"Compare the default preferences of two firefox versions";
323+
public static readonly DESCRIPTION =
324+
DiffCommand.SMALL_DESCRIPTION + " and highlight differences";
325+
326+
public static readonly COMMANDS: readonly CliCommand[] = [
327+
{
328+
values: [`<${CLI_VALUES.OLD_VERSION}> <${CLI_VALUES.NEW_VERSION}>`],
329+
doc: [
330+
"First arg is the old version, second arg is the new version",
331+
` Example: diff ${EXAMPLE_VERSION.OLD_VERSION} ${EXAMPLE_VERSION.NEW_VERSION}`,
332+
].join("\n"),
333+
},
334+
];
335+
330336
public static readonly OPTIONS: readonly CliOption[] = [
337+
{
338+
longOption: CLI_ARGS.CLEAN_ARCHIVES,
339+
340+
doc: "Remove archives after retrieving preferences",
341+
},
342+
{
343+
longOption: CLI_ARGS.CLEAN_SOURCES,
344+
345+
doc: "Remove binaries after retrieving preferences",
346+
},
331347
...printAndSave,
332-
pathToFirefox,
348+
{
349+
longOption: `${CLI_ARGS.COMPARE_USERJS} ${CLI_VALUES.PATH_USAGE}`,
350+
doc: "Check for removed or changed keys in the specified user.js file",
351+
},
333352
];
334353

335354
constructor(fail: boolean = true) {
336355
super(
337356
fail,
338-
DefaultPrefsCommand.DESCRIPTION,
339-
DefaultPrefsCommand.COMMANDS,
340-
DefaultPrefsCommand.OPTIONS,
357+
DiffCommand.DESCRIPTION,
358+
DiffCommand.COMMANDS,
359+
DiffCommand.OPTIONS,
341360
);
342361
}
343362

344363
public async entrypoint(): Promise<void> {
345-
await getDefaultPrefs();
364+
const [, , , oldVersion, newVersion] = process.argv;
365+
366+
if (
367+
!oldVersion ||
368+
!newVersion ||
369+
!startsWithNumberDotNumber(oldVersion) ||
370+
!startsWithNumberDotNumber(newVersion)
371+
) {
372+
this.usage();
373+
}
374+
375+
const compareUserJS = getArgumentValue(CLI_ARGS.COMPARE_USERJS);
376+
377+
await diff({ compareUserJS, oldVersion, newVersion });
346378
}
347379
}
348380

349-
export class DefaultPrefsUserJSCommand extends BaseCli {
350-
public static readonly COMMAND = "default-prefs-userjs";
381+
class UnusedPrefCommand extends BaseCli {
382+
public static readonly COMMAND = "unused-prefs-userjs";
351383
public static readonly SMALL_DESCRIPTION = undefined;
352384
public static readonly DESCRIPTION =
353-
"Identify default preferences from your user.js file";
385+
"Identify unused preferences from your user.js file";
354386
public static readonly COMMANDS: readonly CliCommand[] = [pathUsageUserJS];
355-
public static readonly OPTIONS: readonly CliOption[] = [
356-
pathToFirefox,
357-
...printAndSave,
358-
];
387+
public static readonly OPTIONS: readonly CliOption[] = [pathToFirefox];
359388

360389
constructor(fail: boolean = true) {
361390
super(
362391
fail,
363-
DefaultPrefsUserJSCommand.DESCRIPTION,
364-
DefaultPrefsUserJSCommand.COMMANDS,
365-
DefaultPrefsUserJSCommand.OPTIONS,
392+
UnusedPrefCommand.DESCRIPTION,
393+
UnusedPrefCommand.COMMANDS,
394+
UnusedPrefCommand.OPTIONS,
366395
);
367396
}
368397

369398
public async entrypoint(): Promise<void> {
370-
await defaultPrefsUserJS();
399+
const [, , , compareUserjs] = process.argv;
400+
if (compareUserjs === undefined) {
401+
this.usage();
402+
}
403+
await unusedPrefs(compareUserjs);
371404
}
372405
}
373406

@@ -378,6 +411,7 @@ export const ALL_COMMANDS = [
378411
UnusedPrefCommand,
379412
DefaultPrefsUserJSCommand,
380413
];
414+
381415
export class Cli extends BaseCli {
382416
public static readonly COMMANDS: readonly CliCommand[] = ALL_COMMANDS.map(
383417
(cmd) => ({

0 commit comments

Comments
 (0)