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
|
-
|
|
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 '${
|
|
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
|
|
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 (
|
|
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 (
|
|
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:
|
|
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
|
-
|
|
33539
|
-
|
|
33540
|
-
|
|
33541
|
-
|
|
33542
|
-
|
|
33543
|
-
|
|
33544
|
-
|
|
33545
|
-
|
|
33546
|
-
|
|
33547
|
-
|
|
33548
|
-
|
|
33549
|
-
|
|
33550
|
-
|
|
33551
|
-
|
|
33552
|
-
|
|
33553
|
-
|
|
33554
|
-
|
|
33555
|
-
|
|
33556
|
-
|
|
33557
|
-
|
|
33558
|
-
|
|
33559
|
-
|
|
33560
|
-
|
|
33561
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
|
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