@topik/core 0.1.0-alpha.1 → 0.1.0-alpha.3
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/LICENSE +21 -0
- package/dist/index.d.mts +14 -76
- package/dist/index.mjs +226 -12
- package/package.json +24 -19
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 The Topik Authors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { Guide, Wiki, WikiPage } from "@topik/schema";
|
|
2
|
+
import { Asset, Guide, Wiki, WikiPage } from "@topik/schema";
|
|
3
3
|
|
|
4
4
|
//#region src/config/collection.d.ts
|
|
5
5
|
declare const collectionConfigSchema: z.ZodObject<{
|
|
6
6
|
id: z.ZodString;
|
|
7
7
|
title: z.ZodString;
|
|
8
|
-
tags: z.ZodOptional<z.ZodArray<z.ZodString
|
|
9
|
-
},
|
|
10
|
-
id: string;
|
|
11
|
-
title: string;
|
|
12
|
-
tags?: string[] | undefined;
|
|
13
|
-
}, {
|
|
14
|
-
id: string;
|
|
15
|
-
title: string;
|
|
16
|
-
tags?: string[] | undefined;
|
|
17
|
-
}>;
|
|
8
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
9
|
+
}, z.core.$strip>;
|
|
18
10
|
type CollectionConfig = z.infer<typeof collectionConfigSchema>;
|
|
19
11
|
declare function parseCollectionConfig(raw: unknown): CollectionConfig;
|
|
20
12
|
//#endregion
|
|
@@ -37,81 +29,27 @@ type WikiNavNode = string | {
|
|
|
37
29
|
declare const wikiConfigSchema: z.ZodObject<{
|
|
38
30
|
id: z.ZodString;
|
|
39
31
|
title: z.ZodString;
|
|
40
|
-
navigation: z.ZodOptional<z.ZodArray<z.ZodType<WikiNavNode, z.
|
|
32
|
+
navigation: z.ZodOptional<z.ZodArray<z.ZodType<WikiNavNode, unknown, z.core.$ZodTypeInternals<WikiNavNode, unknown>>>>;
|
|
41
33
|
theme: z.ZodOptional<z.ZodObject<{
|
|
42
34
|
colors: z.ZodOptional<z.ZodObject<{
|
|
43
35
|
primary: z.ZodString;
|
|
44
36
|
light: z.ZodOptional<z.ZodString>;
|
|
45
37
|
dark: z.ZodOptional<z.ZodString>;
|
|
46
|
-
},
|
|
47
|
-
primary: string;
|
|
48
|
-
light?: string | undefined;
|
|
49
|
-
dark?: string | undefined;
|
|
50
|
-
}, {
|
|
51
|
-
primary: string;
|
|
52
|
-
light?: string | undefined;
|
|
53
|
-
dark?: string | undefined;
|
|
54
|
-
}>>;
|
|
38
|
+
}, z.core.$strip>>;
|
|
55
39
|
appearance: z.ZodOptional<z.ZodObject<{
|
|
56
|
-
default: z.ZodOptional<z.ZodEnum<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}>>;
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
primary: string;
|
|
65
|
-
light?: string | undefined;
|
|
66
|
-
dark?: string | undefined;
|
|
67
|
-
} | undefined;
|
|
68
|
-
appearance?: {
|
|
69
|
-
default?: "dark" | "light" | "system" | undefined;
|
|
70
|
-
} | undefined;
|
|
71
|
-
}, {
|
|
72
|
-
colors?: {
|
|
73
|
-
primary: string;
|
|
74
|
-
light?: string | undefined;
|
|
75
|
-
dark?: string | undefined;
|
|
76
|
-
} | undefined;
|
|
77
|
-
appearance?: {
|
|
78
|
-
default?: "dark" | "light" | "system" | undefined;
|
|
79
|
-
} | undefined;
|
|
80
|
-
}>>;
|
|
81
|
-
}, "strip", z.ZodTypeAny, {
|
|
82
|
-
id: string;
|
|
83
|
-
title: string;
|
|
84
|
-
navigation?: WikiNavNode[] | undefined;
|
|
85
|
-
theme?: {
|
|
86
|
-
colors?: {
|
|
87
|
-
primary: string;
|
|
88
|
-
light?: string | undefined;
|
|
89
|
-
dark?: string | undefined;
|
|
90
|
-
} | undefined;
|
|
91
|
-
appearance?: {
|
|
92
|
-
default?: "dark" | "light" | "system" | undefined;
|
|
93
|
-
} | undefined;
|
|
94
|
-
} | undefined;
|
|
95
|
-
}, {
|
|
96
|
-
id: string;
|
|
97
|
-
title: string;
|
|
98
|
-
navigation?: unknown[] | undefined;
|
|
99
|
-
theme?: {
|
|
100
|
-
colors?: {
|
|
101
|
-
primary: string;
|
|
102
|
-
light?: string | undefined;
|
|
103
|
-
dark?: string | undefined;
|
|
104
|
-
} | undefined;
|
|
105
|
-
appearance?: {
|
|
106
|
-
default?: "dark" | "light" | "system" | undefined;
|
|
107
|
-
} | undefined;
|
|
108
|
-
} | undefined;
|
|
109
|
-
}>;
|
|
40
|
+
default: z.ZodOptional<z.ZodEnum<{
|
|
41
|
+
dark: "dark";
|
|
42
|
+
light: "light";
|
|
43
|
+
system: "system";
|
|
44
|
+
}>>;
|
|
45
|
+
}, z.core.$strip>>;
|
|
46
|
+
}, z.core.$strip>>;
|
|
47
|
+
}, z.core.$strip>;
|
|
110
48
|
type WikiConfig = z.infer<typeof wikiConfigSchema>;
|
|
111
49
|
declare function parseWikiConfig(raw: unknown): WikiConfig;
|
|
112
50
|
//#endregion
|
|
113
51
|
//#region src/resource.d.ts
|
|
114
|
-
type Resource = Guide | Wiki | WikiPage;
|
|
52
|
+
type Resource = Asset | Guide | Wiki | WikiPage;
|
|
115
53
|
type ResourceType = Resource["type"];
|
|
116
54
|
//#endregion
|
|
117
55
|
//#region src/compile/shared.d.ts
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { join, resolve } from "node:path";
|
|
2
|
+
import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
3
3
|
import { access, readFile, readdir } from "node:fs/promises";
|
|
4
4
|
import { parse } from "yaml";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import Markdoc from "@markdoc/markdoc";
|
|
5
7
|
import Ajv2020 from "ajv/dist/2020.js";
|
|
6
8
|
import addFormats from "ajv-formats";
|
|
7
|
-
import { guideSchema, wikiPageSchema, wikiSchema } from "@topik/schema";
|
|
9
|
+
import { assetSchema, guideSchema, wikiPageSchema, wikiSchema } from "@topik/schema";
|
|
8
10
|
import { EventEmitter } from "node:events";
|
|
9
11
|
import { watch as watch$1 } from "chokidar";
|
|
10
12
|
//#region src/config/collection.ts
|
|
@@ -91,6 +93,195 @@ async function findConfigFile(dir, candidates) {
|
|
|
91
93
|
return null;
|
|
92
94
|
}
|
|
93
95
|
//#endregion
|
|
96
|
+
//#region src/compile/assets.ts
|
|
97
|
+
const MIME_BY_EXT = {
|
|
98
|
+
".png": "image/png",
|
|
99
|
+
".jpg": "image/jpeg",
|
|
100
|
+
".jpeg": "image/jpeg",
|
|
101
|
+
".gif": "image/gif",
|
|
102
|
+
".svg": "image/svg+xml",
|
|
103
|
+
".webp": "image/webp",
|
|
104
|
+
".avif": "image/avif",
|
|
105
|
+
".ico": "image/x-icon",
|
|
106
|
+
".bmp": "image/bmp",
|
|
107
|
+
".mp4": "video/mp4",
|
|
108
|
+
".webm": "video/webm",
|
|
109
|
+
".pdf": "application/pdf",
|
|
110
|
+
".zip": "application/zip",
|
|
111
|
+
".tar": "application/x-tar",
|
|
112
|
+
".gz": "application/gzip",
|
|
113
|
+
".mp3": "audio/mpeg",
|
|
114
|
+
".wav": "audio/wav",
|
|
115
|
+
".ogg": "audio/ogg",
|
|
116
|
+
".m4a": "audio/mp4",
|
|
117
|
+
".flac": "audio/flac",
|
|
118
|
+
".csv": "text/csv",
|
|
119
|
+
".txt": "text/plain",
|
|
120
|
+
".doc": "application/msword",
|
|
121
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
122
|
+
".xls": "application/vnd.ms-excel",
|
|
123
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
124
|
+
".ppt": "application/vnd.ms-powerpoint",
|
|
125
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
126
|
+
};
|
|
127
|
+
const ASSET_TAG_ATTRS = ["src", "href"];
|
|
128
|
+
async function extractAssets(source, options) {
|
|
129
|
+
const { baseDir, filePath } = options;
|
|
130
|
+
const ast = Markdoc.parse(source);
|
|
131
|
+
const refs = /* @__PURE__ */ new Map();
|
|
132
|
+
const slots = /* @__PURE__ */ new Map();
|
|
133
|
+
const collect = (url, slot, requireKnownExtension) => {
|
|
134
|
+
if (!isLocalRef(url)) return;
|
|
135
|
+
if (requireKnownExtension && !hasKnownAssetExtension(url)) return;
|
|
136
|
+
if (!refs.has(url)) {
|
|
137
|
+
const absPath = resolveAssetPath(url, {
|
|
138
|
+
baseDir,
|
|
139
|
+
filePath
|
|
140
|
+
});
|
|
141
|
+
const relFromBase = relative(baseDir, absPath).split(sep).join(posix.sep);
|
|
142
|
+
refs.set(url, {
|
|
143
|
+
original: url,
|
|
144
|
+
absPath,
|
|
145
|
+
relFromBase
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
const list = slots.get(url) ?? [];
|
|
149
|
+
list.push(slot);
|
|
150
|
+
slots.set(url, list);
|
|
151
|
+
};
|
|
152
|
+
walk(ast, (node) => {
|
|
153
|
+
if (node.type === "image") {
|
|
154
|
+
const url = stringAttr(node, "src");
|
|
155
|
+
if (url) collect(url, {
|
|
156
|
+
node,
|
|
157
|
+
attr: "src"
|
|
158
|
+
}, false);
|
|
159
|
+
} else if (node.type === "link") {
|
|
160
|
+
const url = stringAttr(node, "href");
|
|
161
|
+
if (url) collect(url, {
|
|
162
|
+
node,
|
|
163
|
+
attr: "href"
|
|
164
|
+
}, true);
|
|
165
|
+
} else if (node.type === "tag") for (const attr of ASSET_TAG_ATTRS) {
|
|
166
|
+
const url = stringAttr(node, attr);
|
|
167
|
+
if (url) collect(url, {
|
|
168
|
+
node,
|
|
169
|
+
attr
|
|
170
|
+
}, true);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
if (refs.size === 0) return {
|
|
174
|
+
content: source,
|
|
175
|
+
assets: [],
|
|
176
|
+
manifest: []
|
|
177
|
+
};
|
|
178
|
+
const urlToId = /* @__PURE__ */ new Map();
|
|
179
|
+
const byName = /* @__PURE__ */ new Map();
|
|
180
|
+
const refList = Array.from(refs.values());
|
|
181
|
+
const settled = await Promise.allSettled(refList.map((ref) => toAsset(ref)));
|
|
182
|
+
const failures = [];
|
|
183
|
+
for (let i = 0; i < settled.length; i++) {
|
|
184
|
+
const result = settled[i];
|
|
185
|
+
if (result.status === "fulfilled") {
|
|
186
|
+
urlToId.set(refList[i].original, result.value.name);
|
|
187
|
+
byName.set(result.value.name, result.value);
|
|
188
|
+
} else failures.push(result.reason instanceof Error ? result.reason.message : String(result.reason));
|
|
189
|
+
}
|
|
190
|
+
if (failures.length > 0) throw new Error(`Failed to extract ${failures.length} asset(s):\n - ${failures.join("\n - ")}`);
|
|
191
|
+
const manifest = [];
|
|
192
|
+
const seen = /* @__PURE__ */ new Set();
|
|
193
|
+
for (const ref of refs.values()) {
|
|
194
|
+
const name = urlToId.get(ref.original);
|
|
195
|
+
if (name && !seen.has(name)) {
|
|
196
|
+
manifest.push(name);
|
|
197
|
+
seen.add(name);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
for (const [url, slotList] of slots.entries()) {
|
|
201
|
+
const id = urlToId.get(url);
|
|
202
|
+
if (!id) continue;
|
|
203
|
+
for (const slot of slotList) slot.node.attributes[slot.attr] = `asset:${id}`;
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
content: Markdoc.format(ast),
|
|
207
|
+
assets: Array.from(byName.values()),
|
|
208
|
+
manifest
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function walk(node, fn) {
|
|
212
|
+
fn(node);
|
|
213
|
+
for (const child of node.children) walk(child, fn);
|
|
214
|
+
}
|
|
215
|
+
function stringAttr(node, attr) {
|
|
216
|
+
const value = node.attributes?.[attr];
|
|
217
|
+
return typeof value === "string" ? value : void 0;
|
|
218
|
+
}
|
|
219
|
+
function hasKnownAssetExtension(url) {
|
|
220
|
+
const [path] = splitUrl(url);
|
|
221
|
+
const decoded = decodeSafe(path);
|
|
222
|
+
const m = /\.([a-z0-9]+)$/i.exec(decoded);
|
|
223
|
+
if (!m) return false;
|
|
224
|
+
return `.${m[1].toLowerCase()}` in MIME_BY_EXT;
|
|
225
|
+
}
|
|
226
|
+
function isLocalRef(url) {
|
|
227
|
+
if (url.length === 0) return false;
|
|
228
|
+
if (url.startsWith("#")) return false;
|
|
229
|
+
if (url.startsWith("mailto:")) return false;
|
|
230
|
+
if (url.startsWith("tel:")) return false;
|
|
231
|
+
if (url.startsWith("data:")) return false;
|
|
232
|
+
if (url.startsWith("//")) return false;
|
|
233
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(url)) return false;
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
function resolveAssetPath(url, { baseDir, filePath }) {
|
|
237
|
+
const [pathPart] = splitUrl(url);
|
|
238
|
+
const decoded = decodeSafe(pathPart);
|
|
239
|
+
const absolute = resolve(isAbsolute(decoded) || decoded.startsWith("/") ? baseDir : dirname(filePath), decoded.replace(/^\/+/, ""));
|
|
240
|
+
const rel = relative(baseDir, absolute);
|
|
241
|
+
if (rel.startsWith("..") || isAbsolute(rel)) throw new Error(`Asset reference "${url}" in ${filePath} resolves outside the compilation directory`);
|
|
242
|
+
return absolute;
|
|
243
|
+
}
|
|
244
|
+
function splitUrl(url) {
|
|
245
|
+
const hashIdx = url.indexOf("#");
|
|
246
|
+
const queryIdx = url.indexOf("?");
|
|
247
|
+
let cut = -1;
|
|
248
|
+
if (hashIdx >= 0) cut = hashIdx;
|
|
249
|
+
if (queryIdx >= 0 && (cut < 0 || queryIdx < cut)) cut = queryIdx;
|
|
250
|
+
if (cut < 0) return [url, ""];
|
|
251
|
+
return [url.slice(0, cut), url.slice(cut)];
|
|
252
|
+
}
|
|
253
|
+
function decodeSafe(s) {
|
|
254
|
+
try {
|
|
255
|
+
return decodeURIComponent(s);
|
|
256
|
+
} catch {
|
|
257
|
+
return s;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
async function toAsset(ref) {
|
|
261
|
+
let bytes;
|
|
262
|
+
try {
|
|
263
|
+
bytes = await readFile(ref.absPath);
|
|
264
|
+
} catch (error) {
|
|
265
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
266
|
+
throw new Error(`Asset "${ref.original}" not found at ${ref.absPath}: ${message}`, { cause: error });
|
|
267
|
+
}
|
|
268
|
+
const digest = createHash("sha256").update(bytes).digest();
|
|
269
|
+
const integrity = `sha256-${digest.toString("base64")}`;
|
|
270
|
+
const name = digest.toString("hex").slice(0, 16);
|
|
271
|
+
const extMatch = /\.([a-z0-9]+)(?:[?#].*)?$/i.exec(ref.relFromBase);
|
|
272
|
+
const mediaType = MIME_BY_EXT[extMatch ? `.${extMatch[1].toLowerCase()}` : ""];
|
|
273
|
+
return {
|
|
274
|
+
apiVersion: "v1",
|
|
275
|
+
type: "Asset",
|
|
276
|
+
name,
|
|
277
|
+
spec: {
|
|
278
|
+
uri: ref.relFromBase,
|
|
279
|
+
integrity,
|
|
280
|
+
...mediaType ? { mediaType } : {}
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
//#endregion
|
|
94
285
|
//#region src/compile/shared.ts
|
|
95
286
|
const DNS_LABEL_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
96
287
|
function parseMarkdownFrontmatter(raw, filePath) {
|
|
@@ -143,15 +334,26 @@ async function compileWiki(options) {
|
|
|
143
334
|
if (raw == null) return { resources: [] };
|
|
144
335
|
const config = parseWikiConfig(raw);
|
|
145
336
|
const pagePaths = config.navigation ? [...new Set(collectPagePaths(config.navigation))] : [];
|
|
146
|
-
const
|
|
147
|
-
|
|
337
|
+
const resolvedFiles = await Promise.all(pagePaths.map(async (pagePath) => {
|
|
338
|
+
const filePath = await resolveFilePath(dir, pagePath);
|
|
339
|
+
return {
|
|
340
|
+
filePath,
|
|
341
|
+
raw: await readFile(filePath, "utf-8")
|
|
342
|
+
};
|
|
148
343
|
}));
|
|
149
344
|
const resources = [];
|
|
345
|
+
const assetsById = /* @__PURE__ */ new Map();
|
|
150
346
|
for (let i = 0; i < pagePaths.length; i++) {
|
|
151
347
|
const pagePath = pagePaths[i];
|
|
152
|
-
const {
|
|
348
|
+
const { filePath, raw } = resolvedFiles[i];
|
|
349
|
+
const { frontmatter, content } = parseMarkdownFrontmatter(raw, pagePath);
|
|
350
|
+
const { content: rewritten, assets, manifest } = await extractAssets(content, {
|
|
351
|
+
baseDir: dir,
|
|
352
|
+
filePath
|
|
353
|
+
});
|
|
354
|
+
for (const asset of assets) if (!assetsById.has(asset.name)) assetsById.set(asset.name, asset);
|
|
153
355
|
const name = pagePathToName(pagePath);
|
|
154
|
-
const title = typeof frontmatter.title === "string" ? frontmatter.title : extractMarkdownTitle(
|
|
356
|
+
const title = typeof frontmatter.title === "string" ? frontmatter.title : extractMarkdownTitle(rewritten, name);
|
|
155
357
|
const pageResource = {
|
|
156
358
|
apiVersion: "v1",
|
|
157
359
|
type: "WikiPage",
|
|
@@ -161,12 +363,14 @@ async function compileWiki(options) {
|
|
|
161
363
|
title,
|
|
162
364
|
content: {
|
|
163
365
|
format: "topik",
|
|
164
|
-
value:
|
|
165
|
-
}
|
|
366
|
+
value: rewritten
|
|
367
|
+
},
|
|
368
|
+
...manifest.length > 0 ? { assets: manifest } : {}
|
|
166
369
|
}
|
|
167
370
|
};
|
|
168
371
|
resources.push(pageResource);
|
|
169
372
|
}
|
|
373
|
+
for (const asset of assetsById.values()) resources.push(asset);
|
|
170
374
|
const wikiResource = {
|
|
171
375
|
apiVersion: "v1",
|
|
172
376
|
type: "Wiki",
|
|
@@ -242,11 +446,18 @@ async function compileGuides(options) {
|
|
|
242
446
|
const config = parseCollectionConfig(raw);
|
|
243
447
|
const markdownFiles = (await readdir(dir)).filter((f) => f.endsWith(".md") || f.endsWith(".mdx")).sort();
|
|
244
448
|
const resources = [];
|
|
449
|
+
const assetsById = /* @__PURE__ */ new Map();
|
|
245
450
|
for (const file of markdownFiles) {
|
|
246
|
-
const
|
|
451
|
+
const filePath = join(dir, file);
|
|
452
|
+
const { frontmatter, content } = parseMarkdownFrontmatter(await readFile(filePath, "utf-8"), file);
|
|
453
|
+
const { content: rewritten, assets, manifest } = await extractAssets(content, {
|
|
454
|
+
baseDir: dir,
|
|
455
|
+
filePath
|
|
456
|
+
});
|
|
457
|
+
for (const asset of assets) if (!assetsById.has(asset.name)) assetsById.set(asset.name, asset);
|
|
247
458
|
const slug = fileToSlug(file);
|
|
248
459
|
const name = `${config.id}-${slug}`;
|
|
249
|
-
const title = typeof frontmatter.title === "string" ? frontmatter.title : extractMarkdownTitle(
|
|
460
|
+
const title = typeof frontmatter.title === "string" ? frontmatter.title : extractMarkdownTitle(rewritten, slug);
|
|
250
461
|
const tags = mergeTags(config.tags, frontmatter.tags);
|
|
251
462
|
const authors = parseReferenceList(frontmatter.authors, "authors", file);
|
|
252
463
|
const description = typeof frontmatter.description === "string" ? frontmatter.description : void 0;
|
|
@@ -262,12 +473,14 @@ async function compileGuides(options) {
|
|
|
262
473
|
...tags.length > 0 ? { tags } : {},
|
|
263
474
|
content: {
|
|
264
475
|
format: "topik",
|
|
265
|
-
value:
|
|
266
|
-
}
|
|
476
|
+
value: rewritten
|
|
477
|
+
},
|
|
478
|
+
...manifest.length > 0 ? { assets: manifest } : {}
|
|
267
479
|
}
|
|
268
480
|
};
|
|
269
481
|
resources.push(guide);
|
|
270
482
|
}
|
|
483
|
+
for (const asset of assetsById.values()) resources.push(asset);
|
|
271
484
|
return { resources };
|
|
272
485
|
}
|
|
273
486
|
function fileToSlug(filename) {
|
|
@@ -314,6 +527,7 @@ const ajv = new Ajv2020({
|
|
|
314
527
|
});
|
|
315
528
|
addFormats(ajv);
|
|
316
529
|
const validators = {
|
|
530
|
+
Asset: ajv.compile(assetSchema),
|
|
317
531
|
Guide: ajv.compile(guideSchema),
|
|
318
532
|
Wiki: ajv.compile(wikiSchema),
|
|
319
533
|
WikiPage: ajv.compile(wikiPageSchema)
|
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topik/core",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.3",
|
|
4
4
|
"description": "Topik core library",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/subtopik/topik.git",
|
|
9
|
+
"directory": "packages/core"
|
|
10
|
+
},
|
|
6
11
|
"files": [
|
|
7
12
|
"dist"
|
|
8
13
|
],
|
|
@@ -14,26 +19,26 @@
|
|
|
14
19
|
"publishConfig": {
|
|
15
20
|
"access": "public"
|
|
16
21
|
},
|
|
17
|
-
"scripts": {
|
|
18
|
-
"build": "vp pack",
|
|
19
|
-
"dev": "vp pack --watch",
|
|
20
|
-
"test": "vp test",
|
|
21
|
-
"check": "vp check",
|
|
22
|
-
"prepublishOnly": "vp run build"
|
|
23
|
-
},
|
|
24
22
|
"dependencies": {
|
|
25
|
-
"@
|
|
26
|
-
"ajv": "^8.
|
|
23
|
+
"@markdoc/markdoc": "^0.5.7",
|
|
24
|
+
"ajv": "^8.20.0",
|
|
27
25
|
"ajv-formats": "^3.0.1",
|
|
28
|
-
"chokidar": "^
|
|
29
|
-
"yaml": "^2.
|
|
30
|
-
"zod": "^3.
|
|
26
|
+
"chokidar": "^5.0.0",
|
|
27
|
+
"yaml": "^2.8.3",
|
|
28
|
+
"zod": "^4.3.6",
|
|
29
|
+
"@topik/schema": "0.1.0-alpha.3"
|
|
31
30
|
},
|
|
32
31
|
"devDependencies": {
|
|
33
|
-
"@types/node": "
|
|
34
|
-
"@typescript/native-preview": "
|
|
35
|
-
"typescript": "
|
|
36
|
-
"vite-plus": "
|
|
37
|
-
"vitest": "
|
|
32
|
+
"@types/node": "^24",
|
|
33
|
+
"@typescript/native-preview": "7.0.0-dev.20260427.1",
|
|
34
|
+
"typescript": "^6",
|
|
35
|
+
"vite-plus": "^0.1.18",
|
|
36
|
+
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "vp pack",
|
|
40
|
+
"dev": "vp pack --watch",
|
|
41
|
+
"test": "vp test",
|
|
42
|
+
"check": "vp check"
|
|
38
43
|
}
|
|
39
|
-
}
|
|
44
|
+
}
|