bunki 0.18.6 → 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/README.md +83 -1
- package/dist/cli.js +474 -335
- package/dist/fragments/json-ld.njk +59 -0
- package/dist/fragments/og-image.njk +21 -0
- package/dist/fragments/pagination.njk +12 -0
- package/dist/fragments/share-buttons.njk +21 -0
- package/dist/index.js +0 -32874
- package/dist/utils/markdown/constants.d.ts +1 -0
- package/dist/utils/markdown/validators.d.ts +2 -2
- package/dist/utils/pagination.d.ts +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5,15 +5,29 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
function __accessProp(key) {
|
|
9
|
+
return this[key];
|
|
10
|
+
}
|
|
11
|
+
var __toESMCache_node;
|
|
12
|
+
var __toESMCache_esm;
|
|
8
13
|
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
+
var canCache = mod != null && typeof mod === "object";
|
|
15
|
+
if (canCache) {
|
|
16
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
+
var cached = cache.get(mod);
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
9
21
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
22
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
23
|
for (let key of __getOwnPropNames(mod))
|
|
12
24
|
if (!__hasOwnProp.call(to, key))
|
|
13
25
|
__defProp(to, key, {
|
|
14
|
-
get: (
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
15
27
|
enumerable: true
|
|
16
28
|
});
|
|
29
|
+
if (canCache)
|
|
30
|
+
cache.set(mod, to);
|
|
17
31
|
return to;
|
|
18
32
|
};
|
|
19
33
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
@@ -28555,11 +28569,11 @@ function registerCssCommand(program2) {
|
|
|
28555
28569
|
}
|
|
28556
28570
|
|
|
28557
28571
|
// src/cli/commands/generate.ts
|
|
28558
|
-
import
|
|
28572
|
+
import path11 from "path";
|
|
28559
28573
|
|
|
28560
28574
|
// src/site-generator.ts
|
|
28561
28575
|
var import_slugify = __toESM(require_slugify(), 1);
|
|
28562
|
-
import
|
|
28576
|
+
import path10 from "path";
|
|
28563
28577
|
|
|
28564
28578
|
// src/parser.ts
|
|
28565
28579
|
import path5 from "path";
|
|
@@ -33003,6 +33017,7 @@ var import_sanitize_html = __toESM(require_sanitize_html(), 1);
|
|
|
33003
33017
|
|
|
33004
33018
|
// src/utils/markdown/constants.ts
|
|
33005
33019
|
var RELATIVE_LINK_REGEX = /^(\.\.\/)+(\d{4})\/([a-zA-Z0-9_-]+?)(?:\.md)?(?:\/)?(#[^#]*)?$/;
|
|
33020
|
+
var SAME_DIR_LINK_REGEX = /^\.\/([a-zA-Z0-9_-]+?)(?:\.md)?(?:\/)?(#[^#]*)?$/;
|
|
33006
33021
|
var IMAGE_PATH_REGEX = /^\.\.\/\.\.\/assets\/(\d{4})\/([^/]+)\/(.+)$/;
|
|
33007
33022
|
var IMAGE_PATH_ASSETS_DIR = /^\.\.\/\_assets\/(.+)$/;
|
|
33008
33023
|
var IMAGE_PATH_ASSETS_SAME_DIR = /^\.\/\_assets\/(.+)$/;
|
|
@@ -33113,6 +33128,11 @@ function createMarked(cdnConfig) {
|
|
|
33113
33128
|
const [, , year, slug, anchor = ""] = relativeMatch;
|
|
33114
33129
|
token.href = `/${year}/${slug}/${anchor}`;
|
|
33115
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
|
+
}
|
|
33116
33136
|
const isExternal = token.href && (token.href.startsWith("http://") || token.href.startsWith("https://") || token.href.startsWith("//"));
|
|
33117
33137
|
if (isExternal) {
|
|
33118
33138
|
token.isExternalLink = true;
|
|
@@ -33136,6 +33156,12 @@ function createMarked(cdnConfig) {
|
|
|
33136
33156
|
return markdown2;
|
|
33137
33157
|
},
|
|
33138
33158
|
postprocess(html) {
|
|
33159
|
+
if (cdnConfig?.enabled && cdnConfig.postYear) {
|
|
33160
|
+
const year = cdnConfig.postYear;
|
|
33161
|
+
const base = cdnConfig.baseUrl;
|
|
33162
|
+
html = html.replace(/src=(["'])\.\/\_assets\/([^\s"']+)\1/g, (_m, q2, filename) => `src=${q2}${base}/${year}/${filename}${q2}`);
|
|
33163
|
+
html = html.replace(/!\[([^\]]*)\]\(\.\/\_assets\/([^\s)]+)\)/g, (_m, alt, filename) => `<img src="${base}/${year}/${filename}" alt="${alt}" loading="lazy">`);
|
|
33164
|
+
}
|
|
33139
33165
|
html = html.replace(YOUTUBE_EMBED_REGEX, '<div class="video-container"><iframe src="https://www.youtube.com/embed/$4" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy"></iframe></div>');
|
|
33140
33166
|
html = html.replace(/<img /g, '<img loading="lazy" ');
|
|
33141
33167
|
return html.replace(EXTERNAL_LINK_REGEX, (match, protocol, rest) => {
|
|
@@ -33252,12 +33278,13 @@ function validateBusinessLocation(business, filePath) {
|
|
|
33252
33278
|
suggestion: "Add 'type: Restaurant' (or Market, Park, Hotel, Museum, Cafe, Zoo, etc.) to frontmatter"
|
|
33253
33279
|
};
|
|
33254
33280
|
}
|
|
33255
|
-
|
|
33281
|
+
const locType = String(loc.type);
|
|
33282
|
+
if (!SCHEMA_ORG_PLACE_TYPES.has(locType)) {
|
|
33256
33283
|
const exampleTypes = Array.from(SCHEMA_ORG_PLACE_TYPES).slice(0, 10);
|
|
33257
33284
|
return {
|
|
33258
33285
|
file: filePath,
|
|
33259
33286
|
type: "validation",
|
|
33260
|
-
message: `Invalid business type '${
|
|
33287
|
+
message: `Invalid business type '${locType}' in business${locIndex}`,
|
|
33261
33288
|
suggestion: `Use a valid Schema.org Place type: ${exampleTypes.join(", ")}, etc.`
|
|
33262
33289
|
};
|
|
33263
33290
|
}
|
|
@@ -33374,7 +33401,9 @@ async function parseMarkdownFile(filePath, cdnConfig) {
|
|
|
33374
33401
|
const slug = getBaseFilename(filePath);
|
|
33375
33402
|
const pacificDate = toPacificTime(data.date);
|
|
33376
33403
|
const postYear = getPacificYear(data.date);
|
|
33377
|
-
const
|
|
33404
|
+
const yearFromPath = filePath.match(/\/(\d{4})\//)?.[1];
|
|
33405
|
+
const resolvedYear = String(postYear) !== "NaN" ? String(postYear) : yearFromPath;
|
|
33406
|
+
const cdnConfigWithYear = cdnConfig && resolvedYear ? { ...cdnConfig, postYear: resolvedYear } : undefined;
|
|
33378
33407
|
const sanitizedHtml = convertMarkdownToHtml(content, cdnConfigWithYear);
|
|
33379
33408
|
const post = {
|
|
33380
33409
|
title: data.title,
|
|
@@ -33413,12 +33442,14 @@ async function parseMarkdownFile(filePath, cdnConfig) {
|
|
|
33413
33442
|
};
|
|
33414
33443
|
return { post, error: null };
|
|
33415
33444
|
} catch (error) {
|
|
33416
|
-
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");
|
|
33417
33448
|
let suggestion;
|
|
33418
33449
|
if (isYamlError) {
|
|
33419
|
-
if (
|
|
33450
|
+
if (msg.includes("mapping pair") || msg.includes("colon")) {
|
|
33420
33451
|
suggestion = 'Quote titles/descriptions containing colons (e.g., title: "My Post: A Guide")';
|
|
33421
|
-
} else if (
|
|
33452
|
+
} else if (msg.includes("multiline key")) {
|
|
33422
33453
|
suggestion = "Remove nested quotes or use single quotes inside double quotes";
|
|
33423
33454
|
}
|
|
33424
33455
|
}
|
|
@@ -33427,7 +33458,7 @@ async function parseMarkdownFile(filePath, cdnConfig) {
|
|
|
33427
33458
|
error: {
|
|
33428
33459
|
file: filePath,
|
|
33429
33460
|
type: isYamlError ? "yaml" : "unknown",
|
|
33430
|
-
message:
|
|
33461
|
+
message: msg,
|
|
33431
33462
|
suggestion
|
|
33432
33463
|
}
|
|
33433
33464
|
};
|
|
@@ -33435,6 +33466,22 @@ async function parseMarkdownFile(filePath, cdnConfig) {
|
|
|
33435
33466
|
}
|
|
33436
33467
|
|
|
33437
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
|
+
}
|
|
33438
33485
|
function detectFileConflicts(files) {
|
|
33439
33486
|
const errors = [];
|
|
33440
33487
|
const slugMap = new Map;
|
|
@@ -33513,52 +33560,30 @@ async function parseMarkdownDirectory(contentDir, strictMode = false, cdnConfig)
|
|
|
33513
33560
|
const missingFieldErrors = errors.filter((e) => e.type === "missing_field");
|
|
33514
33561
|
const validationErrors = errors.filter((e) => e.type === "validation");
|
|
33515
33562
|
const otherErrors = errors.filter((e) => e.type !== "yaml" && e.type !== "missing_field" && e.type !== "validation");
|
|
33516
|
-
|
|
33517
|
-
|
|
33518
|
-
|
|
33519
|
-
|
|
33520
|
-
|
|
33521
|
-
|
|
33522
|
-
|
|
33523
|
-
|
|
33524
|
-
|
|
33525
|
-
|
|
33526
|
-
|
|
33527
|
-
|
|
33528
|
-
|
|
33529
|
-
|
|
33530
|
-
|
|
33531
|
-
|
|
33532
|
-
|
|
33533
|
-
|
|
33534
|
-
|
|
33535
|
-
|
|
33536
|
-
|
|
33537
|
-
|
|
33538
|
-
|
|
33539
|
-
|
|
33540
|
-
console.error(` Validation Errors (${validationErrors.length}):`);
|
|
33541
|
-
validationErrors.slice(0, 5).forEach((e) => {
|
|
33542
|
-
console.error(` \u26A0\uFE0F ${e.file}: ${e.message}`);
|
|
33543
|
-
if (e.suggestion) {
|
|
33544
|
-
console.error(` \uD83D\uDCA1 ${e.suggestion}`);
|
|
33545
|
-
}
|
|
33546
|
-
});
|
|
33547
|
-
if (validationErrors.length > 5) {
|
|
33548
|
-
console.error(` ... and ${validationErrors.length - 5} more`);
|
|
33549
|
-
}
|
|
33550
|
-
console.error("");
|
|
33551
|
-
}
|
|
33552
|
-
if (otherErrors.length > 0) {
|
|
33553
|
-
console.error(` Other Errors (${otherErrors.length}):`);
|
|
33554
|
-
otherErrors.slice(0, 3).forEach((e) => {
|
|
33555
|
-
console.error(` \u274C ${e.file}: ${e.message}`);
|
|
33556
|
-
});
|
|
33557
|
-
if (otherErrors.length > 3) {
|
|
33558
|
-
console.error(` ... and ${otherErrors.length - 3} more`);
|
|
33559
|
-
}
|
|
33560
|
-
console.error("");
|
|
33561
|
-
}
|
|
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
|
+
});
|
|
33562
33587
|
console.error(`\uD83D\uDCDD Tip: Fix YAML errors by quoting titles/descriptions with colons`);
|
|
33563
33588
|
console.error(` Example: title: "My Post: A Guide" (quotes required for colons)
|
|
33564
33589
|
`);
|
|
@@ -34146,6 +34171,19 @@ async function writeHtmlFile(outputDir, relativePath, content) {
|
|
|
34146
34171
|
await ensureDir(dir);
|
|
34147
34172
|
await Bun.write(fullPath, content);
|
|
34148
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
|
+
}
|
|
34149
34187
|
async function generateIndexPages(site, config, outputDir, pageSize = PAGINATION.DEFAULT_PAGE_SIZE) {
|
|
34150
34188
|
const totalPages = getTotalPages(site.posts.length, pageSize);
|
|
34151
34189
|
for (let page = 1;page <= totalPages; page++) {
|
|
@@ -34254,35 +34292,10 @@ async function generateYearArchives(site, config, outputDir, pageSize = PAGINATI
|
|
|
34254
34292
|
}
|
|
34255
34293
|
}
|
|
34256
34294
|
async function generate404Page(config, outputDir) {
|
|
34257
|
-
|
|
34258
|
-
const notFoundHtml = import_nunjucks.default.render("404.njk", {
|
|
34259
|
-
site: config
|
|
34260
|
-
});
|
|
34261
|
-
await writeHtmlFile(outputDir, "404.html", notFoundHtml);
|
|
34262
|
-
console.log("Generated 404.html");
|
|
34263
|
-
} catch (error) {
|
|
34264
|
-
if (error instanceof Error && error.message.includes("404.njk")) {
|
|
34265
|
-
console.log("No 404.njk template found, skipping 404 page generation");
|
|
34266
|
-
} else {
|
|
34267
|
-
console.warn("Error generating 404 page:", error);
|
|
34268
|
-
}
|
|
34269
|
-
}
|
|
34295
|
+
await generateOptionalPage("404.njk", { site: config }, outputDir, "404.html", "404.html");
|
|
34270
34296
|
}
|
|
34271
34297
|
async function generateMapPage(site, config, outputDir) {
|
|
34272
|
-
|
|
34273
|
-
const mapHtml = import_nunjucks.default.render("map.njk", {
|
|
34274
|
-
site: config,
|
|
34275
|
-
posts: site.posts
|
|
34276
|
-
});
|
|
34277
|
-
await writeHtmlFile(outputDir, "map/index.html", mapHtml);
|
|
34278
|
-
console.log("Generated map page");
|
|
34279
|
-
} catch (error) {
|
|
34280
|
-
if (error instanceof Error && error.message.includes("map.njk")) {
|
|
34281
|
-
console.log("No map.njk template found, skipping map page generation");
|
|
34282
|
-
} else {
|
|
34283
|
-
console.warn("Error generating map page:", error);
|
|
34284
|
-
}
|
|
34285
|
-
}
|
|
34298
|
+
await generateOptionalPage("map.njk", { site: config, posts: site.posts }, outputDir, "map/index.html", "map page");
|
|
34286
34299
|
}
|
|
34287
34300
|
|
|
34288
34301
|
// src/generators/assets.ts
|
|
@@ -34432,8 +34445,13 @@ function displayMetrics(metrics) {
|
|
|
34432
34445
|
|
|
34433
34446
|
// src/utils/template-engine.ts
|
|
34434
34447
|
var import_nunjucks2 = __toESM(require_nunjucks(), 1);
|
|
34448
|
+
import path9 from "path";
|
|
34449
|
+
import { existsSync } from "fs";
|
|
34450
|
+
var _distFragments = path9.join(import.meta.dir, "fragments");
|
|
34451
|
+
var _srcFragments = path9.join(import.meta.dir, "../fragments");
|
|
34452
|
+
var BUNKI_FRAGMENTS_DIR = existsSync(_distFragments) ? _distFragments : _srcFragments;
|
|
34435
34453
|
function createTemplateEngine(templatesDir, watch = false) {
|
|
34436
|
-
const env = import_nunjucks2.default.configure(templatesDir, {
|
|
34454
|
+
const env = import_nunjucks2.default.configure([templatesDir, BUNKI_FRAGMENTS_DIR], {
|
|
34437
34455
|
autoescape: true,
|
|
34438
34456
|
watch
|
|
34439
34457
|
});
|
|
@@ -34484,12 +34502,24 @@ class SiteGenerator {
|
|
|
34484
34502
|
async initialize() {
|
|
34485
34503
|
this.metrics.startStage("initialization");
|
|
34486
34504
|
console.log("Initializing site generator...");
|
|
34505
|
+
const flatAssetsDir = path10.join(process.cwd(), "content", "_assets");
|
|
34506
|
+
try {
|
|
34507
|
+
const stat = await import("fs/promises").then((m3) => m3.stat(flatAssetsDir));
|
|
34508
|
+
if (stat.isDirectory()) {
|
|
34509
|
+
throw new Error(`Build error: content/_assets/ must not exist.
|
|
34510
|
+
Images must be placed in content/{year}/_assets/ (e.g. content/2025/_assets/).
|
|
34511
|
+
Move any files from content/_assets/ into the correct year folder and retry.`);
|
|
34512
|
+
}
|
|
34513
|
+
} catch (err) {
|
|
34514
|
+
if (err.code !== "ENOENT")
|
|
34515
|
+
throw err;
|
|
34516
|
+
}
|
|
34487
34517
|
await ensureDir(this.options.outputDir);
|
|
34488
34518
|
if (this.options.config.noFollowExceptions) {
|
|
34489
34519
|
setNoFollowExceptions(this.options.config.noFollowExceptions);
|
|
34490
34520
|
}
|
|
34491
34521
|
let tagDescriptions = {};
|
|
34492
|
-
const tagsTomlPath =
|
|
34522
|
+
const tagsTomlPath = path10.join(process.cwd(), "src", "tags.toml");
|
|
34493
34523
|
const tagsTomlFile = Bun.file(tagsTomlPath);
|
|
34494
34524
|
if (await tagsTomlFile.exists()) {
|
|
34495
34525
|
try {
|
|
@@ -34551,8 +34581,8 @@ class SiteGenerator {
|
|
|
34551
34581
|
this.metrics.startStage("cssProcessing");
|
|
34552
34582
|
let cssChanged = true;
|
|
34553
34583
|
if (this.cache && this.incrementalMode && this.options.config.css) {
|
|
34554
|
-
const cssInputPath =
|
|
34555
|
-
const cssOutputPath =
|
|
34584
|
+
const cssInputPath = path10.resolve(process.cwd(), this.options.config.css.input);
|
|
34585
|
+
const cssOutputPath = path10.join(this.options.outputDir, this.options.config.css.output);
|
|
34556
34586
|
const cssOutputExists = await Bun.file(cssOutputPath).exists();
|
|
34557
34587
|
cssChanged = await hasFileChanged(cssInputPath, this.cache);
|
|
34558
34588
|
if (!cssChanged && cssOutputExists) {
|
|
@@ -34586,19 +34616,19 @@ class SiteGenerator {
|
|
|
34586
34616
|
}
|
|
34587
34617
|
async generateFeeds() {
|
|
34588
34618
|
const rssContent = generateRSSFeed(this.site, this.options.config);
|
|
34589
|
-
await Bun.write(
|
|
34619
|
+
await Bun.write(path10.join(this.options.outputDir, "feed.xml"), rssContent);
|
|
34590
34620
|
const sitemapContent = generateSitemap(this.site, this.options.config, PAGINATION.DEFAULT_PAGE_SIZE);
|
|
34591
|
-
await Bun.write(
|
|
34621
|
+
await Bun.write(path10.join(this.options.outputDir, "sitemap.xml"), sitemapContent);
|
|
34592
34622
|
console.log("Generated sitemap.xml");
|
|
34593
34623
|
const urlCount = this.site.posts.length + Object.keys(this.site.tags).length + 10;
|
|
34594
34624
|
const sitemapSize = sitemapContent.length;
|
|
34595
34625
|
if (urlCount > FILES.MAX_SITEMAP_URLS || sitemapSize > FILES.MAX_SITEMAP_SIZE) {
|
|
34596
34626
|
const sitemapIndexContent = generateSitemapIndex(this.options.config);
|
|
34597
|
-
await Bun.write(
|
|
34627
|
+
await Bun.write(path10.join(this.options.outputDir, "sitemap_index.xml"), sitemapIndexContent);
|
|
34598
34628
|
console.log("Generated sitemap_index.xml");
|
|
34599
34629
|
}
|
|
34600
34630
|
const robotsTxtContent = generateRobotsTxt(this.options.config);
|
|
34601
|
-
await Bun.write(
|
|
34631
|
+
await Bun.write(path10.join(this.options.outputDir, "robots.txt"), robotsTxtContent);
|
|
34602
34632
|
console.log("Generated robots.txt");
|
|
34603
34633
|
}
|
|
34604
34634
|
async parseContent() {
|
|
@@ -34615,7 +34645,7 @@ class SiteGenerator {
|
|
|
34615
34645
|
return posts;
|
|
34616
34646
|
}
|
|
34617
34647
|
const allFiles = await findFilesByPattern("**/*.md", this.options.contentDir, true);
|
|
34618
|
-
const configPath =
|
|
34648
|
+
const configPath = path10.join(process.cwd(), "bunki.config.ts");
|
|
34619
34649
|
const configChanged = await hasConfigChanged(configPath, this.cache);
|
|
34620
34650
|
if (configChanged) {
|
|
34621
34651
|
console.log("Config changed, full rebuild required");
|
|
@@ -34694,10 +34724,10 @@ var defaultDeps2 = {
|
|
|
34694
34724
|
};
|
|
34695
34725
|
async function handleGenerateCommand(options2, deps = defaultDeps2) {
|
|
34696
34726
|
try {
|
|
34697
|
-
const configPath =
|
|
34698
|
-
const contentDir =
|
|
34699
|
-
const outputDir =
|
|
34700
|
-
const templatesDir =
|
|
34727
|
+
const configPath = path11.resolve(options2.config);
|
|
34728
|
+
const contentDir = path11.resolve(options2.content);
|
|
34729
|
+
const outputDir = path11.resolve(options2.output);
|
|
34730
|
+
const templatesDir = path11.resolve(options2.templates);
|
|
34701
34731
|
deps.logger.log("Generating site with:");
|
|
34702
34732
|
deps.logger.log(`- Config file: ${configPath}`);
|
|
34703
34733
|
deps.logger.log(`- Content directory: ${contentDir}`);
|
|
@@ -34731,11 +34761,11 @@ function registerGenerateCommand(program2) {
|
|
|
34731
34761
|
}
|
|
34732
34762
|
|
|
34733
34763
|
// src/utils/image-uploader.ts
|
|
34734
|
-
import
|
|
34764
|
+
import path13 from "path";
|
|
34735
34765
|
|
|
34736
34766
|
// src/utils/s3-uploader.ts
|
|
34737
34767
|
var {S3Client } = globalThis.Bun;
|
|
34738
|
-
import
|
|
34768
|
+
import path12 from "path";
|
|
34739
34769
|
|
|
34740
34770
|
class S3Uploader {
|
|
34741
34771
|
s3Config;
|
|
@@ -34858,7 +34888,7 @@ class S3Uploader {
|
|
|
34858
34888
|
let failedCount = 0;
|
|
34859
34889
|
const uploadTasks = imageFiles.map((imageFile) => async () => {
|
|
34860
34890
|
try {
|
|
34861
|
-
const imagePath =
|
|
34891
|
+
const imagePath = path12.join(imagesDir, imageFile);
|
|
34862
34892
|
const s3Key = keyTransform ? keyTransform(imageFile) : imageFile;
|
|
34863
34893
|
const file = Bun.file(imagePath);
|
|
34864
34894
|
if (process.env.BUNKI_DRY_RUN === "true") {} else {
|
|
@@ -34892,13 +34922,13 @@ function createUploader(config) {
|
|
|
34892
34922
|
}
|
|
34893
34923
|
|
|
34894
34924
|
// src/utils/image-uploader.ts
|
|
34895
|
-
var DEFAULT_IMAGES_DIR =
|
|
34896
|
-
var DEFAULT_CONTENT_DIR2 =
|
|
34925
|
+
var DEFAULT_IMAGES_DIR = path13.join(process.cwd(), "assets");
|
|
34926
|
+
var DEFAULT_CONTENT_DIR2 = path13.join(process.cwd(), "content");
|
|
34897
34927
|
async function uploadImages(options2 = {}) {
|
|
34898
34928
|
try {
|
|
34899
34929
|
const contentAssetsMode = options2.contentAssets === true;
|
|
34900
34930
|
const defaultDir = contentAssetsMode ? DEFAULT_CONTENT_DIR2 : DEFAULT_IMAGES_DIR;
|
|
34901
|
-
const imagesDir =
|
|
34931
|
+
const imagesDir = path13.resolve(options2.images || defaultDir);
|
|
34902
34932
|
if (!await fileExists(imagesDir)) {
|
|
34903
34933
|
console.log(`Creating images directory at ${imagesDir}...`);
|
|
34904
34934
|
await ensureDir(imagesDir);
|
|
@@ -34950,7 +34980,7 @@ async function uploadImages(options2 = {}) {
|
|
|
34950
34980
|
const uploader = createUploader(s3Config);
|
|
34951
34981
|
const imageUrlMap = await uploader.uploadImages(imagesDir, options2.minYear, keyTransform);
|
|
34952
34982
|
if (options2.outputJson) {
|
|
34953
|
-
const outputFile =
|
|
34983
|
+
const outputFile = path13.resolve(options2.outputJson);
|
|
34954
34984
|
await Bun.write(outputFile, JSON.stringify(imageUrlMap, null, 2));
|
|
34955
34985
|
console.log(`Image URL mapping saved to ${outputFile}`);
|
|
34956
34986
|
}
|
|
@@ -34998,7 +35028,7 @@ function registerImagesPushCommand(program2) {
|
|
|
34998
35028
|
}
|
|
34999
35029
|
|
|
35000
35030
|
// src/cli/commands/init.ts
|
|
35001
|
-
import
|
|
35031
|
+
import path14 from "path";
|
|
35002
35032
|
var defaultDependencies = {
|
|
35003
35033
|
createDefaultConfig,
|
|
35004
35034
|
ensureDir,
|
|
@@ -35008,7 +35038,7 @@ var defaultDependencies = {
|
|
|
35008
35038
|
};
|
|
35009
35039
|
async function handleInitCommand(options2, deps = defaultDependencies) {
|
|
35010
35040
|
try {
|
|
35011
|
-
const configPath =
|
|
35041
|
+
const configPath = path14.resolve(options2.config);
|
|
35012
35042
|
const configCreated = await deps.createDefaultConfig(configPath);
|
|
35013
35043
|
if (!configCreated) {
|
|
35014
35044
|
deps.logger.log(`
|
|
@@ -35017,19 +35047,19 @@ Skipped initialization because the config file already exists`);
|
|
|
35017
35047
|
}
|
|
35018
35048
|
deps.logger.log("Creating directory structure...");
|
|
35019
35049
|
const baseDir = process.cwd();
|
|
35020
|
-
const contentDir =
|
|
35021
|
-
const templatesDir =
|
|
35022
|
-
const stylesDir =
|
|
35023
|
-
const publicDir =
|
|
35050
|
+
const contentDir = path14.join(baseDir, "content");
|
|
35051
|
+
const templatesDir = path14.join(baseDir, "templates");
|
|
35052
|
+
const stylesDir = path14.join(templatesDir, "styles");
|
|
35053
|
+
const publicDir = path14.join(baseDir, "public");
|
|
35024
35054
|
await deps.ensureDir(contentDir);
|
|
35025
35055
|
await deps.ensureDir(templatesDir);
|
|
35026
35056
|
await deps.ensureDir(stylesDir);
|
|
35027
35057
|
await deps.ensureDir(publicDir);
|
|
35028
35058
|
for (const [filename, content] of Object.entries(getDefaultTemplates())) {
|
|
35029
|
-
await deps.writeFile(
|
|
35059
|
+
await deps.writeFile(path14.join(templatesDir, filename), content);
|
|
35030
35060
|
}
|
|
35031
|
-
await deps.writeFile(
|
|
35032
|
-
await deps.writeFile(
|
|
35061
|
+
await deps.writeFile(path14.join(stylesDir, "main.css"), getDefaultCss());
|
|
35062
|
+
await deps.writeFile(path14.join(contentDir, "welcome.md"), getSamplePost());
|
|
35033
35063
|
deps.logger.log(`
|
|
35034
35064
|
Initialization complete! Here are the next steps:`);
|
|
35035
35065
|
deps.logger.log("1. Edit bunki.config.ts to configure your site");
|
|
@@ -35049,221 +35079,302 @@ function registerInitCommand(program2, deps = defaultDependencies) {
|
|
|
35049
35079
|
function getDefaultTemplates() {
|
|
35050
35080
|
return {
|
|
35051
35081
|
"base.njk": String.raw`<!DOCTYPE html>
|
|
35052
|
-
|
|
35053
|
-
|
|
35054
|
-
|
|
35055
|
-
|
|
35056
|
-
|
|
35057
|
-
|
|
35058
|
-
|
|
35059
|
-
|
|
35060
|
-
|
|
35061
|
-
|
|
35062
|
-
|
|
35063
|
-
|
|
35064
|
-
|
|
35065
|
-
|
|
35066
|
-
|
|
35067
|
-
|
|
35068
|
-
|
|
35069
|
-
|
|
35070
|
-
|
|
35071
|
-
|
|
35072
|
-
|
|
35073
|
-
|
|
35074
|
-
|
|
35075
|
-
|
|
35076
|
-
|
|
35077
|
-
|
|
35078
|
-
|
|
35079
|
-
|
|
35080
|
-
|
|
35081
|
-
|
|
35082
|
-
|
|
35083
|
-
|
|
35084
|
-
|
|
35082
|
+
<html lang="en">
|
|
35083
|
+
<head>
|
|
35084
|
+
<meta charset="UTF-8">
|
|
35085
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
35086
|
+
<title>{% block title %}{{ site.title }}{% endblock %}</title>
|
|
35087
|
+
<meta name="description" content="{% block description %}{{ site.description }}{% endblock %}">
|
|
35088
|
+
|
|
35089
|
+
{# Canonical URL #}
|
|
35090
|
+
<link rel="canonical" href="{% block canonical %}{{ site.baseUrl }}/{% endblock %}">
|
|
35091
|
+
|
|
35092
|
+
{# Open Graph meta tags #}
|
|
35093
|
+
<meta property="og:type" content="{% block og_type %}website{% endblock %}">
|
|
35094
|
+
<meta property="og:title" content="{% block og_title %}{{ site.title }}{% endblock %}">
|
|
35095
|
+
<meta property="og:description" content="{% block og_description %}{{ site.description }}{% endblock %}">
|
|
35096
|
+
<meta property="og:url" content="{% block og_url %}{{ site.baseUrl }}/{% endblock %}">
|
|
35097
|
+
<meta property="og:site_name" content="{{ site.title }}">
|
|
35098
|
+
{% block og_image %}{% endblock %}
|
|
35099
|
+
|
|
35100
|
+
{# Twitter Card meta tags #}
|
|
35101
|
+
<meta name="twitter:card" content="{% block twitter_card %}summary{% endblock %}">
|
|
35102
|
+
<meta name="twitter:title" content="{% block twitter_title %}{{ site.title }}{% endblock %}">
|
|
35103
|
+
<meta name="twitter:description" content="{% block twitter_description %}{{ site.description }}{% endblock %}">
|
|
35104
|
+
{% block twitter_image %}{% endblock %}
|
|
35105
|
+
|
|
35106
|
+
<link rel="stylesheet" href="/css/style.css">
|
|
35107
|
+
<link rel="alternate" type="application/rss+xml" title="{{ site.title }} RSS Feed" href="{{ site.baseUrl }}/feed.xml">
|
|
35108
|
+
{% block head %}{% endblock %}
|
|
35109
|
+
</head>
|
|
35110
|
+
<body>
|
|
35111
|
+
<header>
|
|
35112
|
+
<div class="container">
|
|
35113
|
+
<h1><a href="/">{{ site.title }}</a></h1>
|
|
35114
|
+
<nav>
|
|
35115
|
+
<ul>
|
|
35116
|
+
<li><a href="/">Home</a></li>
|
|
35117
|
+
<li><a href="/tags/">Tags</a></li>
|
|
35118
|
+
</ul>
|
|
35119
|
+
</nav>
|
|
35120
|
+
</div>
|
|
35121
|
+
</header>
|
|
35122
|
+
|
|
35123
|
+
<main class="container">
|
|
35124
|
+
{% block content %}{% endblock %}
|
|
35125
|
+
</main>
|
|
35126
|
+
|
|
35127
|
+
<footer>
|
|
35128
|
+
<div class="container">
|
|
35129
|
+
<p>© {{ "now" | date("YYYY") }} {{ site.title }} - Powered by <a href="https://github.com/kahwee/bunki">Bunki</a></p>
|
|
35130
|
+
</div>
|
|
35131
|
+
</footer>
|
|
35132
|
+
</body>
|
|
35133
|
+
</html>`,
|
|
35085
35134
|
"index.njk": String.raw`{% extends "base.njk" %}
|
|
35086
35135
|
|
|
35087
|
-
|
|
35088
|
-
|
|
35089
|
-
|
|
35090
|
-
|
|
35091
|
-
|
|
35092
|
-
|
|
35093
|
-
|
|
35094
|
-
|
|
35095
|
-
|
|
35096
|
-
|
|
35097
|
-
|
|
35098
|
-
|
|
35099
|
-
|
|
35100
|
-
|
|
35101
|
-
|
|
35102
|
-
|
|
35103
|
-
|
|
35104
|
-
|
|
35105
|
-
|
|
35106
|
-
|
|
35107
|
-
</
|
|
35108
|
-
|
|
35109
|
-
|
|
35110
|
-
|
|
35111
|
-
{%
|
|
35112
|
-
|
|
35113
|
-
|
|
35114
|
-
|
|
35115
|
-
|
|
35116
|
-
|
|
35117
|
-
{% if pagination.
|
|
35118
|
-
|
|
35119
|
-
|
|
35120
|
-
|
|
35121
|
-
|
|
35122
|
-
</
|
|
35123
|
-
|
|
35124
|
-
{% else %}
|
|
35125
|
-
<p>No posts yet!</p>
|
|
35136
|
+
{% block canonical %}{{ site.baseUrl }}/{% if pagination.currentPage > 1 %}page/{{ pagination.currentPage }}/{% endif %}{% endblock %}
|
|
35137
|
+
{% block og_url %}{{ site.baseUrl }}/{% if pagination.currentPage > 1 %}page/{{ pagination.currentPage }}/{% endif %}{% endblock %}
|
|
35138
|
+
|
|
35139
|
+
{% block content %}
|
|
35140
|
+
<h1>Latest Posts</h1>
|
|
35141
|
+
|
|
35142
|
+
{% if posts.length > 0 %}
|
|
35143
|
+
<div class="posts">
|
|
35144
|
+
{% for post in posts %}
|
|
35145
|
+
<article class="post-card">
|
|
35146
|
+
<h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
|
|
35147
|
+
<div class="post-meta">
|
|
35148
|
+
<time datetime="{{ post.date }}">{{ post.date | date("MMMM D, YYYY") }}</time>
|
|
35149
|
+
{% if post.tags.length > 0 %}
|
|
35150
|
+
<span class="tags">
|
|
35151
|
+
{% for tag in post.tags %}
|
|
35152
|
+
<a href="/tags/{{ post.tagSlugs[tag] }}/">{{ tag }}</a>{% if not loop.last %}, {% endif %}
|
|
35153
|
+
{% endfor %}
|
|
35154
|
+
</span>
|
|
35155
|
+
{% endif %}
|
|
35156
|
+
</div>
|
|
35157
|
+
<div class="post-excerpt">{{ post.excerpt }}</div>
|
|
35158
|
+
<a href="{{ post.url }}" class="read-more">Read more \u2192</a>
|
|
35159
|
+
</article>
|
|
35160
|
+
{% endfor %}
|
|
35161
|
+
</div>
|
|
35162
|
+
|
|
35163
|
+
{% if pagination.totalPages > 1 %}
|
|
35164
|
+
<nav class="pagination">
|
|
35165
|
+
{% if pagination.hasPrevPage %}
|
|
35166
|
+
<a href="{{ pagination.pagePath }}{% if pagination.prevPage > 1 %}page/{{ pagination.prevPage }}/{% endif %}" class="prev">\u2190 Previous</a>
|
|
35167
|
+
{% endif %}
|
|
35168
|
+
{% if pagination.hasNextPage %}
|
|
35169
|
+
<a href="{{ pagination.pagePath }}page/{{ pagination.nextPage }}/" class="next">Next \u2192</a>
|
|
35170
|
+
{% endif %}
|
|
35171
|
+
<span class="page-info">Page {{ pagination.currentPage }} of {{ pagination.totalPages }}</span>
|
|
35172
|
+
</nav>
|
|
35126
35173
|
{% endif %}
|
|
35127
|
-
{%
|
|
35174
|
+
{% else %}
|
|
35175
|
+
<p>No posts yet.</p>
|
|
35176
|
+
{% endif %}
|
|
35177
|
+
{% endblock %}`,
|
|
35128
35178
|
"post.njk": String.raw`{% extends "base.njk" %}
|
|
35129
35179
|
|
|
35130
|
-
|
|
35131
|
-
|
|
35132
|
-
|
|
35133
|
-
|
|
35134
|
-
|
|
35135
|
-
|
|
35136
|
-
|
|
35137
|
-
|
|
35138
|
-
|
|
35139
|
-
|
|
35140
|
-
|
|
35141
|
-
|
|
35142
|
-
|
|
35143
|
-
|
|
35144
|
-
|
|
35145
|
-
|
|
35146
|
-
|
|
35147
|
-
|
|
35148
|
-
|
|
35149
|
-
|
|
35150
|
-
|
|
35180
|
+
{% from "og-image.njk" import og_image, twitter_image %}
|
|
35181
|
+
{% from "json-ld.njk" import blog_posting_schema %}
|
|
35182
|
+
|
|
35183
|
+
{% block title %}{{ post.title }} | {{ site.title }}{% endblock %}
|
|
35184
|
+
{% block description %}{{ post.excerpt }}{% endblock %}
|
|
35185
|
+
|
|
35186
|
+
{% block canonical %}{{ site.baseUrl }}{{ post.url }}{% endblock %}
|
|
35187
|
+
|
|
35188
|
+
{% block og_type %}article{% endblock %}
|
|
35189
|
+
{% block og_title %}{{ post.title }}{% endblock %}
|
|
35190
|
+
{% block og_description %}{{ post.excerpt }}{% endblock %}
|
|
35191
|
+
{% block og_url %}{{ site.baseUrl }}{{ post.url }}{% endblock %}
|
|
35192
|
+
{% block og_image %}{{ og_image(post, site) }}{% endblock %}
|
|
35193
|
+
|
|
35194
|
+
{% block twitter_card %}summary_large_image{% endblock %}
|
|
35195
|
+
{% block twitter_title %}{{ post.title }}{% endblock %}
|
|
35196
|
+
{% block twitter_description %}{{ post.excerpt }}{% endblock %}
|
|
35197
|
+
{% block twitter_image %}{{ twitter_image(post, site) }}{% endblock %}
|
|
35198
|
+
|
|
35199
|
+
{% block head %}
|
|
35200
|
+
{{ blog_posting_schema(post, site) }}
|
|
35201
|
+
{% endblock %}
|
|
35202
|
+
|
|
35203
|
+
{% block content %}
|
|
35204
|
+
<article class="post">
|
|
35205
|
+
<header class="post-header">
|
|
35206
|
+
<h1>{{ post.title }}</h1>
|
|
35207
|
+
<div class="post-meta">
|
|
35208
|
+
<time datetime="{{ post.date }}">{{ post.date | date("MMMM D, YYYY") }}</time>
|
|
35209
|
+
{% if post.tags.length > 0 %}
|
|
35210
|
+
<span class="tags">
|
|
35211
|
+
{% for tag in post.tags %}
|
|
35212
|
+
<a href="/tags/{{ post.tagSlugs[tag] }}/">{{ tag }}</a>{% if not loop.last %}, {% endif %}
|
|
35213
|
+
{% endfor %}
|
|
35214
|
+
</span>
|
|
35215
|
+
{% endif %}
|
|
35151
35216
|
</div>
|
|
35152
|
-
</
|
|
35153
|
-
{% endblock %}`,
|
|
35154
|
-
"tag.njk": String.raw`{% extends "base.njk" %}
|
|
35155
|
-
|
|
35156
|
-
{% block title %}{{ tag.name }} | {{ site.title }}{% endblock %}
|
|
35157
|
-
{% block description %}Posts tagged with {{ tag.name }} on {{ site.title }}{% endblock %}
|
|
35158
|
-
|
|
35159
|
-
{% block content %}
|
|
35160
|
-
<h1>Posts tagged "{{ tag.name }}"</h1>
|
|
35161
|
-
|
|
35162
|
-
{% if tag.description %}
|
|
35163
|
-
<div class="tag-description">{{ tag.description }}</div>
|
|
35164
|
-
{% endif %}
|
|
35217
|
+
</header>
|
|
35165
35218
|
|
|
35166
|
-
|
|
35167
|
-
|
|
35168
|
-
|
|
35169
|
-
|
|
35170
|
-
|
|
35171
|
-
|
|
35172
|
-
|
|
35173
|
-
|
|
35174
|
-
|
|
35175
|
-
|
|
35176
|
-
|
|
35177
|
-
|
|
35219
|
+
<div class="post-content">
|
|
35220
|
+
{{ post.html | safe }}
|
|
35221
|
+
</div>
|
|
35222
|
+
|
|
35223
|
+
<footer class="post-footer">
|
|
35224
|
+
<div class="share-buttons">
|
|
35225
|
+
<span class="share-label">Share:</span>
|
|
35226
|
+
<a href="https://twitter.com/intent/tweet?text={{ post.title | urlencode }}&url={{ site.baseUrl }}{{ post.url }}" target="_blank" rel="noopener noreferrer" class="share-button x" aria-label="Share on X">
|
|
35227
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
|
|
35228
|
+
</a>
|
|
35229
|
+
<a href="https://www.facebook.com/sharer/sharer.php?u={{ site.baseUrl }}{{ post.url }}" target="_blank" rel="noopener noreferrer" class="share-button facebook" aria-label="Share on Facebook">
|
|
35230
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M9.101 23.691v-7.98H6.627v-3.667h2.474v-1.58c0-4.085 1.848-5.978 5.858-5.978.401 0 .955.042 1.468.103a8.68 8.68 0 0 1 1.141.195v3.325a8.623 8.623 0 0 0-.653-.036 26.805 26.805 0 0 0-.733-.009c-.707 0-1.259.096-1.675.309a1.686 1.686 0 0 0-.679.622c-.258.42-.374.995-.374 1.752v1.297h3.919l-.386 3.667h-3.533v7.98H9.101z"/></svg>
|
|
35231
|
+
</a>
|
|
35232
|
+
<a href="https://www.linkedin.com/sharing/share-offsite/?url={{ site.baseUrl }}{{ post.url }}" target="_blank" rel="noopener noreferrer" class="share-button linkedin" aria-label="Share on LinkedIn">
|
|
35233
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M6.5 21.5h-5v-13h5v13zM4 6.5C2.5 6.5 1.5 5.3 1.5 4s1-2.4 2.5-2.4c1.6 0 2.5 1 2.6 2.5 0 1.4-1 2.5-2.6 2.5zm11.5 6c-1 0-2 1-2 2v7h-5v-13h5V10s1.6-1.5 4-1.5c3 0 5 2.2 5 6.3v6.7h-5v-7c0-1-1-2-2-2z"/></svg>
|
|
35234
|
+
</a>
|
|
35235
|
+
<a href="mailto:?subject={{ post.title | urlencode }}&body=Check%20out%20this%20article%3A%20{{ site.baseUrl }}{{ post.url }}" class="share-button email" aria-label="Share via Email">
|
|
35236
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg>
|
|
35237
|
+
</a>
|
|
35178
35238
|
</div>
|
|
35239
|
+
</footer>
|
|
35240
|
+
</article>
|
|
35241
|
+
{% endblock %}`,
|
|
35242
|
+
"tag.njk": String.raw`{% extends "base.njk" %}
|
|
35179
35243
|
|
|
35180
|
-
|
|
35181
|
-
|
|
35182
|
-
|
|
35183
|
-
|
|
35184
|
-
|
|
35185
|
-
|
|
35186
|
-
|
|
35187
|
-
|
|
35188
|
-
|
|
35189
|
-
|
|
35190
|
-
|
|
35191
|
-
|
|
35192
|
-
|
|
35193
|
-
|
|
35194
|
-
|
|
35244
|
+
{% block title %}{{ tag.name }} | {{ site.title }}{% endblock %}
|
|
35245
|
+
{% block description %}Posts tagged with {{ tag.name }} on {{ site.title }}{% endblock %}
|
|
35246
|
+
|
|
35247
|
+
{% block canonical %}{{ site.baseUrl }}/tags/{{ tag.slug }}/{% if pagination.currentPage > 1 %}page/{{ pagination.currentPage }}/{% endif %}{% endblock %}
|
|
35248
|
+
|
|
35249
|
+
{% block og_title %}{{ tag.name }} | {{ site.title }}{% endblock %}
|
|
35250
|
+
{% block og_description %}Posts tagged with {{ tag.name }} on {{ site.title }}{% endblock %}
|
|
35251
|
+
{% block og_url %}{{ site.baseUrl }}/tags/{{ tag.slug }}/{% if pagination.currentPage > 1 %}page/{{ pagination.currentPage }}/{% endif %}{% endblock %}
|
|
35252
|
+
|
|
35253
|
+
{% block twitter_title %}{{ tag.name }} | {{ site.title }}{% endblock %}
|
|
35254
|
+
{% block twitter_description %}Posts tagged with {{ tag.name }} on {{ site.title }}{% endblock %}
|
|
35255
|
+
|
|
35256
|
+
{% block content %}
|
|
35257
|
+
<h1>Posts tagged "{{ tag.name }}"</h1>
|
|
35258
|
+
|
|
35259
|
+
{% if tag.description %}
|
|
35260
|
+
<div class="tag-description">{{ tag.description }}</div>
|
|
35261
|
+
{% endif %}
|
|
35262
|
+
|
|
35263
|
+
{% if tag.posts.length > 0 %}
|
|
35264
|
+
<div class="posts">
|
|
35265
|
+
{% for post in tag.posts %}
|
|
35266
|
+
<article class="post-card">
|
|
35267
|
+
<h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
|
|
35268
|
+
<div class="post-meta">
|
|
35269
|
+
<time datetime="{{ post.date }}">{{ post.date | date("MMMM D, YYYY") }}</time>
|
|
35270
|
+
</div>
|
|
35271
|
+
<div class="post-excerpt">{{ post.excerpt }}</div>
|
|
35272
|
+
<a href="{{ post.url }}" class="read-more">Read more \u2192</a>
|
|
35273
|
+
</article>
|
|
35274
|
+
{% endfor %}
|
|
35275
|
+
</div>
|
|
35276
|
+
|
|
35277
|
+
{% if pagination.totalPages > 1 %}
|
|
35278
|
+
<nav class="pagination">
|
|
35279
|
+
{% if pagination.hasPrevPage %}
|
|
35280
|
+
<a href="{{ pagination.pagePath }}{% if pagination.prevPage > 1 %}page/{{ pagination.prevPage }}/{% endif %}" class="prev">\u2190 Previous</a>
|
|
35281
|
+
{% endif %}
|
|
35282
|
+
{% if pagination.hasNextPage %}
|
|
35283
|
+
<a href="{{ pagination.pagePath }}page/{{ pagination.nextPage }}/" class="next">Next \u2192</a>
|
|
35284
|
+
{% endif %}
|
|
35285
|
+
<span class="page-info">Page {{ pagination.currentPage }} of {{ pagination.totalPages }}</span>
|
|
35286
|
+
</nav>
|
|
35195
35287
|
{% endif %}
|
|
35196
|
-
{%
|
|
35288
|
+
{% else %}
|
|
35289
|
+
<p>No posts with this tag yet.</p>
|
|
35290
|
+
{% endif %}
|
|
35291
|
+
{% endblock %}`,
|
|
35197
35292
|
"tags.njk": String.raw`{% extends "base.njk" %}
|
|
35198
35293
|
|
|
35199
|
-
|
|
35200
|
-
|
|
35294
|
+
{% block title %}Tags | {{ site.title }}{% endblock %}
|
|
35295
|
+
{% block description %}Browse all tags on {{ site.title }}{% endblock %}
|
|
35201
35296
|
|
|
35202
|
-
|
|
35203
|
-
<h1>All Tags</h1>
|
|
35297
|
+
{% block canonical %}{{ site.baseUrl }}/tags/{% endblock %}
|
|
35204
35298
|
|
|
35205
|
-
|
|
35206
|
-
|
|
35207
|
-
|
|
35208
|
-
<li>
|
|
35209
|
-
<a href="/tags/{{ tag.slug }}/">{{ tag.name }}</a>
|
|
35210
|
-
<span class="count">({{ tag.count }})</span>
|
|
35211
|
-
{% if tag.description %}
|
|
35212
|
-
<p class="description">{{ tag.description }}</p>
|
|
35213
|
-
{% endif %}
|
|
35214
|
-
</li>
|
|
35215
|
-
{% endfor %}
|
|
35216
|
-
</ul>
|
|
35217
|
-
{% else %}
|
|
35218
|
-
<p>No tags found!</p>
|
|
35219
|
-
{% endif %}
|
|
35220
|
-
{% endblock %}`,
|
|
35221
|
-
"archive.njk": String.raw`{% extends "base.njk" %}
|
|
35299
|
+
{% block og_title %}Tags | {{ site.title }}{% endblock %}
|
|
35300
|
+
{% block og_description %}Browse all tags on {{ site.title }}{% endblock %}
|
|
35301
|
+
{% block og_url %}{{ site.baseUrl }}/tags/{% endblock %}
|
|
35222
35302
|
|
|
35223
|
-
|
|
35224
|
-
|
|
35225
|
-
|
|
35226
|
-
{% block content %}
|
|
35227
|
-
<h1>Posts from {{ year }}</h1>
|
|
35228
|
-
|
|
35229
|
-
{% if posts.length > 0 %}
|
|
35230
|
-
<div class="posts">
|
|
35231
|
-
{% for post in posts %}
|
|
35232
|
-
<article class="post-card">
|
|
35233
|
-
<h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
|
|
35234
|
-
<div class="post-meta">
|
|
35235
|
-
<time datetime="{{ post.date }}">{{ post.date | date("MMMM D, YYYY") }}</time>
|
|
35236
|
-
{% if post.tags.length > 0 %}
|
|
35237
|
-
<span class="tags">
|
|
35238
|
-
{% for tag in post.tags %}
|
|
35239
|
-
<a href="/tags/{{ post.tagSlugs[tag] }}/">{{ tag }}</a>{% if not loop.last %}, {% endif %}
|
|
35240
|
-
{% endfor %}
|
|
35241
|
-
</span>
|
|
35242
|
-
{% endif %}
|
|
35243
|
-
</div>
|
|
35244
|
-
<div class="post-excerpt">{{ post.excerpt }}</div>
|
|
35245
|
-
<a href="{{ post.url }}" class="read-more">Read more \u2192</a>
|
|
35246
|
-
</article>
|
|
35247
|
-
{% endfor %}
|
|
35248
|
-
</div>
|
|
35303
|
+
{% block twitter_title %}Tags | {{ site.title }}{% endblock %}
|
|
35304
|
+
{% block twitter_description %}Browse all tags on {{ site.title }}{% endblock %}
|
|
35249
35305
|
|
|
35250
|
-
|
|
35251
|
-
|
|
35252
|
-
{% if pagination.hasPrevPage %}
|
|
35253
|
-
<a href="/{{ year }}/{% if pagination.prevPage > 1 %}page/{{ pagination.prevPage }}/{% endif %}" class="prev">\u2190 Previous</a>
|
|
35254
|
-
{% endif %}
|
|
35306
|
+
{% block content %}
|
|
35307
|
+
<h1>All Tags</h1>
|
|
35255
35308
|
|
|
35256
|
-
|
|
35257
|
-
|
|
35309
|
+
{% if tags.length > 0 %}
|
|
35310
|
+
<ul class="tags-list">
|
|
35311
|
+
{% for tag in tags %}
|
|
35312
|
+
<li>
|
|
35313
|
+
<a href="/tags/{{ tag.slug }}/">{{ tag.name }}</a>
|
|
35314
|
+
<span class="count">({{ tag.count }})</span>
|
|
35315
|
+
{% if tag.description %}
|
|
35316
|
+
<p class="description">{{ tag.description }}</p>
|
|
35258
35317
|
{% endif %}
|
|
35318
|
+
</li>
|
|
35319
|
+
{% endfor %}
|
|
35320
|
+
</ul>
|
|
35321
|
+
{% else %}
|
|
35322
|
+
<p>No tags yet.</p>
|
|
35323
|
+
{% endif %}
|
|
35324
|
+
{% endblock %}`,
|
|
35325
|
+
"archive.njk": String.raw`{% extends "base.njk" %}
|
|
35259
35326
|
|
|
35260
|
-
|
|
35261
|
-
|
|
35262
|
-
|
|
35263
|
-
|
|
35264
|
-
|
|
35327
|
+
{% block title %}Archive {{ year }} | {{ site.title }}{% endblock %}
|
|
35328
|
+
{% block description %}Posts from {{ year }} on {{ site.title }}{% endblock %}
|
|
35329
|
+
|
|
35330
|
+
{% block canonical %}{{ site.baseUrl }}/{{ year }}/{% if pagination.currentPage > 1 %}page/{{ pagination.currentPage }}/{% endif %}{% endblock %}
|
|
35331
|
+
|
|
35332
|
+
{% block og_title %}Archive {{ year }} | {{ site.title }}{% endblock %}
|
|
35333
|
+
{% block og_description %}Posts from {{ year }} on {{ site.title }}{% endblock %}
|
|
35334
|
+
{% block og_url %}{{ site.baseUrl }}/{{ year }}/{% if pagination.currentPage > 1 %}page/{{ pagination.currentPage }}/{% endif %}{% endblock %}
|
|
35335
|
+
|
|
35336
|
+
{% block twitter_title %}Archive {{ year }} | {{ site.title }}{% endblock %}
|
|
35337
|
+
{% block twitter_description %}Posts from {{ year }} on {{ site.title }}{% endblock %}
|
|
35338
|
+
|
|
35339
|
+
{% block content %}
|
|
35340
|
+
<h1>Posts from {{ year }}</h1>
|
|
35341
|
+
|
|
35342
|
+
{% if posts.length > 0 %}
|
|
35343
|
+
<div class="posts">
|
|
35344
|
+
{% for post in posts %}
|
|
35345
|
+
<article class="post-card">
|
|
35346
|
+
<h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
|
|
35347
|
+
<div class="post-meta">
|
|
35348
|
+
<time datetime="{{ post.date }}">{{ post.date | date("MMMM D, YYYY") }}</time>
|
|
35349
|
+
{% if post.tags.length > 0 %}
|
|
35350
|
+
<span class="tags">
|
|
35351
|
+
{% for tag in post.tags %}
|
|
35352
|
+
<a href="/tags/{{ post.tagSlugs[tag] }}/">{{ tag }}</a>{% if not loop.last %}, {% endif %}
|
|
35353
|
+
{% endfor %}
|
|
35354
|
+
</span>
|
|
35355
|
+
{% endif %}
|
|
35356
|
+
</div>
|
|
35357
|
+
<div class="post-excerpt">{{ post.excerpt }}</div>
|
|
35358
|
+
<a href="{{ post.url }}" class="read-more">Read more \u2192</a>
|
|
35359
|
+
</article>
|
|
35360
|
+
{% endfor %}
|
|
35361
|
+
</div>
|
|
35362
|
+
|
|
35363
|
+
{% if pagination.totalPages > 1 %}
|
|
35364
|
+
<nav class="pagination">
|
|
35365
|
+
{% if pagination.hasPrevPage %}
|
|
35366
|
+
<a href="/{{ year }}/{% if pagination.prevPage > 1 %}page/{{ pagination.prevPage }}/{% endif %}" class="prev">\u2190 Previous</a>
|
|
35367
|
+
{% endif %}
|
|
35368
|
+
{% if pagination.hasNextPage %}
|
|
35369
|
+
<a href="/{{ year }}/page/{{ pagination.nextPage }}/" class="next">Next \u2192</a>
|
|
35370
|
+
{% endif %}
|
|
35371
|
+
<span class="page-info">Page {{ pagination.currentPage }} of {{ pagination.totalPages }}</span>
|
|
35372
|
+
</nav>
|
|
35265
35373
|
{% endif %}
|
|
35266
|
-
{%
|
|
35374
|
+
{% else %}
|
|
35375
|
+
<p>No posts from {{ year }} yet.</p>
|
|
35376
|
+
{% endif %}
|
|
35377
|
+
{% endblock %}`
|
|
35267
35378
|
};
|
|
35268
35379
|
}
|
|
35269
35380
|
function getDefaultCss() {
|
|
@@ -35458,6 +35569,43 @@ function getDefaultCss() {
|
|
|
35458
35569
|
font-size: 0.9rem;
|
|
35459
35570
|
}
|
|
35460
35571
|
|
|
35572
|
+
/* Share buttons */
|
|
35573
|
+
.post-footer {
|
|
35574
|
+
margin-top: 2rem;
|
|
35575
|
+
padding-top: 1.5rem;
|
|
35576
|
+
border-top: 1px solid #eee;
|
|
35577
|
+
}
|
|
35578
|
+
|
|
35579
|
+
.share-buttons {
|
|
35580
|
+
display: flex;
|
|
35581
|
+
align-items: center;
|
|
35582
|
+
gap: 0.75rem;
|
|
35583
|
+
}
|
|
35584
|
+
|
|
35585
|
+
.share-label {
|
|
35586
|
+
font-size: 0.9rem;
|
|
35587
|
+
font-weight: 500;
|
|
35588
|
+
color: #6c757d;
|
|
35589
|
+
}
|
|
35590
|
+
|
|
35591
|
+
.share-button {
|
|
35592
|
+
display: inline-flex;
|
|
35593
|
+
align-items: center;
|
|
35594
|
+
justify-content: center;
|
|
35595
|
+
width: 2.25rem;
|
|
35596
|
+
height: 2.25rem;
|
|
35597
|
+
border-radius: 50%;
|
|
35598
|
+
background-color: #f5f5f5;
|
|
35599
|
+
color: #555;
|
|
35600
|
+
transition: background-color 0.2s, color 0.2s;
|
|
35601
|
+
}
|
|
35602
|
+
|
|
35603
|
+
.share-button:hover { text-decoration: none; }
|
|
35604
|
+
.share-button.x:hover { background-color: #000; color: #fff; }
|
|
35605
|
+
.share-button.facebook:hover { background-color: #1877f2; color: #fff; }
|
|
35606
|
+
.share-button.linkedin:hover { background-color: #0077b5; color: #fff; }
|
|
35607
|
+
.share-button.email:hover { background-color: #6c757d; color: #fff; }
|
|
35608
|
+
|
|
35461
35609
|
/* Footer */
|
|
35462
35610
|
footer {
|
|
35463
35611
|
text-align: center;
|
|
@@ -35521,7 +35669,7 @@ function hello() {
|
|
|
35521
35669
|
}
|
|
35522
35670
|
|
|
35523
35671
|
// src/cli/commands/new-post.ts
|
|
35524
|
-
import
|
|
35672
|
+
import path15 from "path";
|
|
35525
35673
|
var defaultDeps4 = {
|
|
35526
35674
|
writeFile: (filePath, data) => Bun.write(filePath, data),
|
|
35527
35675
|
now: () => new Date,
|
|
@@ -35545,7 +35693,7 @@ async function handleNewCommand(title, options2, deps = defaultDeps4) {
|
|
|
35545
35693
|
` + `# ${title}
|
|
35546
35694
|
|
|
35547
35695
|
`;
|
|
35548
|
-
const filePath =
|
|
35696
|
+
const filePath = path15.join(DEFAULT_CONTENT_DIR, `${slug}.md`);
|
|
35549
35697
|
await deps.writeFile(filePath, frontmatter);
|
|
35550
35698
|
deps.logger.log(`Created new post: ${filePath}`);
|
|
35551
35699
|
return filePath;
|
|
@@ -35562,21 +35710,12 @@ function registerNewCommand(program2) {
|
|
|
35562
35710
|
}
|
|
35563
35711
|
|
|
35564
35712
|
// src/cli/commands/serve.ts
|
|
35565
|
-
import
|
|
35713
|
+
import path17 from "path";
|
|
35566
35714
|
|
|
35567
35715
|
// src/server.ts
|
|
35568
|
-
import
|
|
35569
|
-
import path15 from "path";
|
|
35716
|
+
import path16 from "path";
|
|
35570
35717
|
async function startServer(outputDir = DEFAULT_OUTPUT_DIR, port = 3000) {
|
|
35571
|
-
|
|
35572
|
-
const stats = await fs2.promises.stat(outputDir);
|
|
35573
|
-
if (!stats.isDirectory()) {
|
|
35574
|
-
const msg = `Error: Output directory ${outputDir} does not exist or is not accessible.`;
|
|
35575
|
-
console.error(msg);
|
|
35576
|
-
console.log('Try running "bunki generate" first to build your site.');
|
|
35577
|
-
throw new Error(msg);
|
|
35578
|
-
}
|
|
35579
|
-
} catch (error) {
|
|
35718
|
+
if (!await isDirectory(outputDir)) {
|
|
35580
35719
|
const msg = `Error: Output directory ${outputDir} does not exist or is not accessible.`;
|
|
35581
35720
|
console.error(msg);
|
|
35582
35721
|
console.log('Try running "bunki generate" first to build your site.');
|
|
@@ -35601,18 +35740,18 @@ async function startServer(outputDir = DEFAULT_OUTPUT_DIR, port = 3000) {
|
|
|
35601
35740
|
let filePath = "";
|
|
35602
35741
|
if (homePaginationMatch) {
|
|
35603
35742
|
const pageNumber = homePaginationMatch[1];
|
|
35604
|
-
filePath =
|
|
35743
|
+
filePath = path16.join(outputDir, "page", pageNumber, "index.html");
|
|
35605
35744
|
} else if (tagPaginationMatch) {
|
|
35606
35745
|
const tagSlug = tagPaginationMatch[1];
|
|
35607
35746
|
const pageNumber = tagPaginationMatch[2];
|
|
35608
|
-
filePath =
|
|
35747
|
+
filePath = path16.join(outputDir, "tags", tagSlug, "page", pageNumber, "index.html");
|
|
35609
35748
|
} else if (yearPaginationMatch) {
|
|
35610
35749
|
const year = yearPaginationMatch[1];
|
|
35611
35750
|
const pageNumber = yearPaginationMatch[2];
|
|
35612
|
-
filePath =
|
|
35751
|
+
filePath = path16.join(outputDir, year, "page", pageNumber, "index.html");
|
|
35613
35752
|
} else {
|
|
35614
|
-
const directPath =
|
|
35615
|
-
const withoutSlash =
|
|
35753
|
+
const directPath = path16.join(outputDir, pathname);
|
|
35754
|
+
const withoutSlash = path16.join(outputDir, pathname + ".html");
|
|
35616
35755
|
const withHtml = pathname.endsWith(".html") ? directPath : withoutSlash;
|
|
35617
35756
|
const bunFileDirect = Bun.file(directPath);
|
|
35618
35757
|
const bunFileHtml = Bun.file(withHtml);
|
|
@@ -35621,7 +35760,7 @@ async function startServer(outputDir = DEFAULT_OUTPUT_DIR, port = 3000) {
|
|
|
35621
35760
|
} else if (await bunFileHtml.exists()) {
|
|
35622
35761
|
filePath = withHtml;
|
|
35623
35762
|
} else {
|
|
35624
|
-
const indexPath =
|
|
35763
|
+
const indexPath = path16.join(outputDir, pathname, "index.html");
|
|
35625
35764
|
const bunFileIndex = Bun.file(indexPath);
|
|
35626
35765
|
if (await bunFileIndex.exists()) {
|
|
35627
35766
|
filePath = indexPath;
|
|
@@ -35635,7 +35774,7 @@ async function startServer(outputDir = DEFAULT_OUTPUT_DIR, port = 3000) {
|
|
|
35635
35774
|
}
|
|
35636
35775
|
}
|
|
35637
35776
|
console.log(`Serving file: ${filePath}`);
|
|
35638
|
-
const extname =
|
|
35777
|
+
const extname = path16.extname(filePath);
|
|
35639
35778
|
let contentType = "text/html";
|
|
35640
35779
|
switch (extname) {
|
|
35641
35780
|
case ".js":
|
|
@@ -35694,7 +35833,7 @@ var defaultDeps5 = {
|
|
|
35694
35833
|
};
|
|
35695
35834
|
async function handleServeCommand(options2, deps = defaultDeps5) {
|
|
35696
35835
|
try {
|
|
35697
|
-
const outputDir =
|
|
35836
|
+
const outputDir = path17.resolve(options2.output);
|
|
35698
35837
|
const port = parseInt(options2.port, 10);
|
|
35699
35838
|
await deps.startServer(outputDir, port);
|
|
35700
35839
|
} catch (error) {
|
|
@@ -35737,7 +35876,7 @@ function registerValidateCommand(program2) {
|
|
|
35737
35876
|
}
|
|
35738
35877
|
|
|
35739
35878
|
// src/cli/commands/validate-media.ts
|
|
35740
|
-
import { readdirSync, readFileSync, existsSync, statSync } from "fs";
|
|
35879
|
+
import { readdirSync, readFileSync, existsSync as existsSync2, statSync } from "fs";
|
|
35741
35880
|
import { join, dirname, resolve, basename } from "path";
|
|
35742
35881
|
var imageExtensions = [".jpg", ".jpeg", ".png", ".webp", ".gif"];
|
|
35743
35882
|
var videoExtensions = [".mp4", ".webm", ".mov"];
|
|
@@ -35745,7 +35884,7 @@ var mediaExtensions = [...imageExtensions, ...videoExtensions];
|
|
|
35745
35884
|
async function handleValidateMediaCommand(options2, deps = { logger: console, exit: (code) => process.exit(code) }) {
|
|
35746
35885
|
const contentDir = options2.contentDir || join(process.cwd(), "content");
|
|
35747
35886
|
const assetsDir = join(process.cwd(), "assets");
|
|
35748
|
-
if (!
|
|
35887
|
+
if (!existsSync2(contentDir)) {
|
|
35749
35888
|
deps.logger.error(`Content directory not found: ${contentDir}`);
|
|
35750
35889
|
deps.exit(1);
|
|
35751
35890
|
}
|
|
@@ -35867,7 +36006,7 @@ function validateMedia(contentDir, assetsDir) {
|
|
|
35867
36006
|
}
|
|
35868
36007
|
function getAllMediaFromContentAssets(contentDir) {
|
|
35869
36008
|
const mediaFiles = [];
|
|
35870
|
-
if (!
|
|
36009
|
+
if (!existsSync2(contentDir))
|
|
35871
36010
|
return [];
|
|
35872
36011
|
const years = readdirSync(contentDir).filter((f) => {
|
|
35873
36012
|
const fullPath = join(contentDir, f);
|
|
@@ -35875,7 +36014,7 @@ function getAllMediaFromContentAssets(contentDir) {
|
|
|
35875
36014
|
});
|
|
35876
36015
|
for (const year of years) {
|
|
35877
36016
|
const assetsDir = join(contentDir, year, "_assets");
|
|
35878
|
-
if (!
|
|
36017
|
+
if (!existsSync2(assetsDir))
|
|
35879
36018
|
continue;
|
|
35880
36019
|
const files = readdirSync(assetsDir);
|
|
35881
36020
|
for (const file of files) {
|
|
@@ -35897,7 +36036,7 @@ function getAllMediaFromContentAssets(contentDir) {
|
|
|
35897
36036
|
}
|
|
35898
36037
|
function getAllMediaFromAssets(assetsDir) {
|
|
35899
36038
|
const mediaFiles = [];
|
|
35900
|
-
if (!
|
|
36039
|
+
if (!existsSync2(assetsDir))
|
|
35901
36040
|
return [];
|
|
35902
36041
|
const years = readdirSync(assetsDir).filter((f) => {
|
|
35903
36042
|
const fullPath = join(assetsDir, f);
|
|
@@ -35929,7 +36068,7 @@ function getAllMediaFromAssets(assetsDir) {
|
|
|
35929
36068
|
function checkMediaReference(markdownFile, lineNumber, mediaPath, type, missingReferences) {
|
|
35930
36069
|
const markdownDir = dirname(markdownFile);
|
|
35931
36070
|
const resolvedPath = resolve(markdownDir, mediaPath);
|
|
35932
|
-
if (!
|
|
36071
|
+
if (!existsSync2(resolvedPath)) {
|
|
35933
36072
|
missingReferences.push({
|
|
35934
36073
|
file: markdownFile.replace(process.cwd() + "/", ""),
|
|
35935
36074
|
line: lineNumber,
|