Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add the script command #63

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { createProject } from "./new";
import { addProviderCMD } from "./providers";
import { createExternalProviderCMD } from "./providers/create/cli";
import { printError } from "./utils/cli-ui";
import { scriptsCommand } from "./scripts";

stdout.write(`\n${[chalk.bold.green("🐎 Expressots")]}\n\n`);

Expand All @@ -28,14 +29,14 @@ yargs(hideBin(process.argv))
.command(createExternalProviderCMD())
.command(addProviderCMD())
.command(generateProject())
.command(scriptsCommand())
.command(infoProject())
.command(helpCommand())
.demandCommand(1, "You need at least one command before moving on")
.strict()
.fail((msg, err, yargs) => {
if (msg) {
if (msg.includes("Unknown argument")) {
// Get the command name
const command = process.argv[2];

if (command === "run") {
Expand Down
1 change: 1 addition & 0 deletions src/help/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const helpForm = async (): Promise<void> => {
["new project", "new", "Generate a new project"],
["info", "i", "Provides project information"],
["resources", "r", "Displays cli commands and resources"],
["scripts", "scripts", "Run scripts list or specific scripts"],
["help", "h", "Show command help"],
[
"service",
Expand Down
24 changes: 24 additions & 0 deletions src/scripts/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { CommandModule } from "yargs";
import { scriptsForm } from "./form";

const scriptsCommand = (): CommandModule => {
return {
command: "scripts [scripts..]",
describe: "Run scripts list or specific scripts",
builder: (yargs) => {
return yargs.positional("scripts", {
describe: "The names of the scripts to run",
type: "string",
array: true,
});
},
handler: async (argv) => {
const scripts = Array.isArray(argv.scripts)
? argv.scripts.filter((script) => typeof script === "string")
: [];
await scriptsForm(scripts);
},
};
};

export { scriptsCommand };
133 changes: 133 additions & 0 deletions src/scripts/form.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { ExecSyncOptions, execSync } from "child_process";
import fs from "fs";
import inquirer from "inquirer";
import path from "path";
import { printError, printWarning } from "../utils/cli-ui";

const cwd = process.cwd();
const packageJsonPath = path.join(cwd, "package.json");

interface PackageJson {
scripts?: Record<string, string>;
}

function readPackageJson(): PackageJson {
try {
return JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
} catch (e) {
printError(`Error reading package.json`, "scripts-command");
process.exit(1);
}
}

function listScripts(packageJson: PackageJson): Record<string, string> {
const scripts = packageJson.scripts || {};
if (Object.keys(scripts).length === 0) {
printWarning("No scripts found in package.json", "scripts-command");
process.exit(0);
}
return scripts;
}

async function promptUserToSelectScripts(
scripts: Record<string, string>,
): Promise<{ selectedScripts: string[] }> {
const scriptChoices = Object.keys(scripts).map((key) => ({
name: `${key}`,
value: key,
}));

let selectionOrder: string[] = [];

const answers = await inquirer.prompt([
{
type: "checkbox",
name: "selectedScripts",
message: "Select scripts to run:",
choices: scriptChoices,
filter: (selected: string[]) => {
selectionOrder = selected;
return selected;
},
loop: false,
},
]);

return answers;
}

function executeScripts(
scripts: Record<string, string>,
selectedScripts: string[],
runner: string,
): void {
selectedScripts.forEach((script) => {
console.log(`Running ${script}...`);
try {
const command = `${runner} run ${script}`;
const options: ExecSyncOptions = {
stdio: "inherit",
env: { ...process.env },
};

execSync(command, options);
} catch (e) {
printWarning(
`Command ${script} cancelled or failed - ${e}`,
"scripts-command",
);
}
});
}

process.stdin.on("keypress", (ch, key) => {
if (key && key.name === "escape") {
console.log("Exiting...");
process.exit(0);
}
});

export const scriptsForm = async (scriptArgs: string[] = []): Promise<void> => {
const packageJson = readPackageJson();
const scripts = listScripts(packageJson);

const runner = fs.existsSync("package-lock.json")
? "npm"
: fs.existsSync("yarn.lock")
? "yarn"
: fs.existsSync("pnpm-lock.yaml")
? "pnpm"
: null;

if (!runner) {
printError(
"No package manager found! Please ensure you have npm, yarn, or pnpm installed.",
"scripts-command",
);
process.exit(1);
}

if (scriptArgs.length > 0) {
const validScripts = scriptArgs.filter((script) => scripts[script]);
const invalidScripts = scriptArgs.filter((script) => !scripts[script]);

if (invalidScripts.length > 0) {
console.error(
`Scripts not found in package.json: ${invalidScripts.join(", ")}`,
);
}

if (validScripts.length > 0) {
executeScripts(scripts, validScripts, runner);
}
} else {
const { selectedScripts } = await promptUserToSelectScripts(scripts);

if (selectedScripts.length === 0) {
console.log("No scripts selected.");
process.exit(0);
}

executeScripts(scripts, selectedScripts, runner);
}
};
1 change: 1 addition & 0 deletions src/scripts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./cli";
Loading