@haltcase/run 3.0.1 → 4.0.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 (50) hide show
  1. package/dist/cli/bin.d.mts +1 -0
  2. package/dist/cli/bin.mjs +7 -0
  3. package/dist/cli/handler.mjs +110 -0
  4. package/dist/cli/main.mjs +56 -0
  5. package/dist/cli/parseOptions.d.mts +7 -0
  6. package/dist/cli/parseOptions.mjs +36 -0
  7. package/dist/config.d.mts +18 -0
  8. package/dist/config.mjs +19 -0
  9. package/dist/index.d.mts +5 -0
  10. package/dist/index.mjs +2 -0
  11. package/dist/tasks/executeTask.mjs +34 -0
  12. package/dist/tasks/guards.mjs +4 -0
  13. package/dist/tasks/task.d.mts +10 -0
  14. package/dist/tasks/task.mjs +40 -0
  15. package/dist/tasks/types.d.mts +60 -0
  16. package/dist/util/getSchemaProperties.mjs +34 -0
  17. package/dist/util/loadTaskFile.mjs +30 -0
  18. package/dist/util/resolveTaskFile.mjs +13 -0
  19. package/package.json +41 -39
  20. package/dist/cli/bin.d.ts +0 -2
  21. package/dist/cli/bin.js +0 -7
  22. package/dist/cli/handler.d.ts +0 -28
  23. package/dist/cli/handler.js +0 -105
  24. package/dist/cli/main.d.ts +0 -21
  25. package/dist/cli/main.js +0 -64
  26. package/dist/cli/parseOptions.d.ts +0 -13
  27. package/dist/cli/parseOptions.js +0 -48
  28. package/dist/config.d.ts +0 -16
  29. package/dist/config.js +0 -16
  30. package/dist/index.d.ts +0 -4
  31. package/dist/index.js +0 -1
  32. package/dist/tasks/executeTask.d.ts +0 -12
  33. package/dist/tasks/executeTask.js +0 -45
  34. package/dist/tasks/guards.d.ts +0 -3
  35. package/dist/tasks/guards.js +0 -2
  36. package/dist/tasks/task.d.ts +0 -14
  37. package/dist/tasks/task.js +0 -55
  38. package/dist/tasks/types.d.ts +0 -58
  39. package/dist/tasks/types.js +0 -1
  40. package/dist/tsconfig.build.tsbuildinfo +0 -1
  41. package/dist/util/getSchemaProperties.d.ts +0 -2
  42. package/dist/util/getSchemaProperties.js +0 -40
  43. package/dist/util/loadTaskFile.d.ts +0 -8
  44. package/dist/util/loadTaskFile.js +0 -30
  45. package/dist/util/resolveTaskFile.d.ts +0 -3
  46. package/dist/util/resolveTaskFile.js +0 -17
  47. package/dist/util/result.d.ts +0 -9
  48. package/dist/util/result.js +0 -1
  49. /package/{license → LICENSE} +0 -0
  50. /package/{readme.md → README.md} +0 -0
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { createHandler } from "./handler.mjs";
3
+ import { main } from "./main.mjs";
4
+ //#region src/cli/bin.ts
5
+ main({ handler: createHandler() });
6
+ //#endregion
7
+ export {};
@@ -0,0 +1,110 @@
1
+ import { isBrandedTask } from "../tasks/guards.mjs";
2
+ import { getSchemaProperties } from "../util/getSchemaProperties.mjs";
3
+ import { extensions } from "../util/resolveTaskFile.mjs";
4
+ import { bold, gray, yellow } from "colorette";
5
+ import { readdirSync } from "node:fs";
6
+ import { extname } from "node:path";
7
+ import { Spinner } from "@favware/colorette-spinner";
8
+ import cliui from "cliui";
9
+ //#region src/cli/handler.ts
10
+ const failWith = (spinner, message) => {
11
+ let text;
12
+ if (Array.isArray(message)) text = message.map(String).join("\n");
13
+ if (text == null || typeof text !== "string") text = String(message);
14
+ spinner.error({ text });
15
+ process.exit(1);
16
+ };
17
+ const write = (message) => {
18
+ process.stdout.write(`${message}\n`);
19
+ };
20
+ const usage = `Usage: ${yellow("hr")} <taskFile> [task]`;
21
+ const commandHandler = (_context) => {
22
+ return usage;
23
+ };
24
+ const taskFileListHandler = (context) => {
25
+ return `${[
26
+ usage,
27
+ "Available task files:",
28
+ readdirSync(context.config.taskDirectory).filter((file) => extensions.includes(extname(file))).map((it) => ` ${it}`).join("\n")
29
+ ].join("\n\n")}\n`;
30
+ };
31
+ const taskListHandler = (context) => {
32
+ const ui = cliui({
33
+ width: Math.max(0, process.stdout.columns - 4),
34
+ wrap: true
35
+ });
36
+ const { config } = context.taskFile.data;
37
+ const baseUsage = usage.replace("<taskFile>", context.taskFile.name);
38
+ ui.div(`${baseUsage} <parameter> [...options]`);
39
+ ui.div({
40
+ text: "Available tasks:",
41
+ padding: [
42
+ 1,
43
+ 0,
44
+ 1,
45
+ 0
46
+ ]
47
+ });
48
+ const taskListPaddingX = 2;
49
+ const nameColumnWidth = Object.keys(config).reduce((previous, current) => Math.max(previous, current.length), 1) + 2 + taskListPaddingX * 2;
50
+ for (const [name, value] of Object.entries(config)) {
51
+ if (!value) continue;
52
+ const formattedName = bold(name);
53
+ if (isBrandedTask(value)) {
54
+ if (value.kind === "strictTask") {
55
+ const properties = getSchemaProperties(value.schema) || gray("not available");
56
+ ui.div({
57
+ text: formattedName,
58
+ width: nameColumnWidth,
59
+ padding: [
60
+ 0,
61
+ taskListPaddingX,
62
+ 0,
63
+ taskListPaddingX
64
+ ]
65
+ }, properties);
66
+ continue;
67
+ }
68
+ ui.div({
69
+ text: formattedName,
70
+ width: nameColumnWidth,
71
+ padding: [
72
+ 0,
73
+ taskListPaddingX,
74
+ 0,
75
+ taskListPaddingX
76
+ ]
77
+ });
78
+ continue;
79
+ }
80
+ ui.div({
81
+ text: formattedName,
82
+ width: nameColumnWidth,
83
+ padding: [
84
+ 0,
85
+ taskListPaddingX,
86
+ 0,
87
+ taskListPaddingX
88
+ ]
89
+ });
90
+ }
91
+ ui.div("");
92
+ return ui.toString();
93
+ };
94
+ const help = (context) => {
95
+ if ("command" in context) return commandHandler(context);
96
+ if ("taskFileList" in context) return taskFileListHandler(context);
97
+ if ("taskList" in context) return taskListHandler(context);
98
+ return "";
99
+ };
100
+ const createHandler = () => {
101
+ const spinner = new Spinner();
102
+ return {
103
+ spinner,
104
+ help,
105
+ write,
106
+ failWith: (message) => failWith(spinner, message)
107
+ };
108
+ };
109
+ //#endregion
110
+ export { createHandler };
@@ -0,0 +1,56 @@
1
+ import { parseOptions } from "./parseOptions.mjs";
2
+ import { resolveTaskFile } from "../util/resolveTaskFile.mjs";
3
+ import { getAppConfig } from "../config.mjs";
4
+ import { executeTask } from "../tasks/executeTask.mjs";
5
+ import { loadTaskFile } from "../util/loadTaskFile.mjs";
6
+ import { parse, resolve } from "node:path";
7
+ //#region src/cli/main.ts
8
+ const main = async (props) => {
9
+ const { config } = await getAppConfig();
10
+ const inputFileName = process.argv[2];
11
+ const taskName = process.argv[3] ?? "";
12
+ if (!inputFileName) {
13
+ props.handler.write(props.handler.help({
14
+ taskFileList: true,
15
+ config
16
+ }));
17
+ props.handler.failWith("Task file name is required");
18
+ }
19
+ const fullFilePath = resolve(config.taskDirectory, inputFileName);
20
+ const context = {
21
+ ...props,
22
+ config,
23
+ taskFile: {
24
+ ...parse(fullFilePath),
25
+ path: fullFilePath
26
+ },
27
+ taskName
28
+ };
29
+ const resolutions = resolveTaskFile(context);
30
+ if (Array.isArray(resolutions)) props.handler.failWith([
31
+ `Found multiple task files with the name '${context.taskFile.name}'`,
32
+ `Rename the ambiguous files or specify an extension and try again`,
33
+ resolutions.map((it) => ` ${it}`).join("\n")
34
+ ]);
35
+ context.taskFile = {
36
+ ...parse(resolutions),
37
+ path: resolutions
38
+ };
39
+ const taskFileResult = await loadTaskFile(context);
40
+ if (!taskFileResult.ok) props.handler.failWith(taskFileResult.error);
41
+ const contextWithData = {
42
+ ...context,
43
+ taskFile: {
44
+ ...context.taskFile,
45
+ data: taskFileResult.value
46
+ }
47
+ };
48
+ try {
49
+ await executeTask(contextWithData, parseOptions(process.argv.slice(4)));
50
+ if (!config.quiet) props.handler.spinner.success({ text: "Success" });
51
+ } catch (error) {
52
+ props.handler.failWith([`Failed to execute task`, String(error)]);
53
+ }
54
+ };
55
+ //#endregion
56
+ export { main };
@@ -0,0 +1,7 @@
1
+ //#region src/cli/parseOptions.d.ts
2
+ interface ParsedOptions {
3
+ _: string[];
4
+ [key: string]: unknown;
5
+ }
6
+ //#endregion
7
+ export { ParsedOptions };
@@ -0,0 +1,36 @@
1
+ import { parseArgs } from "node:util";
2
+ //#region src/cli/parseOptions.ts
3
+ const reservedNames = new Set(["_", "env"]);
4
+ /**
5
+ * Wrapper around Node's {@link parseArgs} that treats options as strings
6
+ * by default, instead of boolean flags.
7
+ *
8
+ * @param args - argv-like string
9
+ * @returns
10
+ */
11
+ const parseOptions = (args) => {
12
+ const { tokens } = parseArgs({
13
+ args,
14
+ allowPositionals: true,
15
+ strict: false,
16
+ tokens: true
17
+ });
18
+ const options = { _: [] };
19
+ for (let index = 0; index < tokens.length; index++) {
20
+ const token = tokens[index];
21
+ if (token.kind === "option-terminator") continue;
22
+ if (token.kind === "option") {
23
+ if (reservedNames.has(token.name)) throw new Error(`Reserved name '${token.name}' cannot be used as option`);
24
+ if (index + 1 < tokens.length) {
25
+ const next = tokens[index + 1];
26
+ if (next.kind !== "positional") throw new Error(`Expected value for option ${token.rawName}`);
27
+ options[token.name] = next.value;
28
+ index++;
29
+ } else throw new Error(`Expected option ${token.rawName} to be followed by a value`);
30
+ }
31
+ if (token.kind === "positional") options._.push(token.value);
32
+ }
33
+ return options;
34
+ };
35
+ //#endregion
36
+ export { parseOptions, reservedNames };
@@ -0,0 +1,18 @@
1
+ //#region src/config.d.ts
2
+ interface AppConfig {
3
+ /**
4
+ * Directory containing task files. Defaults to the `scripts` folder within
5
+ * the current working directory.
6
+ *
7
+ * @default `<cwd>/scripts`
8
+ */
9
+ taskDirectory: string;
10
+ /**
11
+ * Print no output unless errors occur.
12
+ *
13
+ * @default false
14
+ */
15
+ quiet: boolean;
16
+ }
17
+ //#endregion
18
+ export { AppConfig };
@@ -0,0 +1,19 @@
1
+ import { join } from "node:path";
2
+ import { loadConfig } from "c12";
3
+ import { cwd } from "node:process";
4
+ //#region src/config.ts
5
+ const getAppConfig = async () => {
6
+ const configName = "haltcase.run";
7
+ return loadConfig({
8
+ name: configName,
9
+ configFile: configName,
10
+ packageJson: configName,
11
+ rcFile: configName,
12
+ defaults: {
13
+ taskDirectory: join(cwd(), "scripts"),
14
+ quiet: false
15
+ }
16
+ });
17
+ };
18
+ //#endregion
19
+ export { getAppConfig };
@@ -0,0 +1,5 @@
1
+ import { ParsedOptions } from "./cli/parseOptions.mjs";
2
+ import { AppConfig } from "./config.mjs";
3
+ import { Task } from "./tasks/types.mjs";
4
+ import { task } from "./tasks/task.mjs";
5
+ export { type AppConfig as HaltcaseRunConfig, type ParsedOptions, type Task, task };
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { task } from "./tasks/task.mjs";
2
+ export { task };
@@ -0,0 +1,34 @@
1
+ import { $, execa } from "execa";
2
+ const taskUtilities = {
3
+ command: execa,
4
+ $,
5
+ exec: execa({
6
+ stdin: "inherit",
7
+ stdout: "inherit",
8
+ stderr: "inherit"
9
+ })
10
+ };
11
+ const executeTask = async (context, options) => {
12
+ const taskFunction = context.taskFile.data.config[context.taskName];
13
+ const specifier = `${context.taskFile.name}::${context.taskName}`;
14
+ if (taskFunction == null) {
15
+ context.handler.write(context.handler.help({
16
+ taskList: true,
17
+ ...context
18
+ }));
19
+ if (!context.taskName) context.handler.failWith(`Task name is required.`);
20
+ context.handler.failWith([`Task file '${context.taskFile.name}' does not export '${context.taskName}'`, `Resolved to: ${context.taskFile.path}`]);
21
+ }
22
+ try {
23
+ await taskFunction({
24
+ ...options,
25
+ env: process.env
26
+ }, taskUtilities);
27
+ } catch (error) {
28
+ const message = error instanceof Error ? error.message : String(error);
29
+ if (message.includes("is not a function")) context.handler.failWith([`Failed to execute ${specifier}`, `Exported value '${context.taskName}' is not a function`]);
30
+ context.handler.failWith([`Failed to execute ${specifier}`, message]);
31
+ }
32
+ };
33
+ //#endregion
34
+ export { executeTask };
@@ -0,0 +1,4 @@
1
+ //#region src/tasks/guards.ts
2
+ const isBrandedTask = (task) => "kind" in task;
3
+ //#endregion
4
+ export { isBrandedTask };
@@ -0,0 +1,10 @@
1
+ import { BrandedTask, BrandedTaskStrict, DefaultOptionsInput, Task } from "./types.mjs";
2
+ import { Type, type } from "arktype";
3
+
4
+ //#region src/tasks/task.d.ts
5
+ declare const task: {
6
+ <T = DefaultOptionsInput>(fn: Task<T>): BrandedTask<T>;
7
+ strict<const TShape>(shape: type.validate<TShape>, fn: Task<NoInfer<Type<type.infer<TShape>>["infer"]>>): BrandedTaskStrict<Type<type.infer<TShape>>["inferIn"]>;
8
+ };
9
+ //#endregion
10
+ export { task };
@@ -0,0 +1,40 @@
1
+ import { type } from "arktype";
2
+ import { red } from "colorette";
3
+ //#region src/tasks/task.ts
4
+ const formatValidationIssues = (errors) => errors.map((error) => {
5
+ const propertyName = String(error.path[0]);
6
+ const dottedPath = error.path.join(".");
7
+ if (!propertyName) return error.message;
8
+ const messageWithoutProperty = error.message.startsWith(dottedPath) ? error.message.slice(dottedPath.length + 1) : error.message;
9
+ if (propertyName === "env") return `${red(`Environment variable '${String(error.path[1])}'`)}: ${messageWithoutProperty}`;
10
+ const optionText = propertyName === "_" ? "Positionals" : `--${propertyName}`;
11
+ if (error.code === "predicate" && error.expected === "removed") return `${red(optionText)}: unknown option`;
12
+ return `${red(optionText)}: ${messageWithoutProperty}`;
13
+ }).join("\n");
14
+ const task = (fn) => {
15
+ fn.kind = "task";
16
+ return fn;
17
+ };
18
+ /**
19
+ * Default schema for the options received by a {@link task}.
20
+ */
21
+ const defaultOptionsInput = type({
22
+ _: "string[]",
23
+ env: "Record<string, string | undefined>"
24
+ });
25
+ /**
26
+ * Create a task that validates its input against a schema.
27
+ */
28
+ task.strict = (shape, fn) => {
29
+ const schema = defaultOptionsInput.merge(type.raw(shape)).onUndeclaredKey("reject");
30
+ const taskFunction = (options, utilities) => {
31
+ const validationResult = schema(options);
32
+ if (validationResult instanceof type.errors) throw new TypeError(formatValidationIssues(validationResult), { cause: validationResult });
33
+ return fn(validationResult, utilities);
34
+ };
35
+ taskFunction.kind = "strictTask";
36
+ taskFunction.schema = schema;
37
+ return taskFunction;
38
+ };
39
+ //#endregion
40
+ export { task };
@@ -0,0 +1,60 @@
1
+ import { ParsedOptions } from "../cli/parseOptions.mjs";
2
+ import { Type } from "arktype";
3
+ import { ExecaMethod, ExecaScriptMethod } from "execa";
4
+
5
+ //#region src/tasks/types.d.ts
6
+ interface TaskUtilities {
7
+ /**
8
+ * Run a command using Execa's
9
+ * {@link https://github.com/sindresorhus/execa/blob/main/docs/scripts.md|script mode}.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const { stdout } = await $`echo ${"hello"}`;
14
+ * console.log(`stdout = ${stdout}`);
15
+ * ```
16
+ *
17
+ * @see {@link https://github.com/sindresorhus/execa/blob/main/docs/execution.md#%EF%B8%8F-basic-execution|Execa docs}
18
+ */
19
+ $: ExecaScriptMethod;
20
+ /**
21
+ * Run a command using {@link https://github.com/sindresorhus/execa|Execa}
22
+ * (e.g., shell command or script).
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const { stdout } = await execa`echo 'hello'`;
27
+ * console.log(stdout);
28
+ * ```
29
+ *
30
+ * @see {@link https://github.com/sindresorhus/execa/blob/main/docs/execution.md#%EF%B8%8F-basic-execution|Execa docs}
31
+ */
32
+ command: ExecaMethod;
33
+ /**
34
+ * Same as `command`, but inherits the parent process' stdio streams by
35
+ * default, i.e., logs and errors will be sent directly to the terminal.
36
+ * Use `command` or `$` if you want to capture the output instead.
37
+ */
38
+ exec: ExecaMethod<{
39
+ stdin: "inherit";
40
+ stdout: "inherit";
41
+ stderr: "inherit";
42
+ }>;
43
+ }
44
+ type DefaultOptionsInput = ParsedOptions & {
45
+ env: Record<string, string | undefined>;
46
+ };
47
+ type MergePositionals<TOptions> = "_" extends keyof TOptions ? TOptions : TOptions & ParsedOptions;
48
+ type MergeEnvironment<TOptions> = "env" extends keyof TOptions ? TOptions : TOptions & Pick<DefaultOptionsInput, "env">;
49
+ type Task<TOptions = unknown> = (options: MergePositionals<MergeEnvironment<TOptions>>, utilities: TaskUtilities) => unknown;
50
+ type BrandedTaskStrict<TShape = DefaultOptionsInput> = Task<TShape> & {
51
+ kind: "strictTask";
52
+ schema: Type<TShape>;
53
+ };
54
+ type BrandedTaskLoose<TOptions = unknown> = Task<TOptions> & {
55
+ kind: "task";
56
+ schema: never;
57
+ };
58
+ type BrandedTask<TOptions = unknown> = BrandedTaskLoose<TOptions> | BrandedTaskStrict<TOptions>;
59
+ //#endregion
60
+ export { BrandedTask, BrandedTaskStrict, DefaultOptionsInput, Task };
@@ -0,0 +1,34 @@
1
+ import { reservedNames } from "../cli/parseOptions.mjs";
2
+ import { type } from "arktype";
3
+ import { dim } from "colorette";
4
+ //#region src/util/getSchemaProperties.ts
5
+ const propertyEntry = type({
6
+ key: "string",
7
+ "+": "delete"
8
+ });
9
+ const arktypeJson = type({
10
+ domain: "string",
11
+ required: propertyEntry.array().default(() => []),
12
+ optional: propertyEntry.array().default(() => [])
13
+ });
14
+ const getSchemaProperties = (schema) => {
15
+ const definition = arktypeJson.assert(schema.json);
16
+ if (definition instanceof type.errors || definition.domain !== "object") return "";
17
+ const { required, optional } = definition;
18
+ const requiredProperties = required.map(({ key }) => ({
19
+ key,
20
+ isOptional: false
21
+ }));
22
+ const optionalProperties = optional.map(({ key }) => ({
23
+ key,
24
+ isOptional: true
25
+ }));
26
+ const properties = [...requiredProperties, ...optionalProperties];
27
+ if (properties.length === 0) return "";
28
+ return `{ ${properties.filter(({ key }) => !reservedNames.has(key)).map(({ key, isOptional }) => {
29
+ if (isOptional) return dim(`[${key}]`);
30
+ return key;
31
+ }).join(", ")} }`;
32
+ };
33
+ //#endregion
34
+ export { getSchemaProperties };
@@ -0,0 +1,30 @@
1
+ import { resolve } from "node:path";
2
+ import { loadConfig } from "c12";
3
+ //#region src/util/loadTaskFile.ts
4
+ const loadTaskFile = async (context) => {
5
+ try {
6
+ const collection = await loadConfig({
7
+ cwd: context.taskFile.dir,
8
+ name: context.taskFile.name,
9
+ configFile: context.taskFile.base,
10
+ dotenv: false,
11
+ rcFile: false,
12
+ packageJson: false
13
+ });
14
+ if (!collection.configFile || collection.layers?.length === 0) return {
15
+ ok: false,
16
+ error: /* @__PURE__ */ new Error(`Failed to load task file at path ${resolve(context.taskFile.dir, context.taskFile.base)}`)
17
+ };
18
+ return {
19
+ ok: true,
20
+ value: collection
21
+ };
22
+ } catch (error) {
23
+ return {
24
+ ok: false,
25
+ error: error instanceof Error ? error : new Error(String(error))
26
+ };
27
+ }
28
+ };
29
+ //#endregion
30
+ export { loadTaskFile };
@@ -0,0 +1,13 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { SUPPORTED_EXTENSIONS } from "c12";
4
+ //#region src/util/resolveTaskFile.ts
5
+ const extensions = SUPPORTED_EXTENSIONS.filter((extension) => extension.endsWith("js") || extension.endsWith("ts"));
6
+ const resolveTaskFile = (context) => {
7
+ if (context.taskFile.ext) return resolve(context.taskFile.dir, context.taskFile.name);
8
+ const foundConfigs = extensions.map((it) => join(context.taskFile.dir, `${context.taskFile.name}${it}`)).filter((path) => existsSync(path));
9
+ if (foundConfigs.length === 1) return foundConfigs[0];
10
+ return foundConfigs;
11
+ };
12
+ //#endregion
13
+ export { extensions, resolveTaskFile };
package/package.json CHANGED
@@ -1,77 +1,79 @@
1
1
  {
2
2
  "name": "@haltcase/run",
3
- "version": "3.0.1",
3
+ "version": "4.0.0",
4
4
  "description": "Flexible, function-based task runner where command line options are props",
5
5
  "keywords": [
6
- "task",
7
- "runner",
6
+ "arktype",
8
7
  "command",
9
- "function",
10
- "typescript",
11
8
  "execa",
12
- "arktype"
9
+ "function",
10
+ "runner",
11
+ "task",
12
+ "typescript"
13
13
  ],
14
14
  "homepage": "https://github.com/haltcase/run#readme",
15
15
  "bugs": "https://github.com/haltcase/run/issues",
16
+ "license": "MIT",
17
+ "author": "Bo Lingen <bo@haltcase.dev> (https://haltcase.dev)",
16
18
  "repository": {
17
19
  "type": "git",
18
20
  "url": "https://github.com/haltcase/run.git"
19
21
  },
20
- "license": "MIT",
21
- "author": "Bo Lingen <bo@haltcase.dev> (https://haltcase.dev)",
22
- "type": "module",
23
- "exports": {
24
- ".": "./dist/index.js"
25
- },
26
- "main": "./dist/index.js",
27
- "types": "./dist/index.d.ts",
28
22
  "bin": {
29
- "hr": "./dist/cli/bin.js"
23
+ "hr": "./dist/cli/bin.mjs"
30
24
  },
31
25
  "files": [
32
26
  "dist"
33
27
  ],
34
- "lint-staged": {
35
- "*.{js,jsx,ts,tsx,css,yml,yaml}": "pnpm prettier --write"
28
+ "type": "module",
29
+ "main": "./dist/index.mjs",
30
+ "types": "./dist/index.d.mts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.mts",
34
+ "default": "./dist/index.mjs"
35
+ }
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
36
39
  },
