From 5021d28bca6ebe1cb4a548f5e595b1220f222c98 Mon Sep 17 00:00:00 2001 From: EvolutionX Date: Mon, 9 May 2022 20:44:48 +0530 Subject: [PATCH 1/5] feat(*): cli is now functional --- src/commands/init.js | 108 +++++++------------ src/prompts/init.js | 58 ++++++++-- src/{commands/test.js => utilities/edits.js} | 14 ++- src/utilities/install.js | 78 ++++++++++++++ src/utilities/npm.js | 21 ++++ 5 files changed, 197 insertions(+), 82 deletions(-) rename src/{commands/test.js => utilities/edits.js} (51%) create mode 100644 src/utilities/install.js create mode 100644 src/utilities/npm.js diff --git a/src/commands/init.js b/src/commands/init.js index 4dc0198..7226fe1 100644 --- a/src/commands/init.js +++ b/src/commands/init.js @@ -1,4 +1,3 @@ -import { findUp } from 'find-up'; import prompts from 'prompts'; import ora from 'ora'; import { redBright, yellowBright } from 'colorette'; @@ -6,18 +5,20 @@ import { execa } from 'execa'; import { cmds_dir, default_prefix, - intent, lang, main_dir, - token, - npmInit, gitInit, + which_manager, + name, } from '../prompts/init.js'; +import { npm } from '../utilities/npm.js'; +import { cloneRepo, installDeps } from '../utilities/install.js'; +import { editMain } from '../utilities/edits.js'; const { prompt } = prompts; -// TODO make this functional and better! export async function init({ flags }) { if (flags?.includes('y')) { + // TODO make this functional console.log("I see a flag there! Seems like you're lazy!\nBye!"); process.exit(0); } @@ -32,75 +33,48 @@ export async function init({ flags }) { return process.exit(1); } - const pkg = await findUp('package.json'); - if (!pkg) { - console.log(`No ${redBright('package.json')} found!`); - const npm = await prompt([npmInit]); - if (!npm.npminit) { - console.log( - `${redBright('Failed')} to initialize Sern!` + - '\nMaybe you should run npm init?' + /** + * TODO edit main_dir and cmds_dir according to user input as well as default_prefix + * will need help @Allyedge + */ + const data = await prompt([name, lang, main_dir, cmds_dir, default_prefix]); + + await cloneRepo(data.lang, data.name); + const git_init = await prompt([gitInit]); + if (!git_init.gitinit) { + console.log(`\nAlright\n`); + } else { + const spin = ora({ + text: 'Initializing git...', + spinner: 'aesthetic', + }).start(); + const exe = await execa('git', ['init', data.name]); + await wait(300); + if (!exe || exe?.failed) { + spin.fail( + `${redBright('Failed')} to initialize git!` + + '\nMaybe you should run git init?' ); return process.exit(1); - } else { - const spin = ora({ - text: 'Initializing npm...', - spinner: 'aesthetic', - }).start(); - const exee = await execa('npm', ['init', '-y']).catch( - () => null - ); /* .stdout.pipe(process.stdout) */ - await wait(300); - if (!exee || exee?.failed) { - spin.fail( - `${redBright('Failed')} to initialize npm!` + - '\nMaybe you should run npm init?' - ); - return process.exit(1); - } else spin.succeed('Npm initialized!'); - } - } - const git = await findUp('.git/config'); - if (!git) { - console.log(`No ${redBright('Git Repository')} found!`); - const git_init = await prompt([gitInit]); - if (!git_init.gitinit) { - console.log( - 'Maybe you should run git init?\n' + `Alright Moving on...` - ); - } else { - const spin = ora({ - text: 'Initializing git...', - spinner: 'aesthetic', - }).start(); - const exe = await execa('git', [ - 'init', - ]); /* .stdout.pipe(process.stdout) */ - await wait(300); - if (!exe || exe?.failed) { - spin.fail( - `${redBright('Failed')} to initialize git!` + - '\nMaybe you should run git init?' - ); - return process.exit(1); - } else spin.succeed('Git initialized!'); - return; - } + } else spin.succeed('Git initialized!'); } + await execa('cd', { cwd: `./${data.name}` }); - const data = await prompt([ - lang, - main_dir, - cmds_dir, - default_prefix, - token, - intent, - ]); - console.log(data); + const pm = await npm(); + let choice = ''; + if (pm === 'both') { + const chosen = await prompt([which_manager]); + choice = chosen.manager; + } else choice = pm; + await installDeps(choice, data.name); + await editMain(data.name); } /** - * @param {number} ms + * Wait for a specified number of milliseconds, then return a promise that resolves to undefined. + * @param {number} ms - The number of milliseconds to wait. + * @returns A function that takes a single argument, ms, and returns a promise that resolves after ms + * milliseconds. */ async function wait(ms) { const wait = (await import('util')).promisify(setTimeout); diff --git a/src/prompts/init.js b/src/prompts/init.js index 6ea7024..80945c8 100644 --- a/src/prompts/init.js +++ b/src/prompts/init.js @@ -1,6 +1,8 @@ import { blueBright } from 'colorette'; -// TODO refactor the intents stuff and add more questions +/** + * @deprecated + */ const Intents = [ 'DIRECT_MESSAGES', 'DIRECT_MESSAGE_REACTIONS', @@ -29,14 +31,21 @@ export const lang = { { title: 'JavaScript', description: 'JS', + value: 'javascript', }, { title: 'TypeScript', description: 'TS', + value: 'typescript', + selected: true, }, ], }; +/** + * @deprecated + * ! Will be removed ! + */ export const intent = { message: 'What intents do you want to keep?', type: 'multiselect', @@ -56,16 +65,18 @@ export const default_prefix = { }; export const token = { - message: 'What is your bot token?', + message: 'What is your bot token? Type "no" to skip', name: 'token', type: 'password', - validate: (/** @type {string} */ token) => - token.match( + validate: (/** @type {string} */ token) => { + if (token === 'no') return true; + return token.match( /(?mfa\.[a-z0-9_-]{20,})|(?[a-z0-9_-]{23,28}\.[a-z0-9_-]{6,7}\.[a-z0-9_-]{27})/i )?.length ? true - : 'Invalid token', + : 'Invalid token'; + }, }; export const main_dir = { @@ -84,13 +95,10 @@ export const cmds_dir = { dir === 'src' ? 'You can not use src as a directory' : true, }; -/** - * @type {import('prompts').PromptObject} - */ -export const npmInit = { - name: 'npminit', +export const projectInit = { + name: 'projectinit', type: 'confirm', - message: `Do you want to ${blueBright('me')} to initialize npm?`, + message: `Do you want to ${blueBright('me')} to initialize project?`, initial: true, }; @@ -100,3 +108,31 @@ export const gitInit = { message: `Do you want to ${blueBright('me')} to initialize git?`, initial: true, }; + +export const which_manager = { + message: `Which manager do you want to use?`, + name: 'manager', + type: 'select', + choices: [ + { + title: 'NPM', + description: 'Default Package Manager', + selected: true, + value: 'npm', + }, + { + title: 'Yarn', + description: 'Yarn Package Manager', + value: 'yarn', + }, + ], +}; + +/** + * @type {import('prompts').PromptObject} + */ +export const name = { + message: 'What is your project name?', + name: 'name', + type: 'text', +}; diff --git a/src/commands/test.js b/src/utilities/edits.js similarity index 51% rename from src/commands/test.js rename to src/utilities/edits.js index 4934480..96d0f0c 100644 --- a/src/commands/test.js +++ b/src/utilities/edits.js @@ -1,16 +1,22 @@ -//@ts-check import { readFile, writeFile } from 'node:fs/promises'; import { findUp } from 'find-up'; + /** - * @param {string} name + * It takes a string, finds the package.json file in the directory of the string, and changes the name + * of the package.json file to the string. + * @param {string} name - The name of the project. + * @returns A promise. */ export async function editMain(name) { - const pjLocation = await findUp('package.json'); + const pjLocation = await findUp('package.json', { + cwd: process.cwd() + '/' + name, + }); + const output = JSON.parse(await readFile(pjLocation, 'utf8')); if (!output) throw new Error("Can't read file."); - output.main = name; + output.name = name; const result = () => writeFile(pjLocation, JSON.stringify(output, null, 2)); return result(); diff --git a/src/utilities/install.js b/src/utilities/install.js new file mode 100644 index 0000000..d25d0bb --- /dev/null +++ b/src/utilities/install.js @@ -0,0 +1,78 @@ +import { execa } from 'execa'; +import { redBright } from 'colorette'; +import fs from 'fs'; +import path from 'path'; +import { readFile } from 'fs/promises'; +import { findUp } from 'find-up'; +import ora from 'ora'; + +/** + * It installs dependencies from a package.json file + * @param choice - The package manager to use. + * @param name - The name of the project + * @returns a promise. + */ +export async function installDeps(choice, name) { + const pkg = await findUp('package.json', { + cwd: process.cwd() + '/' + name, + }); + if (!pkg) throw new Error('No package.json found!'); + const output = JSON.parse(await readFile(pkg, 'utf8')); + if (!output) throw new Error("Can't read file."); + const deps = output.dependencies; + if (!deps) throw new Error("Can't find dependencies."); + const spin = ora({ + text: `Installing dependencies...`, + spinner: 'aesthetic', + }).start(); + const result = await execa(choice, ['install'], { + cwd: process.cwd() + '/' + name, + }).catch(() => null); + if (!result || result?.failed) { + spin.fail(`${redBright('Failed')} to install dependencies!`); + return process.exit(1); + } else spin.succeed(`Dependencies installed!`); +} + +/** + * Clone the repo, copy the files from the repo to the new project directory, and delete the repo + * @param {string} lang - The language of the template + * @param {string} name - The name of the project + */ +export async function cloneRepo(lang, name) { + await execa('git', [ + 'clone', + `https://github.com/sern-handler/templates.git`, // ? See the idea of @Allyedge having templates built in cli + ]); + copyRecursiveSync(`templates/templates/${lang}`, name); + fs.rmSync(`templates/`, { recursive: true, force: true }); +} + +/** + * If the source is a directory, create the destination directory and then recursively copy the + * contents of the source directory to the destination directory. If the source is not a directory, + * copy the source file to the destination file + * @param {string} src - The source path. + * @param {string} dest - The destination folder where the files will be copied to. + */ +function copyRecursiveSync(src, dest) { + var exists = fs.existsSync(src); + var stats = exists && fs.statSync(src); + var isDirectory = exists && stats.isDirectory(); + if (isDirectory) { + fs.mkdirSync(dest); + fs.readdirSync(src).forEach(function (childItemName) { + copyRecursiveSync( + path.join(src, childItemName), + path.join(dest, childItemName) + ); + }); + } else { + fs.copyFileSync(src, dest); + } +} + +// async function wait(ms) { +// const wait = (await import('util')).promisify(setTimeout); +// return wait(ms); +// } diff --git a/src/utilities/npm.js b/src/utilities/npm.js new file mode 100644 index 0000000..2f73a6c --- /dev/null +++ b/src/utilities/npm.js @@ -0,0 +1,21 @@ +import { execa } from 'execa'; + +/** + * It checks if you have npm or yarn installed, and returns a string based on the result + * @returns A promise that resolves to a string. + */ +export async function npm() { + const npm = await execa('npm', ['-v']); + const npm_version = npm?.stdout; + const yarn = await execa('yarn', ['-v']); + const yarn_version = yarn?.stdout; + if (npm_version && !yarn_version) { + return 'npm'; + } + if (!npm_version && yarn_version) { + return 'yarn'; + } + if (npm_version && yarn_version) { + return 'both'; + } +} From 6e006e0073d9d89834ec5256f9fe2c161c6b233b Mon Sep 17 00:00:00 2001 From: EvolutionX Date: Mon, 9 May 2022 20:47:06 +0530 Subject: [PATCH 2/5] chore: shit --- src/utilities/edits.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utilities/edits.js b/src/utilities/edits.js index 96d0f0c..ec5cb17 100644 --- a/src/utilities/edits.js +++ b/src/utilities/edits.js @@ -1,7 +1,6 @@ import { readFile, writeFile } from 'node:fs/promises'; import { findUp } from 'find-up'; - /** * It takes a string, finds the package.json file in the directory of the string, and changes the name * of the package.json file to the string. From f268b1c62fd4d5823d483a33cfef2e2d7f7b127c Mon Sep 17 00:00:00 2001 From: EvolutionX Date: Mon, 9 May 2022 20:52:18 +0530 Subject: [PATCH 3/5] fix: removed useless line --- src/commands/init.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/commands/init.js b/src/commands/init.js index 7226fe1..f598573 100644 --- a/src/commands/init.js +++ b/src/commands/init.js @@ -58,7 +58,6 @@ export async function init({ flags }) { return process.exit(1); } else spin.succeed('Git initialized!'); } - await execa('cd', { cwd: `./${data.name}` }); const pm = await npm(); let choice = ''; From 0094cd5a4532eaa445e7637f0a5b00cd46ee2130 Mon Sep 17 00:00:00 2001 From: EvolutionX Date: Mon, 9 May 2022 20:56:45 +0530 Subject: [PATCH 4/5] chore: update description --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 740ac6d..f8b9e00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "@sern-handler/cli", + "name": "@sern/cli", "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "@sern-handler/cli", + "name": "@sern/cli", "version": "0.1.0", "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 169ee50..03243ca 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@sern/cli", "version": "0.1.0", - "description": "A CLI for @sern-handler", + "description": "A CLI for @sern/handler", "exports": "./src/index.js", "bin": { "sern": "./src/index.js" From 66e2c5f393399b816881e5648951ffb287e9f443 Mon Sep 17 00:00:00 2001 From: EvolutionX Date: Mon, 9 May 2022 22:18:38 +0530 Subject: [PATCH 5/5] chore: this is killing me --- .github/workflows/linter.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 6c1bf94..e0be3d4 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -6,10 +6,15 @@ on: push: branches: - main - pull_request: + pull_request_target: branches: - main +# Down scope as necessary via https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token +permissions: + checks: write + contents: write + jobs: run-linters: name: Run linters