@farming-labs/svelte 0.0.2-beta.13 → 0.0.2-beta.15
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/dist/content.d.ts +2 -1
- package/dist/content.js +75 -30
- package/dist/server.js +62 -4
- package/package.json +2 -2
package/dist/content.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* extracts frontmatter, and builds a navigation tree compatible
|
|
6
6
|
* with @farming-labs/docs DocsConfig.
|
|
7
7
|
*/
|
|
8
|
+
import type { OrderingItem } from "@farming-labs/docs";
|
|
8
9
|
export interface PageNode {
|
|
9
10
|
type: "page";
|
|
10
11
|
name: string;
|
|
@@ -44,7 +45,7 @@ export declare function loadDocsContent(contentDir: string, entry?: string): Con
|
|
|
44
45
|
/**
|
|
45
46
|
* Build a navigation tree from a content directory.
|
|
46
47
|
*/
|
|
47
|
-
export declare function loadDocsNavTree(contentDir: string, entry?: string): NavTree;
|
|
48
|
+
export declare function loadDocsNavTree(contentDir: string, entry?: string, ordering?: "alphabetical" | "numeric" | OrderingItem[]): NavTree;
|
|
48
49
|
/**
|
|
49
50
|
* Flatten a navigation tree into an ordered list of pages.
|
|
50
51
|
* Useful for computing previous/next page links.
|
package/dist/content.js
CHANGED
|
@@ -58,7 +58,7 @@ export function loadDocsContent(contentDir, entry = "docs") {
|
|
|
58
58
|
/**
|
|
59
59
|
* Build a navigation tree from a content directory.
|
|
60
60
|
*/
|
|
61
|
-
export function loadDocsNavTree(contentDir, entry = "docs") {
|
|
61
|
+
export function loadDocsNavTree(contentDir, entry = "docs", ordering) {
|
|
62
62
|
const absDir = path.resolve(contentDir);
|
|
63
63
|
const children = [];
|
|
64
64
|
const indexPath = findIndex(absDir);
|
|
@@ -71,42 +71,87 @@ export function loadDocsNavTree(contentDir, entry = "docs") {
|
|
|
71
71
|
icon: data.icon,
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
|
-
|
|
74
|
+
const rootSlugOrder = Array.isArray(ordering) ? ordering : undefined;
|
|
75
|
+
children.push(...scanDir(absDir, [], entry, ordering, rootSlugOrder));
|
|
75
76
|
return { name: "Docs", children };
|
|
76
77
|
}
|
|
77
|
-
function
|
|
78
|
+
function buildNavNode(dir, name, slugParts, entry, ordering, childSlugOrder) {
|
|
79
|
+
const full = path.join(dir, name);
|
|
80
|
+
if (!fs.statSync(full).isDirectory())
|
|
81
|
+
return null;
|
|
82
|
+
const indexPath = findIndex(full);
|
|
83
|
+
if (!indexPath)
|
|
84
|
+
return null;
|
|
85
|
+
const { data } = matter(fs.readFileSync(indexPath, "utf-8"));
|
|
86
|
+
const slug = [...slugParts, name];
|
|
87
|
+
const url = `/${entry}/${slug.join("/")}`;
|
|
88
|
+
const displayName = data.title ?? name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
89
|
+
const icon = data.icon;
|
|
90
|
+
const childDirs = fs.readdirSync(full).filter((n) => {
|
|
91
|
+
const p = path.join(full, n);
|
|
92
|
+
return fs.statSync(p).isDirectory() && findIndex(p) !== null;
|
|
93
|
+
});
|
|
94
|
+
if (childDirs.length > 0) {
|
|
95
|
+
return {
|
|
96
|
+
type: "folder",
|
|
97
|
+
name: displayName,
|
|
98
|
+
icon,
|
|
99
|
+
index: { type: "page", name: displayName, url, icon },
|
|
100
|
+
children: scanDir(full, slug, entry, ordering, childSlugOrder),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return { type: "page", name: displayName, url, icon };
|
|
104
|
+
}
|
|
105
|
+
function scanDir(dir, slugParts, entry, ordering, slugOrder) {
|
|
78
106
|
if (!fs.existsSync(dir))
|
|
79
107
|
return [];
|
|
80
|
-
const nodes = [];
|
|
81
108
|
const entries = fs.readdirSync(dir).sort();
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (childDirs.length > 0) {
|
|
99
|
-
nodes.push({
|
|
100
|
-
type: "folder",
|
|
101
|
-
name: displayName,
|
|
102
|
-
icon,
|
|
103
|
-
index: { type: "page", name: displayName, url, icon },
|
|
104
|
-
children: scanDir(full, slug, entry),
|
|
105
|
-
});
|
|
109
|
+
if (slugOrder) {
|
|
110
|
+
const nodes = [];
|
|
111
|
+
const slugMap = new Set(slugOrder.map((i) => i.slug));
|
|
112
|
+
for (const item of slugOrder) {
|
|
113
|
+
if (!entries.includes(item.slug))
|
|
114
|
+
continue;
|
|
115
|
+
const node = buildNavNode(dir, item.slug, slugParts, entry, ordering, item.children);
|
|
116
|
+
if (node)
|
|
117
|
+
nodes.push(node);
|
|
118
|
+
}
|
|
119
|
+
for (const name of entries) {
|
|
120
|
+
if (slugMap.has(name))
|
|
121
|
+
continue;
|
|
122
|
+
const node = buildNavNode(dir, name, slugParts, entry, ordering);
|
|
123
|
+
if (node)
|
|
124
|
+
nodes.push(node);
|
|
106
125
|
}
|
|
107
|
-
|
|
108
|
-
|
|
126
|
+
return nodes;
|
|
127
|
+
}
|
|
128
|
+
if (ordering === "numeric") {
|
|
129
|
+
const nodes = [];
|
|
130
|
+
for (const name of entries) {
|
|
131
|
+
const full = path.join(dir, name);
|
|
132
|
+
if (!fs.statSync(full).isDirectory())
|
|
133
|
+
continue;
|
|
134
|
+
const indexPath = findIndex(full);
|
|
135
|
+
if (!indexPath)
|
|
136
|
+
continue;
|
|
137
|
+
const { data } = matter(fs.readFileSync(indexPath, "utf-8"));
|
|
138
|
+
const order = typeof data.order === "number" ? data.order : Infinity;
|
|
139
|
+
const node = buildNavNode(dir, name, slugParts, entry, ordering);
|
|
140
|
+
if (node)
|
|
141
|
+
nodes.push({ order, node });
|
|
109
142
|
}
|
|
143
|
+
nodes.sort((a, b) => {
|
|
144
|
+
if (a.order === b.order)
|
|
145
|
+
return 0;
|
|
146
|
+
return a.order - b.order;
|
|
147
|
+
});
|
|
148
|
+
return nodes.map((n) => n.node);
|
|
149
|
+
}
|
|
150
|
+
const nodes = [];
|
|
151
|
+
for (const name of entries) {
|
|
152
|
+
const node = buildNavNode(dir, name, slugParts, entry, ordering);
|
|
153
|
+
if (node)
|
|
154
|
+
nodes.push(node);
|
|
110
155
|
}
|
|
111
156
|
return nodes;
|
|
112
157
|
}
|
package/dist/server.js
CHANGED
|
@@ -49,7 +49,7 @@ function stripMarkdownText(content) {
|
|
|
49
49
|
.replace(/\n{3,}/g, "\n\n")
|
|
50
50
|
.trim();
|
|
51
51
|
}
|
|
52
|
-
function navTreeFromMap(contentMap, dirPrefix, entry) {
|
|
52
|
+
function navTreeFromMap(contentMap, dirPrefix, entry, ordering) {
|
|
53
53
|
const dirs = [];
|
|
54
54
|
for (const key of Object.keys(contentMap)) {
|
|
55
55
|
if (!key.startsWith(dirPrefix))
|
|
@@ -74,8 +74,10 @@ function navTreeFromMap(contentMap, dirPrefix, entry) {
|
|
|
74
74
|
title: data.title ?? fallbackTitle,
|
|
75
75
|
url,
|
|
76
76
|
icon: data.icon,
|
|
77
|
+
order: typeof data.order === "number" ? data.order : Infinity,
|
|
77
78
|
});
|
|
78
79
|
}
|
|
80
|
+
// Default sort: depth first, then by ordering strategy
|
|
79
81
|
dirs.sort((a, b) => {
|
|
80
82
|
if (a.parts.length !== b.parts.length)
|
|
81
83
|
return a.parts.length - b.parts.length;
|
|
@@ -91,8 +93,19 @@ function navTreeFromMap(contentMap, dirPrefix, entry) {
|
|
|
91
93
|
icon: rootInfo.icon,
|
|
92
94
|
});
|
|
93
95
|
}
|
|
96
|
+
function findSlugOrder(parentParts) {
|
|
97
|
+
if (!Array.isArray(ordering))
|
|
98
|
+
return undefined;
|
|
99
|
+
let items = ordering;
|
|
100
|
+
for (const part of parentParts) {
|
|
101
|
+
const found = items.find((i) => i.slug === part);
|
|
102
|
+
if (!found?.children)
|
|
103
|
+
return undefined;
|
|
104
|
+
items = found.children;
|
|
105
|
+
}
|
|
106
|
+
return items;
|
|
107
|
+
}
|
|
94
108
|
function buildLevel(parentParts) {
|
|
95
|
-
const nodes = [];
|
|
96
109
|
const depth = parentParts.length;
|
|
97
110
|
const directChildren = dirs.filter((d) => {
|
|
98
111
|
if (d.parts.length !== depth + 1)
|
|
@@ -103,6 +116,49 @@ function navTreeFromMap(contentMap, dirPrefix, entry) {
|
|
|
103
116
|
}
|
|
104
117
|
return true;
|
|
105
118
|
});
|
|
119
|
+
const slugOrder = findSlugOrder(parentParts);
|
|
120
|
+
if (slugOrder) {
|
|
121
|
+
const slugMap = new Set(slugOrder.map((i) => i.slug));
|
|
122
|
+
const ordered = [];
|
|
123
|
+
for (const item of slugOrder) {
|
|
124
|
+
const match = directChildren.find((d) => d.parts[depth] === item.slug);
|
|
125
|
+
if (match)
|
|
126
|
+
ordered.push(match);
|
|
127
|
+
}
|
|
128
|
+
for (const child of directChildren) {
|
|
129
|
+
if (!slugMap.has(child.parts[depth]))
|
|
130
|
+
ordered.push(child);
|
|
131
|
+
}
|
|
132
|
+
const nodes = [];
|
|
133
|
+
for (const child of ordered) {
|
|
134
|
+
const hasGrandChildren = dirs.some((d) => {
|
|
135
|
+
if (d.parts.length <= child.parts.length)
|
|
136
|
+
return false;
|
|
137
|
+
return child.parts.every((p, i) => d.parts[i] === p);
|
|
138
|
+
});
|
|
139
|
+
if (hasGrandChildren) {
|
|
140
|
+
nodes.push({
|
|
141
|
+
type: "folder",
|
|
142
|
+
name: child.title,
|
|
143
|
+
icon: child.icon,
|
|
144
|
+
index: { type: "page", name: child.title, url: child.url, icon: child.icon },
|
|
145
|
+
children: buildLevel(child.parts),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
nodes.push({ type: "page", name: child.title, url: child.url, icon: child.icon });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return nodes;
|
|
153
|
+
}
|
|
154
|
+
if (ordering === "numeric") {
|
|
155
|
+
directChildren.sort((a, b) => {
|
|
156
|
+
if (a.order === b.order)
|
|
157
|
+
return 0;
|
|
158
|
+
return a.order - b.order;
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
const nodes = [];
|
|
106
162
|
for (const child of directChildren) {
|
|
107
163
|
const hasGrandChildren = dirs.some((d) => {
|
|
108
164
|
if (d.parts.length <= child.parts.length)
|
|
@@ -206,6 +262,7 @@ export function createDocsServer(config = {}) {
|
|
|
206
262
|
const preloaded = config._preloadedContent;
|
|
207
263
|
const contentDirRel = config.contentDir ?? entry;
|
|
208
264
|
const dirPrefix = `/${contentDirRel}/`;
|
|
265
|
+
const ordering = config.ordering;
|
|
209
266
|
const aiConfig = config.ai ?? {};
|
|
210
267
|
// Allow top-level apiKey as a shorthand
|
|
211
268
|
if (config.apiKey && !aiConfig.apiKey) {
|
|
@@ -214,8 +271,8 @@ export function createDocsServer(config = {}) {
|
|
|
214
271
|
// ─── Unified load (tree + page content in one call) ────────
|
|
215
272
|
async function load(event) {
|
|
216
273
|
const tree = preloaded
|
|
217
|
-
? navTreeFromMap(preloaded, dirPrefix, entry)
|
|
218
|
-
: loadDocsNavTree(contentDir, entry);
|
|
274
|
+
? navTreeFromMap(preloaded, dirPrefix, entry, ordering)
|
|
275
|
+
: loadDocsNavTree(contentDir, entry, ordering);
|
|
219
276
|
const flatPages = flattenNavTree(tree);
|
|
220
277
|
const urlPrefix = new RegExp(`^/${entry}/?`);
|
|
221
278
|
const slug = event.url.pathname.replace(urlPrefix, "");
|
|
@@ -379,6 +436,7 @@ export function createDocsServer(config = {}) {
|
|
|
379
436
|
}), { status: 404, headers: { "Content-Type": "application/json" } });
|
|
380
437
|
}
|
|
381
438
|
const resolvedKey = aiConfig.apiKey ??
|
|
439
|
+
(typeof import.meta !== "undefined" ? import.meta.env?.OPENAI_API_KEY : undefined) ??
|
|
382
440
|
(typeof process !== "undefined" ? process.env?.OPENAI_API_KEY : undefined);
|
|
383
441
|
if (!resolvedKey) {
|
|
384
442
|
return new Response(JSON.stringify({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farming-labs/svelte",
|
|
3
|
-
"version": "0.0.2-beta.
|
|
3
|
+
"version": "0.0.2-beta.15",
|
|
4
4
|
"description": "SvelteKit adapter for @farming-labs/docs — content loading and navigation utilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/node": "^22.10.0",
|
|
52
52
|
"typescript": "^5.9.3",
|
|
53
|
-
"@farming-labs/docs": "0.0.2-beta.
|
|
53
|
+
"@farming-labs/docs": "0.0.2-beta.15"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@farming-labs/docs": "*"
|