Skip to content

Commit fcb1e50

Browse files
chore: remove demo package and services
1 parent 670a814 commit fcb1e50

File tree

18 files changed

+143
-3235
lines changed

18 files changed

+143
-3235
lines changed

bin/index.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import { Command } from 'commander';
33
import chalk from 'chalk';
44
import { scaffoldMonorepo, addService, scaffoldPlugin } from './lib/scaffold.js';
5+
import fs from 'fs';
6+
import path from 'path';
7+
import { renderServicesTable } from './lib/ui.js';
58
import { runDev } from './lib/dev.js';
69

710
const program = new Command();
@@ -103,5 +106,29 @@ program
103106
await runDev({ docker: !!opts.docker });
104107
});
105108

109+
program
110+
.command('services')
111+
.description('List services in the current workspace (table)')
112+
.option('--json', 'Output raw JSON instead of table')
113+
.action(async (opts) => {
114+
try {
115+
const cwd = process.cwd();
116+
const cfgPath = path.join(cwd, 'polyglot.json');
117+
if (!fs.existsSync(cfgPath)) {
118+
console.log(chalk.red('polyglot.json not found. Run inside a generated workspace.'));
119+
process.exit(1);
120+
}
121+
const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf-8'));
122+
if (opts.json) {
123+
console.log(JSON.stringify(cfg.services, null, 2));
124+
} else {
125+
renderServicesTable(cfg.services, { title: 'Workspace Services' });
126+
}
127+
} catch (e) {
128+
console.error(chalk.red('Failed to list services:'), e.message);
129+
process.exit(1);
130+
}
131+
});
132+
106133
program.parse();
107134

bin/lib/scaffold.js

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import fs from 'fs-extra';
44
import path from 'path';
55
import url from 'url';
66
import { execa } from 'execa';
7+
import { renderServicesTable, printBoxMessage } from './ui.js';
78

89
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
910

@@ -234,10 +235,12 @@ export async function scaffoldMonorepo(projectNameArg, options) {
234235
portMap.set(s.port, s.name);
235236
}
236237

