@clarify-labs/cli 0.6.4 → 0.6.6

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.
@@ -11,6 +11,7 @@ import { markdownRemarkPlugins } from "@clarify-labs/renderer";
11
11
  import { remark } from "remark";
12
12
  import matter from "gray-matter";
13
13
  import SwaggerParser from "@apidevtools/swagger-parser";
14
+ import { mkdir, writeFile } from "node:fs/promises";
14
15
  import { z } from "zod";
15
16
  import { tmpdir } from "node:os";
16
17
  import { pathToFileURL } from "node:url";
@@ -47,6 +48,11 @@ function rehypeParseCodeBlocks() {
47
48
  const language = getLanguage(node.properties.className);
48
49
  parentNode.properties.language = language;
49
50
  node.properties.language = language;
51
+ for (const key of [
52
+ "title",
53
+ "tag",
54
+ "label"
55
+ ]) if (node.properties[key] !== void 0) parentNode.properties[key] = node.properties[key];
50
56
  });
51
57
  };
52
58
  }
@@ -100,6 +106,191 @@ var rehypePlugins = [
100
106
  rehypeShiki
101
107
  ];
102
108
  //#endregion
109
+ //#region source/plugins/content-artifacts/artifacts.ts
110
+ var UTF8_SIGNATURE = "";
111
+ function normalizeBasePath(basePath) {
112
+ if (!basePath || basePath === "/") return "";
113
+ return "/" + basePath.replace(/^\/+|\/+$/g, "");
114
+ }
115
+ function containsNonAscii(content) {
116
+ return [...content].some((character) => character.codePointAt(0) > 127);
117
+ }
118
+ function withUtf8Signature(content) {
119
+ if (!containsNonAscii(content) || content.startsWith(UTF8_SIGNATURE)) return content;
120
+ return `${UTF8_SIGNATURE}${content}`;
121
+ }
122
+ function shouldUseUtf8Signature(route) {
123
+ if (route.kind === "mdx") return true;
124
+ return /\.ya?ml$/i.test(route.contentArtifactUrl ?? "");
125
+ }
126
+ function routeToMarkdownArtifactUrl(routePath) {
127
+ return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.md`;
128
+ }
129
+ function routeToOpenAPIArtifactUrl(routePath, filePath) {
130
+ const extension = extname(filePath).toLowerCase() || ".json";
131
+ return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.openapi${extension}`;
132
+ }
133
+ function attachContentArtifactUrls(routes) {
134
+ for (const route of routes) route.contentArtifactUrl = route.kind === "openapi" ? routeToOpenAPIArtifactUrl(route.path, route.filePath) : routeToMarkdownArtifactUrl(route.path);
135
+ }
136
+ function readRouteContent(route) {
137
+ if (route.content !== void 0) return route.content;
138
+ throw new Error(`Route content is missing from route context: ${route.filePath}`);
139
+ }
140
+ function readRouteArtifactContent(route) {
141
+ const content = readRouteContent(route);
142
+ return shouldUseUtf8Signature(route) ? withUtf8Signature(content) : content;
143
+ }
144
+ function writeContentArtifactFiles(routes, outputDirectory) {
145
+ for (const route of routes) {
146
+ if (!route.contentArtifactUrl) continue;
147
+ const outFile = join(outputDirectory, route.contentArtifactUrl.replace(/^\//, ""));
148
+ mkdirSync(dirname(outFile), { recursive: true });
149
+ writeFileSync(outFile, readRouteArtifactContent(route), "utf-8");
150
+ }
151
+ }
152
+ function createLlmsTxt(routes, projectConfig) {
153
+ const basePath = normalizeBasePath(projectConfig.routePrefix);
154
+ const lines = [`# ${projectConfig.title}`, ""];
155
+ if (projectConfig.description) lines.push(`> ${projectConfig.description}`, "");
156
+ lines.push("## Docs");
157
+ for (const route of routes.filter((route) => route.kind === "mdx")) {
158
+ if (!route.contentArtifactUrl) continue;
159
+ lines.push(`- [${route.title}](${basePath}${route.contentArtifactUrl})`);
160
+ }
161
+ const openApiRoutes = routes.filter((route) => route.kind === "openapi");
162
+ if (openApiRoutes.length > 0) {
163
+ lines.push("", "## OpenAPI");
164
+ for (const route of openApiRoutes) {
165
+ if (!route.contentArtifactUrl) continue;
166
+ lines.push(`- [${route.title}](${basePath}${route.contentArtifactUrl})`);
167
+ }
168
+ }
169
+ return `${lines.join("\n")}\n`;
170
+ }
171
+ function createLlmsTxtArtifact(routes, projectConfig) {
172
+ return withUtf8Signature(createLlmsTxt(routes, projectConfig));
173
+ }
174
+ function writeLlmsTxt(routes, projectConfig, outputDirectory) {
175
+ mkdirSync(outputDirectory, { recursive: true });
176
+ writeFileSync(join(outputDirectory, "llms.txt"), createLlmsTxtArtifact(routes, projectConfig), "utf-8");
177
+ }
178
+ //#endregion
179
+ //#region source/plugins/content-artifacts/server.ts
180
+ function normalizeRoutePrefix$1(routePrefix) {
181
+ if (!routePrefix || routePrefix === "/") return "";
182
+ return `/${routePrefix.replace(/^\/+|\/+$/g, "")}`;
183
+ }
184
+ function resolveContentArtifactPath(url, projectConfig) {
185
+ const requestPath = url?.split("?")[0] ?? "";
186
+ const basePath = normalizeRoutePrefix$1(projectConfig.routePrefix);
187
+ return basePath && requestPath.startsWith(basePath) ? requestPath.slice(basePath.length) || "/" : requestPath;
188
+ }
189
+ function resolveContentArtifactType(route) {
190
+ if (route.kind === "openapi" && /\.ya?ml$/i.test(route.contentArtifactUrl ?? "")) return "text/yaml; charset=utf-8";
191
+ return route.kind === "openapi" ? "application/json; charset=utf-8" : "text/markdown; charset=utf-8";
192
+ }
193
+ function serveContentArtifacts(req, res, projectConfig, routes) {
194
+ const contentPath = resolveContentArtifactPath(req.url, projectConfig);
195
+ if (contentPath === "/llms.txt") {
196
+ res.statusCode = 200;
197
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
198
+ res.end(createLlmsTxtArtifact(routes, projectConfig), "utf8");
199
+ return true;
200
+ }
201
+ const route = routes.find((route) => route.contentArtifactUrl === contentPath);
202
+ if (!route) return false;
203
+ res.statusCode = 200;
204
+ res.setHeader("Content-Type", resolveContentArtifactType(route));
205
+ res.end(readRouteArtifactContent(route), "utf8");
206
+ return true;
207
+ }
208
+ //#endregion
209
+ //#region source/plugins/content-artifacts/index.ts
210
+ function createContentArtifactsPlugin() {
211
+ return {
212
+ name: "clarify:content-artifacts",
213
+ hooks: {
214
+ "routes:resolved": (input) => {
215
+ attachContentArtifactUrls(input.routes);
216
+ return input;
217
+ },
218
+ "dev:configureServer": (server, ctx) => {
219
+ server.middlewares.use((req, res, next) => {
220
+ if (serveContentArtifacts(req, res, ctx.projectConfig, ctx.routes)) return;
221
+ next();
222
+ });
223
+ },
224
+ "build:done": (ctx) => {
225
+ const outputDirectory = ctx.generateOptions.outputDirectory;
226
+ if (!outputDirectory) return;
227
+ const outputRoot = join(ctx.generateOptions.projectRoot, outputDirectory);
228
+ writeContentArtifactFiles(ctx.routes, outputRoot);
229
+ writeLlmsTxt(ctx.routes, ctx.projectConfig, outputRoot);
230
+ }
231
+ }
232
+ };
233
+ }
234
+ //#endregion
235
+ //#region source/plugins/html-shell/index.ts
236
+ var THEME_BOOTSTRAP_SCRIPT = `(function(){try{var e=localStorage.getItem('clarify:theme');var t=e==='dark'||e==='light'||e==='system'?e:'system';var r=t==='system'?window.matchMedia('(prefers-color-scheme: dark)').matches:t==='dark';document.documentElement.classList.toggle('dark',r);document.documentElement.style.colorScheme=r?'dark':'light'}catch(e){}})();`;
237
+ var REL_ICON_RE = /<link\s+[^>]*rel=["'][^"']*\bicon\b[^"']*["'][^>]*>/i;
238
+ function inferIconType(href) {
239
+ const path = href.split(/[?#]/, 1)[0]?.toLowerCase() ?? href.toLowerCase();
240
+ if (path.endsWith(".svg")) return "image/svg+xml";
241
+ if (path.endsWith(".png")) return "image/png";
242
+ if (path.endsWith(".ico")) return "image/x-icon";
243
+ if (path.endsWith(".webp")) return "image/webp";
244
+ }
245
+ function createIconTag(href, media) {
246
+ return {
247
+ tag: "link",
248
+ attrs: {
249
+ rel: "icon",
250
+ ...inferIconType(href) ? { type: inferIconType(href) } : {},
251
+ href,
252
+ ...media ? { media } : {}
253
+ },
254
+ injectTo: "head"
255
+ };
256
+ }
257
+ function createIconTags(icon) {
258
+ if (!icon) return [];
259
+ if (typeof icon === "string") return [createIconTag(icon)];
260
+ const lightIcon = icon.light ?? icon.dark;
261
+ const darkIcon = icon.dark ?? icon.light;
262
+ if (!lightIcon || !darkIcon) return [];
263
+ if (lightIcon === darkIcon) return [createIconTag(lightIcon)];
264
+ return [createIconTag(lightIcon, "(prefers-color-scheme: light)"), createIconTag(darkIcon, "(prefers-color-scheme: dark)")];
265
+ }
266
+ function createHtmlShellPlugin() {
267
+ return {
268
+ name: "clarify:html-shell",
269
+ hooks: { "html:transform": (input, ctx) => ({
270
+ html: input.html,
271
+ tags: [
272
+ ...input.tags,
273
+ ...REL_ICON_RE.test(input.html) ? [] : createIconTags(ctx.projectConfig.favicon ?? ctx.projectConfig.logo),
274
+ {
275
+ tag: "script",
276
+ children: THEME_BOOTSTRAP_SCRIPT,
277
+ injectTo: "head-prepend"
278
+ },
279
+ {
280
+ tag: "script",
281
+ attrs: {
282
+ type: "module",
283
+ src: input.clientEntryId
284
+ },
285
+ injectTo: "body"
286
+ }
287
+ ],
288
+ clientEntryId: input.clientEntryId,
289
+ dev: input.dev
290
+ }) }
291
+ };
292
+ }
293
+ //#endregion
103
294
  //#region source/parsers/frontmatter.ts
104
295
  /**
105
296
  * 在内容入口统一解析 MDX frontmatter,并返回可供后续流程复用的标准化正文。
@@ -155,10 +346,21 @@ function normalizePath(path) {
155
346
  const clean = "/" + path.replace(/^\/+|\/+$/g, "");
156
347
  return clean === "/" ? "/" : clean.replace(/\/+/g, "/");
157
348
  }
349
+ function pathSegmentFromOpenAPITag(tag) {
350
+ return tag.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "tag";
351
+ }
352
+ function openAPITagsPathSegment(tags) {
353
+ return tags.map(pathSegmentFromOpenAPITag).join("-");
354
+ }
158
355
  function basePathFromRef(ref) {
159
356
  const withoutExtension = ref.replace(/\.mdx?$/, "").replace(/\.openapi\.(json|yaml|yml)$/, "");
160
357
  return normalizePath(withoutExtension === "index" ? "/" : withoutExtension.replace(/\/index$/, ""));
161
358
  }
359
+ function openAPIPagePathFromRef(ref, tagFilter) {
360
+ const basePath = basePathFromRef(ref);
361
+ if (!tagFilter?.length) return basePath;
362
+ return normalizePath(`${basePath}/${openAPITagsPathSegment(tagFilter)}`);
363
+ }
162
364
  function routePathFromRef(ref) {
163
365
  return basePathFromRef(ref);
164
366
  }
@@ -217,7 +419,7 @@ function frontmatterKeywords(frontmatter) {
217
419
  function buildNavigation(routes) {
218
420
  const root = [];
219
421
  for (const route of routes) {
220
- if (route.path === "/") continue;
422
+ if (route.path === "/" || (route.basePath ?? route.path) === "/404") continue;
221
423
  const parts = route.path.replace(/^\//, "").split("/");
222
424
  let current = root;
223
425
  for (let i = 0; i < parts.length; i++) {
@@ -244,6 +446,7 @@ function resolvePageItem(item) {
244
446
  if (typeof item === "string") return { pageRef: item };
245
447
  if ("openapi" in item) return {
246
448
  openapiRef: item.openapi,
449
+ openapiTagFilter: item.filter?.tags,
247
450
  title: item.title,
248
451
  icon: item.icon
249
452
  };
@@ -282,9 +485,9 @@ function buildNavigationFromConfig(routes, config) {
282
485
  const routeMap = new Map(routes.map((r) => [r.path, r]));
283
486
  return config.map((group) => {
284
487
  const children = group.pages.map((item) => {
285
- const { pageRef, openapiRef, redirect, title, icon } = resolvePageItem(item);
488
+ const { pageRef, openapiRef, openapiTagFilter, redirect, title, icon } = resolvePageItem(item);
286
489
  if (openapiRef) {
287
- const path = "/" + openapiRef.replace(/\.openapi\.(json|yaml|yml)$/, "");
490
+ const path = openAPIPagePathFromRef(openapiRef, openapiTagFilter);
288
491
  const route = routeMap.get(path);
289
492
  return {
290
493
  path,
@@ -326,227 +529,43 @@ function buildLocalizedNavigationForLocale(routes, config, locale, i18n) {
326
529
  const routeMap = new Map(routes.map((route) => [route.basePath ?? route.path, route]));
327
530
  return config.map((group) => {
328
531
  const children = group.pages.map((item) => {
329
- const { pageRef, openapiRef, redirect, title, icon } = resolvePageItem(item);
532
+ const { pageRef, openapiRef, openapiTagFilter, redirect, title, icon } = resolvePageItem(item);
330
533
  const ref = openapiRef ?? pageRef ?? "";
331
- const basePath = basePathFromRef(ref);
534
+ const basePath = openapiRef ? openAPIPagePathFromRef(openapiRef, openapiTagFilter) : basePathFromRef(ref);
332
535
  const route = routeMap.get(basePath);
333
536
  const path = route?.path ?? localizedRoutePath(basePath, locale, i18n);
334
- return {
335
- path: (redirect ? localizedRoutePath(basePathFromRef(redirect), locale, i18n) : void 0) ?? path,
336
- title: resolveLocalizedText(title, locale, i18n.defaultLocale) ?? route?.title ?? kebabToTitle$1(basePath.split("/").pop() ?? ref),
337
- icon,
338
- sections: route?.sections ? navigationSections(route.sections) : void 0
339
- };
340
- });
341
- return {
342
- path: children[0]?.path ?? localizedRoutePath("/", locale, i18n),
343
- title: resolveLocalizedText(group.group, locale, i18n.defaultLocale) ?? "",
344
- icon: group.icon,
345
- children
346
- };
347
- });
348
- }
349
- function buildLocalizedNavigationFromTabsConfig(routes, tabs, i18n) {
350
- if (!i18n) return void 0;
351
- const result = {};
352
- for (const locale of i18n.locales) {
353
- const localeRoutes = routes.filter((route) => route.locale === locale.code);
354
- result[locale.code] = { tabs: tabs.map((tab) => {
355
- const children = buildLocalizedNavigationForLocale(localeRoutes, tab.pages, locale.code, i18n);
356
- return {
357
- type: "tab",
358
- path: firstNavigationPath(children),
359
- title: resolveLocalizedText(tab.tab, locale.code, i18n.defaultLocale) ?? "",
360
- icon: tab.icon,
361
- children
362
- };
363
- }) };
364
- }
365
- return result;
366
- }
367
- //#endregion
368
- //#region source/plugins/content-artifacts/artifacts.ts
369
- var UTF8_SIGNATURE = "";
370
- function normalizeBasePath(basePath) {
371
- if (!basePath || basePath === "/") return "";
372
- return "/" + basePath.replace(/^\/+|\/+$/g, "");
373
- }
374
- function containsNonAscii(content) {
375
- return [...content].some((character) => character.codePointAt(0) > 127);
376
- }
377
- function withUtf8Signature(content) {
378
- if (!containsNonAscii(content) || content.startsWith(UTF8_SIGNATURE)) return content;
379
- return `${UTF8_SIGNATURE}${content}`;
380
- }
381
- function shouldUseUtf8Signature(route) {
382
- if (route.kind === "mdx") return true;
383
- return /\.ya?ml$/i.test(route.contentArtifactUrl ?? "");
384
- }
385
- function routeToMarkdownArtifactUrl(routePath) {
386
- return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.md`;
387
- }
388
- function routeToOpenAPIArtifactUrl(routePath, filePath) {
389
- const extension = extname(filePath).toLowerCase() || ".json";
390
- return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.openapi${extension}`;
391
- }
392
- function attachContentArtifactUrls(routes) {
393
- for (const route of routes) route.contentArtifactUrl = route.kind === "openapi" ? routeToOpenAPIArtifactUrl(route.path, route.filePath) : routeToMarkdownArtifactUrl(route.path);
394
- }
395
- function readRouteContent(route) {
396
- if (route.content !== void 0) return route.content;
397
- throw new Error(`Route content is missing from route context: ${route.filePath}`);
398
- }
399
- function readRouteArtifactContent(route) {
400
- const content = readRouteContent(route);
401
- return shouldUseUtf8Signature(route) ? withUtf8Signature(content) : content;
402
- }
403
- function writeContentArtifactFiles(routes, outputDirectory) {
404
- for (const route of routes) {
405
- if (!route.contentArtifactUrl) continue;
406
- const outFile = join(outputDirectory, route.contentArtifactUrl.replace(/^\//, ""));
407
- mkdirSync(dirname(outFile), { recursive: true });
408
- writeFileSync(outFile, readRouteArtifactContent(route), "utf-8");
409
- }
410
- }
411
- function createLlmsTxt(routes, projectConfig) {
412
- const basePath = normalizeBasePath(projectConfig.routePrefix);
413
- const lines = [`# ${projectConfig.title}`, ""];
414
- if (projectConfig.description) lines.push(`> ${projectConfig.description}`, "");
415
- lines.push("## Docs");
416
- for (const route of routes.filter((route) => route.kind === "mdx")) {
417
- if (!route.contentArtifactUrl) continue;
418
- lines.push(`- [${route.title}](${basePath}${route.contentArtifactUrl})`);
419
- }
420
- const openApiRoutes = routes.filter((route) => route.kind === "openapi");
421
- if (openApiRoutes.length > 0) {
422
- lines.push("", "## OpenAPI");
423
- for (const route of openApiRoutes) {
424
- if (!route.contentArtifactUrl) continue;
425
- lines.push(`- [${route.title}](${basePath}${route.contentArtifactUrl})`);
426
- }
427
- }
428
- return `${lines.join("\n")}\n`;
429
- }
430
- function createLlmsTxtArtifact(routes, projectConfig) {
431
- return withUtf8Signature(createLlmsTxt(routes, projectConfig));
432
- }
433
- function writeLlmsTxt(routes, projectConfig, outputDirectory) {
434
- mkdirSync(outputDirectory, { recursive: true });
435
- writeFileSync(join(outputDirectory, "llms.txt"), createLlmsTxtArtifact(routes, projectConfig), "utf-8");
436
- }
437
- //#endregion
438
- //#region source/plugins/content-artifacts/server.ts
439
- function normalizeRoutePrefix(routePrefix) {
440
- if (!routePrefix || routePrefix === "/") return "";
441
- return `/${routePrefix.replace(/^\/+|\/+$/g, "")}`;
442
- }
443
- function resolveContentArtifactPath(url, projectConfig) {
444
- const requestPath = url?.split("?")[0] ?? "";
445
- const basePath = normalizeRoutePrefix(projectConfig.routePrefix);
446
- return basePath && requestPath.startsWith(basePath) ? requestPath.slice(basePath.length) || "/" : requestPath;
447
- }
448
- function resolveContentArtifactType(route) {
449
- if (route.kind === "openapi" && /\.ya?ml$/i.test(route.contentArtifactUrl ?? "")) return "text/yaml; charset=utf-8";
450
- return route.kind === "openapi" ? "application/json; charset=utf-8" : "text/markdown; charset=utf-8";
451
- }
452
- function serveContentArtifacts(req, res, projectConfig, routes) {
453
- const contentPath = resolveContentArtifactPath(req.url, projectConfig);
454
- if (contentPath === "/llms.txt") {
455
- res.statusCode = 200;
456
- res.setHeader("Content-Type", "text/plain; charset=utf-8");
457
- res.end(createLlmsTxtArtifact(routes, projectConfig), "utf8");
458
- return true;
459
- }
460
- const route = routes.find((route) => route.contentArtifactUrl === contentPath);
461
- if (!route) return false;
462
- res.statusCode = 200;
463
- res.setHeader("Content-Type", resolveContentArtifactType(route));
464
- res.end(readRouteArtifactContent(route), "utf8");
465
- return true;
466
- }
467
- //#endregion
468
- //#region source/plugins/content-artifacts/index.ts
469
- function createContentArtifactsPlugin() {
470
- return {
471
- name: "clarify:content-artifacts",
472
- hooks: {
473
- "routes:resolved": (input) => {
474
- attachContentArtifactUrls(input.routes);
475
- return input;
476
- },
477
- "dev:configureServer": (server, ctx) => {
478
- server.middlewares.use((req, res, next) => {
479
- if (serveContentArtifacts(req, res, ctx.projectConfig, ctx.routes)) return;
480
- next();
481
- });
482
- },
483
- "build:done": (ctx) => {
484
- const outputDirectory = ctx.generateOptions.outputDirectory;
485
- if (!outputDirectory) return;
486
- writeContentArtifactFiles(ctx.routes, outputDirectory);
487
- writeLlmsTxt(ctx.routes, ctx.projectConfig, outputDirectory);
488
- }
489
- }
490
- };
491
- }
492
- //#endregion
493
- //#region source/plugins/html-shell/index.ts
494
- var THEME_BOOTSTRAP_SCRIPT = `(function(){try{var e=localStorage.getItem('clarify:theme');var t=e==='dark'||e==='light'||e==='system'?e:'system';var r=t==='system'?window.matchMedia('(prefers-color-scheme: dark)').matches:t==='dark';document.documentElement.classList.toggle('dark',r);document.documentElement.style.colorScheme=r?'dark':'light'}catch(e){}})();`;
495
- var REL_ICON_RE = /<link\s+[^>]*rel=["'][^"']*\bicon\b[^"']*["'][^>]*>/i;
496
- function inferIconType(href) {
497
- const path = href.split(/[?#]/, 1)[0]?.toLowerCase() ?? href.toLowerCase();
498
- if (path.endsWith(".svg")) return "image/svg+xml";
499
- if (path.endsWith(".png")) return "image/png";
500
- if (path.endsWith(".ico")) return "image/x-icon";
501
- if (path.endsWith(".webp")) return "image/webp";
502
- }
503
- function createIconTag(href, media) {
504
- return {
505
- tag: "link",
506
- attrs: {
507
- rel: "icon",
508
- ...inferIconType(href) ? { type: inferIconType(href) } : {},
509
- href,
510
- ...media ? { media } : {}
511
- },
512
- injectTo: "head"
513
- };
514
- }
515
- function createIconTags(icon) {
516
- if (!icon) return [];
517
- if (typeof icon === "string") return [createIconTag(icon)];
518
- const lightIcon = icon.light ?? icon.dark;
519
- const darkIcon = icon.dark ?? icon.light;
520
- if (!lightIcon || !darkIcon) return [];
521
- if (lightIcon === darkIcon) return [createIconTag(lightIcon)];
522
- return [createIconTag(lightIcon, "(prefers-color-scheme: light)"), createIconTag(darkIcon, "(prefers-color-scheme: dark)")];
523
- }
524
- function createHtmlShellPlugin() {
525
- return {
526
- name: "clarify:html-shell",
527
- hooks: { "html:transform": (input, ctx) => ({
528
- html: input.html,
529
- tags: [
530
- ...input.tags,
531
- ...REL_ICON_RE.test(input.html) ? [] : createIconTags(ctx.projectConfig.favicon ?? ctx.projectConfig.logo),
532
- {
533
- tag: "script",
534
- children: THEME_BOOTSTRAP_SCRIPT,
535
- injectTo: "head-prepend"
536
- },
537
- {
538
- tag: "script",
539
- attrs: {
540
- type: "module",
541
- src: input.clientEntryId
542
- },
543
- injectTo: "body"
544
- }
545
- ],
546
- clientEntryId: input.clientEntryId,
547
- dev: input.dev
548
- }) }
549
- };
537
+ return {
538
+ path: (redirect ? localizedRoutePath(basePathFromRef(redirect), locale, i18n) : void 0) ?? path,
539
+ title: resolveLocalizedText(title, locale, i18n.defaultLocale) ?? route?.title ?? kebabToTitle$1(basePath.split("/").pop() ?? ref),
540
+ icon,
541
+ sections: route?.sections ? navigationSections(route.sections) : void 0
542
+ };
543
+ });
544
+ return {
545
+ path: children[0]?.path ?? localizedRoutePath("/", locale, i18n),
546
+ title: resolveLocalizedText(group.group, locale, i18n.defaultLocale) ?? "",
547
+ icon: group.icon,
548
+ children
549
+ };
550
+ });
551
+ }
552
+ function buildLocalizedNavigationFromTabsConfig(routes, tabs, i18n) {
553
+ if (!i18n) return void 0;
554
+ const result = {};
555
+ for (const locale of i18n.locales) {
556
+ const localeRoutes = routes.filter((route) => route.locale === locale.code);
557
+ result[locale.code] = { tabs: tabs.map((tab) => {
558
+ const children = buildLocalizedNavigationForLocale(localeRoutes, tab.pages, locale.code, i18n);
559
+ return {
560
+ type: "tab",
561
+ path: firstNavigationPath(children),
562
+ title: resolveLocalizedText(tab.tab, locale.code, i18n.defaultLocale) ?? "",
563
+ icon: tab.icon,
564
+ children
565
+ };
566
+ }) };
567
+ }
568
+ return result;
550
569
  }
551
570
  //#endregion
552
571
  //#region source/plugins/openapi/parser.ts
@@ -591,21 +610,26 @@ function findOpenAPIRoutes(dir, base = dir) {
591
610
  }
592
611
  return routes;
593
612
  }
613
+ function operationMatchesTags(operationTags, filterTags) {
614
+ if (!filterTags?.length) return true;
615
+ return operationTags?.some((tag) => filterTags.includes(tag)) ?? false;
616
+ }
594
617
  /** Extract endpoint operations from an OpenAPI spec as page sections. */
595
- function extractOpenAPISections(spec) {
618
+ function extractOpenAPISections(spec, filterTags) {
596
619
  const sections = [];
597
620
  const paths = spec.paths ?? {};
598
621
  for (const [path, pathItem] of Object.entries(paths)) {
599
622
  if (!pathItem) continue;
600
623
  for (const method of OPENAPI_HTTP_METHODS) {
601
624
  const op = pathItem[method];
602
- if (!op) continue;
625
+ if (!op || !operationMatchesTags(op.tags, filterTags)) continue;
603
626
  const title = op.summary ?? `${method.toUpperCase()} ${path}`;
604
627
  sections.push({
605
628
  id: slug(`${method} ${path}`),
606
629
  title,
607
630
  level: 2,
608
- badge: method.toUpperCase()
631
+ badge: method.toUpperCase(),
632
+ tags: op.tags
609
633
  });
610
634
  }
611
635
  }
@@ -630,17 +654,89 @@ async function readOpenAPISpec(filePath) {
630
654
  }
631
655
  }
632
656
  //#endregion
657
+ //#region source/core/virtual-modules.ts
658
+ var VIRTUAL_CONFIG = "virtual:clarify-config";
659
+ var VIRTUAL_ROUTES = "virtual:clarify-routes";
660
+ var VIRTUAL_SERVER_ROUTES = "virtual:clarify-routes/server";
661
+ var VIRTUAL_RUNTIME = "virtual:clarify-runtime";
662
+ var VIRTUAL_CLIENT_ENTRY = "virtual:clarify-entry-client";
663
+ var RESOLVED_CLIENT_ENTRY = "\0virtual:clarify-entry-client";
664
+ function resolveVirtualId(id) {
665
+ return "\0" + id;
666
+ }
667
+ function stripVirtualPrefix(id) {
668
+ return id.startsWith("\0") ? id.slice(1) : id;
669
+ }
670
+ function generateConfigModule(projectConfig, buildOptions) {
671
+ return `export const config = ${JSON.stringify({
672
+ ...projectConfig,
673
+ ...buildOptions
674
+ })};`;
675
+ }
676
+ function moduleSpecifier(value) {
677
+ return JSON.stringify(value);
678
+ }
679
+ function generateRoutesModule(routes, resolvedNavigation, projectConfig, mode = "client") {
680
+ const imports = mode === "server" ? routes.map((r, i) => `import Page${i} from ${moduleSpecifier(r.virtualModuleId)};`).join("\n") : "";
681
+ const routesArray = routes.map((r, i) => {
682
+ const sections = r.sections && r.sections.length > 0 ? `, sections: ${JSON.stringify(r.sections.map((s) => ({
683
+ id: s.id,
684
+ title: s.title,
685
+ badge: s.badge,
686
+ tags: s.tags
687
+ })))}` : "";
688
+ const contentArtifactUrl = r.contentArtifactUrl ? `, contentArtifactUrl: ${JSON.stringify(r.contentArtifactUrl)}` : "";
689
+ const basePath = r.basePath ? `, basePath: ${JSON.stringify(r.basePath)}` : "";
690
+ const locale = r.locale ? `, locale: ${JSON.stringify(r.locale)}` : "";
691
+ const isFallback = r.isFallback ? ", isFallback: true" : "";
692
+ const alternates = r.alternates ? `, alternates: ${JSON.stringify(r.alternates)}` : "";
693
+ const description = r.description ? `, description: ${JSON.stringify(r.description)}` : "";
694
+ const keywords = r.keywords && r.keywords.length > 0 ? `, keywords: ${JSON.stringify(r.keywords)}` : "";
695
+ const sourceUrl = r.sourceUrl ? `, sourceUrl: ${JSON.stringify(r.sourceUrl)}` : "";
696
+ const component = mode === "server" ? `Page${i}` : `() => import(${moduleSpecifier(r.virtualModuleId)})`;
697
+ const lazy = mode === "client" ? ", lazy: true" : "";
698
+ return ` { path: ${JSON.stringify(r.path)}, title: ${JSON.stringify(r.title)}, component: ${component}${lazy}, kind: ${JSON.stringify(r.kind)}${basePath}${locale}${isFallback}${alternates}${description}${keywords}${sections}${contentArtifactUrl}${sourceUrl} }`;
699
+ }).join(",\n");
700
+ const navigation = resolvedNavigation ?? (projectConfig?.tabs ? projectConfig.i18n ? buildLocalizedNavigationFromTabsConfig(routes, projectConfig.tabs, projectConfig.i18n) ?? {} : buildNavigationFromTabsConfig(routes, projectConfig.tabs) : buildNavigation(routes));
701
+ return `${imports}\n\nexport const routes = [\n${routesArray}\n];\n\nexport const navigation = ${JSON.stringify(navigation, null, 2)};\n`;
702
+ }
703
+ function createRuntimeModule() {
704
+ return "export const openApis = {};";
705
+ }
706
+ function createClientEntryModule(options = {}) {
707
+ return `
708
+ import '@clarify-labs/renderer/style.css';
709
+ import { render } from '@clarify-labs/renderer/client';
710
+ import { routes, navigation } from '${VIRTUAL_ROUTES}';
711
+ import { config } from '${VIRTUAL_CONFIG}';
712
+ import { openApis } from '${VIRTUAL_RUNTIME}';
713
+ render({ config, routes, navigation, openApis, themeEditor: ${JSON.stringify(options.themeEditor ?? false)} });`;
714
+ }
715
+ function buildVirtualModules(args) {
716
+ const modules = /* @__PURE__ */ new Map();
717
+ const clientEntryModule = createClientEntryModule({ themeEditor: args.themeEditor });
718
+ modules.set(VIRTUAL_CONFIG, generateConfigModule(args.projectConfig, args.generateOptions));
719
+ modules.set(VIRTUAL_ROUTES, generateRoutesModule(args.routes, args.navigation, args.projectConfig, "client"));
720
+ modules.set(VIRTUAL_SERVER_ROUTES, generateRoutesModule(args.routes, args.navigation, args.projectConfig, "server"));
721
+ modules.set(VIRTUAL_RUNTIME, createRuntimeModule());
722
+ modules.set(VIRTUAL_CLIENT_ENTRY, clientEntryModule);
723
+ modules.set(RESOLVED_CLIENT_ENTRY, clientEntryModule);
724
+ for (const route of args.routes) modules.set(route.virtualModuleId, `export { default } from ${moduleSpecifier(route.filePath)};`);
725
+ return modules;
726
+ }
727
+ //#endregion
633
728
  //#region source/plugins/openapi/virtual-modules.ts
634
- var openApiRegistryModuleId = "virtual:clarify-openapi-registry";
729
+ var openApiRegistryModuleId = VIRTUAL_RUNTIME;
635
730
  function generateOpenAPIRegistryModule(openApis) {
636
731
  return `export const openApis = ${JSON.stringify(openApis)};`;
637
732
  }
638
- function generateOpenAPIModule(spec) {
733
+ function generateOpenAPIModule(spec, tagFilter) {
639
734
  return `import { createElement } from 'react';
640
735
  import { OpenApiDocument } from '@clarify-labs/renderer';
641
736
  const spec = ${JSON.stringify(spec)};
737
+ const tagFilter = ${JSON.stringify(tagFilter)};
642
738
  export default function OpenApiRoutePage() {
643
- return createElement(OpenApiDocument, { spec });
739
+ return createElement(OpenApiDocument, { spec, tagFilter });
644
740
  }`;
645
741
  }
646
742
  function generateOpenAPIErrorModule(diagnostic) {
@@ -666,6 +762,55 @@ export default function OpenApiErrorRoutePage() {
666
762
  }
667
763
  //#endregion
668
764
  //#region source/plugins/openapi/index.ts
765
+ function collectOpenAPIPageItems(config) {
766
+ const items = [];
767
+ function visitPages(pages) {
768
+ if (!pages || pages === "FileTree") return;
769
+ for (const group of pages) for (const item of group.pages) if (typeof item !== "string" && "openapi" in item && item.filter?.tags?.length) items.push(item);
770
+ }
771
+ for (const tab of config.tabs ?? []) visitPages(tab.pages);
772
+ return items;
773
+ }
774
+ function withAlternates$1(route, routes, i18n) {
775
+ const basePath = route.basePath ?? route.path;
776
+ const routeByLocaleAndBase = new Map(routes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
777
+ const alternates = Object.fromEntries(i18n.locales.flatMap((locale) => {
778
+ const alternate = routeByLocaleAndBase.get(`${locale.code}:${basePath}`);
779
+ return alternate ? [[locale.code, alternate.path]] : [];
780
+ }));
781
+ return {
782
+ ...route,
783
+ alternates
784
+ };
785
+ }
786
+ function createTaggedOpenAPIRoutes(routes, config) {
787
+ const pageItems = collectOpenAPIPageItems(config);
788
+ if (!pageItems.length) return routes;
789
+ const additions = [];
790
+ const existingKeys = new Set(routes.map((route) => `${route.locale ?? ""}:${route.basePath ?? route.path}`));
791
+ for (const item of pageItems) {
792
+ const tagFilter = item.filter?.tags?.filter(Boolean);
793
+ if (!tagFilter?.length) continue;
794
+ const sourceBasePath = openAPIPagePathFromRef(item.openapi);
795
+ const filteredBasePath = openAPIPagePathFromRef(item.openapi, tagFilter);
796
+ for (const route of routes) {
797
+ if (route.kind !== "openapi" || (route.basePath ?? route.path) !== sourceBasePath) continue;
798
+ const key = `${route.locale ?? ""}:${filteredBasePath}`;
799
+ if (existingKeys.has(key)) continue;
800
+ existingKeys.add(key);
801
+ additions.push({
802
+ ...route,
803
+ path: route.locale && config.i18n ? localizedRoutePath(filteredBasePath, route.locale, config.i18n) : filteredBasePath,
804
+ basePath: filteredBasePath,
805
+ title: route.title,
806
+ virtualModuleId: `${route.virtualModuleId}/${openAPITagsPathSegment(tagFilter)}`,
807
+ openapiTagFilter: tagFilter
808
+ });
809
+ }
810
+ }
811
+ const nextRoutes = [...routes, ...additions];
812
+ return config.i18n ? nextRoutes.map((route) => withAlternates$1(route, nextRoutes, config.i18n)) : nextRoutes;
813
+ }
669
814
  function createOpenAPIPlugin() {
670
815
  const specs = {};
671
816
  return {
@@ -675,10 +820,16 @@ function createOpenAPIPlugin() {
675
820
  ...input,
676
821
  routes: [...input.routes, ...findOpenAPIRoutes(input.contentRoot)]
677
822
  }),
678
- "routes:discovered": async (routes) => {
823
+ "routes:discovered": async (routes, ctx) => {
679
824
  for (const key of Object.keys(specs)) delete specs[key];
680
- for (const route of routes.filter((route) => route.kind === "openapi")) {
681
- const result = await readOpenAPISpec(route.filePath);
825
+ const nextRoutes = createTaggedOpenAPIRoutes(routes, ctx.projectConfig);
826
+ const specByFilePath = /* @__PURE__ */ new Map();
827
+ for (const route of nextRoutes.filter((route) => route.kind === "openapi")) {
828
+ const specFromCache = specByFilePath.get(route.filePath);
829
+ const result = specFromCache ? {
830
+ ok: true,
831
+ spec: specFromCache
832
+ } : await readOpenAPISpec(route.filePath);
682
833
  if (!result.ok) {
683
834
  route.diagnostic = result.diagnostic;
684
835
  route.title = route.title || "OpenAPI parse error";
@@ -686,17 +837,24 @@ function createOpenAPIPlugin() {
686
837
  continue;
687
838
  }
688
839
  const spec = result.spec;
689
- specs[route.virtualModuleId] = spec;
690
- specs[`virtual:clarify-page/${route.path.replace(/^\//, "")}`] = spec;
840
+ specByFilePath.set(route.filePath, spec);
841
+ specs[route.virtualModuleId] = {
842
+ spec,
843
+ tagFilter: route.openapiTagFilter
844
+ };
845
+ specs[`virtual:clarify-page/${route.path.replace(/^\//, "")}`] = {
846
+ spec,
847
+ tagFilter: route.openapiTagFilter
848
+ };
691
849
  route.title = spec.info?.title ?? route.title;
692
850
  route.description = spec.info?.description ?? route.description;
693
- route.sections = extractOpenAPISections(spec);
851
+ route.sections = extractOpenAPISections(spec, route.openapiTagFilter);
694
852
  }
695
- return routes;
853
+ return nextRoutes;
696
854
  },
697
855
  "modules:before": (modules, ctx) => {
698
- modules.set(openApiRegistryModuleId, generateOpenAPIRegistryModule(specs));
699
- for (const [moduleId, spec] of Object.entries(specs)) modules.set(moduleId, generateOpenAPIModule(spec));
856
+ modules.set(openApiRegistryModuleId, generateOpenAPIRegistryModule(Object.fromEntries(Object.entries(specs).map(([moduleId, entry]) => [moduleId, entry.spec]))));
857
+ for (const [moduleId, entry] of Object.entries(specs)) modules.set(moduleId, generateOpenAPIModule(entry.spec, entry.tagFilter));
700
858
  for (const route of ctx.routes.filter((route) => route.kind === "openapi" && route.diagnostic)) if (route.diagnostic) modules.set(route.virtualModuleId, generateOpenAPIErrorModule(route.diagnostic));
701
859
  return modules;
702
860
  }
@@ -704,6 +862,107 @@ function createOpenAPIPlugin() {
704
862
  };
705
863
  }
706
864
  //#endregion
865
+ //#region source/plugins/seo/artifacts.ts
866
+ function trimTrailingSlash(value) {
867
+ return value.replace(/\/+$/, "");
868
+ }
869
+ function normalizeRoutePrefix(routePrefix) {
870
+ if (!routePrefix || routePrefix === "/") return "";
871
+ return `/${routePrefix.replace(/^\/+|\/+$/g, "")}`;
872
+ }
873
+ function routeUrl(siteUrl, routePrefix, routePath) {
874
+ const base = trimTrailingSlash(siteUrl);
875
+ return `${base}${normalizeRoutePrefix(routePrefix)}${routePath === "/" ? "" : routePath.replace(/^\/+/, "/")}` || base;
876
+ }
877
+ function escapeXml(value) {
878
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
879
+ }
880
+ function publicRoutes(routes) {
881
+ return routes.filter((route) => route.path !== "/404");
882
+ }
883
+ function createSitemapXml(routes, config) {
884
+ if (!config.siteUrl) return void 0;
885
+ return [
886
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
887
+ "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">",
888
+ ...publicRoutes(routes).map((route) => routeUrl(config.siteUrl, config.routePrefix, route.path)).filter((url, index, list) => list.indexOf(url) === index).sort().map((url) => ` <url><loc>${escapeXml(url)}</loc></url>`),
889
+ "</urlset>",
890
+ ""
891
+ ].join("\n");
892
+ }
893
+ function createRobotsTxt(config) {
894
+ if (!config.siteUrl) return void 0;
895
+ return [
896
+ "User-agent: *",
897
+ "Allow: /",
898
+ `Sitemap: ${routeUrl(config.siteUrl, config.routePrefix, "/sitemap.xml")}`,
899
+ ""
900
+ ].join("\n");
901
+ }
902
+ async function writeSeoFiles(outputRoot, routes, config) {
903
+ const sitemap = createSitemapXml(routes, config);
904
+ const robots = createRobotsTxt(config);
905
+ if (!sitemap || !robots) return;
906
+ await mkdir(outputRoot, { recursive: true });
907
+ await Promise.all([writeFile(join(outputRoot, "sitemap.xml"), sitemap, "utf-8"), writeFile(join(outputRoot, "robots.txt"), robots, "utf-8")]);
908
+ }
909
+ //#endregion
910
+ //#region source/plugins/seo/index.ts
911
+ function createSeoPlugin() {
912
+ return {
913
+ name: "clarify:seo",
914
+ hooks: { async "build:done"(ctx) {
915
+ const outputDirectory = ctx.generateOptions.outputDirectory;
916
+ if (!outputDirectory) return;
917
+ await writeSeoFiles(join(ctx.generateOptions.projectRoot, outputDirectory), ctx.routes, ctx.projectConfig);
918
+ } }
919
+ };
920
+ }
921
+ //#endregion
922
+ //#region source/plugins/source-links/source-links.ts
923
+ function trimSlashes(value) {
924
+ return value.replace(/^\/+|\/+$/g, "");
925
+ }
926
+ function normalizeRepositoryUrl(repository) {
927
+ return repository.replace(/\.git$/, "").replace(/\/+$/, "");
928
+ }
929
+ function encodePath(path) {
930
+ return path.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
931
+ }
932
+ function createSourceUrl(filePath, contentRoot, source) {
933
+ const repository = normalizeRepositoryUrl(source.repository);
934
+ const branch = source.branch ?? "main";
935
+ const sourcePath = [source.directory ? trimSlashes(source.directory) : "", relative(contentRoot, filePath).replaceAll("\\", "/")].filter(Boolean).join("/");
936
+ return `${repository}/edit/${encodeURIComponent(branch)}/${encodePath(sourcePath)}`;
937
+ }
938
+ function attachSourceUrls(routes, contentRoot, source) {
939
+ if (!source) return;
940
+ for (const route of routes) route.sourceUrl = createSourceUrl(route.filePath, contentRoot, source);
941
+ }
942
+ //#endregion
943
+ //#region source/plugins/source-links/index.ts
944
+ function createSourceLinksPlugin() {
945
+ return {
946
+ name: "clarify:source-links",
947
+ hooks: { "routes:discovered": (routes, ctx) => {
948
+ attachSourceUrls(routes, join(ctx.generateOptions.projectRoot, ctx.generateOptions.rootDirectory), ctx.projectConfig.source);
949
+ return routes;
950
+ } }
951
+ };
952
+ }
953
+ //#endregion
954
+ //#region source/core/builtin.ts
955
+ function createBuiltinPlugins(options = {}) {
956
+ const plugins = [
957
+ createOpenAPIPlugin(),
958
+ createSourceLinksPlugin(),
959
+ createContentArtifactsPlugin(),
960
+ createSeoPlugin()
961
+ ];
962
+ if (options.htmlShell ?? true) plugins.push(createHtmlShellPlugin());
963
+ return plugins;
964
+ }
965
+ //#endregion
707
966
  //#region source/core/config-schema.ts
708
967
  var clarifyLogoConfigSchema = z.union([z.string(), z.object({
709
968
  light: z.string().optional(),
@@ -761,6 +1020,11 @@ var clarifyFooterConfigSchema = z.object({
761
1020
  socials: z.record(z.string(), z.string()).optional(),
762
1021
  copyright: clarifyLocalizedTextSchema.optional()
763
1022
  });
1023
+ var clarifySourceConfigSchema = z.object({
1024
+ repository: z.string(),
1025
+ branch: z.string().optional(),
1026
+ directory: z.string().optional()
1027
+ });
764
1028
  var clarifyPagesItemSchema = z.union([
765
1029
  z.string(),
766
1030
  z.object({
@@ -772,7 +1036,8 @@ var clarifyPagesItemSchema = z.union([
772
1036
  z.object({
773
1037
  openapi: z.string(),
774
1038
  icon: z.string().optional(),
775
- title: clarifyLocalizedTextSchema.optional()
1039
+ title: clarifyLocalizedTextSchema.optional(),
1040
+ filter: z.object({ tags: z.array(z.string()).optional() }).optional()
776
1041
  })
777
1042
  ]);
778
1043
  var clarifyPagesGroupSchema = z.object({
@@ -788,15 +1053,20 @@ var clarifyTabItemSchema = z.object({
788
1053
  });
789
1054
  var clarifyTabsConfigSchema = z.array(clarifyTabItemSchema);
790
1055
  var clarifyThemePresetSchema = z.enum(["default", "base"]);
1056
+ var clarifyThemeModeColorValueSchema = z.object({
1057
+ light: z.string().optional(),
1058
+ dark: z.string().optional()
1059
+ });
1060
+ var clarifyThemeColorValueSchema = z.union([z.string(), clarifyThemeModeColorValueSchema]);
791
1061
  var clarifyThemeColorTokensConfigSchema = z.object({
792
- primary: z.string().optional(),
793
- accent: z.string().optional(),
794
- background: z.string().optional(),
795
- foreground: z.string().optional(),
796
- surface: z.string().optional(),
797
- muted: z.string().optional(),
798
- border: z.string().optional(),
799
- codeBackground: z.string().optional()
1062
+ primary: clarifyThemeColorValueSchema.optional(),
1063
+ accent: clarifyThemeColorValueSchema.optional(),
1064
+ background: clarifyThemeColorValueSchema.optional(),
1065
+ foreground: clarifyThemeColorValueSchema.optional(),
1066
+ surface: clarifyThemeColorValueSchema.optional(),
1067
+ muted: clarifyThemeColorValueSchema.optional(),
1068
+ border: clarifyThemeColorValueSchema.optional(),
1069
+ codeBackground: clarifyThemeColorValueSchema.optional()
800
1070
  });
801
1071
  var clarifyThemeRadiusTokensConfigSchema = z.object({
802
1072
  sm: z.string().optional(),
@@ -812,11 +1082,14 @@ var clarifyThemeLayoutConfigSchema = z.object({ maxWidth: z.string().optional()
812
1082
  var clarifyThemeConfigSchema = z.object({
813
1083
  preset: clarifyThemePresetSchema.optional(),
814
1084
  tokens: clarifyThemeTokensConfigSchema.optional(),
815
- layout: clarifyThemeLayoutConfigSchema.optional()
1085
+ layout: clarifyThemeLayoutConfigSchema.optional(),
1086
+ editor: z.boolean().optional()
816
1087
  });
817
1088
  var clarifyProjectConfigSchema = z.object({
818
1089
  title: z.string().optional(),
819
1090
  description: z.string().optional(),
1091
+ siteUrl: z.string().optional(),
1092
+ source: clarifySourceConfigSchema.optional(),
820
1093
  logo: clarifyLogoConfigSchema.optional(),
821
1094
  favicon: clarifyFaviconConfigSchema.optional(),
822
1095
  theme: clarifyThemeConfigSchema.optional(),
@@ -834,14 +1107,14 @@ var clarifyThemePresets = {
834
1107
  preset: "default",
835
1108
  tokens: {
836
1109
  colors: {
837
- primary: "#00D492",
838
- accent: "#00F6C9",
1110
+ primary: "#047857",
1111
+ accent: "#0D9488",
839
1112
  background: "#ffffff",
840
1113
  foreground: "#111827",
841
1114
  surface: "#ffffff",
842
1115
  muted: "#64748b",
843
1116
  border: "rgb(15 23 42 / 0.12)",
844
- codeBackground: "#f7fefb"
1117
+ codeBackground: "#f6fbf8"
845
1118
  },
846
1119
  radius: {
847
1120
  sm: "6px",
@@ -850,7 +1123,8 @@ var clarifyThemePresets = {
850
1123
  xl: "18px"
851
1124
  }
852
1125
  },
853
- layout: { maxWidth: "82rem" }
1126
+ layout: { maxWidth: "82rem" },
1127
+ editor: false
854
1128
  },
855
1129
  base: {
856
1130
  preset: "base",
@@ -872,7 +1146,8 @@ var clarifyThemePresets = {
872
1146
  xl: "16px"
873
1147
  }
874
1148
  },
875
- layout: { maxWidth: "80rem" }
1149
+ layout: { maxWidth: "80rem" },
1150
+ editor: false
876
1151
  }
877
1152
  };
878
1153
  function resolveThemeConfig(theme = {}) {
@@ -893,7 +1168,8 @@ function resolveThemeConfig(theme = {}) {
893
1168
  layout: {
894
1169
  ...presetTheme.layout,
895
1170
  ...theme.layout
896
- }
1171
+ },
1172
+ editor: theme.editor ?? presetTheme.editor
897
1173
  };
898
1174
  }
899
1175
  //#endregion
@@ -932,6 +1208,8 @@ function resolveProjectConfig(config = {}) {
932
1208
  return {
933
1209
  title: config.title ?? "Clarify Docs",
934
1210
  description: config.description ?? "",
1211
+ siteUrl: config.siteUrl,
1212
+ source: config.source,
935
1213
  logo: config.logo,
936
1214
  favicon: config.favicon,
937
1215
  routePrefix: config.routePrefix ?? "/",
@@ -984,12 +1262,104 @@ async function runBuildDoneHooks(plugins, ctx) {
984
1262
  //#region source/core/options.ts
985
1263
  function resolveBuildOptions(options = {}) {
986
1264
  return {
1265
+ projectRoot: options.projectRoot ?? process.cwd(),
987
1266
  rootDirectory: options.rootDirectory ?? "source",
988
1267
  outputDirectory: options.outputDirectory,
989
1268
  ssg: { failOnError: options.ssg?.failOnError ?? true }
990
1269
  };
991
1270
  }
992
1271
  //#endregion
1272
+ //#region source/core/site.ts
1273
+ function withAlternates(route, routeList, i18n) {
1274
+ const basePath = route.basePath ?? route.path;
1275
+ const routeByLocaleAndBase = new Map(routeList.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
1276
+ const alternates = Object.fromEntries(i18n.locales.flatMap((locale) => {
1277
+ const alternate = routeByLocaleAndBase.get(`${locale.code}:${basePath}`);
1278
+ return alternate ? [[locale.code, alternate.path]] : [];
1279
+ }));
1280
+ return {
1281
+ ...route,
1282
+ alternates
1283
+ };
1284
+ }
1285
+ async function discoverRoutesForRoot(routeRoot, locale, plugins, ctx) {
1286
+ return (await runHooks(plugins, "routes:discover", {
1287
+ contentRoot: routeRoot,
1288
+ locale,
1289
+ routes: findContentRoutes(routeRoot)
1290
+ }, ctx)).routes;
1291
+ }
1292
+ async function discoverRoutes(root, contentRoot, plugins, ctx) {
1293
+ const i18n = ctx.projectConfig.i18n;
1294
+ if (!i18n) return discoverRoutesForRoot(contentRoot, void 0, plugins, ctx);
1295
+ const localizedRoutes = [];
1296
+ for (const locale of i18n.locales) {
1297
+ const discovered = await discoverRoutesForRoot(join(contentRoot, locale.code), locale.code, plugins, ctx);
1298
+ for (const route of discovered) {
1299
+ const basePath = route.basePath ?? route.path;
1300
+ localizedRoutes.push({
1301
+ ...route,
1302
+ path: localizedRoutePath(basePath, locale.code, i18n),
1303
+ basePath,
1304
+ locale: locale.code,
1305
+ virtualModuleId: virtualModuleIdFromRef(relative(contentRoot, route.filePath))
1306
+ });
1307
+ }
1308
+ }
1309
+ if (i18n.missing === "fallback") {
1310
+ const routeByLocaleAndBase = new Map(localizedRoutes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
1311
+ const defaultRoutes = localizedRoutes.filter((route) => route.locale === i18n.defaultLocale);
1312
+ for (const sourceRoute of defaultRoutes) {
1313
+ const basePath = sourceRoute.basePath ?? sourceRoute.path;
1314
+ for (const locale of i18n.locales) {
1315
+ const key = `${locale.code}:${basePath}`;
1316
+ if (routeByLocaleAndBase.has(key)) continue;
1317
+ localizedRoutes.push({
1318
+ ...sourceRoute,
1319
+ path: localizedRoutePath(basePath, locale.code, i18n),
1320
+ locale: locale.code,
1321
+ isFallback: true
1322
+ });
1323
+ }
1324
+ }
1325
+ }
1326
+ return localizedRoutes.map((route) => withAlternates(route, localizedRoutes, i18n));
1327
+ }
1328
+ async function resolveClarifySite(options = {}, resolveOptions = {}) {
1329
+ const root = options.projectRoot ?? process.cwd();
1330
+ const projectConfig = resolveProjectConfig(options);
1331
+ const generateOptions = resolveBuildOptions(options);
1332
+ const contentRoot = join(root, generateOptions.rootDirectory);
1333
+ const plugins = [...createBuiltinPlugins({ htmlShell: resolveOptions.includeHtmlShellPlugin }), ...options.plugins ?? []];
1334
+ const ctx = {
1335
+ projectConfig,
1336
+ generateOptions,
1337
+ routes: [],
1338
+ navigation: []
1339
+ };
1340
+ let routes = await discoverRoutes(root, contentRoot, plugins, ctx);
1341
+ routes = await runHooks(plugins, "routes:discovered", routes, ctx);
1342
+ const defaultNavigation = projectConfig.tabs ? projectConfig.i18n ? buildLocalizedNavigationFromTabsConfig(routes, projectConfig.tabs, projectConfig.i18n) ?? {} : buildNavigationFromTabsConfig(routes, projectConfig.tabs) : buildNavigation(routes);
1343
+ const resolved = await runHooks(plugins, "routes:resolved", {
1344
+ routes,
1345
+ navigation: defaultNavigation
1346
+ }, ctx);
1347
+ routes = resolved.routes;
1348
+ const navigation = resolved.navigation;
1349
+ ctx.routes = routes;
1350
+ ctx.navigation = navigation;
1351
+ return {
1352
+ root,
1353
+ contentRoot,
1354
+ projectConfig,
1355
+ generateOptions,
1356
+ plugins,
1357
+ ctx,
1358
+ routes,
1359
+ navigation
1360
+ };
1361
+ }
1362
+ //#endregion
993
1363
  //#region source/core/runtime-deps.ts
994
1364
  var requireFromCli = createRequire(import.meta.url);
995
1365
  function resolveCliDependency(id) {
@@ -1084,13 +1454,21 @@ function injectSSRIntoTemplate(template, appHtml, projectConfig, route) {
1084
1454
  html = html.replace(/<div id="root">[\s\S]*?<\/div>/, `<div id="root">${appHtml}</div>`);
1085
1455
  return html;
1086
1456
  }
1457
+ function isNotFoundRoute(route) {
1458
+ return (route.basePath ?? route.path) === "/404";
1459
+ }
1460
+ function routeOutputFiles(outputDirectory, route) {
1461
+ const files = [join(outputDirectory, route.path, "index.html")];
1462
+ if (isNotFoundRoute(route) && (!route.locale || route.path === "/404")) files.push(join(outputDirectory, "404.html"));
1463
+ return files;
1464
+ }
1087
1465
  var SSR_ENTRY_CODE = `import { renderToHTML } from '@clarify-labs/renderer/server';
1088
1466
  import { routes, navigation } from 'virtual:clarify-routes/server';
1089
1467
  import { config } from 'virtual:clarify-config';
1090
- import { openApis } from 'virtual:clarify-openapi-registry';
1468
+ import { openApis } from 'virtual:clarify-runtime';
1091
1469
 
1092
1470
  export function render(url) {
1093
- return renderToHTML({ config, routes, navigation, openApis, url });
1471
+ return renderToHTML({ config, routes, navigation, openApis, url, themeEditor: config.theme.editor });
1094
1472
  }`;
1095
1473
  function createTempEntryFile(content) {
1096
1474
  const entryPath = join(mkdtempSync(join(tmpdir(), "clarify-ssr-")), "entry-server.ts");
@@ -1121,9 +1499,10 @@ async function renderSSGRoutes(routes, projectConfig, outputDirectory, ssrBundle
1121
1499
  if (!template) throw new Error(`[clarify] index.html not found in outputDirectory "${outputDirectory}". Make sure Vite build produces it.`);
1122
1500
  for (const route of routes) try {
1123
1501
  const finalHtml = injectSSRIntoTemplate(template, render(route.path), projectConfig, route);
1124
- const outFile = join(outputDirectory, route.path, "index.html");
1125
- mkdirSync(dirname(outFile), { recursive: true });
1126
- writeFileSync(outFile, finalHtml, "utf-8");
1502
+ for (const outFile of routeOutputFiles(outputDirectory, route)) {
1503
+ mkdirSync(dirname(outFile), { recursive: true });
1504
+ writeFileSync(outFile, finalHtml, "utf-8");
1505
+ }
1127
1506
  } catch (err) {
1128
1507
  console.error(`[clarify] Failed to render route "${route.path}":`, err);
1129
1508
  if (failOnError) throw err;
@@ -1165,75 +1544,6 @@ async function loadClarifyConfig(root, env) {
1165
1544
  return assertConfigObject((await loadConfigFromFile(env, configFile, root, "silent"))?.config, configFile);
1166
1545
  }
1167
1546
  //#endregion
1168
- //#region source/core/virtual-modules.ts
1169
- var VIRTUAL_CONFIG = "virtual:clarify-config";
1170
- var VIRTUAL_ROUTES = "virtual:clarify-routes";
1171
- var VIRTUAL_SERVER_ROUTES = "virtual:clarify-routes/server";
1172
- var VIRTUAL_OPENAPI_REGISTRY = openApiRegistryModuleId;
1173
- var VIRTUAL_CLIENT_ENTRY = "virtual:clarify-entry-client";
1174
- var RESOLVED_CLIENT_ENTRY = "\0virtual:clarify-entry-client";
1175
- function resolveVirtualId(id) {
1176
- return "\0" + id;
1177
- }
1178
- function stripVirtualPrefix(id) {
1179
- return id.startsWith("\0") ? id.slice(1) : id;
1180
- }
1181
- function generateConfigModule(projectConfig, buildOptions) {
1182
- return `export const config = ${JSON.stringify({
1183
- ...projectConfig,
1184
- ...buildOptions
1185
- })};`;
1186
- }
1187
- function moduleSpecifier(value) {
1188
- return JSON.stringify(value);
1189
- }
1190
- function generateRoutesModule(routes, resolvedNavigation, projectConfig, mode = "client") {
1191
- const imports = mode === "server" ? routes.map((r, i) => `import Page${i} from ${moduleSpecifier(r.virtualModuleId)};`).join("\n") : "";
1192
- const routesArray = routes.map((r, i) => {
1193
- const sections = r.sections && r.sections.length > 0 ? `, sections: ${JSON.stringify(r.sections.map((s) => ({
1194
- id: s.id,
1195
- title: s.title,
1196
- badge: s.badge,
1197
- tags: s.tags
1198
- })))}` : "";
1199
- const contentArtifactUrl = r.contentArtifactUrl ? `, contentArtifactUrl: ${JSON.stringify(r.contentArtifactUrl)}` : "";
1200
- const basePath = r.basePath ? `, basePath: ${JSON.stringify(r.basePath)}` : "";
1201
- const locale = r.locale ? `, locale: ${JSON.stringify(r.locale)}` : "";
1202
- const isFallback = r.isFallback ? ", isFallback: true" : "";
1203
- const alternates = r.alternates ? `, alternates: ${JSON.stringify(r.alternates)}` : "";
1204
- const description = r.description ? `, description: ${JSON.stringify(r.description)}` : "";
1205
- const keywords = r.keywords && r.keywords.length > 0 ? `, keywords: ${JSON.stringify(r.keywords)}` : "";
1206
- const component = mode === "server" ? `Page${i}` : `() => import(${moduleSpecifier(r.virtualModuleId)})`;
1207
- const lazy = mode === "client" ? ", lazy: true" : "";
1208
- return ` { path: ${JSON.stringify(r.path)}, title: ${JSON.stringify(r.title)}, component: ${component}${lazy}, kind: ${JSON.stringify(r.kind)}${basePath}${locale}${isFallback}${alternates}${description}${keywords}${sections}${contentArtifactUrl} }`;
1209
- }).join(",\n");
1210
- const navigation = resolvedNavigation ?? (projectConfig?.tabs ? projectConfig.i18n ? buildLocalizedNavigationFromTabsConfig(routes, projectConfig.tabs, projectConfig.i18n) ?? {} : buildNavigationFromTabsConfig(routes, projectConfig.tabs) : buildNavigation(routes));
1211
- return `${imports}\n\nexport const routes = [\n${routesArray}\n];\n\nexport const navigation = ${JSON.stringify(navigation, null, 2)};\n`;
1212
- }
1213
- function createClientEntryModule() {
1214
- return `
1215
- import '@clarify-labs/renderer/style.css';
1216
- import { render } from '@clarify-labs/renderer/client';
1217
- import { routes, navigation } from '${VIRTUAL_ROUTES}';
1218
- import { config } from '${VIRTUAL_CONFIG}';
1219
- import { openApis } from '${VIRTUAL_OPENAPI_REGISTRY}';
1220
- render({ config, routes, navigation, openApis });`;
1221
- }
1222
- function buildVirtualModules(args) {
1223
- const modules = /* @__PURE__ */ new Map();
1224
- modules.set(VIRTUAL_CONFIG, generateConfigModule(args.projectConfig, args.generateOptions));
1225
- modules.set(VIRTUAL_ROUTES, generateRoutesModule(args.routes, args.navigation, args.projectConfig, "client"));
1226
- modules.set(VIRTUAL_SERVER_ROUTES, generateRoutesModule(args.routes, args.navigation, args.projectConfig, "server"));
1227
- modules.set(VIRTUAL_OPENAPI_REGISTRY, "export const openApis = {};");
1228
- modules.set(VIRTUAL_CLIENT_ENTRY, createClientEntryModule());
1229
- modules.set(RESOLVED_CLIENT_ENTRY, createClientEntryModule());
1230
- for (const route of args.routes) {
1231
- if (route.kind === "openapi") continue;
1232
- modules.set(route.virtualModuleId, `export { default } from ${moduleSpecifier(route.filePath)};`);
1233
- }
1234
- return modules;
1235
- }
1236
- //#endregion
1237
1547
  //#region source/core/plugin.ts
1238
1548
  function loadVirtualModule(id, modules) {
1239
1549
  const bareId = stripVirtualPrefix(id);
@@ -1246,12 +1556,7 @@ function clarifyPlugin(options = {}) {
1246
1556
  const contentRoot = join(root, generateOptions.rootDirectory);
1247
1557
  const configFilePath = findClarifyConfigFile(root);
1248
1558
  let routes = [];
1249
- const clarifyPlugins = [
1250
- createOpenAPIPlugin(),
1251
- createContentArtifactsPlugin(),
1252
- createHtmlShellPlugin(),
1253
- ...options.plugins ?? []
1254
- ];
1559
+ let clarifyPlugins = [...createBuiltinPlugins(), ...options.plugins ?? []];
1255
1560
  const ctx = {
1256
1561
  projectConfig,
1257
1562
  generateOptions,
@@ -1276,71 +1581,15 @@ function clarifyPlugin(options = {}) {
1276
1581
  ctx.projectConfig = projectConfig;
1277
1582
  ctx.generateOptions = generateOptions;
1278
1583
  }
1279
- function withAlternates(route, routeList, i18n) {
1280
- const basePath = route.basePath ?? route.path;
1281
- const routeByLocaleAndBase = new Map(routeList.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
1282
- const alternates = Object.fromEntries(i18n.locales.flatMap((locale) => {
1283
- const alternate = routeByLocaleAndBase.get(`${locale.code}:${basePath}`);
1284
- return alternate ? [[locale.code, alternate.path]] : [];
1285
- }));
1286
- return {
1287
- ...route,
1288
- alternates
1289
- };
1290
- }
1291
- async function discoverRoutesForRoot(routeRoot, locale) {
1292
- return (await runHooks(clarifyPlugins, "routes:discover", {
1293
- contentRoot: routeRoot,
1294
- locale,
1295
- routes: findContentRoutes(routeRoot)
1296
- }, ctx)).routes;
1297
- }
1298
- async function discoverRoutes() {
1299
- const i18n = projectConfig.i18n;
1300
- if (!i18n) return discoverRoutesForRoot(contentRoot);
1301
- const localizedRoutes = [];
1302
- for (const locale of i18n.locales) {
1303
- const discovered = await discoverRoutesForRoot(join(contentRoot, locale.code), locale.code);
1304
- for (const route of discovered) {
1305
- const basePath = route.basePath ?? route.path;
1306
- localizedRoutes.push({
1307
- ...route,
1308
- path: localizedRoutePath(basePath, locale.code, i18n),
1309
- basePath,
1310
- locale: locale.code,
1311
- virtualModuleId: virtualModuleIdFromRef(relative(contentRoot, route.filePath))
1312
- });
1313
- }
1314
- }
1315
- if (i18n.missing === "fallback") {
1316
- const routeByLocaleAndBase = new Map(localizedRoutes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
1317
- const defaultRoutes = localizedRoutes.filter((route) => route.locale === i18n.defaultLocale);
1318
- for (const sourceRoute of defaultRoutes) {
1319
- const basePath = sourceRoute.basePath ?? sourceRoute.path;
1320
- for (const locale of i18n.locales) {
1321
- const key = `${locale.code}:${basePath}`;
1322
- if (routeByLocaleAndBase.has(key)) continue;
1323
- localizedRoutes.push({
1324
- ...sourceRoute,
1325
- path: localizedRoutePath(basePath, locale.code, i18n),
1326
- locale: locale.code,
1327
- isFallback: true
1328
- });
1329
- }
1330
- }
1331
- }
1332
- return localizedRoutes.map((route) => withAlternates(route, localizedRoutes, i18n));
1333
- }
1334
1584
  async function resolveRoutesAndSpecs() {
1335
- routes = await discoverRoutes();
1336
- routes = await runHooks(clarifyPlugins, "routes:discovered", routes, ctx);
1337
- const defaultNavigation = projectConfig.tabs ? projectConfig.i18n ? buildLocalizedNavigationFromTabsConfig(routes, projectConfig.tabs, projectConfig.i18n) ?? {} : buildNavigationFromTabsConfig(routes, projectConfig.tabs) : buildNavigation(routes);
1338
- const resolved = await runHooks(clarifyPlugins, "routes:resolved", {
1339
- routes,
1340
- navigation: defaultNavigation
1341
- }, ctx);
1342
- routes = resolved.routes;
1343
- resolvedNavigation = resolved.navigation;
1585
+ const site = await resolveClarifySite(options);
1586
+ projectConfig = site.projectConfig;
1587
+ generateOptions = site.generateOptions;
1588
+ routes = site.routes;
1589
+ resolvedNavigation = site.navigation;
1590
+ clarifyPlugins = site.plugins;
1591
+ ctx.projectConfig = projectConfig;
1592
+ ctx.generateOptions = generateOptions;
1344
1593
  ctx.routes = routes;
1345
1594
  ctx.navigation = resolvedNavigation;
1346
1595
  }
@@ -1349,7 +1598,8 @@ function clarifyPlugin(options = {}) {
1349
1598
  projectConfig,
1350
1599
  generateOptions,
1351
1600
  routes,
1352
- navigation: resolvedNavigation
1601
+ navigation: resolvedNavigation,
1602
+ themeEditor: viteConfig.command === "serve" || projectConfig.theme.editor
1353
1603
  });
1354
1604
  virtualModules = await runHooks(clarifyPlugins, "modules:before", virtualModules, ctx);
1355
1605
  }
@@ -1429,8 +1679,9 @@ function clarifyPlugin(options = {}) {
1429
1679
  if (id === "virtual:clarify-config" || id === resolveVirtualId("virtual:clarify-config")) return resolveVirtualId(VIRTUAL_CONFIG);
1430
1680
  if (id === "virtual:clarify-routes" || id === resolveVirtualId("virtual:clarify-routes")) return resolveVirtualId(VIRTUAL_ROUTES);
1431
1681
  if (id === "virtual:clarify-routes/server" || id === resolveVirtualId("virtual:clarify-routes/server")) return resolveVirtualId(VIRTUAL_SERVER_ROUTES);
1432
- if (id === VIRTUAL_OPENAPI_REGISTRY || id === resolveVirtualId(VIRTUAL_OPENAPI_REGISTRY)) return resolveVirtualId(VIRTUAL_OPENAPI_REGISTRY);
1433
- const route = routes.find((r) => r.virtualModuleId === id || r.virtualModuleId === stripVirtualPrefix(id));
1682
+ const moduleId = stripVirtualPrefix(id);
1683
+ if (virtualModules.has(moduleId)) return resolveVirtualId(moduleId);
1684
+ const route = routes.find((r) => r.virtualModuleId === id || r.virtualModuleId === moduleId);
1434
1685
  if (route) return resolveVirtualId(route.virtualModuleId);
1435
1686
  return null;
1436
1687
  },
@@ -1452,10 +1703,11 @@ function clarifyPlugin(options = {}) {
1452
1703
  transformIndexHtml: {
1453
1704
  order: "pre",
1454
1705
  async handler(html, transformCtx) {
1706
+ const clientEntryId = transformCtx.server ? `/@id/${VIRTUAL_CLIENT_ENTRY}` : VIRTUAL_CLIENT_ENTRY;
1455
1707
  const result = await runHooks(clarifyPlugins, "html:transform", {
1456
1708
  html,
1457
1709
  tags: [],
1458
- clientEntryId: transformCtx.server ? `/@id/${VIRTUAL_CLIENT_ENTRY}` : VIRTUAL_CLIENT_ENTRY,
1710
+ clientEntryId,
1459
1711
  dev: Boolean(transformCtx.server)
1460
1712
  }, ctx);
1461
1713
  return {
@@ -1483,7 +1735,7 @@ function clarifyPlugin(options = {}) {
1483
1735
  if (id === "virtual:clarify-config") return id;
1484
1736
  if (id === "virtual:clarify-routes") return id;
1485
1737
  if (id === "virtual:clarify-routes/server") return id;
1486
- if (id === VIRTUAL_OPENAPI_REGISTRY) return id;
1738
+ if (virtualModules.has(stripVirtualPrefix(id))) return stripVirtualPrefix(id);
1487
1739
  if (routes.find((r) => r.virtualModuleId === id)) return id;
1488
1740
  return null;
1489
1741
  },
@@ -1509,6 +1761,6 @@ function clarifyPlugin(options = {}) {
1509
1761
  ].flat().filter(Boolean);
1510
1762
  }
1511
1763
  //#endregion
1512
- export { createClarifyRuntimeAliases as i, defineConfig as n, loadClarifyConfig as r, clarifyPlugin as t };
1764
+ export { resolveClarifySite as a, createClarifyRuntimeAliases as i, defineConfig as n, loadClarifyConfig as r, clarifyPlugin as t };
1513
1765
 
1514
- //# sourceMappingURL=plugin-DChFdTyI.js.map
1766
+ //# sourceMappingURL=plugin-DC2f5YAH.js.map