bunki 0.17.1 → 0.18.0
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 +144 -8
- package/dist/cli.js +427 -448
- package/dist/index.js +1 -2
- package/package.json +1 -1
- package/dist/cli/commands/templates/archive-njk.d.ts +0 -4
- package/dist/cli/commands/templates/base-njk.d.ts +0 -4
- package/dist/cli/commands/templates/default-css.d.ts +0 -4
- package/dist/cli/commands/templates/index-njk.d.ts +0 -4
- package/dist/cli/commands/templates/index.d.ts +0 -14
- package/dist/cli/commands/templates/post-njk.d.ts +0 -4
- package/dist/cli/commands/templates/sample-post.d.ts +0 -4
- package/dist/cli/commands/templates/tag-njk.d.ts +0 -4
- package/dist/cli/commands/templates/tags-njk.d.ts +0 -4
package/README.md
CHANGED
|
@@ -799,16 +799,134 @@ export S3_PUBLIC_URL="https://img.example.com"
|
|
|
799
799
|
- WebP for photos
|
|
800
800
|
- SVG for icons/graphics
|
|
801
801
|
|
|
802
|
+
## Incremental Builds
|
|
803
|
+
|
|
804
|
+
Bunki supports incremental builds for significantly faster rebuild times during development. When enabled, only changed content is reprocessed while unchanged files are loaded from cache.
|
|
805
|
+
|
|
806
|
+
### Performance Impact
|
|
807
|
+
|
|
808
|
+
**Large site example (455 posts):**
|
|
809
|
+
- Full build: 3,128ms
|
|
810
|
+
- Incremental build (no changes): 985ms (**3.2x faster**)
|
|
811
|
+
|
|
812
|
+
**Speedup breakdown:**
|
|
813
|
+
- Markdown parsing: 1,202ms → 55ms (**22x faster**)
|
|
814
|
+
- CSS processing: 1,024ms → 1ms (**1024x faster**)
|
|
815
|
+
- Overall: **68% faster builds**
|
|
816
|
+
|
|
817
|
+
### Usage
|
|
818
|
+
|
|
819
|
+
```bash
|
|
820
|
+
# Enable incremental builds
|
|
821
|
+
bunki generate --incremental
|
|
822
|
+
|
|
823
|
+
# First run (creates cache)
|
|
824
|
+
# Config changed, full rebuild required
|
|
825
|
+
# Total: 3,128ms (same as normal build)
|
|
826
|
+
|
|
827
|
+
# Subsequent runs (no changes)
|
|
828
|
+
# No content changes detected, using cached posts
|
|
829
|
+
# ✨ Loaded 455 posts from cache (0ms parsing)
|
|
830
|
+
# ⏭️ Skipping CSS (unchanged)
|
|
831
|
+
# Total: 985ms (3.2x faster!)
|
|
832
|
+
|
|
833
|
+
# When one file changes
|
|
834
|
+
# 📦 Incremental build: 1/456 files changed (~2730ms saved)
|
|
835
|
+
# Parsed: 1 new/changed, loaded: 455 from cache
|
|
836
|
+
# Total: ~1,000ms
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
### How It Works
|
|
840
|
+
|
|
841
|
+
1. **First build** creates `.bunki-cache.json` with:
|
|
842
|
+
- File hashes and modification times
|
|
843
|
+
- Parsed post data (title, content, metadata)
|
|
844
|
+
- CSS file checksums
|
|
845
|
+
- Config file hash
|
|
846
|
+
|
|
847
|
+
2. **Subsequent builds** detect changes by comparing:
|
|
848
|
+
- Config file hash (triggers full rebuild if changed)
|
|
849
|
+
- Markdown file hashes/mtimes
|
|
850
|
+
- CSS file hashes
|
|
851
|
+
|
|
852
|
+
3. **Selective processing**:
|
|
853
|
+
- Only parse changed markdown files
|
|
854
|
+
- Load unchanged posts from cache
|
|
855
|
+
- Skip CSS if unchanged
|
|
856
|
+
- Regenerate all pages (currently not selective)
|
|
857
|
+
|
|
858
|
+
### Cache Management
|
|
859
|
+
|
|
860
|
+
The cache is stored in `.bunki-cache.json` at your project root:
|
|
861
|
+
|
|
862
|
+
```bash
|
|
863
|
+
# View cache status
|
|
864
|
+
cat .bunki-cache.json | jq '.version, .configHash'
|
|
865
|
+
|
|
866
|
+
# Clear cache (force full rebuild)
|
|
867
|
+
rm .bunki-cache.json
|
|
868
|
+
|
|
869
|
+
# Exclude from version control
|
|
870
|
+
echo ".bunki-cache.json" >> .gitignore
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
### When to Use
|
|
874
|
+
|
|
875
|
+
**Recommended for:**
|
|
876
|
+
- Large sites (100+ posts)
|
|
877
|
+
- Development workflow with frequent rebuilds
|
|
878
|
+
- Sites with slow CSS processing (Tailwind, PostCSS)
|
|
879
|
+
|
|
880
|
+
**Not needed for:**
|
|
881
|
+
- Small sites (<50 posts) - already fast enough
|
|
882
|
+
- CI/CD builds - prefer clean full builds
|
|
883
|
+
- Production deployments - always use full builds
|
|
884
|
+
|
|
885
|
+
### Cache Format
|
|
886
|
+
|
|
887
|
+
Version 2.0.0 cache structure:
|
|
888
|
+
|
|
889
|
+
```json
|
|
890
|
+
{
|
|
891
|
+
"version": "2.0.0",
|
|
892
|
+
"configHash": "abc123",
|
|
893
|
+
"files": {
|
|
894
|
+
"/path/to/post.md": {
|
|
895
|
+
"hash": "def456",
|
|
896
|
+
"mtime": 1771720766417,
|
|
897
|
+
"post": {
|
|
898
|
+
"title": "Post Title",
|
|
899
|
+
"date": "2024-01-01",
|
|
900
|
+
"content": "...",
|
|
901
|
+
"html": "..."
|
|
902
|
+
}
|
|
903
|
+
},
|
|
904
|
+
"/path/to/main.css": {
|
|
905
|
+
"hash": "ghi789",
|
|
906
|
+
"mtime": 1771720800000
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
### Future Optimizations
|
|
913
|
+
|
|
914
|
+
Current implementation (v0.18.0) optimizes parsing and CSS processing. Future versions may add:
|
|
915
|
+
- Selective page regeneration (only rebuild changed posts)
|
|
916
|
+
- Incremental sitemap/RSS updates
|
|
917
|
+
- Smart index page regeneration
|
|
918
|
+
|
|
802
919
|
## CLI Commands
|
|
803
920
|
|
|
804
921
|
```bash
|
|
805
|
-
bunki init [--config FILE]
|
|
806
|
-
bunki new <TITLE> [--tags TAG1,TAG2]
|
|
807
|
-
bunki generate [--config FILE]
|
|
808
|
-
bunki
|
|
809
|
-
bunki
|
|
810
|
-
bunki
|
|
811
|
-
bunki
|
|
922
|
+
bunki init [--config FILE] # Initialize new site
|
|
923
|
+
bunki new <TITLE> [--tags TAG1,TAG2] # Create new post
|
|
924
|
+
bunki generate [--config FILE] # Build static site (full)
|
|
925
|
+
bunki generate --incremental # Build with caching (3x faster)
|
|
926
|
+
bunki validate [--config FILE] # Validate frontmatter
|
|
927
|
+
bunki serve [--port 3000] # Start dev server
|
|
928
|
+
bunki css [--watch] # Process CSS
|
|
929
|
+
bunki images:push [--domain DOMAIN] # Upload images to cloud
|
|
812
930
|
```
|
|
813
931
|
|
|
814
932
|
## Output Structure
|
|
@@ -895,6 +1013,7 @@ site-generator.ts (orchestrator)
|
|
|
895
1013
|
- **Frontmatter Validation**: Automatic validation of business location data with clear error messages
|
|
896
1014
|
- **Security**: XSS protection, sanitized HTML, link hardening
|
|
897
1015
|
- **High Performance**:
|
|
1016
|
+
- **Incremental builds** with smart caching (3.2x faster, 68% speedup)
|
|
898
1017
|
- Parallel page generation (40-60% faster builds)
|
|
899
1018
|
- Batched post processing (10x faster for 100+ posts)
|
|
900
1019
|
- Pre-compiled regex patterns (2-3x faster parsing)
|
|
@@ -973,7 +1092,24 @@ bunki/
|
|
|
973
1092
|
|
|
974
1093
|
## Changelog
|
|
975
1094
|
|
|
976
|
-
### v0.
|
|
1095
|
+
### v0.18.0 (Current)
|
|
1096
|
+
|
|
1097
|
+
- **Incremental Builds**: Smart caching for 3.2x faster development builds
|
|
1098
|
+
- File change detection using content hashing and modification times
|
|
1099
|
+
- Selective markdown parsing (only parse changed files)
|
|
1100
|
+
- CSS caching (skip processing if unchanged)
|
|
1101
|
+
- Cache format v2.0.0 stores full parsed post data
|
|
1102
|
+
- Automatic config change detection triggers full rebuilds
|
|
1103
|
+
- `.bunki-cache.json` stores file hashes, mtimes, and parsed posts
|
|
1104
|
+
- **Performance Results** (455 posts):
|
|
1105
|
+
- Full build: 3,128ms
|
|
1106
|
+
- Incremental (no changes): 985ms (68% faster)
|
|
1107
|
+
- Markdown parsing: 1,202ms → 55ms (22x faster)
|
|
1108
|
+
- CSS processing: 1,024ms → 1ms (1024x faster)
|
|
1109
|
+
- **CLI Enhancement**: New `--incremental` flag for `bunki generate`
|
|
1110
|
+
- **Code Cleanup**: Removed unused imports, reverted template extraction
|
|
1111
|
+
|
|
1112
|
+
### v0.17.0
|
|
977
1113
|
|
|
978
1114
|
- **Major Architecture Refactoring**: Modular design with single responsibility modules
|
|
979
1115
|
- Split `site-generator.ts` from 957 to 282 lines (-70%)
|
package/dist/cli.js
CHANGED
|
@@ -34387,8 +34387,7 @@ class SiteGenerator {
|
|
|
34387
34387
|
this.metrics = new MetricsCollector;
|
|
34388
34388
|
const env = import_nunjucks2.default.configure(this.options.templatesDir, {
|
|
34389
34389
|
autoescape: true,
|
|
34390
|
-
watch: false
|
|
34391
|
-
noCache: false
|
|
34390
|
+
watch: false
|
|
34392
34391
|
});
|
|
34393
34392
|
env.addFilter("date", (date, format) => {
|
|
34394
34393
|
const d2 = toPacificTime(date);
|
|
@@ -34917,437 +34916,475 @@ function registerImagesPushCommand(program2) {
|
|
|
34917
34916
|
|
|
34918
34917
|
// src/cli/commands/init.ts
|
|
34919
34918
|
import path13 from "path";
|
|
34919
|
+
var defaultDependencies = {
|
|
34920
|
+
createDefaultConfig,
|
|
34921
|
+
ensureDir,
|
|
34922
|
+
writeFile: (filePath, data) => Bun.write(filePath, data),
|
|
34923
|
+
logger: console,
|
|
34924
|
+
exit: (code) => process.exit(code)
|
|
34925
|
+
};
|
|
34926
|
+
async function handleInitCommand(options2, deps = defaultDependencies) {
|
|
34927
|
+
try {
|
|
34928
|
+
const configPath = path13.resolve(options2.config);
|
|
34929
|
+
const configCreated = await deps.createDefaultConfig(configPath);
|
|
34930
|
+
if (!configCreated) {
|
|
34931
|
+
deps.logger.log(`
|
|
34932
|
+
Skipped initialization because the config file already exists`);
|
|
34933
|
+
return;
|
|
34934
|
+
}
|
|
34935
|
+
deps.logger.log("Creating directory structure...");
|
|
34936
|
+
const baseDir = process.cwd();
|
|
34937
|
+
const contentDir = path13.join(baseDir, "content");
|
|
34938
|
+
const templatesDir = path13.join(baseDir, "templates");
|
|
34939
|
+
const stylesDir = path13.join(templatesDir, "styles");
|
|
34940
|
+
const publicDir = path13.join(baseDir, "public");
|
|
34941
|
+
await deps.ensureDir(contentDir);
|
|
34942
|
+
await deps.ensureDir(templatesDir);
|
|
34943
|
+
await deps.ensureDir(stylesDir);
|
|
34944
|
+
await deps.ensureDir(publicDir);
|
|
34945
|
+
for (const [filename, content] of Object.entries(getDefaultTemplates())) {
|
|
34946
|
+
await deps.writeFile(path13.join(templatesDir, filename), content);
|
|
34947
|
+
}
|
|
34948
|
+
await deps.writeFile(path13.join(stylesDir, "main.css"), getDefaultCss());
|
|
34949
|
+
await deps.writeFile(path13.join(contentDir, "welcome.md"), getSamplePost());
|
|
34950
|
+
deps.logger.log(`
|
|
34951
|
+
Initialization complete! Here are the next steps:`);
|
|
34952
|
+
deps.logger.log("1. Edit bunki.config.ts to configure your site");
|
|
34953
|
+
deps.logger.log("2. Add markdown files to the content directory");
|
|
34954
|
+
deps.logger.log('3. Run "bunki generate" to build your site');
|
|
34955
|
+
deps.logger.log('4. Run "bunki serve" to preview your site locally');
|
|
34956
|
+
} catch (error) {
|
|
34957
|
+
deps.logger.error("Error initializing site:", error);
|
|
34958
|
+
deps.exit(1);
|
|
34959
|
+
}
|
|
34960
|
+
}
|
|
34961
|
+
function registerInitCommand(program2, deps = defaultDependencies) {
|
|
34962
|
+
return program2.command("init").description("Initialize a new site with default structure").option("-c, --config <file>", "Path to config file", "bunki.config.ts").action(async (options2) => {
|
|
34963
|
+
await handleInitCommand(options2, deps);
|
|
34964
|
+
});
|
|
34965
|
+
}
|
|
34966
|
+
function getDefaultTemplates() {
|
|
34967
|
+
return {
|
|
34968
|
+
"base.njk": String.raw`<!DOCTYPE html>
|
|
34969
|
+
<html lang="en">
|
|
34970
|
+
<head>
|
|
34971
|
+
<meta charset="UTF-8">
|
|
34972
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
34973
|
+
<title>{% block title %}{{ site.title }}{% endblock %}</title>
|
|
34974
|
+
<meta name="description" content="{% block description %}{{ site.description }}{% endblock %}">
|
|
34975
|
+
<link rel="stylesheet" href="/css/style.css">
|
|
34976
|
+
{% block head %}{% endblock %}
|
|
34977
|
+
</head>
|
|
34978
|
+
<body>
|
|
34979
|
+
<header>
|
|
34980
|
+
<div class="container">
|
|
34981
|
+
<h1><a href="/">{{ site.title }}</a></h1>
|
|
34982
|
+
<nav>
|
|
34983
|
+
<ul>
|
|
34984
|
+
<li><a href="/">Home</a></li>
|
|
34985
|
+
<li><a href="/tags/">Tags</a></li>
|
|
34986
|
+
</ul>
|
|
34987
|
+
</nav>
|
|
34988
|
+
</div>
|
|
34989
|
+
</header>
|
|
34920
34990
|
|
|
34921
|
-
|
|
34922
|
-
|
|
34923
|
-
|
|
34924
|
-
|
|
34925
|
-
|
|
34926
|
-
|
|
34927
|
-
|
|
34928
|
-
|
|
34929
|
-
|
|
34930
|
-
|
|
34931
|
-
</
|
|
34932
|
-
|
|
34933
|
-
|
|
34934
|
-
|
|
34935
|
-
|
|
34936
|
-
|
|
34937
|
-
|
|
34938
|
-
|
|
34939
|
-
|
|
34940
|
-
|
|
34941
|
-
|
|
34942
|
-
|
|
34943
|
-
|
|
34944
|
-
|
|
34945
|
-
|
|
34946
|
-
|
|
34947
|
-
|
|
34948
|
-
|
|
34949
|
-
|
|
34950
|
-
|
|
34951
|
-
|
|
34952
|
-
|
|
34953
|
-
|
|
34954
|
-
</
|
|
34955
|
-
|
|
34956
|
-
|
|
34957
|
-
|
|
34958
|
-
|
|
34959
|
-
|
|
34960
|
-
{%
|
|
34961
|
-
|
|
34962
|
-
|
|
34963
|
-
|
|
34964
|
-
|
|
34965
|
-
|
|
34966
|
-
|
|
34967
|
-
|
|
34968
|
-
<
|
|
34969
|
-
|
|
34970
|
-
|
|
34971
|
-
|
|
34972
|
-
|
|
34973
|
-
<a href="/tags/{{ post.tagSlugs[tag] }}/">{{ tag }}</a>{% if not loop.last %}, {% endif %}
|
|
34974
|
-
{% endfor %}
|
|
34975
|
-
</span>
|
|
34976
|
-
{% endif %}
|
|
34977
|
-
</div>
|
|
34978
|
-
<div class="post-excerpt">{{ post.excerpt }}</div>
|
|
34979
|
-
<a href="{{ post.url }}" class="read-more">Read more \u2192</a>
|
|
34980
|
-
</article>
|
|
34981
|
-
{% endfor %}
|
|
34982
|
-
</div>
|
|
34983
|
-
|
|
34984
|
-
{% if pagination.totalPages > 1 %}
|
|
34985
|
-
<nav class="pagination">
|
|
34986
|
-
{% if pagination.hasPrevPage %}
|
|
34987
|
-
<a href="{{ pagination.pagePath }}{% if pagination.prevPage > 1 %}page/{{ pagination.prevPage }}/{% endif %}" class="prev">\u2190 Previous</a>
|
|
34988
|
-
{% endif %}
|
|
34989
|
-
|
|
34990
|
-
{% if pagination.hasNextPage %}
|
|
34991
|
-
<a href="{{ pagination.pagePath }}page/{{ pagination.nextPage }}/" class="next">Next \u2192</a>
|
|
34992
|
-
{% endif %}
|
|
34993
|
-
|
|
34994
|
-
<span class="page-info">Page {{ pagination.currentPage }} of {{ pagination.totalPages }}</span>
|
|
34995
|
-
</nav>
|
|
34991
|
+
<main class="container">
|
|
34992
|
+
{% block content %}{% endblock %}
|
|
34993
|
+
</main>
|
|
34994
|
+
|
|
34995
|
+
<footer>
|
|
34996
|
+
<div class="container">
|
|
34997
|
+
<p>© {{ "now" | date("YYYY") }} {{ site.title }}</p>
|
|
34998
|
+
</div>
|
|
34999
|
+
</footer>
|
|
35000
|
+
</body>
|
|
35001
|
+
</html>`,
|
|
35002
|
+
"index.njk": String.raw`{% extends "base.njk" %}
|
|
35003
|
+
|
|
35004
|
+
{% block content %}
|
|
35005
|
+
<h1>Latest Posts</h1>
|
|
35006
|
+
|
|
35007
|
+
{% if posts.length > 0 %}
|
|
35008
|
+
<div class="posts">
|
|
35009
|
+
{% for post in posts %}
|
|
35010
|
+
<article class="post-card">
|
|
35011
|
+
<h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
|
|
35012
|
+
<div class="post-meta">
|
|
35013
|
+
<time datetime="{{ post.date }}">{{ post.date | date("MMMM D, YYYY") }}</time>
|
|
35014
|
+
{% if post.tags.length > 0 %}
|
|
35015
|
+
<span class="tags">
|
|
35016
|
+
{% for tag in post.tags %}
|
|
35017
|
+
<a href="/tags/{{ post.tagSlugs[tag] }}/">{{ tag }}</a>{% if not loop.last %}, {% endif %}
|
|
35018
|
+
{% endfor %}
|
|
35019
|
+
</span>
|
|
35020
|
+
{% endif %}
|
|
35021
|
+
</div>
|
|
35022
|
+
<div class="post-excerpt">{{ post.excerpt }}</div>
|
|
35023
|
+
<a href="{{ post.url }}" class="read-more">Read more \u2192</a>
|
|
35024
|
+
</article>
|
|
35025
|
+
{% endfor %}
|
|
35026
|
+
</div>
|
|
35027
|
+
|
|
35028
|
+
{% if pagination.totalPages > 1 %}
|
|
35029
|
+
<nav class="pagination">
|
|
35030
|
+
{% if pagination.hasPrevPage %}
|
|
35031
|
+
<a href="{{ pagination.pagePath }}{% if pagination.prevPage > 1 %}page/{{ pagination.prevPage }}/{% endif %}" class="prev">\u2190 Previous</a>
|
|
35032
|
+
{% endif %}
|
|
35033
|
+
|
|
35034
|
+
{% if pagination.hasNextPage %}
|
|
35035
|
+
<a href="{{ pagination.pagePath }}page/{{ pagination.nextPage }}/" class="next">Next \u2192</a>
|
|
35036
|
+
{% endif %}
|
|
35037
|
+
|
|
35038
|
+
<span class="page-info">Page {{ pagination.currentPage }} of {{ pagination.totalPages }}</span>
|
|
35039
|
+
</nav>
|
|
35040
|
+
{% endif %}
|
|
35041
|
+
{% else %}
|
|
35042
|
+
<p>No posts yet!</p>
|
|
34996
35043
|
{% endif %}
|
|
34997
|
-
{%
|
|
34998
|
-
|
|
34999
|
-
|
|
35000
|
-
{% endblock %}
|
|
35001
|
-
|
|
35002
|
-
|
|
35003
|
-
|
|
35004
|
-
|
|
35005
|
-
|
|
35006
|
-
{
|
|
35007
|
-
|
|
35008
|
-
{
|
|
35009
|
-
|
|
35010
|
-
|
|
35011
|
-
|
|
35012
|
-
|
|
35013
|
-
|
|
35014
|
-
|
|
35015
|
-
|
|
35016
|
-
|
|
35017
|
-
|
|
35018
|
-
|
|
35019
|
-
|
|
35020
|
-
{
|
|
35044
|
+
{% endblock %}`,
|
|
35045
|
+
"post.njk": String.raw`{% extends "base.njk" %}
|
|
35046
|
+
|
|
35047
|
+
{% block title %}{{ post.title }} | {{ site.title }}{% endblock %}
|
|
35048
|
+
{% block description %}{{ post.excerpt }}{% endblock %}
|
|
35049
|
+
|
|
35050
|
+
{% block content %}
|
|
35051
|
+
<article class="post">
|
|
35052
|
+
<header class="post-header">
|
|
35053
|
+
<h1>{{ post.title }}</h1>
|
|
35054
|
+
<div class="post-meta">
|
|
35055
|
+
<time datetime="{{ post.date }}">{{ post.date | date("MMMM D, YYYY") }}</time>
|
|
35056
|
+
{% if post.tags.length > 0 %}
|
|
35057
|
+
<span class="tags">
|
|
35058
|
+
{% for tag in post.tags %}
|
|
35059
|
+
<a href="/tags/{{ post.tagSlugs[tag] }}/">{{ tag }}</a>{% if not loop.last %}, {% endif %}
|
|
35060
|
+
{% endfor %}
|
|
35061
|
+
</span>
|
|
35062
|
+
{% endif %}
|
|
35063
|
+
</div>
|
|
35064
|
+
</header>
|
|
35065
|
+
|
|
35066
|
+
<div class="post-content">
|
|
35067
|
+
{{ post.html | safe }}
|
|
35021
35068
|
</div>
|
|
35022
|
-
</
|
|
35069
|
+
</article>
|
|
35070
|
+
{% endblock %}`,
|
|
35071
|
+
"tag.njk": String.raw`{% extends "base.njk" %}
|
|
35072
|
+
|
|
35073
|
+
{% block title %}{{ tag.name }} | {{ site.title }}{% endblock %}
|
|
35074
|
+
{% block description %}Posts tagged with {{ tag.name }} on {{ site.title }}{% endblock %}
|
|
35023
35075
|
|
|
35024
|
-
|
|
35025
|
-
|
|
35026
|
-
|
|
35027
|
-
|
|
35028
|
-
{
|
|
35029
|
-
|
|
35030
|
-
// src/cli/commands/templates/tag-njk.ts
|
|
35031
|
-
var tagNjk = String.raw`{% extends "base.njk" %}
|
|
35032
|
-
|
|
35033
|
-
{% block title %}{{ tag.name }} | {{ site.title }}{% endblock %}
|
|
35034
|
-
{% block description %}Posts tagged with {{ tag.name }} on {{ site.title }}{% endblock %}
|
|
35035
|
-
|
|
35036
|
-
{% block content %}
|
|
35037
|
-
<h1>Posts tagged "{{ tag.name }}"</h1>
|
|
35038
|
-
|
|
35039
|
-
{% if tag.description %}
|
|
35040
|
-
<div class="tag-description">{{ tag.description }}</div>
|
|
35041
|
-
{% endif %}
|
|
35042
|
-
|
|
35043
|
-
{% if tag.posts.length > 0 %}
|
|
35044
|
-
<div class="posts">
|
|
35045
|
-
{% for post in tag.posts %}
|
|
35046
|
-
<article class="post-card">
|
|
35047
|
-
<h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
|
|
35048
|
-
<div class="post-meta">
|
|
35049
|
-
<time datetime="{{ post.date }}">{{ post.date | date("MMMM D, YYYY") }}</time>
|
|
35050
|
-
</div>
|
|
35051
|
-
<div class="post-excerpt">{{ post.excerpt }}</div>
|
|
35052
|
-
<a href="{{ post.url }}" class="read-more">Read more \u2192</a>
|
|
35053
|
-
</article>
|
|
35054
|
-
{% endfor %}
|
|
35055
|
-
</div>
|
|
35056
|
-
|
|
35057
|
-
{% if pagination.totalPages > 1 %}
|
|
35058
|
-
<nav class="pagination">
|
|
35059
|
-
{% if pagination.hasPrevPage %}
|
|
35060
|
-
<a href="{{ pagination.pagePath }}{% if pagination.prevPage > 1 %}page/{{ pagination.prevPage }}/{% endif %}" class="prev">\u2190 Previous</a>
|
|
35061
|
-
{% endif %}
|
|
35062
|
-
|
|
35063
|
-
{% if pagination.hasNextPage %}
|
|
35064
|
-
<a href="{{ pagination.pagePath }}page/{{ pagination.nextPage }}/" class="next">Next \u2192</a>
|
|
35065
|
-
{% endif %}
|
|
35066
|
-
|
|
35067
|
-
<span class="page-info">Page {{ pagination.currentPage }} of {{ pagination.totalPages }}</span>
|
|
35068
|
-
</nav>
|
|
35076
|
+
{% block content %}
|
|
35077
|
+
<h1>Posts tagged "{{ tag.name }}"</h1>
|
|
35078
|
+
|
|
35079
|
+
{% if tag.description %}
|
|
35080
|
+
<div class="tag-description">{{ tag.description }}</div>
|
|
35069
35081
|
{% endif %}
|
|
35070
|
-
|
|
35071
|
-
|
|
35072
|
-
|
|
35073
|
-
{%
|
|
35074
|
-
|
|
35075
|
-
|
|
35076
|
-
|
|
35077
|
-
|
|
35078
|
-
|
|
35079
|
-
|
|
35080
|
-
|
|
35081
|
-
|
|
35082
|
-
|
|
35083
|
-
|
|
35084
|
-
|
|
35085
|
-
|
|
35086
|
-
|
|
35087
|
-
|
|
35088
|
-
|
|
35089
|
-
|
|
35090
|
-
|
|
35091
|
-
|
|
35082
|
+
|
|
35083
|
+
{% if tag.posts.length > 0 %}
|
|
35084
|
+
<div class="posts">
|
|
35085
|
+
{% for post in tag.posts %}
|
|
35086
|
+
<article class="post-card">
|
|
35087
|
+
<h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
|
|
35088
|
+
<div class="post-meta">
|
|
35089
|
+
<time datetime="{{ post.date }}">{{ post.date | date("MMMM D, YYYY") }}</time>
|
|
35090
|
+
</div>
|
|
35091
|
+
<div class="post-excerpt">{{ post.excerpt }}</div>
|
|
35092
|
+
<a href="{{ post.url }}" class="read-more">Read more \u2192</a>
|
|
35093
|
+
</article>
|
|
35094
|
+
{% endfor %}
|
|
35095
|
+
</div>
|
|
35096
|
+
|
|
35097
|
+
{% if pagination.totalPages > 1 %}
|
|
35098
|
+
<nav class="pagination">
|
|
35099
|
+
{% if pagination.hasPrevPage %}
|
|
35100
|
+
<a href="{{ pagination.pagePath }}{% if pagination.prevPage > 1 %}page/{{ pagination.prevPage }}/{% endif %}" class="prev">\u2190 Previous</a>
|
|
35101
|
+
{% endif %}
|
|
35102
|
+
|
|
35103
|
+
{% if pagination.hasNextPage %}
|
|
35104
|
+
<a href="{{ pagination.pagePath }}page/{{ pagination.nextPage }}/" class="next">Next \u2192</a>
|
|
35092
35105
|
{% endif %}
|
|
35093
|
-
|
|
35094
|
-
|
|
35095
|
-
|
|
35096
|
-
|
|
35097
|
-
|
|
35098
|
-
|
|
35099
|
-
{%
|
|
35100
|
-
|
|
35101
|
-
|
|
35102
|
-
|
|
35103
|
-
|
|
35104
|
-
{% block
|
|
35105
|
-
|
|
35106
|
-
|
|
35107
|
-
|
|
35108
|
-
|
|
35109
|
-
|
|
35110
|
-
|
|
35111
|
-
|
|
35112
|
-
|
|
35113
|
-
|
|
35114
|
-
|
|
35115
|
-
|
|
35116
|
-
|
|
35117
|
-
{% if post.tags.length > 0 %}
|
|
35118
|
-
<span class="tags">
|
|
35119
|
-
{% for tag in post.tags %}
|
|
35120
|
-
<a href="/tags/{{ post.tagSlugs[tag] }}/">{{ tag }}</a>{% if not loop.last %}, {% endif %}
|
|
35121
|
-
{% endfor %}
|
|
35122
|
-
</span>
|
|
35106
|
+
|
|
35107
|
+
<span class="page-info">Page {{ pagination.currentPage }} of {{ pagination.totalPages }}</span>
|
|
35108
|
+
</nav>
|
|
35109
|
+
{% endif %}
|
|
35110
|
+
{% else %}
|
|
35111
|
+
<p>No posts with this tag yet!</p>
|
|
35112
|
+
{% endif %}
|
|
35113
|
+
{% endblock %}`,
|
|
35114
|
+
"tags.njk": String.raw`{% extends "base.njk" %}
|
|
35115
|
+
|
|
35116
|
+
{% block title %}Tags | {{ site.title }}{% endblock %}
|
|
35117
|
+
{% block description %}Browse all tags on {{ site.title }}{% endblock %}
|
|
35118
|
+
|
|
35119
|
+
{% block content %}
|
|
35120
|
+
<h1>All Tags</h1>
|
|
35121
|
+
|
|
35122
|
+
{% if tags.length > 0 %}
|
|
35123
|
+
<ul class="tags-list">
|
|
35124
|
+
{% for tag in tags %}
|
|
35125
|
+
<li>
|
|
35126
|
+
<a href="/tags/{{ tag.slug }}/">{{ tag.name }}</a>
|
|
35127
|
+
<span class="count">({{ tag.count }})</span>
|
|
35128
|
+
{% if tag.description %}
|
|
35129
|
+
<p class="description">{{ tag.description }}</p>
|
|
35123
35130
|
{% endif %}
|
|
35124
|
-
</
|
|
35125
|
-
|
|
35126
|
-
|
|
35127
|
-
|
|
35128
|
-
|
|
35129
|
-
</div>
|
|
35130
|
-
|
|
35131
|
-
{% if pagination.totalPages > 1 %}
|
|
35132
|
-
<nav class="pagination">
|
|
35133
|
-
{% if pagination.hasPrevPage %}
|
|
35134
|
-
<a href="/{{ year }}/{% if pagination.prevPage > 1 %}page/{{ pagination.prevPage }}/{% endif %}" class="prev">\u2190 Previous</a>
|
|
35135
|
-
{% endif %}
|
|
35136
|
-
|
|
35137
|
-
{% if pagination.hasNextPage %}
|
|
35138
|
-
<a href="/{{ year }}/page/{{ pagination.nextPage }}/" class="next">Next \u2192</a>
|
|
35139
|
-
{% endif %}
|
|
35140
|
-
|
|
35141
|
-
<span class="page-info">Page {{ pagination.currentPage }} of {{ pagination.totalPages }}</span>
|
|
35142
|
-
</nav>
|
|
35131
|
+
</li>
|
|
35132
|
+
{% endfor %}
|
|
35133
|
+
</ul>
|
|
35134
|
+
{% else %}
|
|
35135
|
+
<p>No tags found!</p>
|
|
35143
35136
|
{% endif %}
|
|
35144
|
-
{%
|
|
35145
|
-
|
|
35146
|
-
|
|
35147
|
-
{% endblock %}
|
|
35148
|
-
|
|
35149
|
-
|
|
35150
|
-
|
|
35151
|
-
|
|
35152
|
-
|
|
35153
|
-
|
|
35154
|
-
|
|
35155
|
-
}
|
|
35137
|
+
{% endblock %}`,
|
|
35138
|
+
"archive.njk": String.raw`{% extends "base.njk" %}
|
|
35139
|
+
|
|
35140
|
+
{% block title %}Archive {{ year }} | {{ site.title }}{% endblock %}
|
|
35141
|
+
{% block description %}Posts from {{ year }} on {{ site.title }}{% endblock %}
|
|
35142
|
+
|
|
35143
|
+
{% block content %}
|
|
35144
|
+
<h1>Posts from {{ year }}</h1>
|
|
35145
|
+
|
|
35146
|
+
{% if posts.length > 0 %}
|
|
35147
|
+
<div class="posts">
|
|
35148
|
+
{% for post in posts %}
|
|
35149
|
+
<article class="post-card">
|
|
35150
|
+
<h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
|
|
35151
|
+
<div class="post-meta">
|
|
35152
|
+
<time datetime="{{ post.date }}">{{ post.date | date("MMMM D, YYYY") }}</time>
|
|
35153
|
+
{% if post.tags.length > 0 %}
|
|
35154
|
+
<span class="tags">
|
|
35155
|
+
{% for tag in post.tags %}
|
|
35156
|
+
<a href="/tags/{{ post.tagSlugs[tag] }}/">{{ tag }}</a>{% if not loop.last %}, {% endif %}
|
|
35157
|
+
{% endfor %}
|
|
35158
|
+
</span>
|
|
35159
|
+
{% endif %}
|
|
35160
|
+
</div>
|
|
35161
|
+
<div class="post-excerpt">{{ post.excerpt }}</div>
|
|
35162
|
+
<a href="{{ post.url }}" class="read-more">Read more \u2192</a>
|
|
35163
|
+
</article>
|
|
35164
|
+
{% endfor %}
|
|
35165
|
+
</div>
|
|
35156
35166
|
|
|
35157
|
-
|
|
35158
|
-
|
|
35159
|
-
|
|
35160
|
-
|
|
35161
|
-
|
|
35162
|
-
padding-bottom: 2rem;
|
|
35163
|
-
}
|
|
35167
|
+
{% if pagination.totalPages > 1 %}
|
|
35168
|
+
<nav class="pagination">
|
|
35169
|
+
{% if pagination.hasPrevPage %}
|
|
35170
|
+
<a href="/{{ year }}/{% if pagination.prevPage > 1 %}page/{{ pagination.prevPage }}/{% endif %}" class="prev">\u2190 Previous</a>
|
|
35171
|
+
{% endif %}
|
|
35164
35172
|
|
|
35165
|
-
|
|
35166
|
-
|
|
35167
|
-
|
|
35168
|
-
}
|
|
35173
|
+
{% if pagination.hasNextPage %}
|
|
35174
|
+
<a href="/{{ year }}/page/{{ pagination.nextPage }}/" class="next">Next \u2192</a>
|
|
35175
|
+
{% endif %}
|
|
35169
35176
|
|
|
35170
|
-
|
|
35171
|
-
|
|
35177
|
+
<span class="page-info">Page {{ pagination.currentPage }} of {{ pagination.totalPages }}</span>
|
|
35178
|
+
</nav>
|
|
35179
|
+
{% endif %}
|
|
35180
|
+
{% else %}
|
|
35181
|
+
<p>No posts from {{ year }}!</p>
|
|
35182
|
+
{% endif %}
|
|
35183
|
+
{% endblock %}`
|
|
35184
|
+
};
|
|
35172
35185
|
}
|
|
35186
|
+
function getDefaultCss() {
|
|
35187
|
+
return String.raw`/* Reset & base styles */
|
|
35188
|
+
* {
|
|
35189
|
+
margin: 0;
|
|
35190
|
+
padding: 0;
|
|
35191
|
+
box-sizing: border-box;
|
|
35192
|
+
}
|
|
35173
35193
|
|
|
35174
|
-
|
|
35175
|
-
|
|
35176
|
-
|
|
35177
|
-
|
|
35178
|
-
|
|
35194
|
+
body {
|
|
35195
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
|
35196
|
+
line-height: 1.6;
|
|
35197
|
+
color: #333;
|
|
35198
|
+
background-color: #f8f9fa;
|
|
35199
|
+
padding-bottom: 2rem;
|
|
35200
|
+
}
|
|
35179
35201
|
|
|
35180
|
-
|
|
35181
|
-
|
|
35182
|
-
|
|
35183
|
-
|
|
35184
|
-
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
35185
|
-
margin-bottom: 2rem;
|
|
35186
|
-
}
|
|
35202
|
+
a {
|
|
35203
|
+
color: #0066cc;
|
|
35204
|
+
text-decoration: none;
|
|
35205
|
+
}
|
|
35187
35206
|
|
|
35188
|
-
|
|
35189
|
-
|
|
35190
|
-
|
|
35191
|
-
}
|
|
35207
|
+
a:hover {
|
|
35208
|
+
text-decoration: underline;
|
|
35209
|
+
}
|
|
35192
35210
|
|
|
35193
|
-
|
|
35194
|
-
|
|
35195
|
-
|
|
35196
|
-
|
|
35211
|
+
.container {
|
|
35212
|
+
max-width: 800px;
|
|
35213
|
+
margin: 0 auto;
|
|
35214
|
+
padding: 0 1.5rem;
|
|
35215
|
+
}
|
|
35197
35216
|
|
|
35198
|
-
|
|
35199
|
-
|
|
35200
|
-
|
|
35217
|
+
/* Header */
|
|
35218
|
+
header {
|
|
35219
|
+
background-color: #fff;
|
|
35220
|
+
padding: 1.5rem 0;
|
|
35221
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
35222
|
+
margin-bottom: 2rem;
|
|
35223
|
+
}
|
|
35201
35224
|
|
|
35202
|
-
header
|
|
35203
|
-
|
|
35204
|
-
|
|
35205
|
-
|
|
35206
|
-
}
|
|
35225
|
+
header h1 {
|
|
35226
|
+
font-size: 1.8rem;
|
|
35227
|
+
margin: 0;
|
|
35228
|
+
}
|
|
35207
35229
|
|
|
35208
|
-
|
|
35209
|
-
|
|
35210
|
-
|
|
35211
|
-
|
|
35212
|
-
border-radius: 5px;
|
|
35213
|
-
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
35214
|
-
}
|
|
35230
|
+
header h1 a {
|
|
35231
|
+
color: #333;
|
|
35232
|
+
text-decoration: none;
|
|
35233
|
+
}
|
|
35215
35234
|
|
|
35216
|
-
|
|
35217
|
-
.
|
|
35218
|
-
|
|
35219
|
-
flex-direction: column;
|
|
35220
|
-
gap: 2rem;
|
|
35221
|
-
}
|
|
35235
|
+
header nav {
|
|
35236
|
+
margin-top: 0.5rem;
|
|
35237
|
+
}
|
|
35222
35238
|
|
|
35223
|
-
|
|
35224
|
-
|
|
35225
|
-
|
|
35226
|
-
|
|
35239
|
+
header nav ul {
|
|
35240
|
+
display: flex;
|
|
35241
|
+
list-style: none;
|
|
35242
|
+
gap: 1.5rem;
|
|
35243
|
+
}
|
|
35227
35244
|
|
|
35228
|
-
|
|
35229
|
-
|
|
35230
|
-
|
|
35245
|
+
/* Main content */
|
|
35246
|
+
main {
|
|
35247
|
+
background-color: #fff;
|
|
35248
|
+
padding: 2rem;
|
|
35249
|
+
border-radius: 5px;
|
|
35250
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
35251
|
+
}
|
|
35231
35252
|
|
|
35232
|
-
|
|
35233
|
-
|
|
35234
|
-
|
|
35253
|
+
/* Posts */
|
|
35254
|
+
.posts {
|
|
35255
|
+
display: flex;
|
|
35256
|
+
flex-direction: column;
|
|
35257
|
+
gap: 2rem;
|
|
35258
|
+
}
|
|
35235
35259
|
|
|
35236
|
-
.post-
|
|
35237
|
-
|
|
35238
|
-
|
|
35239
|
-
|
|
35240
|
-
}
|
|
35260
|
+
.post-card {
|
|
35261
|
+
border-bottom: 1px solid #eee;
|
|
35262
|
+
padding-bottom: 1.5rem;
|
|
35263
|
+
}
|
|
35241
35264
|
|
|
35242
|
-
.post-
|
|
35243
|
-
|
|
35244
|
-
}
|
|
35265
|
+
.post-card:last-child {
|
|
35266
|
+
border-bottom: none;
|
|
35267
|
+
}
|
|
35245
35268
|
|
|
35246
|
-
.
|
|
35247
|
-
|
|
35248
|
-
}
|
|
35269
|
+
.post-card h2 {
|
|
35270
|
+
margin-bottom: 0.5rem;
|
|
35271
|
+
}
|
|
35249
35272
|
|
|
35250
|
-
|
|
35251
|
-
|
|
35252
|
-
|
|
35253
|
-
|
|
35273
|
+
.post-meta {
|
|
35274
|
+
font-size: 0.9rem;
|
|
35275
|
+
color: #6c757d;
|
|
35276
|
+
margin-bottom: 1rem;
|
|
35277
|
+
}
|
|
35254
35278
|
|
|
35255
|
-
.post-
|
|
35256
|
-
|
|
35257
|
-
}
|
|
35279
|
+
.post-excerpt {
|
|
35280
|
+
margin-bottom: 1rem;
|
|
35281
|
+
}
|
|
35258
35282
|
|
|
35259
|
-
.
|
|
35260
|
-
|
|
35261
|
-
|
|
35262
|
-
.post-content blockquote {
|
|
35263
|
-
margin-bottom: 1.5rem;
|
|
35264
|
-
}
|
|
35283
|
+
.read-more {
|
|
35284
|
+
font-weight: 500;
|
|
35285
|
+
}
|
|
35265
35286
|
|
|
35266
|
-
|
|
35267
|
-
.post-
|
|
35268
|
-
|
|
35269
|
-
|
|
35270
|
-
margin-bottom: 1rem;
|
|
35271
|
-
}
|
|
35287
|
+
/* Single post */
|
|
35288
|
+
.post-header {
|
|
35289
|
+
margin-bottom: 2rem;
|
|
35290
|
+
}
|
|
35272
35291
|
|
|
35273
|
-
.post-content
|
|
35274
|
-
|
|
35275
|
-
|
|
35276
|
-
display: block;
|
|
35277
|
-
margin: 2rem auto;
|
|
35278
|
-
}
|
|
35292
|
+
.post-content {
|
|
35293
|
+
line-height: 1.8;
|
|
35294
|
+
}
|
|
35279
35295
|
|
|
35280
|
-
.post-content
|
|
35281
|
-
|
|
35282
|
-
|
|
35283
|
-
|
|
35284
|
-
|
|
35285
|
-
|
|
35286
|
-
}
|
|
35296
|
+
.post-content p,
|
|
35297
|
+
.post-content ul,
|
|
35298
|
+
.post-content ol,
|
|
35299
|
+
.post-content blockquote {
|
|
35300
|
+
margin-bottom: 1.5rem;
|
|
35301
|
+
}
|
|
35287
35302
|
|
|
35288
|
-
.post-content
|
|
35289
|
-
|
|
35290
|
-
|
|
35291
|
-
|
|
35292
|
-
|
|
35293
|
-
|
|
35294
|
-
}
|
|
35303
|
+
.post-content h2,
|
|
35304
|
+
.post-content h3,
|
|
35305
|
+
.post-content h4 {
|
|
35306
|
+
margin-top: 2rem;
|
|
35307
|
+
margin-bottom: 1rem;
|
|
35308
|
+
}
|
|
35295
35309
|
|
|
35296
|
-
.post-content
|
|
35297
|
-
|
|
35298
|
-
|
|
35299
|
-
|
|
35310
|
+
.post-content img {
|
|
35311
|
+
max-width: 100%;
|
|
35312
|
+
height: auto;
|
|
35313
|
+
display: block;
|
|
35314
|
+
margin: 2rem auto;
|
|
35315
|
+
}
|
|
35300
35316
|
|
|
35301
|
-
|
|
35302
|
-
|
|
35303
|
-
|
|
35304
|
-
|
|
35305
|
-
|
|
35317
|
+
.post-content pre {
|
|
35318
|
+
background-color: #f5f5f5;
|
|
35319
|
+
padding: 1rem;
|
|
35320
|
+
border-radius: 4px;
|
|
35321
|
+
overflow-x: auto;
|
|
35322
|
+
margin-bottom: 1.5rem;
|
|
35323
|
+
}
|
|
35306
35324
|
|
|
35307
|
-
.
|
|
35308
|
-
|
|
35309
|
-
|
|
35325
|
+
.post-content code {
|
|
35326
|
+
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
35327
|
+
font-size: 0.9em;
|
|
35328
|
+
background-color: #f5f5f5;
|
|
35329
|
+
padding: 0.2em 0.4em;
|
|
35330
|
+
border-radius: 3px;
|
|
35331
|
+
}
|
|
35310
35332
|
|
|
35311
|
-
.
|
|
35312
|
-
|
|
35313
|
-
|
|
35333
|
+
.post-content pre code {
|
|
35334
|
+
padding: 0;
|
|
35335
|
+
background-color: transparent;
|
|
35336
|
+
}
|
|
35314
35337
|
|
|
35315
|
-
|
|
35316
|
-
|
|
35317
|
-
|
|
35318
|
-
|
|
35338
|
+
/* Tags */
|
|
35339
|
+
.tags a {
|
|
35340
|
+
display: inline-block;
|
|
35341
|
+
margin-left: 0.5rem;
|
|
35342
|
+
}
|
|
35319
35343
|
|
|
35320
|
-
.tags-list
|
|
35321
|
-
|
|
35322
|
-
|
|
35323
|
-
color: #6c757d;
|
|
35324
|
-
}
|
|
35344
|
+
.tags-list {
|
|
35345
|
+
list-style: none;
|
|
35346
|
+
}
|
|
35325
35347
|
|
|
35326
|
-
|
|
35327
|
-
|
|
35328
|
-
|
|
35329
|
-
justify-content: space-between;
|
|
35330
|
-
align-items: center;
|
|
35331
|
-
margin-top: 2rem;
|
|
35332
|
-
padding-top: 1rem;
|
|
35333
|
-
border-top: 1px solid #eee;
|
|
35334
|
-
}
|
|
35348
|
+
.tags-list li {
|
|
35349
|
+
margin-bottom: 1rem;
|
|
35350
|
+
}
|
|
35335
35351
|
|
|
35336
|
-
.
|
|
35337
|
-
|
|
35338
|
-
|
|
35339
|
-
}
|
|
35352
|
+
.tags-list .count {
|
|
35353
|
+
color: #6c757d;
|
|
35354
|
+
font-size: 0.9rem;
|
|
35355
|
+
}
|
|
35340
35356
|
|
|
35341
|
-
|
|
35342
|
-
|
|
35343
|
-
|
|
35344
|
-
|
|
35345
|
-
|
|
35346
|
-
font-size: 0.9rem;
|
|
35347
|
-
}`;
|
|
35357
|
+
.tags-list .description {
|
|
35358
|
+
margin-top: 0.25rem;
|
|
35359
|
+
font-size: 0.9rem;
|
|
35360
|
+
color: #6c757d;
|
|
35361
|
+
}
|
|
35348
35362
|
|
|
35349
|
-
|
|
35350
|
-
|
|
35363
|
+
/* Pagination */
|
|
35364
|
+
.pagination {
|
|
35365
|
+
display: flex;
|
|
35366
|
+
justify-content: space-between;
|
|
35367
|
+
align-items: center;
|
|
35368
|
+
margin-top: 2rem;
|
|
35369
|
+
padding-top: 1rem;
|
|
35370
|
+
border-top: 1px solid #eee;
|
|
35371
|
+
}
|
|
35372
|
+
|
|
35373
|
+
.pagination .page-info {
|
|
35374
|
+
color: #6c757d;
|
|
35375
|
+
font-size: 0.9rem;
|
|
35376
|
+
}
|
|
35377
|
+
|
|
35378
|
+
/* Footer */
|
|
35379
|
+
footer {
|
|
35380
|
+
text-align: center;
|
|
35381
|
+
padding: 2rem 0;
|
|
35382
|
+
color: #6c757d;
|
|
35383
|
+
font-size: 0.9rem;
|
|
35384
|
+
}`;
|
|
35385
|
+
}
|
|
35386
|
+
function getSamplePost() {
|
|
35387
|
+
return `---
|
|
35351
35388
|
title: Welcome to Bunki
|
|
35352
35389
|
date: 2025-01-15T12:00:00Z
|
|
35353
35390
|
tags: [getting-started, bunki]
|
|
@@ -35398,64 +35435,6 @@ function hello() {
|
|
|
35398
35435
|
4. Run \`bunki generate\` to build your site
|
|
35399
35436
|
5. Run \`bunki serve\` to preview your site locally
|
|
35400
35437
|
`;
|
|
35401
|
-
|
|
35402
|
-
// src/cli/commands/templates/index.ts
|
|
35403
|
-
var nunjucks3 = {
|
|
35404
|
-
"base.njk": baseNjk,
|
|
35405
|
-
"index.njk": indexNjk,
|
|
35406
|
-
"post.njk": postNjk,
|
|
35407
|
-
"tag.njk": tagNjk,
|
|
35408
|
-
"tags.njk": tagsNjk,
|
|
35409
|
-
"archive.njk": archiveNjk
|
|
35410
|
-
};
|
|
35411
|
-
|
|
35412
|
-
// src/cli/commands/init.ts
|
|
35413
|
-
var defaultDependencies = {
|
|
35414
|
-
createDefaultConfig,
|
|
35415
|
-
ensureDir,
|
|
35416
|
-
writeFile: (filePath, data) => Bun.write(filePath, data),
|
|
35417
|
-
logger: console,
|
|
35418
|
-
exit: (code) => process.exit(code)
|
|
35419
|
-
};
|
|
35420
|
-
async function handleInitCommand(options2, deps = defaultDependencies) {
|
|
35421
|
-
try {
|
|
35422
|
-
const configPath = path13.resolve(options2.config);
|
|
35423
|
-
const configCreated = await deps.createDefaultConfig(configPath);
|
|
35424
|
-
if (!configCreated) {
|
|
35425
|
-
deps.logger.log(`
|
|
35426
|
-
Skipped initialization because the config file already exists`);
|
|
35427
|
-
return;
|
|
35428
|
-
}
|
|
35429
|
-
deps.logger.log("Creating directory structure...");
|
|
35430
|
-
const baseDir = process.cwd();
|
|
35431
|
-
const contentDir = path13.join(baseDir, "content");
|
|
35432
|
-
const templatesDir = path13.join(baseDir, "templates");
|
|
35433
|
-
const stylesDir = path13.join(templatesDir, "styles");
|
|
35434
|
-
const publicDir = path13.join(baseDir, "public");
|
|
35435
|
-
await deps.ensureDir(contentDir);
|
|
35436
|
-
await deps.ensureDir(templatesDir);
|
|
35437
|
-
await deps.ensureDir(stylesDir);
|
|
35438
|
-
await deps.ensureDir(publicDir);
|
|
35439
|
-
for (const [filename, content] of Object.entries(nunjucks3)) {
|
|
35440
|
-
await deps.writeFile(path13.join(templatesDir, filename), content);
|
|
35441
|
-
}
|
|
35442
|
-
await deps.writeFile(path13.join(stylesDir, "main.css"), defaultCss);
|
|
35443
|
-
await deps.writeFile(path13.join(contentDir, "welcome.md"), samplePost);
|
|
35444
|
-
deps.logger.log(`
|
|
35445
|
-
Initialization complete! Here are the next steps:`);
|
|
35446
|
-
deps.logger.log("1. Edit bunki.config.ts to configure your site");
|
|
35447
|
-
deps.logger.log("2. Add markdown files to the content directory");
|
|
35448
|
-
deps.logger.log('3. Run "bunki generate" to build your site');
|
|
35449
|
-
deps.logger.log('4. Run "bunki serve" to preview your site locally');
|
|
35450
|
-
} catch (error) {
|
|
35451
|
-
deps.logger.error("Error initializing site:", error);
|
|
35452
|
-
deps.exit(1);
|
|
35453
|
-
}
|
|
35454
|
-
}
|
|
35455
|
-
function registerInitCommand(program2, deps = defaultDependencies) {
|
|
35456
|
-
return program2.command("init").description("Initialize a new site with default structure").option("-c, --config <file>", "Path to config file", "bunki.config.ts").action(async (options2) => {
|
|
35457
|
-
await handleInitCommand(options2, deps);
|
|
35458
|
-
});
|
|
35459
35438
|
}
|
|
35460
35439
|
|
|
35461
35440
|
// src/cli/commands/new-post.ts
|
package/dist/index.js
CHANGED
|
@@ -32333,8 +32333,7 @@ class SiteGenerator {
|
|
|
32333
32333
|
this.metrics = new MetricsCollector;
|
|
32334
32334
|
const env = import_nunjucks2.default.configure(this.options.templatesDir, {
|
|
32335
32335
|
autoescape: true,
|
|
32336
|
-
watch: false
|
|
32337
|
-
noCache: false
|
|
32336
|
+
watch: false
|
|
32338
32337
|
});
|
|
32339
32338
|
env.addFilter("date", (date, format) => {
|
|
32340
32339
|
const d2 = toPacificTime(date);
|
package/package.json
CHANGED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { defaultCss } from "./default-css";
|
|
2
|
-
import { samplePost } from "./sample-post";
|
|
3
|
-
/**
|
|
4
|
-
* Nunjucks template files
|
|
5
|
-
*/
|
|
6
|
-
export declare const nunjucks: Record<string, string>;
|
|
7
|
-
/**
|
|
8
|
-
* Default CSS stylesheet
|
|
9
|
-
*/
|
|
10
|
-
export { defaultCss };
|
|
11
|
-
/**
|
|
12
|
-
* Sample markdown post
|
|
13
|
-
*/
|
|
14
|
-
export { samplePost };
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sample blog post for new Bunki sites
|
|
3
|
-
*/
|
|
4
|
-
export declare const samplePost = "---\ntitle: Welcome to Bunki\ndate: 2025-01-15T12:00:00Z\ntags: [getting-started, bunki]\n---\n\n# Welcome to Your New Bunki Site\n\nThis 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.\n\n## Features\n\n- Markdown support with frontmatter\n- Syntax highlighting for code blocks\n- Tag-based organization\n- Pagination for post listings\n- RSS feed generation\n- Sitemap generation\n\n## Adding Content\n\nCreate new markdown files in the `content` directory with frontmatter like this:\n\n```markdown\n---\ntitle: Your Post Title\ndate: 2025-01-01T12:00:00Z\ntags: [tag1, tag2]\n---\n\nYour post content goes here...\n```\n\n## Code Highlighting\n\nBunki supports syntax highlighting for code blocks:\n\n```javascript\nfunction hello() {\n console.log('Hello, world!');\n}\n```\n\n## Next Steps\n\n1. Edit the site configuration in `bunki.config.ts`\n2. Create your own templates in the `templates` directory\n3. Add more blog posts in the `content` directory\n4. Run `bunki generate` to build your site\n5. Run `bunki serve` to preview your site locally\n";
|