htmlhost-cli 2.1.5 → 2.1.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "htmlhost-cli",
3
- "version": "2.1.5",
3
+ "version": "2.1.8",
4
4
  "description": "Deploy HTML files from the terminal — htmlhost.co CLI",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.mjs CHANGED
@@ -4,7 +4,7 @@
4
4
  import { bold, dim, cyan, err } from "./ui.mjs";
5
5
  import { ApiError } from "./api.mjs";
6
6
 
7
- const VERSION = "2.1.5";
7
+ const VERSION = "2.1.7";
8
8
 
9
9
  const HELP = `
10
10
  ${bold("htmlhost")} ${dim(`v${VERSION}`)} — deploy HTML from the terminal
@@ -1,6 +1,7 @@
1
- import { readFileSync, writeFileSync, statSync, existsSync, readdirSync } from "node:fs";
1
+ import { readFileSync, writeFileSync, statSync, existsSync, readdirSync, mkdirSync } from "node:fs";
2
2
  import { basename, resolve, dirname, join, extname } from "node:path";
3
3
  import { createInterface } from "node:readline";
4
+ import { createHash } from "node:crypto";
4
5
  import { post } from "../api.mjs";
5
6
  import { ok, err, info, cyan, dim, bold, yellow, green, formatBytes, mimeFromExt } from "../ui.mjs";
6
7
 
@@ -347,6 +348,7 @@ async function deployDirectory(dirPath, { ttl, title, slug: explicitSlug, forceN
347
348
  info(`Processing ${cyan(bold(String(htmlFiles.length)))} page${htmlFiles.length > 1 ? "s" : ""}…`);
348
349
 
349
350
  const pagePayloads = [];
351
+ const seenPaths = new Set();
350
352
 
351
353
  for (const file of htmlFiles) {
352
354
  const html = readFileSync(file.filePath, "utf8");
@@ -362,6 +364,12 @@ async function deployDirectory(dirPath, { ttl, title, slug: explicitSlug, forceN
362
364
  pagePath = "/" + file.relativePath.replace(/\.html$/, "");
363
365
  }
364
366
 
367
+ if (seenPaths.has(pagePath)) {
368
+ info(`Skipping duplicate route: ${cyan(file.relativePath)} (conflicts with existing ${pagePath})`);
369
+ continue;
370
+ }
371
+ seenPaths.add(pagePath);
372
+
365
373
  pagePayloads.push({ path: pagePath, html });
366
374
  ok(`${cyan(file.relativePath)} → ${cyan(pagePath)}`);
367
375
  }
@@ -377,11 +385,18 @@ async function deployDirectory(dirPath, { ttl, title, slug: explicitSlug, forceN
377
385
  slug: existingSlug || undefined,
378
386
  isNewSite: !existingSlug,
379
387
  html: pagePayloads[0]?.html || "",
380
- assets: assetFiles.map(f => ({
381
- path: f.relativePath,
382
- sizeBytes: f.size,
383
- mimeType: mimeFromExt(basename(f.filePath))
384
- }))
388
+ assets: assetFiles.map(f => {
389
+ const buffer = readFileSync(f.filePath);
390
+ const hash = createHash('sha256').update(buffer).digest('hex');
391
+ // Store buffer on file object to avoid reading again
392
+ f.buffer = buffer;
393
+ return {
394
+ path: f.relativePath,
395
+ sizeBytes: f.size,
396
+ mimeType: mimeFromExt(basename(f.filePath)),
397
+ hash
398
+ };
399
+ })
385
400
  };
386
401
 
387
402
  let urlData;
@@ -397,29 +412,37 @@ async function deployDirectory(dirPath, { ttl, title, slug: explicitSlug, forceN
397
412
  console.log("");
398
413
  info(`Uploading ${cyan(bold(String(assetFiles.length)))} asset${assetFiles.length > 1 ? "s" : ""}…`);
399
414
 
415
+ let uploadedCount = 0;
416
+ let skippedCount = 0;
417
+
400
418
  for (const u of uploadUrls) {
401
419
  const file = assetFiles.find(f => f.relativePath === u.path);
402
- const buffer = readFileSync(file.filePath);
403
-
404
- const res = await fetch(u.uploadUrl, {
405
- method: "PUT",
406
- headers: { "Content-Type": u.mimeType },
407
- body: buffer
408
- });
409
420
 
410
- if (!res.ok) {
411
- err(`Failed to upload ${u.path}`);
412
- process.exit(1);
421
+ if (u.uploadUrl) {
422
+ const res = await fetch(u.uploadUrl, {
423
+ method: "PUT",
424
+ headers: { "Content-Type": u.mimeType },
425
+ body: file.buffer
426
+ });
427
+
428
+ if (!res.ok) {
429
+ err(`Failed to upload ${u.path}`);
430
+ process.exit(1);
431
+ }
432
+ ok(`✓ Uploaded ${cyan(u.path)} ${dim(`(${formatBytes(file.size)})`)}`);
433
+ uploadedCount++;
434
+ } else {
435
+ ok(`● Skipped (unchanged) ${cyan(u.path)} ${dim(`(${formatBytes(file.size)})`)}`);
436
+ skippedCount++;
413
437
  }
414
438
 
415
439
  assetPayloads.push({
416
440
  path: u.path,
417
441
  mimeType: u.mimeType,
418
442
  sizeBytes: u.sizeBytes,
419
- storageKey: u.storageKey
443
+ storageKey: u.storageKey,
444
+ hash: u.hash
420
445
  });
421
-
422
- ok(`✓ Uploaded ${cyan(u.path)} ${dim(`(${formatBytes(file.size)})`)}`);
423
446
  }
424
447
 
425
448
  if (!existingSlug) {