@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Red Hat
3
+ Copyright (c) 2025 - 2026, Red Hat
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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
- const templatesToUse = mergeTemplates(defaultTemplates, options?.templateFile);
24
- // If project directory is not provided, prompt for it
25
- if (!projectDirectory) {
26
- const projectDirAnswer = await inquirer.prompt([
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
- // Look up the template by name
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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"}
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/patternfly-cli",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Patternfly cli for scaffolding projects, performing code mods, and running project related tasks",
5
5
  "author": "Red Hat",
6
6
  "license": "MIT",
@@ -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
- const templatesToUse = mergeTemplates(defaultTemplates, options?.templateFile);
38
-
39
- // If project directory is not provided, prompt for it
40
- if (!projectDirectory) {
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
- console.log('');
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
+ }