bunki 0.19.0 → 0.19.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
@@ -33017,6 +33017,7 @@ var import_sanitize_html = __toESM(require_sanitize_html(), 1);
33017
33017
 
33018
33018
  // src/utils/markdown/constants.ts
33019
33019
  var RELATIVE_LINK_REGEX = /^(\.\.\/)+(\d{4})\/([a-zA-Z0-9_-]+?)(?:\.md)?(?:\/)?(#[^#]*)?$/;
33020
+ var SAME_DIR_LINK_REGEX = /^\.\/([a-zA-Z0-9_-]+?)(?:\.md)?(?:\/)?(#[^#]*)?$/;
33020
33021
  var IMAGE_PATH_REGEX = /^\.\.\/\.\.\/assets\/(\d{4})\/([^/]+)\/(.+)$/;
33021
33022
  var IMAGE_PATH_ASSETS_DIR = /^\.\.\/\_assets\/(.+)$/;
33022
33023
  var IMAGE_PATH_ASSETS_SAME_DIR = /^\.\/\_assets\/(.+)$/;
@@ -33127,6 +33128,11 @@ function createMarked(cdnConfig) {
33127
33128
  const [, , year, slug, anchor = ""] = relativeMatch;
33128
33129
  token.href = `/${year}/${slug}/${anchor}`;
33129
33130
  }
33131
+ const sameDirMatch = token.href.match(SAME_DIR_LINK_REGEX);
33132
+ if (sameDirMatch && cdnConfig?.postYear) {
33133
+ const [, slug, anchor = ""] = sameDirMatch;
33134
+ token.href = `/${cdnConfig.postYear}/${slug}/${anchor}`;
33135
+ }
33130
33136
  const isExternal = token.href && (token.href.startsWith("http://") || token.href.startsWith("https://") || token.href.startsWith("//"));
33131
33137
  if (isExternal) {
33132
33138
  token.isExternalLink = true;
@@ -33272,12 +33278,13 @@ function validateBusinessLocation(business, filePath) {
33272
33278
  suggestion: "Add 'type: Restaurant' (or Market, Park, Hotel, Museum, Cafe, Zoo, etc.) to frontmatter"
33273
33279
  };
33274
33280
  }
33275
- if (!SCHEMA_ORG_PLACE_TYPES.has(loc.type)) {
33281
+ const locType = String(loc.type);
33282
+ if (!SCHEMA_ORG_PLACE_TYPES.has(locType)) {
33276
33283
  const exampleTypes = Array.from(SCHEMA_ORG_PLACE_TYPES).slice(0, 10);
33277
33284
  return {
33278
33285
  file: filePath,
33279
33286
  type: "validation",
33280
- message: `Invalid business type '${loc.type}' in business${locIndex}`,
33287
+ message: `Invalid business type '${locType}' in business${locIndex}`,
33281
33288
  suggestion: `Use a valid Schema.org Place type: ${exampleTypes.join(", ")}, etc.`
33282
33289
  };
33283
33290
  }
@@ -33435,12 +33442,14 @@ async function parseMarkdownFile(filePath, cdnConfig) {
33435
33442
  };
33436
33443
  return { post, error: null };
33437
33444
  } catch (error) {
33438
- const isYamlError = error?.name === "YAMLException" || error?.message?.includes("YAML") || error?.message?.includes("mapping pair");
33445
+ const msg = error instanceof Error ? error.message : String(error);
33446
+ const name = error instanceof Error ? error.name : "";
33447
+ const isYamlError = name === "YAMLException" || msg.includes("YAML") || msg.includes("mapping pair");
33439
33448
  let suggestion;
33440
33449
  if (isYamlError) {
33441
- if (error?.message?.includes("mapping pair") || error?.message?.includes("colon")) {
33450
+ if (msg.includes("mapping pair") || msg.includes("colon")) {
33442
33451
  suggestion = 'Quote titles/descriptions containing colons (e.g., title: "My Post: A Guide")';
33443
- } else if (error?.message?.includes("multiline key")) {
33452
+ } else if (msg.includes("multiline key")) {
33444
33453
  suggestion = "Remove nested quotes or use single quotes inside double quotes";
33445
33454
  }
33446
33455
  }
@@ -33449,7 +33458,7 @@ async function parseMarkdownFile(filePath, cdnConfig) {
33449
33458
  error: {
33450
33459
  file: filePath,
33451
33460
  type: isYamlError ? "yaml" : "unknown",
33452
- message: error?.message || String(error),
33461
+ message: msg,
33453
33462
  suggestion
33454
33463
  }
33455
33464
  };
@@ -33457,6 +33466,22 @@ async function parseMarkdownFile(filePath, cdnConfig) {
33457
33466
  }
33458
33467
 
33459
33468
  // src/parser.ts
33469
+ function logErrorGroup(label, errors, opts) {
33470
+ if (errors.length === 0)
33471
+ return;
33472
+ console.error(` ${label} (${errors.length}):`);
33473
+ errors.slice(0, opts.limit).forEach((e) => {
33474
+ const msg = opts.showMessage ? `: ${e.message}` : "";
33475
+ console.error(` ${opts.icon} ${e.file}${msg}`);
33476
+ if (opts.showSuggestion && e.suggestion) {
33477
+ console.error(` \uD83D\uDCA1 ${e.suggestion}`);
33478
+ }
33479
+ });
33480
+ if (errors.length > opts.limit) {
33481
+ console.error(` ... and ${errors.length - opts.limit} more`);
33482
+ }
33483
+ console.error("");
33484
+ }
33460
33485
  function detectFileConflicts(files) {
33461
33486
  const errors = [];
33462
33487
  const slugMap = new Map;
@@ -33535,52 +33560,30 @@ async function parseMarkdownDirectory(contentDir, strictMode = false, cdnConfig)
33535
33560
  const missingFieldErrors = errors.filter((e) => e.type === "missing_field");
33536
33561
  const validationErrors = errors.filter((e) => e.type === "validation");
33537
33562
  const otherErrors = errors.filter((e) => e.type !== "yaml" && e.type !== "missing_field" && e.type !== "validation");
33538
- if (yamlErrors.length > 0) {
33539
- console.error(` YAML Parsing Errors (${yamlErrors.length}):`);
33540
- yamlErrors.slice(0, 5).forEach((e) => {
33541
- console.error(` \u274C ${e.file}`);
33542
- if (e.suggestion) {
33543
- console.error(` \uD83D\uDCA1 ${e.suggestion}`);
33544
- }
33545
- });
33546
- if (yamlErrors.length > 5) {
33547
- console.error(` ... and ${yamlErrors.length - 5} more`);
33548
- }
33549
- console.error("");
33550
- }
33551
- if (missingFieldErrors.length > 0) {
33552
- console.error(` Missing Required Fields (${missingFieldErrors.length}):`);
33553
- missingFieldErrors.slice(0, 5).forEach((e) => {
33554
- console.error(` \u26A0\uFE0F ${e.file}: ${e.message}`);
33555
- });
33556
- if (missingFieldErrors.length > 5) {
33557
- console.error(` ... and ${missingFieldErrors.length - 5} more`);
33558
- }
33559
- console.error("");
33560
- }
33561
- if (validationErrors.length > 0) {
33562
- console.error(` Validation Errors (${validationErrors.length}):`);
33563
- validationErrors.slice(0, 5).forEach((e) => {
33564
- console.error(` \u26A0\uFE0F ${e.file}: ${e.message}`);
33565
- if (e.suggestion) {
33566
- console.error(` \uD83D\uDCA1 ${e.suggestion}`);
33567
- }
33568
- });
33569
- if (validationErrors.length > 5) {
33570
- console.error(` ... and ${validationErrors.length - 5} more`);
33571
- }
33572
- console.error("");
33573
- }
33574
- if (otherErrors.length > 0) {
33575
- console.error(` Other Errors (${otherErrors.length}):`);
33576
- otherErrors.slice(0, 3).forEach((e) => {
33577
- console.error(` \u274C ${e.file}: ${e.message}`);
33578
- });
33579
- if (otherErrors.length > 3) {
33580
- console.error(` ... and ${otherErrors.length - 3} more`);
33581
- }
33582
- console.error("");
33583
- }
33563
+ logErrorGroup("YAML Parsing Errors", yamlErrors, {
33564
+ icon: "\u274C",
33565
+ showMessage: false,
33566
+ showSuggestion: true,
33567
+ limit: 5
33568
+ });
33569
+ logErrorGroup("Missing Required Fields", missingFieldErrors, {
33570
+ icon: "\u26A0\uFE0F ",
33571
+ showMessage: true,
33572
+ showSuggestion: false,
33573
+ limit: 5
33574
+ });
33575
+ logErrorGroup("Validation Errors", validationErrors, {
33576
+ icon: "\u26A0\uFE0F ",
33577
+ showMessage: true,
33578
+ showSuggestion: true,
33579
+ limit: 5
33580
+ });
33581
+ logErrorGroup("Other Errors", otherErrors, {
33582
+ icon: "\u274C",
33583
+ showMessage: true,
33584
+ showSuggestion: false,
33585
+ limit: 3
33586
+ });
33584
33587
  console.error(`\uD83D\uDCDD Tip: Fix YAML errors by quoting titles/descriptions with colons`);
33585
33588
  console.error(` Example: title: "My Post: A Guide" (quotes required for colons)
33586
33589
  `);
@@ -34168,6 +34171,19 @@ async function writeHtmlFile(outputDir, relativePath, content) {
34168
34171
  await ensureDir(dir);
34169
34172
  await Bun.write(fullPath, content);
34170
34173
  }
34174
+ async function generateOptionalPage(templateName, context, outputDir, outputPath, label) {
34175
+ try {
34176
+ const html = import_nunjucks.default.render(templateName, context);
34177
+ await writeHtmlFile(outputDir, outputPath, html);
34178
+ console.log(`Generated ${label}`);
34179
+ } catch (error) {
34180
+ if (error instanceof Error && error.message.includes(templateName)) {
34181
+ console.log(`No ${templateName} template found, skipping ${label}`);
34182
+ } else {
34183
+ console.warn(`Error generating ${label}:`, error);
34184
+ }
34185
+ }
34186
+ }
34171
34187
  async function generateIndexPages(site, config, outputDir, pageSize = PAGINATION.DEFAULT_PAGE_SIZE) {
34172
34188
  const totalPages = getTotalPages(site.posts.length, pageSize);
34173
34189
  for (let page = 1;page <= totalPages; page++) {
@@ -34276,35 +34292,10 @@ async function generateYearArchives(site, config, outputDir, pageSize = PAGINATI
34276
34292
  }
34277
34293
  }
34278
34294
  async function generate404Page(config, outputDir) {
34279
- try {
34280
- const notFoundHtml = import_nunjucks.default.render("404.njk", {
34281
- site: config
34282
- });
34283
- await writeHtmlFile(outputDir, "404.html", notFoundHtml);
34284
- console.log("Generated 404.html");
34285
- } catch (error) {
34286
- if (error instanceof Error && error.message.includes("404.njk")) {
34287
- console.log("No 404.njk template found, skipping 404 page generation");
34288
- } else {
34289
- console.warn("Error generating 404 page:", error);
34290
- }
34291
- }
34295
+ await generateOptionalPage("404.njk", { site: config }, outputDir, "404.html", "404.html");
34292
34296
  }
34293
34297
  async function generateMapPage(site, config, outputDir) {
34294
- try {
34295
- const mapHtml = import_nunjucks.default.render("map.njk", {
34296
- site: config,
34297
- posts: site.posts
34298
- });
34299
- await writeHtmlFile(outputDir, "map/index.html", mapHtml);
34300
- console.log("Generated map page");
34301
- } catch (error) {
34302
- if (error instanceof Error && error.message.includes("map.njk")) {
34303
- console.log("No map.njk template found, skipping map page generation");
34304
- } else {
34305
- console.warn("Error generating map page:", error);
34306
- }
34307
- }
34298
+ await generateOptionalPage("map.njk", { site: config, posts: site.posts }, outputDir, "map/index.html", "map page");
34308
34299
  }
34309
34300
 
34310
34301
  // src/generators/assets.ts
@@ -35722,18 +35713,9 @@ function registerNewCommand(program2) {
35722
35713
  import path17 from "path";
35723
35714
 
35724
35715
  // src/server.ts
35725
- import fs2 from "fs";
35726
35716
  import path16 from "path";
35727
35717
  async function startServer(outputDir = DEFAULT_OUTPUT_DIR, port = 3000) {
35728
- try {
35729
- const stats = await fs2.promises.stat(outputDir);
35730
- if (!stats.isDirectory()) {
35731
- const msg = `Error: Output directory ${outputDir} does not exist or is not accessible.`;
35732
- console.error(msg);
35733
- console.log('Try running "bunki generate" first to build your site.');
35734
- throw new Error(msg);
35735
- }
35736
- } catch (error) {
35718
+ if (!await isDirectory(outputDir)) {
35737
35719
  const msg = `Error: Output directory ${outputDir} does not exist or is not accessible.`;
35738
35720
  console.error(msg);
35739
35721
  console.log('Try running "bunki generate" first to build your site.');
@@ -3,6 +3,7 @@
3
3
  * Extracted for better code organization and performance
4
4
  */
5
5
  export declare const RELATIVE_LINK_REGEX: RegExp;
6
+ export declare const SAME_DIR_LINK_REGEX: RegExp;
6
7
  export declare const IMAGE_PATH_REGEX: RegExp;
7
8
  export declare const IMAGE_PATH_ASSETS_DIR: RegExp;
8
9
  export declare const IMAGE_PATH_ASSETS_SAME_DIR: RegExp;
@@ -14,7 +14,7 @@ export interface ValidationError {
14
14
  * @param filePath - File path for error reporting
15
15
  * @returns ValidationError if invalid, null if valid
16
16
  */
17
- export declare function validateBusinessLocation(business: any, filePath: string): ValidationError | null;
17
+ export declare function validateBusinessLocation(business: unknown, filePath: string): ValidationError | null;
18
18
  /**
19
19
  * Validate that tags don't contain spaces (must use hyphens)
20
20
  * @param tags - Array of tag strings
@@ -28,4 +28,4 @@ export declare function validateTags(tags: string[], filePath: string): Validati
28
28
  * @param filePath - File path for error reporting
29
29
  * @returns ValidationError if found, null otherwise
30
30
  */
31
- export declare function checkDeprecatedLocationField(data: any, filePath: string): ValidationError | null;
31
+ export declare function checkDeprecatedLocationField(data: Record<string, unknown>, filePath: string): ValidationError | null;
@@ -20,7 +20,7 @@ export interface PaginationData {
20
20
  * @param pagePath - Base path for pagination (e.g., "/", "/tags/tech/")
21
21
  * @returns Pagination data object
22
22
  */
23
- export declare function createPagination<T>(items: T[], currentPage: number, pageSize: number, pagePath: string): PaginationData;
23
+ export declare function createPagination(items: readonly unknown[], currentPage: number, pageSize: number, pagePath: string): PaginationData;
24
24
  /**
25
25
  * Get paginated slice of items for a specific page
26
26
  * @param items - Array of items to paginate
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunki",
3
- "version": "0.19.0",
3
+ "version": "0.19.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",