237-
console.log(chalk.magenta('\nSummary:'));
238-
console.table(services.map(s => ({ type: s.type, name: s.name, port: s.port })));
239-
console.log(`Preset: ${options.preset || 'none'}`);
240-
console.log(`Package Manager: ${options.packageManager}`);
238+
printBoxMessage([
239+
`Project: ${projectName}`,
240+
`Preset: ${options.preset || 'none'} | Package Manager: ${options.packageManager}`,
241+
'Selected Services:'
242+
], { color: chalk.magenta });
243+
renderServicesTable(services.map(s => ({ ...s, path: `services/${s.name}` })), { title: 'Service Summary' });
241244
let proceed = true;
242245
if (!nonInteractive) {
243246
const answer = await prompts({
@@ -318,23 +321,24 @@ export async function scaffoldMonorepo(projectNameArg, options) {
318321
console.log(chalk.green(`✅ Created ${svcName} (${svcType}) service on port ${svcPort}`));
319322
}
320323

321-
const rootPkgPath = path.join(projectDir, 'package.json');
324+
const rootPkgPath = path.join(projectDir, 'package.json');
322325
const rootPkg = {
323326
name: projectName,
324327
private: true,
325328
version: '0.1.0',
326-
workspaces: ['apps/*', 'packages/*'],
329+
workspaces: ['services/*', 'packages/*'],
327330
scripts: {
328331
dev: 'node scripts/dev-basic.cjs',
329-
'list:services': 'ls apps',
332+
'list:services': 'node scripts/list-services.mjs',
330333
format: 'prettier --write .',
331-
lint: 'eslint "apps/**/*.{js,jsx,ts,tsx}" --max-warnings 0 || true'
334+
lint: 'eslint "services/**/*.{js,jsx,ts,tsx}" --max-warnings 0 || true'
332335
},
333336
devDependencies: {
334337
prettier: '^3.3.3',
335338
eslint: '^9.11.1',
336339
'eslint-config-prettier': '^9.1.0',
337-
'eslint-plugin-import': '^2.29.1'
340+
'eslint-plugin-import': '^2.29.1',
341+
chalk: '^5.6.2'
338342
}
339343
};
340344
if (options.preset === 'turborepo') {
@@ -346,9 +350,10 @@ export async function scaffoldMonorepo(projectNameArg, options) {
346350
}
347351
await fs.writeJSON(rootPkgPath, rootPkg, { spaces: 2 });
348352

353+
// Always ensure scripts dir exists (needed for list-services script)
354+
const scriptsDir = path.join(projectDir, 'scripts');
355+
await fs.mkdirp(scriptsDir);
349356
if (!options.preset) {
350-
const scriptsDir = path.join(projectDir, 'scripts');
351-
await fs.mkdirp(scriptsDir);
352357
const runnerSrc = path.join(__dirname, '../../scripts/dev-basic.cjs');
353358
try {
354359
if (await fs.pathExists(runnerSrc)) {
@@ -358,6 +363,9 @@ export async function scaffoldMonorepo(projectNameArg, options) {
358363
console.log(chalk.yellow('⚠️ Failed to copy dev-basic runner:', e.message));
359364
}
360365
}
366+
// Create list-services script with runtime status detection
367+
const listScriptPath = path.join(scriptsDir, 'list-services.mjs');
368+
await fs.writeFile(listScriptPath, `#!/usr/bin/env node\nimport fs from 'fs';\nimport path from 'path';\nimport net from 'net';\nimport chalk from 'chalk';\nconst cwd = process.cwd();\nconst cfgPath = path.join(cwd, 'polyglot.json');\nif(!fs.existsSync(cfgPath)){ console.error(chalk.red('polyglot.json not found.')); process.exit(1);}\nconst cfg = JSON.parse(fs.readFileSync(cfgPath,'utf-8'));\n\nfunction strip(str){return str.replace(/\\x1B\\[[0-9;]*m/g,'');}\nfunction pad(str,w){const raw=strip(str);return str+' '.repeat(Math.max(0,w-raw.length));}\nfunction table(items){ if(!items.length){console.log(chalk.yellow('No services.'));return;} const cols=[{k:'name',h:'Name'},{k:'type',h:'Type'},{k:'port',h:'Port'},{k:'status',h:'Status'},{k:'path',h:'Path'}]; const widths=cols.map(c=>Math.max(c.h.length,...items.map(i=>strip(i[c.k]).length))+2); const top='┌'+widths.map(w=>'─'.repeat(w)).join('┬')+'┐'; const sep='├'+widths.map(w=>'─'.repeat(w)).join('┼')+'┤'; const bot='└'+widths.map(w=>'─'.repeat(w)).join('┴')+'┘'; console.log(top); console.log('│'+cols.map((c,i)=>pad(chalk.bold.white(c.h),widths[i])).join('│')+'│'); console.log(sep); for(const it of items){ console.log('│'+cols.map((c,i)=>pad(it[c.k],widths[i])).join('│')+'│'); } console.log(bot); console.log(chalk.gray('Total: '+items.length)); }\n\nasync function check(port){ return new Promise(res=>{ const sock=net.createConnection({port,host:'127.0.0.1'},()=>{sock.destroy();res(true);}); sock.setTimeout(350,()=>{sock.destroy();res(false);}); sock.on('error',()=>{res(false);});}); }\nconst promises = cfg.services.map(async s=>{ const up = await check(s.port); return { ...s, _up: up }; });\nconst results = await Promise.all(promises);\nconst rows = results.map(s=>({ name: chalk.cyan(s.name), type: colorType(s.type)(s.type), port: chalk.green(String(s.port)), status: s._up ? chalk.bgGreen.black(' UP ') : chalk.bgRed.white(' DOWN '), path: chalk.dim(s.path) }));\nfunction colorType(t){ switch(t){case 'node': return chalk.green; case 'python': return chalk.yellow; case 'go': return chalk.cyan; case 'java': return chalk.red; case 'frontend': return chalk.blue; default: return chalk.white;} }\nif(process.argv.includes('--json')) { console.log(JSON.stringify(results.map(r=>({name:r.name,type:r.type,port:r.port,up:r._up,path:r.path})),null,2)); } else { console.log(chalk.magentaBright('\nWorkspace Services (runtime status)')); table(rows); }\n`);
361369

362370
const readmePath = path.join(projectDir, 'README.md');
363371
const svcList = services.map(s => `- ${s.name} (${s.type}) port:${s.port}`).join('\n');
@@ -459,14 +467,16 @@ export async function scaffoldMonorepo(projectNameArg, options) {
459467
};
460468
await fs.writeJSON(path.join(projectDir, 'polyglot.json'), polyglotConfig, { spaces: 2 });
461469

462-
console.log(chalk.blueBright('\n🎉 Monorepo setup complete!\n'));
463-
console.log('👉 Next steps:');
464-
console.log(` cd ${projectName}`);
465-
if (options.noInstall) console.log(` ${pm} install`);
466-
console.log(` ${pm} run list:services`);
467-
console.log(` ${pm} run dev`);
468-
console.log(' docker compose up --build');
469-
console.log('\nHappy hacking!\n');
470+
printBoxMessage([
471+
'🎉 Monorepo setup complete!',
472+
`cd ${projectName}`,
473+
options.noInstall ? `${pm} install` : '',
474+
`${pm} run list:services # quick list (fancy table)`,
475+
`${pm} run dev # run local node/frontend services`,
476+
'docker compose up --build# run all via docker',
477+
'',
478+
'Happy hacking!'
479+
].filter(Boolean));
470480
} catch (err) {
471481
console.error(chalk.red('Failed to scaffold project:'), err);
472482
process.exit(1);

bin/lib/ui.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import chalk from 'chalk';
2+
3+
// Simple table renderer without external heavy deps (keep bundle light)
4+
// Falls back to console.table if terminal width is too narrow.
5+
export function renderServicesTable(services, { title = 'Services', showHeader = true } = {}) {
6+
if (!services || !services.length) {
7+
console.log(chalk.yellow('No services to display.'));
8+
return;
9+
}
10+
const cols = [
11+
{ key: 'name', label: 'Name' },
12+
{ key: 'type', label: 'Type' },
13+
{ key: 'port', label: 'Port' },
14+
{ key: 'path', label: 'Path' }
15+
];
16+
const rows = services.map(s => ({
17+
name: chalk.bold.cyan(s.name),
18+
type: colorType(s.type)(s.type),
19+
port: chalk.green(String(s.port)),
20+
path: chalk.dim(s.path || `services/${s.name}`)
21+
}));
22+
23+
const termWidth = process.stdout.columns || 80;
24+
const minWidthNeeded = cols.reduce((a,c)=>a + c.label.length + 5, 0);
25+
if (termWidth < minWidthNeeded) {
26+
console.table(services.map(s => ({ name: s.name, type: s.type, port: s.port, path: s.path || `services/${s.name}` })));
27+
return;
28+
}
29+
30+
const colWidths = cols.map(c => Math.max(c.label.length, ...rows.map(r => strip(r[c.key]).length)) + 2);
31+
const totalWidth = colWidths.reduce((a,b)=>a+b,0) + cols.length + 1;
32+
const top = '┌' + colWidths.map(w => '─'.repeat(w)).join('┬') + '┐';
33+
const sep = '├' + colWidths.map(w => '─'.repeat(w)).join('┼') + '┤';
34+
const bottom = '└' + colWidths.map(w => '─'.repeat(w)).join('┴') + '┘';
35+
36+
console.log(chalk.magentaBright(`\n${title}`));
37+
console.log(top);
38+
if (showHeader) {
39+
const header = '│' + cols.map((c,i)=>pad(chalk.bold.white(c.label), colWidths[i])).join('│') + '│';
40+
console.log(header);
41+
console.log(sep);
42+
}
43+
for (const r of rows) {
44+
const line = '│' + cols.map((c,i)=>pad(r[c.key], colWidths[i])).join('│') + '│';
45+
console.log(line);
46+
}
47+
console.log(bottom);
48+
console.log(chalk.gray(`Total: ${services.length}`));
49+
}
50+
51+
function pad(str, width) {
52+
const raw = strip(str);
53+
const diff = width - raw.length;
54+
return str + ' '.repeat(diff);
55+
}
56+
57+
function strip(str) {
58+
return str.replace(/\x1B\[[0-9;]*m/g,'');
59+
}
60+
61+
function colorType(type) {
62+
switch(type) {
63+
case 'node': return chalk.green;
64+
case 'python': return chalk.yellow;
65+
case 'go': return chalk.cyan;
66+
case 'java': return chalk.red;
67+
case 'frontend': return chalk.blue;
68+
default: return chalk.white;
69+
}
70+
}
71+
72+
export function printBoxMessage(lines, { color = chalk.blueBright } = {}) {
73+
const clean = lines.filter(Boolean);
74+
const width = Math.min(Math.max(...clean.map(l => l.length))+4, process.stdout.columns || 100);
75+
const top = '┏' + '━'.repeat(width-2) + '┓';
76+
const bottom = '┗' + '━'.repeat(width-2) + '┛';
77+
console.log(color(top));
78+
for (const l of clean) {
79+
const truncated = l.length + 4 > width ? l.slice(0,width-5) + '…' : l;
80+
const pad = width - 2 - truncated.length;
81+
console.log(color('┃ ' + truncated + ' '.repeat(pad-1) + '┃'));
82+
}
83+
console.log(color(bottom));
84+
}

demo/.eslintrc.cjs

Lines changed: 0 additions & 1 deletion
This file was deleted.

demo/.prettierrc

Lines changed: 0 additions & 5 deletions
This file was deleted.

demo/README.md

Lines changed: 0 additions & 18 deletions
This file was deleted.

demo/compose.yaml

Lines changed: 0 additions & 16 deletions
This file was deleted.

0 commit comments

Comments
 (0)