bunki 0.7.0 → 0.7.1

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/dist/cli.js CHANGED
@@ -1596,7 +1596,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1596
1596
  return arg.length > 1 && arg[0] === "-";
1597
1597
  }
1598
1598
  const negativeNumberArg = (arg) => {
1599
- if (!/^-\d*\.?\d+(e[+-]?\d+)?$/.test(arg))
1599
+ if (!/^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/.test(arg))
1600
1600
  return false;
1601
1601
  return !this._getCommandAndAncestors().some((cmd) => cmd.options.map((opt) => opt.short).some((short) => /^-\d$/.test(short)));
1602
1602
  };
@@ -33602,6 +33602,28 @@ class S3Uploader {
33602
33602
  }
33603
33603
  }
33604
33604
  }
33605
+ async executeWithConcurrency(tasks, concurrency) {
33606
+ const results = [];
33607
+ const executing = [];
33608
+ for (const task of tasks) {
33609
+ const promise = task().then((result) => {
33610
+ results.push(result);
33611
+ const index = executing.indexOf(promise);
33612
+ if (index > -1)
33613
+ executing.splice(index, 1);
33614
+ }).catch((error) => {
33615
+ const index = executing.indexOf(promise);
33616
+ if (index > -1)
33617
+ executing.splice(index, 1);
33618
+ });
33619
+ executing.push(promise);
33620
+ if (executing.length >= concurrency) {
33621
+ await Promise.race(executing);
33622
+ }
33623
+ }
33624
+ await Promise.all(executing);
33625
+ return results;
33626
+ }
33605
33627
  async uploadImages(imagesDir, minYear) {
33606
33628
  console.log(`[S3] Uploading all images from ${imagesDir} to bucket ${this.s3Config.bucket}...`);
33607
33629
  if (minYear) {
@@ -33609,59 +33631,30 @@ class S3Uploader {
33609
33631
  }
33610
33632
  const imageUrls = {};
33611
33633
  try {
33612
- console.log(`[S3] Checking if directory exists: ${imagesDir}`);
33613
- try {
33614
- const glob2 = new Bun.Glob("**/*");
33615
- let hasContent = false;
33616
- for await (const file of glob2.scan({
33617
- cwd: imagesDir,
33618
- absolute: false
33619
- })) {
33620
- hasContent = true;
33621
- break;
33622
- }
33623
- if (!hasContent) {
33624
- console.warn(`Directory exists but is empty: ${imagesDir}`);
33625
- }
33626
- console.log(`[S3] Directory exists and is accessible`);
33627
- } catch (err) {
33628
- const errorMessage = err instanceof Error ? err.message : String(err);
33629
- console.warn(`No images directory found at ${imagesDir}, skipping image upload. Error: ${errorMessage}`);
33630
- return imageUrls;
33631
- }
33632
33634
  const glob = new Bun.Glob("**/*.{jpg,jpeg,png,gif,webp,svg}");
33633
33635
  const files = [];
33634
33636
  console.log(`[S3] Scanning directory ${imagesDir} for image files...`);
33635
33637
  try {
33636
- const dirGlob = new Bun.Glob("**/*");
33637
- const allFiles = [];
33638
- for await (const file of dirGlob.scan({
33638
+ for await (const file of glob.scan({
33639
33639
  cwd: imagesDir,
33640
33640
  absolute: false
33641
33641
  })) {
33642
- allFiles.push(file);
33643
- }
33644
- console.log(`[S3] Files in directory (including subdirs): ${allFiles.length > 0 ? allFiles.slice(0, 10).join(", ") + (allFiles.length > 10 ? "..." : "") : "none"}`);
33645
- } catch (err) {
33646
- console.error(`[S3] Error reading directory:`, err);
33647
- }
33648
- for await (const file of glob.scan({
33649
- cwd: imagesDir,
33650
- absolute: false
33651
- })) {
33652
- if (minYear) {
33653
- const yearMatch = file.match(/^(\d{4})\//);
33654
- if (yearMatch) {
33655
- const fileYear = parseInt(yearMatch[1], 10);
33656
- if (fileYear >= minYear) {
33657
- console.log(`[S3] Found image file: ${file}`);
33658
- files.push(file);
33642
+ if (minYear) {
33643
+ const yearMatch = file.match(/^(\d{4})\//);
33644
+ if (yearMatch) {
33645
+ const fileYear = parseInt(yearMatch[1], 10);
33646
+ if (fileYear >= minYear) {
33647
+ files.push(file);
33648
+ }
33659
33649
  }
33650
+ } else {
33651
+ files.push(file);
33660
33652
  }
33661
- } else {
33662
- console.log(`[S3] Found image file: ${file}`);
33663
- files.push(file);
33664
33653
  }
33654
+ } catch (err) {
33655
+ const errorMessage = err instanceof Error ? err.message : String(err);
33656
+ console.warn(`Error scanning images directory: ${errorMessage}`);
33657
+ return imageUrls;
33665
33658
  }
33666
33659
  const imageFiles = files;
33667
33660
  if (imageFiles.length === 0) {
@@ -33669,27 +33662,35 @@ class S3Uploader {
33669
33662
  return imageUrls;
33670
33663
  }
33671
33664
  console.log(`Found ${imageFiles.length} images to upload`);
33672
- for (const imageFile of imageFiles) {
33665
+ console.log(`[S3] Processing with 10 concurrent uploads...`);
33666
+ const concurrencyLimit = 10;
33667
+ let uploadedCount = 0;
33668
+ let failedCount = 0;
33669
+ const uploadTasks = imageFiles.map((imageFile) => async () => {
33673
33670
  try {
33674
33671
  const imagePath = path7.join(imagesDir, imageFile);
33675
33672
  const filename = path7.basename(imagePath);
33676
- console.log(`[S3] Uploading image ${imagePath} to S3 bucket ${this.s3Config.bucket}/${imageFile}...`);
33677
33673
  const file = Bun.file(imagePath);
33678
33674
  const contentType = file.type;
33679
- if (process.env.BUNKI_DRY_RUN === "true") {
33680
- console.log(`[S3] Dry run: would upload ${imageFile} with content type ${contentType}`);
33681
- } else {
33675
+ if (process.env.BUNKI_DRY_RUN === "true") {} else {
33682
33676
  const s3File = this.client.file(imageFile);
33683
33677
  await s3File.write(file);
33684
33678
  }
33685
33679
  const imageUrl = this.getPublicUrl(imageFile);
33686
- console.log(`[S3] Image uploaded to ${imageUrl}`);
33687
33680
  imageUrls[imageFile] = imageUrl;
33681
+ uploadedCount++;
33682
+ if (uploadedCount % 10 === 0) {
33683
+ console.log(`[S3] Progress: ${uploadedCount}/${imageFiles.length} images uploaded`);
33684
+ }
33685
+ return { success: true, file: imageFile };
33688
33686
  } catch (error) {
33689
- console.error(`Error uploading ${imageFile}:`, error);
33687
+ failedCount++;
33688
+ console.error(`[S3] Error uploading ${imageFile}:`, error);
33689
+ return { success: false, file: imageFile };
33690
33690
  }
33691
- }
33692
- console.log(`[S3] Successfully uploaded ${Object.keys(imageUrls).length} of ${imageFiles.length} images`);
33691
+ });
33692
+ await this.executeWithConcurrency(uploadTasks, concurrencyLimit);
33693
+ console.log(`[S3] Upload complete: ${uploadedCount} succeeded, ${failedCount} failed out of ${imageFiles.length} images`);
33693
33694
  return imageUrls;
33694
33695
  } catch (error) {
33695
33696
  console.error(`Error uploading images:`, error);
package/dist/index.js CHANGED
@@ -31509,6 +31509,28 @@ class S3Uploader {
31509
31509
  }
31510
31510
  }
31511
31511
  }
31512
+ async executeWithConcurrency(tasks, concurrency) {
31513
+ const results = [];
31514
+ const executing = [];
31515
+ for (const task of tasks) {
31516
+ const promise = task().then((result) => {
31517
+ results.push(result);
31518
+ const index = executing.indexOf(promise);
31519
+ if (index > -1)
31520
+ executing.splice(index, 1);
31521
+ }).catch((error) => {
31522
+ const index = executing.indexOf(promise);
31523
+ if (index > -1)
31524
+ executing.splice(index, 1);
31525
+ });
31526
+ executing.push(promise);
31527
+ if (executing.length >= concurrency) {
31528
+ await Promise.race(executing);
31529
+ }
31530
+ }
31531
+ await Promise.all(executing);
31532
+ return results;
31533
+ }
31512
31534
  async uploadImages(imagesDir, minYear) {
31513
31535
  console.log(`[S3] Uploading all images from ${imagesDir} to bucket ${this.s3Config.bucket}...`);
31514
31536
  if (minYear) {
@@ -31516,59 +31538,30 @@ class S3Uploader {
31516
31538
  }
31517
31539
  const imageUrls = {};
31518
31540
  try {
31519
- console.log(`[S3] Checking if directory exists: ${imagesDir}`);
31520
- try {
31521
- const glob2 = new Bun.Glob("**/*");
31522
- let hasContent = false;
31523
- for await (const file of glob2.scan({
31524
- cwd: imagesDir,
31525
- absolute: false
31526
- })) {
31527
- hasContent = true;
31528
- break;
31529
- }
31530
- if (!hasContent) {
31531
- console.warn(`Directory exists but is empty: ${imagesDir}`);
31532
- }
31533
- console.log(`[S3] Directory exists and is accessible`);
31534
- } catch (err) {
31535
- const errorMessage = err instanceof Error ? err.message : String(err);
31536
- console.warn(`No images directory found at ${imagesDir}, skipping image upload. Error: ${errorMessage}`);
31537
- return imageUrls;
31538
- }
31539
31541
  const glob = new Bun.Glob("**/*.{jpg,jpeg,png,gif,webp,svg}");
31540
31542
  const files = [];
31541
31543
  console.log(`[S3] Scanning directory ${imagesDir} for image files...`);
31542
31544
  try {
31543
- const dirGlob = new Bun.Glob("**/*");
31544
- const allFiles = [];
31545
- for await (const file of dirGlob.scan({
31545
+ for await (const file of glob.scan({
31546
31546
  cwd: imagesDir,
31547
31547
  absolute: false
31548
31548
  })) {
31549
- allFiles.push(file);
31550
- }
31551
- console.log(`[S3] Files in directory (including subdirs): ${allFiles.length > 0 ? allFiles.slice(0, 10).join(", ") + (allFiles.length > 10 ? "..." : "") : "none"}`);
31552
- } catch (err) {
31553
- console.error(`[S3] Error reading directory:`, err);
31554
- }
31555
- for await (const file of glob.scan({
31556
- cwd: imagesDir,
31557
- absolute: false
31558
- })) {
31559
- if (minYear) {
31560
- const yearMatch = file.match(/^(\d{4})\//);
31561
- if (yearMatch) {
31562
- const fileYear = parseInt(yearMatch[1], 10);
31563
- if (fileYear >= minYear) {
31564
- console.log(`[S3] Found image file: ${file}`);
31565
- files.push(file);
31549
+ if (minYear) {
31550
+ const yearMatch = file.match(/^(\d{4})\//);
31551
+ if (yearMatch) {
31552
+ const fileYear = parseInt(yearMatch[1], 10);
31553
+ if (fileYear >= minYear) {
31554
+ files.push(file);
31555
+ }
31566
31556
  }
31557
+ } else {
31558
+ files.push(file);
31567
31559
  }
31568
- } else {
31569
- console.log(`[S3] Found image file: ${file}`);
31570
- files.push(file);
31571
31560
  }
31561
+ } catch (err) {
31562
+ const errorMessage = err instanceof Error ? err.message : String(err);
31563
+ console.warn(`Error scanning images directory: ${errorMessage}`);
31564
+ return imageUrls;
31572
31565
  }
31573
31566
  const imageFiles = files;
31574
31567
  if (imageFiles.length === 0) {
@@ -31576,27 +31569,35 @@ class S3Uploader {
31576
31569
  return imageUrls;
31577
31570
  }
31578
31571
  console.log(`Found ${imageFiles.length} images to upload`);
31579
- for (const imageFile of imageFiles) {
31572
+ console.log(`[S3] Processing with 10 concurrent uploads...`);
31573
+ const concurrencyLimit = 10;
31574
+ let uploadedCount = 0;
31575
+ let failedCount = 0;
31576
+ const uploadTasks = imageFiles.map((imageFile) => async () => {
31580
31577
  try {
31581
31578
  const imagePath = path6.join(imagesDir, imageFile);
31582
31579
  const filename = path6.basename(imagePath);
31583
- console.log(`[S3] Uploading image ${imagePath} to S3 bucket ${this.s3Config.bucket}/${imageFile}...`);
31584
31580
  const file = Bun.file(imagePath);
31585
31581
  const contentType = file.type;
31586
- if (process.env.BUNKI_DRY_RUN === "true") {
31587
- console.log(`[S3] Dry run: would upload ${imageFile} with content type ${contentType}`);
31588
- } else {
31582
+ if (process.env.BUNKI_DRY_RUN === "true") {} else {
31589
31583
  const s3File = this.client.file(imageFile);
31590
31584
  await s3File.write(file);
31591
31585
  }
31592
31586
  const imageUrl = this.getPublicUrl(imageFile);
31593
- console.log(`[S3] Image uploaded to ${imageUrl}`);
31594
31587
  imageUrls[imageFile] = imageUrl;
31588
+ uploadedCount++;
31589
+ if (uploadedCount % 10 === 0) {
31590
+ console.log(`[S3] Progress: ${uploadedCount}/${imageFiles.length} images uploaded`);
31591
+ }
31592
+ return { success: true, file: imageFile };
31595
31593
  } catch (error) {
31596
- console.error(`Error uploading ${imageFile}:`, error);
31594
+ failedCount++;
31595
+ console.error(`[S3] Error uploading ${imageFile}:`, error);
31596
+ return { success: false, file: imageFile };
31597
31597
  }
31598
- }
31599
- console.log(`[S3] Successfully uploaded ${Object.keys(imageUrls).length} of ${imageFiles.length} images`);
31598
+ });
31599
+ await this.executeWithConcurrency(uploadTasks, concurrencyLimit);
31600
+ console.log(`[S3] Upload complete: ${uploadedCount} succeeded, ${failedCount} failed out of ${imageFiles.length} images`);
31600
31601
  return imageUrls;
31601
31602
  } catch (error) {
31602
31603
  console.error(`Error uploading images:`, error);
@@ -13,6 +13,12 @@ export declare class S3Uploader implements Uploader, ImageUploader {
13
13
  * @returns The public URL for the file
14
14
  */
15
15
  private getPublicUrl;
16
+ /**
17
+ * Execute async tasks with concurrency limit
18
+ * @param tasks Array of task functions that return promises
19
+ * @param concurrency Maximum number of concurrent tasks
20
+ */
21
+ private executeWithConcurrency;
16
22
  uploadImages(imagesDir: string, minYear?: number): Promise<Record<string, string>>;
17
23
  }
18
24
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunki",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "An opinionated static site generator built with Bun featuring PostCSS integration and modern web development workflows",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "homepage": "https://github.com/kahwee/bunki#readme",
49
49
  "dependencies": {
50
- "commander": "^14.0.1",
50
+ "commander": "^14.0.2",
51
51
  "gray-matter": "^4.0.3",
52
52
  "highlight.js": "^11.11.1",
53
53
  "marked": "^16.4.1",
@@ -59,15 +59,15 @@
59
59
  "slugify": "^1.6.6"
60
60
  },
61
61
  "devDependencies": {
62
- "@tailwindcss/postcss": "^4.1.15",
62
+ "@tailwindcss/postcss": "^4.1.16",
63
63
  "@types/nunjucks": "^3.2.6",
64
64
  "@types/sanitize-html": "^2.16.0",
65
- "autoprefixer": "^10.4.20",
66
- "bun-types": "^1.3.0",
65
+ "autoprefixer": "^10.4.21",
66
+ "bun-types": "^1.3.1",
67
67
  "husky": "^9.1.7",
68
- "lint-staged": "^16.2.5",
68
+ "lint-staged": "^16.2.6",
69
69
  "prettier": "^3.6.2",
70
- "tailwindcss": "^4.1.15",
70
+ "tailwindcss": "^4.1.16",
71
71
  "typescript": "^5.9.3"
72
72
  },
73
73
  "peerDependencies": {