@terrymooreii/sia 2.0.0 → 2.0.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/_config.yml CHANGED
@@ -30,4 +30,3 @@ pagination:
30
30
 
31
31
  server:
32
32
  port: 3000
33
-
@@ -6,8 +6,8 @@
6
6
  </p>
7
7
 
8
8
  <nav class="footer-nav">
9
- <a href="/feed.xml" class="footer-link">RSS</a>
10
- <a href="/tags/" class="footer-link">Tags</a>
9
+ <a href="{{ '/feed.xml' | url }}" class="footer-link">RSS</a>
10
+ <a href="{{ '/tags/' | url }}" class="footer-link">Tags</a>
11
11
  </nav>
12
12
  </div>
13
13
  </footer>
@@ -1,12 +1,12 @@
1
1
  <header class="site-header">
2
2
  <div class="container">
3
- <a href="/" class="site-logo">{{ site.title }}</a>
3
+ <a href="{{ '/' | url }}" class="site-logo">{{ site.title }}</a>
4
4
 
5
5
  <nav class="site-nav">
6
- <a href="/" class="nav-link{% if page.url == '/' %} active{% endif %}">Home</a>
7
- <a href="/blog/" class="nav-link{% if page.url and page.url.startsWith('/blog') %} active{% endif %}">Blog</a>
8
- <a href="/notes/" class="nav-link{% if page.url and page.url.startsWith('/notes') %} active{% endif %}">Notes</a>
9
- <a href="/tags/" class="nav-link{% if page.url and page.url.startsWith('/tags') %} active{% endif %}">Tags</a>
6
+ <a href="{{ '/' | url }}" class="nav-link{% if page.url == ('/' | url) %} active{% endif %}">Home</a>
7
+ <a href="{{ '/blog/' | url }}" class="nav-link{% if page.url and page.url.startsWith('/blog') %} active{% endif %}">Blog</a>
8
+ <a href="{{ '/notes/' | url }}" class="nav-link{% if page.url and page.url.startsWith('/notes') %} active{% endif %}">Notes</a>
9
+ <a href="{{ '/tags/' | url }}" class="nav-link{% if page.url and page.url.startsWith('/tags') %} active{% endif %}">Tags</a>
10
10
  {% if collections.pages %}
11
11
  {% for p in collections.pages | limit(3) %}
12
12
  <a href="{{ p.url }}" class="nav-link{% if page.url == p.url %} active{% endif %}">{{ p.title }}</a>
@@ -1,7 +1,7 @@
1
1
  {% if allTags and allTags.length %}
2
2
  <div class="tag-cloud">
3
3
  {% for tag in allTags %}
4
- <a href="/tags/{{ tag.slug }}/" class="tag tag-{{ 'lg' if tag.count > 10 else ('md' if tag.count > 5 else 'sm') }}">
4
+ <a href="{{ '/tags/' | url }}{{ tag.slug }}/" class="tag tag-{{ 'lg' if tag.count > 10 else ('md' if tag.count > 5 else 'sm') }}">
5
5
  {{ tag.name }}
6
6
  <span class="tag-count">({{ tag.count }})</span>
7
7
  </a>
@@ -15,16 +15,16 @@
15
15
  })();
16
16
  </script>
17
17
 
18
- <link rel="stylesheet" href="/styles/main.css">
18
+ <link rel="stylesheet" href="{{ '/styles/main.css' | url }}">
19
19
 
20
20
  <!-- Open Graph -->
21
21
  <meta property="og:title" content="{{ title or site.title }}">
22
22
  <meta property="og:description" content="{{ page.excerpt or site.description }}">
23
23
  <meta property="og:type" content="{% if page.collection == 'posts' %}article{% else %}website{% endif %}">
24
- <meta property="og:url" content="{{ site.url }}{{ page.url or '/' }}">
24
+ <meta property="og:url" content="{{ site.url }}{{ page.url or ('/' | url) }}">
25
25
 
26
26
  <!-- RSS Feed -->
27
- <link rel="alternate" type="application/rss+xml" title="{{ site.title }}" href="/feed.xml">
27
+ <link rel="alternate" type="application/rss+xml" title="{{ site.title }}" href="{{ '/feed.xml' | url }}">
28
28
 
29
29
  {% block head %}{% endblock %}
30
30
  </head>
@@ -11,7 +11,7 @@
11
11
  {% if page.tags and page.tags.length %}
12
12
  <span class="note-tags">
13
13
  {% for tag in page.tags %}
