Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(create-discord-bot): bun/deno templates (#9795)
  • Loading branch information
iCrawl committed Aug 21, 2023
1 parent 8eb978d commit dd5e745
Show file tree
Hide file tree
Showing 46 changed files with 691 additions and 106 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Expand Up @@ -23,13 +23,20 @@ dist-docs
.vscode
!.vscode/extensions.json
!.vscode/settings.json
!packages/create-discord-bot/template/**/.vscode
.idea
.DS_Store
.turbo
tsconfig.tsbuildinfo
coverage
out

# Deno
deno.lock

# Bun
bun.lockb

# yarn
.pnp.*
.yarn/*
Expand Down
6 changes: 5 additions & 1 deletion .vscode/settings.json
Expand Up @@ -14,5 +14,9 @@
"files.insertFinalNewline": true,
"files.eol": "\n",
"npm.packageManager": "yarn",
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"deno.enablePaths": ["./packages/create-discord-bot/template/Deno"],
"deno.lint": true,
"deno.unstable": false,
"deno.config": "./packages/create-discord-bot/template/Deno/deno.jsonc"
}
3 changes: 2 additions & 1 deletion packages/create-discord-bot/.eslintrc.json
@@ -1,3 +1,4 @@
{
"extends": "../../.eslintrc.json"
"extends": "../../.eslintrc.json",
"ignorePatterns": ["**/template/Deno/*"]
}
2 changes: 2 additions & 0 deletions packages/create-discord-bot/.gitignore
Expand Up @@ -13,6 +13,8 @@ pids

# Env
.env
!template/Bun/.env
!template/Deno/.env
!template/JavaScript/.env
!template/TypeScript/.env

Expand Down
4 changes: 3 additions & 1 deletion packages/create-discord-bot/package.json
Expand Up @@ -45,8 +45,9 @@
},
"homepage": "https://discord.js.org",
"dependencies": {
"chalk": "^5.3.0",
"commander": "^11.0.0",
"fast-glob": "^3.3.1",
"picocolors": "^1.0.0",
"prompts": "^2.4.2",
"validate-npm-package-name": "^5.0.0"
},
Expand All @@ -62,6 +63,7 @@
"eslint-config-neon": "^0.1.47",
"eslint-formatter-pretty": "^5.0.0",
"prettier": "^2.8.8",
"terser": "^5.19.2",
"tsup": "^7.2.0",
"typescript": "^5.1.6",
"vitest": "^0.34.2"
Expand Down
105 changes: 66 additions & 39 deletions packages/create-discord-bot/src/create-discord-bot.ts
@@ -1,27 +1,26 @@
import type { ExecException } from 'node:child_process';
import { cp, stat, mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
import path from 'node:path';
import process from 'node:process';
import { URL } from 'node:url';
import chalk from 'chalk';
import validateProjectName from 'validate-npm-package-name';
import { install, resolvePackageManager } from './helpers/packageManager.js';
import glob from 'fast-glob';
import { red, yellow, green, cyan } from 'picocolors';
import type { PackageManager } from './helpers/packageManager.js';
import { install } from './helpers/packageManager.js';
import { GUIDE_URL } from './util/constants.js';

interface Options {
directory: string;
javascript?: boolean;
packageManager: PackageManager;
typescript?: boolean;
}

export async function createDiscordBot({ typescript, javascript, directory }: Options) {
if (!directory) {
console.error(chalk.red('Please specify the project directory.'));
process.exit(1);
}

export async function createDiscordBot({ directory, typescript, packageManager }: Options) {
const root = path.resolve(directory);
const directoryName = path.basename(root);

console.log();

const directoryStats = await stat(root).catch(async (error) => {
// Create a new directory if the specified one does not exist.
if (error.code === 'ENOENT') {
Expand All @@ -34,47 +33,75 @@ export async function createDiscordBot({ typescript, javascript, directory }: Op

// If the directory is actually a file or if it's not empty, throw an error.
if (!directoryStats.isDirectory() || (await readdir(root)).length > 0) {
console.error(
chalk.red(`The directory ${chalk.yellow(`"${directoryName}"`)} is either not a directory or is not empty.`),
);
console.error(chalk.red(`Please specify an empty directory.`));
console.error(red(`The directory ${yellow(`"${directoryName}"`)} is either not a directory or is not empty.`));
console.error(red(`Please specify an empty directory.`));
process.exit(1);
}

// We'll use the directory name as the project name. Check npm name validity.
const validationResult = validateProjectName(directoryName);
console.log(`Creating ${directoryName} in ${green(root)}.`);
const deno = packageManager === 'deno';
await cp(new URL(`../template/${deno ? 'Deno' : typescript ? 'TypeScript' : 'JavaScript'}`, import.meta.url), root, {
recursive: true,
});

if (!validationResult.validForNewPackages) {
console.error(
chalk.red(
`Cannot create a project named ${chalk.yellow(
`"${directoryName}"`,
)} due to npm naming restrictions.\n\nErrors:`,
),
const bun = packageManager === 'bun';
if (bun) {
await cp(
new URL(`../template/Bun/${typescript ? 'TypeScript' : 'JavaScript'}/package.json`, import.meta.url),
`${root}/package.json`,
);

for (const error of [...(validationResult.errors ?? []), ...(validationResult.warnings ?? [])]) {
console.error(chalk.red(`- ${error}`));
if (typescript) {
await cp(
new URL('../template/Bun/Typescript/tsconfig.eslint.json', import.meta.url),
`${root}/tsconfig.eslint.json`,
);
await cp(new URL('../template/Bun/Typescript/tsconfig.json', import.meta.url), `${root}/tsconfig.json`);
}

console.error(chalk.red('\nSee https://docs.npmjs.com/cli/configuring-npm/package-json for more details.'));
process.exit(1);
}

console.log(`Creating ${directoryName} in ${chalk.green(root)}.`);
await cp(new URL(`../template/${typescript ? 'TypeScript' : 'JavaScript'}`, import.meta.url), root, {
recursive: true,
process.chdir(root);

const newVSCodeSettings = await readFile('./.vscode/settings.json', { encoding: 'utf8' }).then((str) => {
let newStr = str.replace('[REPLACE_ME]', deno || bun ? 'auto' : packageManager);
if (deno) {
// @ts-expect-error: This is fine
newStr = newStr.replaceAll('"[REPLACE_BOOL]"', true);
}

return newStr;
});
await writeFile('./.vscode/settings.json', newVSCodeSettings);

process.chdir(root);
const globStream = glob.stream('./src/**/*.ts');
for await (const file of globStream) {
const newData = await readFile(file, { encoding: 'utf8' }).then((str) =>
str.replaceAll('[REPLACE_IMPORT_EXT]', typescript ? 'ts' : 'js'),
);
await writeFile(file, newData);
}

const newPackageJSON = await readFile('./package.json', { encoding: 'utf8' }).then((str) =>
str.replace('[REPLACE-NAME]', directoryName),
);
const newPackageJSON = await readFile('./package.json', { encoding: 'utf8' }).then((str) => {
let newStr = str.replace('[REPLACE_ME]', directoryName);
newStr = newStr.replaceAll('[REPLACE_IMPORT_EXT]', typescript ? 'ts' : 'js');
return newStr;
});
await writeFile('./package.json', newPackageJSON);

const packageManager = resolvePackageManager();
install(packageManager);
console.log(chalk.green('All done! Be sure to read through the discord.js guide for help on your journey.'));
console.log(`Link: ${chalk.cyan(GUIDE_URL)}`);
try {
install(packageManager);
} catch (error) {
console.log();
const err = error as ExecException;
if (err.signal === 'SIGINT') {
console.log(red('Installation aborted.'));
} else {
console.error(red('Installation failed.'));
process.exit(1);
}
}

console.log();
console.log(green('All done! Be sure to read through the discord.js guide for help on your journey.'));
console.log(`Link: ${cyan(GUIDE_URL)}`);
}
59 changes: 50 additions & 9 deletions packages/create-discord-bot/src/helpers/packageManager.ts
@@ -1,12 +1,12 @@
import { execSync } from 'node:child_process';
import process from 'node:process';
import chalk from 'chalk';
import { yellow } from 'picocolors';
import { DEFAULT_PACKAGE_MANAGER } from '../util/constants.js';

