Skip to content

Commit

Permalink
docs for create
Browse files Browse the repository at this point in the history
  • Loading branch information
catplvsplus committed Jun 18, 2024
1 parent 50be64e commit 95b2bb0
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 1 deletion.
25 changes: 25 additions & 0 deletions packages/create-reciple/src/classes/Addon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ export class Addon implements AddonOptions {
this.registry = options?.registry ?? Addon.NPM_REGISTRY;
}

/**
* Asynchronously fetches package metadata and downloads the tarball for the specified module.
* If the metadata is already available, it will be used instead of fetching it again.
*
* @return {Promise<AbbreviatedVersion>} The fetched package metadata.
*/
public async fetch(): Promise<AbbreviatedVersion> {
this.metadata ??= await fetchPackage(this.module, { version: this.version, registry: this.registry });
await this.downloadTarball();
Expand All @@ -60,6 +66,12 @@ export class Addon implements AddonOptions {
return this.metadata;
}

/**
* Reads the tarball and extracts the necessary data from it.
*
* @return {Promise<AddonReadTarballData>} The data extracted from the tarball.
* @throws {Error} If the tarball is not downloaded or if the temporary directory does not exist.
*/
public async readTarball(): Promise<AddonReadTarballData> {
if (!this.tarball || !this.tmpDir) throw new Error('Tarball not downloaded');
if (this.tarballData) return this.tarballData;
Expand All @@ -86,6 +98,13 @@ export class Addon implements AddonOptions {
};
}

/**
* Downloads the tarball from the specified tarballURL if it hasn't been downloaded already.
* Verifies the tarball's shasum against the stored tarballShasum.
*
* @return {Promise<Buffer|undefined>} The downloaded tarball as a Buffer, or undefined if the tarballURL is falsy or the tarball already exists.
* @throws {Error} If the tarball cannot be downloaded or if the shasum does not match.
*/
protected async downloadTarball(): Promise<Buffer|undefined> {
if (!this.tarballURL) return;
if (this.tarball) return this.tarball;
Expand All @@ -101,6 +120,12 @@ export class Addon implements AddonOptions {
return this.tarball = buffer;
}

/**
* Verifies the tarball's shasum against the stored tarballShasum.
*
* @param {Buffer} buffer - The tarball buffer to be verified.
* @return {Promise<boolean>} A promise that resolves to a boolean indicating whether the shasum matches or not.
*/
protected async verifyTarball(buffer: Buffer): Promise<boolean> {
const i = createHash('sha1');
i.update(buffer);
Expand Down
17 changes: 17 additions & 0 deletions packages/create-reciple/src/classes/Setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ export class Setup implements SetupOptions {
this.token = options?.token;
}

/**
* Prompts the user for input to set up the necessary parameters for creating a reciple.
*
* @param {boolean} force - Whether to force override existing files in the directory.
* @return {Promise<this>} A promise that resolves to the current instance of the Setup class.
*/
public async prompt(force: boolean = false): Promise<this> {
if (this.isDone) return this;

Expand Down Expand Up @@ -145,11 +151,22 @@ export class Setup implements SetupOptions {
return isCancel(newToken) ? this.cancelPrompts() : this.token = newToken;
}

/**
* Cancels the prompts and exits the process with the given reason.
*
* @param {string} [reason] - The reason for cancelling the prompts.
* @return {Promise<never>} - A promise that never resolves.
*/
public async cancelPrompts(reason?: string): Promise<never> {
cancel(reason ?? 'Operation cancelled');
process.exit(0);
}

/**
* Returns a SetupOptions object with the current setup details.
*
* @return {SetupOptions} The setup options object.
*/
public toJSON(): SetupOptions {
return {
dir: this.dir,
Expand Down
66 changes: 66 additions & 0 deletions packages/create-reciple/src/classes/TemplateBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,26 @@ export class TemplateBuilder implements TemplateBuilderOptions {
this.template = options.template;
}

/**
* Builds the template by performing the following steps:
* 1. Initializes the spinner.
* 2. Creates the directory if it does not exist.
* 3. Copies the template files.
* 4. Copies the assets.
* 5. Sets up the package.json file.
* 6. Sets up the configuration.
* 7. Sets up the addons.
* 8. Sets up the environment.
* 9. Succeeds the spinner.
* 10. Installs dependencies if the package manager is specified.
* 11. Logs the project readiness message.
* 12. Logs the start developing message.
* 13. Logs the command to navigate to the project directory if the current working directory is not the project directory.
* 14. Logs the command to install all dependencies if the package manager is not specified.
* 15. Logs the command to run the dev script.
*
* @return {Promise<void>} A Promise that resolves when the build is complete.
*/
public async build(): Promise<void> {
this.spinner = ora({
text: '',
Expand Down Expand Up @@ -72,12 +92,22 @@ export class TemplateBuilder implements TemplateBuilderOptions {
console.log(` • ${kleur.cyan().bold(`${this.packageManagerPlaceholders.SCRIPT_RUN} dev`)}`);
}

/**
* Copies the template files to the specified directory.
*
* @return {Promise<void>} A Promise that resolves when the template files are copied.
*/
public async copyTemplateFiles(): Promise<void> {
this.setSpinnerText('Copying template files...');
await recursiveCopyFiles(this.template.path, this.dir, f => f.replace('dot.', '.'));
this.persistSpinner({ symbol: kleur.bold().green('✔'), text: 'Template files copied.' });
}

/**
* Copies the asset files from the root directory to the current directory.
*
* @return {Promise<void>} A Promise that resolves when the asset files are copied.
*/
public async copyAssets(): Promise<void> {
this.setSpinnerText('Copying asset files...');

Expand All @@ -88,6 +118,11 @@ export class TemplateBuilder implements TemplateBuilderOptions {
this.persistSpinner({ symbol: kleur.bold().green('✔'), text: 'Copied all assets.' });
}

/**
* Configures the package versions and scripts in the package.json file.
*
* @return {Promise<void>} A Promise that resolves when the package.json file is configured.
*/
public async setupPackageJson(): Promise<void> {
this.setSpinnerText(`Configuring ${kleur.green('package.json')} package versions...`);

Expand All @@ -108,12 +143,30 @@ export class TemplateBuilder implements TemplateBuilderOptions {
this.persistSpinner({ symbol: kleur.bold().green('✔'), text: `${kleur.green('package.json')} configured.` });
}

/**
* Sets up the configuration for the `reciple.mjs` file by creating a new
* configuration file in the specified directory. This function does not take
* any parameters and returns a Promise that resolves when the configuration
* file is successfully created.
*
* @return {Promise<void>} A Promise that resolves when the configuration
* file is successfully created.
*/
public async setupConfig(): Promise<void> {
this.setSpinnerText(`Configuring ${kleur.green('reciple.mjs')}...`);
await ConfigReader.createConfigJS(path.join(this.dir, 'reciple.mjs'));
this.persistSpinner({ symbol: kleur.bold().green('✔'), text: `${kleur.green('reciple.mjs')} configured.` });
}

/**
* Sets up the addons for the template by installing the specified addons
* and updating the package.json file with their dependencies. This function
* takes no parameters and returns a Promise that resolves when the addons
* are successfully installed.
*
* @return {Promise<void>} A Promise that resolves when the addons are
* installed.
*/
public async setupAddons(): Promise<void> {
const addons = (this.setup.addons ?? []).map(a => new Addon({ module: a, version: Addon.DEFAULT_ADDON_VERSIONS[a as keyof typeof Addon.DEFAULT_ADDON_VERSIONS] || undefined }));
if (!addons.length) return;
Expand Down Expand Up @@ -153,6 +206,14 @@ export class TemplateBuilder implements TemplateBuilderOptions {
this.persistSpinner({ symbol: kleur.bold().green('✔'), text: `${kleur.green('Addons installed.')}` });
}

/**
* Sets up the environment by updating the .env file with the Discord bot token.
* If the file already exists, it reads the content and appends the token.
* If the token is not already present in the file, it adds a comment and the token.
* The function does not return anything.
*
* @return {Promise<void>} A Promise that resolves when the environment setup is complete.
*/
public async setupEnv(): Promise<void> {
const file = path.resolve(path.join(this.dir, '.env'));

Expand All @@ -167,6 +228,11 @@ export class TemplateBuilder implements TemplateBuilderOptions {
await writeFile(file, content);
}

/**
* Installs dependencies by running a script with the package manager placeholders.
*
* @return {Promise<void>} A Promise that resolves when the dependencies are installed.
*/
public async installDependencies(): Promise<void> {
await runScript(this.packageManagerPlaceholders.INSTALL_ALL, this.dir);
}
Expand Down
29 changes: 28 additions & 1 deletion packages/create-reciple/src/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import { kleur } from 'fallout-utility/strings';
import { execSync } from 'node:child_process';
import path from 'node:path';

/**
* Retrieves and returns template metadata from the specified directory.
*
* @param {string} dir - The directory path to retrieve templates from.
* @return {Promise<TemplateMetadata[]>} An array of template metadata objects.
*/
export async function getTemplates(dir: string): Promise<TemplateMetadata[]> {
if (!await existsAsync(dir)) {
await mkdir(dir, { recursive: true });
Expand Down Expand Up @@ -38,6 +44,14 @@ export async function getTemplates(dir: string): Promise<TemplateMetadata[]> {
return templates;
}

/**
* Recursively copies files from one directory to another.
*
* @param {string} from - The source directory path.
* @param {string} to - The destination directory path.
* @param {(f: string) => string} [rename] - An optional function to rename files during the copy process.
* @return {Promise<void>} A promise that resolves when the copy is complete.
*/
export async function recursiveCopyFiles(from: string, to: string, rename?: (f: string) => string): Promise<void> {
if ((await stat(from)).isDirectory()) {
const contents = await readdir(from);
Expand All @@ -55,7 +69,14 @@ export async function recursiveCopyFiles(from: string, to: string, rename?: (f:
await copyFile(from, to);
}

export async function runScript(command: string, cwd?: string) {
/**
* Executes a shell command and logs the command before running it. If the command fails, the process exits with a status code of 1.
*
* @param {string} command - The shell command to execute.
* @param {string} [cwd] - The current working directory in which to execute the command. Defaults to the current working directory.
* @return {Promise<void>} A promise that resolves when the command has completed.
*/
export async function runScript(command: string, cwd?: string): Promise<void> {
console.log(kleur.gray(kleur.bold('$') + ' ' + command));
try {
execSync(`${command}`, { cwd, env: { ...process.env, FORCE_COLOR: '1' }, stdio: ['inherit', 'inherit', 'inherit'] });
Expand All @@ -64,6 +85,12 @@ export async function runScript(command: string, cwd?: string) {
}
}

/**
* Checks if a directory is empty by filtering out hidden files.
*
* @param {string} dir - The directory path to check.
* @return {Promise<boolean>} True if the directory is empty, false otherwise.
*/
export async function isDirEmpty(dir: string): Promise<boolean> {
if (!await existsAsync(dir)) return true;

Expand Down

0 comments on commit 95b2bb0

Please sign in to comment.