@clarify-labs/cli 0.8.0 → 0.9.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/README.md +7 -0
- package/output/cli/program.d.ts +2 -0
- package/output/cli.cjs +32 -10
- package/output/cli.cjs.map +1 -1
- package/output/cli.js +32 -10
- package/output/cli.js.map +1 -1
- package/output/core/config-schema.d.ts +5 -0
- package/output/core/content.d.ts +5 -0
- package/output/core/dev-routes.d.ts +58 -0
- package/output/core/options.d.ts +1 -1
- package/output/core/project-info.d.ts +42 -0
- package/output/core/user-config.d.ts +6 -0
- package/output/index.cjs +1 -1
- package/output/index.js +1 -1
- package/output/parsers/content.d.ts +11 -0
- package/output/parsers/routes.d.ts +6 -2
- package/output/parsers/variables.d.ts +2 -0
- package/output/{plugin-B7c0iHMy.cjs → plugin-JvxU6ZjU.cjs} +462 -107
- package/output/plugin-JvxU6ZjU.cjs.map +1 -0
- package/output/{plugin-CcD5HeTN.js → plugin-_mqQSBqE.js} +463 -108
- package/output/plugin-_mqQSBqE.js.map +1 -0
- package/output/plugins/openapi/parser.d.ts +2 -1
- package/output/plugins/variables/index.d.ts +2 -0
- package/output/types.d.ts +20 -0
- package/package.json +3 -3
- package/output/plugin-B7c0iHMy.cjs.map +0 -1
- package/output/plugin-CcD5HeTN.js.map +0 -1
|
@@ -36,14 +36,14 @@ shiki = __toESM(shiki, 1);
|
|
|
36
36
|
let unist_util_visit = require("unist-util-visit");
|
|
37
37
|
let _clarify_labs_renderer = require("@clarify-labs/renderer");
|
|
38
38
|
let yaml = require("yaml");
|
|
39
|
-
let remark = require("remark");
|
|
40
39
|
let gray_matter = require("gray-matter");
|
|
41
40
|
gray_matter = __toESM(gray_matter, 1);
|
|
41
|
+
let remark = require("remark");
|
|
42
|
+
let node_url = require("node:url");
|
|
42
43
|
let _apidevtools_swagger_parser = require("@apidevtools/swagger-parser");
|
|
43
44
|
_apidevtools_swagger_parser = __toESM(_apidevtools_swagger_parser, 1);
|
|
44
45
|
let node_os = require("node:os");
|
|
45
46
|
let zod = require("zod");
|
|
46
|
-
let node_url = require("node:url");
|
|
47
47
|
let vite = require("vite");
|
|
48
48
|
let node_module = require("node:module");
|
|
49
49
|
//#region source/core/utils.ts
|
|
@@ -169,23 +169,76 @@ function readRouteArtifactContent(route) {
|
|
|
169
169
|
const content = readRouteContent(route);
|
|
170
170
|
return shouldUseUtf8Signature(route) ? withUtf8Signature(content) : content;
|
|
171
171
|
}
|
|
172
|
+
function isLlmsTxtRoute(route) {
|
|
173
|
+
return !route.path.split("/").includes("404");
|
|
174
|
+
}
|
|
175
|
+
function llmsTxtDescription(route) {
|
|
176
|
+
if (route.description) return route.description;
|
|
177
|
+
const sections = route.sections?.filter((section) => section.level === 2).slice(0, 3).map((section) => section.title);
|
|
178
|
+
if (sections?.length) return `Covers ${sections.join(", ")}.`;
|
|
179
|
+
if (route.keywords?.length) return `Related topics: ${route.keywords.join(", ")}.`;
|
|
180
|
+
if (route.kind === "openapi") return "OpenAPI artifact for machine-readable API reference data.";
|
|
181
|
+
}
|
|
182
|
+
function llmsTxtListItem(route, basePath) {
|
|
183
|
+
if (!route.contentArtifactUrl) return void 0;
|
|
184
|
+
const description = llmsTxtDescription(route);
|
|
185
|
+
return description ? `- [${route.title}](${basePath}${route.contentArtifactUrl}): ${description}` : `- [${route.title}](${basePath}${route.contentArtifactUrl})`;
|
|
186
|
+
}
|
|
187
|
+
function llmsTxtLocaleLabel(locale, projectConfig) {
|
|
188
|
+
return projectConfig.i18n?.locales.find((item) => item.code === locale)?.label ?? locale;
|
|
189
|
+
}
|
|
190
|
+
function groupRoutesByLocale(routes) {
|
|
191
|
+
const groups = /* @__PURE__ */ new Map();
|
|
192
|
+
for (const route of routes) {
|
|
193
|
+
const key = route.locale ?? "default";
|
|
194
|
+
groups.set(key, [...groups.get(key) ?? [], route]);
|
|
195
|
+
}
|
|
196
|
+
return groups;
|
|
197
|
+
}
|
|
198
|
+
function groupLlmsTxtRoutesByLocale(routes, projectConfig) {
|
|
199
|
+
const routesBySource = /* @__PURE__ */ new Map();
|
|
200
|
+
const defaultLocale = projectConfig.i18n?.defaultLocale;
|
|
201
|
+
for (const route of routes) {
|
|
202
|
+
const sourceKey = `${route.locale ?? defaultLocale ?? "default"}:${route.basePath ?? route.contentArtifactUrl ?? route.path}`;
|
|
203
|
+
const previousRoute = routesBySource.get(sourceKey);
|
|
204
|
+
if (!previousRoute || previousRoute.locale && !route.locale) routesBySource.set(sourceKey, route);
|
|
205
|
+
}
|
|
206
|
+
return groupRoutesByLocale([...routesBySource.values()].map((route) => ({
|
|
207
|
+
...route,
|
|
208
|
+
locale: route.locale ?? defaultLocale
|
|
209
|
+
})));
|
|
210
|
+
}
|
|
211
|
+
function llmsTxtDocsSectionTitle(locale, groupCount, projectConfig) {
|
|
212
|
+
if (groupCount <= 1) return "Docs";
|
|
213
|
+
return `Docs - ${locale === "default" ? "Default" : llmsTxtLocaleLabel(locale, projectConfig)}`;
|
|
214
|
+
}
|
|
172
215
|
function createLlmsTxt(routes, projectConfig) {
|
|
173
216
|
const basePath = normalizeBasePath(projectConfig.routePrefix);
|
|
174
217
|
const lines = [`# ${projectConfig.title}`, ""];
|
|
175
218
|
if (projectConfig.description) lines.push(`> ${projectConfig.description}`, "");
|
|
176
|
-
lines.push("
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
219
|
+
lines.push("This file lists the source-ready Markdown and OpenAPI artifacts for this documentation site.", "");
|
|
220
|
+
const mdxRoutes = routes.filter((route) => route.kind === "mdx" && isLlmsTxtRoute(route));
|
|
221
|
+
if (mdxRoutes.length > 0) {
|
|
222
|
+
const localizedGroups = groupLlmsTxtRoutesByLocale(mdxRoutes, projectConfig);
|
|
223
|
+
for (const [locale, localeRoutes] of localizedGroups) {
|
|
224
|
+
lines.push(`## ${llmsTxtDocsSectionTitle(locale, localizedGroups.size, projectConfig)}`);
|
|
225
|
+
for (const route of localeRoutes) {
|
|
226
|
+
const item = llmsTxtListItem(route, basePath);
|
|
227
|
+
if (item) lines.push(item);
|
|
228
|
+
}
|
|
229
|
+
lines.push("");
|
|
230
|
+
}
|
|
180
231
|
}
|
|
181
|
-
const openApiRoutes = routes.filter((route) => route.kind === "openapi");
|
|
232
|
+
const openApiRoutes = routes.filter((route) => route.kind === "openapi" && isLlmsTxtRoute(route));
|
|
182
233
|
if (openApiRoutes.length > 0) {
|
|
183
|
-
lines.
|
|
234
|
+
if (lines.at(-1) !== "") lines.push("");
|
|
235
|
+
lines.push("## OpenAPI");
|
|
184
236
|
for (const route of openApiRoutes) {
|
|
185
|
-
|
|
186
|
-
lines.push(
|
|
237
|
+
const item = llmsTxtListItem(route, basePath);
|
|
238
|
+
if (item) lines.push(item);
|
|
187
239
|
}
|
|
188
240
|
}
|
|
241
|
+
while (lines.at(-1) === "") lines.pop();
|
|
189
242
|
return `${lines.join("\n")}\n`;
|
|
190
243
|
}
|
|
191
244
|
function createLlmsTxtArtifact(routes, projectConfig) {
|
|
@@ -345,6 +398,101 @@ function parseFrontmatter(content) {
|
|
|
345
398
|
};
|
|
346
399
|
}
|
|
347
400
|
//#endregion
|
|
401
|
+
//#region source/parsers/content.ts
|
|
402
|
+
async function transformContent(transform, input) {
|
|
403
|
+
return transform ? await transform(input) : input;
|
|
404
|
+
}
|
|
405
|
+
function createContentProcessor(transform) {
|
|
406
|
+
return {
|
|
407
|
+
async processMdx(source, filePath) {
|
|
408
|
+
const { frontmatter, content } = parseFrontmatter(source);
|
|
409
|
+
const transformed = await transformContent(transform, {
|
|
410
|
+
kind: "mdx",
|
|
411
|
+
source,
|
|
412
|
+
filePath,
|
|
413
|
+
frontmatter,
|
|
414
|
+
content
|
|
415
|
+
});
|
|
416
|
+
return {
|
|
417
|
+
frontmatter: transformed.frontmatter,
|
|
418
|
+
content: transformed.content
|
|
419
|
+
};
|
|
420
|
+
},
|
|
421
|
+
async processText(source, kind, filePath) {
|
|
422
|
+
return (await transformContent(transform, {
|
|
423
|
+
kind,
|
|
424
|
+
source,
|
|
425
|
+
filePath,
|
|
426
|
+
frontmatter: {},
|
|
427
|
+
content: source
|
|
428
|
+
})).content;
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
//#endregion
|
|
433
|
+
//#region source/core/hooks.ts
|
|
434
|
+
async function runHooks(plugins, hookName, input, ctx) {
|
|
435
|
+
let result = input;
|
|
436
|
+
for (const plugin of plugins) {
|
|
437
|
+
const hook = plugin.hooks?.[hookName];
|
|
438
|
+
if (!hook) continue;
|
|
439
|
+
try {
|
|
440
|
+
result = await hook(result, ctx);
|
|
441
|
+
} catch (err) {
|
|
442
|
+
throw new Error(`[clarify] plugin "${plugin.name}" hook "${hookName}" failed: ${err}`, { cause: err });
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return result;
|
|
446
|
+
}
|
|
447
|
+
async function runDevConfigureServerHooks(plugins, server, ctx) {
|
|
448
|
+
for (const plugin of plugins) {
|
|
449
|
+
const hook = plugin.hooks?.["dev:configureServer"];
|
|
450
|
+
if (!hook) continue;
|
|
451
|
+
try {
|
|
452
|
+
await hook(server, ctx);
|
|
453
|
+
} catch (err) {
|
|
454
|
+
throw new Error(`[clarify] plugin "${plugin.name}" hook "dev:configureServer" failed: ${err}`, { cause: err });
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
async function runBuildAssetsHooks(plugins, ctx) {
|
|
459
|
+
const assets = [];
|
|
460
|
+
for (const plugin of plugins) {
|
|
461
|
+
const hook = plugin.hooks?.["build:assets"];
|
|
462
|
+
if (!hook) continue;
|
|
463
|
+
try {
|
|
464
|
+
const pluginAssets = await hook(ctx);
|
|
465
|
+
assets.push(...pluginAssets);
|
|
466
|
+
} catch (err) {
|
|
467
|
+
throw new Error(`[clarify] plugin "${plugin.name}" hook "build:assets" failed: ${err}`, { cause: err });
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return assets;
|
|
471
|
+
}
|
|
472
|
+
async function runBuildDoneHooks(plugins, ctx) {
|
|
473
|
+
for (const plugin of plugins) {
|
|
474
|
+
const hook = plugin.hooks?.["build:done"];
|
|
475
|
+
if (!hook) continue;
|
|
476
|
+
try {
|
|
477
|
+
await hook(ctx);
|
|
478
|
+
} catch (err) {
|
|
479
|
+
throw new Error(`[clarify] plugin "${plugin.name}" hook "build:done" failed: ${err}`, { cause: err });
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
//#endregion
|
|
484
|
+
//#region source/core/content.ts
|
|
485
|
+
var contentProcessors = /* @__PURE__ */ new WeakMap();
|
|
486
|
+
function createProjectContentProcessor(plugins, ctx) {
|
|
487
|
+
return createContentProcessor((input) => runHooks(plugins, "content:transform", input, ctx));
|
|
488
|
+
}
|
|
489
|
+
function setProjectContentProcessor(ctx, processor) {
|
|
490
|
+
contentProcessors.set(ctx, processor);
|
|
491
|
+
}
|
|
492
|
+
function getProjectContentProcessor(ctx) {
|
|
493
|
+
return contentProcessors.get(ctx);
|
|
494
|
+
}
|
|
495
|
+
//#endregion
|
|
348
496
|
//#region source/parsers/routes.ts
|
|
349
497
|
function kebabToTitle(str) {
|
|
350
498
|
return str.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
@@ -421,18 +569,19 @@ function resolveLocalizedText(text, locale, fallbackLocale) {
|
|
|
421
569
|
if (text === void 0) return void 0;
|
|
422
570
|
return text[locale] ?? (fallbackLocale ? text[fallbackLocale] : void 0) ?? Object.values(text)[0];
|
|
423
571
|
}
|
|
424
|
-
function findContentRoutes(dir, base = dir) {
|
|
572
|
+
async function findContentRoutes(dir, base = dir, options = {}) {
|
|
425
573
|
const routes = [];
|
|
426
574
|
if (!(0, node_fs.existsSync)(dir)) return routes;
|
|
427
575
|
const entries = (0, node_fs.readdirSync)(dir, { withFileTypes: true });
|
|
428
576
|
for (const entry of entries) {
|
|
429
577
|
const fullPath = (0, node_path.join)(dir, entry.name);
|
|
430
|
-
if (entry.isDirectory()) routes.push(...findContentRoutes(fullPath, base));
|
|
578
|
+
if (entry.isDirectory()) routes.push(...await findContentRoutes(fullPath, base, options));
|
|
431
579
|
else if (entry.isFile() && /\.mdx?$/.test(entry.name)) {
|
|
432
580
|
const relativePath = (0, node_path.relative)(base, fullPath);
|
|
433
581
|
const pathParts = relativePath.replace(/\.mdx?$/, "").split("/");
|
|
434
582
|
const cleanPath = ("/" + pathParts.map((p) => p === "index" ? "" : p).filter(Boolean).join("/")).replace(/\/+/g, "/").replace(/\/$/, "") || "/";
|
|
435
|
-
const
|
|
583
|
+
const source = (0, node_fs.readFileSync)(fullPath, "utf-8");
|
|
584
|
+
const { frontmatter, content } = await (options.contentProcessor ?? createContentProcessor()).processMdx(source, fullPath);
|
|
436
585
|
let title = typeof frontmatter.title === "string" ? frontmatter.title : "";
|
|
437
586
|
if (!title) {
|
|
438
587
|
const lastPart = pathParts[pathParts.length - 1] ?? "";
|
|
@@ -689,6 +838,13 @@ var OPENAPI_HTTP_METHODS = [
|
|
|
689
838
|
function errorMessage(error) {
|
|
690
839
|
return error instanceof Error ? error.message : String(error);
|
|
691
840
|
}
|
|
841
|
+
function isSameOpenAPIFile(url, filePath) {
|
|
842
|
+
try {
|
|
843
|
+
return (0, node_path.resolve)((0, node_url.fileURLToPath)(url)) === (0, node_path.resolve)(filePath);
|
|
844
|
+
} catch {
|
|
845
|
+
return (0, node_path.resolve)(url) === (0, node_path.resolve)(filePath);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
692
848
|
function findOpenAPIRoutes(dir, base = dir) {
|
|
693
849
|
const routes = [];
|
|
694
850
|
if (!(0, node_fs.existsSync)(dir)) return routes;
|
|
@@ -752,12 +908,26 @@ function extractOpenAPISections(spec, filterTags) {
|
|
|
752
908
|
}
|
|
753
909
|
return sections;
|
|
754
910
|
}
|
|
755
|
-
async function readOpenAPISpec(filePath) {
|
|
911
|
+
async function readOpenAPISpec(filePath, contentProcessor) {
|
|
756
912
|
try {
|
|
757
|
-
return {
|
|
913
|
+
if (!contentProcessor) return {
|
|
758
914
|
ok: true,
|
|
759
915
|
spec: await _apidevtools_swagger_parser.default.dereference(filePath)
|
|
760
916
|
};
|
|
917
|
+
const source = (0, node_fs.readFileSync)(filePath, "utf-8");
|
|
918
|
+
const transformedSource = await contentProcessor.processText(source, "openapi", filePath);
|
|
919
|
+
if (transformedSource === source) return {
|
|
920
|
+
ok: true,
|
|
921
|
+
spec: await _apidevtools_swagger_parser.default.dereference(filePath)
|
|
922
|
+
};
|
|
923
|
+
return {
|
|
924
|
+
ok: true,
|
|
925
|
+
spec: await _apidevtools_swagger_parser.default.dereference(filePath, { resolve: { transformedOpenAPISource: {
|
|
926
|
+
order: 1,
|
|
927
|
+
canRead: (file) => isSameOpenAPIFile(file.url, filePath),
|
|
928
|
+
read: () => transformedSource
|
|
929
|
+
} } })
|
|
930
|
+
};
|
|
761
931
|
} catch (error) {
|
|
762
932
|
return {
|
|
763
933
|
ok: false,
|
|
@@ -1018,7 +1188,7 @@ function createOpenAPIPlugin() {
|
|
|
1018
1188
|
const result = specFromCache ? {
|
|
1019
1189
|
ok: true,
|
|
1020
1190
|
spec: specFromCache
|
|
1021
|
-
} : await readOpenAPISpec(route.filePath);
|
|
1191
|
+
} : await readOpenAPISpec(route.filePath, getProjectContentProcessor(ctx));
|
|
1022
1192
|
if (!result.ok) {
|
|
1023
1193
|
route.diagnostic = result.diagnostic;
|
|
1024
1194
|
route.title = route.title || "OpenAPI parse error";
|
|
@@ -1335,9 +1505,51 @@ function createSourceLinksPlugin() {
|
|
|
1335
1505
|
};
|
|
1336
1506
|
}
|
|
1337
1507
|
//#endregion
|
|
1508
|
+
//#region source/parsers/variables.ts
|
|
1509
|
+
var variablePattern = /{{\s*([A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*)\s*}}/g;
|
|
1510
|
+
function getVariableValue(variables, path) {
|
|
1511
|
+
let current = variables;
|
|
1512
|
+
for (const segment of path.split(".")) {
|
|
1513
|
+
if (!current || typeof current !== "object" || Array.isArray(current)) return void 0;
|
|
1514
|
+
current = current[segment];
|
|
1515
|
+
}
|
|
1516
|
+
return current;
|
|
1517
|
+
}
|
|
1518
|
+
function stringifyVariableValue(variables, path, stack) {
|
|
1519
|
+
if (stack.includes(path)) throw new Error(`[clarify] variable "${path}" contains a circular reference: ${[...stack, path].join(" -> ")}`);
|
|
1520
|
+
const value = getVariableValue(variables, path);
|
|
1521
|
+
if (value === void 0 || typeof value === "object") return void 0;
|
|
1522
|
+
if (typeof value !== "string") return String(value);
|
|
1523
|
+
return value.replace(variablePattern, (match, nestedPath) => stringifyVariableValue(variables, nestedPath, [...stack, path]) ?? match);
|
|
1524
|
+
}
|
|
1525
|
+
function applyVariables(content, variables = {}) {
|
|
1526
|
+
return content.replace(variablePattern, (match, path) => stringifyVariableValue(variables, path, []) ?? match);
|
|
1527
|
+
}
|
|
1528
|
+
//#endregion
|
|
1529
|
+
//#region source/plugins/variables/index.ts
|
|
1530
|
+
function applyVariablesToFrontmatter(frontmatter, variables) {
|
|
1531
|
+
const nextFrontmatter = { ...frontmatter };
|
|
1532
|
+
for (const key of ["title", "description"]) {
|
|
1533
|
+
if (typeof nextFrontmatter[key] !== "string") continue;
|
|
1534
|
+
nextFrontmatter[key] = applyVariables(nextFrontmatter[key], variables);
|
|
1535
|
+
}
|
|
1536
|
+
return nextFrontmatter;
|
|
1537
|
+
}
|
|
1538
|
+
function createVariablesPlugin() {
|
|
1539
|
+
return {
|
|
1540
|
+
name: "clarify:variables",
|
|
1541
|
+
hooks: { "content:transform": (input, ctx) => ({
|
|
1542
|
+
...input,
|
|
1543
|
+
frontmatter: applyVariablesToFrontmatter(input.frontmatter, ctx.projectConfig.variables),
|
|
1544
|
+
content: applyVariables(input.content, ctx.projectConfig.variables)
|
|
1545
|
+
}) }
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
//#endregion
|
|
1338
1549
|
//#region source/core/builtin.ts
|
|
1339
1550
|
function createBuiltinPlugins(options = {}) {
|
|
1340
1551
|
const plugins = [
|
|
1552
|
+
createVariablesPlugin(),
|
|
1341
1553
|
createOpenAPIPlugin(),
|
|
1342
1554
|
createSourceLinksPlugin(),
|
|
1343
1555
|
createContentArtifactsPlugin(),
|
|
@@ -1406,6 +1618,13 @@ var clarifyFooterConfigSchema = zod.z.object({
|
|
|
1406
1618
|
socials: zod.z.record(zod.z.string(), zod.z.string()).optional(),
|
|
1407
1619
|
copyright: clarifyLocalizedTextSchema.optional()
|
|
1408
1620
|
}).strict();
|
|
1621
|
+
var clarifyVariableValueSchema = zod.z.lazy(() => zod.z.union([
|
|
1622
|
+
zod.z.string(),
|
|
1623
|
+
zod.z.number(),
|
|
1624
|
+
zod.z.boolean(),
|
|
1625
|
+
zod.z.record(zod.z.string(), clarifyVariableValueSchema)
|
|
1626
|
+
]));
|
|
1627
|
+
var clarifyVariablesConfigSchema = zod.z.record(zod.z.string(), clarifyVariableValueSchema);
|
|
1409
1628
|
var clarifySourceConfigSchema = zod.z.object({
|
|
1410
1629
|
repository: zod.z.string(),
|
|
1411
1630
|
branch: zod.z.string().optional(),
|
|
@@ -1483,9 +1702,11 @@ var clarifyProjectConfigSchema = zod.z.object({
|
|
|
1483
1702
|
favicon: clarifyFaviconConfigSchema.optional(),
|
|
1484
1703
|
theme: clarifyThemeConfigSchema.optional(),
|
|
1485
1704
|
routePrefix: zod.z.string().optional(),
|
|
1705
|
+
assetPrefix: zod.z.string().optional(),
|
|
1486
1706
|
navbar: zod.z.object({ links: zod.z.array(clarifyNavbarLinkSchema).optional() }).optional(),
|
|
1487
1707
|
banner: clarifyBannerConfigSchema.optional(),
|
|
1488
1708
|
footer: clarifyFooterConfigSchema.optional(),
|
|
1709
|
+
variables: clarifyVariablesConfigSchema.optional(),
|
|
1489
1710
|
i18n: clarifyI18nConfigSchema.optional(),
|
|
1490
1711
|
tabs: clarifyTabsConfigSchema.optional()
|
|
1491
1712
|
});
|
|
@@ -1545,7 +1766,22 @@ function resolveI18nConfig(i18n) {
|
|
|
1545
1766
|
locales: i18n.locales
|
|
1546
1767
|
};
|
|
1547
1768
|
}
|
|
1769
|
+
function resolveRoutePrefix(routePrefix) {
|
|
1770
|
+
const trimmed = routePrefix?.trim();
|
|
1771
|
+
if (!trimmed || trimmed === "/") return "/";
|
|
1772
|
+
return `/${trimmed.replace(/^\/+|\/+$/g, "")}/`;
|
|
1773
|
+
}
|
|
1774
|
+
function resolveAssetPrefix(assetPrefix, routePrefix) {
|
|
1775
|
+
const trimmed = assetPrefix?.trim();
|
|
1776
|
+
if (trimmed === void 0) return routePrefix;
|
|
1777
|
+
if (trimmed === "./" || trimmed === "") return trimmed || "/";
|
|
1778
|
+
if (!trimmed || trimmed === "/") return "/";
|
|
1779
|
+
if (/^[a-z][a-z\d+.-]*:\/\//i.test(trimmed)) return trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
|
|
1780
|
+
if (trimmed.startsWith("./") || trimmed.startsWith("../")) return trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
|
|
1781
|
+
return `/${trimmed.replace(/^\/+|\/+$/g, "")}/`;
|
|
1782
|
+
}
|
|
1548
1783
|
function resolveProjectConfig(config = {}) {
|
|
1784
|
+
const routePrefix = resolveRoutePrefix(config.routePrefix);
|
|
1549
1785
|
return {
|
|
1550
1786
|
title: config.title ?? "Clarify Docs",
|
|
1551
1787
|
description: config.description ?? "",
|
|
@@ -1554,15 +1790,137 @@ function resolveProjectConfig(config = {}) {
|
|
|
1554
1790
|
logo: config.logo,
|
|
1555
1791
|
homeUrl: config.homeUrl,
|
|
1556
1792
|
favicon: config.favicon,
|
|
1557
|
-
routePrefix
|
|
1793
|
+
routePrefix,
|
|
1794
|
+
assetPrefix: resolveAssetPrefix(config.assetPrefix, routePrefix),
|
|
1558
1795
|
theme: resolveThemeConfig(config.theme),
|
|
1559
1796
|
navbar: config.navbar,
|
|
1560
1797
|
banner: config.banner,
|
|
1561
1798
|
footer: config.footer,
|
|
1799
|
+
variables: config.variables ?? {},
|
|
1562
1800
|
i18n: resolveI18nConfig(config.i18n),
|
|
1563
1801
|
tabs: config.tabs
|
|
1564
1802
|
};
|
|
1565
1803
|
}
|
|
1804
|
+
/** Serialize a ContentRoute into the minimal shape exposed to tooling. */
|
|
1805
|
+
function toDevRouteEntry(route) {
|
|
1806
|
+
return {
|
|
1807
|
+
path: route.path,
|
|
1808
|
+
filePath: route.filePath,
|
|
1809
|
+
locale: route.locale,
|
|
1810
|
+
basePath: route.basePath,
|
|
1811
|
+
kind: route.kind,
|
|
1812
|
+
title: route.title
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
/**
|
|
1816
|
+
* Resolve a single file path to its preview route.
|
|
1817
|
+
*
|
|
1818
|
+
* Selection order:
|
|
1819
|
+
* 1. Routes whose `filePath` exactly matches.
|
|
1820
|
+
* 2. Among matches, prefer the one whose `locale` matches the file's locale
|
|
1821
|
+
* directory (e.g. `source/zh-CN/x.mdx` → locale `zh-CN`).
|
|
1822
|
+
* 3. If no locale match, fall back to the default locale (if i18n is enabled)
|
|
1823
|
+
* or the first match.
|
|
1824
|
+
*/
|
|
1825
|
+
function resolveRouteForFile(filePath, routes, defaultLocale) {
|
|
1826
|
+
const matches = routes.filter((r) => r.filePath === filePath);
|
|
1827
|
+
if (matches.length === 0) return void 0;
|
|
1828
|
+
if (matches.length === 1) return matches[0];
|
|
1829
|
+
const fileLocale = inferLocaleFromPath(filePath);
|
|
1830
|
+
if (fileLocale) {
|
|
1831
|
+
const localeMatch = matches.find((r) => r.locale === fileLocale);
|
|
1832
|
+
if (localeMatch) return localeMatch;
|
|
1833
|
+
}
|
|
1834
|
+
if (defaultLocale) {
|
|
1835
|
+
const defaultMatch = matches.find((r) => r.locale === defaultLocale);
|
|
1836
|
+
if (defaultMatch) return defaultMatch;
|
|
1837
|
+
}
|
|
1838
|
+
return matches[0];
|
|
1839
|
+
}
|
|
1840
|
+
/**
|
|
1841
|
+
* Derive a best-effort preview route for a file that is not in the route
|
|
1842
|
+
* manifest (e.g. a draft or newly created file). The path is computed from the
|
|
1843
|
+
* file's location relative to the content root, following the same convention
|
|
1844
|
+
* as route discovery (`source/{locale}/path/index.mdx` → `/{locale}/path`).
|
|
1845
|
+
*
|
|
1846
|
+
* Returns null when the file is outside the content root or is not a
|
|
1847
|
+
* recognized content file type.
|
|
1848
|
+
*/
|
|
1849
|
+
function inferRouteForFile(filePath, contentRoot, defaultLocale) {
|
|
1850
|
+
const ref = (0, node_path.relative)(contentRoot, filePath);
|
|
1851
|
+
if (!ref || ref.startsWith("..")) return null;
|
|
1852
|
+
const isMdx = /\.mdx?$/.test(filePath);
|
|
1853
|
+
const isOpenApi = /\.openapi\.(json|ya?ml)$/.test(filePath);
|
|
1854
|
+
if (!isMdx && !isOpenApi) return null;
|
|
1855
|
+
const segments = ref.split("/");
|
|
1856
|
+
const fileLocale = inferLocaleFromPath(filePath);
|
|
1857
|
+
const localeSegmentIndex = fileLocale ? segments.indexOf(fileLocale) : -1;
|
|
1858
|
+
const basePath = basePathFromRef(localeSegmentIndex >= 0 ? segments.slice(localeSegmentIndex + 1).join("/") : ref);
|
|
1859
|
+
const path = fileLocale && defaultLocale ? localizedRoutePath(basePath, fileLocale, {
|
|
1860
|
+
defaultLocale,
|
|
1861
|
+
missing: "fallback",
|
|
1862
|
+
locales: []
|
|
1863
|
+
}) : basePath;
|
|
1864
|
+
const title = segments[segments.length - 1]?.replace(/\.(mdx?|openapi\.(json|ya?ml))$/, "") || "Untitled";
|
|
1865
|
+
return {
|
|
1866
|
+
path: normalizePath(path),
|
|
1867
|
+
filePath,
|
|
1868
|
+
locale: fileLocale,
|
|
1869
|
+
basePath: normalizePath(basePath),
|
|
1870
|
+
kind: isMdx ? "mdx" : "openapi",
|
|
1871
|
+
title,
|
|
1872
|
+
inferred: true
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
/** Best-effort locale inference from a file path segment like `.../zh-CN/x.mdx`. */
|
|
1876
|
+
function inferLocaleFromPath(filePath) {
|
|
1877
|
+
const segments = filePath.split("/");
|
|
1878
|
+
for (const segment of segments) if (/^[a-z]{2,3}(-[A-Z][a-z]{3})?(-[A-Z]{2})?$/.test(segment)) return segment;
|
|
1879
|
+
}
|
|
1880
|
+
/** Read and parse the JSON body of an IncomingMessage. */
|
|
1881
|
+
function readJsonBody(req) {
|
|
1882
|
+
return new Promise((resolveBody, reject) => {
|
|
1883
|
+
const chunks = [];
|
|
1884
|
+
req.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
|
1885
|
+
req.on("error", reject);
|
|
1886
|
+
req.on("end", () => {
|
|
1887
|
+
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1888
|
+
if (!raw) return resolveBody({});
|
|
1889
|
+
try {
|
|
1890
|
+
resolveBody(JSON.parse(raw));
|
|
1891
|
+
} catch (err) {
|
|
1892
|
+
reject(err);
|
|
1893
|
+
}
|
|
1894
|
+
});
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
/**
|
|
1898
|
+
* Handle a request to the dev route endpoint. Pure function — does not touch
|
|
1899
|
+
* the network directly, only reads the request body and writes to `res`.
|
|
1900
|
+
*/
|
|
1901
|
+
async function handleDevRouteRequest(req, res, routes, projectConfig, contentRoot) {
|
|
1902
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
1903
|
+
let body;
|
|
1904
|
+
try {
|
|
1905
|
+
body = await readJsonBody(req);
|
|
1906
|
+
} catch {
|
|
1907
|
+
res.statusCode = 400;
|
|
1908
|
+
res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
1909
|
+
return;
|
|
1910
|
+
}
|
|
1911
|
+
const file = typeof body === "object" && body !== null ? body.file : void 0;
|
|
1912
|
+
if (typeof file === "string" && file.length > 0) {
|
|
1913
|
+
const route = resolveRouteForFile(file, routes, projectConfig.i18n?.defaultLocale);
|
|
1914
|
+
if (route) {
|
|
1915
|
+
res.end(JSON.stringify(toDevRouteEntry(route)));
|
|
1916
|
+
return;
|
|
1917
|
+
}
|
|
1918
|
+
const inferred = inferRouteForFile(file, contentRoot, projectConfig.i18n?.defaultLocale);
|
|
1919
|
+
res.end(JSON.stringify(inferred));
|
|
1920
|
+
return;
|
|
1921
|
+
}
|
|
1922
|
+
res.end(JSON.stringify(routes.map(toDevRouteEntry)));
|
|
1923
|
+
}
|
|
1566
1924
|
//#endregion
|
|
1567
1925
|
//#region source/core/env-types.ts
|
|
1568
1926
|
var GENERATED_FILE_NAME = "clarify-env.d.ts";
|
|
@@ -1612,57 +1970,6 @@ function writeClarifyEnvDts(projectRoot, plugins) {
|
|
|
1612
1970
|
(0, node_fs.writeFileSync)(filePath, next, "utf-8");
|
|
1613
1971
|
}
|
|
1614
1972
|
//#endregion
|
|
1615
|
-
//#region source/core/hooks.ts
|
|
1616
|
-
async function runHooks(plugins, hookName, input, ctx) {
|
|
1617
|
-
let result = input;
|
|
1618
|
-
for (const plugin of plugins) {
|
|
1619
|
-
const hook = plugin.hooks?.[hookName];
|
|
1620
|
-
if (!hook) continue;
|
|
1621
|
-
try {
|
|
1622
|
-
result = await hook(result, ctx);
|
|
1623
|
-
} catch (err) {
|
|
1624
|
-
throw new Error(`[clarify] plugin "${plugin.name}" hook "${hookName}" failed: ${err}`, { cause: err });
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
return result;
|
|
1628
|
-
}
|
|
1629
|
-
async function runDevConfigureServerHooks(plugins, server, ctx) {
|
|
1630
|
-
for (const plugin of plugins) {
|
|
1631
|
-
const hook = plugin.hooks?.["dev:configureServer"];
|
|
1632
|
-
if (!hook) continue;
|
|
1633
|
-
try {
|
|
1634
|
-
await hook(server, ctx);
|
|
1635
|
-
} catch (err) {
|
|
1636
|
-
throw new Error(`[clarify] plugin "${plugin.name}" hook "dev:configureServer" failed: ${err}`, { cause: err });
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
async function runBuildAssetsHooks(plugins, ctx) {
|
|
1641
|
-
const assets = [];
|
|
1642
|
-
for (const plugin of plugins) {
|
|
1643
|
-
const hook = plugin.hooks?.["build:assets"];
|
|
1644
|
-
if (!hook) continue;
|
|
1645
|
-
try {
|
|
1646
|
-
const pluginAssets = await hook(ctx);
|
|
1647
|
-
assets.push(...pluginAssets);
|
|
1648
|
-
} catch (err) {
|
|
1649
|
-
throw new Error(`[clarify] plugin "${plugin.name}" hook "build:assets" failed: ${err}`, { cause: err });
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
return assets;
|
|
1653
|
-
}
|
|
1654
|
-
async function runBuildDoneHooks(plugins, ctx) {
|
|
1655
|
-
for (const plugin of plugins) {
|
|
1656
|
-
const hook = plugin.hooks?.["build:done"];
|
|
1657
|
-
if (!hook) continue;
|
|
1658
|
-
try {
|
|
1659
|
-
await hook(ctx);
|
|
1660
|
-
} catch (err) {
|
|
1661
|
-
throw new Error(`[clarify] plugin "${plugin.name}" hook "build:done" failed: ${err}`, { cause: err });
|
|
1662
|
-
}
|
|
1663
|
-
}
|
|
1664
|
-
}
|
|
1665
|
-
//#endregion
|
|
1666
1973
|
//#region source/core/options.ts
|
|
1667
1974
|
function resolveBuildOptions(options = {}) {
|
|
1668
1975
|
return {
|
|
@@ -1673,12 +1980,90 @@ function resolveBuildOptions(options = {}) {
|
|
|
1673
1980
|
};
|
|
1674
1981
|
}
|
|
1675
1982
|
//#endregion
|
|
1983
|
+
//#region source/core/user-config.ts
|
|
1984
|
+
function defineConfig(config) {
|
|
1985
|
+
validateProjectConfig(config);
|
|
1986
|
+
return config;
|
|
1987
|
+
}
|
|
1988
|
+
/**
|
|
1989
|
+
* Config filenames Clarify CLI looks for at the project root, in resolution
|
|
1990
|
+
* order. Exported so other modules (e.g. the dev `project-info` endpoint) can
|
|
1991
|
+
* surface the same list to tooling without duplicating it.
|
|
1992
|
+
*/
|
|
1993
|
+
var CONFIG_FILENAMES = [
|
|
1994
|
+
"clarify.ts",
|
|
1995
|
+
"clarify.js",
|
|
1996
|
+
"clarify.json"
|
|
1997
|
+
];
|
|
1998
|
+
function findClarifyConfigFile(root) {
|
|
1999
|
+
for (const filename of CONFIG_FILENAMES) {
|
|
2000
|
+
const filePath = (0, node_path.resolve)(root, filename);
|
|
2001
|
+
if ((0, node_fs.existsSync)(filePath)) return filePath;
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
function assertConfigObject(config, configFile) {
|
|
2005
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) throw new Error(`[clarify] ${configFile} must export a configuration object`);
|
|
2006
|
+
return config;
|
|
2007
|
+
}
|
|
2008
|
+
function loadJsonConfig(configFile) {
|
|
2009
|
+
const config = assertConfigObject(JSON.parse((0, node_fs.readFileSync)(configFile, "utf-8")), configFile);
|
|
2010
|
+
const projectConfig = validateProjectConfig(config);
|
|
2011
|
+
const ssg = config.ssg && typeof config.ssg === "object" && !Array.isArray(config.ssg) ? config.ssg : void 0;
|
|
2012
|
+
return {
|
|
2013
|
+
...projectConfig,
|
|
2014
|
+
...ssg ? { ssg } : {}
|
|
2015
|
+
};
|
|
2016
|
+
}
|
|
2017
|
+
async function loadClarifyConfig(root, env) {
|
|
2018
|
+
const configFile = findClarifyConfigFile(root);
|
|
2019
|
+
if (!configFile) return {};
|
|
2020
|
+
if (configFile.endsWith(".json")) return loadJsonConfig(configFile);
|
|
2021
|
+
return assertConfigObject((await (0, vite.loadConfigFromFile)(env, configFile, root, "silent"))?.config, configFile);
|
|
2022
|
+
}
|
|
2023
|
+
/**
|
|
2024
|
+
* File extensions Clarify treats as content files. Kept in sync with the
|
|
2025
|
+
* patterns used by route discovery in `parsers/routes.ts` and the OpenAPI
|
|
2026
|
+
* page generator. Exposed to tooling so the extension's "is this a content
|
|
2027
|
+
* file?" check stays accurate without duplicating the regex.
|
|
2028
|
+
*/
|
|
2029
|
+
var CONTENT_FILE_EXTENSIONS = [
|
|
2030
|
+
".md",
|
|
2031
|
+
".mdx",
|
|
2032
|
+
".openapi.json",
|
|
2033
|
+
".openapi.yaml",
|
|
2034
|
+
".openapi.yml"
|
|
2035
|
+
];
|
|
2036
|
+
/**
|
|
2037
|
+
* Build the project-info response payload from the resolved plugin state.
|
|
2038
|
+
*/
|
|
2039
|
+
function buildProjectInfo(projectRoot, contentRoot, projectConfig) {
|
|
2040
|
+
const response = {
|
|
2041
|
+
configFilenames: CONFIG_FILENAMES,
|
|
2042
|
+
contentFileExtensions: CONTENT_FILE_EXTENSIONS,
|
|
2043
|
+
contentRoot: (contentRoot.startsWith(projectRoot) ? contentRoot.slice(projectRoot.length).replace(/^[/\\]+/, "") : contentRoot) || "source",
|
|
2044
|
+
projectRoot
|
|
2045
|
+
};
|
|
2046
|
+
if (projectConfig.i18n) response.i18n = {
|
|
2047
|
+
defaultLocale: projectConfig.i18n.defaultLocale,
|
|
2048
|
+
locales: projectConfig.i18n.locales.map((l) => l.code)
|
|
2049
|
+
};
|
|
2050
|
+
return response;
|
|
2051
|
+
}
|
|
2052
|
+
/**
|
|
2053
|
+
* Handle a request to the project-info endpoint. Responds to any method
|
|
2054
|
+
* (GET is typical) with the JSON payload — there is no request body to parse.
|
|
2055
|
+
*/
|
|
2056
|
+
function handleProjectInfoRequest(_req, res, projectRoot, contentRoot, projectConfig) {
|
|
2057
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
2058
|
+
res.end(JSON.stringify(buildProjectInfo(projectRoot, contentRoot, projectConfig)));
|
|
2059
|
+
}
|
|
2060
|
+
//#endregion
|
|
1676
2061
|
//#region source/core/site.ts
|
|
1677
2062
|
async function discoverRoutesForRoot(routeRoot, locale, plugins, ctx) {
|
|
1678
2063
|
return (await runHooks(plugins, "routes:discover", {
|
|
1679
2064
|
contentRoot: routeRoot,
|
|
1680
2065
|
locale,
|
|
1681
|
-
routes: findContentRoutes(routeRoot)
|
|
2066
|
+
routes: await findContentRoutes(routeRoot, routeRoot, { contentProcessor: getProjectContentProcessor(ctx) })
|
|
1682
2067
|
}, ctx)).routes;
|
|
1683
2068
|
}
|
|
1684
2069
|
async function discoverRoutes(root, contentRoot, plugins, ctx) {
|
|
@@ -1743,6 +2128,7 @@ async function resolveClarifySite(options = {}, resolveOptions = {}) {
|
|
|
1743
2128
|
routes: [],
|
|
1744
2129
|
navigation: []
|
|
1745
2130
|
};
|
|
2131
|
+
setProjectContentProcessor(ctx, createProjectContentProcessor(plugins, ctx));
|
|
1746
2132
|
let routes = await discoverRoutes(root, contentRoot, plugins, ctx);
|
|
1747
2133
|
routes = await runHooks(plugins, "routes:discovered", routes, ctx);
|
|
1748
2134
|
routes = applyConfiguredPageRoutePaths(routes, projectConfig.tabs, projectConfig.i18n);
|
|
@@ -1945,42 +2331,6 @@ async function renderSSGRoutes(routes, projectConfig, outputDirectory, ssrBundle
|
|
|
1945
2331
|
}
|
|
1946
2332
|
}
|
|
1947
2333
|
//#endregion
|
|
1948
|
-
//#region source/core/user-config.ts
|
|
1949
|
-
function defineConfig(config) {
|
|
1950
|
-
validateProjectConfig(config);
|
|
1951
|
-
return config;
|
|
1952
|
-
}
|
|
1953
|
-
var CONFIG_FILENAMES = [
|
|
1954
|
-
"clarify.ts",
|
|
1955
|
-
"clarify.js",
|
|
1956
|
-
"clarify.json"
|
|
1957
|
-
];
|
|
1958
|
-
function findClarifyConfigFile(root) {
|
|
1959
|
-
for (const filename of CONFIG_FILENAMES) {
|
|
1960
|
-
const filePath = (0, node_path.resolve)(root, filename);
|
|
1961
|
-
if ((0, node_fs.existsSync)(filePath)) return filePath;
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
function assertConfigObject(config, configFile) {
|
|
1965
|
-
if (!config || typeof config !== "object" || Array.isArray(config)) throw new Error(`[clarify] ${configFile} must export a configuration object`);
|
|
1966
|
-
return config;
|
|
1967
|
-
}
|
|
1968
|
-
function loadJsonConfig(configFile) {
|
|
1969
|
-
const config = assertConfigObject(JSON.parse((0, node_fs.readFileSync)(configFile, "utf-8")), configFile);
|
|
1970
|
-
const projectConfig = validateProjectConfig(config);
|
|
1971
|
-
const ssg = config.ssg && typeof config.ssg === "object" && !Array.isArray(config.ssg) ? config.ssg : void 0;
|
|
1972
|
-
return {
|
|
1973
|
-
...projectConfig,
|
|
1974
|
-
...ssg ? { ssg } : {}
|
|
1975
|
-
};
|
|
1976
|
-
}
|
|
1977
|
-
async function loadClarifyConfig(root, env) {
|
|
1978
|
-
const configFile = findClarifyConfigFile(root);
|
|
1979
|
-
if (!configFile) return {};
|
|
1980
|
-
if (configFile.endsWith(".json")) return loadJsonConfig(configFile);
|
|
1981
|
-
return assertConfigObject((await (0, vite.loadConfigFromFile)(env, configFile, root, "silent"))?.config, configFile);
|
|
1982
|
-
}
|
|
1983
|
-
//#endregion
|
|
1984
2334
|
//#region source/core/plugin.ts
|
|
1985
2335
|
function loadVirtualModule(id, modules) {
|
|
1986
2336
|
const bareId = stripVirtualPrefix(id);
|
|
@@ -2071,7 +2421,7 @@ function clarifyPlugin(options = {}) {
|
|
|
2071
2421
|
async config() {
|
|
2072
2422
|
await resolveRoutesAndSpecs();
|
|
2073
2423
|
return {
|
|
2074
|
-
base: projectConfig.
|
|
2424
|
+
base: projectConfig.assetPrefix,
|
|
2075
2425
|
build: {
|
|
2076
2426
|
...generateOptions.outputDirectory ? { outDir: generateOptions.outputDirectory } : {},
|
|
2077
2427
|
manifest: true
|
|
@@ -2128,6 +2478,11 @@ function clarifyPlugin(options = {}) {
|
|
|
2128
2478
|
async configureServer(server) {
|
|
2129
2479
|
server.watcher.add(contentRoot);
|
|
2130
2480
|
if (configFilePath) server.watcher.add(configFilePath);
|
|
2481
|
+
server.middlewares.use((req, res, next) => {
|
|
2482
|
+
if (req.url === "/dev/query-preview-route" && req.method === "POST") return handleDevRouteRequest(req, res, routes, projectConfig, contentRoot);
|
|
2483
|
+
if (req.url === "/dev/project-info" && (req.method === "GET" || req.method === "HEAD")) return handleProjectInfoRequest(req, res, root, contentRoot, projectConfig);
|
|
2484
|
+
next();
|
|
2485
|
+
});
|
|
2131
2486
|
const handleContentTreeChange = async (filePath) => {
|
|
2132
2487
|
const relativeContentFile = (0, node_path.relative)(contentRoot, (0, node_path.isAbsolute)(filePath) ? filePath : (0, node_path.join)(root, filePath));
|
|
2133
2488
|
if (!(relativeContentFile && !relativeContentFile.startsWith("..") && !(0, node_path.isAbsolute)(relativeContentFile))) return;
|
|
@@ -2246,4 +2601,4 @@ Object.defineProperty(exports, "resolveClarifySite", {
|
|
|
2246
2601
|
}
|
|
2247
2602
|
});
|
|
2248
2603
|
|
|
2249
|
-
//# sourceMappingURL=plugin-
|
|
2604
|
+
//# sourceMappingURL=plugin-JvxU6ZjU.cjs.map
|