@terrymooreii/sia 2.1.9 → 2.1.10

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/docs/README.md CHANGED
@@ -121,8 +121,47 @@ pagination:
121
121
  server:
122
122
  port: 3000
123
123
  showDrafts: false
124
+
125
+ assets:
126
+ css: [] # Custom CSS files (paths relative to root)
127
+ js: [] # Custom JavaScript files (paths relative to root)
128
+ ```
129
+
130
+ ## Custom CSS and JavaScript
131
+
132
+ You can inject custom CSS and JavaScript files into your theme by defining them in `_config.yml`:
133
+
134
+ ```yaml
135
+ assets:
136
+ css:
137
+ - custom/styles.css
138
+ - vendor/library.css
139
+ js:
140
+ - custom/script.js
141
+ - vendor/analytics.js
124
142
  ```
125
143
 
144
+ Files are specified as paths relative to your project root. During build, CSS files are copied to `dist/styles/` and JavaScript files to `dist/scripts/`, preserving directory structure. Custom CSS is injected after theme styles (allowing overrides), and JavaScript is injected before the closing `</body>` tag.
145
+
146
+ **Example:**
147
+ ```yaml
148
+ assets:
149
+ css:
150
+ - assets/custom.css
151
+ - vendor/prism.css
152
+ js:
153
+ - assets/analytics.js
154
+ - vendor/prism.js
155
+ ```
156
+
157
+ This will:
158
+ - Copy `assets/custom.css` → `dist/styles/assets/custom.css`
159
+ - Copy `vendor/prism.css` → `dist/styles/vendor/prism.css`
160
+ - Copy `assets/analytics.js` → `dist/scripts/assets/analytics.js`
161
+ - Copy `vendor/prism.js` → `dist/scripts/vendor/prism.js`
162
+
163
+ And inject them into all pages automatically.
164
+
126
165
  ## Static Assets
127
166
 
128
167
  Sia automatically copies static assets during the build process. You can place static files in any of these locations:
@@ -107,6 +107,35 @@ Array of all tags sorted by count (most used first):
107
107
  </ul>
108
108
  ```
109
109
 
110
+ ### `customAssets`
111
+
112
+ Custom CSS and JavaScript files defined in `_config.yml`:
113
+
114
+ | Property | Type | Description |
115
+ |----------|------|-------------|
116
+ | `customAssets.css` | array | Array of CSS file paths (relative to output root) |
117
+ | `customAssets.js` | array | Array of JavaScript file paths (relative to output root) |
118
+
119
+ **Note:** Custom assets are automatically injected into all theme base layouts via shared includes (`custom-assets-css.njk` and `custom-assets-js.njk`). You typically don't need to access this variable directly unless creating custom layouts.
120
+
121
+ **Example:**
122
+
123
+ ```nunjucks
124
+ {# Manually inject custom CSS #}
125
+ {% if customAssets and customAssets.css %}
126
+ {% for css in customAssets.css %}
127
+ <link rel="stylesheet" href="{{ css | url }}">
128
+ {% endfor %}
129
+ {% endif %}
130
+
131
+ {# Manually inject custom JavaScript #}
132
+ {% if customAssets and customAssets.js %}
133
+ {% for js in customAssets.js %}
134
+ <script src="{{ js | url }}"></script>
135
+ {% endfor %}
136
+ {% endif %}
137
+ ```
138
+
110
139
  ---
111
140
 
112
141
  ## Page Context Variables
package/lib/assets.js CHANGED
@@ -200,6 +200,82 @@ export function copyStaticAssets(config) {
200
200
  return totalCopied;
201
201
  }
202
202
 
