@terrymooreii/sia 1.0.1 → 2.0.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.
Files changed (62) hide show
  1. package/_config.yml +33 -0
  2. package/bin/cli.js +51 -0
  3. package/defaults/includes/footer.njk +14 -0
  4. package/defaults/includes/header.njk +71 -0
  5. package/defaults/includes/pagination.njk +26 -0
  6. package/defaults/includes/tag-list.njk +11 -0
  7. package/defaults/layouts/base.njk +41 -0
  8. package/defaults/layouts/note.njk +25 -0
  9. package/defaults/layouts/page.njk +14 -0
  10. package/defaults/layouts/post.njk +43 -0
  11. package/defaults/pages/blog.njk +36 -0
  12. package/defaults/pages/feed.njk +28 -0
  13. package/defaults/pages/index.njk +60 -0
  14. package/defaults/pages/notes.njk +34 -0
  15. package/defaults/pages/tag.njk +41 -0
  16. package/defaults/pages/tags.njk +39 -0
  17. package/defaults/styles/main.css +1074 -0
  18. package/lib/assets.js +234 -0
  19. package/lib/build.js +260 -19
  20. package/lib/collections.js +191 -0
  21. package/lib/config.js +114 -0
  22. package/lib/content.js +323 -0
  23. package/lib/index.js +53 -18
  24. package/lib/init.js +555 -6
  25. package/lib/new.js +379 -41
  26. package/lib/server.js +257 -0
  27. package/lib/templates.js +249 -0
  28. package/package.json +30 -15
  29. package/readme.md +216 -52
  30. package/src/images/.gitkeep +3 -0
  31. package/src/notes/2024-12-17-first-note.md +6 -0
  32. package/src/pages/about.md +29 -0
  33. package/src/posts/2024-12-16-markdown-features.md +76 -0
  34. package/src/posts/2024-12-17-welcome-to-sia.md +78 -0
  35. package/src/posts/2024-12-17-welcome-to-static-forge.md +78 -0
  36. package/.github/workflows/main.yml +0 -33
  37. package/.prettierignore +0 -3
  38. package/.prettierrc +0 -8
  39. package/lib/helpers.js +0 -37
  40. package/lib/markdown.js +0 -33
  41. package/lib/parse.js +0 -94
  42. package/lib/readconfig.js +0 -16
  43. package/lib/rss.js +0 -63
  44. package/templates/siarc-template.js +0 -53
  45. package/templates/src/_partials/_footer.njk +0 -1
  46. package/templates/src/_partials/_head.njk +0 -35
  47. package/templates/src/_partials/_header.njk +0 -1
  48. package/templates/src/_partials/_layout.njk +0 -12
  49. package/templates/src/_partials/_nav.njk +0 -12
  50. package/templates/src/_partials/page.njk +0 -5
  51. package/templates/src/_partials/post.njk +0 -13
  52. package/templates/src/_partials/posts.njk +0 -19
  53. package/templates/src/assets/android-chrome-192x192.png +0 -0
  54. package/templates/src/assets/android-chrome-512x512.png +0 -0
  55. package/templates/src/assets/apple-touch-icon.png +0 -0
  56. package/templates/src/assets/favicon-16x16.png +0 -0
  57. package/templates/src/assets/favicon-32x32.png +0 -0
  58. package/templates/src/assets/favicon.ico +0 -0
  59. package/templates/src/assets/site.webmanifest +0 -19
  60. package/templates/src/content/index.md +0 -7
  61. package/templates/src/css/markdown.css +0 -1210
  62. package/templates/src/css/theme.css +0 -120
package/lib/init.js CHANGED
@@ -1,8 +1,557 @@
1
- import path from 'path'
2
- import { cpdir } from './helpers.js'
1
+ import prompts from 'prompts';
2
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, copyFileSync } from 'fs';
3
+ import { join, dirname, resolve } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import yaml from 'js-yaml';
3
6
 
