koguma 2.2.0 → 2.2.2
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 +69 -12
- package/cli/wrangler.ts +39 -2
- package/package.json +1 -1
- package/src/admin/_bundle.ts +1 -1
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 {
|
|
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
|
-
): {
|
|
322
|
+
): {
|
|
323
|
+
content: string;
|
|
324
|
+
extension: string;
|
|
325
|
+
siblingFiles?: { fieldId: string; content: string }[];
|
|
326
|
+
} {
|
|
319
327
|
const frontmatter: Record<string, unknown> = {};
|
|
320
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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/cli/wrangler.ts
CHANGED
|
@@ -320,12 +320,36 @@ export function wranglerDev(
|
|
|
320
320
|
/^✘\s+/, // npm dep install error lines (let warn() handle these)
|
|
321
321
|
/^▲\s+/, // npm dep install warning lines
|
|
322
322
|
/^>\s+/, // npm progress lines
|
|
323
|
-
/^\s
|
|
323
|
+
/^\s*$/, // blank lines
|
|
324
|
+
/Reloading local server/, // wrangler dev reload spam
|
|
325
|
+
/Local server updated/, // wrangler dev reload (handled as status line)
|
|
326
|
+
/Unable to find and open the program executable/ // benign diagnostic
|
|
324
327
|
];
|
|
325
328
|
|
|
326
329
|
const shouldSuppress = (line: string): boolean =>
|
|
327
330
|
suppressPatterns.some(p => p.test(line));
|
|
328
331
|
|
|
332
|
+
// ── In-place status line for transient events ──
|
|
333
|
+
let reloadCount = 0;
|
|
334
|
+
let hasStatusLine = false;
|
|
335
|
+
const isTTY = process.stdout.isTTY;
|
|
336
|
+
|
|
337
|
+
/** Write a transient status that overwrites itself on the next call */
|
|
338
|
+
const writeStatus = (text: string) => {
|
|
339
|
+
if (isTTY) {
|
|
340
|
+
process.stdout.write(`\r\x1b[K ⟳ ${text}`);
|
|
341
|
+
hasStatusLine = true;
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
/** Clear the status line before printing a permanent line */
|
|
346
|
+
const clearStatus = () => {
|
|
347
|
+
if (hasStatusLine && isTTY) {
|
|
348
|
+
process.stdout.write('\r\x1b[K');
|
|
349
|
+
hasStatusLine = false;
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
329
353
|
const handleOutput = (data: Buffer, isErr: boolean) => {
|
|
330
354
|
const text = data.toString();
|
|
331
355
|
for (const line of text.split('\n')) {
|
|
@@ -336,16 +360,29 @@ export function wranglerDev(
|
|
|
336
360
|
if (trimmed.includes('Ready on http')) {
|
|
337
361
|
const urlMatch = trimmed.match(/(https?:\/\/[^\s]+)/);
|
|
338
362
|
const url = urlMatch?.[1] ?? 'http://localhost:8787';
|
|
363
|
+
clearStatus();
|
|
339
364
|
ok(`Server ready → ${url}`);
|
|
340
365
|
continue;
|
|
341
366
|
}
|
|
342
367
|
|
|
368
|
+
// Reload events → transient status line (overwrites in place)
|
|
369
|
+
if (/Reloading local server/.test(trimmed)) {
|
|
370
|
+
reloadCount++;
|
|
371
|
+
writeStatus(`reload #${reloadCount}`);
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (/Local server updated/.test(trimmed)) {
|
|
375
|
+
writeStatus(`reload #${reloadCount} ✓`);
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
|
|
343
379
|
if (shouldSuppress(line)) continue;
|
|
344
380
|
|
|
345
381
|
if (trimmed.includes('Starting local server')) continue;
|
|
346
382
|
if (trimmed.includes('Shutting down')) continue;
|
|
347
383
|
|
|
348
|
-
//
|
|
384
|
+
// Permanent line — clear status first
|
|
385
|
+
clearStatus();
|
|
349
386
|
if (isErr) {
|
|
350
387
|
warn(trimmed);
|
|
351
388
|
} else {
|