bunki 0.5.3 → 0.6.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 +133 -513
- package/dist/cli.js +172 -128
- package/dist/index.js +133 -96
- package/dist/site-generator.d.ts +8 -0
- package/dist/types.d.ts +10 -0
- package/dist/utils/css-processor.d.ts +2 -1
- package/dist/utils/file-utils.d.ts +140 -0
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -28185,7 +28185,7 @@ function getDefaultConfig() {
|
|
|
28185
28185
|
return base;
|
|
28186
28186
|
}
|
|
28187
28187
|
async function createDefaultConfig(configPath = DEFAULT_CONFIG_FILE) {
|
|
28188
|
-
if (await configExists()) {
|
|
28188
|
+
if (await configExists(configPath)) {
|
|
28189
28189
|
console.log(`Config file already exists`);
|
|
28190
28190
|
return false;
|
|
28191
28191
|
}
|
|
@@ -28243,15 +28243,6 @@ async function processCSS(options2) {
|
|
|
28243
28243
|
} catch (error) {
|
|
28244
28244
|
throw new Error(`CSS input file not found: ${inputPath}`);
|
|
28245
28245
|
}
|
|
28246
|
-
let postcssConfigExists = false;
|
|
28247
|
-
try {
|
|
28248
|
-
await fs.promises.access(postcssConfigPath);
|
|
28249
|
-
postcssConfigExists = true;
|
|
28250
|
-
} catch (error) {
|
|
28251
|
-
if (verbose) {
|
|
28252
|
-
console.log(`PostCSS config not found at ${postcssConfigPath}, will fallback to simple copy`);
|
|
28253
|
-
}
|
|
28254
|
-
}
|
|
28255
28246
|
const outputDirPath = path2.dirname(outputPath);
|
|
28256
28247
|
await fs.promises.mkdir(outputDirPath, { recursive: true });
|
|
28257
28248
|
if (verbose) {
|
|
@@ -28260,71 +28251,33 @@ async function processCSS(options2) {
|
|
|
28260
28251
|
console.log(`Output: ${outputPath}`);
|
|
28261
28252
|
console.log(`Config: ${postcssConfigPath}`);
|
|
28262
28253
|
}
|
|
28263
|
-
|
|
28264
|
-
|
|
28265
|
-
|
|
28266
|
-
|
|
28267
|
-
|
|
28268
|
-
|
|
28269
|
-
|
|
28270
|
-
|
|
28271
|
-
|
|
28272
|
-
|
|
28273
|
-
|
|
28274
|
-
|
|
28275
|
-
|
|
28276
|
-
stdio: verbose ? "inherit" : ["ignore", "pipe", "pipe"],
|
|
28277
|
-
cwd: projectRoot
|
|
28254
|
+
await runPostCSS(inputPath, outputPath, postcssConfigPath, projectRoot, verbose);
|
|
28255
|
+
}
|
|
28256
|
+
function runPostCSS(inputPath, outputPath, configPath, projectRoot, verbose) {
|
|
28257
|
+
return new Promise((resolve, reject) => {
|
|
28258
|
+
const args = ["postcss", inputPath, "-o", outputPath, "--config", configPath];
|
|
28259
|
+
const postcss = spawn("bunx", args, {
|
|
28260
|
+
stdio: verbose ? "inherit" : ["ignore", "pipe", "pipe"],
|
|
28261
|
+
cwd: projectRoot
|
|
28262
|
+
});
|
|
28263
|
+
let errorOutput = "";
|
|
28264
|
+
if (!verbose) {
|
|
28265
|
+
postcss.stderr?.on("data", (data) => {
|
|
28266
|
+
errorOutput += data.toString();
|
|
28278
28267
|
});
|
|
28279
|
-
|
|
28280
|
-
|
|
28281
|
-
|
|
28282
|
-
|
|
28283
|
-
|
|
28268
|
+
}
|
|
28269
|
+
postcss.on("close", (code) => {
|
|
28270
|
+
if (code === 0) {
|
|
28271
|
+
if (verbose)
|
|
28272
|
+
console.log("\u2705 CSS build completed successfully!");
|
|
28273
|
+
return resolve();
|
|
28284
28274
|
}
|
|
28285
|
-
|
|
28286
|
-
if (code === 0) {
|
|
28287
|
-
if (verbose)
|
|
28288
|
-
console.log("\u2705 CSS build completed successfully!");
|
|
28289
|
-
return resolve();
|
|
28290
|
-
}
|
|
28291
|
-
if (/module is not defined in ES module scope/i.test(errorOutput) && configPathToUse.endsWith(".js")) {
|
|
28292
|
-
const cjsPath = configPathToUse.replace(/\.js$/, ".cjs");
|
|
28293
|
-
try {
|
|
28294
|
-
if (!fs.existsSync(cjsPath)) {
|
|
28295
|
-
const original = await fs.promises.readFile(configPathToUse, "utf-8");
|
|
28296
|
-
await fs.promises.writeFile(cjsPath, original, "utf-8");
|
|
28297
|
-
if (verbose) {
|
|
28298
|
-
console.log(`Retrying PostCSS with converted CommonJS config at ${cjsPath}`);
|
|
28299
|
-
}
|
|
28300
|
-
}
|
|
28301
|
-
return resolve(runPostCSS(cjsPath));
|
|
28302
|
-
} catch (e) {
|
|
28303
|
-
if (verbose)
|
|
28304
|
-
console.warn("CJS fallback failed, copying CSS.");
|
|
28305
|
-
await fs.promises.copyFile(inputPath, outputPath);
|
|
28306
|
-
return resolve();
|
|
28307
|
-
}
|
|
28308
|
-
}
|
|
28309
|
-
if (verbose) {
|
|
28310
|
-
console.warn(`PostCSS failed (code ${code}). Falling back to simple copy. Error: ${errorOutput.trim()}`);
|
|
28311
|
-
}
|
|
28312
|
-
try {
|
|
28313
|
-
await fs.promises.copyFile(inputPath, outputPath);
|
|
28314
|
-
resolve();
|
|
28315
|
-
} catch (copyErr) {
|
|
28316
|
-
reject(new Error(`CSS build failed with code ${code} and fallback copy also failed: ${copyErr.message}`));
|
|
28317
|
-
}
|
|
28318
|
-
});
|
|
28319
|
-
postcss.on("error", (err) => {
|
|
28320
|
-
if (verbose) {
|
|
28321
|
-
console.warn(`Failed to start PostCSS process (${err.message}). Falling back to copy.`);
|
|
28322
|
-
}
|
|
28323
|
-
fs.promises.copyFile(inputPath, outputPath).then(() => resolve()).catch((copyErr) => reject(new Error(`Failed to start PostCSS and fallback copy failed: ${copyErr.message}`)));
|
|
28324
|
-
});
|
|
28275
|
+
reject(new Error(`PostCSS failed with exit code ${code}: ${errorOutput.trim()}`));
|
|
28325
28276
|
});
|
|
28326
|
-
|
|
28327
|
-
|
|
28277
|
+
postcss.on("error", (err) => {
|
|
28278
|
+
reject(new Error(`Failed to start PostCSS: ${err.message}`));
|
|
28279
|
+
});
|
|
28280
|
+
});
|
|
28328
28281
|
}
|
|
28329
28282
|
async function watchCSS(options2) {
|
|
28330
28283
|
const { css, projectRoot, verbose = false } = options2;
|
|
@@ -28422,6 +28375,7 @@ import path5 from "path";
|
|
|
28422
28375
|
// src/utils/file-utils.ts
|
|
28423
28376
|
var {Glob } = globalThis.Bun;
|
|
28424
28377
|
import path4 from "path";
|
|
28378
|
+
import { mkdir } from "fs/promises";
|
|
28425
28379
|
async function findFilesByPattern(pattern, directory, absolute = true) {
|
|
28426
28380
|
const glob = new Glob(pattern);
|
|
28427
28381
|
const files = [];
|
|
@@ -28434,8 +28388,20 @@ async function findFilesByPattern(pattern, directory, absolute = true) {
|
|
|
28434
28388
|
return files;
|
|
28435
28389
|
}
|
|
28436
28390
|
async function fileExists(filePath) {
|
|
28437
|
-
|
|
28438
|
-
|
|
28391
|
+
try {
|
|
28392
|
+
const file = Bun.file(filePath);
|
|
28393
|
+
return await file.exists();
|
|
28394
|
+
} catch {
|
|
28395
|
+
return false;
|
|
28396
|
+
}
|
|
28397
|
+
}
|
|
28398
|
+
async function isDirectory(dirPath) {
|
|
28399
|
+
try {
|
|
28400
|
+
const stat = await Bun.file(dirPath).stat();
|
|
28401
|
+
return stat?.isDirectory() ?? false;
|
|
28402
|
+
} catch {
|
|
28403
|
+
return false;
|
|
28404
|
+
}
|
|
28439
28405
|
}
|
|
28440
28406
|
async function readFileAsText(filePath) {
|
|
28441
28407
|
try {
|
|
@@ -28452,18 +28418,33 @@ async function readFileAsText(filePath) {
|
|
|
28452
28418
|
function getBaseFilename(filePath, extension = ".md") {
|
|
28453
28419
|
return path4.basename(filePath, extension);
|
|
28454
28420
|
}
|
|
28455
|
-
async function
|
|
28421
|
+
async function createDir(dirPath) {
|
|
28456
28422
|
try {
|
|
28457
|
-
await
|
|
28458
|
-
} catch (error) {
|
|
28423
|
+
await mkdir(dirPath, { recursive: true });
|
|
28424
|
+
} catch (error) {
|
|
28425
|
+
if (await isDirectory(dirPath)) {
|
|
28426
|
+
return;
|
|
28427
|
+
}
|
|
28428
|
+
if (await fileExists(dirPath)) {
|
|
28429
|
+
throw new Error(`Path exists but is not a directory: ${dirPath}`);
|
|
28430
|
+
}
|
|
28431
|
+
console.error(`Error creating directory ${dirPath}:`, error);
|
|
28432
|
+
throw error;
|
|
28433
|
+
}
|
|
28434
|
+
}
|
|
28435
|
+
async function ensureDir(dirPath) {
|
|
28436
|
+
return createDir(dirPath);
|
|
28459
28437
|
}
|
|
28460
28438
|
async function copyFile(sourcePath, targetPath) {
|
|
28461
28439
|
try {
|
|
28462
28440
|
const sourceFile = Bun.file(sourcePath);
|
|
28463
|
-
|
|
28464
|
-
|
|
28441
|
+
if (!await sourceFile.exists()) {
|
|
28442
|
+
throw new Error(`Source file does not exist: ${sourcePath}`);
|
|
28443
|
+
}
|
|
28444
|
+
await Bun.write(targetPath, sourceFile);
|
|
28465
28445
|
} catch (error) {
|
|
28466
28446
|
console.error(`Error copying file from ${sourcePath} to ${targetPath}:`, error);
|
|
28447
|
+
throw error;
|
|
28467
28448
|
}
|
|
28468
28449
|
}
|
|
28469
28450
|
|
|
@@ -33263,29 +33244,85 @@ class SiteGenerator {
|
|
|
33263
33244
|
console.log("Copied public files to site (including extensionless & dotfiles)");
|
|
33264
33245
|
}
|
|
33265
33246
|
}
|
|
33247
|
+
extractFirstImageUrl(html) {
|
|
33248
|
+
const imgRegex = /<img[^>]+src=["']([^"']+)["']/;
|
|
33249
|
+
const match = html.match(imgRegex);
|
|
33250
|
+
return match ? match[1] : null;
|
|
33251
|
+
}
|
|
33252
|
+
escapeXml(text) {
|
|
33253
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
33254
|
+
}
|
|
33266
33255
|
async generateRSSFeed() {
|
|
33267
33256
|
const posts = this.site.posts.slice(0, 15);
|
|
33268
33257
|
const config = this.options.config;
|
|
33258
|
+
const now = this.getPacificDate(new Date);
|
|
33259
|
+
const latestPostDate = posts.length > 0 ? posts[0].date : now.toISOString();
|
|
33260
|
+
const lastBuildDate = this.formatRSSDate(latestPostDate);
|
|
33269
33261
|
const rssItems = posts.map((post) => {
|
|
33270
33262
|
const postUrl = `${config.baseUrl}${post.url}`;
|
|
33271
33263
|
const pubDate = this.formatRSSDate(post.date);
|
|
33272
|
-
|
|
33264
|
+
const featuredImage = this.extractFirstImageUrl(post.html);
|
|
33265
|
+
const categoryTags = post.tags.map((tag) => ` <category>${this.escapeXml(tag)}</category>`).join(`
|
|
33266
|
+
`);
|
|
33267
|
+
let itemXml = ` <item>
|
|
33273
33268
|
<title><![CDATA[${post.title}]]></title>
|
|
33274
33269
|
<link>${postUrl}</link>
|
|
33275
|
-
<guid>${postUrl}</guid>
|
|
33276
|
-
<pubDate>${pubDate}</pubDate
|
|
33277
|
-
|
|
33270
|
+
<guid isPermaLink="true">${postUrl}</guid>
|
|
33271
|
+
<pubDate>${pubDate}</pubDate>`;
|
|
33272
|
+
if (config.authorEmail && config.authorName) {
|
|
33273
|
+
itemXml += `
|
|
33274
|
+
<author>${config.authorEmail} (${config.authorName})</author>`;
|
|
33275
|
+
} else if (config.authorEmail) {
|
|
33276
|
+
itemXml += `
|
|
33277
|
+
<author>${config.authorEmail}</author>`;
|
|
33278
|
+
}
|
|
33279
|
+
itemXml += `
|
|
33280
|
+
<description><![CDATA[${post.excerpt}]]></description>`;
|
|
33281
|
+
if (post.tags.length > 0) {
|
|
33282
|
+
itemXml += `
|
|
33283
|
+
${categoryTags}`;
|
|
33284
|
+
}
|
|
33285
|
+
itemXml += `
|
|
33286
|
+
<content:encoded><![CDATA[${post.html}]]></content:encoded>`;
|
|
33287
|
+
if (featuredImage) {
|
|
33288
|
+
const absoluteImageUrl = featuredImage.startsWith("http") ? featuredImage : `${config.baseUrl}${featuredImage}`;
|
|
33289
|
+
itemXml += `
|
|
33290
|
+
<media:thumbnail url="${this.escapeXml(absoluteImageUrl)}" />`;
|
|
33291
|
+
}
|
|
33292
|
+
itemXml += `
|
|
33278
33293
|
</item>`;
|
|
33294
|
+
return itemXml;
|
|
33279
33295
|
}).join(`
|
|
33280
33296
|
`);
|
|
33281
|
-
|
|
33282
|
-
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
33283
|
-
<channel>
|
|
33297
|
+
let channelXml = ` <channel>
|
|
33284
33298
|
<title><![CDATA[${config.title}]]></title>
|
|
33285
|
-
<description><![CDATA[${config.description}]]></description>
|
|
33286
33299
|
<link>${config.baseUrl}</link>
|
|
33287
|
-
<
|
|
33288
|
-
|
|
33300
|
+
<description><![CDATA[${config.description}]]></description>`;
|
|
33301
|
+
const language = config.rssLanguage || "en-US";
|
|
33302
|
+
channelXml += `
|
|
33303
|
+
<language>${language}</language>`;
|
|
33304
|
+
if (config.authorEmail && config.authorName) {
|
|
33305
|
+
channelXml += `
|
|
33306
|
+
<managingEditor>${config.authorEmail} (${config.authorName})</managingEditor>`;
|
|
33307
|
+
} else if (config.authorEmail) {
|
|
33308
|
+
channelXml += `
|
|
33309
|
+
<managingEditor>${config.authorEmail}</managingEditor>`;
|
|
33310
|
+
}
|
|
33311
|
+
if (config.webMaster) {
|
|
33312
|
+
channelXml += `
|
|
33313
|
+
<webMaster>${config.webMaster}</webMaster>`;
|
|
33314
|
+
}
|
|
33315
|
+
if (config.copyright) {
|
|
33316
|
+
channelXml += `
|
|
33317
|
+
<copyright><![CDATA[${config.copyright}]]></copyright>`;
|
|
33318
|
+
}
|
|
33319
|
+
channelXml += `
|
|
33320
|
+
<pubDate>${this.formatRSSDate(latestPostDate)}</pubDate>
|
|
33321
|
+
<lastBuildDate>${lastBuildDate}</lastBuildDate>
|
|
33322
|
+
<atom:link href="${config.baseUrl}/feed.xml" rel="self" type="application/rss+xml" />`;
|
|
33323
|
+
const rssContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
33324
|
+
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/">
|
|
33325
|
+
${channelXml}
|
|
33289
33326
|
${rssItems}
|
|
33290
33327
|
</channel>
|
|
33291
33328
|
</rss>`;
|
|
@@ -33476,7 +33513,12 @@ async function handleGenerateCommand(options2, deps = defaultDeps2) {
|
|
|
33476
33513
|
deps.logger.log(`- Output directory: ${outputDir}`);
|
|
33477
33514
|
deps.logger.log(`- Templates directory: ${templatesDir}`);
|
|
33478
33515
|
const config = await deps.loadConfig(configPath);
|
|
33479
|
-
const generator = deps.createGenerator({
|
|
33516
|
+
const generator = deps.createGenerator({
|
|
33517
|
+
contentDir,
|
|
33518
|
+
outputDir,
|
|
33519
|
+
templatesDir,
|
|
33520
|
+
config
|
|
33521
|
+
});
|
|
33480
33522
|
await generator.initialize();
|
|
33481
33523
|
await generator.generate();
|
|
33482
33524
|
deps.logger.log("Site generation completed successfully!");
|
|
@@ -34191,56 +34233,56 @@ function getDefaultCss() {
|
|
|
34191
34233
|
}
|
|
34192
34234
|
function getSamplePost() {
|
|
34193
34235
|
return `---
|
|
34194
|
-
|
|
34195
|
-
|
|
34196
|
-
|
|
34197
|
-
|
|
34236
|
+
title: Welcome to Bunki
|
|
34237
|
+
date: ${new Date().toISOString()}
|
|
34238
|
+
tags: [getting-started, bunki]
|
|
34239
|
+
---
|
|
34198
34240
|
|
|
34199
|
-
|
|
34241
|
+
# Welcome to Your New Bunki Site
|
|
34200
34242
|
|
|
34201
|
-
|
|
34243
|
+
This is a sample blog post to help you get started with Bunki. You can edit this file or create new markdown files in the \`content\` directory.
|
|
34202
34244
|
|
|
34203
|
-
|
|
34245
|
+
## Features
|
|
34204
34246
|
|
|
34205
|
-
|
|
34206
|
-
|
|
34207
|
-
|
|
34208
|
-
|
|
34209
|
-
|
|
34210
|
-
|
|
34247
|
+
- Markdown support with frontmatter
|
|
34248
|
+
- Syntax highlighting for code blocks
|
|
34249
|
+
- Tag-based organization
|
|
34250
|
+
- Pagination for post listings
|
|
34251
|
+
- RSS feed generation
|
|
34252
|
+
- Sitemap generation
|
|
34211
34253
|
|
|
34212
|
-
|
|
34254
|
+
## Adding Content
|
|
34213
34255
|
|
|
34214
|
-
|
|
34256
|
+
Create new markdown files in the \`content\` directory with frontmatter like this:
|
|
34215
34257
|
|
|
34216
|
-
|
|
34217
|
-
|
|
34218
|
-
|
|
34219
|
-
|
|
34220
|
-
|
|
34221
|
-
|
|
34258
|
+
\`\`\`markdown
|
|
34259
|
+
---
|
|
34260
|
+
title: Your Post Title
|
|
34261
|
+
date: 2025-01-01T12:00:00Z
|
|
34262
|
+
tags: [tag1, tag2]
|
|
34263
|
+
---
|
|
34222
34264
|
|
|
34223
|
-
|
|
34224
|
-
|
|
34265
|
+
Your post content goes here...
|
|
34266
|
+
\`\`\`
|
|
34225
34267
|
|
|
34226
|
-
|
|
34268
|
+
## Code Highlighting
|
|
34227
34269
|
|
|
34228
|
-
|
|
34270
|
+
Bunki supports syntax highlighting for code blocks:
|
|
34229
34271
|
|
|
34230
|
-
|
|
34231
|
-
|
|
34232
|
-
|
|
34233
|
-
|
|
34234
|
-
|
|
34272
|
+
\`\`\`javascript
|
|
34273
|
+
function hello() {
|
|
34274
|
+
console.log('Hello, world!');
|
|
34275
|
+
}
|
|
34276
|
+
\`\`\`
|
|
34235
34277
|
|
|
34236
|
-
|
|
34278
|
+
## Next Steps
|
|
34237
34279
|
|
|
34238
|
-
|
|
34239
|
-
|
|
34240
|
-
|
|
34241
|
-
|
|
34242
|
-
|
|
34243
|
-
|
|
34280
|
+
1. Edit the site configuration in \`bunki.config.ts\`
|
|
34281
|
+
2. Create your own templates in the \`templates\` directory
|
|
34282
|
+
3. Add more blog posts in the \`content\` directory
|
|
34283
|
+
4. Run \`bunki generate\` to build your site
|
|
34284
|
+
5. Run \`bunki serve\` to preview your site locally
|
|
34285
|
+
`;
|
|
34244
34286
|
}
|
|
34245
34287
|
|
|
34246
34288
|
// src/cli/commands/new-post.ts
|
|
@@ -34440,6 +34482,8 @@ registerServeCommand(program2);
|
|
|
34440
34482
|
registerCssCommand(program2);
|
|
34441
34483
|
registerImagesPushCommand(program2);
|
|
34442
34484
|
program2.name("bunki").description("An opinionated static site generator built with Bun").version("0.5.3");
|
|
34443
|
-
|
|
34485
|
+
var currentFile = import.meta.url.replace("file://", "");
|
|
34486
|
+
var mainFile = Bun.main;
|
|
34487
|
+
if (currentFile === mainFile || currentFile.endsWith(mainFile)) {
|
|
34444
34488
|
program2.parse(Bun.argv);
|
|
34445
34489
|
}
|