@repokit/core 1.3.6 → 1.5.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.
Files changed (38) hide show
  1. package/dist/CommandParser.d.ts +6 -0
  2. package/dist/CommandParser.js +55 -0
  3. package/dist/ConfigurationParser.d.ts +5 -0
  4. package/dist/ConfigurationParser.js +36 -0
  5. package/dist/RepoKitCommand.d.ts +8 -0
  6. package/dist/RepoKitCommand.js +16 -0
  7. package/dist/RepoKitConfig.d.ts +17 -0
  8. package/dist/RepoKitConfig.js +22 -0
  9. package/dist/TSCompiler.d.ts +2 -0
  10. package/dist/TSCompiler.js +11 -0
  11. package/dist/commands/parse_commands.d.ts +1 -0
  12. package/dist/commands/parse_commands.js +6 -0
  13. package/dist/commands/parse_configuration.d.ts +1 -0
  14. package/dist/commands/parse_configuration.js +4 -0
  15. package/dist/index.d.ts +3 -0
  16. package/dist/index.js +19 -0
  17. package/dist/types.d.ts +21 -0
  18. package/dist/types.js +2 -0
  19. package/externals/CommandParser.ts +5 -10
  20. package/externals/ConfigurationParser.ts +4 -3
  21. package/externals/TSCompiler.ts +8 -0
  22. package/externals/commands/parse_configuration.ts +1 -3
  23. package/installation/install.sh +6 -27
  24. package/internals/configuration/configuration.rs +11 -10
  25. package/internals/file_walker/walker.rs +4 -6
  26. package/internals/internal_commands/help.rs +8 -1
  27. package/internals/internal_commands/register_command.rs +6 -10
  28. package/internals/internal_commands/typescript_command.rs +17 -6
  29. package/internals/internal_filesystem/file_builder.rs +57 -0
  30. package/internals/internal_filesystem/internal_filesystem.rs +18 -7
  31. package/internals/internal_filesystem/mod.rs +1 -0
  32. package/internals/logger/logger.rs +31 -3
  33. package/internals/repokit/repokit.rs +7 -5
  34. package/internals/validations/command_validations.rs +1 -1
  35. package/package.json +25 -9
  36. package/externals/TaskPooler.ts +0 -31
  37. /package/externals/templates/{command_template.ts → command_template.txt} +0 -0
  38. /package/externals/templates/{configuration_template.ts → configuration_template.txt} +0 -0
