Skip to content

Commit

Permalink
feat: add command: new
Browse files Browse the repository at this point in the history
build(deps): downgrade `ora` to `5.4.1`

feat: move repository url to config file

feat: add template for the config file

feat: add a prompt for `new` command
  • Loading branch information
enxg committed Sep 12, 2021
1 parent 5fe04e6 commit 4afa8cd
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 80 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"@oclif/config": "^1",
"@oclif/plugin-help": "^3",
"chalk": "^4.1.2",
"ora": "^6.0.0",
"ora": "5.4.1",
"prompts": "^2.4.1",
"tslib": "^2.3.1"
},
Expand Down Expand Up @@ -70,7 +70,7 @@
"/oclif.manifest.json"
],
"engines": {
"node": ">=12",
"node": ">=16.7.0",
"npm": ">=6"
},
"keywords": [
Expand Down
136 changes: 136 additions & 0 deletions src/commands/new.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { CommandExists, CreateFileFromTemplate } from '#functions';
import { PromptNew } from '#prompts';
import { Command, flags } from '@oclif/command';
import { exec, spawn } from 'child_process';
import { existsSync } from 'fs';
import { cp, readFile, rm, writeFile } from 'fs/promises';
import ora from 'ora';
import { resolve } from 'path';
import prompts from 'prompts';
import { config } from '../config';
import chalk from 'chalk';

export default class New extends Command {
public async run() {
const { args, flags } = this.parse(New);
const response = await prompts(PromptNew(args.ProjectName, await CommandExists('yarn')));
if (!response.projectName || !response.projectLang || !response.projectTemplate || !response.packageManager) {
process.exit(1);
}
const projectName = response.projectName === '.' ? '' : response.projectName;

const gitSpinner = ora('Cloning the repository').start();
await this.cloneRepo(response.projectName, flags.verbose).catch((err) => {
gitSpinner.fail('An error occurred while cloning the repository');
console.log(chalk.red(err.message));
process.exit(1);
});
gitSpinner.succeed('Cloned the repository');

const stpSpinner = ora('Setting up the project').start();
const stpSpinnerFail = (err: Error) => {
stpSpinner.fail('An error occurred while setting up the project');
console.log(chalk.red(err.message));
process.exit(1);
};

await cp(`./${response.projectName}/ghr/examples/${response.projectTemplate}/.`, `./${response.projectName}/`, { recursive: true }).catch(
stpSpinnerFail
);
await rm(`./${response.projectName}/ghr`, { recursive: true, force: true }).catch(stpSpinnerFail);

await CreateFileFromTemplate('.sapphirerc.json', resolve(`./${response.projectName}/.sapphirerc.json`), {
language: response.projectLang
}).catch(stpSpinnerFail);
await this.editPackageJson(response.projectName, projectName).catch(stpSpinnerFail);

stpSpinner.succeed();

const pmSpinner = ora(`Installing dependencies using ${response.packageManager}`).start();
await this.installDeps(response.projectName, response.packageManager, flags.verbose).catch((err) => {
pmSpinner.fail('An error occurred while installing the dependencies.');
console.log(chalk.red(err.message));
process.exit(1);
});
await pmSpinner.succeed();

if (response.git) {
const gitSpinner = ora('Initializing git repo').start();
await this.initializeGitRepo(response.projectName).catch((err) => {
gitSpinner.fail('An error occurred while initializing the git repo');
console.log(chalk.red(err.message));
process.exit(1);
});
gitSpinner.succeed();
}

console.log(chalk.blueBright('Done!'));
}

public cloneRepo(location: string, verbose: boolean) {
const git = spawn('git', ['clone', config.repoUrl, `${location}/ghr`], {
stdio: verbose ? 'inherit' : undefined
});

return new Promise((resolve, reject) => {
git.on('error', reject);

git.on('exit', (code) => {
code === 0
? resolve(true)
: reject(new Error('An unknown error occured while cloning the repository. Try running Sapphire CLI with "--verbose" flag.'));
});
});
}

public editPackageJson(location: string, name: string) {
return new Promise(async (resolve, reject) => {
const pjLocation = `./${location}/package.json`;
const output = JSON.parse(await readFile(pjLocation, 'utf8'));
if (!output) return reject(new Error("Can't read file."));

output.name = name;
await writeFile(pjLocation, JSON.stringify(output, null, 2)).catch(reject);
return resolve(true);
});
}

public installDeps(location: string, pm: string, verbose: boolean) {
const pmp = spawn(pm.toLowerCase(), ['install'], {
stdio: verbose ? 'inherit' : undefined,
cwd: `./${location}/`
});

return new Promise((resolve, reject) => {
pmp.on('error', reject);

pmp.on('exit', async (code) => {
if (code === 0) {
const lockfile = `./${location}/${pm === 'npm' ? 'yarn.lock' : 'package-lock.json'}`;
if (existsSync(lockfile)) await rm(lockfile);
resolve(true);
} else {
reject(new Error('An unknown error occured while installing the dependencies. Try running Sapphire CLI with "--verbose" flag.'));
}
});
});
}

public initializeGitRepo(location: string) {
return new Promise((resolve, reject) => {
return exec('git init', { cwd: `./${location}/` }, (e) => {
if (!e) return resolve(true);
return reject(e);
});
});
}

public static description = 'create a new Sapphire project';

public static flags = {
help: flags.help({ char: 'h' }),
verbose: flags.boolean({ char: 'v' })
};

public static args = [{ name: 'ProjectName', default: 'NewSapphireProject' }];
}
3 changes: 3 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const config = {
repoUrl: 'https://github.com/sapphiredev/examples.git'
};
53 changes: 53 additions & 0 deletions src/prompts/PromptNew.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { Choice, PromptObject } from 'prompts';

const tsTemplates: Choice[] = [
{ title: 'Default template (Recommended)', value: 'with-typescript-recommended' },
{ title: 'with Docker', value: 'with-docker' }
];
const jsTemplates: Choice[] = [{ title: 'Default template (Recommended)', value: 'with-javascript', selected: true }];

export const PromptNew = (projectName: string, yarn: boolean) => {
const pmChoices = [
{
title: `Yarn (Recommended) ${yarn ? '' : '(Not installed)'}`,
value: 'Yarn',
disabled: !yarn
},
{ title: 'npm', value: 'npm' }
];

return [
{
type: 'text',
name: 'projectName',
message: "What's the name of your project?",
initial: projectName
},
{
type: 'select',
name: 'projectLang',
message: 'Choose a language for your project',
choices: [
{ title: 'TypeScript (Recommended)', value: 'ts' },
{ title: 'JavaScript', value: 'js' }
]
},
{
type: 'select',
name: 'projectTemplate',
message: 'Choose a template for your project',
choices: (prev) => (prev === 'ts' ? tsTemplates : jsTemplates)
},
{
type: 'select',
name: 'packageManager',
message: 'What package manager do you want to use?',
choices: yarn ? pmChoices : pmChoices.slice().reverse()
},
{
type: 'confirm',
name: 'git',
message: 'Do you want to create a git repository for this project?'
}
] as PromptObject<any>[];
};
1 change: 1 addition & 0 deletions src/prompts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './PromptNew';
13 changes: 13 additions & 0 deletions src/templates/.sapphirerc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"projectLanguage": "{{language}}",
"locations": {
"arguments": "src/arguments",
"commands": "src/commands",
"listeners": "src/listeners",
"preconditions": "src/preconditions"
},
"customFileTemplates": {
"enabled": false,
"locations": {}
}
}
5 changes: 4 additions & 1 deletion src/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@
"composite": true,
"preserveConstEnums": true
},
"include": ["."]
"include": [
".",
"./templates/.sapphirerc.json",
]
}
Loading

0 comments on commit 4afa8cd

Please sign in to comment.