@patternfly/patternfly-cli 1.0.3 โ 1.0.4
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/LICENSE +1 -1
- package/dist/cli.js +6 -145
- package/dist/cli.js.map +1 -1
- package/dist/create.d.ts +10 -0
- package/dist/create.d.ts.map +1 -0
- package/dist/create.js +151 -0
- package/dist/create.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/cli.test.ts +0 -23
- package/src/__tests__/create.test.ts +306 -0
- package/src/cli.ts +6 -176
- package/src/create.ts +187 -0
package/LICENSE
CHANGED
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { program } from 'commander';
|
|
3
3
|
import { execa } from 'execa';
|
|
4
|
-
import inquirer from 'inquirer';
|
|
5
4
|
import fs from 'fs-extra';
|
|
6
5
|
import path from 'path';
|
|
7
6
|
import { defaultTemplates } from './templates.js';
|
|
8
7
|
import { mergeTemplates } from './template-loader.js';
|
|
9
8
|
import { offerAndCreateGitHubRepo } from './github.js';
|
|
9
|
+
import { runCreate } from './create.js';
|
|
10
10
|
import { runSave } from './save.js';
|
|
11
11
|
import { runLoad } from './load.js';
|
|
12
12
|
import { runDeployToGitHubPages } from './gh-pages.js';
|
|
@@ -20,154 +20,15 @@ program
|
|
|
20
20
|
.option('-t, --template-file <path>', 'Path to a JSON file with custom templates (same format as built-in)')
|
|
21
21
|
.option('--ssh', 'Use SSH URL for cloning the template repository')
|
|
22
22
|
.action(async (projectDirectory, templateName, options) => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
{
|
|
28
|
-
type: 'input',
|
|
29
|
-
name: 'projectDirectory',
|
|
30
|
-
message: 'Please provide the directory where you want to create the project?',
|
|
31
|
-
default: 'my-app',
|
|
32
|
-
},
|
|
33
|
-
]);
|
|
34
|
-
projectDirectory = projectDirAnswer.projectDirectory;
|
|
35
|
-
}
|
|
36
|
-
// If template name is not provided, show available templates and let user select
|
|
37
|
-
if (!templateName) {
|
|
38
|
-
console.log('\n๐ Available templates:\n');
|
|
39
|
-
templatesToUse.forEach(t => {
|
|
40
|
-
console.log(` ${t.name.padEnd(12)} - ${t.description}`);
|
|
23
|
+
try {
|
|
24
|
+
await runCreate(projectDirectory, templateName, {
|
|
25
|
+
templateFile: options?.templateFile,
|
|
26
|
+
ssh: options?.ssh,
|
|
41
27
|
});
|
|
42
|
-
console.log('');
|
|
43
|
-
const templateQuestion = [
|
|
44
|
-
{
|
|
45
|
-
type: 'list',
|
|
46
|
-
name: 'templateName',
|
|
47
|
-
message: 'Select a template:',
|
|
48
|
-
choices: templatesToUse.map(t => ({
|
|
49
|
-
name: `${t.name} - ${t.description}`,
|
|
50
|
-
value: t.name
|
|
51
|
-
}))
|
|
52
|
-
}
|
|
53
|
-
];
|
|
54
|
-
const templateAnswer = await inquirer.prompt(templateQuestion);
|
|
55
|
-
templateName = templateAnswer.templateName;
|
|
56
28
|
}
|
|
57
|
-
|
|
58
|
-
const template = templatesToUse.find(t => t.name === templateName);
|
|
59
|
-
if (!template) {
|
|
60
|
-
console.error(`โ Template "${templateName}" not found.\n`);
|
|
61
|
-
console.log('๐ Available templates:\n');
|
|
62
|
-
templatesToUse.forEach(t => {
|
|
63
|
-
console.log(` ${t.name.padEnd(12)} - ${t.description}`);
|
|
64
|
-
});
|
|
65
|
-
console.log('');
|
|
29
|
+
catch {
|
|
66
30
|
process.exit(1);
|
|
67
31
|
}
|
|
68
|
-
// If --ssh was not passed, prompt whether to use SSH
|
|
69
|
-
let useSSH = options?.ssh;
|
|
70
|
-
if (useSSH === undefined && template.repoSSH) {
|
|
71
|
-
const sshAnswer = await inquirer.prompt([
|
|
72
|
-
{
|
|
73
|
-
type: 'confirm',
|
|
74
|
-
name: 'useSSH',
|
|
75
|
-
message: 'Use SSH URL for cloning?',
|
|
76
|
-
default: false,
|
|
77
|
-
},
|
|
78
|
-
]);
|
|
79
|
-
useSSH = sshAnswer.useSSH;
|
|
80
|
-
}
|
|
81
|
-
const templateRepoUrl = useSSH && template.repoSSH ? template.repoSSH : template.repo;
|
|
82
|
-
// Define the full path for the new project
|
|
83
|
-
const projectPath = path.resolve(projectDirectory);
|
|
84
|
-
console.log(`Cloning template "${templateName}" from ${templateRepoUrl} into ${projectPath}...`);
|
|
85
|
-
try {
|
|
86
|
-
// Clone the repository
|
|
87
|
-
const cloneArgs = ['clone'];
|
|
88
|
-
if (template.options && Array.isArray(template.options)) {
|
|
89
|
-
cloneArgs.push(...template.options);
|
|
90
|
-
}
|
|
91
|
-
cloneArgs.push(templateRepoUrl, projectPath);
|
|
92
|
-
await execa('git', cloneArgs, { stdio: 'inherit' });
|
|
93
|
-
console.log('โ
Template cloned successfully.');
|
|
94
|
-
// Remove the .git folder from the *new* project
|
|
95
|
-
await fs.remove(path.join(projectPath, '.git'));
|
|
96
|
-
console.log('๐งน Cleaned up template .git directory.');
|
|
97
|
-
// Ask user for customization details
|
|
98
|
-
const questions = [
|
|
99
|
-
{
|
|
100
|
-
type: 'input',
|
|
101
|
-
name: 'name',
|
|
102
|
-
message: 'What is the project name?',
|
|
103
|
-
default: path.basename(projectPath),
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
type: 'input',
|
|
107
|
-
name: 'version',
|
|
108
|
-
message: 'What version number would you like to use?',
|
|
109
|
-
default: '1.0.0',
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
type: 'input',
|
|
113
|
-
name: 'description',
|
|
114
|
-
message: 'What is the project description?',
|
|
115
|
-
default: '',
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
type: 'input',
|
|
119
|
-
name: 'author',
|
|
120
|
-
message: 'Who is the author of the project?',
|
|
121
|
-
default: '',
|
|
122
|
-
},
|
|
123
|
-
];
|
|
124
|
-
const answers = await inquirer.prompt(questions);
|
|
125
|
-
// Update the package.json in the new project
|
|
126
|
-
const pkgJsonPath = path.join(projectPath, 'package.json');
|
|
127
|
-
if (await fs.pathExists(pkgJsonPath)) {
|
|
128
|
-
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
129
|
-
// Overwrite fields with user's answers
|
|
130
|
-
pkgJson.name = answers.name;
|
|
131
|
-
pkgJson.version = answers.version;
|
|
132
|
-
pkgJson.description = answers.description;
|
|
133
|
-
pkgJson.author = answers.author;
|
|
134
|
-
// Write the updated package.json back
|
|
135
|
-
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
136
|
-
console.log('๐ Customized package.json.');
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
console.log('โน๏ธ No package.json found in template, skipping customization.');
|
|
140
|
-
}
|
|
141
|
-
const packageManager = template.packageManager || "npm";
|
|
142
|
-
// Install dependencies
|
|
143
|
-
console.log('๐ฆ Installing dependencies... (This may take a moment)');
|
|
144
|
-
await execa(packageManager, ['install'], { cwd: projectPath, stdio: 'inherit' });
|
|
145
|
-
console.log('โ
Dependencies installed.');
|
|
146
|
-
// Optional: Create GitHub repository
|
|
147
|
-
await offerAndCreateGitHubRepo(projectPath);
|
|
148
|
-
// Let the user know the project was created successfully
|
|
149
|
-
console.log('\nโจ Project created successfully! โจ\n');
|
|
150
|
-
console.log(`To get started:`);
|
|
151
|
-
console.log(` cd ${projectDirectory}`);
|
|
152
|
-
console.log(' Happy coding! ๐');
|
|
153
|
-
}
|
|
154
|
-
catch (error) {
|
|
155
|
-
console.error('โ An error occurred:');
|
|
156
|
-
if (error instanceof Error) {
|
|
157
|
-
console.error(error.message);
|
|
158
|
-
}
|
|
159
|
-
else if (error && typeof error === 'object' && 'stderr' in error) {
|
|
160
|
-
console.error(error.stderr || String(error));
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
console.error(String(error));
|
|
164
|
-
}
|
|
165
|
-
// Clean up the created directory if an error occurred
|
|
166
|
-
if (await fs.pathExists(projectPath)) {
|
|
167
|
-
await fs.remove(projectPath);
|
|
168
|
-
console.log('๐งน Cleaned up failed project directory.');
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
32
|
});
|
|
172
33
|
/** Command to initialize a project and optionally create a GitHub repository */
|
|
173
34
|
program
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/create.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type RunCreateOptions = {
|
|
2
|
+
templateFile?: string;
|
|
3
|
+
ssh?: boolean;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Runs the create flow: clone template, customize package.json, install deps, optionally create GitHub repo.
|
|
7
|
+
* Throws on fatal errors. Caller should catch and process.exit(1).
|
|
8
|
+
*/
|
|
9
|
+
export declare function runCreate(projectDirectory: string | undefined, templateName: string | undefined, options?: RunCreateOptions): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,gBAAgB,EAAE,MAAM,GAAG,SAAS,EACpC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,IAAI,CAAC,CAyJf"}
|
package/dist/create.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import { execa } from 'execa';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import { defaultTemplates } from './templates.js';
|
|
6
|
+
import { mergeTemplates } from './template-loader.js';
|
|
7
|
+
import { offerAndCreateGitHubRepo } from './github.js';
|
|
8
|
+
/**
|
|
9
|
+
* Runs the create flow: clone template, customize package.json, install deps, optionally create GitHub repo.
|
|
10
|
+
* Throws on fatal errors. Caller should catch and process.exit(1).
|
|
11
|
+
*/
|
|
12
|
+
export async function runCreate(projectDirectory, templateName, options) {
|
|
13
|
+
const templatesToUse = mergeTemplates(defaultTemplates, options?.templateFile);
|
|
14
|
+
// If project directory is not provided, prompt for it
|
|
15
|
+
if (!projectDirectory) {
|
|
16
|
+
const projectDirAnswer = await inquirer.prompt([
|
|
17
|
+
{
|
|
18
|
+
type: 'input',
|
|
19
|
+
name: 'projectDirectory',
|
|
20
|
+
message: 'Please provide the directory where you want to create the project?',
|
|
21
|
+
default: 'my-app',
|
|
22
|
+
},
|
|
23
|
+
]);
|
|
24
|
+
projectDirectory = projectDirAnswer.projectDirectory;
|
|
25
|
+
}
|
|
26
|
+
// If template name is not provided, show available templates and let user select
|
|
27
|
+
if (!templateName) {
|
|
28
|
+
console.log('\n๐ Available templates:\n');
|
|
29
|
+
templatesToUse.forEach(t => {
|
|
30
|
+
console.log(` ${t.name.padEnd(12)} - ${t.description}`);
|
|
31
|
+
});
|
|
32
|
+
console.log('');
|
|
33
|
+
const templateQuestion = [
|
|
34
|
+
{
|
|
35
|
+
type: 'list',
|
|
36
|
+
name: 'templateName',
|
|
37
|
+
message: 'Select a template:',
|
|
38
|
+
choices: templatesToUse.map(t => ({
|
|
39
|
+
name: `${t.name} - ${t.description}`,
|
|
40
|
+
value: t.name,
|
|
41
|
+
})),
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
const templateAnswer = await inquirer.prompt(templateQuestion);
|
|
45
|
+
templateName = templateAnswer.templateName;
|
|
46
|
+
}
|
|
47
|
+
// Look up the template by name
|
|
48
|
+
const template = templatesToUse.find(t => t.name === templateName);
|
|
49
|
+
if (!template) {
|
|
50
|
+
console.error(`โ Template "${templateName}" not found.\n`);
|
|
51
|
+
console.log('๐ Available templates:\n');
|
|
52
|
+
templatesToUse.forEach(t => {
|
|
53
|
+
console.log(` ${t.name.padEnd(12)} - ${t.description}`);
|
|
54
|
+
});
|
|
55
|
+
console.log('');
|
|
56
|
+
throw new Error(`Template "${templateName}" not found`);
|
|
57
|
+
}
|
|
58
|
+
const templateRepoUrl = options?.ssh && template.repoSSH ? template.repoSSH : template.repo;
|
|
59
|
+
// Define the full path for the new project (projectDirectory is set above via arg or prompt)
|
|
60
|
+
const dir = projectDirectory ?? 'my-app';
|
|
61
|
+
const projectPath = path.resolve(dir);
|
|
62
|
+
console.log(`Cloning template "${templateName}" from ${templateRepoUrl} into ${projectPath}...`);
|
|
63
|
+
try {
|
|
64
|
+
// Clone the repository
|
|
65
|
+
const cloneArgs = ['clone'];
|
|
66
|
+
if (template.options && Array.isArray(template.options)) {
|
|
67
|
+
cloneArgs.push(...template.options);
|
|
68
|
+
}
|
|
69
|
+
cloneArgs.push(templateRepoUrl, projectPath);
|
|
70
|
+
await execa('git', cloneArgs, { stdio: 'inherit' });
|
|
71
|
+
console.log('โ
Template cloned successfully.');
|
|
72
|
+
// Remove the .git folder from the *new* project
|
|
73
|
+
await fs.remove(path.join(projectPath, '.git'));
|
|
74
|
+
console.log('๐งน Cleaned up template .git directory.');
|
|
75
|
+
// Ask user for customization details
|
|
76
|
+
const questions = [
|
|
77
|
+
{
|
|
78
|
+
type: 'input',
|
|
79
|
+
name: 'name',
|
|
80
|
+
message: 'What is the project name?',
|
|
81
|
+
default: path.basename(projectPath),
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
type: 'input',
|
|
85
|
+
name: 'version',
|
|
86
|
+
message: 'What version number would you like to use?',
|
|
87
|
+
default: '1.0.0',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
type: 'input',
|
|
91
|
+
name: 'description',
|
|
92
|
+
message: 'What is the project description?',
|
|
93
|
+
default: '',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
type: 'input',
|
|
97
|
+
name: 'author',
|
|
98
|
+
message: 'Who is the author of the project?',
|
|
99
|
+
default: '',
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
const answers = await inquirer.prompt(questions);
|
|
103
|
+
// Update the package.json in the new project
|
|
104
|
+
const pkgJsonPath = path.join(projectPath, 'package.json');
|
|
105
|
+
if (await fs.pathExists(pkgJsonPath)) {
|
|
106
|
+
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
107
|
+
// Overwrite fields with user's answers
|
|
108
|
+
pkgJson.name = answers.name;
|
|
109
|
+
pkgJson.version = answers.version;
|
|
110
|
+
pkgJson.description = answers.description;
|
|
111
|
+
pkgJson.author = answers.author;
|
|
112
|
+
// Write the updated package.json back
|
|
113
|
+
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
114
|
+
console.log('๐ Customized package.json.');
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
console.log('โน๏ธ No package.json found in template, skipping customization.');
|
|
118
|
+
}
|
|
119
|
+
const packageManager = template.packageManager || 'npm';
|
|
120
|
+
// Install dependencies
|
|
121
|
+
console.log('๐ฆ Installing dependencies... (This may take a moment)');
|
|
122
|
+
await execa(packageManager, ['install'], { cwd: projectPath, stdio: 'inherit' });
|
|
123
|
+
console.log('โ
Dependencies installed.');
|
|
124
|
+
// Optional: Create GitHub repository
|
|
125
|
+
await offerAndCreateGitHubRepo(projectPath);
|
|
126
|
+
// Let the user know the project was created successfully
|
|
127
|
+
console.log('\nโจ Project created successfully! โจ\n');
|
|
128
|
+
console.log(`To get started:`);
|
|
129
|
+
console.log(` cd ${dir}`);
|
|
130
|
+
console.log(' Happy coding! ๐');
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
console.error('โ An error occurred:');
|
|
134
|
+
if (error instanceof Error) {
|
|
135
|
+
console.error(error.message);
|
|
136
|
+
}
|
|
137
|
+
else if (error && typeof error === 'object' && 'stderr' in error) {
|
|
138
|
+
console.error(error.stderr || String(error));
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
console.error(String(error));
|
|
142
|
+
}
|
|
143
|
+
// Clean up the created directory if an error occurred
|
|
144
|
+
if (await fs.pathExists(projectPath)) {
|
|
145
|
+
await fs.remove(projectPath);
|
|
146
|
+
console.log('๐งน Cleaned up failed project directory.');
|
|
147
|
+
}
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
jest.mock('inquirer', () => ({
|
|
2
|
-
__esModule: true,
|
|
3
|
-
default: { prompt: jest.fn() },
|
|
4
|
-
}));
|
|
5
|
-
|
|
6
1
|
import path from 'path';
|
|
7
2
|
import fs from 'fs-extra';
|
|
8
3
|
import { loadCustomTemplates, mergeTemplates } from '../template-loader.js';
|
|
9
4
|
import templates from '../templates.js';
|
|
10
|
-
import { sanitizeRepoName } from '../github.js';
|
|
11
5
|
|
|
12
6
|
const fixturesDir = path.join(process.cwd(), 'src', '__tests__', 'fixtures');
|
|
13
7
|
|
|
@@ -152,20 +146,3 @@ describe('mergeTemplates', () => {
|
|
|
152
146
|
}
|
|
153
147
|
});
|
|
154
148
|
});
|
|
155
|
-
|
|
156
|
-
describe('GitHub support (create command)', () => {
|
|
157
|
-
it('derives repo name from package name the same way the create command does', () => {
|
|
158
|
-
// Create command uses sanitizeRepoName(pkgJson.name) for initial repo name
|
|
159
|
-
expect(sanitizeRepoName('my-app')).toBe('my-app');
|
|
160
|
-
expect(sanitizeRepoName('@patternfly/my-project')).toBe('my-project');
|
|
161
|
-
expect(sanitizeRepoName('My Project Name')).toBe('my-project-name');
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('builds repo URL in the format used by the create command', () => {
|
|
165
|
-
// Create command builds https://github.com/${auth.username}/${repoName}
|
|
166
|
-
const username = 'testuser';
|
|
167
|
-
const repoName = sanitizeRepoName('@org/my-package');
|
|
168
|
-
const repoUrl = `https://github.com/${username}/${repoName}`;
|
|
169
|
-
expect(repoUrl).toBe('https://github.com/testuser/my-package');
|
|
170
|
-
});
|
|
171
|
-
});
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
jest.mock('inquirer', () => ({
|
|
2
|
+
__esModule: true,
|
|
3
|
+
default: { prompt: jest.fn() },
|
|
4
|
+
}));
|
|
5
|
+
|
|
6
|
+
jest.mock('fs-extra', () => {
|
|
7
|
+
const real = jest.requireActual<typeof import('fs-extra')>('fs-extra');
|
|
8
|
+
return {
|
|
9
|
+
__esModule: true,
|
|
10
|
+
default: {
|
|
11
|
+
pathExists: jest.fn(),
|
|
12
|
+
readJson: jest.fn(),
|
|
13
|
+
writeJson: jest.fn(),
|
|
14
|
+
remove: jest.fn(),
|
|
15
|
+
existsSync: real.existsSync,
|
|
16
|
+
readFileSync: real.readFileSync,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
jest.mock('execa', () => ({
|
|
22
|
+
__esModule: true,
|
|
23
|
+
execa: jest.fn(),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
jest.mock('../github.js', () => ({
|
|
27
|
+
...jest.requireActual('../github.js'),
|
|
28
|
+
offerAndCreateGitHubRepo: jest.fn(),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
import path from 'path';
|
|
32
|
+
import fs from 'fs-extra';
|
|
33
|
+
import { execa } from 'execa';
|
|
34
|
+
import inquirer from 'inquirer';
|
|
35
|
+
import { sanitizeRepoName, offerAndCreateGitHubRepo } from '../github.js';
|
|
36
|
+
import { runCreate } from '../create.js';
|
|
37
|
+
import { defaultTemplates } from '../templates.js';
|
|
38
|
+
|
|
39
|
+
const mockPathExists = fs.pathExists as jest.MockedFunction<typeof fs.pathExists>;
|
|
40
|
+
const mockReadJson = fs.readJson as jest.MockedFunction<typeof fs.readJson>;
|
|
41
|
+
const mockWriteJson = fs.writeJson as jest.MockedFunction<typeof fs.writeJson>;
|
|
42
|
+
const mockRemove = fs.remove as jest.MockedFunction<typeof fs.remove>;
|
|
43
|
+
const mockExeca = execa as jest.MockedFunction<typeof execa>;
|
|
44
|
+
const mockPrompt = inquirer.prompt as jest.MockedFunction<typeof inquirer.prompt>;
|
|
45
|
+
const mockOfferAndCreateGitHubRepo = offerAndCreateGitHubRepo as jest.MockedFunction<
|
|
46
|
+
typeof offerAndCreateGitHubRepo
|
|
47
|
+
>;
|
|
48
|
+
|
|
49
|
+
const projectDir = 'my-test-project';
|
|
50
|
+
const projectPath = path.resolve(projectDir);
|
|
51
|
+
|
|
52
|
+
const projectData = {
|
|
53
|
+
name: 'my-app',
|
|
54
|
+
version: '1.0.0',
|
|
55
|
+
description: 'Test app',
|
|
56
|
+
author: 'Test Author',
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function setupHappyPathMocks() {
|
|
60
|
+
mockExeca.mockResolvedValue({ stdout: '', stderr: '', exitCode: 0 } as Awaited<ReturnType<typeof execa>>);
|
|
61
|
+
mockPathExists.mockResolvedValue(true);
|
|
62
|
+
mockReadJson.mockResolvedValue({ name: 'template-name', version: '0.0.0' });
|
|
63
|
+
mockWriteJson.mockResolvedValue(undefined);
|
|
64
|
+
mockRemove.mockResolvedValue(undefined);
|
|
65
|
+
mockOfferAndCreateGitHubRepo.mockResolvedValue(undefined);
|
|
66
|
+
mockPrompt.mockResolvedValue(projectData);
|
|
67
|
+
return projectData;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
describe('GitHub support (create command)', () => {
|
|
71
|
+
it('derives repo name from package name the same way the create command does', () => {
|
|
72
|
+
expect(sanitizeRepoName('my-app')).toBe('my-app');
|
|
73
|
+
expect(sanitizeRepoName('@patternfly/my-project')).toBe('my-project');
|
|
74
|
+
expect(sanitizeRepoName('My Project Name')).toBe('my-project-name');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('builds repo URL in the format used by the create command', () => {
|
|
78
|
+
const username = 'testuser';
|
|
79
|
+
const repoName = sanitizeRepoName('@org/my-package');
|
|
80
|
+
const repoUrl = `https://github.com/${username}/${repoName}`;
|
|
81
|
+
expect(repoUrl).toBe('https://github.com/testuser/my-package');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('runCreate', () => {
|
|
86
|
+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
87
|
+
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
88
|
+
|
|
89
|
+
beforeEach(() => {
|
|
90
|
+
jest.clearAllMocks();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
afterAll(() => {
|
|
94
|
+
consoleErrorSpy.mockRestore();
|
|
95
|
+
consoleLogSpy.mockRestore();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('throws when template name is not found', async () => {
|
|
99
|
+
await expect(runCreate(projectDir, 'nonexistent-template')).rejects.toThrow(
|
|
100
|
+
'Template "nonexistent-template" not found'
|
|
101
|
+
);
|
|
102
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
103
|
+
expect.stringContaining('Template "nonexistent-template" not found')
|
|
104
|
+
);
|
|
105
|
+
expect(mockExeca).not.toHaveBeenCalled();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('prompts for project directory when not provided', async () => {
|
|
109
|
+
setupHappyPathMocks();
|
|
110
|
+
|
|
111
|
+
await runCreate(undefined, 'starter');
|
|
112
|
+
|
|
113
|
+
expect(mockPrompt).toHaveBeenCalledWith(
|
|
114
|
+
expect.arrayContaining([
|
|
115
|
+
expect.objectContaining({
|
|
116
|
+
type: 'input',
|
|
117
|
+
name: 'projectDirectory',
|
|
118
|
+
message: expect.stringContaining('directory where you want to create the project'),
|
|
119
|
+
default: 'my-app',
|
|
120
|
+
}),
|
|
121
|
+
])
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('prompts for template when not provided', async () => {
|
|
126
|
+
setupHappyPathMocks();
|
|
127
|
+
mockPrompt
|
|
128
|
+
.mockResolvedValueOnce({ templateName: 'starter' })
|
|
129
|
+
.mockResolvedValueOnce(projectData);
|
|
130
|
+
|
|
131
|
+
await runCreate(projectDir, undefined);
|
|
132
|
+
|
|
133
|
+
expect(mockPrompt).toHaveBeenCalledWith(
|
|
134
|
+
expect.arrayContaining([
|
|
135
|
+
expect.objectContaining({
|
|
136
|
+
type: 'list',
|
|
137
|
+
name: 'templateName',
|
|
138
|
+
message: 'Select a template:',
|
|
139
|
+
choices: expect.any(Array),
|
|
140
|
+
}),
|
|
141
|
+
])
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('uses HTTPS repo URL when ssh option is false', async () => {
|
|
146
|
+
setupHappyPathMocks();
|
|
147
|
+
|
|
148
|
+
await runCreate(projectDir, 'starter', { ssh: false });
|
|
149
|
+
|
|
150
|
+
const starter = defaultTemplates.find(t => t.name === 'starter');
|
|
151
|
+
expect(mockExeca).toHaveBeenNthCalledWith(
|
|
152
|
+
1,
|
|
153
|
+
'git',
|
|
154
|
+
['clone', starter!.repo, projectPath],
|
|
155
|
+
expect.objectContaining({ stdio: 'inherit' })
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('uses SSH repo URL when ssh option is true and template has repoSSH', async () => {
|
|
160
|
+
setupHappyPathMocks();
|
|
161
|
+
|
|
162
|
+
await runCreate(projectDir, 'starter', { ssh: true });
|
|
163
|
+
|
|
164
|
+
const starter = defaultTemplates.find(t => t.name === 'starter');
|
|
165
|
+
expect(mockExeca).toHaveBeenNthCalledWith(
|
|
166
|
+
1,
|
|
167
|
+
'git',
|
|
168
|
+
['clone', starter!.repoSSH, projectPath],
|
|
169
|
+
expect.objectContaining({ stdio: 'inherit' })
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('passes template clone options to git clone', async () => {
|
|
174
|
+
setupHappyPathMocks();
|
|
175
|
+
|
|
176
|
+
await runCreate(projectDir, 'compass-starter');
|
|
177
|
+
|
|
178
|
+
const compass = defaultTemplates.find(t => t.name === 'compass-starter');
|
|
179
|
+
expect(mockExeca).toHaveBeenNthCalledWith(
|
|
180
|
+
1,
|
|
181
|
+
'git',
|
|
182
|
+
['clone', ...compass!.options!, compass!.repo, projectPath],
|
|
183
|
+
expect.any(Object)
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('removes .git from cloned project and customizes package.json', async () => {
|
|
188
|
+
setupHappyPathMocks();
|
|
189
|
+
|
|
190
|
+
await runCreate(projectDir, 'starter');
|
|
191
|
+
|
|
192
|
+
expect(mockRemove).toHaveBeenCalledWith(path.join(projectPath, '.git'));
|
|
193
|
+
expect(mockReadJson).toHaveBeenCalledWith(path.join(projectPath, 'package.json'));
|
|
194
|
+
expect(mockWriteJson).toHaveBeenCalledWith(
|
|
195
|
+
path.join(projectPath, 'package.json'),
|
|
196
|
+
expect.objectContaining({
|
|
197
|
+
name: projectData.name,
|
|
198
|
+
version: projectData.version,
|
|
199
|
+
description: projectData.description,
|
|
200
|
+
author: projectData.author,
|
|
201
|
+
}),
|
|
202
|
+
{ spaces: 2 }
|
|
203
|
+
);
|
|
204
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('Customized package.json'));
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('skips package.json customization when package.json does not exist', async () => {
|
|
208
|
+
setupHappyPathMocks();
|
|
209
|
+
mockPathExists.mockResolvedValue(false);
|
|
210
|
+
|
|
211
|
+
await runCreate(projectDir, 'starter');
|
|
212
|
+
|
|
213
|
+
expect(mockReadJson).not.toHaveBeenCalled();
|
|
214
|
+
expect(mockWriteJson).not.toHaveBeenCalled();
|
|
215
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
216
|
+
expect.stringContaining('No package.json found in template')
|
|
217
|
+
);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('uses template packageManager and runs install', async () => {
|
|
221
|
+
setupHappyPathMocks();
|
|
222
|
+
|
|
223
|
+
await runCreate(projectDir, 'starter');
|
|
224
|
+
|
|
225
|
+
expect(mockExeca).toHaveBeenNthCalledWith(
|
|
226
|
+
2,
|
|
227
|
+
'yarn',
|
|
228
|
+
['install'],
|
|
229
|
+
expect.objectContaining({ cwd: projectPath, stdio: 'inherit' })
|
|
230
|
+
);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('falls back to npm when template has no packageManager', async () => {
|
|
234
|
+
setupHappyPathMocks();
|
|
235
|
+
// rhoai_enabled_starter has no packageManager
|
|
236
|
+
const rhoai = defaultTemplates.find(t => t.name === 'rhoai_enabled_starter');
|
|
237
|
+
expect(rhoai?.packageManager).toBeUndefined();
|
|
238
|
+
|
|
239
|
+
await runCreate(projectDir, 'rhoai_enabled_starter');
|
|
240
|
+
|
|
241
|
+
expect(mockExeca).toHaveBeenNthCalledWith(
|
|
242
|
+
2,
|
|
243
|
+
'npm',
|
|
244
|
+
['install'],
|
|
245
|
+
expect.objectContaining({ cwd: projectPath, stdio: 'inherit' })
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('calls offerAndCreateGitHubRepo after install', async () => {
|
|
250
|
+
setupHappyPathMocks();
|
|
251
|
+
|
|
252
|
+
await runCreate(projectDir, 'starter');
|
|
253
|
+
|
|
254
|
+
expect(mockOfferAndCreateGitHubRepo).toHaveBeenCalledWith(projectPath);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('logs success message with project directory', async () => {
|
|
258
|
+
setupHappyPathMocks();
|
|
259
|
+
|
|
260
|
+
await runCreate(projectDir, 'starter');
|
|
261
|
+
|
|
262
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('Project created successfully'));
|
|
263
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining(`cd ${projectDir}`));
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('cleans up project directory and rethrows when clone fails', async () => {
|
|
267
|
+
mockExeca.mockRejectedValueOnce(new Error('clone failed'));
|
|
268
|
+
mockPathExists.mockResolvedValue(true);
|
|
269
|
+
|
|
270
|
+
await expect(runCreate(projectDir, 'starter')).rejects.toThrow('clone failed');
|
|
271
|
+
|
|
272
|
+
expect(mockRemove).toHaveBeenCalledWith(projectPath);
|
|
273
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('Cleaned up failed project directory'));
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('cleans up project directory and rethrows when install fails', async () => {
|
|
277
|
+
mockExeca
|
|
278
|
+
.mockResolvedValueOnce({ stdout: '', stderr: '', exitCode: 0 } as Awaited<ReturnType<typeof execa>>)
|
|
279
|
+
.mockRejectedValueOnce(new Error('install failed'));
|
|
280
|
+
mockPathExists.mockResolvedValue(true);
|
|
281
|
+
mockReadJson.mockResolvedValue({});
|
|
282
|
+
mockWriteJson.mockResolvedValue(undefined);
|
|
283
|
+
mockRemove.mockResolvedValue(undefined);
|
|
284
|
+
mockPrompt.mockResolvedValue(projectData);
|
|
285
|
+
|
|
286
|
+
await expect(runCreate(projectDir, 'starter')).rejects.toThrow('install failed');
|
|
287
|
+
|
|
288
|
+
expect(mockRemove).toHaveBeenCalledWith(projectPath);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('uses custom templates when templateFile option is provided', async () => {
|
|
292
|
+
const fixturesDir = path.join(process.cwd(), 'src', '__tests__', 'fixtures');
|
|
293
|
+
const customPath = path.join(fixturesDir, 'valid-templates.json');
|
|
294
|
+
setupHappyPathMocks();
|
|
295
|
+
mockPrompt.mockResolvedValueOnce(projectData);
|
|
296
|
+
|
|
297
|
+
await runCreate(projectDir, 'custom-one', { templateFile: customPath });
|
|
298
|
+
|
|
299
|
+
expect(mockExeca).toHaveBeenNthCalledWith(
|
|
300
|
+
1,
|
|
301
|
+
'git',
|
|
302
|
+
['clone', 'https://github.com/example/custom-one.git', projectPath],
|
|
303
|
+
expect.any(Object)
|
|
304
|
+
);
|
|
305
|
+
});
|
|
306
|
+
});
|
package/src/cli.ts
CHANGED
|
@@ -2,28 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
import { program } from 'commander';
|
|
4
4
|
import { execa } from 'execa';
|
|
5
|
-
import inquirer from 'inquirer';
|
|
6
5
|
import fs from 'fs-extra';
|
|
7
6
|
import path from 'path';
|
|
8
7
|
import { defaultTemplates } from './templates.js';
|
|
9
8
|
import { mergeTemplates } from './template-loader.js';
|
|
10
9
|
import { offerAndCreateGitHubRepo } from './github.js';
|
|
10
|
+
import { runCreate } from './create.js';
|
|
11
11
|
import { runSave } from './save.js';
|
|
12
12
|
import { runLoad } from './load.js';
|
|
13
13
|
import { runDeployToGitHubPages } from './gh-pages.js';
|
|
14
14
|
|
|
15
|
-
/** Project data provided by the user */
|
|
16
|
-
type ProjectData = {
|
|
17
|
-
/** Project name */
|
|
18
|
-
name: string,
|
|
19
|
-
/** Project version */
|
|
20
|
-
version: string,
|
|
21
|
-
/** Project description */
|
|
22
|
-
description: string,
|
|
23
|
-
/** Project author */
|
|
24
|
-
author: string
|
|
25
|
-
}
|
|
26
|
-
|
|
27
15
|
/** Command to create a new project */
|
|
28
16
|
program
|
|
29
17
|
.version('1.0.0')
|
|
@@ -34,172 +22,14 @@ program
|
|
|
34
22
|
.option('-t, --template-file <path>', 'Path to a JSON file with custom templates (same format as built-in)')
|
|
35
23
|
.option('--ssh', 'Use SSH URL for cloning the template repository')
|
|
36
24
|
.action(async (projectDirectory, templateName, options) => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const projectDirAnswer = await inquirer.prompt([
|
|
42
|
-
{
|
|
43
|
-
type: 'input',
|
|
44
|
-
name: 'projectDirectory',
|
|
45
|
-
message: 'Please provide the directory where you want to create the project?',
|
|
46
|
-
default: 'my-app',
|
|
47
|
-
},
|
|
48
|
-
]);
|
|
49
|
-
projectDirectory = projectDirAnswer.projectDirectory;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// If template name is not provided, show available templates and let user select
|
|
53
|
-
if (!templateName) {
|
|
54
|
-
console.log('\n๐ Available templates:\n');
|
|
55
|
-
templatesToUse.forEach(t => {
|
|
56
|
-
console.log(` ${t.name.padEnd(12)} - ${t.description}`);
|
|
57
|
-
});
|
|
58
|
-
console.log('');
|
|
59
|
-
|
|
60
|
-
const templateQuestion = [
|
|
61
|
-
{
|
|
62
|
-
type: 'list',
|
|
63
|
-
name: 'templateName',
|
|
64
|
-
message: 'Select a template:',
|
|
65
|
-
choices: templatesToUse.map(t => ({
|
|
66
|
-
name: `${t.name} - ${t.description}`,
|
|
67
|
-
value: t.name
|
|
68
|
-
}))
|
|
69
|
-
}
|
|
70
|
-
];
|
|
71
|
-
|
|
72
|
-
const templateAnswer = await inquirer.prompt(templateQuestion);
|
|
73
|
-
templateName = templateAnswer.templateName;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Look up the template by name
|
|
77
|
-
const template = templatesToUse.find(t => t.name === templateName);
|
|
78
|
-
if (!template) {
|
|
79
|
-
console.error(`โ Template "${templateName}" not found.\n`);
|
|
80
|
-
console.log('๐ Available templates:\n');
|
|
81
|
-
templatesToUse.forEach(t => {
|
|
82
|
-
console.log(` ${t.name.padEnd(12)} - ${t.description}`);
|
|
25
|
+
try {
|
|
26
|
+
await runCreate(projectDirectory, templateName, {
|
|
27
|
+
templateFile: options?.templateFile,
|
|
28
|
+
ssh: options?.ssh,
|
|
83
29
|
});
|
|
84
|
-
|
|
30
|
+
} catch {
|
|
85
31
|
process.exit(1);
|
|
86
32
|
}
|
|
87
|
-
|
|
88
|
-
// If --ssh was not passed, prompt whether to use SSH
|
|
89
|
-
let useSSH = options?.ssh;
|
|
90
|
-
if (useSSH === undefined && template.repoSSH) {
|
|
91
|
-
const sshAnswer = await inquirer.prompt([
|
|
92
|
-
{
|
|
93
|
-
type: 'confirm',
|
|
94
|
-
name: 'useSSH',
|
|
95
|
-
message: 'Use SSH URL for cloning?',
|
|
96
|
-
default: false,
|
|
97
|
-
},
|
|
98
|
-
]);
|
|
99
|
-
useSSH = sshAnswer.useSSH;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const templateRepoUrl = useSSH && template.repoSSH ? template.repoSSH : template.repo;
|
|
103
|
-
|
|
104
|
-
// Define the full path for the new project
|
|
105
|
-
const projectPath = path.resolve(projectDirectory);
|
|
106
|
-
console.log(`Cloning template "${templateName}" from ${templateRepoUrl} into ${projectPath}...`);
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
|
|
110
|
-
// Clone the repository
|
|
111
|
-
const cloneArgs = ['clone'];
|
|
112
|
-
if (template.options && Array.isArray(template.options)) {
|
|
113
|
-
cloneArgs.push(...template.options);
|
|
114
|
-
}
|
|
115
|
-
cloneArgs.push(templateRepoUrl, projectPath);
|
|
116
|
-
await execa('git', cloneArgs, { stdio: 'inherit' });
|
|
117
|
-
console.log('โ
Template cloned successfully.');
|
|
118
|
-
|
|
119
|
-
// Remove the .git folder from the *new* project
|
|
120
|
-
await fs.remove(path.join(projectPath, '.git'));
|
|
121
|
-
console.log('๐งน Cleaned up template .git directory.');
|
|
122
|
-
|
|
123
|
-
// Ask user for customization details
|
|
124
|
-
const questions = [
|
|
125
|
-
{
|
|
126
|
-
type: 'input',
|
|
127
|
-
name: 'name',
|
|
128
|
-
message: 'What is the project name?',
|
|
129
|
-
default: path.basename(projectPath),
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
type: 'input',
|
|
133
|
-
name: 'version',
|
|
134
|
-
message: 'What version number would you like to use?',
|
|
135
|
-
default: '1.0.0',
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
type: 'input',
|
|
139
|
-
name: 'description',
|
|
140
|
-
message: 'What is the project description?',
|
|
141
|
-
default: '',
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
type: 'input',
|
|
145
|
-
name: 'author',
|
|
146
|
-
message: 'Who is the author of the project?',
|
|
147
|
-
default: '',
|
|
148
|
-
},
|
|
149
|
-
];
|
|
150
|
-
|
|
151
|
-
const answers: ProjectData = await inquirer.prompt(questions);
|
|
152
|
-
|
|
153
|
-
// Update the package.json in the new project
|
|
154
|
-
const pkgJsonPath = path.join(projectPath, 'package.json');
|
|
155
|
-
|
|
156
|
-
if (await fs.pathExists(pkgJsonPath)) {
|
|
157
|
-
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
158
|
-
|
|
159
|
-
// Overwrite fields with user's answers
|
|
160
|
-
pkgJson.name = answers.name;
|
|
161
|
-
pkgJson.version = answers.version;
|
|
162
|
-
pkgJson.description = answers.description;
|
|
163
|
-
pkgJson.author = answers.author;
|
|
164
|
-
|
|
165
|
-
// Write the updated package.json back
|
|
166
|
-
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
167
|
-
console.log('๐ Customized package.json.');
|
|
168
|
-
} else {
|
|
169
|
-
console.log('โน๏ธ No package.json found in template, skipping customization.');
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const packageManager = template.packageManager || "npm";
|
|
173
|
-
// Install dependencies
|
|
174
|
-
console.log('๐ฆ Installing dependencies... (This may take a moment)');
|
|
175
|
-
await execa(packageManager, ['install'], { cwd: projectPath, stdio: 'inherit' });
|
|
176
|
-
console.log('โ
Dependencies installed.');
|
|
177
|
-
|
|
178
|
-
// Optional: Create GitHub repository
|
|
179
|
-
await offerAndCreateGitHubRepo(projectPath);
|
|
180
|
-
|
|
181
|
-
// Let the user know the project was created successfully
|
|
182
|
-
console.log('\nโจ Project created successfully! โจ\n');
|
|
183
|
-
console.log(`To get started:`);
|
|
184
|
-
console.log(` cd ${projectDirectory}`);
|
|
185
|
-
console.log(' Happy coding! ๐');
|
|
186
|
-
|
|
187
|
-
} catch (error) {
|
|
188
|
-
console.error('โ An error occurred:');
|
|
189
|
-
if (error instanceof Error) {
|
|
190
|
-
console.error(error.message);
|
|
191
|
-
} else if (error && typeof error === 'object' && 'stderr' in error) {
|
|
192
|
-
console.error((error as { stderr?: string }).stderr || String(error));
|
|
193
|
-
} else {
|
|
194
|
-
console.error(String(error));
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Clean up the created directory if an error occurred
|
|
198
|
-
if (await fs.pathExists(projectPath)) {
|
|
199
|
-
await fs.remove(projectPath);
|
|
200
|
-
console.log('๐งน Cleaned up failed project directory.');
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
33
|
});
|
|
204
34
|
|
|
205
35
|
/** Command to initialize a project and optionally create a GitHub repository */
|
package/src/create.ts
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import { execa } from 'execa';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import { defaultTemplates } from './templates.js';
|
|
6
|
+
import { mergeTemplates } from './template-loader.js';
|
|
7
|
+
import { offerAndCreateGitHubRepo } from './github.js';
|
|
8
|
+
|
|
9
|
+
/** Project data provided by the user */
|
|
10
|
+
type ProjectData = {
|
|
11
|
+
/** Project name */
|
|
12
|
+
name: string;
|
|
13
|
+
/** Project version */
|
|
14
|
+
version: string;
|
|
15
|
+
/** Project description */
|
|
16
|
+
description: string;
|
|
17
|
+
/** Project author */
|
|
18
|
+
author: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type RunCreateOptions = {
|
|
22
|
+
templateFile?: string;
|
|
23
|
+
ssh?: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Runs the create flow: clone template, customize package.json, install deps, optionally create GitHub repo.
|
|
28
|
+
* Throws on fatal errors. Caller should catch and process.exit(1).
|
|
29
|
+
*/
|
|
30
|
+
export async function runCreate(
|
|
31
|
+
projectDirectory: string | undefined,
|
|
32
|
+
templateName: string | undefined,
|
|
33
|
+
options?: RunCreateOptions
|
|
34
|
+
): Promise<void> {
|
|
35
|
+
const templatesToUse = mergeTemplates(defaultTemplates, options?.templateFile);
|
|
36
|
+
|
|
37
|
+
// If project directory is not provided, prompt for it
|
|
38
|
+
if (!projectDirectory) {
|
|
39
|
+
const projectDirAnswer = await inquirer.prompt([
|
|
40
|
+
{
|
|
41
|
+
type: 'input',
|
|
42
|
+
name: 'projectDirectory',
|
|
43
|
+
message: 'Please provide the directory where you want to create the project?',
|
|
44
|
+
default: 'my-app',
|
|
45
|
+
},
|
|
46
|
+
]);
|
|
47
|
+
projectDirectory = projectDirAnswer.projectDirectory;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// If template name is not provided, show available templates and let user select
|
|
51
|
+
if (!templateName) {
|
|
52
|
+
console.log('\n๐ Available templates:\n');
|
|
53
|
+
templatesToUse.forEach(t => {
|
|
54
|
+
console.log(` ${t.name.padEnd(12)} - ${t.description}`);
|
|
55
|
+
});
|
|
56
|
+
console.log('');
|
|
57
|
+
|
|
58
|
+
const templateQuestion = [
|
|
59
|
+
{
|
|
60
|
+
type: 'list',
|
|
61
|
+
name: 'templateName',
|
|
62
|
+
message: 'Select a template:',
|
|
63
|
+
choices: templatesToUse.map(t => ({
|
|
64
|
+
name: `${t.name} - ${t.description}`,
|
|
65
|
+
value: t.name,
|
|
66
|
+
})),
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
const templateAnswer = await inquirer.prompt(templateQuestion);
|
|
71
|
+
templateName = templateAnswer.templateName;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Look up the template by name
|
|
75
|
+
const template = templatesToUse.find(t => t.name === templateName);
|
|
76
|
+
if (!template) {
|
|
77
|
+
console.error(`โ Template "${templateName}" not found.\n`);
|
|
78
|
+
console.log('๐ Available templates:\n');
|
|
79
|
+
templatesToUse.forEach(t => {
|
|
80
|
+
console.log(` ${t.name.padEnd(12)} - ${t.description}`);
|
|
81
|
+
});
|
|
82
|
+
console.log('');
|
|
83
|
+
throw new Error(`Template "${templateName}" not found`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const templateRepoUrl = options?.ssh && template.repoSSH ? template.repoSSH : template.repo;
|
|
87
|
+
|
|
88
|
+
// Define the full path for the new project (projectDirectory is set above via arg or prompt)
|
|
89
|
+
const dir = projectDirectory ?? 'my-app';
|
|
90
|
+
const projectPath = path.resolve(dir);
|
|
91
|
+
console.log(`Cloning template "${templateName}" from ${templateRepoUrl} into ${projectPath}...`);
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
// Clone the repository
|
|
95
|
+
const cloneArgs = ['clone'];
|
|
96
|
+
if (template.options && Array.isArray(template.options)) {
|
|
97
|
+
cloneArgs.push(...template.options);
|
|
98
|
+
}
|
|
99
|
+
cloneArgs.push(templateRepoUrl, projectPath);
|
|
100
|
+
await execa('git', cloneArgs, { stdio: 'inherit' });
|
|
101
|
+
console.log('โ
Template cloned successfully.');
|
|
102
|
+
|
|
103
|
+
// Remove the .git folder from the *new* project
|
|
104
|
+
await fs.remove(path.join(projectPath, '.git'));
|
|
105
|
+
console.log('๐งน Cleaned up template .git directory.');
|
|
106
|
+
|
|
107
|
+
// Ask user for customization details
|
|
108
|
+
const questions = [
|
|
109
|
+
{
|
|
110
|
+
type: 'input',
|
|
111
|
+
name: 'name',
|
|
112
|
+
message: 'What is the project name?',
|
|
113
|
+
default: path.basename(projectPath),
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
type: 'input',
|
|
117
|
+
name: 'version',
|
|
118
|
+
message: 'What version number would you like to use?',
|
|
119
|
+
default: '1.0.0',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
type: 'input',
|
|
123
|
+
name: 'description',
|
|
124
|
+
message: 'What is the project description?',
|
|
125
|
+
default: '',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
type: 'input',
|
|
129
|
+
name: 'author',
|
|
130
|
+
message: 'Who is the author of the project?',
|
|
131
|
+
default: '',
|
|
132
|
+
},
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
const answers: ProjectData = await inquirer.prompt(questions);
|
|
136
|
+
|
|
137
|
+
// Update the package.json in the new project
|
|
138
|
+
const pkgJsonPath = path.join(projectPath, 'package.json');
|
|
139
|
+
|
|
140
|
+
if (await fs.pathExists(pkgJsonPath)) {
|
|
141
|
+
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
142
|
+
|
|
143
|
+
// Overwrite fields with user's answers
|
|
144
|
+
pkgJson.name = answers.name;
|
|
145
|
+
pkgJson.version = answers.version;
|
|
146
|
+
pkgJson.description = answers.description;
|
|
147
|
+
pkgJson.author = answers.author;
|
|
148
|
+
|
|
149
|
+
// Write the updated package.json back
|
|
150
|
+
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
151
|
+
console.log('๐ Customized package.json.');
|
|
152
|
+
} else {
|
|
153
|
+
console.log('โน๏ธ No package.json found in template, skipping customization.');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const packageManager = template.packageManager || 'npm';
|
|
157
|
+
// Install dependencies
|
|
158
|
+
console.log('๐ฆ Installing dependencies... (This may take a moment)');
|
|
159
|
+
await execa(packageManager, ['install'], { cwd: projectPath, stdio: 'inherit' });
|
|
160
|
+
console.log('โ
Dependencies installed.');
|
|
161
|
+
|
|
162
|
+
// Optional: Create GitHub repository
|
|
163
|
+
await offerAndCreateGitHubRepo(projectPath);
|
|
164
|
+
|
|
165
|
+
// Let the user know the project was created successfully
|
|
166
|
+
console.log('\nโจ Project created successfully! โจ\n');
|
|
167
|
+
console.log(`To get started:`);
|
|
168
|
+
console.log(` cd ${dir}`);
|
|
169
|
+
console.log(' Happy coding! ๐');
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error('โ An error occurred:');
|
|
172
|
+
if (error instanceof Error) {
|
|
173
|
+
console.error(error.message);
|
|
174
|
+
} else if (error && typeof error === 'object' && 'stderr' in error) {
|
|
175
|
+
console.error((error as { stderr?: string }).stderr || String(error));
|
|
176
|
+
} else {
|
|
177
|
+
console.error(String(error));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Clean up the created directory if an error occurred
|
|
181
|
+
if (await fs.pathExists(projectPath)) {
|
|
182
|
+
await fs.remove(projectPath);
|
|
183
|
+
console.log('๐งน Cleaned up failed project directory.');
|
|
184
|
+
}
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
}
|