@@ -0,0 +1,6 @@
1
+ import { TSCompiler } from "./TSCompiler";
2
+ export declare class CommandParser extends TSCompiler {
3
+ static parse(): Promise<void>;
4
+ private static parseCommand;
5
+ private static parsePaths;
6
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CommandParser = void 0;
4
+ const node_util_1 = require("node:util");
5
+ const node_path_1 = require("node:path");
6
+ const promises_1 = require("node:fs/promises");
7
+ const node_fs_1 = require("node:fs");
8
+ const TSCompiler_1 = require("./TSCompiler");
9
+ const RepoKitCommand_1 = require("./RepoKitCommand");
10
+ /* oxlint-disable typescript-eslint(no-misused-spread) */
11
+ class CommandParser extends TSCompiler_1.TSCompiler {
12
+ static async parse() {
13
+ const { paths, root } = this.parsePaths();
14
+ if (!root || !(0, node_fs_1.existsSync)(root) || !(await (0, promises_1.stat)(root)).isDirectory()) {
15
+ return console.log(JSON.stringify([]));
16
+ }
17
+ const pathList = paths.split(",").filter(Boolean);
18
+ const results = pathList.map(path => this.parseCommand((0, node_path_1.join)(root, path)));
19
+ console.log(JSON.stringify(results.flat()));
20
+ }
21
+ static parseCommand(path) {
22
+ const commands = [];
23
+ const declaredExports = require(path);
24
+ for (const key in declaredExports) {
25
+ if (declaredExports[key] instanceof RepoKitCommand_1.RepoKitCommand) {
26
+ commands.push({ ...declaredExports[key], location: path });
27
+ }
28
+ }
29
+ return commands;
30
+ }
31
+ static parsePaths() {
32
+ try {
33
+ return (0, node_util_1.parseArgs)({
34
+ options: {
35
+ paths: {
36
+ default: "",
37
+ multiple: false,
38
+ short: "p",
39
+ type: "string",
40
+ },
41
+ root: {
42
+ default: "",
43
+ multiple: false,
44
+ short: "r",
45
+ type: "string",
46
+ },
47
+ },
48
+ }).values;
49
+ }
50
+ catch {
51
+ return { paths: "", root: "" };
52
+ }
53
+ }
54
+ }
55
+ exports.CommandParser = CommandParser;
@@ -0,0 +1,5 @@
1
+ import { TSCompiler } from "./TSCompiler";
2
+ export declare class ConfigurationParser extends TSCompiler {
3
+ static parse(): void;
4
+ private static parseRoot;
5
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConfigurationParser = void 0;
4
+ const node_util_1 = require("node:util");
5
+ const node_path_1 = require("node:path");
6
+ const node_fs_1 = require("node:fs");
7
+ const TSCompiler_1 = require("./TSCompiler");
8
+ const RepoKitConfig_1 = require("./RepoKitConfig");
9
+ class ConfigurationParser extends TSCompiler_1.TSCompiler {
10
+ static parse() {
11
+ const root = this.parseRoot();
12
+ const path = (0, node_path_1.join)(root, "repokit.ts");
13
+ if (!(0, node_fs_1.existsSync)(path)) {
14
+ return;
15
+ }
16
+ const config = require(path);
17
+ for (const key in config) {
18
+ if (config[key] instanceof RepoKitConfig_1.RepoKitConfig) {
19
+ return console.log(JSON.stringify(config[key].toScoped(path)));
20
+ }
21
+ }
22
+ }
23
+ static parseRoot() {
24
+ return (0, node_util_1.parseArgs)({
25
+ options: {
26
+ root: {
27
+ default: "",
28
+ multiple: false,
29
+ short: "r",
30
+ type: "string",
31
+ },
32
+ },
33
+ }).values.root;
34
+ }
35
+ }
36
+ exports.ConfigurationParser = ConfigurationParser;
@@ -0,0 +1,8 @@
1
+ import type { ICommand, IRepoKitCommand } from "./types";
2
+ export declare class RepoKitCommand {
3
+ name: string;
4
+ owner: string;
5
+ description: string;
6
+ commands: Record<string, ICommand>;
7
+ constructor({ name, description, owner, commands, }: IRepoKitCommand);
8
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RepoKitCommand = void 0;
4
+ class RepoKitCommand {
5
+ name;
6
+ owner;
7
+ description;
8
+ commands;
9
+ constructor({ name, description, owner = "", commands = {}, }) {
10
+ this.name = name;
11
+ this.owner = owner;
12
+ this.commands = commands;
13
+ this.description = description;
14
+ }
15
+ }
16
+ exports.RepoKitCommand = RepoKitCommand;
@@ -0,0 +1,17 @@
1
+ import type { ICommand, IRepoKitConfig } from "./types";
2
+ import { RepoKitCommand } from "./RepoKitCommand";
3
+ export declare class RepoKitConfig implements Required<IRepoKitConfig> {
4
+ project: string;
5
+ thirdParty: RepoKitCommand[];
6
+ commands: Record<string, ICommand>;
7
+ constructor({ project, commands, thirdParty }: IRepoKitConfig);
8
+ toScoped(location: string): this & {
9
+ thirdParty: {
10
+ location: string;
11
+ name: string;
12
+ owner: string;
13
+ description: string;
14
+ commands: Record<string, ICommand>;
15
+ }[];
16
+ };
17
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RepoKitConfig = void 0;
4
+ const RepoKitCommand_1 = require("./RepoKitCommand");
5
+ /* eslint-disable typescript-eslint(no-misused-spread */
6
+ class RepoKitConfig {
7
+ project;
8
+ thirdParty;
9
+ commands;
10
+ constructor({ project, commands = {}, thirdParty = [] }) {
11
+ this.project = project;
12
+ this.commands = commands;
13
+ this.thirdParty = thirdParty.map(command => new RepoKitCommand_1.RepoKitCommand(command));
14
+ }
15
+ toScoped(location) {
16
+ return {
17
+ ...this,
18
+ thirdParty: this.thirdParty.map(command => ({ ...command, location })),
19
+ };
20
+ }
21
+ }
22
+ exports.RepoKitConfig = RepoKitConfig;
@@ -0,0 +1,2 @@
1
+ export declare class TSCompiler {
2
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TSCompiler = void 0;
4
+ const ts_node_1 = require("ts-node");
5
+ class TSCompiler {
6
+ static {
7
+ const service = (0, ts_node_1.register)();
8
+ service.enabled(true);
9
+ }
10
+ }
11
+ exports.TSCompiler = TSCompiler;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const CommandParser_1 = require("../CommandParser");
4
+ void (async () => {
5
+ await CommandParser_1.CommandParser.parse();
6
+ })();
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const ConfigurationParser_1 = require("../ConfigurationParser");
4
+ ConfigurationParser_1.ConfigurationParser.parse();
@@ -0,0 +1,3 @@
1
+ export * from "./RepoKitConfig";
2
+ export * from "./RepoKitCommand";
3
+ export * from "./types";
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./RepoKitConfig"), exports);
18
+ __exportStar(require("./RepoKitCommand"), exports);
19
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,21 @@
1
+ import type { RepoKitCommand } from "./RepoKitCommand";
2
+ export interface IRepoKitConfig {
3
+ project: string;
4
+ thirdParty?: RepoKitCommand[];
5
+ commands?: Record<string, ICommand>;
6
+ }
7
+ export interface IRepoKitCommand {
8
+ name: string;
9
+ owner?: string;
10
+ description: string;
11
+ commands: Record<string, ICommand>;
12
+ }
13
+ export interface ICommand {
14
+ command: string;
15
+ description: string;
16
+ args?: Record<string, string>;
17
+ }
18
+ export interface ILocatedCommand extends IRepoKitCommand {
19
+ location: string;
20
+ }
21
+ export type AsyncTask<T> = () => Promise<T>;
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -4,29 +4,24 @@ import { stat } from "node:fs/promises";
4
4
  import { existsSync } from "node:fs";
5
5
 
6
6
  import type { ILocatedCommand } from "./types";
7
- import { TaskPooler } from "./TaskPooler";
7
+ import { TSCompiler } from "./TSCompiler";
8
8
  import { RepoKitCommand } from "./RepoKitCommand";
9
9
  /* oxlint-disable typescript-eslint(no-misused-spread) */
10
10
 
11
- export class CommandParser {
11
+ export class CommandParser extends TSCompiler {
12
12
  public static async parse() {
13
13
  const { paths, root } = this.parsePaths();
14
14
  if (!root || !existsSync(root) || !(await stat(root)).isDirectory()) {
15
15
  return console.log(JSON.stringify([]));
16
16
  }
17
17
  const pathList = paths.split(",").filter(Boolean);
18
- const pool = new TaskPooler<ILocatedCommand[]>();
19
- const results = await Promise.all(
20
- pathList.map(path =>
21
- pool.enqueue(() => this.parseCommand(join(root, path))),
22
- ),
23
- );
18
+ const results = pathList.map(path => this.parseCommand(join(root, path)));
24
19
  console.log(JSON.stringify(results.flat()));
25
20
  }
26
21
 
27
- private static async parseCommand(path: string) {
22
+ private static parseCommand(path: string) {
28
23
  const commands: ILocatedCommand[] = [];
29
- const declaredExports = await import(path);
24
+ const declaredExports = require(path);
30
25
  for (const key in declaredExports) {
31
26
  if (declaredExports[key] instanceof RepoKitCommand) {
32
27
  commands.push({ ...declaredExports[key], location: path });
@@ -2,16 +2,17 @@ import { parseArgs } from "node:util";
2
2
  import { join } from "node:path";
3
3
  import { existsSync } from "node:fs";
4
4
 
5
+ import { TSCompiler } from "./TSCompiler";
5
6
  import { RepoKitConfig } from "./RepoKitConfig";
6
7
 
7
- export class ConfigurationParser {
8
- public static async parse() {
8
+ export class ConfigurationParser extends TSCompiler {
9
+ public static parse() {
9
10
  const root = this.parseRoot();
10
11
  const path = join(root, "repokit.ts");
11
12
  if (!existsSync(path)) {
12
13
  return;
13
14
  }
14
- const config = await import(path);
15
+ const config = require(path);
15
16
  for (const key in config) {
16
17
  if (config[key] instanceof RepoKitConfig) {
17
18
  return console.log(JSON.stringify(config[key].toScoped(path)));
@@ -0,0 +1,8 @@
1
+ import { register } from "ts-node";
2
+
3
+ export class TSCompiler {
4
+ static {
5
+ const service = register();
6
+ service.enabled(true);
7
+ }
8
+ }
@@ -1,5 +1,3 @@
1
1
  import { ConfigurationParser } from "../ConfigurationParser";
2
2
 
3
- void (async () => {
4
- await ConfigurationParser.parse();
5
- })();
3
+ ConfigurationParser.parse();
@@ -1,20 +1,16 @@
1
1
 
2
2
  CWD=$(pwd)
3
3
 
4
- MODULE_DIRECTORY="node_modules"
5
-
6
- if [[ "$CWD" != *"$MODULE_DIRECTORY"* ]]; then
7
- yarn symlink;
8
- exit 0;
9
- fi
10
-
11
4
  REPLACEMENT="/node_modules"
12
5
  FALLBACK_ROOT="${CWD%${REPLACEMENT}*}"
13
6
 
14
- SCRIPT_ORIGIN=$(pwd)
15
7
  GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
16
8
  REPO_ROOT=${GIT_ROOT:-$FALLBACK_ROOT}
17
9
 
10
+ if [[ "$CWD" != *"$REPLACEMENT"* ]]; then
11
+ exit 0;
12
+ fi
13
+
18
14
 
19
15
  cd $REPO_ROOT
20
16
 
@@ -29,28 +25,11 @@ else
29
25
  curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
30
26
  fi
31
27
 
32
- if npm list --depth=0 tsx; then
33
- echo "Found tsx installation"
34
- else
35
- # Node Dependencies installation
36
- if [ -f "${REPO_ROOT}/yarn.lock" ]; then
37
- yarn global add tsx
38
- elif [ -f "${REPO_ROOT}/pnpm-lock.yaml" ]; then
39
- pnpm add -g tsx
40
- elif [ -f "${REPO_ROOT}/package-lock.json" ]; then
41
- npm i -g tsx
42
- else
43
- echo "No node.js package manager detected"
44
- echo "Run npm init to create your node.js project"
45
- fi
46
- fi
47
-
48
-
49
28
  echo "Installing Repokit CLI"
50
29
 
51
- cd $SCRIPT_ORIGIN
30
+ cd $CWD
52
31
 
53
- echo "Compiling from $SCRIPT_ORIGIN"
32
+ echo "Compiling from $CWD"
54
33
 
55
34
  . "$HOME/.cargo/env"
56
35
  RUSTFLAGS="-Awarnings" cargo build --release
@@ -1,23 +1,24 @@
1
- use std::{fs::File, io, path::Path, process::exit};
1
+ use std::{path::Path, process::exit};
2
2
 
3
- use crate::{internal_filesystem::internal_filesystem::InternalFileSystem, logger::logger::Logger};
3
+ use crate::{
4
+ internal_filesystem::{file_builder::FileBuilder, internal_filesystem::InternalFileSystem},
5
+ logger::logger::Logger,
6
+ };
4
7
 
5
8
  pub struct Configuration;
6
9
 
7
10
  impl Configuration {
8
11
  pub fn create(root: &str) {
9
12
  let file_path = format!("{root}/repokit.ts");
10
- let path_buf = Path::new(&file_path);
11
- if path_buf.exists() {
13
+ let path = Path::new(&file_path);
14
+ if path.exists() {
12
15
  return;
13
16
  }
14
17
  Configuration::welcome();
15
- let template_path =
16
- InternalFileSystem::new(root).resolve_template("configuration_template.ts");
17
- let mut source = File::open(template_path).expect("Template");
18
- let mut target = File::create(path_buf).expect("creating");
19
- io::copy(&mut source, &mut target).expect("writing");
20
- target.sync_all().expect("Flushing");
18
+ let mut source =
19
+ InternalFileSystem::new(root).resolve_template("configuration_template.txt");
20
+ let mut target = FileBuilder::create(path, |_| Logger::file_create_error());
21
+ FileBuilder::copy_to(&mut source, &mut target, |_| Logger::file_write_error());
21
22
  Logger::info(
22
23
  format!(
23
24
  "Please fill out this file with your desired settings. Then run {}",
@@ -7,6 +7,8 @@ use std::{
7
7
  use ignore::{DirEntry, Error, ParallelVisitor, ParallelVisitorBuilder, WalkState};
8
8
  use regex::Regex;
9
9
 
10
+ use crate::{internal_filesystem::file_builder::FileBuilder, logger::logger::Logger};
11
+
10
12
  pub struct TSFileVisitor {
11
13
  root: String,
12
14
  paths: Arc<Mutex<Vec<String>>>,
@@ -17,16 +19,12 @@ impl ParallelVisitor for TSFileVisitor {
17
19
  let root_replacer = format!("{}/", self.root);
18
20
  let repokit_import_matcher =
19
21
  Regex::new(r#"(require\(|from[\s*]?)['"]@repokit/core["'][\)]?[;]?$"#).unwrap();
20
- let template_matcher = Regex::new(r"externals\/templates\/[^\s]*?_template\.ts$").unwrap();
21
22
  if let Ok(entry) = entry {
22
23
  let path = entry.path();
23
24
  let path_string = path.to_str().map_or("", |f| f);
24
- if entry.file_type().is_some_and(|ft| ft.is_file())
25
- && path_string.ends_with(".ts")
26
- && !template_matcher.is_match(path_string)
27
- {
25
+ if entry.file_type().is_some_and(|ft| ft.is_file()) && path_string.ends_with(".ts") {
28
26
  let mut open_comment = false;
29
- let file: File = File::open(path).expect("file");
27
+ let file: File = FileBuilder::open(path_string, |_| Logger::open_file_error());
30
28
  let reader: BufReader<File> = BufReader::new(file);
31
29
  for line_result in reader.lines() {
32
30
  let unwrapped = line_result.unwrap();
@@ -145,7 +145,14 @@ impl Help {
145
145
  sort_str_slice(&mut vector);
146
146
  vector
147
147
  .iter()
148
- .map(|&name| RootCommand::from(name, commands.get(name).expect("known keys only")))
148
+ .map(|&name| {
149
+ RootCommand::from(
150
+ name,
151
+ commands
152
+ .get(name)
153
+ .expect("iteration is over known keys only"),
154
+ )
155
+ })
149
156
  .collect()
150
157
  }
151
158
  }
@@ -1,8 +1,6 @@
1
1
  use normalize_path::NormalizePath;
2
2
  use std::{
3
3
  collections::HashMap,
4
- fs::{File, create_dir_all},
5
- io,
6
4
  path::{Path, PathBuf},
7
5
  process,
8
6
  };
@@ -15,7 +13,7 @@ use crate::{
15
13
  },
16
14
  },
17
15
  internal_commands::help::Help,
18
- internal_filesystem::internal_filesystem::InternalFileSystem,
16
+ internal_filesystem::{file_builder::FileBuilder, internal_filesystem::InternalFileSystem},
19
17
  logger::logger::Logger,
20
18
  };
21
19
 
@@ -56,7 +54,7 @@ impl RegisterCommand {
56
54
  )
57
55
  .as_str(),
58
56
  );
59
- create_dir_all(&path).expect("");
57
+ FileBuilder::create_dir_all(&path, |_| Logger::file_directory_error());
60
58
  }
61
59
  if !path.is_dir() {
62
60
  RegisterCommand::exit_on_missing_path();
@@ -90,12 +88,10 @@ impl InternalExecutable for RegisterCommand {
90
88
  fn run(&self, args: Vec<String>, _: &HashMap<String, Box<dyn InternalExecutable>>) {
91
89
  Logger::info("Registering a new command");
92
90
  let command_path = self.validate_path(args);
93
- let template_path =
94
- InternalFileSystem::new(&self.scope.root).resolve_template("command_template.ts");
95
- let mut source = File::open(template_path).expect("Template");
96
- let mut target = File::create(&command_path).expect("creating");
97
- io::copy(&mut source, &mut target).expect("writing");
98
- target.sync_all().expect("Flushing");
91
+ let mut source =
92
+ InternalFileSystem::new(&self.scope.root).resolve_template("command_template.txt");
93
+ let mut target = FileBuilder::create(&command_path, |_| Logger::file_create_error());
94
+ FileBuilder::copy_to(&mut source, &mut target, |_| Logger::file_write_error());
99
95
  Logger::info("Creating command file");
100
96
  Logger::info("Please fill out your command file located at:");
101
97
  Logger::log_file_path(command_path.to_str().expect("path"));
@@ -1,4 +1,4 @@
1
- use std::{path::Path, sync::MutexGuard};
1
+ use std::{path::Path, process::exit, sync::MutexGuard};
2
2
 
3
3
  use serde_json::from_str;
4
4
 
@@ -6,6 +6,7 @@ use crate::{
6
6
  configuration::configuration::Configuration,
7
7
  executor::executor::Executor,
8
8
  internal_filesystem::internal_filesystem::InternalFileSystem,
9
+ logger::logger::Logger,
9
10
  repokit::interfaces::{RepoKitCommand, RepoKitConfig},
10
11
  };
11
12
 
@@ -22,7 +23,7 @@ impl TypescriptCommand {
22
23
 
23
24
  pub fn parse_configuration(&self) -> RepoKitConfig {
24
25
  let executable =
25
- InternalFileSystem::new(&self.root).resolve_command("parse_configuration.ts");
26
+ InternalFileSystem::new(&self.root).resolve_command("parse_configuration.js");
26
27
  let stdout = self.execute(format!("{executable} --root {}", &self.root).as_str());
27
28
  if stdout.is_empty() {
28
29
  Configuration::create(&self.root);
@@ -33,15 +34,25 @@ impl TypescriptCommand {
33
34
 
34
35
  pub fn parse_commands(&self, path_list: &MutexGuard<Vec<String>>) -> Vec<RepoKitCommand> {
35
36
  let paths = path_list.join(",");
36
- let executable = InternalFileSystem::new(&self.root).resolve_command("parse_commands.ts");
37
+ let executable = InternalFileSystem::new(&self.root).resolve_command("parse_commands.js");
37
38
  let stdout =
38
39
  self.execute(format!("{executable} --paths {paths} --root {}", self.root).as_str());
39
- let commands: Vec<RepoKitCommand> = serde_json::from_str(&stdout).expect("parse");
40
- commands
40
+ let result: Result<Vec<RepoKitCommand>, serde_json::Error> = serde_json::from_str(&stdout);
41
+ match result {
42
+ Ok(commands) => commands,
43
+ Err(_) => {
44
+ Logger::info("There was an error parsing one of your commands");
45
+ Logger::info(
46
+ "You can validate a command file's syntactical correctness by running",
47
+ );
48
+ Logger::log_file_path("tsc --noEmit");
49
+ exit(0);
50
+ }
51
+ }
41
52
  }
42
53
 
43
54
  fn execute(&self, args: &str) -> String {
44
- Executor::exec(format!("npx tsx {args}"), |cmd| {
55
+ Executor::exec(format!("node {args}"), |cmd| {
45
56
  cmd.current_dir(Path::new(&self.root))
46
57
  })
47
58
  }
@@ -0,0 +1,57 @@
1
+ use std::{
2
+ fs::{File, create_dir_all},
3
+ io::{Error, copy},
4
+ path::Path,
5
+ process::exit,
6
+ };
7
+
8
+ pub struct FileBuilder;
9
+
10
+ impl FileBuilder {
11
+ pub fn open(source: &str, on_error: impl Fn(Error)) -> File {
12
+ let source = File::open(source);
13
+ match source {
14
+ Ok(file) => file,
15
+ Err(error) => {
16
+ on_error(error);
17
+ exit(0);
18
+ }
19
+ }
20
+ }
21
+
22
+ pub fn create(destination: &Path, on_error: impl Fn(Error)) -> File {
23
+ let source = File::create(destination);
24
+ match source {
25
+ Ok(file) => file,
26
+ Err(error) => {
27
+ on_error(error);
28
+ exit(0);
29
+ }
30
+ }
31
+ }
32
+
33
+ pub fn copy_to(source: &mut File, target: &mut File, on_error: impl Fn(Error)) {
34
+ let result = copy(source, target);
35
+ match result {
36
+ Ok(_) => {
37
+ let sync = target.sync_all();
38
+ match sync {
39
+ Ok(_) => {}
40
+ Err(err) => on_error(err),
41
+ }
42
+ }
43
+ Err(err) => on_error(err),
44
+ }
45
+ }
46
+
47
+ pub fn create_dir_all(path: &Path, on_error: impl Fn(Error)) {
48
+ let source = create_dir_all(path);
49
+ match source {
50
+ Ok(result) => result,
51
+ Err(error) => {
52
+ on_error(error);
53
+ exit(0);
54
+ }
55
+ }
56
+ }
57
+ }
@@ -1,7 +1,13 @@
1
1
  use normalize_path::NormalizePath;
2
- use std::path::{Path, PathBuf};
2
+ use std::{
3
+ fs::File,
4
+ path::{Path, PathBuf},
5
+ };
3
6
 
4
- use crate::{executor::executor::Executor, logger::logger::Logger};
7
+ use crate::{
8
+ executor::executor::Executor, internal_filesystem::file_builder::FileBuilder,
9
+ logger::logger::Logger,
10
+ };
5
11
 
6
12
  pub struct InternalFileSystem {
7
13
  root: String,
@@ -23,8 +29,13 @@ impl InternalFileSystem {
23
29
  self.path_buf_to_str(self.commands_directory().join(file_name))
24
30
  }
25
31
 
26
- pub fn resolve_template(&self, file_name: &str) -> String {
27
- self.path_buf_to_str(self.templates_directory().join(file_name))
32
+ pub fn resolve_template(&self, file_name: &str) -> File {
33
+ let path = self.path_buf_to_str(self.templates_directory().join(file_name));
34
+ FileBuilder::open(&path, |_| {
35
+ Logger::error(format!("Unable to locate internal {file_name}").as_str());
36
+ Logger::error("Please file a bug here");
37
+ Logger::log_issue_link();
38
+ })
28
39
  }
29
40
 
30
41
  pub fn find_root() -> String {
@@ -45,15 +56,15 @@ impl InternalFileSystem {
45
56
  }
46
57
 
47
58
  fn commands_directory(&self) -> PathBuf {
48
- self.absolute(format!("{}/commands", self.package_directory()).as_str())
59
+ self.absolute(format!("{}/dist/commands", self.package_directory()).as_str())
49
60
  }
50
61
 
51
62
  fn templates_directory(&self) -> PathBuf {
52
- self.absolute(format!("{}/templates", self.package_directory()).as_str())
63
+ self.absolute(format!("{}/externals/templates", self.package_directory()).as_str())
53
64
  }
54
65
 
55
66
  fn package_directory(&self) -> String {
56
- format!("./node_modules/{}/externals", self.package_name())
67
+ format!("./node_modules/{}", self.package_name())
57
68
  }
58
69
 
59
70
  fn package_name(&self) -> String {
@@ -1 +1,2 @@
1
+ pub mod file_builder;
1
2
  pub mod internal_filesystem;
@@ -1,5 +1,6 @@
1
+ use std::process::exit;
2
+ use std::sync::LazyLock;
1
3
  use std::sync::Mutex;
2
- use std::{process, sync::LazyLock};
3
4
 
4
5
  use colored::{ColoredString, Colorize, CustomColor};
5
6
 
@@ -23,12 +24,12 @@ impl Logger {
23
24
 
24
25
  pub fn exit_with_info(message: &str) {
25
26
  Logger::info(message);
26
- process::exit(0);
27
+ exit(0);
27
28
  }
28
29
 
29
30
  pub fn exit_with_error(message: &str) {
30
31
  Logger::error(message);
31
- process::exit(0);
32
+ exit(0);
32
33
  }
33
34
 
34
35
  pub fn space_around(message: &str) {
@@ -92,6 +93,33 @@ impl Logger {
92
93
  })
93
94
  }
94
95
 
96
+ pub fn file_create_error() {
97
+ Logger::file_error("create a file");
98
+ }
99
+
100
+ pub fn file_directory_error() {
101
+ Logger::file_error("create a directory");
102
+ }
103
+
104
+ pub fn open_file_error() {
105
+ Logger::file_error("read a file");
106
+ }
107
+
108
+ pub fn file_write_error() {
109
+ Logger::file_error("write to a file");
110
+ }
111
+
112
+ pub fn log_issue_link() {
113
+ Logger::log_file_path("https://github.com/alexfigliolia/repokit/issues");
114
+ }
115
+
116
+ fn file_error(operation: &str) {
117
+ Logger::info(format!("I was unable to {operation} in your repository").as_str());
118
+ Logger::error("Please verify the permissions on your working directory or file a bug here");
119
+ Logger::log_issue_link();
120
+ exit(0);
121
+ }
122
+
95
123
  fn info_prefix() -> ColoredString {
96
124
  format!("{}: ", *REGISTERED_NAME.lock().unwrap())
97
125
  .bright_magenta()
@@ -36,7 +36,7 @@ impl RepoKit {
36
36
  let validator = CommandValidations::from(self);
37
37
  let internals = validator.collect_and_validate_internals();
38
38
  if internals.contains_key(&command) {
39
- let interface = internals.get(&command).expect("exists");
39
+ let interface = internals.get(&command).expect("Unknown command");
40
40
  return interface.run(args, &internals);
41
41
  }
42
42
  if self.scope.configuration.commands.contains_key(&command) {
@@ -45,7 +45,7 @@ impl RepoKit {
45
45
  .configuration
46
46
  .commands
47
47
  .get(&command)
48
- .expect("exists");
48
+ .expect("Unknown command");
49
49
  return Executor::with_stdio(
50
50
  format!("{} {}", root_script.command, &args.join(" ")),
51
51
  |cmd| cmd.current_dir(Path::new(&self.scope.root)),
@@ -56,14 +56,16 @@ impl RepoKit {
56
56
  &internals, &externals,
57
57
  );
58
58
  if externals.contains_key(&command) {
59
- let interface = externals.get(&command).expect("exists");
59
+ let interface = externals.get(&command).expect("Unknown command");
60
60
  if args.is_empty() {
61
61
  return self.log_external_command(interface);
62
62
  }
63
63
  let sub_command = &args[0];
64
64
  if interface.commands.contains_key(sub_command) {
65
- let script = interface.commands.get(sub_command).expect("exists");
66
- let working_dir = Path::new(&interface.location).parent().expect("exists");
65
+ let script = interface.commands.get(sub_command).expect("Unknown script");
66
+ let working_dir = Path::new(&interface.location)
67
+ .parent()
68
+ .expect("Working directory not found");
67
69
  return Executor::with_stdio(
68
70
  format!("{} {}", &script.command, &args[1..].join(" ")),
69
71
  |cmd| cmd.current_dir(working_dir),
@@ -98,7 +98,7 @@ impl CommandValidations {
98
98
  let mut map: HashMap<String, RepoKitCommand> = HashMap::new();
99
99
  for command in externals {
100
100
  if map.contains_key(&command.name) {
101
- let original = map.get(&command.name).expect("existent key");
101
+ let original = map.get(&command.name).expect("Unknown command");
102
102
  self.on_external_duplicate_collision(command, &original.location);
103
103
  }
104
104
  map.insert(command.name.clone(), command.clone());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@repokit/core",
3
- "version": "1.3.6",
3
+ "version": "1.5.0",
4
4
  "description": "A knowledgebase for your repository - wrapped in a CLI",
5
5
  "keywords": [
6
6
  "cli",
@@ -15,31 +15,47 @@
15
15
  "type": "git",
16
16
  "url": "git+https://github.com/alexfigliolia/repokit.git"
17
17
  },
18
- "main": "externals/index.ts",
18
+ "files": [
19
+ "media",
20
+ "dist",
21
+ "externals",
22
+ "internals",
23
+ "installation",
24
+ "Cargo.lock",
25
+ "Cargo.toml"
26
+ ],
27
+ "main": "./dist/index.js",
28
+ "module": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
19
30
  "publishConfig": {
20
31
  "access": "public"
21
32
  },
22
33
  "scripts": {
23
34
  "build:all": "yarn lint:ts && yarn lint:rust && yarn install:rust",
24
35
  "build:rust": "cargo build --release",
36
+ "build:ts": "tsx devserver/run.ts",
37
+ "clean:ts": "tsx devserver/run.ts -c",
25
38
  "grant:exec": "chmod -R +x ./installation",
26
39
  "install:rust": "yarn build:rust && cargo install --path .",
27
40
  "lint:rust": "cargo clippy --fix --allow-dirty",
28
41
  "lint:ts": "yarn oxlint --type-aware --type-check --report-unused-disable-directives --fix && yarn oxfmt",
42
+ "package:ts": "yarn lint:ts && yarn lint:rust && yarn build:ts",
29
43
  "postinstall": "yarn grant:exec && ./installation/install.sh",
30
44
  "repokit": "yarn install:rust && repokit",
31
- "run:dev": "yarn symlink && cargo run --package repokit --bin repokit",
32
- "symlink": "yarn grant:exec && ./installation/symlink.sh"
45
+ "run:dev": "yarn build:ts && cargo run --package repokit --bin repokit",
46
+ "watch:ts": "tsx devserver/run.ts -w"
33
47
  },
34
48
  "dependencies": {
35
- "@figliolia/event-emitter": "^1.1.6",
36
- "tsx": "^4.21.0"
49
+ "@figliolia/event-emitter": "^1.2.0",
50
+ "ts-node": "^10.9.2"
37
51
  },
38
52
  "devDependencies": {
39
- "@types/node": "^25.2.1",
40
- "oxfmt": "^0.33.0",
53
+ "@figliolia/child-process": "^1.0.4",
54
+ "@types/node": "^25.3.0",
55
+ "oxfmt": "^0.34.0",
41
56
  "oxlint": "^1.42.0",
42
- "oxlint-tsgolint": "^0.13.0",
57
+ "oxlint-tsgolint": "^0.14.0",
58
+ "tsx": "^4.21.0",
43
59
  "typescript": "^5.9.3"
44
60
  }
45
61
  }
@@ -1,31 +0,0 @@
1
- import { AutoIncrementingID } from "@figliolia/event-emitter";
2
-
3
- import type { AsyncTask } from "./types";
4
-
5
- export class TaskPooler<T> {
6
- private readonly IDs = new AutoIncrementingID();
7
- private readonly runningTasks = new Map<string, Promise<T>>();
8
- constructor(public maxSize = 10) {}
9
-
10
- public enqueue(task: AsyncTask<T>) {
11
- return new Promise<T>(resolve => {
12
- if (this.runningTasks.size < 10) {
13
- return resolve(this.indexRunningTask(task));
14
- }
15
- resolve(this.indexBehindNextOpening(task));
16
- });
17
- }
18
-
19
- private indexRunningTask(task: AsyncTask<T>) {
20
- const ID = this.IDs.get();
21
- const promise = task();
22
- this.runningTasks.set(ID, promise);
23
- void promise.finally(() => this.runningTasks.delete(ID));
24
- return promise;
25
- }
26
-
27
- private async indexBehindNextOpening(task: AsyncTask<T>) {
28
- await Promise.race(this.runningTasks.values());
29
- return this.indexRunningTask(task);
30
- }
31
- }