Skip to content

Commit 4afa8cd

Browse files
committed
feat: add command: new
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
1 parent 5fe04e6 commit 4afa8cd

File tree

8 files changed

+232
-80
lines changed

8 files changed

+232
-80
lines changed

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"@oclif/config": "^1",
3131
"@oclif/plugin-help": "^3",
3232
"chalk": "^4.1.2",
33-
"ora": "^6.0.0",
33+
"ora": "5.4.1",
3434
"prompts": "^2.4.1",
3535
"tslib": "^2.3.1"
3636
},
@@ -70,7 +70,7 @@
7070
"/oclif.manifest.json"
7171
],
7272
"engines": {
73-
"node": ">=12",
73+
"node": ">=16.7.0",
7474
"npm": ">=6"
7575
},
7676
"keywords": [

src/commands/new.ts

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { CommandExists, CreateFileFromTemplate } from '#functions';
2+
import { PromptNew } from '#prompts';
3+
import { Command, flags } from '@oclif/command';
4+
import { exec, spawn } from 'child_process';
5+
import { existsSync } from 'fs';
6+
import { cp, readFile, rm, writeFile } from 'fs/promises';
7+
import ora from 'ora';
8+
import { resolve } from 'path';
9+
import prompts from 'prompts';
10+
import { config } from '../config';
11+
import chalk from 'chalk';
12+
13+
export default class New extends Command {
14+
public async run() {
15+
const { args, flags } = this.parse(New);
16+
const response = await prompts(PromptNew(args.ProjectName, await CommandExists('yarn')));
17+
if (!response.projectName || !response.projectLang || !response.projectTemplate || !response.packageManager) {
18+
process.exit(1);
19+
}
20+
const projectName = response.projectName === '.' ? '' : response.projectName;
21+
22+
const gitSpinner = ora('Cloning the repository').start();
23+
await this.cloneRepo(response.projectName, flags.verbose).catch((err) => {
24+
gitSpinner.fail('An error occurred while cloning the repository');
25+
console.log(chalk.red(err.message));
26+
process.exit(1);
27+
});
28+
gitSpinner.succeed('Cloned the repository');
29+
30+
const stpSpinner = ora('Setting up the project').start();
31+
const stpSpinnerFail = (err: Error) => {
32+
stpSpinner.fail('An error occurred while setting up the project');
33+
console.log(chalk.red(err.message));
34+
process.exit(1);
35+
};
36+
37+
await cp(`./${response.projectName}/ghr/examples/${response.projectTemplate}/.`, `./${response.projectName}/`, { recursive: true }).catch(
38+
stpSpinnerFail
39+
);
40+
await rm(`./${response.projectName}/ghr`, { recursive: true, force: true }).catch(stpSpinnerFail);
41+
42+
await CreateFileFromTemplate('.sapphirerc.json', resolve(`./${response.projectName}/.sapphirerc.json`), {
43+
language: response.projectLang
44+
}).catch(stpSpinnerFail);
45+
await this.editPackageJson(response.projectName, projectName).catch(stpSpinnerFail);
46+
47+
stpSpinner.succeed();
48+
49+
const pmSpinner = ora(`Installing dependencies using ${response.packageManager}`).start();
50+
await this.installDeps(response.projectName, response.packageManager, flags.verbose).catch((err) => {
51+
pmSpinner.fail('An error occurred while installing the dependencies.');
52+
console.log(chalk.red(err.message));
53+
process.exit(1);
54+
});
55+
await pmSpinner.succeed();
56+
57+
if (response.git) {
58+
const gitSpinner = ora('Initializing git repo').start();
59+
await this.initializeGitRepo(response.projectName).catch((err) => {
60+
gitSpinner.fail('An error occurred while initializing the git repo');
61+
console.log(chalk.red(err.message));
62+
process.exit(1);
63+
});
64+
gitSpinner.succeed();
65+
}
66+
67+
console.log(chalk.blueBright('Done!'));
68+
}
69+
70+
public cloneRepo(location: string, verbose: boolean) {
71+
const git = spawn('git', ['clone', config.repoUrl, `${location}/ghr`], {
72+
stdio: verbose ? 'inherit' : undefined
73+
});
74+
75+
return new Promise((resolve, reject) => {
76+
git.on('error', reject);
77+
78+
git.on('exit', (code) => {
79+
code === 0
80+
? resolve(true)
81+
: reject(new Error('An unknown error occured while cloning the repository. Try running Sapphire CLI with "--verbose" flag.'));
82+
});
83+
});
84+
}
85+
86+
public editPackageJson(location: string, name: string) {
87+
return new Promise(async (resolve, reject) => {
88+
const pjLocation = `./${location}/package.json`;
89+
const output = JSON.parse(await readFile(pjLocation, 'utf8'));
90+
if (!output) return reject(new Error("Can't read file."));
91+
92+
output.name = name;
93+
await writeFile(pjLocation, JSON.stringify(output, null, 2)).catch(reject);
94+
return resolve(true);
95+
});
96+
}
97+
98+
public installDeps(location: string, pm: string, verbose: boolean) {
99+
const pmp = spawn(pm.toLowerCase(), ['install'], {
100+
stdio: verbose ? 'inherit' : undefined,
101+
cwd: `./${location}/`
102+
});
103+
104+
return new Promise((resolve, reject) => {
105+
pmp.on('error', reject);
106+
107+
pmp.on('exit', async (code) => {
108+
if (code === 0) {
109+
const lockfile = `./${location}/${pm === 'npm' ? 'yarn.lock' : 'package-lock.json'}`;
110+
if (existsSync(lockfile)) await rm(lockfile);
111+
resolve(true);
112+
} else {
113+
reject(new Error('An unknown error occured while installing the dependencies. Try running Sapphire CLI with "--verbose" flag.'));
114+
}
115+
});
116+
});
117+
}
118+
119+
public initializeGitRepo(location: string) {
120+
return new Promise((resolve, reject) => {
121+
return exec('git init', { cwd: `./${location}/` }, (e) => {
122+
if (!e) return resolve(true);
123+
return reject(e);
124+
});
125+
});
126+
}
127+
128+
public static description = 'create a new Sapphire project';
129+
130+
public static flags = {
131+
help: flags.help({ char: 'h' }),
132+
verbose: flags.boolean({ char: 'v' })
133+
};
134+
135+
public static args = [{ name: 'ProjectName', default: 'NewSapphireProject' }];
136+
}

src/config.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const config = {
2+
repoUrl: 'https://github.com/sapphiredev/examples.git'
3+
};

src/prompts/PromptNew.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { Choice, PromptObject } from 'prompts';
2+
3+
const tsTemplates: Choice[] = [
4+
{ title: 'Default template (Recommended)', value: 'with-typescript-recommended' },
5+
{ title: 'with Docker', value: 'with-docker' }
6+
];
7+
const jsTemplates: Choice[] = [{ title: 'Default template (Recommended)', value: 'with-javascript', selected: true }];
8+
9+
export const PromptNew = (projectName: string, yarn: boolean) => {
10+
const pmChoices = [
11+
{
12+
title: `Yarn (Recommended) ${yarn ? '' : '(Not installed)'}`,
13+
value: 'Yarn',
14+
disabled: !yarn
15+
},
16+
{ title: 'npm', value: 'npm' }
17+
];
18+
19+
return [
20+
{
21+
type: 'text',
22+
name: 'projectName',
23+
message: "What's the name of your project?",
24+
initial: projectName
25+
},
26+
{
27+
type: 'select',
28+
name: 'projectLang',
29+
message: 'Choose a language for your project',
30+
choices: [
31+
{ title: 'TypeScript (Recommended)', value: 'ts' },
32+
{ title: 'JavaScript', value: 'js' }
33+
]
34+
},
35+
{
36+
type: 'select',
37+
name: 'projectTemplate',
38+
message: 'Choose a template for your project',
39+
choices: (prev) => (prev === 'ts' ? tsTemplates : jsTemplates)
40+
},
41+
{
42+
type: 'select',
43+
name: 'packageManager',
44+
message: 'What package manager do you want to use?',
45+
choices: yarn ? pmChoices : pmChoices.slice().reverse()
46+
},
47+
{
48+
type: 'confirm',
49+
name: 'git',
50+
message: 'Do you want to create a git repository for this project?'
51+
}
52+
] as PromptObject<any>[];
53+
};

src/prompts/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './PromptNew';

src/templates/.sapphirerc.json

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"projectLanguage": "{{language}}",
3+
"locations": {
4+
"arguments": "src/arguments",
5+
"commands": "src/commands",
6+
"listeners": "src/listeners",
7+
"preconditions": "src/preconditions"
8+
},
9+
"customFileTemplates": {
10+
"enabled": false,
11+
"locations": {}
12+
}
13+
}

src/tsconfig.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,8 @@
66
"composite": true,
77
"preserveConstEnums": true
88
},
9-
"include": ["."]
9+
"include": [
10+
".",
11+
"./templates/.sapphirerc.json",
12+
]
1013
}

0 commit comments

Comments
 (0)