37
- "prettier": "@haltcase/style/prettier",
38
40
  "dependencies": {
39
41
  "@favware/colorette-spinner": "^1.0.1",
40
- "arktype": "^2.1.17",
41
- "c12": "^3.0.3",
42
+ "arktype": "^2.2.0",
43
+ "c12": "^3.3.4",
42
44
  "cliui": "^9.0.1",
43
45
  "colorette": "^2.0.20",
44
- "execa": "^9.5.1"
46
+ "execa": "^9.6.1"
45
47
  },
46
48
  "devDependencies": {
49
+ "@arethetypeswrong/core": "^0.18.3",
47
50
  "@haltcase/style": "^7.3.1",
48
- "@types/node": "^22.8.6",
49
- "eslint": "^9.24.0",
50
- "husky": "^9.1.7",
51
- "lint-staged": "^15.5.1",
52
- "prettier": "^3.5.3",
53
- "tsx": "^4.19.3",
54
- "typescript": "^5.8.3",
55
- "vitest": "^3.1.1"
51
+ "@types/node": "^24.13.1",
52
+ "@typescript/native-preview": "7.0.0-dev.20260608.1",
53
+ "eslint": "^9.39.4",
54
+ "oxlint-tsgolint": "^0.23.0",
55
+ "tsx": "^4.22.4",
56
+ "typescript": "^6.0.3",
57
+ "vite": "npm:@voidzero-dev/vite-plus-core@0.1.24",
58
+ "vite-plus": "0.1.24",
59
+ "vitest": "npm:@voidzero-dev/vite-plus-test@0.1.24"
56
60
  },
