@repokit/core 4.0.3 → 4.0.5

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/Cargo.lock CHANGED
@@ -121,6 +121,12 @@ version = "0.6.9"
121
121
  source = "registry+https://github.com/rust-lang/crates.io-index"
122
122
  checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
123
123
 
124
+ [[package]]
125
+ name = "bytes"
126
+ version = "1.11.1"
127
+ source = "registry+https://github.com/rust-lang/crates.io-index"
128
+ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
129
+
124
130
  [[package]]
125
131
  name = "cfg-if"
126
132
  version = "1.0.4"
@@ -170,7 +176,7 @@ dependencies = [
170
176
  "bitflags 1.3.2",
171
177
  "crossterm_winapi",
172
178
  "libc",
173
- "mio",
179
+ "mio 0.7.14",
174
180
  "parking_lot 0.11.2",
175
181
  "signal-hook",
176
182
  "signal-hook-mio",
@@ -685,6 +691,17 @@ dependencies = [
685
691
  "winapi",
686
692
  ]
687
693
 
694
+ [[package]]
695
+ name = "mio"
696
+ version = "1.2.0"
697
+ source = "registry+https://github.com/rust-lang/crates.io-index"
698
+ checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
699
+ dependencies = [
700
+ "libc",
701
+ "wasi",
702
+ "windows-sys",
703
+ ]
704
+
688
705
  [[package]]
689
706
  name = "miow"
690
707
  version = "0.3.7"
@@ -996,7 +1013,7 @@ checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
996
1013
 
997
1014
  [[package]]
998
1015
  name = "repokit"
999
- version = "4.0.3"
1016
+ version = "4.0.5"
1000
1017
  dependencies = [
1001
1018
  "alphanumeric-sort",
1002
1019
  "colored",
@@ -1010,6 +1027,7 @@ dependencies = [
1010
1027
  "serde_json",
1011
1028
  "shellexpand",
1012
1029
  "terminal-spinners",
1030
+ "tokio",
1013
1031
  ]
1014
1032
 
1015
1033
  [[package]]
@@ -1138,7 +1156,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1138
1156
  checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc"
1139
1157
  dependencies = [
1140
1158
  "libc",
1141
- "mio",
1159
+ "mio 0.7.14",
1142
1160
  "signal-hook",
1143
1161
  ]
1144
1162
 
@@ -1164,6 +1182,16 @@ version = "1.15.1"
1164
1182
  source = "registry+https://github.com/rust-lang/crates.io-index"
1165
1183
  checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
1166
1184
 
1185
+ [[package]]
1186
+ name = "socket2"
1187
+ version = "0.6.3"
1188
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1189
+ checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
1190
+ dependencies = [
1191
+ "libc",
1192
+ "windows-sys",
1193
+ ]
1194
+
1167
1195
  [[package]]
1168
1196
  name = "stable_deref_trait"
1169
1197
  version = "1.2.1"
@@ -1275,6 +1303,34 @@ dependencies = [
1275
1303
  "zerovec",
1276
1304
  ]
1277
1305
 
1306
+ [[package]]
1307
+ name = "tokio"
1308
+ version = "1.52.1"
1309
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1310
+ checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6"
1311
+ dependencies = [
1312
+ "bytes",
1313
+ "libc",
1314
+ "mio 1.2.0",
1315
+ "parking_lot 0.12.5",
1316
+ "pin-project-lite",
1317
+ "signal-hook-registry",
1318
+ "socket2",
1319
+ "tokio-macros",
1320
+ "windows-sys",
1321
+ ]
1322
+
1323
+ [[package]]
1324
+ name = "tokio-macros"
1325
+ version = "2.7.0"
1326
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1327
+ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
1328
+ dependencies = [
1329
+ "proc-macro2",
1330
+ "quote",
1331
+ "syn",
1332
+ ]
1333
+
1278
1334
  [[package]]
1279
1335
  name = "unicode-general-category"
1280
1336
  version = "1.1.0"
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "repokit"
3
- version = "4.0.3"
3
+ version = "4.0.5"
4
4
  edition = "2024"
5
5
 
6
6
  [[bin]]
@@ -20,3 +20,4 @@ shellexpand = "3.1.2"
20
20
  schemars = "1.2.1"
21
21
  terminal-spinners = "0.3.2"
22
22
  futures = "0.3.32"
23
+ tokio = { version = "1.52.1", features = ["full"] }
@@ -1,17 +1,16 @@
1
1
  import { RepoKitCommand } from "./RepoKitCommand.mjs";
2
2
  import { TSCompiler } from "./TSCompiler.mjs";
3
- import { existsSync } from "node:fs";
4
- import { stat } from "node:fs/promises";
5
- import { join } from "node:path";
6
3
  import { parseArgs } from "node:util";
4
+ import { join } from "node:path";
5
+ import { stat } from "node:fs/promises";
6
+ import { existsSync } from "node:fs";
7
7
  //#region externals/CommandParser.ts
8
8
  var CommandParser = class extends TSCompiler {
9
- static async parse() {
9
+ static parse = this.wrapParsingOperation(async () => {
10
10
  const { paths, root } = this.parsePaths();
11
- if (!root || !existsSync(root) || !(await stat(root)).isDirectory()) return console.log(JSON.stringify([]));
12
- const commands = paths.split(",").filter(Boolean).map((path) => this.parseCommand(root, path));
13
- console.log(JSON.stringify(commands.flat()));
14
- }
11
+ if (!root || !existsSync(root) || !(await stat(root)).isDirectory()) return [];
12
+ return paths.split(",").filter(Boolean).map((path) => this.parseCommand(root, path)).flat();
13
+ });
15
14
  static parseCommand(root, path) {
16
15
  const commands = [];
17
16
  const declaredExports = super.compile(join(root, path));
@@ -1,16 +1,16 @@
1
1
  import { RepoKitConfig } from "./RepoKitConfig.mjs";
2
2
  import { TSCompiler } from "./TSCompiler.mjs";
3
- import { existsSync } from "node:fs";
4
- import { join } from "node:path";
5
3
  import { parseArgs } from "node:util";
4
+ import { join } from "node:path";
5
+ import { existsSync } from "node:fs";
6
6
  //#region externals/ConfigurationParser.ts
7
7
  var ConfigurationParser = class extends TSCompiler {
8
- static parse() {
8
+ static parse = this.wrapParsingOperation(() => {
9
9
  const path = join(this.parseRoot(), "repokit.ts");
10
10
  if (!existsSync(path)) return;
11
11
  const config = super.compile(path);
12
- for (const key in config) if (config[key] instanceof RepoKitConfig) return console.log(JSON.stringify(config[key].toScoped(path)));
13
- }
12
+ for (const key in config) if (config[key] instanceof RepoKitConfig) return config[key].toScoped(path);
13
+ });
14
14
  static parseRoot() {
15
15
  return parseArgs({ options: { root: {
16
16
  default: "",
@@ -4,7 +4,7 @@ var RepoKitCommand = class {
4
4
  owner;
5
5
  description;
6
6
  commands;
7
- constructor({ name, description, owner = "", commands = {} }) {
7
+ constructor({ name, description, owner = "", commands }) {
8
8
  this.name = name;
9
9
  this.owner = owner;
10
10
  this.commands = commands;
@@ -2,6 +2,7 @@ import { __require } from "./_virtual/_rolldown/runtime.mjs";
2
2
  import { register } from "ts-node";
3
3
  //#region externals/TSCompiler.ts
4
4
  var TSCompiler = class {
5
+ static PARSE_INDICATOR = "=============== REPOKIT PARSE FLAG ===============";
5
6
  static compilerOptions = {
6
7
  swc: true,
7
8
  typeCheck: false,
@@ -14,11 +15,38 @@ var TSCompiler = class {
14
15
  moduleTypes: { "**": "cjs" }
15
16
  };
16
17
  static compile(path) {
17
- const compiler = register(this.compilerOptions);
18
- compiler.enabled(true);
19
- const result = __require(path);
20
- compiler.enabled(false);
21
- return result;
18
+ try {
19
+ const compiler = register(this.compilerOptions);
20
+ compiler.enabled(true);
21
+ const result = __require(path);
22
+ compiler.enabled(false);
23
+ return result;
24
+ } catch (error) {
25
+ console.error(`Failure to parse module at path ${path}`, error);
26
+ return {};
27
+ }
28
+ }
29
+ static wrapParsingOperation(operation) {
30
+ return (...params) => {
31
+ const restore = this.plugExits();
32
+ const result = operation(...params);
33
+ if (result instanceof Promise) return result.then((v) => this.toStdout(v, restore));
34
+ return this.toStdout(result, restore);
35
+ };
36
+ }
37
+ static toStdout(result, ...callbacks) {
38
+ if (typeof result !== "undefined") console.log(`${this.PARSE_INDICATOR}${JSON.stringify(result)}${this.PARSE_INDICATOR}`);
39
+ callbacks.forEach((c) => c());
40
+ }
41
+ static plugExits() {
42
+ const { exit, abort } = process;
43
+ process.abort = () => void 0;
44
+ process.exit = (_code) => void 0;
45
+ return () => {
46
+ process.exit = exit;
47
+ process.abort = abort;
48
+ process.exitCode = 0;
49
+ };
22
50
  }
23
51
  };
24
52
  //#endregion
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { RepoKitTheme } from "./RepoKitTheme.mjs";
2
2
  import { RepoKitConfig } from "./RepoKitConfig.mjs";
3
- import { AsyncTask, ICommand, ILocatedCommand, IRepoKitCommand, IRepoKitConfig, IRepoKitTheme, RGBString, RepoKitThemeColors } from "./types.mjs";
3
+ import { AsyncTask, ICommand, ILocatedCommand, IRepoKitCommand, IRepoKitConfig, IRepoKitTheme, RGBString, RepoKitThemeColors, UnwrappedBrigdeOperation } from "./types.mjs";
4
4
  import { RepoKitCommand } from "./RepoKitCommand.mjs";
5
- export { AsyncTask, ICommand, ILocatedCommand, IRepoKitCommand, IRepoKitConfig, IRepoKitTheme, RGBString, RepoKitCommand, RepoKitConfig, RepoKitTheme, RepoKitThemeColors };
5
+ export { AsyncTask, ICommand, ILocatedCommand, IRepoKitCommand, IRepoKitConfig, IRepoKitTheme, RGBString, RepoKitCommand, RepoKitConfig, RepoKitTheme, RepoKitThemeColors, UnwrappedBrigdeOperation };
package/dist/types.d.mts CHANGED
@@ -34,5 +34,6 @@ interface IRepoKitTheme {
34
34
  name: string;
35
35
  colors: RepoKitThemeColors;
36
36
  }
37
+ type UnwrappedBrigdeOperation<F extends (...args: unknown[]) => unknown, T = void> = ReturnType<F> extends Promise<unknown> ? Promise<T> : T;
37
38
  //#endregion
38
- export { AsyncTask, ICommand, ILocatedCommand, IRepoKitCommand, IRepoKitConfig, IRepoKitTheme, RGBString, RepoKitThemeColors };
39
+ export { AsyncTask, ICommand, ILocatedCommand, IRepoKitCommand, IRepoKitConfig, IRepoKitTheme, RGBString, RepoKitThemeColors, UnwrappedBrigdeOperation };
@@ -1,28 +1,28 @@
1
- import { existsSync } from "node:fs";
2
- import { stat } from "node:fs/promises";
3
- import { join } from "node:path";
4
1
  import { parseArgs } from "node:util";
5
- import { RepoKitCommand } from "./RepoKitCommand";
6
- import { TSCompiler } from "./TSCompiler";
2
+ import { join } from "node:path";
3
+ import { stat } from "node:fs/promises";
4
+ import { existsSync } from "node:fs";
5
+
7
6
  import type { ILocatedCommand } from "./types";
8
- /* oxlint-disable typescript-eslint(no-misused-spread) */
7
+ import { TSCompiler } from "./TSCompiler";
8
+ import { RepoKitCommand } from "./RepoKitCommand";
9
9
 
10
10
  export class CommandParser extends TSCompiler {
11
- public static async parse() {
11
+ public static readonly parse = this.wrapParsingOperation(async () => {
12
12
  const { paths, root } = this.parsePaths();
13
13
  if (!root || !existsSync(root) || !(await stat(root)).isDirectory()) {
14
- return console.log(JSON.stringify([]));
14
+ return [];
15
15
  }
16
16
  const pathList = paths.split(",").filter(Boolean);
17
- const commands = pathList.map(path => this.parseCommand(root, path));
18
- console.log(JSON.stringify(commands.flat()));
19
- }
17
+ return pathList.map(path => this.parseCommand(root, path)).flat();
18
+ });
20
19
 
21
20
  private static parseCommand(root: string, path: string) {
22
21
  const commands: ILocatedCommand[] = [];
23
22
  const declaredExports = super.compile(join(root, path));
24
23
  for (const key in declaredExports) {
25
24
  if (declaredExports[key] instanceof RepoKitCommand) {
25
+ // oxlint-disable-next-line typescript-eslint(no-misused-spread)
26
26
  commands.push({ ...declaredExports[key], location: path });
27
27
  }
28
28
  }
@@ -1,4 +1,5 @@
1
1
  import { AutoIncrementingID } from "@figliolia/event-emitter";
2
+
2
3
  import type { AsyncTask } from "./types";
3
4
 
4
5
  export class ConcurrencyPool<T> {
@@ -1,23 +1,25 @@
1
- import { existsSync } from "node:fs";
2
- import { join } from "node:path";
3
1
  import { parseArgs } from "node:util";
4
- import { RepoKitConfig } from "./RepoKitConfig";
2
+ import { join } from "node:path";
3
+ import { existsSync } from "node:fs";
4
+
5
5
  import { TSCompiler } from "./TSCompiler";
6
+ import { RepoKitConfig } from "./RepoKitConfig";
6
7
 
7
8
  export class ConfigurationParser extends TSCompiler {
8
- public static parse() {
9
+ public static readonly parse = this.wrapParsingOperation(() => {
9
10
  const root = this.parseRoot();
10
11
  const path = join(root, "repokit.ts");
11
12
  if (!existsSync(path)) {
12
- return;
13
+ return undefined;
13
14
  }
14
15
  const config = super.compile(path);
15
16
  for (const key in config) {
16
17
  if (config[key] instanceof RepoKitConfig) {
17
- return console.log(JSON.stringify(config[key].toScoped(path)));
18
+ return config[key].toScoped(path);
18
19
  }
19
20
  }
20
- }
21
+ return undefined;
22
+ });
21
23
 
22
24
  private static parseRoot() {
23
25
  return parseArgs({
@@ -5,12 +5,7 @@ export class RepoKitCommand {
5
5
  owner: string;
6
6
  description: string;
7
7
  commands: Record<string, ICommand>;
8
- constructor({
9
- name,
10
- description,
11
- owner = "",
12
- commands = {},
13
- }: IRepoKitCommand) {
8
+ constructor({ name, description, owner = "", commands }: IRepoKitCommand) {
14
9
  this.name = name;
15
10
  this.owner = owner;
16
11
  this.commands = commands;
@@ -1,6 +1,6 @@
1
- import { RepoKitCommand } from "./RepoKitCommand";
2
- import type { RepoKitTheme } from "./RepoKitTheme";
3
1
  import type { ICommand, IRepoKitConfig } from "./types";
2
+ import type { RepoKitTheme } from "./RepoKitTheme";
3
+ import { RepoKitCommand } from "./RepoKitCommand";
4
4
  /* eslint-disable typescript-eslint(no-misused-spread */
5
5
 
6
6
  export class RepoKitConfig {
@@ -1,7 +1,11 @@
1
1
  import type { RegisterOptions } from "ts-node";
2
2
  import { register } from "ts-node";
3
3
 
4
+ import type { UnwrappedBrigdeOperation } from "./types";
5
+
4
6
  export class TSCompiler {
7
+ private static PARSE_INDICATOR =
8
+ "=============== REPOKIT PARSE FLAG ===============";
5
9
  private static readonly compilerOptions: RegisterOptions = {
6
10
  swc: true,
7
11
  typeCheck: false,
@@ -17,10 +21,51 @@ export class TSCompiler {
17
21
  };
18
22
 
19
23
  public static compile<T extends Record<string, unknown>>(path: string) {
20
- const compiler = register(this.compilerOptions);
21
- compiler.enabled(true);
22
- const result = require(path) as T;
23
- compiler.enabled(false);
24
- return result;
24
+ try {
25
+ const compiler = register(this.compilerOptions);
26
+ compiler.enabled(true);
27
+ const result = require(path) as T;
28
+ compiler.enabled(false);
29
+ return result;
30
+ } catch (error) {
31
+ console.error(`Failure to parse module at path ${path}`, error);
32
+ return {} as T;
33
+ }
34
+ }
35
+
36
+ public static wrapParsingOperation<F extends (...args: unknown[]) => unknown>(
37
+ operation: F,
38
+ ) {
39
+ return (...params: Parameters<F>) => {
40
+ const restore = this.plugExits();
41
+ const result = operation(...params);
42
+ if (result instanceof Promise) {
43
+ return result.then(v =>
44
+ this.toStdout(v, restore),
45
+ ) as UnwrappedBrigdeOperation<F>;
46
+ }
47
+ return this.toStdout(result, restore) as UnwrappedBrigdeOperation<F>;
48
+ };
49
+ }
50
+
51
+ private static toStdout<T>(result: T, ...callbacks: (() => void)[]) {
52
+ if (typeof result !== "undefined") {
53
+ console.log(
54
+ `${this.PARSE_INDICATOR}${JSON.stringify(result)}${this.PARSE_INDICATOR}`,
55
+ );
56
+ }
57
+ callbacks.forEach(c => c());
58
+ }
59
+
60
+ private static plugExits() {
61
+ // oxlint-disable-next-line typescript-eslint(unbound-method)
62
+ const { exit, abort } = process;
63
+ process.abort = () => undefined as never;
64
+ process.exit = (_code?: string | number | null) => undefined as never;
65
+ return () => {
66
+ process.exit = exit;
67
+ process.abort = abort;
68
+ process.exitCode = 0;
69
+ };
25
70
  }
26
71
  }
@@ -1,5 +1,3 @@
1
1
  import { ConfigurationParser } from "../ConfigurationParser";
2
2
 
3
- (() => {
4
- ConfigurationParser.parse();
5
- })();
3
+ ConfigurationParser.parse();
@@ -45,3 +45,8 @@ export interface IRepoKitTheme {
45
45
  name: string;
46
46
  colors: RepoKitThemeColors;
47
47
  }
48
+
49
+ export type UnwrappedBrigdeOperation<
50
+ F extends (...args: unknown[]) => unknown,
51
+ T = void,
52
+ > = ReturnType<F> extends Promise<unknown> ? Promise<T> : T;
@@ -1,4 +1,4 @@
1
- CURRENT_VERSION="4.0.3"
1
+ CURRENT_VERSION="4.0.5"
2
2
  CWD=$(pwd)
3
3
 
4
4
  REPLACEMENT="/node_modules"
@@ -13,12 +13,14 @@ use crate::{
13
13
  #[derive(Clone)]
14
14
  pub struct CrawlCache {
15
15
  pub cache_directory: Option<PathBuf>,
16
+ pub changed_files: Option<Vec<String>>,
16
17
  pub files_to_crawl: Option<Vec<String>>,
17
18
  }
18
19
 
19
20
  impl CrawlCache {
20
21
  pub fn new(cache_directory: &Option<PathBuf>) -> Self {
21
22
  CrawlCache {
23
+ changed_files: None,
22
24
  files_to_crawl: None,
23
25
  cache_directory: cache_directory.clone(),
24
26
  }
@@ -34,7 +36,7 @@ impl CrawlCache {
34
36
  if commit_hash != head_commit {
35
37
  return;
36
38
  }
37
- let mut lines = self.line_buffer_to_vec(lines);
39
+ let lines = self.line_buffer_to_vec(lines);
38
40
  if lines.is_empty() {
39
41
  return;
40
42
  }
@@ -48,8 +50,8 @@ impl CrawlCache {
48
50
  changed_files.remove(line);
49
51
  }
50
52
  }
51
- for line in changed_files {
52
- lines.push(line);
53
+ if !changed_files.is_empty() {
54
+ self.changed_files = Some(changed_files.into_iter().collect());
53
55
  }
54
56
  self.files_to_crawl = Some(lines);
55
57
  }
@@ -107,7 +107,7 @@ impl NodeScope {
107
107
  let manager_map = HashMap::from([
108
108
  ("npm", "npx"),
109
109
  ("yarn", "yarn run -T"),
110
- ("pnpm", "pnpm run"),
110
+ ("pnpm", "pnpm"),
111
111
  ("bun", "bunx"),
112
112
  ]);
113
113
  manager_map.get(package_manager).unwrap_or(&npx)
@@ -1,20 +1,26 @@
1
1
  use core::panic;
2
2
  use std::{
3
3
  path::{Path, PathBuf},
4
- sync::MutexGuard,
4
+ sync::{LazyLock, MutexGuard},
5
5
  };
6
6
 
7
+ use regex::Regex;
7
8
  use serde_json::{Value, from_str};
8
9
 
9
10
  use crate::{
10
11
  context::{file_system::FileSystem, node_scope::NodeScope},
11
12
  executor::executor::Executor,
13
+ logger::logger::Logger,
12
14
  repokit::{
13
15
  repokit_command::RepoKitCommand, repokit_config::RepoKitConfig,
14
16
  repokit_runtime::RepoKitRuntime,
15
17
  },
16
18
  };
17
19
 
20
+ static BRIDGE_PARSING_REGEX: LazyLock<Regex> = LazyLock::new(|| {
21
+ Regex::new(r#"=============== REPOKIT PARSE FLAG ===============(.*)=============== REPOKIT PARSE FLAG ==============="#).unwrap()
22
+ });
23
+
18
24
  pub struct TypeScriptBridge;
19
25
 
20
26
  impl TypeScriptBridge {
@@ -27,14 +33,14 @@ impl TypeScriptBridge {
27
33
  if stdout.is_empty() {
28
34
  RepoKitConfig::create(files);
29
35
  }
30
- let result: Result<Value, serde_json::Error> = from_str(stdout.as_str());
31
- match result {
32
- Ok(config) => RepoKitConfig::from_input(&files.git_root, node, config),
33
- Err(_) => {
34
- RepoKitConfig::on_parsing_error(&files.git_root, node, Value::Null);
35
- panic!();
36
+ if let Some(parsed_config) = TypeScriptBridge::unwrap_stdout(&stdout) {
37
+ let parse_result: Result<Value, serde_json::Error> = from_str(parsed_config);
38
+ if let Ok(config) = parse_result {
39
+ return RepoKitConfig::from_input(&files.git_root, node, config);
36
40
  }
37
41
  }
42
+ Logger::parse_error("configuration", &stdout);
43
+ panic!();
38
44
  }
39
45
 
40
46
  pub fn parse_commands(path_list: &MutexGuard<Vec<String>>) -> Vec<RepoKitCommand> {
@@ -50,14 +56,15 @@ impl TypeScriptBridge {
50
56
  .as_str(),
51
57
  )
52
58
  });
53
- let result: Result<Vec<Value>, serde_json::Error> = serde_json::from_str(&stdout);
54
- match result {
55
- Ok(commands) => RepoKitCommand::from_input(commands),
56
- Err(_) => {
57
- RepoKitCommand::full_blown_crash();
58
- panic!();
59
+ if let Some(parsed_commands) = TypeScriptBridge::unwrap_stdout(&stdout) {
60
+ let parse_result: Result<Vec<Value>, serde_json::Error> =
61
+ serde_json::from_str(parsed_commands);
62
+ if let Ok(commands) = parse_result {
63
+ return RepoKitCommand::from_input(commands);
59
64
  }
60
65
  }
66
+ Logger::parse_error("commands", &stdout);
67
+ panic!();
61
68
  }
62
69
 
63
70
  fn execute_with_node(root: &PathBuf, args: &str) -> String {
@@ -65,4 +72,13 @@ impl TypeScriptBridge {
65
72
  cmd.current_dir(Path::new(root))
66
73
  })
67
74
  }
75
+
76
+ fn unwrap_stdout(stdout: &str) -> Option<&str> {
77
+ if let Some(capture) = BRIDGE_PARSING_REGEX.captures(stdout)
78
+ && let Some(result) = capture.get(1)
79
+ {
80
+ return Some(result.as_str());
81
+ }
82
+ None
83
+ }
68
84
  }
@@ -2,7 +2,10 @@ use std::sync::{Arc, Mutex, MutexGuard};
2
2
 
3
3
  use ignore::WalkBuilder;
4
4
 
5
- use crate::{file_walker::walker::TSFileVisitorBuilder, repokit::repokit_runtime::RepoKitRuntime};
5
+ use crate::{
6
+ file_walker::walker::{TSFileVisitor, TSFileVisitorBuilder},
7
+ repokit::repokit_runtime::RepoKitRuntime,
8
+ };
6
9
 
7
10
  pub struct FileWalker {
8
11
  pub command_paths: Arc<Mutex<Vec<String>>>,
@@ -47,13 +50,20 @@ impl FileWalker {
47
50
  }
48
51
 
49
52
  fn from_cache(&self) {
50
- let mut paths_to_search = self.command_paths.lock().unwrap();
51
53
  RepoKitRuntime::with_runtime(|runtime| {
54
+ let mut paths_to_search = self.command_paths.lock().unwrap();
52
55
  if let Some(files_to_crawl) = &runtime.caches.crawl_cache.files_to_crawl {
53
56
  for file in files_to_crawl {
54
57
  paths_to_search.push(file.to_owned());
55
58
  }
56
59
  }
57
- })
60
+ if let Some(changed_files) = &runtime.caches.crawl_cache.changed_files {
61
+ let mut paths_to_import =
62
+ TSFileVisitor::traverse_list(&runtime.git.root, changed_files.to_owned());
63
+ if !paths_to_import.is_empty() {
64
+ paths_to_search.append(&mut paths_to_import);
65
+ }
66
+ }
67
+ });
58
68
  }
59
69
  }
@@ -1,11 +1,13 @@
1
1
  use std::{
2
2
  fs::File,
3
3
  io::{BufRead, BufReader},
4
- sync::{Arc, Mutex},
4
+ path::Path,
5
+ sync::{Arc, LazyLock, Mutex},
5
6
  };
6
7
 
7
8
  use ignore::{DirEntry, Error, ParallelVisitor, ParallelVisitorBuilder, WalkState};
8
9
  use regex::Regex;
10
+ use tokio::task::JoinSet;
9
11
 
10
12
  use crate::{internal_filesystem::file_builder::FileBuilder, logger::logger::Logger};
11
13
 
@@ -14,47 +16,80 @@ pub struct TSFileVisitor {
14
16
  paths: Arc<Mutex<Vec<String>>>,
15
17
  }
16
18
 
19
+ static REPOKIT_IMPORT_MATCHER: LazyLock<Regex> = LazyLock::new(|| {
20
+ Regex::new(r#"(require\(|from[\s*]?)['"]@repokit/core["'][\)]?[;]?$"#).unwrap()
21
+ });
22
+
17
23
  impl ParallelVisitor for TSFileVisitor {
18
24
  fn visit(&mut self, entry: Result<DirEntry, Error>) -> WalkState {
19
25
  let root_replacer = format!("{}/", self.root);
20
- let repokit_import_matcher =
21
- Regex::new(r#"(require\(|from[\s*]?)['"]@repokit/core["'][\)]?[;]?$"#).unwrap();
22
- if let Ok(entry) = entry {
23
- let path = entry.path();
24
- let path_string = path.to_str().map_or("", |f| f);
25
- if entry.file_type().is_some_and(|ft| ft.is_file()) && path_string.ends_with(".ts") {
26
- let mut open_comment = false;
27
- let file: File = FileBuilder::open(path_string, |_| Logger::open_file_error());
28
- let reader: BufReader<File> = BufReader::new(file);
29
- for line_result in reader.lines() {
30
- let unwrapped = line_result.unwrap();
31
- let line = unwrapped.trim();
32
- if !open_comment && line.starts_with("/**") {
33
- open_comment = true;
34
- continue;
35
- }
36
- if open_comment {
37
- if line == "*/" {
38
- open_comment = false;
39
- }
40
- continue;
41
- }
42
- if repokit_import_matcher.is_match(line) {
43
- let mut vector = self.paths.lock().unwrap();
44
- vector.push(path_string.to_string().replace(&root_replacer, ""));
45
- break;
46
- }
47
- if !line.is_empty()
48
- && !line.starts_with("import ")
49
- && !line.contains("require(")
50
- && !(line.starts_with("//") || line.starts_with("/*"))
51
- {
52
- break;
53
- }
26
+ if let Ok(file_entry) = entry
27
+ && file_entry.file_type().is_some_and(|ft| ft.is_file())
28
+ && file_entry.file_name().to_string_lossy().ends_with(".ts")
29
+ && let Some(matched_path) = TSFileVisitor::on_file(file_entry.path())
30
+ {
31
+ let mut vector = self.paths.lock().unwrap();
32
+ vector.push(matched_path.to_string_lossy().replace(&root_replacer, ""));
33
+ }
34
+
35
+ WalkState::Continue
36
+ }
37
+ }
38
+
39
+ impl TSFileVisitor {
40
+ #[tokio::main]
41
+ pub async fn traverse_list(root: &str, path_list: Vec<String>) -> Vec<String> {
42
+ let mut handles = JoinSet::new();
43
+ let root_replacer = format!("{}/", root);
44
+ for path in path_list {
45
+ handles.spawn(async move {
46
+ if let Some(result) = TSFileVisitor::on_file(Path::new(&path)) {
47
+ return Some(result.to_owned());
54
48
  }
49
+ None
50
+ });
51
+ }
52
+ handles
53
+ .join_all()
54
+ .await
55
+ .iter()
56
+ .filter_map(|result| {
57
+ result
58
+ .as_ref()
59
+ .map(|path| path.to_string_lossy().replace(&root_replacer, ""))
60
+ })
61
+ .collect()
62
+ }
63
+
64
+ pub fn on_file(path: &Path) -> Option<&Path> {
65
+ let mut open_comment = false;
66
+ let file: File = FileBuilder::open(path, |_| Logger::open_file_error());
67
+ let reader: BufReader<File> = BufReader::new(file);
68
+ for line_result in reader.lines() {
69
+ let unwrapped = line_result.unwrap();
70
+ let line = unwrapped.trim();
71
+ if !open_comment && line.starts_with("/*") {
72
+ open_comment = true;
73
+ continue;
74
+ }
75
+ if open_comment {
76
+ if line.ends_with("*/") {
77
+ open_comment = false;
78
+ }
79
+ continue;
80
+ }
81
+ if REPOKIT_IMPORT_MATCHER.is_match(line) {
82
+ return Some(path);
83
+ }
84
+ if !line.is_empty()
85
+ && !line.starts_with("import ")
86
+ && !line.contains("require(")
87
+ && !(line.starts_with("//") || line.starts_with("/*"))
88
+ {
89
+ break;
55
90
  }
56
91
  }
57
- WalkState::Continue
92
+ None
58
93
  }
59
94
  }
60
95
 
@@ -72,8 +107,8 @@ impl<'a> TSFileVisitorBuilder<'a> {
72
107
  impl<'s> ParallelVisitorBuilder<'s> for TSFileVisitorBuilder<'s> {
73
108
  fn build(&mut self) -> Box<dyn ParallelVisitor + 's> {
74
109
  Box::new(TSFileVisitor {
75
- paths: self.paths.clone(),
76
110
  root: self.root.to_string(),
111
+ paths: self.paths.clone(),
77
112
  })
78
113
  }
79
114
  }
@@ -55,6 +55,20 @@ impl Logger {
55
55
  })
56
56
  }
57
57
 
58
+ pub fn parse_error(file_type: &str, stdout: &str) {
59
+ Logger::info(format!("There was an error parsing your {}", file_type).as_str());
60
+ Logger::info(
61
+ "This can occur when RepoKit attempts to parse a TypeScript file that can terminate the process upon evaluation",
62
+ );
63
+ Logger::info("Please file a bug to with any related error or stack trace found below");
64
+ Logger::log_issue_link();
65
+ println!();
66
+ if !stdout.is_empty() {
67
+ println!("{stdout}");
68
+ }
69
+ panic!();
70
+ }
71
+
58
72
  pub fn space_around(message: &str) {
59
73
  println!("\n{}{}\n", Logger::info_prefix(), message);
60
74
  }
@@ -73,32 +73,6 @@ impl RepoKitCommand {
73
73
  None
74
74
  }
75
75
 
76
- pub fn full_blown_crash() {
77
- Logger::error("I hit a snag parsing your commands");
78
- Logger::error(
79
- format!(
80
- "This kind of error is indicative of a bug within {}",
81
- Logger::with_theme(|theme| theme.highlight("Repokit"))
82
- )
83
- .as_str(),
84
- );
85
- println!();
86
- Logger::info("Let's blaim the AI's for this one");
87
- let version = RepoKitRuntime::with_runtime(|runtime| {
88
- runtime.caches.version_cache.installed_version.clone()
89
- });
90
- Logger::info(
91
- format!(
92
- "In the interim, please file a bug here and downgrade to the most recent version behind {}",
93
- Logger::with_theme(|theme| theme.highlight(&version))
94
- )
95
- .as_str(),
96
- );
97
- Logger::log_issue_link();
98
- Logger::info("We'll get a hotfix out asap!");
99
- panic!();
100
- }
101
-
102
76
  fn register_encountered_errors(failed_paths: Vec<String>) {
103
77
  PostProcessor::get().register_task(move || {
104
78
  println!();
@@ -74,12 +74,14 @@ impl RepoKitConfig {
74
74
  if path.exists() {
75
75
  Logger::info(
76
76
  format!(
77
- "I found a Repokit configuration without an exported {} instance",
77
+ "I found a Repokit configuration but could not resolve the exported {} instance",
78
78
  Logger::with_theme(|theme| theme.highlight("RepokitConfig"))
79
79
  )
80
80
  .as_str(),
81
81
  );
82
- return Logger::exit_with_info("Please create an instance and export it");
82
+ Logger::exit_with_info(
83
+ "Please double check that your config file is free of any sideffects that can cause the parser to crash",
84
+ );
83
85
  }
84
86
  Logger::info("Welcome to Repokit! Let's get you setup");
85
87
  Logger::info("Creating your configuration file:");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@repokit/core",
3
- "version": "4.0.3",
3
+ "version": "4.0.5",
4
4
  "description": "A knowledgebase for your repository - wrapped in a CLI",
5
5
  "keywords": [
6
6
  "cli",
@@ -31,19 +31,19 @@
31
31
  "access": "public"
32
32
  },
33
33
  "scripts": {
34
- "build:all": "yarn lint:ts && yarn lint:rust && yarn install:rust",
34
+ "build:all": "pnpm lint:ts && pnpm lint:rust && pnpm install:rust",
35
35
  "build:rust": "cargo build --release",
36
36
  "build:ts": "tsx devserver/run.ts",
37
37
  "clean:ts": "tsx devserver/run.ts -c",
38
38
  "grant:exec": "chmod -R +x ./installation",
39
- "install:rust": "yarn build:rust && cargo install --path .",
40
- "lint:all": "yarn lint:ts && yarn lint:rust",
39
+ "install:rust": "pnpm build:rust && cargo install --path .",
40
+ "lint:all": "pnpm lint:ts && pnpm lint:rust",
41
41
  "lint:rust": "cargo clippy --fix --allow-dirty",
42
- "lint:ts": "yarn oxlint --type-aware --type-check --report-unused-disable-directives --fix && yarn oxfmt",
43
- "package:ts": "yarn lint:ts && yarn lint:rust && yarn build:ts",
44
- "postinstall": "yarn grant:exec && ./installation/install.sh",
45
- "repokit": "yarn install:rust && repokit",
46
- "run:dev": "yarn build:ts && cargo run --package repokit --bin repokit",
42
+ "lint:ts": "oxlint --type-aware --type-check --report-unused-disable-directives --fix && oxfmt",
43
+ "package:ts": "pnpm lint:ts && pnpm lint:rust && pnpm build:ts",
44
+ "postinstall": "pnpm grant:exec && ./installation/install.sh",
45
+ "repokit": "pnpm install:rust && repokit",
46
+ "run:dev": "pnpm build:ts && cargo run --package repokit --bin repokit",
47
47
  "watch:ts": "tsx devserver/run.ts -w"
48
48
  },
49
49
  "dependencies": {
@@ -55,19 +55,13 @@
55
55
  "@figliolia/child-process": "^1.0.4",
56
56
  "@figliolia/semver": "^1.0.0",
57
57
  "@types/node": "^25.3.0",
58
- "@typescript-eslint/eslint-plugin": "^8.57.1",
59
- "@typescript-eslint/parser": "^8.57.1",
60
58
  "chalk": "^5.6.2",
61
- "eslint": "^10.0.3",
62
- "eslint-import-resolver-typescript": "^4.4.4",
63
- "eslint-plugin-import": "^2.32.0",
64
- "eslint-plugin-simple-import-sort": "^13.0.0",
65
- "eslint-plugin-unused-imports": "^4.4.1",
66
59
  "oxfmt": "^0.47.0",
67
60
  "oxlint": "^1.42.0",
68
61
  "oxlint-tsgolint": "^0.22.0",
69
62
  "tsdown": "^0.21.0",
70
63
  "tsx": "^4.21.0",
71
64
  "typescript": "^6.0.2"
72
- }
65
+ },
66
+ "packageManager": "pnpm@10.33.2"
73
67
  }