smippo 0.0.8 → 0.0.9

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/package.json +1 -1
  2. package/src/server.js +100 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smippo",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "S.M.I.P.P.O. — Structured Mirroring of Internet Pages and Public Objects. Modern website copier that captures sites exactly as they appear in your browser.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/server.js CHANGED
@@ -4,6 +4,8 @@ import fs from 'fs-extra';
4
4
  import path from 'path';
5
5
  import chalk from 'chalk';
6
6
  import {exec} from 'child_process';
7
+ import * as p from '@clack/prompts';
8
+ import {readManifest} from './manifest.js';
7
9
 
8
10
  // MIME type mapping
9
11
  const MIME_TYPES = {
@@ -335,6 +337,7 @@ export async function createServer(options = {}) {
335
337
  port: requestedPort = 8080,
336
338
  host = '127.0.0.1',
337
339
  open = false,
340
+ openPath = null, // Specific path to open (e.g., domain directory)
338
341
  cors = true,
339
342
  verbose = false,
340
343
  quiet = false,
@@ -470,7 +473,10 @@ export async function createServer(options = {}) {
470
473
  // Start listening
471
474
  return new Promise(resolve => {
472
475
  server.listen(port, host, () => {
473
- const url = `http://${host === '0.0.0.0' ? 'localhost' : host}:${port}`;
476
+ const baseUrl = `http://${host === '0.0.0.0' ? 'localhost' : host}:${port}`;
477
+
478
+ // Build the URL with optional path
479
+ const url = openPath ? `${baseUrl}/${openPath}/` : baseUrl;
474
480
 
475
481
  if (!quiet) {
476
482
  console.log('');
@@ -570,16 +576,108 @@ function truncatePath(p, maxLen) {
570
576
  return '...' + p.slice(-(maxLen - 3));
571
577
  }
572
578
 
579
+ /**
580
+ * Get captured sites from a smippo output directory
581
+ */
582
+ async function getCapturedSites(directory) {
583
+ const sites = [];
584
+ const smippoDir = path.join(directory, '.smippo');
585
+
586
+ if (!(await fs.pathExists(smippoDir))) {
587
+ return sites;
588
+ }
589
+
590
+ // Read manifest for site info
591
+ const manifest = await readManifest(directory);
592
+
593
+ // Find domain directories
594
+ const entries = await fs.readdir(directory);
595
+ for (const entry of entries) {
596
+ if (entry.startsWith('.')) continue;
597
+ const entryPath = path.join(directory, entry);
598
+ const stat = await fs.stat(entryPath);
599
+ if (stat.isDirectory()) {
600
+ // Check if this directory has an index.html
601
+ const indexPath = path.join(entryPath, 'index.html');
602
+ const hasIndex = await fs.pathExists(indexPath);
603
+
604
+ sites.push({
605
+ domain: entry,
606
+ path: entry,
607
+ hasIndex,
608
+ rootUrl: manifest?.rootUrl || null,
609
+ title: manifest?.pages?.[0]?.title || entry,
610
+ pagesCount: manifest?.stats?.pagesCapt || 0,
611
+ assetsCount: manifest?.stats?.assetsCapt || 0,
612
+ lastUpdated: manifest?.updated || null,
613
+ });
614
+ }
615
+ }
616
+
617
+ return sites;
618
+ }
619
+
573
620
  /**
574
621
  * Serve command for CLI
575
622
  */
576
623
  export async function serve(options) {
577
624
  try {
625
+ const directory = options.output || options.directory || './site';
626
+ const resolvedDir = path.resolve(directory);
627
+
628
+ // Check if this is a smippo capture directory
629
+ const sites = await getCapturedSites(resolvedDir);
630
+ let sitePath = null;
631
+
632
+ if (sites.length > 0 && process.stdin.isTTY && !options.quiet) {
633
+ // Show interactive site selection
634
+ console.log('');
635
+ p.intro(chalk.cyan('Smippo Server'));
636
+
637
+ if (sites.length === 1) {
638
+ // Single site - auto-select but show info
639
+ const site = sites[0];
640
+ console.log(
641
+ chalk.dim(' Found captured site: ') + chalk.bold(site.domain),
642
+ );
643
+ if (site.pagesCount > 0) {
644
+ console.log(
645
+ chalk.dim(` ${site.pagesCount} pages, ${site.assetsCount} assets`),
646
+ );
647
+ }
648
+ sitePath = site.path;
649
+ } else {
650
+ // Multiple sites - let user choose
651
+ const siteOptions = sites.map(site => ({
652
+ value: site.path,
653
+ label: site.domain,
654
+ hint: site.pagesCount > 0 ? `${site.pagesCount} pages` : undefined,
655
+ }));
656
+
657
+ const selected = await p.select({
658
+ message: 'Which site would you like to serve?',
659
+ options: siteOptions,
660
+ });
661
+
662
+ if (p.isCancel(selected)) {
663
+ p.cancel('Cancelled');
664
+ process.exit(0);
665
+ }
666
+
667
+ sitePath = selected;
668
+ }
669
+ console.log('');
670
+ } else if (sites.length === 1) {
671
+ // Non-interactive mode with single site
672
+ sitePath = sites[0].path;
673
+ }
674
+
578
675
  const serverInfo = await createServer({
579
- directory: options.output || options.directory || './site',
676
+ directory: resolvedDir,
580
677
  port: options.port || 8080,
581
678
  host: options.host || '127.0.0.1',
582
679
  open: options.open,
680
+ openPath: sitePath, // Pass the site path to open
583
681
  cors: options.cors !== false,
584
682
  verbose: options.verbose,
585
683
  quiet: options.quiet,