/**
* A union of supported package managers.
*/
export type PackageManager = 'npm' | 'pnpm' | 'yarn';
export type PackageManager = 'bun' | 'deno' | 'npm' | 'pnpm' | 'yarn';

/**
* Resolves the package manager from `npm_config_user_agent`.
Expand All @@ -32,7 +32,7 @@ export function resolvePackageManager(): PackageManager {
}

console.error(
chalk.yellow(
yellow(
`Detected an unsupported package manager (${npmConfigUserAgent}). Falling back to ${DEFAULT_PACKAGE_MANAGER}.`,
),
);
Expand All @@ -47,21 +47,62 @@ export function resolvePackageManager(): PackageManager {
* @param packageManager - The package manager to use
*/
export function install(packageManager: PackageManager) {
let installCommand;
let installCommand: string[] | string = `${packageManager} install`;

console.log(`Installing dependencies with ${packageManager}...`);

switch (packageManager) {
case 'npm':
case 'yarn':
console.log();
installCommand = [
`${packageManager} set version stable`,
`${packageManager} config set nodeLinker node-modules`,
`${packageManager} config set logFilters --json '[{ "code": "YN0002", "level": "discard" }, { "code": "YN0013", "level": "discard" }, { "code": "YN0032", "level": "discard" }, { "code": "YN0060", "level": "discard" }]'`,
`${packageManager} plugin import interactive-tools`,
`${packageManager} plugin import workspace-tools`,
installCommand,
];
break;
case 'deno':
installCommand = `${packageManager} cache --reload src/index.ts`;
break;
case 'pnpm':
installCommand = `${packageManager} install`;
case 'bun':
console.log();
break;
case 'yarn':
installCommand = packageManager;
default:
break;
}

console.log(`Installing dependencies with ${packageManager}...`);
const env = {
...process.env,
ADBLOCK: '1',
NODE_ENV: 'development',
DISABLE_OPENCOLLECTIVE: '1',
};

if (Array.isArray(installCommand)) {
for (const [index, command] of installCommand.entries()) {
if (index === installCommand.length - 1) {
execSync(command, {
stdio: 'inherit',
env,
});

break;
}

execSync(command, {
stdio: 'ignore',
env,
});
}

return;
}

execSync(installCommand, {
stdio: 'inherit',
env,
});
}

0 comments on commit dd5e745

Please sign in to comment.