57
61
  "engines": {
58
- "node": ">=20"
59
- },
60
- "publishConfig": {
61
- "access": "public"
62
+ "node": ">=22"
62
63
  },
63
64
  "haltcase.run": {
64
65
  "taskDirectory": "./scripts",
65
66
  "quiet": true
66
67
  },
67
68
  "scripts": {
68
- "build": "tsc -p tsconfig.build.json",
69
- "build:watch": "tsc -p tsconfig.build.json --watch",
69
+ "build": "vp pack",
70
+ "build:watch": "vp pack --watch",
71
+ "check": "pnpm eslint:check && vp check --no-lint",
70
72
  "eslint:check": "eslint src",
71
- "format": "eslint --fix src && prettier src --write",
73
+ "format": "eslint --fix src && vp check --fix --no-lint",
72
74
  "hr": "tsx ./src/cli/bin.ts",
73
- "prettier:check": "prettier --check src",
74
- "test": "vitest",
75
- "typescript:check": "tsc --noEmit && echo \"No errors reported by tsc.\""
75
+ "test": "vp test watch",
76
+ "test:run": "vp test",
77
+ "types": "vp check --no-fmt --no-lint"
76
78
  }
77
79
  }
package/dist/cli/bin.d.ts DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
package/dist/cli/bin.js DELETED
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env node
2
- import { createHandler } from "./handler.js";
3
- import { main } from "./main.js";
4
- const handler = createHandler();
5
- void main({
6
- handler
7
- });
@@ -1,28 +0,0 @@
1
- import { Spinner } from "@favware/colorette-spinner";
2
- import type { AppConfig } from "../config.js";
3
- import type { MainContextWithData } from "./main.js";
4
- export declare const failWith: (spinner: Spinner, message: unknown) => never;
5
- export declare const write: (message: string) => void;
6
- interface HelpContextCommand {
7
- command: string;
8
- }
9
- interface HelpContextFile {
10
- taskFileList: true;
11
- config: AppConfig;
12
- }
13
- interface HelpContextScript extends MainContextWithData {
14
- taskList: true;
15
- }
16
- type HelpContext = HelpContextCommand | HelpContextFile | HelpContextScript;
17
- export declare const commandHandler: (_context: HelpContextCommand) => string;
18
- export declare const taskFileListHandler: (context: HelpContextFile) => string;
19
- export declare const taskListHandler: (context: HelpContextScript) => string;
20
- export declare const help: (context: HelpContext) => string;
21
- export interface Handler {
22
- spinner: Spinner;
23
- help: (context: HelpContext) => string;
24
- write: (message: string) => void;
25
- failWith: (message: unknown) => never;
26
- }
27
- export declare const createHandler: () => Handler;
28
- export {};