@haxtheweb/create
Version:
CLI for all things HAX the web
374 lines (370 loc) • 16.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.partyActions = partyActions;
exports.partyCommandDetected = partyCommandDetected;
var fs = _interopRequireWildcard(require("node:fs"));
var _utils = require("../utils.js");
var _open = _interopRequireDefault(require("open"));
var p = _interopRequireWildcard(require("@clack/prompts"));
var _picocolors = _interopRequireDefault(require("picocolors"));
var _statements = require("../statements.js");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
const osPathCmd = process.platform === "win32" ? "where.exe" : "which";
let sysGit = true;
(0, _utils.exec)(`${osPathCmd} git`, error => {
if (error) {
sysGit = false;
}
});
let sysGh = true;
(0, _utils.exec)(`${osPathCmd} gh`, error => {
if (error) {
sysGh = false;
}
});
let sysSSH = true;
function partyActions() {
return [{
value: 'docs',
label: "View the HAX documentation"
}, {
value: 'playground',
label: "Explore HAX.cloud playground"
}, {
value: 'psu',
label: "Visit official hax.psu.edu site"
}, {
value: 'issues',
label: "Engage with HAX issues on GitHub"
}, {
value: 'discord',
label: "Join the HAX Discord community"
}, {
value: 'club',
label: "Check out HAX The Club at PSU"
}, {
value: 'github',
label: "Contribute to core development for HAX"
}];
}
async function partyCommandDetected(commandRun) {
if (!commandRun.options.quiet) {
p.intro(`${_picocolors.default.bgBlack(_picocolors.default.white(` HAXTheWeb : Party detected `))}`);
}
let actions = partyActions();
let actionAssigned = false;
// default to status unless already set so we don't issue a create in a create
if (!commandRun.arguments.action) {
actionAssigned = true;
commandRun.arguments.action = 'party:status';
}
commandRun.command = "party";
let operation = {
...commandRun.arguments,
...commandRun.options
};
actions.push({
value: 'quit',
label: "🚪 Quit"
});
while (operation.action !== 'quit') {
if (!operation.action) {
commandRun = {
command: null,
arguments: {},
options: {
npmClient: `${operation.npmClient}`,
author: `${operation.author}`
}
};
operation = await p.group({
action: ({
results
}) => p.select({
message: `Actions you can take:`,
defaultValue: actions[0],
initialValue: actions[0],
options: actions
})
}, {
onCancel: () => {
if (!commandRun.options.quiet) {
p.cancel('🧙 Merlin: Canceling CLI.. HAX ya later 🪄');
}
process.exit(0);
}
});
}
if (operation.action) {
p.intro(`hax party ${_picocolors.default.bold(operation.action)}`);
}
switch (operation.action) {
case "party:status":
case "party:stats":
if (!commandRun.options.quiet) {
p.intro(`${_picocolors.default.green(_picocolors.default.bold(`
888
888
88888b. 8888b. 888d888888888888 888
888 "88b "88b888P" 888 888 888
888 888.d888888888 888 888 888
888 d88P888 888888 Y88b. Y88b 888
88888P" "Y888888888 "Y888 "Y88888
888 888
888 Y8bd88P`))}`);
}
;
break;
case "docs":
// open the docs
p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` HAX Documentation `))}`);
p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` Opening in browser `))}`);
await (0, _open.default)("https://haxtheweb.org/");
p.outro(`${_picocolors.default.bgBlue(_picocolors.default.white(` https://haxtheweb.org/ `))}`);
break;
case "playground":
// open the playground
p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` HAX Playground `))}`);
p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` Opening in browser `))}`);
await (0, _open.default)("https://hax.cloud/");
p.outro(`${_picocolors.default.bgBlue(_picocolors.default.white(` https://hax.cloud/ `))}`);
break;
case "psu":
// open the psu site
p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` HAX PSU `))}`);
p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` Opening in browser `))}`);
await (0, _open.default)("https://hax.psu.edu/");
p.outro(`${_picocolors.default.bgBlue(_picocolors.default.white(` https://hax.psu.edu/ `))}`);
break;
case "issues":
// open the issues
p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` HAX Issues on Github `))}`);
p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` Opening in browser `))}`);
await (0, _open.default)("https://github.com/haxtheweb/issues/issues");
p.outro(`${_picocolors.default.bgBlue(_picocolors.default.white(` https://github.com/haxtheweb/issues/issues `))}`);
break;
case "discord":
// open the discord
p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` HAX Discord `))}`);
p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` Opening in browser `))}`);
await (0, _open.default)("https://discord.gg/EKYJAjqGhf");
p.outro(`${_picocolors.default.bgBlue(_picocolors.default.white(` https://discord.gg/EKYJAjqGhf `))}`);
break;
case "club":
// open the club
p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` HAX The Club is a student organization at PSU `))}`);
p.intro(`${_picocolors.default.bgBlue(_picocolors.default.white(` Opening in browser `))}`);
await (0, _open.default)("https://orgcentral.psu.edu/organization/hax-the-club");
p.outro(`${_picocolors.default.bgBlue(_picocolors.default.white(` https://orgcentral.psu.edu/organization/hax-the-club `))}`);
// we can change this to the club website when it's up
break;
case "gh":
case "github":
try {
// auto and y are aliases
if (!commandRun.options.root && !commandRun.options.auto) {
commandRun.options.root = await p.text({
message: 'What folder will your HAX projects live in?',
placeholder: process.cwd(),
initialValue: process.cwd(),
required: true,
validate: value => {
if (!value) {
return "Path is required (Enter accepts default)";
}
if (!fs.existsSync(value)) {
return `${value} does not exist. Select a valid folder`;
}
}
});
// subprocesses are based on the root process folder
process.chdir(commandRun.options.root);
}
// list HAX repos
const initialValues = ["webcomponents"];
const options = [{
value: "webcomponents",
label: "Webcomponents Monorepo - haxtheweb/webcomponents"
}, {
value: "create",
label: "Create CLI - haxtheweb/create"
}, {
value: "hax-the-club",
label: "HAX The Club - haxtheweb/hax-the-club"
}, {
value: "haxcms-nodejs",
label: "HAXcms Node.js - haxtheweb/haxcms-nodejs"
}, {
value: "haxcms-php",
label: "HAXcms PHP - haxtheweb/haxcms-php"
}, {
value: "desktop",
label: "HAX The Desktop - haxtheweb/desktop"
}, {
value: "HAXiam",
label: "HAXiam - haxtheweb/HAXiam"
}];
p.note(`${(0, _statements.merlinSays)(`${_picocolors.default.magenta(_picocolors.default.bold('HAX is a party,'))} so you can select ${_picocolors.default.bold('multiple')} repositories at once!`)}
Remember to ${_picocolors.default.bold('fork each project')} on ${_picocolors.default.bold('GitHub')} (or you'll be asked to later!)`);
if (!commandRun.options.repos && commandRun.options.auto) {
commandRun.options.repos = initialValues;
} else if (!commandRun.options.repos) {
commandRun.options.repos = await p.multiselect({
message: 'Choose GitHub repositories to clone',
initialValues: initialValues,
options: options,
required: false
});
} else if (commandRun.options.repos.includes("all")) {
commandRun.options.repos = options.map(item => item.value);
}
await cloneHAXRepositories(commandRun);
} catch (e) {}
break;
case "quit":
// quit
process.exit(0);
break;
}
// y or noi need to act like it ran and finish instead of looping options
if (commandRun.options.y || !commandRun.options.i || !actionAssigned) {
process.exit(0);
}
operation.action = null;
}
}
async function cloneHAXRepositories(commandRun) {
let s = p.spinner();
// check for system dependencies: ssh, yarn, etc.
if (!sysGit) {
console.error(`${_picocolors.default.red(`Git is not installed. The Git CLI is required to access GitHub with ${_picocolors.default.bold('hax party')}.`)}
Please follow the instructions at:
${_picocolors.default.underline(_picocolors.default.cyan(`https://git-scm.com/book/en/v2/Getting-Started-Installing-Git`))}`);
process.exit(1);
}
if (!sysGh) {
try {
const {
stdout,
stderr
} = await (0, _utils.exec)('ssh -T git@github.com');
} catch (error) {
// The GitHub SSH test always returns as stderr
if (!error.stderr.includes('successfully authenticated')) {
sysSSH = false;
}
}
if (!sysSSH) {
console.error(`${_picocolors.default.red(`SSH keys are not set up correctly. SSH is required to access GitHub with ${_picocolors.default.bold('hax party')}.`)}
Please follow the instructions at:
${_picocolors.default.underline(_picocolors.default.cyan(`https://docs.github.com/en/authentication/connecting-to-github-with-ssh`))}`);
process.exit(1);
}
} else {
// gh cli can make new ssh keys and push them to GitHub
try {
await (0, _utils.exec)('gh auth status');
} catch {
console.log(`${_picocolors.default.red(`You have GitHub CLI installed, but it is not properly authenticated.`)}
To access GitHub with ${_picocolors.default.bold('hax party')}, please follow this guided login:
Select the ${_picocolors.default.cyan(`Generate a new SSH key`)} and ${_picocolors.default.cyan(`Login with a web browser`)} options`);
await (0, _utils.interactiveExec)('gh', ['auth', 'login', '-p', 'ssh', '--clipboard']);
}
}
try {
await (0, _utils.exec)(`yarn --version`);
} catch (e) {
await (0, _utils.exec)(`npm install -g yarn`);
}
for (const item of commandRun.options.repos) {
// If GitHub CLI is installed
if (sysGh) {
try {
// ssh link is used since https prompts for password
s.start(`Cloning ${_picocolors.default.bold(item)} to ${_picocolors.default.bold(process.cwd())}`);
await (0, _utils.exec)(`git clone git@github.com:${commandRun.options.author}/${item}.git`);
s.stop(`${_picocolors.default.green("Successfully")} cloned ${_picocolors.default.bold(item)} to ${_picocolors.default.bold(process.cwd())}`);
} catch (e) {
// skip the loop if the repo already exists
if (e.stderr.includes("already exists and is not an empty directory")) {
s.stop(`${_picocolors.default.yellow(`${_picocolors.default.bold(`${item}`)} already exists in ${_picocolors.default.bold(process.cwd())}`)}`);
break;
}
s.stop(`${_picocolors.default.red("Failed")} to clone ${_picocolors.default.bold(item)} to ${_picocolors.default.bold(process.cwd())}`);
p.note(`${_picocolors.default.red(`Error: HAX could not find your personal ${_picocolors.default.bold("fork")} of the ${_picocolors.default.bold(item)} repository on GitHub`)}`);
s.start(`Using ${_picocolors.default.bold(`gh repo`)} utility to fork and clone ${_picocolors.default.bold(item)}`);
await (0, _utils.exec)(`gh repo fork haxtheweb/${item} --clone`);
s.stop(`${_picocolors.default.green("Successfully")} cloned ${_picocolors.default.bold(item)} to ${_picocolors.default.bold(process.cwd())}`);
}
}
// Standard git, not GitHub CLI
else {
// while loop keeps HAX active until the user is ready
let isForked = false;
let firstRun = true;
while (!isForked) {
try {
// ssh link is used since https prompts for password
if (firstRun) {
s.start(`Cloning ${_picocolors.default.bold(item)} to ${_picocolors.default.bold(process.cwd())}`);
} else {
s.start(`Trying again... Cloning ${item} to ${_picocolors.default.bold(process.cwd())}`);
}
await (0, _utils.exec)(`git clone git@github.com:${commandRun.options.author}/${item}.git`);
s.stop(`${_picocolors.default.green("Successfully")} cloned ${_picocolors.default.bold(item)} to ${_picocolors.default.bold(process.cwd())}`);
isForked = true;
} catch (e) {
// skip the loop if the repo already exists
if (e.stderr.includes("already exists and is not an empty directory")) {
s.stop(`${_picocolors.default.yellow(`${_picocolors.default.bold(`${item}`)} already exists in ${_picocolors.default.bold(process.cwd())}`)}`);
break;
}
s.stop(`${_picocolors.default.red("Failed")} to clone ${_picocolors.default.bold(item)} to ${_picocolors.default.bold(process.cwd())}`);
p.note(`${_picocolors.default.red(`Error: HAX cannot find a personal ${_picocolors.default.bold("fork")} of the ${_picocolors.default.bold(item)} repository on your GitHub account`)}
Use the following link to fork ${_picocolors.default.bold(item)}: ${_picocolors.default.underline(_picocolors.default.cyan(`https://github.com/haxtheweb/${item}/fork`))}`);
// We don't want to spam the link every time
if (firstRun) {
p.intro(`${(0, _statements.merlinSays)("The link will open in your browser in a few seconds")}`);
setTimeout(async () => {
await (0, _open.default)(`https://github.com/haxtheweb/${item}/fork`);
}, 3000);
firstRun = false;
}
let response = await p.confirm({
message: `Have you forked the repository? Would you like to try again?`,
initialValue: true
});
// Multiple ways to quit (select no, ctrl+c, etc)
if (p.isCancel(response) || !response) {
p.cancel('🧙 Merlin: Canceling CLI.. HAX ya later 🪄');
process.exit(0);
}
}
}
}
s.start(`Installing dependencies for ${item}`);
switch (item) {
case "webcomponents":
await (0, _utils.exec)(`yarn global add lerna web-component-analyzer`);
await (0, _utils.exec)(`cd ${process.cwd()}/${item} && yarn install`);
break;
case "create":
case "hax-the-club":
case "haxcms-nodejs":
case "desktop":
await (0, _utils.exec)(`cd ${process.cwd()}/${item} && ${commandRun.options.npmClient} install`);
break;
case "haxcms-php":
// ddev setup
break;
case "HAXiam":
// curl setup?
break;
}
s.stop(`Dependencies installed for ${item}`);
}
;
}