203
+ /**
204
+ * Copy custom CSS and JavaScript files defined in config
205
+ *
206
+ * @param {object} config - Site configuration
207
+ * @returns {object} Object with css and js arrays of output paths
208
+ */
209
+ export function copyCustomAssets(config) {
210
+ const customAssets = {
211
+ css: [],
212
+ js: []
213
+ };
214
+
215
+ if (!config.assets) {
216
+ return customAssets;
217
+ }
218
+
219
+ const outputStylesDir = join(config.outputDir, 'styles');
220
+ const outputScriptsDir = join(config.outputDir, 'scripts');
221
+
222
+ // Copy CSS files
223
+ if (Array.isArray(config.assets.css) && config.assets.css.length > 0) {
224
+ for (const cssPath of config.assets.css) {
225
+ const srcPath = join(config.rootDir, cssPath);
226
+
227
+ if (!existsSync(srcPath)) {
228
+ console.warn(`⚠️ Custom CSS file not found: ${cssPath}`);
229
+ continue;
230
+ }
231
+
232
+ // Preserve directory structure in output
233
+ // e.g., custom/styles.css -> dist/styles/custom/styles.css
234
+ const relativePath = cssPath.startsWith('/') ? cssPath.slice(1) : cssPath;
235
+ const destPath = join(outputStylesDir, relativePath);
236
+
237
+ copyFile(srcPath, destPath);
238
+
239
+ // Store output path for template injection (relative to output root)
240
+ const outputPath = `/styles/${relativePath}`;
241
+ customAssets.css.push(outputPath);
242
+ }
243
+
244
+ if (customAssets.css.length > 0) {
245
+ console.log(`📝 Copied ${customAssets.css.length} custom CSS file(s)`);
246
+ }
247
+ }
248
+
249
+ // Copy JavaScript files
250
+ if (Array.isArray(config.assets.js) && config.assets.js.length > 0) {
251
+ for (const jsPath of config.assets.js) {
252
+ const srcPath = join(config.rootDir, jsPath);
253
+
254
+ if (!existsSync(srcPath)) {
255
+ console.warn(`⚠️ Custom JavaScript file not found: ${jsPath}`);
256
+ continue;
257
+ }
258
+
259
+ // Preserve directory structure in output
260
+ // e.g., custom/script.js -> dist/scripts/custom/script.js
261
+ const relativePath = jsPath.startsWith('/') ? jsPath.slice(1) : jsPath;
262
+ const destPath = join(outputScriptsDir, relativePath);
263
+
264
+ copyFile(srcPath, destPath);
265
+
266
+ // Store output path for template injection (relative to output root)
267
+ const outputPath = `/scripts/${relativePath}`;
268
+ customAssets.js.push(outputPath);
269
+ }
270
+
271
+ if (customAssets.js.length > 0) {
272
+ console.log(`📜 Copied ${customAssets.js.length} custom JavaScript file(s)`);
273
+ }
274
+ }
275
+
276
+ return customAssets;
277
+ }
278
+
203
279
  /**
204
280
  * Write a file with directory creation
205
281
  */
@@ -245,6 +321,7 @@ export default {
245
321
  copyImages,
246
322
  copyDefaultStyles,
247
323
  copyStaticAssets,
324
+ copyCustomAssets,
248
325
  writeFile,
249
326
  cleanDir
250
327
  };
package/lib/build.js CHANGED
@@ -4,7 +4,7 @@ import { fileURLToPath } from 'url';
4
4
  import { loadConfig } from './config.js';
5
5
  import { buildSiteData, paginate, getPaginationUrls } from './collections.js';
6
6
  import { createTemplateEngine, renderTemplate } from './templates.js';
7
- import { copyImages, copyDefaultStyles, copyStaticAssets, writeFile, ensureDir } from './assets.js';
7
+ import { copyImages, copyDefaultStyles, copyStaticAssets, copyCustomAssets, writeFile, ensureDir } from './assets.js';
8
8
  import { resolveTheme } from './theme-resolver.js';
9
9
 
10
10
  const __filename = fileURLToPath(import.meta.url);
