@ilanalimanjs/open_pack 2.4.5 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/index.js +45 -82
  2. package/package.json +1 -1
package/bin/index.js CHANGED
@@ -108,19 +108,10 @@ const isWindows = process.platform === 'win32';
108
108
  const isTTY = Boolean(process.stdout.isTTY && process.stdin.isTTY);
109
109
  const SHELL_TOOLS = new Set(Object.keys(PACKAGE_MANAGERS));
110
110
 
111
- const STATE_ICONS = {
112
- done: chalk.green('●'),
113
- warning: chalk.yellow('●'),
114
- failed: chalk.red('●'),
115
- pending: muted('○')
116
- };
117
-
118
- const STATUS_VALUES = {
119
- done: (result) => muted(`${(result.duration / 1000).toFixed(1)}s`),
120
- warning: (result) => chalk.yellow(`${(result.duration / 1000).toFixed(1)}s`),
121
- failed: () => chalk.red('failed'),
122
- pending: () => muted('pending')
123
- };
111
+ const BAR = muted('│');
112
+ const END = muted('└');
113
+ const SPINNER = { interval: 80, frames: ['◒', '◐', '◓', '◑'] };
114
+ const activity = { spinner: null };
124
115
 
125
116
  class UsageError extends Error {}
126
117
 
@@ -294,6 +285,13 @@ const detectIpAddress = () => {
294
285
  return entry?.address ?? null;
295
286
  };
296
287
 