14
- <a href="/tags/{{ tag | slug }}/" class="tag">{{ tag }}</a>
14
+ <a href="{{ '/tags/' | url }}{{ tag | slug }}/" class="tag">{{ tag }}</a>
15
15
  {% endfor %}
16
16
  </span>
17
17
  {% endif %}
@@ -19,7 +19,7 @@
19
19
  </article>
20
20
 
21
21
  <nav class="note-nav">
22
- <a href="/notes/" class="btn btn-secondary">← All Notes</a>
22
+ <a href="{{ '/notes/' | url }}" class="btn btn-secondary">← All Notes</a>
23
23
  </nav>
24
24
  {% endblock %}
25
25
 
@@ -11,7 +11,7 @@
11
11
  <span class="post-meta-divider">·</span>
12
12
  <span class="post-tags">
13
13
  {% for tag in page.tags %}
14
- <a href="/tags/{{ tag | slug }}/" class="tag">{{ tag }}</a>{% if not loop.last %}, {% endif %}
14
+ <a href="{{ '/tags/' | url }}{{ tag | slug }}/" class="tag">{{ tag }}</a>{% if not loop.last %}, {% endif %}
15
15
  {% endfor %}
16
16
  </span>
17
17
  {% endif %}
@@ -29,13 +29,13 @@
29
29
  <div class="post-tags-footer">
30
30
  <span class="tags-label">Tagged:</span>
31
31
  {% for tag in page.tags %}
32
- <a href="/tags/{{ tag | slug }}/" class="tag">{{ tag }}</a>
32
+ <a href="{{ '/tags/' | url }}{{ tag | slug }}/" class="tag">{{ tag }}</a>
33
33
  {% endfor %}
34
34
  </div>
35
35
  {% endif %}
36
36
 
37
37
  <nav class="post-nav">
38
- <a href="/blog/" class="btn btn-secondary">← Back to Blog</a>
38
+ <a href="{{ '/blog/' | url }}" class="btn btn-secondary">← Back to Blog</a>
39
39
  </nav>
40
40
  </footer>
41
41
  </article>
@@ -19,7 +19,7 @@
19
19
  {% if post.tags and post.tags.length %}
20
20
  <div class="post-card-tags">
21
21
  {% for tag in post.tags %}
22
- <a href="/tags/{{ tag | slug }}/" class="tag tag-sm">{{ tag }}</a>
22
+ <a href="{{ '/tags/' | url }}{{ tag | slug }}/" class="tag tag-sm">{{ tag }}</a>
23
23
  {% endfor %}
24
24
  </div>
25
25
  {% endif %}
@@ -9,7 +9,7 @@
9
9
  <section class="section">
10
10
  <div class="section-header">
11
11
  <h2 class="section-title">Latest Posts</h2>
12
- <a href="/blog/" class="section-link">View all →</a>
12
+ <a href="{{ '/blog/' | url }}" class="section-link">View all →</a>
13
13
  </div>
14
14
 
15
15
  <div class="post-list">
@@ -23,7 +23,7 @@
23
23
  {% if post.tags and post.tags.length %}
24
24
  <span class="post-card-tags">
25
25
  {% for tag in post.tags | limit(3) %}
26
- <a href="/tags/{{ tag | slug }}/" class="tag tag-sm">{{ tag }}</a>
26
+ <a href="{{ '/tags/' | url }}{{ tag | slug }}/" class="tag tag-sm">{{ tag }}</a>
27
27
  {% endfor %}
28
28
  </span>
29
29
  {% endif %}
@@ -40,7 +40,7 @@
40
40
  <section class="section">
41
41
  <div class="section-header">
42
42
  <h2 class="section-title">Recent Notes</h2>
43
- <a href="/notes/" class="section-link">View all →</a>
43
+ <a href="{{ '/notes/' | url }}" class="section-link">View all →</a>
44
44
  </div>
45
45
 
46
46
  <div class="notes-grid">
@@ -17,7 +17,7 @@
17
17
  {% if note.tags and note.tags.length %}
18
18
  <div class="note-item-tags">
19
19
  {% for tag in note.tags %}
20
- <a href="/tags/{{ tag | slug }}/" class="tag tag-sm">{{ tag }}</a>
20
+ <a href="{{ '/tags/' | url }}{{ tag | slug }}/" class="tag tag-sm">{{ tag }}</a>
21
21
  {% endfor %}
22
22
  </div>
23
23
  {% endif %}
