@glw907/cairn-cms 0.54.0 → 0.56.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.
- package/CHANGELOG.md +57 -0
- package/dist/components/ConceptList.svelte +216 -75
- package/dist/components/ConceptList.svelte.d.ts +6 -4
- package/dist/components/MarkdownEditor.svelte +64 -45
- package/dist/components/cairn-admin.css +50 -0
- package/dist/components/editor-folding.d.ts +4 -3
- package/dist/components/editor-folding.js +129 -97
- package/dist/components/editor-highlight.js +1 -13
- package/dist/content/concepts.js +3 -1
- package/dist/content/manifest.d.ts +1 -0
- package/dist/content/manifest.js +6 -0
- package/dist/content/types.d.ts +5 -0
- package/dist/delivery/content-index.js +1 -1
- package/dist/delivery/data.d.ts +1 -1
- package/dist/delivery/data.js +1 -1
- package/dist/sveltekit/content-routes.d.ts +6 -0
- package/dist/sveltekit/content-routes.js +11 -6
- package/dist/sveltekit/index.d.ts +1 -0
- package/dist/vite/index.d.ts +6 -4
- package/dist/vite/index.js +11 -7
- package/dist/vite/resolve-root.d.ts +16 -0
- package/dist/vite/resolve-root.js +16 -0
- package/package.json +2 -1
- package/src/lib/components/ConceptList.svelte +216 -75
- package/src/lib/components/MarkdownEditor.svelte +64 -45
- package/src/lib/components/editor-folding.ts +137 -104
- package/src/lib/components/editor-highlight.ts +0 -12
- package/src/lib/content/concepts.ts +3 -1
- package/src/lib/content/manifest.ts +7 -0
- package/src/lib/content/types.ts +5 -0
- package/src/lib/delivery/content-index.ts +1 -1
- package/src/lib/delivery/data.ts +1 -1
- package/src/lib/sveltekit/content-routes.ts +17 -6
- package/src/lib/sveltekit/index.ts +2 -0
- package/src/lib/vite/index.ts +11 -7
- package/src/lib/vite/resolve-root.ts +24 -0
- /package/dist/{delivery → content}/excerpt.d.ts +0 -0
- /package/dist/{delivery → content}/excerpt.js +0 -0
- /package/src/lib/{delivery → content}/excerpt.ts +0 -0
|
@@ -6,6 +6,8 @@ import { redirect, error, fail } from '@sveltejs/kit';
|
|
|
6
6
|
import { findConcept } from '../content/concepts.js';
|
|
7
7
|
import { extractCairnLinks, formatCairnToken, rewriteCairnLink } from '../content/links.js';
|
|
8
8
|
import { frontmatterFromForm, parseMarkdown, dateInputValue, serializeMarkdown } from '../content/frontmatter.js';
|
|
9
|
+
import { deriveExcerpt } from '../content/excerpt.js';
|
|
10
|
+
import { asString } from '../content/identity.js';
|
|
9
11
|
import { isValidId, slugify, filenameFromId, composeDatedId, slugFromId, renameId } from '../content/ids.js';
|
|
10
12
|
import { appCredentials, type GithubKeyEnv } from '../github/credentials.js';
|
|
11
13
|
import { listMarkdown, readRaw, commitFiles, type FileChange } from '../github/repo.js';
|
|
@@ -56,12 +58,18 @@ export interface EntrySummary {
|
|
|
56
58
|
draft: boolean;
|
|
57
59
|
/** Publish state derived from the ref set: live as-is, live with pending edits, or branch-only. */
|
|
58
60
|
status: 'published' | 'edited' | 'new';
|
|
61
|
+
/** The row's one-line summary: the manifest's indexed excerpt for a published row, the branch
|
|
62
|
+
* frontmatter/body excerpt for a pending one, and null when neither yields text. */
|
|
63
|
+
summary: string | null;
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
/** The concept list view's data. */
|
|
62
67
|
export interface ListData {
|
|
63
68
|
conceptId: string;
|
|
64
69
|
label: string;
|
|
70
|
+
/** The singular noun for the create affordances ("New post"); from the descriptor, which defaults
|
|
71
|
+
* it to `label`. */
|
|
72
|
+
singular: string;
|
|
65
73
|
/** Posts carry a date in the new-entry form; pages do not (concept routing, spec §7.2). */
|
|
66
74
|
dated: boolean;
|
|
67
75
|
entries: EntrySummary[];
|
|
@@ -251,13 +259,16 @@ export function createContentRoutes(runtime: CairnRuntime, deps: ContentRoutesDe
|
|
|
251
259
|
): Promise<EntrySummary> {
|
|
252
260
|
try {
|
|
253
261
|
const raw = await readRaw(repo, file.path, token);
|
|
254
|
-
if (raw === null) return { id: file.id, title: file.id, date: null, draft: false, status };
|
|
255
|
-
const { frontmatter } = parseMarkdown(raw);
|
|
262
|
+
if (raw === null) return { id: file.id, title: file.id, date: null, draft: false, status, summary: null };
|
|
263
|
+
const { frontmatter, body } = parseMarkdown(raw);
|
|
256
264
|
const title = typeof frontmatter.title === 'string' && frontmatter.title.trim() ? frontmatter.title : file.id;
|
|
257
265
|
const date = dateInputValue(frontmatter.date) || null;
|
|
258
|
-
|
|
266
|
+
// Normalize an empty excerpt to null, so a pending row matches EntrySummary's `string | null`
|
|
267
|
+
// contract (the published builder already coalesces with `?? null`).
|
|
268
|
+
const summary = deriveExcerpt(body, { description: asString(frontmatter.description) }) || null;
|
|
269
|
+
return { id: file.id, title, date, draft: frontmatter.draft === true, status, summary };
|
|
259
270
|
} catch {
|
|
260
|
-
return { id: file.id, title: file.id, date: null, draft: false, status };
|
|
271
|
+
return { id: file.id, title: file.id, date: null, draft: false, status, summary: null };
|
|
261
272
|
}
|
|
262
273
|
}
|
|
263
274
|
|
|
@@ -298,7 +309,7 @@ export function createContentRoutes(runtime: CairnRuntime, deps: ContentRoutesDe
|
|
|
298
309
|
const formError = event.url.searchParams.get('error');
|
|
299
310
|
const publishedAllRaw = event.url.searchParams.get('publishedAll');
|
|
300
311
|
const publishedAll = publishedAllRaw !== null && /^\d+$/.test(publishedAllRaw) ? Number(publishedAllRaw) : null;
|
|
301
|
-
const base = { conceptId: concept.id, label: concept.label, dated: concept.routing.dated, formError, publishedAll };
|
|
312
|
+
const base = { conceptId: concept.id, label: concept.label, singular: concept.singular, dated: concept.routing.dated, formError, publishedAll };
|
|
302
313
|
let token: string;
|
|
303
314
|
try {
|
|
304
315
|
token = await mintToken(event.platform?.env ?? {});
|
|
@@ -329,7 +340,7 @@ export function createContentRoutes(runtime: CairnRuntime, deps: ContentRoutesDe
|
|
|
329
340
|
rows.map((e) =>
|
|
330
341
|
pendingIds.has(e.id)
|
|
331
342
|
? pendingRow(concept, e.id, 'edited', token)
|
|
332
|
-
: { id: e.id, title: e.title, date: e.date ?? null, draft: e.draft, status: 'published' as const },
|
|
343
|
+
: { id: e.id, title: e.title, date: e.date ?? null, draft: e.draft, status: 'published' as const, summary: e.summary ?? null },
|
|
333
344
|
),
|
|
334
345
|
);
|
|
335
346
|
const listed = new Set(rows.map((e) => e.id));
|
|
@@ -25,3 +25,5 @@ export { healthLoad, type HealthData } from './health.js';
|
|
|
25
25
|
export type { RequestContext, CookieJar, HandleInput } from './types.js';
|
|
26
26
|
// Re-exported here, not from root, so the public ContentRoutesDeps consumer can name it.
|
|
27
27
|
export type { GithubKeyEnv } from '../github/credentials.js';
|
|
28
|
+
// Re-exported here, not just from root, so the app.d.ts Platform block can name it.
|
|
29
|
+
export type { AuthEnv } from '../auth/types.js';
|
package/src/lib/vite/index.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import type { Plugin, PluginOption } from 'vite';
|
|
9
9
|
import { writeFile, mkdir } from 'node:fs/promises';
|
|
10
10
|
import { dirname, join } from 'node:path';
|
|
11
|
+
import { resolveViteRoot } from './resolve-root.js';
|
|
11
12
|
|
|
12
13
|
/** The key the cairnManifest plugin stashes its options under, so the write path can read them off the
|
|
13
14
|
* plugin instance in the consumer's loaded config without re-parsing the config file. */
|
|
@@ -152,10 +153,12 @@ export function cairnManifest(opts: CairnManifestOptions): Plugin {
|
|
|
152
153
|
}
|
|
153
154
|
|
|
154
155
|
/** Regenerate the committed manifest from the consumer's corpus and write it to the configured
|
|
155
|
-
* manifestPath. It
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
156
|
+
* manifestPath. It searches for the consumer's Vite config from `cwd`, derives the authoritative
|
|
157
|
+
* Vite root from the loaded config (so a configured `root` or a non-root cwd resolves correctly),
|
|
158
|
+
* reads the cairnManifest plugin's options off the instance, evaluates the write-mode virtual
|
|
159
|
+
* module through the build's own resolution, and writes the serialized manifest under the Vite
|
|
160
|
+
* root. The cairn-manifest bin calls this; it is exported so the write logic is testable apart
|
|
161
|
+
* from the CLI shell. */
|
|
159
162
|
export async function writeManifest(cwd: string = process.cwd()): Promise<void> {
|
|
160
163
|
const { loadConfigFromFile } = await import('vite');
|
|
161
164
|
const loaded = await loadConfigFromFile({ command: 'build', mode: 'production' }, undefined, cwd);
|
|
@@ -168,11 +171,12 @@ export async function writeManifest(cwd: string = process.cwd()): Promise<void>
|
|
|
168
171
|
'cairn-manifest: the Vite config has no cairnManifest() plugin. Add it so the bin shares the build options.',
|
|
169
172
|
);
|
|
170
173
|
}
|
|
171
|
-
const
|
|
174
|
+
const root = resolveViteRoot(loaded, cwd);
|
|
175
|
+
const serialized = await buildManifestFromVite(opts, root);
|
|
172
176
|
const manifestPath = opts.manifestPath ?? DEFAULT_MANIFEST_PATH;
|
|
173
177
|
// The manifest path is app-root-absolute (a leading slash relative to the project), so resolve it
|
|
174
|
-
// against
|
|
175
|
-
const outPath = join(
|
|
178
|
+
// against the Vite root, not the filesystem root or the config-search cwd.
|
|
179
|
+
const outPath = join(root, manifestPath.replace(/^\//, ''));
|
|
176
180
|
await mkdir(dirname(outPath), { recursive: true });
|
|
177
181
|
await writeFile(outPath, serialized);
|
|
178
182
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// The manifest bin's root derivation, split out so it is unit-testable without widening the public
|
|
2
|
+
// /vite surface (only src/lib/vite/index.ts is the package subpath; this sibling is internal).
|
|
3
|
+
import { dirname, isAbsolute, resolve } from 'node:path';
|
|
4
|
+
|
|
5
|
+
/** The shape of `loadConfigFromFile`'s result that the root derivation reads: the config file's own
|
|
6
|
+
* path and its `root` field. Typed structurally so the helper is testable without a real load. */
|
|
7
|
+
export interface LoadedViteConfig {
|
|
8
|
+
/** The resolved path of the config file Vite loaded. */
|
|
9
|
+
path: string;
|
|
10
|
+
/** The user config, of which only `root` is read here. */
|
|
11
|
+
config: { root?: string };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** The authoritative Vite root for the manifest bin, derived from the loaded config the way Vite
|
|
15
|
+
* resolves a relative `root`: against the config file's own directory, not cwd. An absolute `root`
|
|
16
|
+
* stands as given, and no `root` falls back to `cwd` (the directory the bin was run from). This
|
|
17
|
+
* separates the config-search dir (cwd) from the Vite root, so a non-root cwd or a config that
|
|
18
|
+
* sets `root` reads and writes the manifest under the real app root. */
|
|
19
|
+
export function resolveViteRoot(loaded: LoadedViteConfig, cwd: string): string {
|
|
20
|
+
const root = loaded.config.root;
|
|
21
|
+
if (!root) return cwd;
|
|
22
|
+
if (isAbsolute(root)) return root;
|
|
23
|
+
return resolve(dirname(loaded.path), root);
|
|
24
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|