288
+ const formatSystem = () => {
289
+ const release = os.release().split('-')[0];
290
+ const arch = os.arch();
291
+ if (isAndroid()) return `Android (kernel ${release}, ${arch})`;
292
+ return `${PLATFORM_LABELS[process.platform] ?? process.platform} ${release} (${arch})`;
293
+ };
294
+
297
295
  const detectEnvironment = async ({ port, checkUpdate }) => {
298
296
  const managerNames = Object.keys(PACKAGE_MANAGERS);
299
297
  const [gitVersion, portFree, latestVersion, device, ...managerVersions] = await Promise.all([
@@ -308,7 +306,7 @@ const detectEnvironment = async ({ port, checkUpdate }) => {
308
306
  );
309
307
 
310
308
  return {
311
- system: `${PLATFORM_LABELS[process.platform] ?? process.platform} ${os.release()} (${os.arch()})`,
309
+ system: formatSystem(),
312
310
  device,
313
311
  ipAddress: detectIpAddress(),
314
312
  node: process.versions.node,
@@ -348,15 +346,6 @@ const printEnvironmentCard = (environment) => {
348
346
  console.log();
349
347
  };
350
348
 
351
- const printStatusCard = (results) => {
352
- const rows = results.map((result) => ({
353
- label: `${STATE_ICONS[result.state]} ${result.label}`,
354
- value: STATUS_VALUES[result.state](result)
355
- }));
356
- console.log();
357
- console.log(renderCard('Build status', rows));
358
- };
359
-
360
349
  const printBanner = async () => {
361
350
  console.log();
362
351
  for (const [index, line] of BANNER_LINES.entries()) {
@@ -534,7 +523,6 @@ const resolveDefaultOptions = async (cli, environment) => {
534
523
  const buildSteps = ({ name, packageManager, mode, port }) => {
535
524
  const targetDirectory = path.resolve(process.cwd(), name);
536
525
  const manager = PACKAGE_MANAGERS[packageManager];
537
- const customPort = port !== DEFAULT_PORT;
538
526
  const steps = [
539
527
  {
540
528
  active: 'Fetching template',
@@ -545,28 +533,20 @@ const buildSteps = ({ name, packageManager, mode, port }) => {
545
533
  }
546
534
  },
547
535
  {
548
- active: 'Preparing environment',
549
- done: `${ENV_FILE} is ready`,
536
+ active: 'Configuring environment',
537
+ done: 'Environment configured',
550
538
  task: async () => {
551
539
  const source = path.join(targetDirectory, ENV_EXAMPLE_FILE);
552
540
  if (!(await fse.pathExists(source))) throw new Error(`${ENV_EXAMPLE_FILE} not found in the template`);
553
- await fse.copy(source, path.join(targetDirectory, ENV_FILE));
554
- }
555
- },
556
- {
557
- active: 'Injecting template configuration',
558
- done: customPort ? 'SESSION_SECRET and PORT injected' : 'SESSION_SECRET injected',
559
- task: async () => {
560
- const envPath = path.join(targetDirectory, ENV_FILE);
561
- const current = await fse.readFile(envPath, 'utf8');
562
- const withSecret = setEnvValue(current, 'SESSION_SECRET', randomBytes(32).toString('hex'));
563
- await fse.writeFile(envPath, customPort ? setEnvValue(withSecret, 'PORT', String(port)) : withSecret);
541
+ const withSecret = setEnvValue(
542
+ await fse.readFile(source, 'utf8'),
543
+ 'SESSION_SECRET',
544
+ randomBytes(32).toString('hex')
545
+ );
546
+ const content = port === DEFAULT_PORT ? withSecret : setEnvValue(withSecret, 'PORT', String(port));
547
+ await fse.writeFile(path.join(targetDirectory, ENV_FILE), content);
548
+ process.chdir(targetDirectory);
564
549
  }
565
- },
566
- {
567
- active: 'Entering project directory',
568
- done: `Entered ${name}`,
569
- task: async () => process.chdir(targetDirectory)
570
550
  }
571
551
  ];
572
552
 
@@ -579,35 +559,25 @@ const buildSteps = ({ name, packageManager, mode, port }) => {
579
559
  });
580
560
  }
581
561
 
582
- if (mode === MODES.run) {
583
- steps.push({
584
- active: 'Starting template',
585
- done: 'Template ready to run',
586
- task: async () => undefined
587
- });
588
- }
589
-
590
562
  return { steps, targetDirectory, manager };
591
563
  };
592
564
 
593
- const runSteps = async (steps, results) => {
594
- const total = steps.length;
595
- for (const [index, step] of steps.entries()) {
596
- const prefix = muted(`[${index + 1}/${total}]`);
597
- const spinner = ora({ text: `${prefix} ${step.active}`, indent: 2, spinner: 'dots12', color: 'cyan' }).start();
565
+ const runSteps = async (steps) => {
566
+ for (const step of steps) {
567
+ console.log(BAR);
568
+ const spinner = ora({ text: ` ${step.active}`, spinner: SPINNER, color: 'magenta' }).start();
569
+ activity.spinner = spinner;
598
570
  const startedAt = Date.now();
599
571
  try {
600
- const outcome = (await step.task()) ?? {};
572
+ await step.task();
601
573
  const remaining = (step.minDuration ?? STEP_MIN_DURATION_MS) - (Date.now() - startedAt);
602
574
  if (remaining > 0) await pause(remaining);
603
- const state = outcome.state ?? 'done';
604
- const label = outcome.label ?? step.done;
605
- results[index] = { label, state, duration: Date.now() - startedAt };
606
- spinner.stopAndPersist({ symbol: STATE_ICONS[state], text: `${prefix} ${label}` });
575
+ spinner.stopAndPersist({ symbol: chalk.green('◇'), text: ` ${step.done}` });
607
576
  } catch (error) {
608
- results[index] = { label: step.active, state: 'failed', duration: Date.now() - startedAt };
609
- spinner.stopAndPersist({ symbol: STATE_ICONS.failed, text: `${prefix} ${step.active} ${chalk.red('failed')}` });
577
+ spinner.stopAndPersist({ symbol: chalk.red('■'), text: ` ${step.active} ${chalk.red('failed')}` });
610
578
  throw error;
579
+ } finally {
580
+ activity.spinner = null;
611
581
  }
612
582
  }
613
583
  };
@@ -620,20 +590,17 @@ const printSummary = ({ name, packageManager, mode, port, startedAt }) => {
620
590
  if (mode === MODES.none) pending.push(`${packageManager} install`);
621
591
  if (mode !== MODES.run) pending.push(devCommand);
622
592
 
623
- console.log();
624
- console.log(` ${chalk.green('●')} ${chalk.bold(name)} is ready ${muted(`in ${duration}s`)}`);
625
- console.log();
593
+ console.log(BAR);
594
+ console.log(`${chalk.green('◇')} ${chalk.bold(name)} is ready ${muted(`in ${duration}s`)}`);
626
595
  if (mode === MODES.run) {
627
- console.log(` ${muted('Server')} ${accent(devUrl)}`);
628
- console.log(` ${muted('Health check')} ${accent(`${devUrl}/api/health`)}`);
629
- console.log(` ${muted('Press Ctrl+C to stop')}`);
596
+ console.log(`${BAR} ${muted('Server')} ${accent(devUrl)}`);
597
+ console.log(`${BAR} ${muted('Health')} ${accent(`${devUrl}/api/health`)}`);
598
+ console.log(`${END} ${muted('Press Ctrl+C to stop')}`);
630
599
  console.log();
631
600
  return;
632
601
  }
633
- console.log(` ${muted('Next steps')}`);
634
- pending.forEach((step, index) => console.log(` ${muted(String(index + 1))} ${accent(step)}`));
635
- console.log();
636
- console.log(` ${muted('Health check')} ${accent(`${devUrl}/api/health`)}`);
602
+ pending.forEach((step) => console.log(`${BAR} ${accent(step)}`));
603
+ console.log(`${END} ${muted('Health')} ${accent(`${devUrl}/api/health`)}`);
637
604
  console.log();
638
605
  };
639
606
 
@@ -656,7 +623,7 @@ async function main() {
656
623
  const flagError = validateFlags(cli);
657
624
  if (flagError) throw new UsageError(flagError);
658
625
 
659
- const inspector = ora({ text: 'Inspecting your environment', indent: 2, spinner: 'dots12', color: 'cyan' }).start();
626
+ const inspector = ora({ text: 'Inspecting your environment', indent: 2, spinner: SPINNER, color: 'magenta' }).start();
660
627
  const environment = await detectEnvironment({
661
628
  port: cli.values.port === undefined ? DEFAULT_PORT : Number(cli.values.port),
662
629
  checkUpdate: !cli.values['no-update-check']
@@ -676,33 +643,30 @@ async function main() {
676
643
  }
677
644
 
678
645
  const interactive = isTTY && !cli.values.yes;
679
- if (!interactive) console.log(` ${muted('Non-interactive mode')}\n`);
646
+ if (!interactive) console.log(` ${muted('Non-interactive mode')}`);
680
647
 
681
648
  const options = interactive ? await askOptions(cli, environment) : await resolveDefaultOptions(cli, environment);
682
649
  const originalCwd = process.cwd();
683
650
  const { steps, targetDirectory, manager } = buildSteps(options);
684
651
  const rollback = createRollback(targetDirectory, originalCwd, !(await fse.pathExists(targetDirectory)));
685
- const results = steps.map((step) => ({ label: step.done, state: 'pending', duration: null }));
686
652
  const startedAt = performance.now();
687
653
 
688
654
  const handleInterrupt = async () => {
655
+ activity.spinner?.stop();
689
656
  const removed = await rollback.run();
690
- console.log();
691
- console.log(` ${chalk.yellow('!')} Interrupted${removed ? ', partial project removed' : ''}`);
657
+ console.log(`${chalk.yellow('■')} Interrupted${removed ? ', partial project removed' : ''}`);
692
658
  console.log();
693
659
  process.exit(EXIT_INTERRUPTED);
694
660
  };
695
661
 
696
- console.log();
697
662
  process.once('SIGINT', handleInterrupt);
698
663
 
699
664
  try {
700
- await runSteps(steps, results);
665
+ await runSteps(steps);
701
666
  rollback.disarm();
702
667
  } catch (error) {
703
668
  const removed = await rollback.run();
704
- printStatusCard(results);
705
- if (removed) console.log(`\n ${muted('Partial project removed')}`);
669
+ if (removed) console.log(`${BAR} ${muted('Partial project removed')}`);
706
670
  const alreadyExists = /already exists/i.test(error.message);
707
671
  printFailure(
708
672
  alreadyExists ? 'Directory already exists' : 'Failed to create project',
@@ -713,7 +677,6 @@ async function main() {
713
677
  process.removeListener('SIGINT', handleInterrupt);
714
678
  }
715
679
 
716
- printStatusCard(results);
717
680
  printSummary({ ...options, startedAt });
718
681
 
719
682
  if (options.mode === MODES.run) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ilanalimanjs/open_pack",
3
- "version": "2.4.5",
3
+ "version": "3.0.0",
4
4
  "description": "Open Pack CLI: create a new project from the Open Pack source template",
5
5
  "type": "module",
6
6
  "bin": {