@theholocron/cli 1.0.0 → 1.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/LICENSE +21 -674
- package/README.md +19 -8
- package/dist/capabilities/index.d.mts +2 -0
- package/dist/capabilities/index.mjs +2 -0
- package/dist/capabilities-QjjhVlDd.mjs +43 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +852 -0
- package/dist/config-DWlIfFZm.mjs +113 -0
- package/dist/index-CSxf0Yc7.d.mts +485 -0
- package/dist/index.d.mts +58 -0
- package/dist/index.mjs +3 -0
- package/package.json +46 -53
- package/.alexignore +0 -3
- package/.editorconfig +0 -24
- package/.editorconfig-checker.json +0 -20
- package/.env +0 -3
- package/.gitattributes +0 -12
- package/.github/CODEOWNERS +0 -1
- package/.github/dependabot.yml +0 -6
- package/.github/labeler.yml +0 -21
- package/.github/workflows/bookkeeping-pr.yml +0 -32
- package/.github/workflows/greetings.yml +0 -32
- package/.github/workflows/lint.yml +0 -96
- package/.github/workflows/publish.yml +0 -71
- package/.github/workflows/review.yml +0 -72
- package/.github/workflows/stale.yml +0 -25
- package/.husky/commit-msg +0 -1
- package/.husky/pre-commit +0 -13
- package/.husky/prepare-commit-msg +0 -18
- package/commitlint.config.js +0 -9
- package/eslint.config.js +0 -9
- package/media/README.md +0 -11
- package/media/error.mp3 +0 -0
- package/media/success.mp3 +0 -0
- package/media/warning.mp3 +0 -0
- package/prettier.config.js +0 -11
- package/src/cli.ts +0 -65
- package/src/commands/README.md +0 -13
- package/src/commands/bootstrap.ts +0 -102
- package/src/commands/conf/README.md +0 -45
- package/src/commands/conf/add.ts +0 -49
- package/src/commands/conf/edit.ts +0 -12
- package/src/commands/conf/view.ts +0 -40
- package/src/commands/conf.ts +0 -10
- package/src/commands/log.ts +0 -91
- package/src/const.ts +0 -19
- package/src/tasks/README.md +0 -33
- package/src/tasks/find/README.md +0 -49
- package/src/tasks/find/find-project.example.ts +0 -23
- package/src/tasks/find/find-project.ts +0 -76
- package/src/tasks/find/index.ts +0 -1
- package/src/tasks/index.ts +0 -2
- package/src/tasks/replace/README.md +0 -61
- package/src/tasks/replace/index.ts +0 -1
- package/src/tasks/replace/replace.example.ts +0 -31
- package/src/tasks/replace/replace.ts +0 -95
- package/src/ui/README.md +0 -8
- package/src/ui/index.ts +0 -2
- package/src/ui/open/README.md +0 -58
- package/src/ui/open/index.ts +0 -7
- package/src/ui/open/open-browser.ts +0 -25
- package/src/ui/open/open-editor.example.ts +0 -14
- package/src/ui/open/open-editor.ts +0 -30
- package/src/ui/prompts/README.md +0 -36
- package/src/ui/prompts/autocomplete.prompt.ts +0 -18
- package/src/ui/prompts/confirm.prompt.ts +0 -21
- package/src/ui/prompts/index.ts +0 -17
- package/src/ui/prompts/input.prompt.ts +0 -15
- package/src/ui/prompts/search.prompt.ts +0 -33
- package/src/ui/prompts/select.prompt.ts +0 -17
- package/src/ui/prompts/types.ts +0 -8
- package/src/ui/prompts/utils.ts +0 -11
- package/src/utils/$/README.md +0 -87
- package/src/utils/$/command.test.ts +0 -48
- package/src/utils/$/command.ts +0 -21
- package/src/utils/$/directory.ts +0 -108
- package/src/utils/$/file.ts +0 -98
- package/src/utils/$/index.example.ts +0 -37
- package/src/utils/$/index.ts +0 -22
- package/src/utils/$/path.ts +0 -13
- package/src/utils/$/remove.ts +0 -47
- package/src/utils/$/spawn.ts +0 -26
- package/src/utils/$/utils.ts +0 -21
- package/src/utils/README.md +0 -15
- package/src/utils/config/README.md +0 -41
- package/src/utils/config/config.ts +0 -26
- package/src/utils/config/index.ts +0 -1
- package/src/utils/config/preferences.conf.ts +0 -45
- package/src/utils/env/README.md +0 -52
- package/src/utils/env/env.example.ts +0 -21
- package/src/utils/env/env.ts +0 -57
- package/src/utils/env/index.ts +0 -1
- package/src/utils/index.ts +0 -5
- package/src/utils/log/README.md +0 -117
- package/src/utils/log/index.ts +0 -2
- package/src/utils/log/log.example.ts +0 -13
- package/src/utils/log/log.ts +0 -58
- package/src/utils/log/logger.ts +0 -28
- package/src/utils/log/sound.ts +0 -35
- package/src/utils/node/README.md +0 -23
- package/src/utils/node/index.ts +0 -5
- package/src/utils/node/package.ts +0 -12
- package/tsconfig.json +0 -15
- package/yamllint.config.yml +0 -20
package/src/cli.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env npx tsx
|
|
2
|
-
|
|
3
|
-
import * as str from "@theholocron/utils-string";
|
|
4
|
-
import updateNotifier from "update-notifier";
|
|
5
|
-
import { hideBin } from "yargs/helpers";
|
|
6
|
-
import yargs from "yargs/yargs";
|
|
7
|
-
import { __cmddir } from "@/const";
|
|
8
|
-
import pkg from "@/package";
|
|
9
|
-
import * as utils from "@/utils";
|
|
10
|
-
|
|
11
|
-
const [, env] = utils.env.read();
|
|
12
|
-
|
|
13
|
-
export interface CLIOptions {
|
|
14
|
-
d?: boolean;
|
|
15
|
-
debug?: boolean;
|
|
16
|
-
s?: boolean;
|
|
17
|
-
sound?: boolean;
|
|
18
|
-
verbose?: boolean;
|
|
19
|
-
// spinner: Ora;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
23
|
-
yargs(hideBin(process.argv))
|
|
24
|
-
.usage("Usage: $0 <command> [options]")
|
|
25
|
-
.commandDir(__cmddir("./commands"), {
|
|
26
|
-
extensions: ["ts"],
|
|
27
|
-
})
|
|
28
|
-
.demandCommand()
|
|
29
|
-
.env("HOLOCRON")
|
|
30
|
-
.completion()
|
|
31
|
-
.recommendCommands()
|
|
32
|
-
.options({
|
|
33
|
-
d: {
|
|
34
|
-
alias: ["debug"],
|
|
35
|
-
default: utils.config.get("preferences.debug") || str.toBoolean(env?.HOLOCRON_DEBUG) || false,
|
|
36
|
-
describe: "Turn on debugging mode",
|
|
37
|
-
type: "boolean",
|
|
38
|
-
global: true,
|
|
39
|
-
hidden: true,
|
|
40
|
-
},
|
|
41
|
-
s: {
|
|
42
|
-
alias: ["sound"],
|
|
43
|
-
default: utils.config.get("preferences.sound") || str.toBoolean(env?.HOLOCRON_SOUND) || false,
|
|
44
|
-
describe: "Turn on sound effects",
|
|
45
|
-
type: "boolean",
|
|
46
|
-
global: true,
|
|
47
|
-
hidden: true,
|
|
48
|
-
},
|
|
49
|
-
verbose: {
|
|
50
|
-
default: str.toBoolean(env?.HOLOCRON_VERBOSE) || false,
|
|
51
|
-
describe: "Turn on logging",
|
|
52
|
-
type: "boolean",
|
|
53
|
-
global: true,
|
|
54
|
-
},
|
|
55
|
-
})
|
|
56
|
-
.alias({
|
|
57
|
-
h: "help",
|
|
58
|
-
v: "version",
|
|
59
|
-
})
|
|
60
|
-
.strict()
|
|
61
|
-
.help("h")
|
|
62
|
-
.version()
|
|
63
|
-
.epilogue(`© 2024-${new Date().getFullYear()} The Holocron, Inc. All rights reserved.`).argv as CLIOptions;
|
|
64
|
-
|
|
65
|
-
updateNotifier({ pkg }).notify();
|
package/src/commands/README.md
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# Commands
|
|
2
|
-
|
|
3
|
-
A direct interaction with the command-line via `yargs`.
|
|
4
|
-
|
|
5
|
-
## What's Available
|
|
6
|
-
|
|
7
|
-
- bootstrap - bootstrap a repository by swapping out template variables
|
|
8
|
-
- conf - interact with the configuration file
|
|
9
|
-
- log - print out the log file
|
|
10
|
-
|
|
11
|
-
## Rules
|
|
12
|
-
|
|
13
|
-
- There should be error logging and success logging; process/warning if it make sense; data for sure
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import * as str from "@theholocron/utils-string";
|
|
4
|
-
import { type CommandBuilder } from "yargs";
|
|
5
|
-
import { type CLIOptions } from "@/cli";
|
|
6
|
-
import { HOME } from "@/const";
|
|
7
|
-
import { findReplace } from "@/tasks";
|
|
8
|
-
import { prompt } from "@/ui";
|
|
9
|
-
import { getDirectoryByLevel, log, node } from "@/utils";
|
|
10
|
-
|
|
11
|
-
function getRepoName(pathname) {
|
|
12
|
-
const gitconfig = path.join(pathname, ".git", "config");
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
const content = fs.readFileSync(gitconfig, "utf-8");
|
|
16
|
-
const match = content.match(/url = .*\/(.*)\.git/); // Regex to capture repo name
|
|
17
|
-
return match ? match[1] : null;
|
|
18
|
-
} catch (error) {
|
|
19
|
-
console.error("Error reading .git/config:", error);
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface BootstrapOpts extends CLIOptions {
|
|
25
|
-
description?: string;
|
|
26
|
-
name?: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
interface BootstrapPositional extends CLIOptions {
|
|
30
|
-
filepath?: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const builder: CommandBuilder<BootstrapOpts, BootstrapPositional> = (yargs) =>
|
|
34
|
-
yargs
|
|
35
|
-
.options({
|
|
36
|
-
description: {
|
|
37
|
-
describe: "The description for the project",
|
|
38
|
-
nargs: 1,
|
|
39
|
-
requiresArg: true,
|
|
40
|
-
type: "string",
|
|
41
|
-
},
|
|
42
|
-
name: {
|
|
43
|
-
describe: "The name of the project",
|
|
44
|
-
nargs: 1,
|
|
45
|
-
requiresArg: true,
|
|
46
|
-
type: "string",
|
|
47
|
-
},
|
|
48
|
-
})
|
|
49
|
-
.positional("filepath", {
|
|
50
|
-
describe: "The path to where the project is stored",
|
|
51
|
-
type: "string",
|
|
52
|
-
});
|
|
53
|
-
export const command: string = "bootstrap [filepath] [name] [description]";
|
|
54
|
-
export const desc: string = "Bootstrap a template";
|
|
55
|
-
export async function handler(options: CLIOptions): Promise<void> {
|
|
56
|
-
const FN = "bootstrap";
|
|
57
|
-
log.data(FN, "arguments", options, options);
|
|
58
|
-
|
|
59
|
-
// search the home directory for projects
|
|
60
|
-
// @TODO set this to check a configurable directory and default to HOME
|
|
61
|
-
const projectPath = await prompt.search(options?.filepath, HOME, options);
|
|
62
|
-
// from the project i need to determine if its has a package.json
|
|
63
|
-
// and what type of template it is based on the name on the package.json
|
|
64
|
-
// e.g. { name: "@theholocron/cli-template" }
|
|
65
|
-
const [, pkg] = await node.pkg(projectPath);
|
|
66
|
-
// template type, if any
|
|
67
|
-
const type = getDirectoryByLevel(pkg.name, -1) ?? "react-template";
|
|
68
|
-
|
|
69
|
-
const name =
|
|
70
|
-
options?.name ??
|
|
71
|
-
(await prompt.input("The name. What are we calling this thing?", true, {
|
|
72
|
-
default: getRepoName(projectPath),
|
|
73
|
-
transformer: str.toKebabCase,
|
|
74
|
-
}));
|
|
75
|
-
|
|
76
|
-
const description =
|
|
77
|
-
options?.description ??
|
|
78
|
-
(await prompt.input("A brief description. What does it do?", true, {
|
|
79
|
-
transformer: str.toSentenceCase,
|
|
80
|
-
}));
|
|
81
|
-
|
|
82
|
-
const readme = `${projectPath}/README.md`;
|
|
83
|
-
const pkgJson = `${projectPath}/package.json`;
|
|
84
|
-
|
|
85
|
-
await findReplace([readme, `${projectPath}/tsconfig.json`], type, str.toTitleCase(name));
|
|
86
|
-
await findReplace(
|
|
87
|
-
[`${projectPath}/src/utils/config/config.ts`, pkgJson, `${projectPath}/vite.config.ts`],
|
|
88
|
-
type,
|
|
89
|
-
str.toKebabCase(name)
|
|
90
|
-
);
|
|
91
|
-
await findReplace([readme, pkgJson], "<description>", str.toSentenceCase(description));
|
|
92
|
-
|
|
93
|
-
if (type === "cli-template") {
|
|
94
|
-
await findReplace(
|
|
95
|
-
[`${projectPath}/src/cli.ts`, `${projectPath}/.env`],
|
|
96
|
-
"CLI_TEMPLATE",
|
|
97
|
-
str.toConstantCase(name)
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
process.exit(0);
|
|
102
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# Configuration File
|
|
2
|
-
|
|
3
|
-
An action to interact with the configuration file.
|
|
4
|
-
|
|
5
|
-
## How Do I Use This?
|
|
6
|
-
|
|
7
|
-
Run the `--help` or `-h` command to find out how to use the command.
|
|
8
|
-
|
|
9
|
-
```sh
|
|
10
|
-
Usage: tsx ./src/cli.ts conf <command> [options]
|
|
11
|
-
|
|
12
|
-
Commands:
|
|
13
|
-
tsx ./src/cli.ts conf add <key> <value> [options]
|
|
14
|
-
tsx ./src/cli.ts conf edit [options]
|
|
15
|
-
tsx ./src/cli.ts conf view [key] [options]
|
|
16
|
-
|
|
17
|
-
Examples:
|
|
18
|
-
tsx ./src/cli.ts conf add
|
|
19
|
-
tsx ./src/cli.ts conf edit
|
|
20
|
-
tsx ./src/cli.ts conf view
|
|
21
|
-
tsx ./src/cli.ts conf view domains
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
All [global options](/README.md#options) are supported as well.
|
|
25
|
-
|
|
26
|
-
## API
|
|
27
|
-
|
|
28
|
-
### Return
|
|
29
|
-
|
|
30
|
-
If command is valid, it will exit with a `0` code and the file path. Otherwise, it will exit with `1` code and an error message.
|
|
31
|
-
|
|
32
|
-
### `add`
|
|
33
|
-
|
|
34
|
-
Adds a key-value pair to the configuration and returns the updated configuration.
|
|
35
|
-
|
|
36
|
-
### `edit`
|
|
37
|
-
|
|
38
|
-
Opens the configuration file in the default editor for editing.
|
|
39
|
-
|
|
40
|
-
### `view`
|
|
41
|
-
|
|
42
|
-
Retrieves and prints the configuration settings based on provided keys or all configurations if no keys are specified.
|
|
43
|
-
|
|
44
|
-
* `options:CLIOptions` - The options containing configuration details and the keys to be retrieved.
|
|
45
|
-
* @returns {Record<string, any> | Record<string, any>[]} - The requested configuration settings.
|
package/src/commands/conf/add.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import type { CommandBuilder } from "yargs";
|
|
2
|
-
import { CLIOptions } from "@/cli";
|
|
3
|
-
import { config, log } from "@/utils";
|
|
4
|
-
|
|
5
|
-
interface AddConfOpts extends CLIOptions {
|
|
6
|
-
name: string;
|
|
7
|
-
value: any;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const builder: CommandBuilder<AddConfOpts, AddConfOpts> = (yargs) =>
|
|
11
|
-
yargs
|
|
12
|
-
.positional("name", {
|
|
13
|
-
demandOption: true,
|
|
14
|
-
describe: "A key to store as within the configuration file",
|
|
15
|
-
type: "string",
|
|
16
|
-
})
|
|
17
|
-
.positional("value", {
|
|
18
|
-
demandOption: true,
|
|
19
|
-
describe: "The value to store within the configuration file",
|
|
20
|
-
type: "string",
|
|
21
|
-
});
|
|
22
|
-
export const command: string = "add <name> <value>";
|
|
23
|
-
export const desc: string = "Add to the configuration";
|
|
24
|
-
export function handler(options: AddConfOpts): void {
|
|
25
|
-
const FN = "conf add";
|
|
26
|
-
log.data(FN, "arguments", options, options);
|
|
27
|
-
|
|
28
|
-
const { name, value } = options;
|
|
29
|
-
const conf = config.get(name);
|
|
30
|
-
|
|
31
|
-
if (Array.isArray(conf)) {
|
|
32
|
-
config.set(name, Array.from(new Set([...conf, value])));
|
|
33
|
-
const obj = config.get(name);
|
|
34
|
-
log.success(FN, obj);
|
|
35
|
-
return obj;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (Object.prototype.toString.call(conf) === "[object Object]") {
|
|
39
|
-
config.set(name, { ...conf, ...value });
|
|
40
|
-
const obj = config.get(name);
|
|
41
|
-
log.success(FN, obj);
|
|
42
|
-
return obj;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
config.set(name, value);
|
|
46
|
-
const obj = config.get(name);
|
|
47
|
-
log.success(FN, obj);
|
|
48
|
-
return obj;
|
|
49
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { type CLIOptions } from "@/cli";
|
|
2
|
-
import { open } from "@/ui";
|
|
3
|
-
import { config, log } from "@/utils";
|
|
4
|
-
|
|
5
|
-
export const command: string = "edit";
|
|
6
|
-
export const desc: string = "Edit the configuration";
|
|
7
|
-
export async function handler(options: CLIOptions): Promise<void> {
|
|
8
|
-
const FN = "conf edit";
|
|
9
|
-
log.data(FN, "arguments", options, options);
|
|
10
|
-
|
|
11
|
-
await open.editor(config.path, undefined, true);
|
|
12
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { CommandBuilder } from "yargs";
|
|
2
|
-
import { CLIOptions } from "@/cli";
|
|
3
|
-
import { config, log, str } from "@/utils";
|
|
4
|
-
|
|
5
|
-
interface ViewConfOpts extends CLIOptions {
|
|
6
|
-
name?: string[];
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const builder: CommandBuilder<ViewConfOpts, ViewConfOpts> = (yargs) =>
|
|
10
|
-
yargs.positional("name", {
|
|
11
|
-
coerce: str.toArray,
|
|
12
|
-
describe: "Any key within the configuration file",
|
|
13
|
-
type: "string",
|
|
14
|
-
});
|
|
15
|
-
export const command: string = "view [name..]";
|
|
16
|
-
export const desc: string = "View the configuration";
|
|
17
|
-
export function handler(options: ViewConfOpts): Record<string, any> | Record<string, any>[] {
|
|
18
|
-
const FN = "conf.view";
|
|
19
|
-
log.data(FN, "arguments", options, options);
|
|
20
|
-
|
|
21
|
-
const { name } = options;
|
|
22
|
-
|
|
23
|
-
if (name && name.length > 0) {
|
|
24
|
-
const items = name.reduce(
|
|
25
|
-
(acc, item) => {
|
|
26
|
-
acc[item] = config.get(item);
|
|
27
|
-
return acc;
|
|
28
|
-
},
|
|
29
|
-
{} as Record<string, any>
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
console.log(items);
|
|
33
|
-
return items;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const allConfig = config.get();
|
|
37
|
-
const obj = { ...allConfig };
|
|
38
|
-
console.log(obj);
|
|
39
|
-
return obj;
|
|
40
|
-
}
|
package/src/commands/conf.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { type CommandBuilder } from "yargs";
|
|
2
|
-
|
|
3
|
-
interface ConfArgs {
|
|
4
|
-
command: string;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export const builder: CommandBuilder<ConfArgs, ConfArgs> = (yargs) =>
|
|
8
|
-
yargs.commandDir("conf", { extensions: ["js", "ts"] });
|
|
9
|
-
export const command: string = "conf <command>";
|
|
10
|
-
export const desc: string = "Interact with the configuration";
|
package/src/commands/log.ts
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import readline from "readline";
|
|
3
|
-
import chalk from "chalk";
|
|
4
|
-
import type { CommandBuilder } from "yargs";
|
|
5
|
-
import { type CLIOptions } from "@/cli";
|
|
6
|
-
import { config, type LogLevel } from "@/utils";
|
|
7
|
-
|
|
8
|
-
export interface LogOpts extends CLIOptions {
|
|
9
|
-
level?: LogLevel;
|
|
10
|
-
maxLines?: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const levels: Record<LogLevel, string> = {
|
|
14
|
-
all: "log",
|
|
15
|
-
error: "error.log",
|
|
16
|
-
warn: "warn.log",
|
|
17
|
-
info: "info.log",
|
|
18
|
-
verbose: "verbose.log",
|
|
19
|
-
debug: "debug.log",
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export const builder: CommandBuilder<LogOpts, LogOpts> = (yargs) =>
|
|
23
|
-
yargs.options({
|
|
24
|
-
l: {
|
|
25
|
-
alias: ["level", "log-level"],
|
|
26
|
-
choices: Object.keys(levels) as LogLevel[],
|
|
27
|
-
default: "all",
|
|
28
|
-
demandOption: true,
|
|
29
|
-
describe: "The log level to show",
|
|
30
|
-
},
|
|
31
|
-
o: {
|
|
32
|
-
alias: ["output", "max-lines"],
|
|
33
|
-
default: 20,
|
|
34
|
-
describe: "Maximum number of lines to show, from the end of the file",
|
|
35
|
-
type: "number",
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
export const command: string = "log";
|
|
39
|
-
export const desc: string = "Print out the logs";
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Reads and displays the last `maxLines` lines from a log file with enhanced formatting.
|
|
43
|
-
*
|
|
44
|
-
* @param {string} [level='all'] - The log level to read (e.g., 'all', 'error'). Defaults to 'all'.
|
|
45
|
-
* @param {number} [maxLines] - The number of lines to display from the end of the log file. If not provided, displays the entire file.
|
|
46
|
-
*/
|
|
47
|
-
export async function handler(options: LogOpts): Promise<void> {
|
|
48
|
-
const { level, maxLines } = options;
|
|
49
|
-
const logFilePath = `${config.get("preferences.logs")}/${levels[level || "all"] || levels.all}`;
|
|
50
|
-
const fileStream = fs.createReadStream(logFilePath);
|
|
51
|
-
const rl = readline.createInterface({
|
|
52
|
-
input: fileStream,
|
|
53
|
-
crlfDelay: Infinity,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const lines: { number: number; content: string }[] = [];
|
|
57
|
-
let lineNumber = 0;
|
|
58
|
-
|
|
59
|
-
rl.on("line", (line: string) => {
|
|
60
|
-
lineNumber++;
|
|
61
|
-
lines.push({ number: lineNumber, content: line });
|
|
62
|
-
if (maxLines !== undefined && lines.length > maxLines) {
|
|
63
|
-
lines.shift();
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
await new Promise<void>((resolve) => rl.on("close", resolve));
|
|
68
|
-
|
|
69
|
-
// Define chalk styles
|
|
70
|
-
const timestampStyle = chalk.yellow;
|
|
71
|
-
const levelStyle = chalk.bold.red;
|
|
72
|
-
const messageStyle = chalk.white;
|
|
73
|
-
const bracketStyle = chalk.magenta;
|
|
74
|
-
|
|
75
|
-
lines.forEach(({ number, content }) => {
|
|
76
|
-
const match = content.match(/(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) (\w+): (.+)/);
|
|
77
|
-
if (match) {
|
|
78
|
-
let [, timestamp, level, message] = match;
|
|
79
|
-
|
|
80
|
-
// Apply magenta style to bracketed content
|
|
81
|
-
message = message.replace(/\[([^\]]+)\]/g, (match, p1) => `${bracketStyle(`[${p1}]`)}`);
|
|
82
|
-
console.log(
|
|
83
|
-
`${chalk.gray(number)}: ${timestampStyle(`[${timestamp}]`)} ${levelStyle(level)}: ${messageStyle(message)}`
|
|
84
|
-
);
|
|
85
|
-
} else {
|
|
86
|
-
// Apply magenta style to bracketed content in lines that don't match the main pattern
|
|
87
|
-
content = content.replace(/\[([^\]]+)\]/g, (match, p1) => `${bracketStyle(`[${p1}]`)}`);
|
|
88
|
-
console.log(`${chalk.gray(number)}: ${content}`); // If the line doesn't match the expected format, print it as is with line number
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
}
|
package/src/const.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import os from "node:os";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
|
|
5
|
-
export const __filename = fileURLToPath(import.meta.url);
|
|
6
|
-
export const __dirname = path.dirname(__filename);
|
|
7
|
-
export const __cmddir = (dir: string) => path.resolve(__dirname, dir);
|
|
8
|
-
export const HOME = os.homedir();
|
|
9
|
-
export const OS = process.platform;
|
|
10
|
-
export const SYSTEM_IGNORED_FOLDERS = [
|
|
11
|
-
"Applications",
|
|
12
|
-
"Library",
|
|
13
|
-
"Movies",
|
|
14
|
-
"Music",
|
|
15
|
-
"Pictures",
|
|
16
|
-
"Public",
|
|
17
|
-
"Users",
|
|
18
|
-
] as const;
|
|
19
|
-
export type IgnoredFolders = (typeof SYSTEM_IGNORED_FOLDERS)[number];
|
package/src/tasks/README.md
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# Tasks
|
|
2
|
-
|
|
3
|
-
A set of functions designed to be strung together to create actions. An API of sorts, to power the command-line client.
|
|
4
|
-
|
|
5
|
-
## What's Available
|
|
6
|
-
|
|
7
|
-
- [find](./find/README.md) - find things
|
|
8
|
-
- [replace](./replace/README.md) - replace things
|
|
9
|
-
|
|
10
|
-
## Rules
|
|
11
|
-
|
|
12
|
-
A set of rules for how I decide when to create a task, as opposed to creating an action.
|
|
13
|
-
|
|
14
|
-
### A single responsibility
|
|
15
|
-
|
|
16
|
-
It should only have a single responsibility. It shouldn't interact with the user in any sort of decision tree (no interaction with `inquirer`). Testing this through the provided examples should allow for a few inputs with some expected outputs. For example:
|
|
17
|
-
|
|
18
|
-
- I clone a project; I get a clone somewhere
|
|
19
|
-
- I search a folder; I get the location of that folder
|
|
20
|
-
- I search a project; I get the location of that project
|
|
21
|
-
- I create a directory; I get a directory somewhere
|
|
22
|
-
|
|
23
|
-
In the case of actions, these are where I may ask it do several things while having it interact with the user. This also means that the return type is typically consistent; meaning it will be an array of consistent types, etc. If you have forks in your code that change the type from say a string to a number you might need to split them up into separate tasks.
|
|
24
|
-
|
|
25
|
-
### Return type
|
|
26
|
-
|
|
27
|
-
It should return in the form of <code>[null | Error, null | data, null | duration]</code>. Tasks returns data; actions return process error codes or voids or logs.
|
|
28
|
-
|
|
29
|
-
### Other rules
|
|
30
|
-
|
|
31
|
-
- Tasks are designed to be used to compose an action; this means the output of one task will be input for another
|
|
32
|
-
- Tasks are atomic; as close to bare metal Node processes as possible
|
|
33
|
-
- Most of the arguments in the function should be required unless where sensible defaults make sense
|
package/src/tasks/find/README.md
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# Find a Folder
|
|
2
|
-
|
|
3
|
-
Find a folder recursively.
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
```javascript
|
|
8
|
-
import { findFolder } from "@/tasks";
|
|
9
|
-
|
|
10
|
-
async function main() {
|
|
11
|
-
const [, , folder] = process.argv;
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
const [err, data] = await findFolder(folder);
|
|
15
|
-
|
|
16
|
-
if (err) {
|
|
17
|
-
console.error(err);
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
console.log("Folders Found:\n");
|
|
22
|
-
console.table(data);
|
|
23
|
-
} catch (error) {
|
|
24
|
-
console.error("An error occurred:", error);
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
main();
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## API
|
|
33
|
-
|
|
34
|
-
### `findFolder(folder: string, location?: string)`
|
|
35
|
-
|
|
36
|
-
Returns an array of file paths that were found.
|
|
37
|
-
|
|
38
|
-
#### folder
|
|
39
|
-
|
|
40
|
-
The folder with which to search.
|
|
41
|
-
|
|
42
|
-
Type: `string`
|
|
43
|
-
|
|
44
|
-
#### location
|
|
45
|
-
|
|
46
|
-
The directory path to where to search for the folder.
|
|
47
|
-
|
|
48
|
-
Type: `string`
|
|
49
|
-
Default: `os.homedir()`
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { HOME } from "@/const";
|
|
2
|
-
import { find } from "@/tasks";
|
|
3
|
-
|
|
4
|
-
async function main() {
|
|
5
|
-
const [, , folder] = process.argv;
|
|
6
|
-
|
|
7
|
-
try {
|
|
8
|
-
const [err, data] = await find(folder, HOME, {});
|
|
9
|
-
|
|
10
|
-
if (err) {
|
|
11
|
-
console.error(err);
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
console.log("Folders Found:\n");
|
|
16
|
-
console.table(data);
|
|
17
|
-
} catch (error) {
|
|
18
|
-
console.error("An error occurred:", error);
|
|
19
|
-
process.exit(1);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
main();
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { type CLIOptions } from "@/cli";
|
|
4
|
-
import { HOME, SYSTEM_IGNORED_FOLDERS, type IgnoredFolders } from "@/const";
|
|
5
|
-
import { type Return } from "@/types";
|
|
6
|
-
import { $, config, log } from "@/utils";
|
|
7
|
-
|
|
8
|
-
export type Project = Record<string, string>;
|
|
9
|
-
export interface FindProjectsOpts extends CLIOptions {
|
|
10
|
-
maxSearchDepth?: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const FN = "find.projects";
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Searches directory for any Node.js project (by detecting package.json), ignoring hidden directories.
|
|
17
|
-
*
|
|
18
|
-
* @param {string} [location] - Directory path to search within.
|
|
19
|
-
* @param {FindProjectsOpts} [options] - Additional options for customization.
|
|
20
|
-
* @returns {Promise<Return<Project[]>>} An error or an array of locations with all of the file paths found.
|
|
21
|
-
*/
|
|
22
|
-
export async function findProjects(location?: string, options: FindProjectsOpts = {}): Promise<Return<Project[]>> {
|
|
23
|
-
log.data(FN, "arguments", { location, ...options }, options);
|
|
24
|
-
const start = performance.now();
|
|
25
|
-
const maxDepth: number = options?.maxSearchDepth || 5;
|
|
26
|
-
const results: Project[] = [];
|
|
27
|
-
const loc: string = location || HOME;
|
|
28
|
-
|
|
29
|
-
async function searchDirectory(cwd: string, depth: number = 0): Promise<void> {
|
|
30
|
-
log.process(FN, `searching in ${cwd}`, options);
|
|
31
|
-
let files: string[];
|
|
32
|
-
try {
|
|
33
|
-
files = await fs.readdir(cwd);
|
|
34
|
-
} catch {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
log.data(FN, "files", files, options);
|
|
39
|
-
for (const file of files) {
|
|
40
|
-
if (
|
|
41
|
-
file.startsWith(".") ||
|
|
42
|
-
SYSTEM_IGNORED_FOLDERS.includes(file as IgnoredFolders) ||
|
|
43
|
-
config.get("preferences.ignoredFolders").includes(file)
|
|
44
|
-
) {
|
|
45
|
-
continue; // skip all of these files, folders
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const filePath = path.join(cwd, file);
|
|
49
|
-
log.process(FN, `checking if ${filePath} is a directory`, options);
|
|
50
|
-
const [, dir] = await $.directory(filePath);
|
|
51
|
-
// if there are no files in it, its not a folder i care about
|
|
52
|
-
if (!dir?.files.length || !dir?.path) {
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
log.data(FN, "isValidDirectory", dir?.path ?? "", options);
|
|
57
|
-
|
|
58
|
-
if (dir.files.includes(path.join(dir.path, "package.json"))) {
|
|
59
|
-
// Node.js project detected
|
|
60
|
-
const pkg = await fs.readFile(path.join(dir.path, "package.json"), "utf-8");
|
|
61
|
-
const packageData = JSON.parse(pkg);
|
|
62
|
-
results.push({
|
|
63
|
-
name: packageData.name,
|
|
64
|
-
value: dir.path,
|
|
65
|
-
});
|
|
66
|
-
} else if (maxDepth === -1 || depth < maxDepth) {
|
|
67
|
-
await searchDirectory(filePath, depth + 1);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
await searchDirectory(loc);
|
|
73
|
-
|
|
74
|
-
const end = performance.now();
|
|
75
|
-
return [null, results, end - start];
|
|
76
|
-
}
|
package/src/tasks/find/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./find-project";
|
package/src/tasks/index.ts
DELETED