koguma 2.2.0 → 2.2.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/cli/scaffold.ts CHANGED
@@ -11,7 +11,11 @@ import {
11
11
  import { resolve } from 'path';
12
12
  import { ok, warn } from './log.ts';
13
13
  import { generateKogumaToml } from './config.ts';
14
- import { findMarkdownField, type ContentTypeInfo } from './content.ts';
14
+ import {
15
+ findMarkdownField,
16
+ findMarkdownFields,
17
+ type ContentTypeInfo
18
+ } from './content.ts';
15
19
  import matter from 'gray-matter';
16
20
 
17
21
  // ── Template types ─────────────────────────────────────────────────
@@ -315,29 +319,50 @@ export function generateExampleFile(
315
319
  ctId: string,
316
320
  fields: Record<string, { fieldType: string }>,
317
321
  singleton?: boolean
318
- ): { content: string; extension: string } {
322
+ ): {
323
+ content: string;
324
+ extension: string;
325
+ siblingFiles?: { fieldId: string; content: string }[];
326
+ } {
319
327
  const frontmatter: Record<string, unknown> = {};
320
- let hasMarkdown = false;
328
+ const mdFields = Object.entries(fields)
329
+ .filter(([, meta]) => meta.fieldType === 'markdown')
330
+ .map(([id]) => id);
331
+ const primaryMdField = mdFields[0] ?? null;
332
+ const extraMdFields = mdFields.slice(1);
321
333
 
322
334
  for (const [fieldId, meta] of Object.entries(fields)) {
323
335
  if (meta.fieldType === 'markdown') {
324
- hasMarkdown = true;
325
- continue; // markdown goes in body, not frontmatter
336
+ continue; // markdown goes in body or sibling files, not frontmatter
326
337
  }
327
338
  frontmatter[fieldId] = placeholderForFieldType(meta.fieldType);
328
339
  }
329
340
 
330
- if (hasMarkdown) {
341
+ // Build sibling example files for extra markdown fields
342
+ const siblingFiles: { fieldId: string; content: string }[] = [];
343
+ for (const fieldId of extraMdFields) {
344
+ siblingFiles.push({
345
+ fieldId,
346
+ content: `Write your ${fieldId} content here.`
347
+ });
348
+ }
349
+
350
+ if (primaryMdField) {
331
351
  const fm = matter.stringify('', frontmatter).trim();
332
352
  const bodyHint = singleton ? '' : `\nWrite your ${ctId} content here.\n`;
333
353
  return {
334
354
  content: `${fm}\n${bodyHint}`,
335
- extension: '.md'
355
+ extension: '.md',
356
+ ...(siblingFiles.length > 0 ? { siblingFiles } : {})
336
357
  };
337
358
  }
338
359
 
339
360
  const fm = matter.stringify('', frontmatter).trim();
340
- return { content: fm + '\n', extension: '.md' };
361
+ return {
362
+ content: fm + '\n',
363
+ extension: '.md',
364
+ ...(siblingFiles.length > 0 ? { siblingFiles } : {})
365
+ };
341
366
  }
342
367
 
343
368
  /**
@@ -367,7 +392,7 @@ export function scaffoldContentDirFromTemplate(
367
392
  fields[fid] = { fieldType: fieldTypeFromExpression(expr) };
368
393
  }
369
394
 
370
- const { content, extension } = generateExampleFile(
395
+ const { content, extension, siblingFiles } = generateExampleFile(
371
396
  ct.id,
372
397
  fields,
373
398
  ct.singleton
@@ -375,6 +400,14 @@ export function scaffoldContentDirFromTemplate(
375
400
  const filename = `_example${extension}`;
376
401
  writeFileSync(resolve(typeDir, filename), content);
377
402
  ok(`Created content/${ct.id}/${filename}`);
403
+
404
+ // Write sibling example files for extra markdown fields
405
+ if (siblingFiles) {
406
+ for (const { fieldId, content: siblingContent } of siblingFiles) {
407
+ const siblingName = `_example.${fieldId}.md`;
408
+ writeFileSync(resolve(typeDir, siblingName), siblingContent + '\n');
409
+ }
410
+ }
378
411
  } else if (!dirExisted) {
379
412
  ok(`Created content/${ct.id}/`);
380
413
  }
@@ -408,7 +441,7 @@ export function scaffoldContentDir(
408
441
  const isEmpty = dirExisted ? readdirSync(typeDir).length === 0 : true;
409
442
 
410
443
  if (isEmpty) {
411
- const { content, extension } = generateExampleFile(
444
+ const { content, extension, siblingFiles } = generateExampleFile(
412
445
  ct.id,
413
446
  ct.fieldMeta,
414
447
  ct.singleton
@@ -416,6 +449,14 @@ export function scaffoldContentDir(
416
449
  const filename = `_example${extension}`;
417
450
  writeFileSync(resolve(typeDir, filename), content);
418
451
  ok(`Created content/${ct.id}/${filename}`);
452
+
453
+ // Write sibling example files for extra markdown fields
454
+ if (siblingFiles) {
455
+ for (const { fieldId, content: siblingContent } of siblingFiles) {
456
+ const siblingName = `_example.${fieldId}.md`;
457
+ writeFileSync(resolve(typeDir, siblingName), siblingContent + '\n');
458
+ }
459
+ }
419
460
  } else if (!dirExisted) {
420
461
  ok(`Created content/${ct.id}/`);
421
462
  }
@@ -538,7 +579,7 @@ export function syncContentDirsWithConfig(
538
579
  mkdirSync(typeDir, { recursive: true });
539
580
  }
540
581
 
541
- const { content, extension } = generateExampleFile(
582
+ const { content, extension, siblingFiles } = generateExampleFile(
542
583
  ct.id,
543
584
  ct.fieldMeta,
544
585
  ct.singleton
@@ -551,6 +592,14 @@ export function syncContentDirsWithConfig(
551
592
  if (!dryRun) {
552
593
  writeFileSync(resolve(typeDir, filename), content);
553
594
  ok(`Created content/${ct.id}/${filename}`);
595
+
596
+ // Write sibling example files for extra markdown fields
597
+ if (siblingFiles) {
598
+ for (const { fieldId, content: siblingContent } of siblingFiles) {
599
+ const siblingName = `_example.${fieldId}.md`;
600
+ writeFileSync(resolve(typeDir, siblingName), siblingContent + '\n');
601
+ }
602
+ }
554
603
  }
555
604
  continue;
556
605
  }
@@ -559,7 +608,7 @@ export function syncContentDirsWithConfig(
559
608
 
560
609
  // If dir is empty, create _example
561
610
  if (files.length === 0) {
562
- const { content, extension } = generateExampleFile(
611
+ const { content, extension, siblingFiles } = generateExampleFile(
563
612
  ct.id,
564
613
  ct.fieldMeta,
565
614
  ct.singleton
@@ -572,6 +621,14 @@ export function syncContentDirsWithConfig(
572
621
  if (!dryRun) {
573
622
  writeFileSync(resolve(typeDir, filename), content);
574
623
  ok(`Created content/${ct.id}/${filename}`);
624
+
625
+ // Write sibling example files for extra markdown fields
626
+ if (siblingFiles) {
627
+ for (const { fieldId, content: siblingContent } of siblingFiles) {
628
+ const siblingName = `_example.${fieldId}.md`;
629
+ writeFileSync(resolve(typeDir, siblingName), siblingContent + '\n');
630
+ }
631
+ }
575
632
  }
576
633
  continue;
577
634
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koguma",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "🐻 A little CMS with big heart — schema-driven, runs on Cloudflare's free tier",
5
5
  "type": "module",
6
6
  "license": "MIT",