Skip to content

Commit

Permalink
Merge pull request #63 from expressots/42-integrate-ntl-node-task-lis…
Browse files Browse the repository at this point in the history
…t-into-cli-for-commands-management

feat: add the script command
  • Loading branch information
rsaz authored Aug 8, 2024
2 parents b634184 + d17e583 commit 0f95464
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 1 deletion.
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";

0 comments on commit 0f95464

Please sign in to comment.