@@ -21,7 +21,7 @@
21
21
  {% if post.tags and post.tags.length %}
22
22
  <div class="post-card-tags">
23
23
  {% for t in post.tags %}
24
- <a href="/tags/{{ t | slug }}/" class="tag tag-sm{% if t | slug == tag.slug %} active{% endif %}">{{ t }}</a>
24
+ <a href="{{ '/tags/' | url }}{{ t | slug }}/" class="tag tag-sm{% if t | slug == tag.slug %} active{% endif %}">{{ t }}</a>
25
25
  {% endfor %}
26
26
  </div>
27
27
  {% endif %}
@@ -35,7 +35,7 @@
35
35
  {% include "pagination.njk" %}
36
36
 
37
37
  <nav class="page-nav">
38
- <a href="/tags/" class="btn btn-secondary">← All Tags</a>
38
+ <a href="{{ '/tags/' | url }}" class="btn btn-secondary">← All Tags</a>
39
39
  </nav>
40
40
  {% endblock %}
41
41
 
@@ -14,7 +14,7 @@
14
14
  {% for tag in allTags %}
15
15
  <section class="tag-section" id="{{ tag.slug }}">
16
16
  <h2 class="tag-section-title">
17
- <a href="/tags/{{ tag.slug }}/">{{ tag.name }}</a>
17
+ <a href="{{ '/tags/' | url }}{{ tag.slug }}/">{{ tag.name }}</a>
18
18
  <span class="tag-section-count">({{ tag.count }})</span>
19
19
  </h2>
20
20
  <ul class="tag-posts">
@@ -25,7 +25,7 @@
25
25
  </li>
26
26
  {% endfor %}
27
27
  {% if tag.count > 5 %}
28
- <li><a href="/tags/{{ tag.slug }}/" class="more-link">View all {{ tag.count }} →</a></li>
28
+ <li><a href="{{ '/tags/' | url }}{{ tag.slug }}/" class="more-link">View all {{ tag.count }} →</a></li>
29
29
  {% endif %}
30
30
  </ul>
31
31
  </section>
