Skip to content

Commit b9a7d77

Browse files
committed
feat: add support for passing params directly
1 parent b25d8a9 commit b9a7d77

5 files changed

Lines changed: 203 additions & 111 deletions

File tree

src/commands/run.ts

Lines changed: 139 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,165 @@
11
/* eslint-disable no-await-in-loop */
2+
import * as fs from "node:fs";
23
import { command } from "cleye";
34
import chalk from "chalk";
5+
import { globSync } from "glob";
6+
import * as inquirer from "@inquirer/prompts";
47
import { Project } from "../Project";
58
import { AutoReturnType } from "../types";
69
import { tildify } from "../utils/path";
710
import { dirname, resolve } from "node:path";
8-
import { globSync } from "glob";
9-
import * as inquirer from "@inquirer/prompts";
11+
import * as log from "../utils/logger";
12+
13+
const toKebabCase = (str: string) => {
14+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
15+
};
1016

1117
const createRunCommand = (project: Project, scripts: AutoReturnType[]) =>
12-
command({ name: "run", alias: "r", parameters: ["<script id>"] }, async (argv) => {
13-
const { scriptId } = argv._;
14-
const script = scripts.find((t) => t.id === scriptId);
18+
command(
19+
{
20+
name: "run",
21+
alias: "r",
22+
parameters: ["<script id>"],
23+
flags: {
24+
help: { type: Boolean, alias: "h" },
25+
},
26+
allowUnknownFlags: true,
27+
},
28+
async (argv) => {
29+
const { scriptId } = argv._;
30+
const script = scripts.find((t) => t.id === scriptId);
1531

16-
if (!script) {
17-
console.error(chalk.red(`Error: script "%s" not found.`), scriptId);
18-
process.exit(1);
19-
}
20-
if (script.isValid && !script.isValid(project)) {
21-
console.error(chalk.red(`Error: script "%s" is not valid for this context.`), scriptId);
22-
process.exit(1);
23-
}
32+
if (!script) {
33+
console.error(chalk.red(`Error: script "%s" not found.`), scriptId);
34+
process.exit(1);
35+
}
36+
if (script.isValid && !script.isValid(project)) {
37+
console.error(chalk.red(`Error: script "%s" is not valid for this context.`), scriptId);
38+
process.exit(1);
39+
}
2440

25-
console.log(chalk.blue("Info:"), "Running", chalk.magenta(tildify(script.path)));
41+
console.log(chalk.blue("Info:"), "Running", chalk.magenta(tildify(script.path)));
2642

27-
// gather params
28-
const scriptParams = script.bootstrapParams();
29-
for (const [_, param] of Object.entries(scriptParams)) {
30-
// dynamic default values
31-
if (typeof param.defaultValue === "function") {
32-
const value = param.defaultValue({
33-
project,
34-
params: Object.fromEntries(
35-
Object.entries(scriptParams).map(([key, currentParam]) => {
36-
return [key, currentParam.value];
37-
})
38-
),
39-
});
40-
if (value !== undefined) param.value = value;
41-
}
43+
// gather params
44+
const scriptParams = script.bootstrapParams();
4245

43-
// eslint-disable-next-line default-case
44-
switch (param.type) {
45-
case "boolean": {
46-
param.value = await inquirer.confirm({
47-
message: param.title,
48-
default: param.value as boolean,
49-
});
50-
break;
46+
// get CLI-passed params
47+
const cliParams: Record<string, any> = {};
48+
49+
for (const [key, param] of Object.entries(scriptParams)) {
50+
// cli values
51+
const cliValue = (argv.unknownFlags[key] ?? argv.unknownFlags[toKebabCase(key)] ?? [])[0];
52+
if (cliValue !== undefined) {
53+
switch (param.type) {
54+
case "boolean":
55+
param.value = cliValue === "true" || cliValue === true;
56+
break;
57+
case "number":
58+
param.value = Number(cliValue);
59+
break;
60+
case "string":
61+
param.value = cliValue;
62+
break;
63+
}
64+
cliParams[key] = cliValue;
65+
log.debug("CLI value", key, cliValue);
66+
continue;
5167
}
52-
case "number": {
53-
const stringValue = await inquirer.input({
54-
message: param.title,
55-
default: param.value.toString(),
68+
69+
// dynamic default values
70+
if (typeof param.defaultValue === "function") {
71+
const value = param.defaultValue({
72+
project,
73+
params: Object.fromEntries(
74+
Object.entries(scriptParams).map(([key, currentParam]) => {
75+
return [key, currentParam.value];
76+
})
77+
),
5678
});
57-
param.value = Number(stringValue);
58-
break;
79+
if (value !== undefined) param.value = value;
5980
}
60-
case "string": {
61-
param.value = await inquirer.input({
62-
message: param.title,
63-
default: param.value as string,
64-
});
65-
if (param.required && param.value === "") {
66-
console.error(chalk.red(`Error: Parameter "%s" is required.`), param.title);
67-
process.exit(1);
81+
82+
// input values
83+
if (!(key in cliParams)) {
84+
switch (param.type) {
85+
case "boolean": {
86+
param.value = await inquirer.confirm({
87+
message: param.title,
88+
default: param.value as boolean,
89+
});
90+
break;
91+
}
92+
case "number": {
93+
const stringValue = await inquirer.input({
94+
message: param.title,
95+
default: param.value.toString(),
96+
});
97+
param.value = Number(stringValue);
98+
break;
99+
}
100+
case "string": {
101+
param.value = await inquirer.input({
102+
message: param.title,
103+
default: param.value as string,
104+
});
105+
if (param.required && param.value === "") {
106+
console.error(chalk.red(`Error: Parameter "%s" is required.`), param.title);
107+
process.exit(1);
108+
}
109+
break;
110+
}
68111
}
69-
break;
70112
}
71113
}
72-
}
73-
const paramValues = Object.fromEntries(
74-
Object.entries(scriptParams).map(([key, param]) => {
75-
return [key, param.value];
76-
})
77-
);
78114

79-
const t = (text: string, params: Record<string, boolean | number | string> = paramValues) => {
80-
let result = text;
81-
for (const [key, value] of Object.entries(params)) {
82-
result = result.replace(new RegExp(`__${key}__`, "g"), String(value));
83-
}
115+
const paramValues = Object.fromEntries(
116+
Object.entries(scriptParams).map(([key, param]) => {
117+
return [key, param.value];
118+
})
119+
);
84120

85-
return result;
86-
};
121+
const t = (text: string, params: Record<string, boolean | number | string> = paramValues) => {
122+
let result = text;
123+
for (const [key, value] of Object.entries(params)) {
124+
result = result.replace(new RegExp(`__${key}__`, "g"), String(value));
125+
}
87126

88-
// collect script files
89-
const scriptDir = dirname(script.path);
90-
const files = globSync("**/*", { cwd: scriptDir, dot: true, nodir: true, ignore: script.path }).map((path) => {
91-
return {
92-
path,
93-
get content() {
94-
return fs.readFileSync(resolve(scriptDir, path), "utf8");
95-
},
127+
return result;
96128
};
97-
});
98129

99-
// run the script
100-
script.run({
101-
cwd: process.cwd(),
102-
files,
103-
params: paramValues as Parameters<typeof script.run>[0]["params"],
104-
project: Project.resolveFromPath(process.cwd()),
105-
self: script,
106-
get fileMap() {
107-
return new Proxy(
108-
{},
109-
{
110-
get(_, path) {
111-
if (typeof path !== "string") throw new Error("Invalid path.");
112-
if (!fs.existsSync(resolve(scriptDir, path))) throw new Error(`File "${path}" not found.`);
113-
return fs.readFileSync(resolve(scriptDir, path as string), "utf8");
114-
},
115-
}
116-
);
117-
},
118-
t,
119-
});
120-
});
130+
// collect script files
131+
const scriptDir = dirname(script.path);
132+
const files = globSync("**/*", { cwd: scriptDir, dot: true, nodir: true, ignore: script.path }).map((path) => {
133+
return {
134+
path,
135+
get content() {
136+
return fs.readFileSync(resolve(scriptDir, path), "utf8");
137+
},
138+
};
139+
});
140+
141+
// run the script
142+
script.run({
143+
cwd: process.cwd(),
144+
files,
145+
params: paramValues as Parameters<typeof script.run>[0]["params"],
146+
project: Project.resolveFromPath(process.cwd()),
147+
self: script,
148+
get fileMap() {
149+
return new Proxy(
150+
{},
151+
{
152+
get(_, path) {
153+
if (typeof path !== "string") throw new Error("Invalid path.");
154+
if (!fs.existsSync(resolve(scriptDir, path))) throw new Error(`File "${path}" not found.`);
155+
return fs.readFileSync(resolve(scriptDir, path as string), "utf8");
156+
},
157+
}
158+
);
159+
},
160+
t,
161+
});
162+
}
163+
);
121164

122165
export { createRunCommand };

src/e2e/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ for (const [name, test] of Object.entries(tests)) {
7272
assert.equal(result.stdout.trim(), expectedStdout.trim(), `Test "${test.name ?? name}" stdout is invalid.`);
7373
}
7474
if (test.expected.files) {
75+
const existingFiles = await fs.readdir(cwd);
76+
console.log("Files in cwd:", existingFiles);
7577
for (const [path, expectedContent] of Object.entries(test.expected.files)) {
7678
const filePath = resolve(cwd, path);
7779
const actualContent = await fs.readFile(filePath, "utf8");

src/main.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/* eslint-disable no-await-in-loop */
22
import { dirname, resolve } from "node:path";
33
import { fileURLToPath } from "node:url";
4-
54
import { cli as cleye } from "cleye";
65
import chalk from "chalk";
76
import fs from "fs-extra";
@@ -10,34 +9,50 @@ import { globSync } from "glob";
109
import * as inquirer from "@inquirer/prompts";
1110

1211
import packageJson from "../package.json";
13-
import { Project } from "./Project";
1412
import { getGlobalRepositoryPath, resolveProjectRoot, tildify } from "./utils/path";
13+
import * as log from "./utils/logger";
1514
import { createListCommand } from "./commands/list";
1615
import { createRunCommand } from "./commands/run";
1716
import { createReplCommand } from "./commands/repl";
1817
import { AutoReturnType, autoSymbol } from "./types";
1918
import { setupPackage, setupTSConfig } from "./setup";
19+
import { Project } from "./Project";
2020

2121
const main = async () => {
22+
log.debug("Starting auto...");
2223
const isParentProcess = typeof process.send !== "function";
24+
log.debug("Is parent process:", isParentProcess);
2325

2426
// main repo
2527
const developmentRepositoryPath = resolve(dirname(fileURLToPath(import.meta.url)), "..", "examples");
2628
const globalRepositoryPath = getGlobalRepositoryPath();
2729
const envRepositoryPath = process.env.AUTO_REPO;
30+
log.debug("Repository paths:", {
31+
development: developmentRepositoryPath,
32+
global: globalRepositoryPath,
33+
env: envRepositoryPath,
34+
});
35+
2836
let mainRepositoryPath =
2937
envRepositoryPath ??
3038
globalRepositoryPath ??
3139
(fs.existsSync(developmentRepositoryPath) ? developmentRepositoryPath : null);
3240
const hasMainRepository = fs.existsSync(mainRepositoryPath);
41+
log.debug("Selected main repository:", mainRepositoryPath);
42+
log.debug("Main repository exists:", hasMainRepository);
43+
3344
if (hasMainRepository && isParentProcess) {
3445
console.log(chalk.blue("Info:"), "Using main repository:", chalk.magenta(tildify(mainRepositoryPath)));
3546
}
3647

3748
// local repo
3849
const projectRoot = resolveProjectRoot(process.cwd());
50+
log.debug("Project root:", projectRoot);
3951
const localRepositoryPaths = ["./auto", "./.auto"].map((p) => resolve(projectRoot, p));
52+
log.debug("Checking local repository paths:", localRepositoryPaths);
4053
const localRepositoryPath = localRepositoryPaths.find((p) => fs.existsSync(p));
54+
log.debug("Local repository:", localRepositoryPath);
55+
4156
if (localRepositoryPath && isParentProcess) {
4257
console.log(chalk.blue("Info:"), "Using local repository:", chalk.magenta(tildify(localRepositoryPath)));
4358
}
@@ -46,10 +61,12 @@ const main = async () => {
4661
const repositoryPaths: string[] = [];
4762
if (hasMainRepository) repositoryPaths.push(mainRepositoryPath);
4863
if (localRepositoryPath) repositoryPaths.push(localRepositoryPath);
64+
log.debug("Final repository paths:", repositoryPaths);
4965

5066
// no repo found
5167
if (repositoryPaths.length === 0) {
52-
console.error(chalk.red("Error:"), "Cannot resolve repository directory, to fix this either:");
68+
log.debug("No repositories found");
69+
log.error("Cannot resolve repository directory, to fix this either:");
5370
console.log(`- Create a directory at: ${chalk.magenta(tildify(globalRepositoryPath))}`);
5471
console.log(
5572
`- Create a directory at:\n ${chalk.magenta(resolve(projectRoot, "auto"))}\nor\n ${chalk.magenta(
@@ -182,12 +199,14 @@ const main = async () => {
182199
}
183200

184201
scriptMap[script.id] = script;
185-
// console.log(chalk.green("Success:"), "Loaded:", chalk.magenta(path));
202+
log.debug("Loaded:", path);
186203
} else {
187-
// console.log(chalk.yellow("Skipped:"), "Not a module:", chalk.magenta(file.path));
204+
log.debug("Skipped:", "Not a module:", file.path);
188205
}
189206
}
190207

208+
log.debug("Loaded scripts:", Object.keys(scriptMap));
209+
191210
const project = Project.resolveFromPath(process.cwd());
192211
const scripts = Object.values(scriptMap);
193212

0 commit comments

Comments
 (0)