@sapphire/cli 1.1.1-next.bbb2fe9.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +14 -6
- package/dist/commands/generate.js +49 -57
- package/dist/commands/init.js +8 -8
- package/dist/commands/new.js +93 -72
- package/dist/functions/CommandExists.js +55 -47
- package/dist/functions/CreateFileFromTemplate.js +38 -39
- package/dist/functions/FileExists.js +7 -0
- package/dist/functions/Spinner.js +122 -0
- package/dist/prompts/PromptNew.js +5 -0
- package/package.json +19 -21
- package/dist/functions/index.js +0 -3
- package/dist/prompts/index.js +0 -3
package/dist/cli.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Command } from 'commander';
|
|
3
|
-
import { readFile } from 'fs/promises';
|
|
4
|
-
import { URL } from 'url';
|
|
5
|
-
import newCmd from '#commands/new';
|
|
6
2
|
import generateCmd from '#commands/generate';
|
|
7
3
|
import initCmd from '#commands/init';
|
|
4
|
+
import newCmd from '#commands/new';
|
|
5
|
+
import { createColors } from 'colorette';
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import { readFile } from 'node:fs/promises';
|
|
8
|
+
import { URL } from 'node:url';
|
|
9
|
+
createColors({ useColor: true });
|
|
8
10
|
const sapphire = new Command();
|
|
9
11
|
const packageFile = new URL('../package.json', import.meta.url);
|
|
10
12
|
const packageJson = JSON.parse(await readFile(packageFile, 'utf-8'));
|
|
11
|
-
sapphire
|
|
13
|
+
sapphire //
|
|
14
|
+
.name('sapphire')
|
|
15
|
+
.version(packageJson.version);
|
|
12
16
|
sapphire
|
|
13
17
|
.command('new')
|
|
14
18
|
.description('creates a new Sapphire project')
|
|
@@ -23,6 +27,10 @@ sapphire
|
|
|
23
27
|
.argument('<component>', 'component/piece name')
|
|
24
28
|
.argument('<name>', 'file name')
|
|
25
29
|
.action(generateCmd);
|
|
26
|
-
sapphire
|
|
30
|
+
sapphire //
|
|
31
|
+
.command('init')
|
|
32
|
+
.description('creates a config file on an existing Sapphire project')
|
|
33
|
+
.alias('i')
|
|
34
|
+
.action(initCmd);
|
|
27
35
|
sapphire.parse(process.argv);
|
|
28
36
|
//# sourceMappingURL=cli.js.map
|
|
@@ -1,69 +1,61 @@
|
|
|
1
1
|
import { componentsFolder } from '#constants';
|
|
2
|
-
import { CreateFileFromTemplate } from '#functions';
|
|
3
|
-
import
|
|
2
|
+
import { CreateFileFromTemplate } from '#functions/CreateFileFromTemplate';
|
|
3
|
+
import { fileExists } from '#functions/FileExists';
|
|
4
|
+
import { Spinner } from '#functions/Spinner';
|
|
5
|
+
import { fromAsync, isErr } from '@sapphire/result';
|
|
6
|
+
import { blueBright, red } from 'colorette';
|
|
4
7
|
import findUp from 'find-up';
|
|
5
|
-
import {
|
|
6
|
-
import { readFile } from 'fs/promises';
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return CreateFileFromTemplate(`components/${template}`, target, config, params, false, true).then(resolve).catch(reject);
|
|
27
|
-
}
|
|
28
|
-
return reject(new Error("Can't find the template."));
|
|
29
|
-
});
|
|
8
|
+
import { load } from 'js-yaml';
|
|
9
|
+
import { readFile } from 'node:fs/promises';
|
|
10
|
+
import { basename, join } from 'node:path';
|
|
11
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
12
|
+
async function createComponent(component, name, config, configLoc) {
|
|
13
|
+
const { projectLanguage } = config;
|
|
14
|
+
if (!projectLanguage) {
|
|
15
|
+
throw new Error("There is no 'projectLanguage' field in .sapphirerc.json");
|
|
16
|
+
}
|
|
17
|
+
const template = `${component.toLowerCase()}.${projectLanguage}.sapphire`;
|
|
18
|
+
const corePath = `${componentsFolder}${template}`;
|
|
19
|
+
const userPath = config.customFileTemplates.enabled ? join(configLoc, config.customFileTemplates.location, template) : null;
|
|
20
|
+
const target = join(configLoc, config.locations.base, '%L%', `${name}.${projectLanguage}`);
|
|
21
|
+
const params = { name: basename(name) };
|
|
22
|
+
if (userPath && (await fileExists(userPath))) {
|
|
23
|
+
return CreateFileFromTemplate(userPath, target, config, params, true, true);
|
|
24
|
+
}
|
|
25
|
+
else if (await fileExists(corePath)) {
|
|
26
|
+
return CreateFileFromTemplate(`components/${template}`, target, config, params, false, true);
|
|
27
|
+
}
|
|
28
|
+
throw new Error("Can't find the template.");
|
|
30
29
|
}
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return resolve(null);
|
|
38
|
-
})
|
|
39
|
-
.catch(reject);
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
function fetchConfig() {
|
|
43
|
-
return Promise.race([findUp('.sapphirerc.json', { cwd: '.' }), timeout(5000)]).then((a) => {
|
|
44
|
-
if (a)
|
|
45
|
-
return a;
|
|
46
|
-
return Promise.race([findUp('.sapphirerc.yml', { cwd: '.' }), timeout(5000)]);
|
|
47
|
-
});
|
|
30
|
+
async function fetchConfig() {
|
|
31
|
+
const a = await Promise.race([findUp('.sapphirerc.json', { cwd: '.' }), sleep(5000)]);
|
|
32
|
+
if (a) {
|
|
33
|
+
return a;
|
|
34
|
+
}
|
|
35
|
+
return Promise.race([findUp('.sapphirerc.yml', { cwd: '.' }), sleep(5000)]);
|
|
48
36
|
}
|
|
49
37
|
export default async (component, name) => {
|
|
50
|
-
const spinner =
|
|
51
|
-
const fail = (error) => {
|
|
52
|
-
spinner.
|
|
38
|
+
const spinner = new Spinner(`Creating a ${component.toLowerCase()}`).start();
|
|
39
|
+
const fail = (error, additionalExecution) => {
|
|
40
|
+
spinner.error({ text: error });
|
|
41
|
+
if (additionalExecution)
|
|
42
|
+
additionalExecution();
|
|
53
43
|
process.exit(1);
|
|
54
44
|
};
|
|
55
45
|
const configLoc = await fetchConfig();
|
|
56
|
-
if (!configLoc)
|
|
46
|
+
if (!configLoc) {
|
|
57
47
|
return fail("Can't find the Sapphire CLI config.");
|
|
58
|
-
|
|
59
|
-
|
|
48
|
+
}
|
|
49
|
+
const config = configLoc.endsWith('json') ? JSON.parse(await readFile(configLoc, 'utf8')) : load(await readFile(configLoc, 'utf8'));
|
|
50
|
+
if (!config) {
|
|
60
51
|
return fail("Can't parse the Sapphire CLI config.");
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
spinner.
|
|
67
|
-
|
|
52
|
+
}
|
|
53
|
+
const result = await fromAsync(async () => createComponent(component, name, config, configLoc.replace(/.sapphirerc.(json|yml)/g, '')));
|
|
54
|
+
if (isErr(result)) {
|
|
55
|
+
return fail(result.error.message, () => console.log(red(result.error.message)));
|
|
56
|
+
}
|
|
57
|
+
spinner.success();
|
|
58
|
+
console.log(blueBright('Done!'));
|
|
59
|
+
process.exit(0);
|
|
68
60
|
};
|
|
69
61
|
//# sourceMappingURL=generate.js.map
|
package/dist/commands/init.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { PromptInit } from '#prompts';
|
|
2
|
-
import
|
|
1
|
+
import { PromptInit } from '#prompts/PromptInit';
|
|
2
|
+
import { red } from 'colorette';
|
|
3
3
|
import findUp from 'find-up';
|
|
4
|
-
import {
|
|
4
|
+
import { dump } from 'js-yaml';
|
|
5
|
+
import { writeFile } from 'node:fs/promises';
|
|
5
6
|
import prompts from 'prompts';
|
|
6
|
-
import YAML from 'yaml';
|
|
7
|
-
const { red } = chalk;
|
|
8
7
|
export default async () => {
|
|
9
8
|
const packageJson = await findUp('package.json');
|
|
10
9
|
if (!packageJson) {
|
|
@@ -13,7 +12,7 @@ export default async () => {
|
|
|
13
12
|
}
|
|
14
13
|
const response = await prompts(PromptInit);
|
|
15
14
|
if (!response.preconditions)
|
|
16
|
-
|
|
15
|
+
process.exit(1);
|
|
17
16
|
const config = {
|
|
18
17
|
projectLanguage: response.projectLanguage,
|
|
19
18
|
locations: {
|
|
@@ -28,7 +27,8 @@ export default async () => {
|
|
|
28
27
|
location: response.cftLocation ?? ''
|
|
29
28
|
}
|
|
30
29
|
};
|
|
31
|
-
const file = response.configFormat === 'json' ? JSON.stringify(config, null, 2) :
|
|
32
|
-
|
|
30
|
+
const file = response.configFormat === 'json' ? JSON.stringify(config, null, 2) : dump(config);
|
|
31
|
+
await writeFile(packageJson.replace('package.json', `.sapphirerc.${response.configFormat}`), file);
|
|
32
|
+
process.exit(0);
|
|
33
33
|
};
|
|
34
34
|
//# sourceMappingURL=init.js.map
|
package/dist/commands/new.js
CHANGED
|
@@ -1,81 +1,93 @@
|
|
|
1
|
-
import { CommandExists, CreateFileFromTemplate } from '#functions';
|
|
2
|
-
import { PromptNew } from '#prompts';
|
|
3
|
-
import chalk from 'chalk';
|
|
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
1
|
import { repoUrl } from '#constants';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
2
|
+
import { CommandExists } from '#functions/CommandExists';
|
|
3
|
+
import { CreateFileFromTemplate } from '#functions/CreateFileFromTemplate';
|
|
4
|
+
import { fileExists } from '#functions/FileExists';
|
|
5
|
+
import { Spinner } from '#functions/Spinner';
|
|
6
|
+
import { PromptNew } from '#prompts/PromptNew';
|
|
7
|
+
import { fromAsync, isErr, isOk } from '@sapphire/result';
|
|
8
|
+
import { blueBright, red } from 'colorette';
|
|
9
|
+
import { execa } from 'execa';
|
|
10
|
+
import { cp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
11
|
+
import { resolve } from 'node:path';
|
|
12
|
+
import prompts from 'prompts';
|
|
13
|
+
async function editPackageJson(location, name) {
|
|
14
|
+
const pjLocation = `./${location}/package.json`;
|
|
15
|
+
const output = JSON.parse(await readFile(pjLocation, 'utf8'));
|
|
16
|
+
if (!output)
|
|
17
|
+
throw new Error("Can't read file.");
|
|
18
|
+
output.name = name;
|
|
19
|
+
const result = await fromAsync(() => writeFile(pjLocation, JSON.stringify(output, null, 2)));
|
|
20
|
+
return isOk(result);
|
|
22
21
|
}
|
|
23
|
-
function installDeps(location, pm, verbose) {
|
|
24
|
-
const
|
|
22
|
+
async function installDeps(location, pm, verbose) {
|
|
23
|
+
const result = await fromAsync(() => execa(pm.toLowerCase(), ['install'], {
|
|
25
24
|
stdio: verbose ? 'inherit' : undefined,
|
|
26
25
|
cwd: `./${location}/`
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
});
|
|
26
|
+
}));
|
|
27
|
+
if (isErr(result)) {
|
|
28
|
+
throw result.error;
|
|
29
|
+
}
|
|
30
|
+
if (result.value.exitCode !== 0) {
|
|
31
|
+
throw new Error('An unknown error occurred while installing the dependencies. Try running Sapphire CLI with "--verbose" flag.');
|
|
32
|
+
}
|
|
33
|
+
const oppositeLockfile = `./${location}/${pm === 'npm' ? 'yarn.lock' : 'package-lock.json'}`;
|
|
34
|
+
if (await fileExists(oppositeLockfile)) {
|
|
35
|
+
await rm(oppositeLockfile);
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
42
38
|
}
|
|
43
|
-
function
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (!e)
|
|
47
|
-
return resolve(true);
|
|
48
|
-
return reject(e);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
39
|
+
async function configureYarnRc(location, name, value) {
|
|
40
|
+
await execa('yarn', ['config', 'set', name, value], { cwd: `./${location}/` });
|
|
41
|
+
return true;
|
|
51
42
|
}
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
43
|
+
async function installYarnV3(location, verbose) {
|
|
44
|
+
const result = await fromAsync(() => execa('yarn', ['set', 'version', 'berry'], {
|
|
45
|
+
stdio: verbose ? 'inherit' : undefined,
|
|
46
|
+
cwd: `./${location}/`
|
|
47
|
+
}));
|
|
48
|
+
if (isErr(result)) {
|
|
49
|
+
throw result.error;
|
|
50
|
+
}
|
|
51
|
+
if (result.value.exitCode !== 0) {
|
|
52
|
+
throw new Error('An unknown error occurred while installing Yarn v3. Try running Sapphire CLI with "--verbose" flag.');
|
|
53
|
+
}
|
|
54
|
+
await Promise.all([
|
|
55
|
+
//
|
|
56
|
+
configureYarnRc(location, 'enableGlobalCache', 'true'),
|
|
57
|
+
configureYarnRc(location, 'nodeLinker', 'node-modules')
|
|
58
|
+
]);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
async function installYarnTypescriptPlugin(location) {
|
|
62
|
+
await execa('yarn', ['plugin', 'import', 'typescript'], { cwd: `./${location}/` });
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
async function initializeGitRepo(location) {
|
|
66
|
+
await execa('git', ['init'], { cwd: `./${location}/` });
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
async function runJob(job, name) {
|
|
70
|
+
const spinner = new Spinner(name).start();
|
|
71
|
+
const result = await fromAsync(async () => job());
|
|
72
|
+
if (isErr(result)) {
|
|
73
|
+
spinner.error({ text: red(result.error.message) });
|
|
74
|
+
console.error(red(result.error.message));
|
|
75
|
+
throw result.error;
|
|
76
|
+
}
|
|
77
|
+
spinner.success();
|
|
78
|
+
return true;
|
|
66
79
|
}
|
|
67
|
-
function cloneRepo(location, verbose) {
|
|
68
|
-
const
|
|
80
|
+
async function cloneRepo(location, verbose) {
|
|
81
|
+
const result = await fromAsync(async () => execa('git', ['clone', repoUrl, `${location}/ghr`], {
|
|
69
82
|
stdio: verbose ? 'inherit' : undefined
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
});
|
|
83
|
+
}));
|
|
84
|
+
if (isErr(result)) {
|
|
85
|
+
throw result.error;
|
|
86
|
+
}
|
|
87
|
+
if (result.value.exitCode !== 0) {
|
|
88
|
+
throw new Error('An unknown error occurred while cloning the repository. Try running Sapphire CLI with "--verbose" flag.');
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
79
91
|
}
|
|
80
92
|
export default async (name, flags) => {
|
|
81
93
|
const response = await prompts(PromptNew(name, await CommandExists('yarn')));
|
|
@@ -96,15 +108,24 @@ export default async (name, flags) => {
|
|
|
96
108
|
};
|
|
97
109
|
const jobs = [
|
|
98
110
|
[() => cloneRepo(response.projectName, flags.verbose), 'Cloning the repository'],
|
|
99
|
-
[stpJob, 'Setting up the project']
|
|
100
|
-
[() => installDeps(response.projectName, response.packageManager, flags.verbose), `Installing dependencies using ${response.packageManager}`]
|
|
111
|
+
[stpJob, 'Setting up the project']
|
|
101
112
|
];
|
|
102
113
|
if (response.git) {
|
|
103
114
|
jobs.push([() => initializeGitRepo(response.projectName), 'Initializing git repo']);
|
|
104
115
|
}
|
|
116
|
+
if (response.yarnV3) {
|
|
117
|
+
jobs.push([() => installYarnV3(response.projectName, flags.verbose), 'Installing Yarn v3']);
|
|
118
|
+
if (response.projectLang === 'ts')
|
|
119
|
+
jobs.push([() => installYarnTypescriptPlugin(response.projectName), 'Installing Yarn Typescript Plugin']);
|
|
120
|
+
}
|
|
121
|
+
jobs.push([
|
|
122
|
+
() => installDeps(response.projectName, response.packageManager, flags.verbose),
|
|
123
|
+
`Installing dependencies using ${response.packageManager}`
|
|
124
|
+
]);
|
|
105
125
|
for (const [job, name] of jobs) {
|
|
106
126
|
await runJob(job, name).catch(() => process.exit(1));
|
|
107
127
|
}
|
|
108
128
|
console.log(blueBright('Done!'));
|
|
129
|
+
process.exit(0);
|
|
109
130
|
};
|
|
110
131
|
//# sourceMappingURL=new.js.map
|
|
@@ -1,59 +1,67 @@
|
|
|
1
1
|
/*
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
The MIT License (MIT)
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2019 Raphaël Thériault
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
4
23
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import
|
|
24
|
+
import { fileExists } from '#functions/FileExists';
|
|
25
|
+
import { fromAsync, isErr } from '@sapphire/result';
|
|
26
|
+
import { execa } from 'execa';
|
|
27
|
+
import { constants } from 'node:fs';
|
|
28
|
+
import { access } from 'node:fs/promises';
|
|
8
29
|
const windows = process.platform === 'win32';
|
|
9
|
-
function
|
|
10
|
-
|
|
30
|
+
async function isExecutable(command) {
|
|
31
|
+
const result = await fromAsync(() => access(command, constants.X_OK));
|
|
32
|
+
return isErr(result);
|
|
11
33
|
}
|
|
12
|
-
function
|
|
13
|
-
|
|
34
|
+
function cleanWindowsCommand(input) {
|
|
35
|
+
if (/[^A-Za-z0-9_\/:=-]/.test(input)) {
|
|
36
|
+
input = `'${input.replace(/'/g, "'\\''")}'`;
|
|
37
|
+
input = input.replace(/^(?:'')+/g, '').replace(/\\'''/g, "\\'");
|
|
38
|
+
}
|
|
39
|
+
return input;
|
|
14
40
|
}
|
|
15
|
-
function
|
|
16
|
-
if (
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
input = input.replace(/^(?:'')+/g, '').replace(/\\'''/g, "\\'");
|
|
41
|
+
async function commandExistsUnix(command) {
|
|
42
|
+
if (await fileExists(command)) {
|
|
43
|
+
if (await isExecutable(command)) {
|
|
44
|
+
return true;
|
|
20
45
|
}
|
|
21
|
-
return input;
|
|
22
46
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return `${dirname}:${basename}`;
|
|
47
|
+
const result = await fromAsync(() => execa('which', [command]));
|
|
48
|
+
if (isErr(result)) {
|
|
49
|
+
return false;
|
|
27
50
|
}
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
function commandExistsUnix(command, cleanCommand) {
|
|
31
|
-
return new Promise((res) => {
|
|
32
|
-
return fileExists(command).then((exists) => {
|
|
33
|
-
if (exists) {
|
|
34
|
-
void executable(command).then((exists) => res(exists));
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
exec(`command -v ${cleanCommand} 2>/dev/null && { echo >&1 ${cleanCommand}; exit 0; }`, (_err, stdout) => res(Boolean(stdout)));
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
});
|
|
51
|
+
return Boolean(result.value.stdout);
|
|
41
52
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
});
|
|
53
|
-
});
|
|
53
|
+
const invalidWindowsCommandNameRegex = /[\x00-\x1f<>:"|?*]/;
|
|
54
|
+
async function commandExistsWindows(command) {
|
|
55
|
+
if (invalidWindowsCommandNameRegex.test(command)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const result = await fromAsync(async () => execa('where', [cleanWindowsCommand(command)]));
|
|
59
|
+
if (isErr(result)) {
|
|
60
|
+
return fileExists(command);
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
54
63
|
}
|
|
55
|
-
export function CommandExists(command) {
|
|
56
|
-
|
|
57
|
-
return windows ? commandExistsWindows(command, cleanCommand) : commandExistsUnix(command, cleanCommand);
|
|
64
|
+
export async function CommandExists(command) {
|
|
65
|
+
return windows ? commandExistsWindows(command) : commandExistsUnix(command);
|
|
58
66
|
}
|
|
59
67
|
//# sourceMappingURL=CommandExists.js.map
|
|
@@ -1,45 +1,44 @@
|
|
|
1
|
-
import { existsSync } from 'fs';
|
|
2
|
-
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
3
|
-
import path from 'path';
|
|
4
1
|
import { templatesFolder } from '#constants';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
import { fileExists } from '#functions/FileExists';
|
|
3
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { dirname, resolve } from 'node:path';
|
|
5
|
+
export async function CreateFileFromTemplate(template, target, config, params, custom = false, component = false) {
|
|
6
|
+
const location = custom ? template : `${templatesFolder}${template}`;
|
|
7
|
+
const output = {};
|
|
8
|
+
if (component) {
|
|
9
|
+
const [c, f] = await getComponentTemplateWithConfig(location);
|
|
10
|
+
output.c = c;
|
|
11
|
+
output.f = f;
|
|
12
|
+
}
|
|
13
|
+
output.f ??= await readFile(location, 'utf8');
|
|
14
|
+
if (!output.f) {
|
|
15
|
+
throw new Error("Can't read file.");
|
|
16
|
+
}
|
|
17
|
+
if (params) {
|
|
18
|
+
for (const param of Object.entries(params)) {
|
|
19
|
+
output.f = output.f.replaceAll(`{{${param[0]}}}`, param[1]);
|
|
13
20
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const ta = component ? target.replace('%L%', dir) : target;
|
|
26
|
-
if (existsSync(ta))
|
|
27
|
-
reject(new Error('Component already exists'));
|
|
28
|
-
await writeFileRecursive(ta, output.f).catch(reject);
|
|
29
|
-
return resolve(true);
|
|
30
|
-
});
|
|
21
|
+
}
|
|
22
|
+
if (!output || (component && (!output.c || !output.c.category))) {
|
|
23
|
+
throw new Error('Invalid template.');
|
|
24
|
+
}
|
|
25
|
+
const dir = component ? config.locations[output.c.category] : null;
|
|
26
|
+
const ta = component ? target.replace('%L%', dir) : target;
|
|
27
|
+
if (await fileExists(ta)) {
|
|
28
|
+
throw new Error('Component already exists');
|
|
29
|
+
}
|
|
30
|
+
await writeFileRecursive(ta, output.f);
|
|
31
|
+
return true;
|
|
31
32
|
}
|
|
32
|
-
function getComponentTemplateWithConfig(path) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
33
|
+
async function getComponentTemplateWithConfig(path) {
|
|
34
|
+
const file = await readFile(path, 'utf8');
|
|
35
|
+
const fa = file.split(/---(\r\n|\r|\n|)/gm);
|
|
36
|
+
return [JSON.parse(fa[0]), fa[2]];
|
|
37
37
|
}
|
|
38
|
-
function writeFileRecursive(target, data) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
});
|
|
38
|
+
async function writeFileRecursive(target, data) {
|
|
39
|
+
const resolvedTarget = resolve(target);
|
|
40
|
+
const dir = dirname(resolvedTarget);
|
|
41
|
+
await mkdir(dir, { recursive: true });
|
|
42
|
+
return writeFile(resolvedTarget, data);
|
|
44
43
|
}
|
|
45
44
|
//# sourceMappingURL=CreateFileFromTemplate.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { fromAsync, isOk } from '@sapphire/result';
|
|
2
|
+
import { access } from 'node:fs/promises';
|
|
3
|
+
export async function fileExists(filePath) {
|
|
4
|
+
const result = await fromAsync(() => access(filePath));
|
|
5
|
+
return isOk(result);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=FileExists.js.map
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import * as colorette from 'colorette';
|
|
2
|
+
import tty from 'node:tty';
|
|
3
|
+
/**
|
|
4
|
+
* A very minimal terminal spinner
|
|
5
|
+
*
|
|
6
|
+
* @license ISC
|
|
7
|
+
* @copyright 2021 Usman Yunusov <usman.iunusov@gmail.com>
|
|
8
|
+
* @see https://github.com/usmanyunusov/nanospinner/blob/master/index.js
|
|
9
|
+
*/
|
|
10
|
+
export class Spinner {
|
|
11
|
+
#isCI = process.env.CI ||
|
|
12
|
+
process.env.WT_SESSION ||
|
|
13
|
+
process.env.ConEmuTask === '{cmd::Cmder}' ||
|
|
14
|
+
process.env.TERM_PROGRAM === 'vscode' ||
|
|
15
|
+
process.env.TERM === 'xterm-256color' ||
|
|
16
|
+
process.env.TERM === 'alacritty';
|
|
17
|
+
#isTTY = tty.isatty(1) && process.env.TERM !== 'dumb' && !('CI' in process.env);
|
|
18
|
+
#supportUnicode = process.platform === 'win32' ? this.#isCI : process.env.TERM !== 'linux';
|
|
19
|
+
#symbols = {
|
|
20
|
+
frames: this.#isTTY ? (this.#supportUnicode ? ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] : ['-', '\\', '|', '/']) : ['-'],
|
|
21
|
+
tick: this.#supportUnicode ? '✔' : '√',
|
|
22
|
+
cross: this.#supportUnicode ? '✖' : '×'
|
|
23
|
+
};
|
|
24
|
+
#text = '';
|
|
25
|
+
#current = 0;
|
|
26
|
+
#interval = 50;
|
|
27
|
+
#stream = process.stderr;
|
|
28
|
+
#frames = this.#symbols.frames;
|
|
29
|
+
#color = 'greenBright';
|
|
30
|
+
#lines = 0;
|
|
31
|
+
#timer;
|
|
32
|
+
constructor(text, options) {
|
|
33
|
+
this.#text = text ?? this.#text;
|
|
34
|
+
this.#interval = options?.interval ?? this.#interval;
|
|
35
|
+
this.#stream = options?.stream ?? this.#stream;
|
|
36
|
+
if (options?.frames && options?.frames.length) {
|
|
37
|
+
this.#frames = options.frames;
|
|
38
|
+
}
|
|
39
|
+
this.#color = options?.color ?? this.#color;
|
|
40
|
+
}
|
|
41
|
+
clear() {
|
|
42
|
+
this.write('\x1b[1G');
|
|
43
|
+
for (let i = 0; i < this.#lines; i++) {
|
|
44
|
+
i > 0 && this.write('\x1b[1A');
|
|
45
|
+
this.write('\x1b[2K\x1b[1G');
|
|
46
|
+
}
|
|
47
|
+
this.#lines = 0;
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
error(options) {
|
|
51
|
+
const mark = colorette.red(this.#symbols.cross);
|
|
52
|
+
return this.stop({ mark, ...options });
|
|
53
|
+
}
|
|
54
|
+
reset() {
|
|
55
|
+
this.#current = 0;
|
|
56
|
+
this.#lines = 0;
|
|
57
|
+
if (this.#timer) {
|
|
58
|
+
clearTimeout(this.#timer);
|
|
59
|
+
}
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
spin() {
|
|
63
|
+
this.render();
|
|
64
|
+
this.#current = ++this.#current % this.#frames.length;
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
start(opts = {}) {
|
|
68
|
+
this.#timer && this.reset();
|
|
69
|
+
return this.update({ text: opts.text, color: opts.color }).loop();
|
|
70
|
+
}
|
|
71
|
+
stop(opts = {}) {
|
|
72
|
+
if (this.#timer) {
|
|
73
|
+
clearTimeout(this.#timer);
|
|
74
|
+
}
|
|
75
|
+
const mark = colorette[opts.color || this.#color](this.#frames[this.#current]);
|
|
76
|
+
const optsMark = opts.mark && opts.color ? colorette[opts.color](opts.mark) : opts.mark;
|
|
77
|
+
this.write(`${optsMark || mark} ${opts.text || this.#text}\n`, true);
|
|
78
|
+
return this.#isTTY ? this.write(`\x1b[?25h`) : this;
|
|
79
|
+
}
|
|
80
|
+
success(opts = {}) {
|
|
81
|
+
const mark = colorette.green(this.#symbols.tick);
|
|
82
|
+
return this.stop({ mark, ...opts });
|
|
83
|
+
}
|
|
84
|
+
update(opts = {}) {
|
|
85
|
+
this.#text = opts.text || this.#text;
|
|
86
|
+
this.#interval = opts?.interval ?? this.#interval;
|
|
87
|
+
this.#stream = opts?.stream ?? this.#stream;
|
|
88
|
+
if (opts?.frames && opts?.frames.length) {
|
|
89
|
+
this.#frames = opts.frames;
|
|
90
|
+
}
|
|
91
|
+
this.#color = opts?.color ?? this.#color;
|
|
92
|
+
if (this.#frames.length - 1 < this.#current) {
|
|
93
|
+
this.#current = 0;
|
|
94
|
+
}
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
loop() {
|
|
98
|
+
this.#isTTY && (this.#timer = setTimeout(() => this.loop(), this.#interval));
|
|
99
|
+
return this.spin();
|
|
100
|
+
}
|
|
101
|
+
write(str, clear = false) {
|
|
102
|
+
if (clear && this.#isTTY) {
|
|
103
|
+
this.clear();
|
|
104
|
+
}
|
|
105
|
+
this.#stream.write(str);
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
render() {
|
|
109
|
+
const mark = colorette[this.#color](this.#frames[this.#current]);
|
|
110
|
+
let str = `${mark} ${this.#text}`;
|
|
111
|
+
this.#isTTY ? this.write(`\x1b[?25l`) : (str += '\n');
|
|
112
|
+
this.write(str, true);
|
|
113
|
+
this.#isTTY && (this.#lines = this.getLines(str, this.#stream.columns));
|
|
114
|
+
}
|
|
115
|
+
getLines(str = '', width = 80) {
|
|
116
|
+
return str
|
|
117
|
+
.replace(/\u001b[^m]*?m/g, '')
|
|
118
|
+
.split('\n')
|
|
119
|
+
.reduce((col, line) => (col += Math.max(1, Math.ceil(line.length / width))), 0);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=Spinner.js.map
|
|
@@ -54,6 +54,11 @@ export const PromptNew = (projectName, yarn) => {
|
|
|
54
54
|
message: 'What package manager do you want to use?',
|
|
55
55
|
choices: yarn ? pmChoices : pmChoices.slice().reverse()
|
|
56
56
|
},
|
|
57
|
+
{
|
|
58
|
+
type: (prev) => (prev === 'Yarn' ? 'confirm' : false),
|
|
59
|
+
name: 'yarnV3',
|
|
60
|
+
message: 'Do you want to use Yarn v3?'
|
|
61
|
+
},
|
|
57
62
|
{
|
|
58
63
|
type: 'confirm',
|
|
59
64
|
name: 'git',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sapphire/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "CLI for Sapphire Framework",
|
|
5
5
|
"author": "@sapphire",
|
|
6
6
|
"license": "MIT",
|
|
@@ -10,9 +10,7 @@
|
|
|
10
10
|
"sapphire": "./dist/cli.js"
|
|
11
11
|
},
|
|
12
12
|
"imports": {
|
|
13
|
-
"#prompts": "./dist/prompts/index.js",
|
|
14
13
|
"#prompts/*": "./dist/prompts/*.js",
|
|
15
|
-
"#functions": "./dist/functions/index.js",
|
|
16
14
|
"#functions/*": "./dist/functions/*.js",
|
|
17
15
|
"#commands/*": "./dist/commands/*.js",
|
|
18
16
|
"#constants": "./dist/constants.js"
|
|
@@ -28,44 +26,44 @@
|
|
|
28
26
|
],
|
|
29
27
|
"scripts": {
|
|
30
28
|
"lint": "eslint src --ext ts --fix",
|
|
31
|
-
"format": "prettier --write src/**/*.ts",
|
|
29
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
32
30
|
"update": "yarn up \"*\" -i",
|
|
33
31
|
"build": "tsc -b src",
|
|
34
32
|
"clean": "tsc -b src --clean",
|
|
35
33
|
"watch": "tsc -b src -w",
|
|
36
|
-
"sversion": "standard-version
|
|
37
|
-
"commit": "git-cz",
|
|
38
|
-
"cz": "git-cz",
|
|
34
|
+
"sversion": "standard-version",
|
|
39
35
|
"prepublishOnly": "yarn build",
|
|
40
36
|
"prepare": "husky install .github/husky"
|
|
41
37
|
},
|
|
42
38
|
"dependencies": {
|
|
43
|
-
"
|
|
39
|
+
"@sapphire/result": "^1.0.0",
|
|
40
|
+
"colorette": "^2.0.16",
|
|
44
41
|
"commander": "^9.0.0",
|
|
42
|
+
"execa": "^6.1.0",
|
|
45
43
|
"find-up": "^5.0.0",
|
|
46
|
-
"
|
|
44
|
+
"js-yaml": "^4.1.0",
|
|
47
45
|
"prompts": "^2.4.2",
|
|
48
|
-
"tslib": "^2.3.1"
|
|
49
|
-
"yaml": "^1.10.2"
|
|
46
|
+
"tslib": "^2.3.1"
|
|
50
47
|
},
|
|
51
48
|
"devDependencies": {
|
|
52
|
-
"@commitlint/cli": "^16.1
|
|
53
|
-
"@commitlint/config-conventional": "^16.
|
|
49
|
+
"@commitlint/cli": "^16.2.1",
|
|
50
|
+
"@commitlint/config-conventional": "^16.2.1",
|
|
54
51
|
"@favware/npm-deprecate": "^1.0.4",
|
|
55
|
-
"@sapphire/eslint-config": "^4.2.
|
|
52
|
+
"@sapphire/eslint-config": "^4.2.1",
|
|
56
53
|
"@sapphire/prettier-config": "^1.3.0",
|
|
57
54
|
"@sapphire/ts-config": "^3.3.1",
|
|
58
|
-
"@types/
|
|
55
|
+
"@types/js-yaml": "^4.0.5",
|
|
56
|
+
"@types/node": "^17.0.21",
|
|
59
57
|
"@types/prompts": "^2.0.14",
|
|
60
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
61
|
-
"@typescript-eslint/parser": "^5.
|
|
58
|
+
"@typescript-eslint/eslint-plugin": "^5.12.1",
|
|
59
|
+
"@typescript-eslint/parser": "^5.12.1",
|
|
62
60
|
"cz-conventional-changelog": "^3.3.0",
|
|
63
|
-
"eslint": "
|
|
64
|
-
"eslint-config-prettier": "^8.
|
|
61
|
+
"eslint": "^8.9.0",
|
|
62
|
+
"eslint-config-prettier": "^8.4.0",
|
|
65
63
|
"eslint-plugin-prettier": "^4.0.0",
|
|
66
64
|
"globby": "^13.1.1",
|
|
67
65
|
"husky": "^7.0.4",
|
|
68
|
-
"lint-staged": "^12.3.
|
|
66
|
+
"lint-staged": "^12.3.4",
|
|
69
67
|
"prettier": "^2.5.1",
|
|
70
68
|
"pretty-quick": "^3.1.3",
|
|
71
69
|
"standard-version": "^9.3.2",
|
|
@@ -114,5 +112,5 @@
|
|
|
114
112
|
"ansi-regex": "^5.0.1"
|
|
115
113
|
},
|
|
116
114
|
"prettier": "@sapphire/prettier-config",
|
|
117
|
-
"packageManager": "yarn@3.
|
|
115
|
+
"packageManager": "yarn@3.2.0"
|
|
118
116
|
}
|
package/dist/functions/index.js
DELETED
package/dist/prompts/index.js
DELETED