package/lib/build.js CHANGED
@@ -42,13 +42,14 @@ function renderContentItem(env, item, siteData) {
42
42
  */
43
43
  function renderPaginatedPages(env, siteData, baseUrl, outputBase, templateName, extraData = {}) {
44
44
  const { paginatedCollections } = siteData;
45
+ const basePath = siteData.config.site.basePath || '';
45
46
 
46
47
  // Get all posts for the main blog listing
47
48
  const posts = siteData.collections.posts || [];
48
49
  const pages = paginate(posts, siteData.config.pagination.size);
49
50
 
50
51
  for (const page of pages) {
51
- const pagination = getPaginationUrls(baseUrl, page);
52
+ const pagination = getPaginationUrls(baseUrl, page, basePath);
52
53
 
53
54
  const html = renderTemplate(env, templateName, {
54
55
  ...siteData,
@@ -72,6 +73,7 @@ function renderPaginatedPages(env, siteData, baseUrl, outputBase, templateName,
72
73
  */
73
74
  function renderTagPages(env, siteData) {
74
75
  const { tags, allTags, config } = siteData;
76
+ const basePath = config.site.basePath || '';
75
77
 
76
78
  // Render main tags listing page
77
79
  const tagsHtml = renderTemplate(env, 'tags.njk', {
@@ -86,7 +88,7 @@ function renderTagPages(env, siteData) {
86
88
  const baseUrl = `/tags/${tagSlug}/`;
87
89
 
88
90
  for (const page of tagPages) {
89
- const pagination = getPaginationUrls(baseUrl, page);
91
+ const pagination = getPaginationUrls(baseUrl, page, basePath);
90
92
 
91
93
  const html = renderTemplate(env, 'tag.njk', {
92
94
  ...siteData,
@@ -144,6 +146,7 @@ function renderBlogListing(env, siteData) {
144
146
  function renderNotesListing(env, siteData) {
145
147
  const { config, collections } = siteData;
146
148
  const notes = collections.notes || [];
149
+ const basePath = config.site.basePath || '';
147
150
 
148
151
  if (notes.length === 0) return;
149
152
 
@@ -151,7 +154,7 @@ function renderNotesListing(env, siteData) {
151
154
  const pages = paginate(notes, config.pagination.size);
152
155
 
153
156
  for (const page of pages) {
154
- const pagination = getPaginationUrls('/notes/', page);
157
+ const pagination = getPaginationUrls('/notes/', page, basePath);
155
158
 
156
159
  const html = renderTemplate(env, 'notes.njk', {
157
160
  ...siteData,
@@ -79,18 +79,22 @@ export function paginate(items, pageSize = 10) {
79
79
 
80
80
  /**
81
81
  * Generate pagination URLs
82
+ * @param {string} baseUrl - The base URL path (without basePath)
83
+ * @param {object} pagination - Pagination object
84
+ * @param {string} basePath - Optional basePath for subpath hosting
82
85
  */
83
- export function getPaginationUrls(baseUrl, pagination) {
86
+ export function getPaginationUrls(baseUrl, pagination, basePath = '') {
87
+ const prefixedBaseUrl = basePath + baseUrl;
84
88
  return {
85
89
  ...pagination,
86
90
  url: pagination.pageNumber === 1
87
- ? baseUrl
88
- : `${baseUrl}page/${pagination.pageNumber}/`,
91
+ ? prefixedBaseUrl
92
+ : `${prefixedBaseUrl}page/${pagination.pageNumber}/`,
89
93
  previousUrl: pagination.previousPage
90
- ? (pagination.previousPage === 1 ? baseUrl : `${baseUrl}page/${pagination.previousPage}/`)
94
+ ? (pagination.previousPage === 1 ? prefixedBaseUrl : `${prefixedBaseUrl}page/${pagination.previousPage}/`)
91
95
  : null,
92
96
  nextUrl: pagination.nextPage
93
- ? `${baseUrl}page/${pagination.nextPage}/`
97
+ ? `${prefixedBaseUrl}page/${pagination.nextPage}/`
94
98
  : null
95
99
  };
96
100
  }
package/lib/config.js CHANGED
@@ -93,6 +93,14 @@ export function loadConfig(rootDir = process.cwd()) {
93
93
  // Merge user config with defaults
94
94
  const config = deepMerge(defaultConfig, userConfig);
95
95
 
96
+ // Extract basePath from site URL (e.g., "https://example.org/test" -> "/test")
97
+ try {
98
+ const siteUrl = new URL(config.site.url);
99
+ config.site.basePath = siteUrl.pathname.replace(/\/$/, '') || '';
100
+ } catch (e) {
101
+ config.site.basePath = '';
102
+ }
103
+
96
104
  // Add computed paths
97
105
  config.rootDir = rootDir;
98
106
  config.inputDir = join(rootDir, config.input);
package/lib/content.js CHANGED
@@ -262,7 +262,9 @@ export function loadCollection(config, collectionName) {
262
262
  .replace(':month', String(item.date.getMonth() + 1).padStart(2, '0'))
263
263
  .replace(':day', String(item.date.getDate()).padStart(2, '0'));
264
264
 
265
- item.url = permalink;
265
+ // Prepend basePath for subpath hosting support
266
+ const basePath = config.site.basePath || '';
267
+ item.url = basePath + permalink;
266
268
  item.outputPath = join(config.outputDir, permalink, 'index.html');
267
269
 
268
270
  return item;
package/lib/templates.js CHANGED
@@ -170,6 +170,22 @@ function jsonFilter(obj, spaces = 2) {
170
170
  return JSON.stringify(obj, null, spaces);
171
171
  }
172
172
 
173
+ /**
174
+ * Create a URL filter that prepends the basePath
175
+ */
176
+ function createUrlFilter(basePath) {
177
+ return function urlFilter(path) {
178
+ if (!path) return basePath || '/';
179
+ // If path is already absolute with protocol, return as-is
180
+ if (path.startsWith('http://') || path.startsWith('https://')) {
181
+ return path;
182
+ }
183
+ // Ensure path starts with /
184
+ const normalizedPath = path.startsWith('/') ? path : '/' + path;
185
+ return (basePath || '') + normalizedPath;
186
+ };
187
+ }
188
+
173
189
  /**
174
190
  * Create and configure the Nunjucks environment
175
191
  */
@@ -214,6 +230,9 @@ export function createTemplateEngine(config) {
214
230
  env.addFilter('withTag', withTagFilter);
215
231
  env.addFilter('json', jsonFilter);
216
232
 
233
+ // Add URL filter with basePath support
234
+ env.addFilter('url', createUrlFilter(config.site.basePath));
235
+
217
236
  return env;
218
237
  }
219
238
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@terrymooreii/sia",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "A simple, powerful static site generator with markdown, front matter, and Nunjucks templates",
5
5
  "main": "lib/index.js",
6
6
  "bin": {