@@ -222,6 +222,10 @@ export async function build(options = {}) {
222
222
  // Build site data (collections, tags, etc.)
223
223
  const siteData = buildSiteData(config);
224
224
 
225
+ // Copy custom assets and add to siteData
226
+ const customAssets = copyCustomAssets(config);
227
+ siteData.customAssets = customAssets;
228
+
225
229
  // Create template engine with resolved theme
226
230
  const env = createTemplateEngine(config, resolvedTheme);
227
231
 
@@ -249,6 +253,7 @@ export async function build(options = {}) {
249
253
  // Copy assets
250
254
  copyImages(config);
251
255
  copyDefaultStyles(config, resolvedTheme);
256
+ // Custom assets already copied above and added to siteData
252
257
  copyStaticAssets(config);
253
258
 
254
259
  const duration = ((Date.now() - startTime) / 1000).toFixed(2);
package/lib/config.js CHANGED
@@ -44,6 +44,10 @@ const defaultConfig = {
44
44
  server: {
45
45
  port: 3000,
46
46
  showDrafts: false // Show draft posts when using dev server
47
+ },
48
+ assets: {
49
+ css: [], // Array of custom CSS file paths (relative to root)
50
+ js: [] // Array of custom JavaScript file paths (relative to root)
47
51
  }
48
52
  };
49
53
 
package/lib/content.js CHANGED
@@ -351,7 +351,9 @@ export function getDateFromFilename(filename) {
351
351
  const match = name.match(datePattern);
352
352
 
353
353
  if (match) {
354
- return new Date(match[1]);
354
+ // Parse as local date, not UTC
355
+ const [year, month, day] = match[1].split('-').map(Number);
356
+ return new Date(year, month - 1, day);
355
357
  }
356
358
 
357
359
  return null;
@@ -377,7 +379,13 @@ export function parseContent(filePath) {
377
379
  // Get date from front matter or filename
378
380
  let date = frontMatter.date;
379
381
  if (date) {
380
- date = new Date(date);
382
+ // If it's a date-only string (YYYY-MM-DD), parse as local date
383
+ if (typeof date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(date)) {
384
+ const [year, month, day] = date.split('-').map(Number);
385
+ date = new Date(year, month - 1, day);
386
+ } else {
387
+ date = new Date(date);
388
+ }
381
389
  } else {
382
390
  date = getDateFromFilename(filePath) || new Date();
383
391
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@terrymooreii/sia",
3
- "version": "2.1.9",
3
+ "version": "2.1.10",
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": {
@@ -0,0 +1,7 @@
1
+ {# Custom CSS files - include in <head> after theme styles #}
2
+ {% if customAssets and customAssets.css %}
3
+ {% for css in customAssets.css %}
4
+ <link rel="stylesheet" href="{{ css | url }}">
5
+ {% endfor %}
6
+ {% endif %}
7
+
@@ -0,0 +1,7 @@
1
+ {# Custom JavaScript files - include before </body> #}
2
+ {% if customAssets and customAssets.js %}
3
+ {% for js in customAssets.js %}
4
+ <script src="{{ js | url }}"></script>
5
+ {% endfor %}
6
+ {% endif %}
7
+
@@ -8,6 +8,7 @@
8
8
  <link rel="preconnect" href="https://fonts.googleapis.com">
9
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
10
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
11
+ {% include "custom-assets-css.njk" %}
11
12
 
12
13
  {% block head %}{% endblock %}
13
14
  </head>
@@ -25,5 +26,6 @@
25
26
  {% include "footer.njk" %}
26
27
  </div>
27
28
  </div>
29
+ {% include "custom-assets-js.njk" %}
28
30
  </body>
29
31
  </html>
@@ -26,7 +26,7 @@
26
26
  <a href="{{ post.url }}">{{ post.title }}</a>
27
27
  </h3>
28
28
 
29
- <p class="card-excerpt">{{ post.excerpt | excerpt(120) }}</p>
29
+ <p class="card-excerpt">{{ post.excerptHtml | safe }}</p>
30
30
 
31
31
  <div class="card-meta">
32
32
  <span class="card-date">{{ post.date | date('short') }}</span>
@@ -23,7 +23,7 @@
23
23
  <a href="{{ post.url }}">{{ post.title }}</a>
24
24
  </h2>
25
25
 
26
- <p class="card-excerpt">{{ post.excerpt | excerpt(150) }}</p>
26
+ <p class="card-excerpt">{{ post.excerptHtml | safe }}</p>
27
27
 
28
28
  <div class="card-meta">
29
29
  <span class="card-date">{{ post.date | date('short') }}</span>
@@ -8,6 +8,7 @@
8
8
  <link rel="preconnect" href="https://fonts.googleapis.com">
9
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
10
  <link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&family=Source+Sans+3:wght@400;500;600&family=Source+Serif+4:opsz,wght@8..60,400;8..60,500&display=swap" rel="stylesheet">
11
+ {% include "custom-assets-css.njk" %}
11
12
 
12
13
  {% block head %}{% endblock %}
13
14
  </head>
@@ -19,5 +20,6 @@
19
20
  </main>
20
21
 
21
22
  {% include "footer.njk" %}
23
+ {% include "custom-assets-js.njk" %}
22
24
  </body>
23
25
  </html>
@@ -14,7 +14,7 @@
14
14
  <h2 class="row-title">
15
15
  <a href="{{ post.url }}">{{ post.title }}</a>
16
16
  </h2>
17
- <p class="row-excerpt">{{ post.excerpt | excerpt(180) }}</p>
17
+ <p class="row-excerpt">{{ post.excerptHtml | safe }}</p>
18
18
  <div class="row-meta">
19
19
  <span class="row-author">{{ post.author or site.author }}</span>
20
20
  <span class="meta-separator">·</span>
@@ -5,6 +5,7 @@
5
5
  {% include "theme-script.njk" %}
6
6
 
7
7
  <link rel="stylesheet" href="{{ '/styles/main.css' | url }}">
8
+ {% include "custom-assets-css.njk" %}
8
9
 
9
10
  {% block head %}{% endblock %}
10
11
  </head>
@@ -16,6 +17,7 @@
16
17
  </main>
17
18
 
18
19
  {% include "footer.njk" %}
20
+ {% include "custom-assets-js.njk" %}
19
21
  </body>
20
22
  </html>
21
23
 
@@ -20,7 +20,7 @@
20
20
  <ul class="tag-posts">
21
21
  {% for item in tag.items | limit(5) %}
22
22
  <li>
23
- <a href="{{ item.url }}">{{ item.title or item.excerpt }}</a>
23
+ <a href="{{ item.url }}">{{ item.title or item.excerptHtml | safe }}</a>
24
24
  <time datetime="{{ item.date | date('iso') }}">{{ item.date | date('short') }}</time>
25
25
  </li>
26
26
  {% endfor %}
@@ -5,6 +5,7 @@
5
5
  {% include "theme-script.njk" %}
6
6
 
7
7
  <link rel="stylesheet" href="{{ '/styles/main.css' | url }}">
8
+ {% include "custom-assets-css.njk" %}
8
9
 
9
10
  {% block head %}{% endblock %}
10
11
  </head>
@@ -16,6 +17,7 @@
16
17
  </main>
17
18
 
18
19
  {% include "footer.njk" %}
20
+ {% include "custom-assets-js.njk" %}
19
21
  </body>
20
22
  </html>
21
23