mirror of
https://github.com/SrIzan10/sern-cli.git
synced 2026-05-01 11:05:17 +00:00
feat: publish command (#105)
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com> Co-authored-by: jacob <jacoobes@sern.dev>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"tabWidth": 4,
|
||||
"useTabs": true,
|
||||
"singleQuote": true
|
||||
"useTabs": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 140
|
||||
}
|
||||
|
||||
539
package-lock.json
generated
539
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -32,8 +32,11 @@
|
||||
},
|
||||
"homepage": "https://sern.dev",
|
||||
"dependencies": {
|
||||
"@esbuild-kit/cjs-loader": "^2.4.2",
|
||||
"@esbuild-kit/esm-loader": "^2.5.5",
|
||||
"colorette": "2.0.20",
|
||||
"commander": "11.0.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"execa": "7.1.1",
|
||||
"find-up": "6.3.0",
|
||||
"ora": "6.3.1",
|
||||
@@ -41,6 +44,7 @@
|
||||
"undici": "5.22.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/parser": "^7.22.5",
|
||||
"@favware/npm-deprecate": "1.0.7",
|
||||
"@types/prompts": "2.4.4",
|
||||
"esbuild-plugin-version-injector": "1.1.0",
|
||||
@@ -49,7 +53,7 @@
|
||||
"typescript": "5.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16.10.x"
|
||||
"node": ">= 18.16.x"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
import { cyanBright, green, magentaBright } from 'colorette';
|
||||
|
||||
export function help() {
|
||||
return `
|
||||
export const help = `
|
||||
___ ___ _ __ _ __
|
||||
/ __|/ _ \\ '__| '_ \\
|
||||
\\__ \\ __/ | | | | |
|
||||
|___/\\___|_| |_| |_|
|
||||
|
||||
Welcome!
|
||||
If you're new to ${cyanBright('sern')}, run ${magentaBright(
|
||||
'npm create @sern/bot'
|
||||
)} for an interactive setup to your new bot project!
|
||||
If you're new to ${cyanBright('sern')}, run ${magentaBright('npm create @sern/bot')} for an interactive setup to your new bot project!
|
||||
|
||||
${green(
|
||||
`If you have any ideas, suggestions, bug reports, kindly join our support server: https://sern.dev/discord`
|
||||
)}`;
|
||||
}
|
||||
${green(`If you have any ideas, suggestions, bug reports, kindly join our support server: https://sern.dev/discord`)}`;
|
||||
|
||||
@@ -3,15 +3,7 @@ import { execa } from 'execa';
|
||||
import { findUp } from 'find-up';
|
||||
import ora from 'ora';
|
||||
import prompt from 'prompts';
|
||||
import {
|
||||
cmds_dir,
|
||||
gitInit,
|
||||
lang,
|
||||
main_dir,
|
||||
name,
|
||||
skip_install_dep,
|
||||
which_manager,
|
||||
} from '../prompts/init.js';
|
||||
import { cmds_dir, gitInit, lang, main_dir, name, skip_install_dep, which_manager } from '../prompts/init.js';
|
||||
|
||||
import { writeFile } from 'fs/promises';
|
||||
import { editDirs, editMain } from '../utilities/edits.js';
|
||||
@@ -21,13 +13,7 @@ import type { PackageManagerChoice } from '../utilities/types.js';
|
||||
|
||||
/** @deprecated Use npm create instead */
|
||||
export async function init(flags: Flags) {
|
||||
console.log(
|
||||
`${yellowBright(
|
||||
'[WARN]:'
|
||||
)} This command is deprecated, use ${greenBright(
|
||||
'npm create @sern/bot'
|
||||
)} instead`
|
||||
);
|
||||
console.log(`${yellowBright('[WARN]:')} This command is deprecated, use ${greenBright('npm create @sern/bot')} instead`);
|
||||
|
||||
let data: PromptData;
|
||||
let git_init = true; // the default;
|
||||
@@ -87,9 +73,7 @@ export async function init(flags: Flags) {
|
||||
if (pm === 'both') {
|
||||
choice = (await prompt([which_manager])).manager;
|
||||
} else {
|
||||
choice = (
|
||||
(await prompt([skip_install_dep])).skip_install_dep ? pm : 'skip'
|
||||
) as PackageManagerChoice;
|
||||
choice = ((await prompt([skip_install_dep])).skip_install_dep ? pm : 'skip') as PackageManagerChoice;
|
||||
}
|
||||
|
||||
await installDeps(choice, data.name);
|
||||
@@ -112,13 +96,7 @@ async function git(data: Data) {
|
||||
await wait(300);
|
||||
spin.succeed('Git initialized!');
|
||||
} catch (error) {
|
||||
spin.fail(
|
||||
`${redBright(
|
||||
'Failed'
|
||||
)} to initialize git!\nTry to install it at ${underline(
|
||||
'https://git-scm.com'
|
||||
)}\nSkipping for now.`
|
||||
);
|
||||
spin.fail(`${redBright('Failed')} to initialize git!\nTry to install it at ${underline('https://git-scm.com')}\nSkipping for now.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,7 @@ export async function plugins() {
|
||||
await download(url);
|
||||
}
|
||||
const pluginNames = e.map((e) => e.split('/').pop());
|
||||
console.log(
|
||||
`Successfully downloaded plugin(s):\n${greenBright(
|
||||
pluginNames.join('\n')
|
||||
)}`
|
||||
);
|
||||
console.log(`Successfully downloaded plugin(s):\n${greenBright(pluginNames.join('\n'))}`);
|
||||
}
|
||||
|
||||
async function download(url: string) {
|
||||
|
||||
37
src/commands/publish.ts
Normal file
37
src/commands/publish.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { getConfig } from '../utilities/getConfig';
|
||||
import { fork } from 'node:child_process';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
export async function publish(commandDir: string | undefined, args: Partial<PublishArgs>) {
|
||||
const config = await getConfig();
|
||||
// pass in args into the command.
|
||||
const rootPath = new URL('../', import.meta.url),
|
||||
publishScript = new URL('./dist/create-publish.js', rootPath);
|
||||
// assign args.import to empty array if non existent
|
||||
args.import ??= [];
|
||||
|
||||
args.token && console.info('token passed through command line');
|
||||
args.applicationId && console.info('applicationId passed through command line');
|
||||
commandDir && console.info('Publishing with override path: ', commandDir);
|
||||
|
||||
const dotenvLocation = new URL('./node_modules/dotenv/config.js', rootPath),
|
||||
esmLoader = new URL('./node_modules/@esbuild-kit/esm-loader/dist/index.js', rootPath);
|
||||
|
||||
// We dynamically load the create-publish script in a child process so that we can pass the special
|
||||
// loader flag to require typescript files
|
||||
const command = fork(fileURLToPath(publishScript), [], {
|
||||
execArgv: ['--loader', esmLoader.toString(), '-r', fileURLToPath(dotenvLocation), '--no-warnings'],
|
||||
env: {
|
||||
token: args.token ?? '',
|
||||
applicationId: args.applicationId ?? '',
|
||||
},
|
||||
});
|
||||
// send paths object so we dont have to recalculate it in script
|
||||
command.send({ config, preloads: args.import, commandDir });
|
||||
}
|
||||
|
||||
interface PublishArgs {
|
||||
import: string[];
|
||||
token: string;
|
||||
applicationId: string;
|
||||
}
|
||||
17
src/create-publish.d.ts
vendored
Normal file
17
src/create-publish.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
export interface PublishableData {
|
||||
name: string;
|
||||
type: number;
|
||||
description: string;
|
||||
absPath: string;
|
||||
options: Typeable[];
|
||||
}
|
||||
export interface Typeable {
|
||||
type: number;
|
||||
}
|
||||
export interface Config {
|
||||
guildIds?: string[];
|
||||
}
|
||||
export interface PublishableModule {
|
||||
data: PublishableData;
|
||||
config: Config;
|
||||
}
|
||||
241
src/create-publish.mts
Normal file
241
src/create-publish.mts
Normal file
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* This file is meant to be run with the esm / cjs esbuild-kit loader to properly import typescript modules
|
||||
*/
|
||||
|
||||
import { readdir, stat, mkdir, writeFile } from 'fs/promises';
|
||||
import { join, basename, extname, resolve } from 'node:path';
|
||||
import { pathExistsSync } from 'find-up';
|
||||
import assert from 'assert';
|
||||
import { once } from 'node:events';
|
||||
import * as Rest from './rest';
|
||||
import type { sernConfig } from './utilities/getConfig';
|
||||
import type { PublishableData, PublishableModule, Typeable } from './create-publish.d.ts';
|
||||
import { cyanBright, greenBright, redBright } from 'colorette';
|
||||
import ora from 'ora';
|
||||
|
||||
async function deriveFileInfo(dir: string, file: string) {
|
||||
const fullPath = join(dir, file);
|
||||
return {
|
||||
fullPath,
|
||||
fileStats: await stat(fullPath),
|
||||
base: basename(file),
|
||||
};
|
||||
}
|
||||
|
||||
function isSkippable(filename: string) {
|
||||
// empty string is for non extension files (directories)
|
||||
const validExtensions = ['.js', '.cjs', '.mts', '.mjs', '.cts', '.ts', ''];
|
||||
return filename[0] === '!' || !validExtensions.includes(extname(filename));
|
||||
}
|
||||
|
||||
async function* readPaths(dir: string, shouldDebug: boolean): AsyncGenerator<string> {
|
||||
try {
|
||||
const files = await readdir(dir);
|
||||
for (const file of files) {
|
||||
const { fullPath, fileStats, base } = await deriveFileInfo(dir, file);
|
||||
|
||||
if (fileStats.isDirectory()) {
|
||||
// TODO: refactor so that i dont repeat myself for files (line 71)
|
||||
if (isSkippable(base)) {
|
||||
if (shouldDebug) console.info(`ignored directory: ${fullPath}`);
|
||||
} else {
|
||||
yield* readPaths(fullPath, shouldDebug);
|
||||
}
|
||||
} else {
|
||||
if (isSkippable(base)) {
|
||||
if (shouldDebug) console.info(`ignored: ${fullPath}`);
|
||||
} else {
|
||||
yield 'file:///' + fullPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// recieved sern config
|
||||
const [{ config, preloads, commandDir }] = await once(process, 'message'),
|
||||
{ paths } = config as sernConfig;
|
||||
for (const preload of preloads) {
|
||||
await import('file:///' + resolve(preload));
|
||||
}
|
||||
|
||||
const commandsPath = commandDir ? resolve(commandDir) : resolve(paths.base, paths.commands);
|
||||
const filePaths = readPaths(commandsPath, true);
|
||||
const modules = [];
|
||||
const PUBLISHABLE = 0b1110;
|
||||
|
||||
for await (const absPath of filePaths) {
|
||||
let mod = await import(absPath);
|
||||
let commandModule = mod.default;
|
||||
let config = mod.config;
|
||||
|
||||
if ('default' in commandModule) {
|
||||
commandModule = commandModule.default;
|
||||
}
|
||||
|
||||
if (typeof config === 'function') {
|
||||
config = config(absPath, commandModule);
|
||||
}
|
||||
|
||||
try {
|
||||
commandModule = commandModule.getInstance();
|
||||
} catch {}
|
||||
|
||||
if ((PUBLISHABLE & commandModule.type) != 0) {
|
||||
// assign defaults
|
||||
const filename = basename(absPath);
|
||||
const filenameNoExtension = filename.substring(0, filename.lastIndexOf('.'));
|
||||
commandModule.name ??= filenameNoExtension;
|
||||
commandModule.description ??= '';
|
||||
commandModule.absPath = absPath;
|
||||
modules.push({ commandModule, config });
|
||||
}
|
||||
}
|
||||
|
||||
const cacheDir = resolve('./.sern');
|
||||
if (!pathExistsSync(cacheDir)) {
|
||||
// TODO: add this in verbose flag
|
||||
// console.log('Making .sern directory: ', cacheDir);
|
||||
await mkdir(cacheDir);
|
||||
}
|
||||
|
||||
const optionsTransformer = (ops: Array<Typeable>) => {
|
||||
return ops.map((el) => {
|
||||
if ('command' in el) {
|
||||
const { command, ...rest } = el;
|
||||
return rest;
|
||||
}
|
||||
return el;
|
||||
});
|
||||
};
|
||||
|
||||
const intoApplicationType = (type: number) => {
|
||||
if (type === 3) {
|
||||
return 1;
|
||||
}
|
||||
return Math.log2(type);
|
||||
};
|
||||
|
||||
const makeDescription = (type: number, desc: string) => {
|
||||
if (type !== 1 && desc !== '') {
|
||||
console.warn('Found context menu that has non empty description field. Implictly publishing with empty description');
|
||||
return '';
|
||||
}
|
||||
return desc;
|
||||
};
|
||||
|
||||
const makePublishData = ({ commandModule, config }: Record<string, Record<string, unknown>>) => {
|
||||
const applicationType = intoApplicationType(commandModule.type as number);
|
||||
return {
|
||||
data: {
|
||||
name: commandModule.name as string,
|
||||
type: applicationType,
|
||||
description: makeDescription(applicationType, commandModule.description as string),
|
||||
absPath: commandModule.absPath as string,
|
||||
options: optionsTransformer((commandModule?.options ?? []) as Typeable[]),
|
||||
dm_permission: config?.dmPermission,
|
||||
default_member_permissions: config?.defaultMemberPermissions ?? null,
|
||||
},
|
||||
config,
|
||||
};
|
||||
};
|
||||
|
||||
// We can use these objects to publish to DAPI
|
||||
const publishableData = modules.map(makePublishData),
|
||||
token = process.env.DISCORD_TOKEN ?? process.env.token,
|
||||
appid = process.env.APPLICATION_ID ?? process.env.applicationId;
|
||||
|
||||
assert(token, 'Could not find a token for this bot in .env or commandline. Do you have DISCORD_TOKEN in env?');
|
||||
assert(appid, 'Could not find an application id for this bot in .env or commandline. Do you have APPLICATION_ID in env?');
|
||||
|
||||
// partition globally published and guilded commands
|
||||
const [globalCommands, guildedCommands] = publishableData.reduce(
|
||||
([globals, guilded], module) => {
|
||||
const isPublishableGlobally = !module.config || !Array.isArray(module.config.guildIds);
|
||||
if (isPublishableGlobally) {
|
||||
return [[module, ...globals], guilded];
|
||||
}
|
||||
return [globals, [module, ...guilded]];
|
||||
},
|
||||
[[], []] as [PublishableModule[], PublishableModule[]]
|
||||
);
|
||||
|
||||
const spin = ora(`Publishing ${cyanBright('Global')} commands`);
|
||||
|
||||
globalCommands.length && spin.start();
|
||||
|
||||
const rest = Rest.create(appid, token);
|
||||
const res = await rest.updateGlobal(globalCommands);
|
||||
|
||||
let globalCommandsResponse: unknown;
|
||||
|
||||
if (res.ok) {
|
||||
spin.succeed(`All ${cyanBright('Global')} commands published`);
|
||||
globalCommandsResponse = await res.json();
|
||||
} else {
|
||||
spin.fail(`Failed to publish global commands [Code: ${redBright(res.status)}]`);
|
||||
if (res.status === 429) {
|
||||
throw Error('Chill out homie, too many requests');
|
||||
}
|
||||
console.error(
|
||||
'errors:',
|
||||
await res.json().then((res) => {
|
||||
const errors = Object.values(res.errors);
|
||||
// @ts-ignore
|
||||
return errors.map((err) => err?.name?._errors);
|
||||
})
|
||||
);
|
||||
console.error(res.statusText);
|
||||
}
|
||||
|
||||
function associateGuildIdsWithData(data: PublishableModule[]): Map<string, PublishableData[]> {
|
||||
const guildIdMap: Map<string, PublishableData[]> = new Map();
|
||||
|
||||
data.forEach((entry) => {
|
||||
const { data, config } = entry;
|
||||
const { guildIds } = config || {};
|
||||
|
||||
if (guildIds) {
|
||||
guildIds.forEach((guildId) => {
|
||||
if (guildIdMap.has(guildId)) {
|
||||
guildIdMap.get(guildId)?.push(data);
|
||||
} else {
|
||||
guildIdMap.set(guildId, [data]);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return guildIdMap;
|
||||
}
|
||||
const guildCommandMap = associateGuildIdsWithData(guildedCommands);
|
||||
|
||||
let guildCommandMapResponse = new Map<string, Record<string, unknown>>();
|
||||
|
||||
for (const [guildId, array] of guildCommandMap.entries()) {
|
||||
const spin = ora(`[${cyanBright(guildId)}] Updating commands for guild`);
|
||||
spin.start();
|
||||
|
||||
const response = await rest.putGuildCommands(guildId, array);
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
guildCommandMapResponse.set(guildId, result);
|
||||
spin.succeed(`[${greenBright(guildId)}] Successfully updated commands for guild`);
|
||||
} else {
|
||||
spin.fail(`[${redBright(guildId)}] Failed to update commands for guild, Reason: ${result.message}`);
|
||||
throw Error(result.message);
|
||||
}
|
||||
}
|
||||
const remoteData = {
|
||||
global: globalCommandsResponse,
|
||||
...Object.fromEntries(guildCommandMapResponse),
|
||||
};
|
||||
|
||||
await writeFile(resolve(cacheDir, 'command-data-remote.json'), JSON.stringify(remoteData, null, 4), 'utf8');
|
||||
|
||||
// TODO: add this in a verbose flag
|
||||
// console.info('View json output in ' + resolve(cacheDir, 'command-data-remote.json'));
|
||||
process.exit(0);
|
||||
33
src/index.ts
33
src/index.ts
@@ -3,6 +3,7 @@
|
||||
import { extra } from './commands/extra.js';
|
||||
import { help } from './commands/help.js';
|
||||
import { init } from './commands/init.js';
|
||||
import { publish } from './commands/publish.js';
|
||||
|
||||
import { Command } from 'commander';
|
||||
import { plugins } from './commands/plugins.js';
|
||||
@@ -10,32 +11,42 @@ import { yellowBright } from 'colorette';
|
||||
export const program = new Command();
|
||||
|
||||
const version: string = '[VI]{{inject}}[/VI]';
|
||||
program
|
||||
|
||||
program //
|
||||
.name('sern')
|
||||
.description(help())
|
||||
.description(help)
|
||||
.version(`sern CLI v${version}`, '-v, --version')
|
||||
.exitOverride(() => process.exit(0));
|
||||
|
||||
program
|
||||
program //
|
||||
.command(init.name)
|
||||
.description(
|
||||
`Quickest way to scaffold a new project ${yellowBright('[DEPRECATED]')}`
|
||||
)
|
||||
.description(`Quickest way to scaffold a new project ${yellowBright('[DEPRECATED]')}`)
|
||||
.option('-y', 'Finishes setup as default')
|
||||
.option('-s, --sync', 'Syncs the project and generates sern.config.json')
|
||||
.action(init);
|
||||
|
||||
program
|
||||
program //
|
||||
.command(plugins.name)
|
||||
.description(
|
||||
'Install plugins from https://github.com/sern-handler/awesome-plugins'
|
||||
)
|
||||
.description('Install plugins from https://github.com/sern-handler/awesome-plugins')
|
||||
.option('-n --name', 'Name of plugin')
|
||||
.action(plugins);
|
||||
|
||||
program
|
||||
program //
|
||||
.command(extra.name)
|
||||
.description('Easy way to add extra things in your sern project')
|
||||
.action(extra);
|
||||
|
||||
program //
|
||||
.command('commands')
|
||||
.description('Defacto way to manage your slash commands')
|
||||
.addCommand(
|
||||
new Command(publish.name)
|
||||
.description('New way to manage your slash commands')
|
||||
.option('-i, --import [scriptPath...]', 'Prerequire a script to load into publisher')
|
||||
.option('-t, --token [token]')
|
||||
.option('--appId [applicationId]')
|
||||
.argument('[path]', 'path with respect to current working directory that will locate all published files')
|
||||
.action(publish)
|
||||
);
|
||||
|
||||
program.parse();
|
||||
|
||||
@@ -36,8 +36,7 @@ export const cmds_dir: PromptObject = {
|
||||
name: 'cmds_dir',
|
||||
type: 'text',
|
||||
initial: 'commands',
|
||||
validate: (dir: string) =>
|
||||
dir === 'src' ? 'You can not use src as a directory' : true,
|
||||
validate: (dir: string) => (dir === 'src' ? 'You can not use src as a directory' : true),
|
||||
};
|
||||
|
||||
export const npmInit: PromptObject = {
|
||||
@@ -89,8 +88,5 @@ export const name: PromptObject = {
|
||||
message: 'What is your project name?',
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
validate: (name: string) =>
|
||||
name.match('^(?:@[a-z0-9-*~][a-z0-9-*._~]*/)?[a-z0-9-~][a-z0-9-._~]*$')
|
||||
? true
|
||||
: 'Invalid name',
|
||||
validate: (name: string) => (name.match('^(?:@[a-z0-9-*~][a-z0-9-*._~]*/)?[a-z0-9-~][a-z0-9-._~]*$') ? true : 'Invalid name'),
|
||||
};
|
||||
|
||||
@@ -16,8 +16,7 @@ async function gimmechoices(): Promise<Choice[]> {
|
||||
const link = `https://api.github.com/repos/sern-handler/awesome-plugins/contents/${lang}`;
|
||||
|
||||
const resp = await fetch(link).catch(() => null);
|
||||
if (!resp)
|
||||
return [{ title: 'No plugins found!', value: '', disabled: true }];
|
||||
if (!resp) return [{ title: 'No plugins found!', value: '', disabled: true }];
|
||||
|
||||
const data = (await resp.json()) as Data[];
|
||||
const choices = data.map((e) => ({
|
||||
|
||||
41
src/rest.ts
Normal file
41
src/rest.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import type { PublishableModule } from './create-publish.d.ts';
|
||||
|
||||
const baseURL = new URL('https://discord.com/api/v10/applications/');
|
||||
|
||||
const excludedKeys = new Set(['command', 'absPath']);
|
||||
|
||||
const publishablesIntoJson = (ps: PublishableModule[]) =>
|
||||
JSON.stringify(
|
||||
ps.map((module) => module.data),
|
||||
(key, value) => (excludedKeys.has(key) ? undefined : value),
|
||||
4
|
||||
);
|
||||
|
||||
export const create = (appid: string, token: string) => {
|
||||
const globalURL = new URL(`${appid}/commands`, baseURL);
|
||||
const headers = {
|
||||
Authorization: 'Bot ' + token,
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
return {
|
||||
updateGlobal: (commands: PublishableModule[]) =>
|
||||
fetch(globalURL, {
|
||||
method: 'PUT',
|
||||
body: publishablesIntoJson(commands),
|
||||
headers,
|
||||
}),
|
||||
getGuildCommands: (id: string) => {
|
||||
const guildCommandURL = new URL(`${appid}/guilds/${id}/commands`, baseURL);
|
||||
return fetch(guildCommandURL, { headers });
|
||||
},
|
||||
|
||||
putGuildCommands: (guildId: string, guildCommand: any) => {
|
||||
const guildCommandURL = new URL(`${appid}/guilds/${guildId}/commands`, baseURL);
|
||||
return fetch(guildCommandURL, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(guildCommand),
|
||||
headers,
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -15,17 +15,10 @@ const extraFolder = fileURLToPath(extraURL);
|
||||
* @param location - The location of the file to be created.
|
||||
* @param no_ext - If true, the file will be created without an extension.
|
||||
*/
|
||||
export async function create(
|
||||
name: string,
|
||||
lang: string,
|
||||
location: string,
|
||||
no_ext: boolean
|
||||
) {
|
||||
export async function create(name: string, lang: string, location: string, no_ext: boolean) {
|
||||
const file = `${name}.${lang}.sern`;
|
||||
|
||||
const target = no_ext
|
||||
? `${location}/${name}`
|
||||
: `${location}/${name}.${lang}`;
|
||||
const target = no_ext ? `${location}/${name}` : `${location}/${name}.${lang}`;
|
||||
|
||||
return createFile(file, target);
|
||||
}
|
||||
|
||||
@@ -74,10 +74,7 @@ export async function editDirs(
|
||||
const newfold = ext === 'ts' ? 'dist' : srcName;
|
||||
|
||||
const regex = new RegExp(`commands: '${oldfold}/commands'`);
|
||||
const edit = output.replace(
|
||||
regex,
|
||||
`commands: '${newfold}/${cmds_dirName}'`
|
||||
);
|
||||
const edit = output.replace(regex, `commands: '${newfold}/${cmds_dirName}'`);
|
||||
|
||||
return writeFile(index, edit);
|
||||
}
|
||||
|
||||
23
src/utilities/getConfig.ts
Normal file
23
src/utilities/getConfig.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { findUp } from 'find-up';
|
||||
import assert from 'node:assert';
|
||||
export async function getConfig(): Promise<sernConfig> {
|
||||
const sernLocation = await findUp('sern.config.json');
|
||||
assert(sernLocation, "Can't find sern.config.json");
|
||||
|
||||
const output = JSON.parse(await readFile(sernLocation, 'utf8')) as sernConfig;
|
||||
|
||||
assert(output, "Can't read your sern.config.json.");
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
export interface sernConfig {
|
||||
language: 'typescript' | 'javascript';
|
||||
defaultPrefix?: string;
|
||||
paths: {
|
||||
base: string;
|
||||
commands: string;
|
||||
events?: string;
|
||||
};
|
||||
}
|
||||
@@ -50,18 +50,11 @@ export async function installDeps(choice: PackageManagerChoice, name: string) {
|
||||
*/
|
||||
export async function cloneRepo(lang: string, name: string) {
|
||||
try {
|
||||
await execa('git', [
|
||||
'clone',
|
||||
`https://github.com/sern-handler/templates.git`,
|
||||
]);
|
||||
await execa('git', ['clone', `https://github.com/sern-handler/templates.git`]);
|
||||
copyRecursiveSync(`templates/templates/${lang}`, name);
|
||||
fs.rmSync(`templates/`, { recursive: true, force: true });
|
||||
} catch (error) {
|
||||
console.log(
|
||||
`${redBright(
|
||||
'✖ Failed'
|
||||
)} to clone github templates repo. Install git and try again!`
|
||||
);
|
||||
console.log(`${redBright('✖ Failed')} to clone github templates repo. Install git and try again!`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -83,10 +76,7 @@ export function copyRecursiveSync(src: string, dest: string) {
|
||||
fs.mkdirSync(dest);
|
||||
|
||||
fs.readdirSync(src).forEach(function (childItemName) {
|
||||
copyRecursiveSync(
|
||||
path.join(src, childItemName),
|
||||
path.join(dest, childItemName)
|
||||
);
|
||||
copyRecursiveSync(path.join(src, childItemName), path.join(dest, childItemName));
|
||||
});
|
||||
} else {
|
||||
fs.copyFileSync(src, dest);
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { defineConfig } from 'tsup';
|
||||
import { esbuildPluginVersionInjector } from 'esbuild-plugin-version-injector';
|
||||
const shared = {
|
||||
entry: ['src/index.ts'],
|
||||
entry: ['src/index.ts', 'src/create-publish.mts'],
|
||||
clean: true,
|
||||
sourcemap: true,
|
||||
};
|
||||
export default defineConfig({
|
||||
format: 'esm',
|
||||
target: 'node16',
|
||||
target: 'node18',
|
||||
tsconfig: './tsconfig.json',
|
||||
outDir: './dist',
|
||||
treeshake: true,
|
||||
esbuildPlugins: [esbuildPluginVersionInjector()],
|
||||
platform: 'node',
|
||||
splitting: false,
|
||||
...shared,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user