4
- // copy source
5
- export const init = () => {
6
- // TODO Make this smarter
7
- // cpdir(path.join('../', 'templates', 'src'), process.cwd())
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+
10
+ /**
11
+ * Default site configuration
12
+ */
13
+ function getDefaultConfig(answers) {
14
+ return {
15
+ site: {
16
+ title: answers.title,
17
+ description: answers.description,
18
+ url: answers.url,
19
+ author: answers.author
20
+ },
21
+ input: 'src',
22
+ output: 'dist',
23
+ layouts: '_layouts',
24
+ includes: '_includes',
25
+ collections: {
26
+ posts: {
27
+ path: 'posts',
28
+ layout: 'post',
29
+ permalink: '/blog/:slug/',
30
+ sortBy: 'date',
31
+ sortOrder: 'desc'
32
+ },
33
+ pages: {
34
+ path: 'pages',
35
+ layout: 'page',
36
+ permalink: '/:slug/'
37
+ },
38
+ notes: {
39
+ path: 'notes',
40
+ layout: 'note',
41
+ permalink: '/notes/:slug/',
42
+ sortBy: 'date',
43
+ sortOrder: 'desc'
44
+ }
45
+ },
46
+ pagination: {
47
+ size: 10
48
+ },
49
+ server: {
50
+ port: 3000
51
+ }
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Generate package.json for new site
57
+ */
58
+ function getPackageJson(name) {
59
+ return {
60
+ name: name.toLowerCase().replace(/\s+/g, '-'),
61
+ version: '1.0.0',
62
+ private: true,
63
+ type: 'module',
64
+ scripts: {
65
+ dev: 'sia dev',
66
+ build: 'sia build',
67
+ new: 'sia new'
68
+ },
69
+ dependencies: {
70
+ sia: '^1.0.0'
71
+ }
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Sample first post content
77
+ */
78
+ function getSamplePost(author) {
79
+ const date = new Date().toISOString().split('T')[0];
80
+ return `---
81
+ title: "Hello World"
82
+ date: ${date}
83
+ tags: [welcome, first-post]
84
+ ---
85
+
86
+ Welcome to my new blog! This is my first post.
87
+
88
+ ## About This Site
89
+
90
+ This site is built with [Sia](https://github.com/sia/sia), a simple and powerful static site generator.
91
+
92
+ ## What's Next?
93
+
94
+ - Write more posts using \`sia new post "My Post Title"\`
95
+ - Customize the theme by adding files to \`_layouts/\` and \`_includes/\`
96
+ - Deploy to your favorite static hosting
97
+
98
+ Happy writing! šŸš€
99
+ `;
100
+ }
101
+
102
+ /**
103
+ * Sample about page content
104
+ */
105
+ function getSampleAboutPage(author) {
106
+ return `---
107
+ title: "About"
108
+ layout: page
109
+ ---
110
+
111
+ ## About Me
112
+
113
+ Hello! I'm ${author}. Welcome to my corner of the internet.
114
+
115
+ ## About This Site
116
+
117
+ This site is built with [Sia](https://github.com/sia/sia), a simple static site generator that supports:
118
+
119
+ - Markdown with front matter
120
+ - Blog posts, pages, and notes
121
+ - Tags and pagination
122
+ - RSS feeds
123
+ - Dark mode
124
+
125
+ ## Contact
126
+
127
+ You can reach me at:
128
+
129
+ - Email: your.email@example.com
130
+ - Twitter: @yourhandle
131
+ - GitHub: @yourusername
132
+ `;
133
+ }
134
+
135
+ /**
136
+ * Sample note content
137
+ */
138
+ function getSampleNote() {
139
+ return `---
140
+ date: ${new Date().toISOString()}
141
+ tags: [welcome]
142
+ ---
143
+
144
+ Just set up my new site! Excited to start sharing my thoughts here.
145
+ `;
8
146
  }
147
+
148
+ /**
149
+ * .gitignore content
150
+ */
151
+ function getGitignore() {
152
+ return `# Dependencies
153
+ node_modules/
154
+
155
+ # Output
156
+ dist/
157
+
158
+ # OS files
159
+ .DS_Store
160
+ Thumbs.db
161
+
162
+ # Editor files
163
+ *.swp
164
+ *.swo
165
+ *~
166
+ .idea/
167
+ .vscode/
168
+
169
+ # Logs
170
+ *.log
171
+ npm-debug.log*
172
+
173
+ # Environment
174
+ .env
175
+ .env.local
176
+ `;
177
+ }
178
+
179
+ /**
180
+ * README content
181
+ */
182
+ function getReadme(title, hasGitHubActions = false) {
183
+ let deploymentSection = `## Deployment
184
+
185
+ After running \`npm run build\`, deploy the \`dist/\` folder to any static hosting:
186
+
187
+ - Netlify
188
+ - Vercel
189
+ - GitHub Pages
190
+ - Cloudflare Pages
191
+ `;
192
+
193
+ if (hasGitHubActions) {
194
+ deploymentSection = `## Deployment
195
+
196
+ This site is configured to automatically deploy to GitHub Pages.
197
+
198
+ ### Automatic Deployment
199
+
200
+ 1. Push your code to GitHub
201
+ 2. Go to your repository Settings → Pages
202
+ 3. Under "Build and deployment", select "GitHub Actions" as the source
203
+ 4. Your site will deploy automatically on every push to the main branch
204
+
205
+ ### Manual Deployment
206
+
207
+ You can also deploy manually:
208
+
209
+ \`\`\`bash
210
+ npm run build
211
+ \`\`\`
212
+
213
+ Then upload the \`dist/\` folder to any static hosting.
214
+ `;
215
+ }
216
+
217
+ return `# ${title}
218
+
219
+ A static site built with [Sia](https://github.com/sia/sia).
220
+
221
+ ## Getting Started
222
+
223
+ \`\`\`bash
224
+ # Install dependencies
225
+ npm install
226
+
227
+ # Start development server
228
+ npm run dev
229
+
230
+ # Build for production
231
+ npm run build
232
+ \`\`\`
233
+
234
+ ## Creating Content
235
+
236
+ \`\`\`bash
237
+ # Create a new blog post
238
+ npm run new post "My Post Title"
239
+
240
+ # Create a new page
241
+ npm run new page "Page Title"
242
+
243
+ # Create a short note
244
+ npm run new note "Quick thought"
245
+ \`\`\`
246
+
247
+ ## Project Structure
248
+
249
+ \`\`\`
250
+ ā”œā”€ā”€ _config.yml # Site configuration
251
+ ā”œā”€ā”€ src/
252
+ │ ā”œā”€ā”€ posts/ # Blog posts (markdown)
253
+ │ ā”œā”€ā”€ pages/ # Static pages
254
+ │ ā”œā”€ā”€ notes/ # Short notes
255
+ │ └── images/ # Images
256
+ ā”œā”€ā”€ _layouts/ # Custom layouts (optional)
257
+ ā”œā”€ā”€ _includes/ # Custom includes (optional)
258
+ ā”œā”€ā”€ styles/ # Custom styles (optional)
259
+ └── dist/ # Generated output
260
+ \`\`\`
261
+
262
+ ## Customization
263
+
264
+ - Edit \`_config.yml\` to change site settings
265
+ - Add custom layouts in \`_layouts/\` to override defaults
266
+ - Add custom includes in \`_includes/\`
267
+ - Add \`styles/main.css\` to override default styles
268
+
269
+ ${deploymentSection}`;
270
+ }
271
+
272
+ /**
273
+ * GitHub Actions workflow for GitHub Pages deployment
274
+ */
275
+ function getGitHubActionsWorkflow() {
276
+ return `name: Deploy to GitHub Pages
277
+
278
+ on:
279
+ push:
280
+ branches: [main, master]
281
+ workflow_dispatch:
282
+
283
+ permissions:
284
+ contents: read
285
+ pages: write
286
+ id-token: write
287
+
288
+ concurrency:
289
+ group: "pages"
290
+ cancel-in-progress: false
291
+
292
+ jobs:
293
+ build:
294
+ runs-on: ubuntu-latest
295
+ steps:
296
+ - name: Checkout
297
+ uses: actions/checkout@v4
298
+
299
+ - name: Setup Node.js
300
+ uses: actions/setup-node@v4
301
+ with:
302
+ node-version: '20'
303
+ cache: 'npm'
304
+
305
+ - name: Install dependencies
306
+ run: npm ci
307
+
308
+ - name: Build site
309
+ run: npm run build
310
+
311
+ - name: Setup Pages
312
+ uses: actions/configure-pages@v4
313
+
314
+ - name: Upload artifact
315
+ uses: actions/upload-pages-artifact@v3
316
+ with:
317
+ path: './dist'
318
+
319
+ deploy:
320
+ environment:
321
+ name: github-pages
322
+ url: \${{ steps.deployment.outputs.page_url }}
323
+ runs-on: ubuntu-latest
324
+ needs: build
325
+ steps:
326
+ - name: Deploy to GitHub Pages
327
+ id: deployment
328
+ uses: actions/deploy-pages@v4
329
+ `;
330
+ }
331
+
332
+ /**
333
+ * Ensure a directory exists
334
+ */
335
+ function ensureDir(dirPath) {
336
+ if (!existsSync(dirPath)) {
337
+ mkdirSync(dirPath, { recursive: true });
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Initialize a new Sia site
343
+ */
344
+ export async function initSite(targetDir, options = {}) {
345
+ const isCurrentDir = !targetDir || targetDir === '.';
346
+ const projectDir = isCurrentDir ? process.cwd() : resolve(process.cwd(), targetDir);
347
+ const projectName = isCurrentDir ? 'my-site' : targetDir;
348
+
349
+ console.log('\nšŸš€ Creating a new Sia site...\n');
350
+
351
+ // Check if directory exists and has files
352
+ if (!isCurrentDir && existsSync(projectDir)) {
353
+ const { proceed } = await prompts({
354
+ type: 'confirm',
355
+ name: 'proceed',
356
+ message: `Directory "${targetDir}" already exists. Continue anyway?`,
357
+ initial: false
358
+ });
359
+
360
+ if (!proceed) {
361
+ console.log('\nāŒ Cancelled.\n');
362
+ process.exit(0);
363
+ }
364
+ }
365
+
366
+ // Interactive prompts
367
+ let answers;
368
+
369
+ if (options.yes) {
370
+ // Use defaults for non-interactive mode
371
+ answers = {
372
+ title: projectName,
373
+ description: 'A personal blog',
374
+ author: 'Anonymous',
375
+ url: 'http://localhost:3000',
376
+ sample: true,
377
+ githubActions: options.githubActions || false
378
+ };
379
+ } else {
380
+ answers = await prompts([
381
+ {
382
+ type: 'text',
383
+ name: 'title',
384
+ message: 'Site title:',
385
+ initial: projectName.charAt(0).toUpperCase() + projectName.slice(1).replace(/-/g, ' ')
386
+ },
387
+ {
388
+ type: 'text',
389
+ name: 'description',
390
+ message: 'Site description:',
391
+ initial: 'A personal blog'
392
+ },
393
+ {
394
+ type: 'text',
395
+ name: 'author',
396
+ message: 'Author name:',
397
+ initial: 'Anonymous'
398
+ },
399
+ {
400
+ type: 'text',
401
+ name: 'url',
402
+ message: 'Site URL (for production):',
403
+ initial: 'http://localhost:3000'
404
+ },
405
+ {
406
+ type: 'confirm',
407
+ name: 'sample',
408
+ message: 'Include sample content?',
409
+ initial: true
410
+ },
411
+ {
412
+ type: 'confirm',
413
+ name: 'githubActions',
414
+ message: 'Add GitHub Actions workflow for GitHub Pages deployment?',
415
+ initial: true
416
+ }
417
+ ]);
418
+ }
419
+
420
+ // Handle cancelled prompts
421
+ if (!answers.title) {
422
+ console.log('\nāŒ Cancelled.\n');
423
+ process.exit(0);
424
+ }
425
+
426
+ console.log('\nšŸ“ Creating project structure...');
427
+
428
+ // Create directories
429
+ ensureDir(projectDir);
430
+ ensureDir(join(projectDir, 'src', 'posts'));
431
+ ensureDir(join(projectDir, 'src', 'pages'));
432
+ ensureDir(join(projectDir, 'src', 'notes'));
433
+ ensureDir(join(projectDir, 'src', 'images'));
434
+ ensureDir(join(projectDir, '_layouts'));
435
+ ensureDir(join(projectDir, '_includes'));
436
+ ensureDir(join(projectDir, 'styles'));
437
+
438
+ // Create _config.yml
439
+ const config = getDefaultConfig(answers);
440
+ writeFileSync(
441
+ join(projectDir, '_config.yml'),
442
+ yaml.dump(config),
443
+ 'utf-8'
444
+ );
445
+ console.log(' āœ“ _config.yml');
446
+
447
+ // Create package.json
448
+ writeFileSync(
449
+ join(projectDir, 'package.json'),
450
+ JSON.stringify(getPackageJson(projectName), null, 2),
451
+ 'utf-8'
452
+ );
453
+ console.log(' āœ“ package.json');
454
+
455
+ // Create .gitignore
456
+ writeFileSync(
457
+ join(projectDir, '.gitignore'),
458
+ getGitignore(),
459
+ 'utf-8'
460
+ );
461
+ console.log(' āœ“ .gitignore');
462
+
463
+ // Create README
464
+ writeFileSync(
465
+ join(projectDir, 'README.md'),
466
+ getReadme(answers.title, answers.githubActions),
467
+ 'utf-8'
468
+ );
469
+ console.log(' āœ“ README.md');
470
+
471
+ // Create GitHub Actions workflow if requested
472
+ if (answers.githubActions) {
473
+ ensureDir(join(projectDir, '.github', 'workflows'));
474
+ writeFileSync(
475
+ join(projectDir, '.github', 'workflows', 'deploy.yml'),
476
+ getGitHubActionsWorkflow(),
477
+ 'utf-8'
478
+ );
479
+ console.log(' āœ“ .github/workflows/deploy.yml');
480
+ }
481
+
482
+ // Create sample content
483
+ if (answers.sample) {
484
+ console.log('\nšŸ“ Creating sample content...');
485
+
486
+ // Sample post
487
+ const date = new Date().toISOString().split('T')[0];
488
+ writeFileSync(
489
+ join(projectDir, 'src', 'posts', `${date}-hello-world.md`),
490
+ getSamplePost(answers.author),
491
+ 'utf-8'
492
+ );
493
+ console.log(' āœ“ src/posts/hello-world.md');
494
+
495
+ // Sample about page
496
+ writeFileSync(
497
+ join(projectDir, 'src', 'pages', 'about.md'),
498
+ getSampleAboutPage(answers.author),
499
+ 'utf-8'
500
+ );
501
+ console.log(' āœ“ src/pages/about.md');
502
+
503
+ // Sample note
504
+ writeFileSync(
505
+ join(projectDir, 'src', 'notes', `${date}-first-note.md`),
506
+ getSampleNote(),
507
+ 'utf-8'
508
+ );
509
+ console.log(' āœ“ src/notes/first-note.md');
510
+ }
511
+
512
+ // Create .gitkeep for images
513
+ writeFileSync(
514
+ join(projectDir, 'src', 'images', '.gitkeep'),
515
+ '# Add your images here\n',
516
+ 'utf-8'
517
+ );
518
+
519
+ // Success message
520
+ console.log('\n✨ Site created successfully!\n');
521
+
522
+ if (!isCurrentDir) {
523
+ console.log('Next steps:\n');
524
+ console.log(` cd ${targetDir}`);
525
+ console.log(' npm install');
526
+ console.log(' npm run dev\n');
527
+ } else {
528
+ console.log('Next steps:\n');
529
+ console.log(' npm install');
530
+ console.log(' npm run dev\n');
531
+ }
532
+
533
+ if (answers.githubActions) {
534
+ console.log('šŸš€ GitHub Pages deployment:\n');
535
+ console.log(' 1. Push your code to GitHub');
536
+ console.log(' 2. Go to Settings → Pages');
537
+ console.log(' 3. Set source to "GitHub Actions"');
538
+ console.log(' 4. Your site will deploy automatically!\n');
539
+ }
540
+
541
+ console.log('Happy writing! šŸ“\n');
542
+ }
543
+
544
+ /**
545
+ * Init command handler for CLI
546
+ */
547
+ export async function initCommand(targetDir, options) {
548
+ try {
549
+ await initSite(targetDir, options);
550
+ } catch (err) {
551
+ console.error('āŒ Failed to create site:', err.message);
552
+ process.exit(1);
553
+ }
554
+ }
555
+
556
+ export default { initSite, initCommand };
557
+