Skip to content

Commit bf78197

Browse files
committed
feat: update CLI to use 'init' command for project scaffolding and improve service health check endpoint
Signed-off-by: kaifcoder <kaifmohd2014@gmail.com>
1 parent 842bfe7 commit bf78197

File tree

4 files changed

+29
-87
lines changed

4 files changed

+29
-87
lines changed

bin/index.js

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,22 @@ program
2929
.option('--package-manager <pm>', 'npm | pnpm | yarn | bun (default: npm)')
3030
.option('--frontend-generator', 'Use create-next-app to scaffold the frontend instead of the bundled template')
3131
.option('--yes', 'Skip confirmation (assume yes) for non-interactive use')
32-
.action(async (projectNameArg, options) => {
32+
.action(async (...args) => {
33+
const projectNameArg = args[0];
34+
const command = args[args.length - 1];
35+
const options = command.opts();
36+
3337
await scaffoldMonorepo(projectNameArg, options);
3438
});
3539

3640
// Backward compatibility: calling the root command directly still scaffolds (deprecated path).
41+
// Simplified to avoid option conflicts with subcommands
3742
program
3843
.argument('[project-name]', '(Deprecated: call `create-polyglot init <name>` instead) Project name')
39-
.option('-s, --services <services>', '(Deprecated) Services list')
40-
.option('--preset <preset>', '(Deprecated) Preset turborepo|nx')
41-
.option('--no-install', '(Deprecated) Skip install')
42-
.option('--git', '(Deprecated) Init git')
43-
.option('--force', '(Deprecated) Overwrite directory')
44-
.option('--package-manager <pm>', '(Deprecated) Package manager')
45-
.option('--frontend-generator', '(Deprecated) Use create-next-app for frontend')
46-
.option('--yes', '(Deprecated) Assume yes for prompts')
47-
.action(async (projectNameArg, options) => {
48-
if (!options._deprecatedNoticeShown) {
49-
console.log(chalk.yellow('⚠️ Direct invocation is deprecated. Use `create-polyglot init` going forward.'));
50-
options._deprecatedNoticeShown = true;
51-
}
52-
await scaffoldMonorepo(projectNameArg, options);
44+
.action(async (projectNameArg) => {
45+
console.log(chalk.yellow('⚠️ Direct invocation is deprecated. Use `create-polyglot init` going forward.'));
46+
console.log(chalk.yellow(' Example: create-polyglot init ' + (projectNameArg || 'my-project')));
47+
process.exit(1);
5348
});
5449

5550
// Additional commands must be registered before final parse.

bin/lib/admin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ async function checkServiceStatus(service) {
1111
resolve({ status: 'down', error: 'Timeout' });
1212
}, 3000);
1313

14-
const req = http.get(`http://localhost:${service.port}`, (res) => {
14+
const req = http.get(`http://localhost:${service.port}/health`, (res) => {
1515
clearTimeout(timeout);
1616
resolve({
1717
status: res.statusCode < 400 ? 'up' : 'error',

bin/lib/scaffold.js

Lines changed: 17 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,7 @@ export async function scaffoldMonorepo(projectNameArg, options) {
2323
validate: v => v && /^[a-zA-Z0-9._-]+$/.test(v) ? true : 'Use alphanumerics, dash, underscore, dot'
2424
});
2525
}
26-
if (!options.services) {
27-
interactiveQuestions.push({
28-
type: 'multiselect',
29-
name: 'services',
30-
message: 'Select services to include:',
31-
choices: [
32-
{ title: 'Node.js (Express)', value: 'node' },
33-
{ title: 'Python (FastAPI)', value: 'python' },
34-
{ title: 'Go (Fiber-like)', value: 'go' },
35-
{ title: 'Java (Spring Boot)', value: 'java' },
36-
{ title: 'Frontend (Next.js)', value: 'frontend' }
37-
],
38-
instructions: false,
39-
min: 1
40-
});
41-
}
26+
4227
if (!options.preset) {
4328
interactiveQuestions.push({
4429
type: 'select',
@@ -87,9 +72,6 @@ export async function scaffoldMonorepo(projectNameArg, options) {
8772
case 'projectName':
8873
answers.projectName = projectNameArg || 'app';
8974
break;
90-
case 'services':
91-
answers.services = ['node'];
92-
break;
9375
case 'preset':
9476
answers.preset = '';
9577
break;
@@ -118,11 +100,7 @@ export async function scaffoldMonorepo(projectNameArg, options) {
118100
console.error(chalk.red('Project name is required.'));
119101
process.exit(1);
120102
}
121-
options.services = options.services || (answers.services ? answers.services.join(',') : undefined);
122-
if (!options.services) {
123-
console.error(chalk.red('At least one service must be selected.'));
124-
process.exit(1);
125-
}
103+
// Note: options.services will be handled in the dynamic flow below if not provided via CLI
126104
options.preset = options.preset || answers.preset || '';
127105
options.packageManager = options.packageManager || answers.packageManager || 'npm';
128106
if (options.git === undefined) options.git = answers.git;
@@ -173,17 +151,21 @@ export async function scaffoldMonorepo(projectNameArg, options) {
173151
services.push({ type, name, port });
174152
}
175153
} else {
176-
// New dynamic interactive flow: ask how many services, then collect each.
177-
const countAns = await prompts({
178-
type: 'number',
179-
name: 'svcCount',
180-
message: 'How many services do you want to create?',
181-
initial: 1,
182-
min: 1,
183-
validate: v => Number.isInteger(v) && v > 0 && v <= 50 ? true : 'Enter a positive integer (max 50)'
184-
});
185-
const svcCount = countAns.svcCount || 1;
186-
for (let i = 0; i < svcCount; i++) {
154+
// Dynamic interactive flow: ask how many services, then collect each.
155+
// In non-interactive mode, default to a single node service
156+
if (nonInteractive) {
157+
services.push({ type: 'node', name: 'node', port: defaultPorts.node });
158+
} else {
159+
const countAns = await prompts({
160+
type: 'number',
161+
name: 'svcCount',
162+
message: 'How many services do you want to create?',
163+
initial: 1,
164+
min: 1,
165+
validate: v => Number.isInteger(v) && v > 0 && v <= 50 ? true : 'Enter a positive integer (max 50)'
166+
});
167+
const svcCount = countAns.svcCount || 1;
168+
for (let i = 0; i < svcCount; i++) {
187169
const typeAns = await prompts({
188170
type: 'select',
189171
name: 'svcType',
@@ -224,41 +206,6 @@ export async function scaffoldMonorepo(projectNameArg, options) {
224206
}
225207
}
226208
services.push({ type: svcType, name: svcName, port: svcPort });
227-
}
228-
}
229-
230-
// Always allow customization of name & port in interactive mode (not nonInteractive)
231-
if (!nonInteractive) {
232-
for (let i = 0; i < services.length; i++) {
233-
const svc = services[i];
234-
const rename = await prompts({
235-
type: 'text',
236-
name: 'newName',
237-
message: `Name for ${svc.type} service (leave blank to keep '${svc.name}'):`,
238-
validate: v => !v || (/^[a-zA-Z0-9._-]+$/.test(v) ? true : 'Use alphanumerics, dash, underscore, dot')
239-
});
240-
if (rename.newName && rename.newName !== svc.name) {
241-
if (reservedNames.has(rename.newName)) {
242-
console.log(chalk.red(`Name '${rename.newName}' is reserved. Keeping '${svc.name}'.`));
243-
} else if (services.find(s => s !== svc && s.name === rename.newName)) {
244-
console.log(chalk.red(`Name '${rename.newName}' already used. Keeping '${svc.name}'.`));
245-
} else {
246-
svc.name = rename.newName;
247-
}
248-
}
249-
const portResp = await prompts({
250-
type: 'text',
251-
name: 'newPort',
252-
message: `Port for ${svc.name} (${svc.type}) (default ${svc.port}):`,
253-
validate: v => !v || (/^\d+$/.test(v) && +v > 0 && +v <= 65535) ? true : 'Enter a valid port 1-65535'
254-
});
255-
if (portResp.newPort) {
256-
const newPort = Number(portResp.newPort);
257-
if (services.find(s => s !== svc && s.port === newPort)) {
258-
console.log(chalk.red(`Port ${newPort} already used; keeping ${svc.port}.`));
259-
} else {
260-
svc.port = newPort;
261-
}
262209
}
263210
}
264211
}

tests/smoke.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe('create-polyglot CLI smoke', () => {
2020
it('scaffolds a project with a node service', async () => {
2121
const repoRoot = process.cwd();
2222
const cliPath = path.join(repoRoot, 'bin/index.js');
23-
await execa('node', [cliPath, projName, '--services', 'node', '--no-install', '--yes'], { cwd: tmpDir });
23+
await execa('node', [cliPath, 'init', projName, '--services', 'node', '--no-install', '--yes'], { cwd: tmpDir });
2424
const projectPath = path.join(tmpDir, projName);
2525
expect(fs.existsSync(path.join(projectPath, 'services/node'))).toBe(true);
2626
expect(fs.existsSync(path.join(projectPath, 'package.json'))).toBe(true);

0 commit comments

Comments
 (0)