@clarify-labs/cli 0.8.0 → 0.8.2

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.
@@ -9,12 +9,12 @@ import shiki from "shiki";
9
9
  import { visit } from "unist-util-visit";
10
10
  import { markdownRemarkPlugins, themeBootstrapScript, themePresets } from "@clarify-labs/renderer";
11
11
  import { stringify } from "yaml";
12
- import { remark } from "remark";
13
12
  import matter from "gray-matter";
13
+ import { remark } from "remark";
14
+ import { fileURLToPath, pathToFileURL } from "node:url";
14
15
  import SwaggerParser from "@apidevtools/swagger-parser";
15
16
  import { tmpdir } from "node:os";
16
17
  import { z } from "zod";
17
- import { pathToFileURL } from "node:url";
18
18
  import { build, loadConfigFromFile } from "vite";
19
19
  import { createRequire } from "node:module";
20
20
  //#region source/core/utils.ts
@@ -140,23 +140,76 @@ function readRouteArtifactContent(route) {
140
140
  const content = readRouteContent(route);
141
141
  return shouldUseUtf8Signature(route) ? withUtf8Signature(content) : content;
142
142
  }
143
+ function isLlmsTxtRoute(route) {
144
+ return !route.path.split("/").includes("404");
145
+ }
146
+ function llmsTxtDescription(route) {
147
+ if (route.description) return route.description;
148
+ const sections = route.sections?.filter((section) => section.level === 2).slice(0, 3).map((section) => section.title);
149
+ if (sections?.length) return `Covers ${sections.join(", ")}.`;
150
+ if (route.keywords?.length) return `Related topics: ${route.keywords.join(", ")}.`;
151
+ if (route.kind === "openapi") return "OpenAPI artifact for machine-readable API reference data.";
152
+ }
153
+ function llmsTxtListItem(route, basePath) {
154
+ if (!route.contentArtifactUrl) return void 0;
155
+ const description = llmsTxtDescription(route);
156
+ return description ? `- [${route.title}](${basePath}${route.contentArtifactUrl}): ${description}` : `- [${route.title}](${basePath}${route.contentArtifactUrl})`;
157
+ }
158
+ function llmsTxtLocaleLabel(locale, projectConfig) {
159
+ return projectConfig.i18n?.locales.find((item) => item.code === locale)?.label ?? locale;
160
+ }
161
+ function groupRoutesByLocale(routes) {
162
+ const groups = /* @__PURE__ */ new Map();
163
+ for (const route of routes) {
164
+ const key = route.locale ?? "default";
165
+ groups.set(key, [...groups.get(key) ?? [], route]);
166
+ }
167
+ return groups;
168
+ }
169
+ function groupLlmsTxtRoutesByLocale(routes, projectConfig) {
170
+ const routesBySource = /* @__PURE__ */ new Map();
171
+ const defaultLocale = projectConfig.i18n?.defaultLocale;
172
+ for (const route of routes) {
173
+ const sourceKey = `${route.locale ?? defaultLocale ?? "default"}:${route.basePath ?? route.contentArtifactUrl ?? route.path}`;
174
+ const previousRoute = routesBySource.get(sourceKey);
175
+ if (!previousRoute || previousRoute.locale && !route.locale) routesBySource.set(sourceKey, route);
176
+ }
177
+ return groupRoutesByLocale([...routesBySource.values()].map((route) => ({
178
+ ...route,
179
+ locale: route.locale ?? defaultLocale
180
+ })));
181
+ }
182
+ function llmsTxtDocsSectionTitle(locale, groupCount, projectConfig) {
183
+ if (groupCount <= 1) return "Docs";
184
+ return `Docs - ${locale === "default" ? "Default" : llmsTxtLocaleLabel(locale, projectConfig)}`;
185
+ }
143
186
  function createLlmsTxt(routes, projectConfig) {
144
187
  const basePath = normalizeBasePath(projectConfig.routePrefix);
145
188
  const lines = [`# ${projectConfig.title}`, ""];
146
189
  if (projectConfig.description) lines.push(`> ${projectConfig.description}`, "");
147
- lines.push("## Docs");
148
- for (const route of routes.filter((route) => route.kind === "mdx")) {
149
- if (!route.contentArtifactUrl) continue;
150
- lines.push(`- [${route.title}](${basePath}${route.contentArtifactUrl})`);
190
+ lines.push("This file lists the source-ready Markdown and OpenAPI artifacts for this documentation site.", "");
191
+ const mdxRoutes = routes.filter((route) => route.kind === "mdx" && isLlmsTxtRoute(route));
192
+ if (mdxRoutes.length > 0) {
193
+ const localizedGroups = groupLlmsTxtRoutesByLocale(mdxRoutes, projectConfig);
194
+ for (const [locale, localeRoutes] of localizedGroups) {
195
+ lines.push(`## ${llmsTxtDocsSectionTitle(locale, localizedGroups.size, projectConfig)}`);
196
+ for (const route of localeRoutes) {
197
+ const item = llmsTxtListItem(route, basePath);
198
+ if (item) lines.push(item);
199
+ }
200
+ lines.push("");
201
+ }
151
202
  }
152
- const openApiRoutes = routes.filter((route) => route.kind === "openapi");
203
+ const openApiRoutes = routes.filter((route) => route.kind === "openapi" && isLlmsTxtRoute(route));
153
204
  if (openApiRoutes.length > 0) {
154
- lines.push("", "## OpenAPI");
205
+ if (lines.at(-1) !== "") lines.push("");
206
+ lines.push("## OpenAPI");
155
207
  for (const route of openApiRoutes) {
156
- if (!route.contentArtifactUrl) continue;
157
- lines.push(`- [${route.title}](${basePath}${route.contentArtifactUrl})`);
208
+ const item = llmsTxtListItem(route, basePath);
209
+ if (item) lines.push(item);
158
210
  }
159
211
  }
212
+ while (lines.at(-1) === "") lines.pop();
160
213
  return `${lines.join("\n")}\n`;
161
214
  }
162
215
  function createLlmsTxtArtifact(routes, projectConfig) {
@@ -316,6 +369,101 @@ function parseFrontmatter(content) {
316
369
  };
317
370
  }
318
371
  //#endregion
372
+ //#region source/parsers/content.ts
373
+ async function transformContent(transform, input) {
374
+ return transform ? await transform(input) : input;
375
+ }
376
+ function createContentProcessor(transform) {
377
+ return {
378
+ async processMdx(source, filePath) {
379
+ const { frontmatter, content } = parseFrontmatter(source);
380
+ const transformed = await transformContent(transform, {
381
+ kind: "mdx",
382
+ source,
383
+ filePath,
384
+ frontmatter,
385
+ content
386
+ });
387
+ return {
388
+ frontmatter: transformed.frontmatter,
389
+ content: transformed.content
390
+ };
391
+ },
392
+ async processText(source, kind, filePath) {
393
+ return (await transformContent(transform, {
394
+ kind,
395
+ source,
396
+ filePath,
397
+ frontmatter: {},
398
+ content: source
399
+ })).content;
400
+ }
401
+ };
402
+ }
403
+ //#endregion
404
+ //#region source/core/hooks.ts
405
+ async function runHooks(plugins, hookName, input, ctx) {
406
+ let result = input;
407
+ for (const plugin of plugins) {
408
+ const hook = plugin.hooks?.[hookName];
409
+ if (!hook) continue;
410
+ try {
411
+ result = await hook(result, ctx);
412
+ } catch (err) {
413
+ throw new Error(`[clarify] plugin "${plugin.name}" hook "${hookName}" failed: ${err}`, { cause: err });
414
+ }
415
+ }
416
+ return result;
417
+ }
418
+ async function runDevConfigureServerHooks(plugins, server, ctx) {
419
+ for (const plugin of plugins) {
420
+ const hook = plugin.hooks?.["dev:configureServer"];
421
+ if (!hook) continue;
422
+ try {
423
+ await hook(server, ctx);
424
+ } catch (err) {
425
+ throw new Error(`[clarify] plugin "${plugin.name}" hook "dev:configureServer" failed: ${err}`, { cause: err });
426
+ }
427
+ }
428
+ }
429
+ async function runBuildAssetsHooks(plugins, ctx) {
430
+ const assets = [];
431
+ for (const plugin of plugins) {
432
+ const hook = plugin.hooks?.["build:assets"];
433
+ if (!hook) continue;
434
+ try {
435
+ const pluginAssets = await hook(ctx);
436
+ assets.push(...pluginAssets);
437
+ } catch (err) {
438
+ throw new Error(`[clarify] plugin "${plugin.name}" hook "build:assets" failed: ${err}`, { cause: err });
439
+ }
440
+ }
441
+ return assets;
442
+ }
443
+ async function runBuildDoneHooks(plugins, ctx) {
444
+ for (const plugin of plugins) {
445
+ const hook = plugin.hooks?.["build:done"];
446
+ if (!hook) continue;
447
+ try {
448
+ await hook(ctx);
449
+ } catch (err) {
450
+ throw new Error(`[clarify] plugin "${plugin.name}" hook "build:done" failed: ${err}`, { cause: err });
451
+ }
452
+ }
453
+ }
454
+ //#endregion
455
+ //#region source/core/content.ts
456
+ var contentProcessors = /* @__PURE__ */ new WeakMap();
457
+ function createProjectContentProcessor(plugins, ctx) {
458
+ return createContentProcessor((input) => runHooks(plugins, "content:transform", input, ctx));
459
+ }
460
+ function setProjectContentProcessor(ctx, processor) {
461
+ contentProcessors.set(ctx, processor);
462
+ }
463
+ function getProjectContentProcessor(ctx) {
464
+ return contentProcessors.get(ctx);
465
+ }
466
+ //#endregion
319
467
  //#region source/parsers/routes.ts
320
468
  function kebabToTitle(str) {
321
469
  return str.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
@@ -392,18 +540,19 @@ function resolveLocalizedText(text, locale, fallbackLocale) {
392
540
  if (text === void 0) return void 0;
393
541
  return text[locale] ?? (fallbackLocale ? text[fallbackLocale] : void 0) ?? Object.values(text)[0];
394
542
  }
395
- function findContentRoutes(dir, base = dir) {
543
+ async function findContentRoutes(dir, base = dir, options = {}) {
396
544
  const routes = [];
397
545
  if (!existsSync(dir)) return routes;
398
546
  const entries = readdirSync(dir, { withFileTypes: true });
399
547
  for (const entry of entries) {
400
548
  const fullPath = join(dir, entry.name);
401
- if (entry.isDirectory()) routes.push(...findContentRoutes(fullPath, base));
549
+ if (entry.isDirectory()) routes.push(...await findContentRoutes(fullPath, base, options));
402
550
  else if (entry.isFile() && /\.mdx?$/.test(entry.name)) {
403
551
  const relativePath = relative(base, fullPath);
404
552
  const pathParts = relativePath.replace(/\.mdx?$/, "").split("/");
405
553
  const cleanPath = ("/" + pathParts.map((p) => p === "index" ? "" : p).filter(Boolean).join("/")).replace(/\/+/g, "/").replace(/\/$/, "") || "/";
406
- const { frontmatter, content } = parseFrontmatter(readFileSync(fullPath, "utf-8"));
554
+ const source = readFileSync(fullPath, "utf-8");
555
+ const { frontmatter, content } = await (options.contentProcessor ?? createContentProcessor()).processMdx(source, fullPath);
407
556
  let title = typeof frontmatter.title === "string" ? frontmatter.title : "";
408
557
  if (!title) {
409
558
  const lastPart = pathParts[pathParts.length - 1] ?? "";
@@ -660,6 +809,13 @@ var OPENAPI_HTTP_METHODS = [
660
809
  function errorMessage(error) {
661
810
  return error instanceof Error ? error.message : String(error);
662
811
  }
812
+ function isSameOpenAPIFile(url, filePath) {
813
+ try {
814
+ return resolve(fileURLToPath(url)) === resolve(filePath);
815
+ } catch {
816
+ return resolve(url) === resolve(filePath);
817
+ }
818
+ }
663
819
  function findOpenAPIRoutes(dir, base = dir) {
664
820
  const routes = [];
665
821
  if (!existsSync(dir)) return routes;
@@ -723,12 +879,26 @@ function extractOpenAPISections(spec, filterTags) {
723
879
  }
724
880
  return sections;
725
881
  }
726
- async function readOpenAPISpec(filePath) {
882
+ async function readOpenAPISpec(filePath, contentProcessor) {
727
883
  try {
728
- return {
884
+ if (!contentProcessor) return {
729
885
  ok: true,
730
886
  spec: await SwaggerParser.dereference(filePath)
731
887
  };
888
+ const source = readFileSync(filePath, "utf-8");
889
+ const transformedSource = await contentProcessor.processText(source, "openapi", filePath);
890
+ if (transformedSource === source) return {
891
+ ok: true,
892
+ spec: await SwaggerParser.dereference(filePath)
893
+ };
894
+ return {
895
+ ok: true,
896
+ spec: await SwaggerParser.dereference(filePath, { resolve: { transformedOpenAPISource: {
897
+ order: 1,
898
+ canRead: (file) => isSameOpenAPIFile(file.url, filePath),
899
+ read: () => transformedSource
900
+ } } })
901
+ };
732
902
  } catch (error) {
733
903
  return {
734
904
  ok: false,
@@ -989,7 +1159,7 @@ function createOpenAPIPlugin() {
989
1159
  const result = specFromCache ? {
990
1160
  ok: true,
991
1161
  spec: specFromCache
992
- } : await readOpenAPISpec(route.filePath);
1162
+ } : await readOpenAPISpec(route.filePath, getProjectContentProcessor(ctx));
993
1163
  if (!result.ok) {
994
1164
  route.diagnostic = result.diagnostic;
995
1165
  route.title = route.title || "OpenAPI parse error";
@@ -1306,9 +1476,51 @@ function createSourceLinksPlugin() {
1306
1476
  };
1307
1477
  }
1308
1478
  //#endregion
1479
+ //#region source/parsers/variables.ts
1480
+ var variablePattern = /{{\s*([A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*)\s*}}/g;
1481
+ function getVariableValue(variables, path) {
1482
+ let current = variables;
1483
+ for (const segment of path.split(".")) {
1484
+ if (!current || typeof current !== "object" || Array.isArray(current)) return void 0;
1485
+ current = current[segment];
1486
+ }
1487
+ return current;
1488
+ }
1489
+ function stringifyVariableValue(variables, path, stack) {
1490
+ if (stack.includes(path)) throw new Error(`[clarify] variable "${path}" contains a circular reference: ${[...stack, path].join(" -> ")}`);
1491
+ const value = getVariableValue(variables, path);
1492
+ if (value === void 0 || typeof value === "object") return void 0;
1493
+ if (typeof value !== "string") return String(value);
1494
+ return value.replace(variablePattern, (match, nestedPath) => stringifyVariableValue(variables, nestedPath, [...stack, path]) ?? match);
1495
+ }
1496
+ function applyVariables(content, variables = {}) {
1497
+ return content.replace(variablePattern, (match, path) => stringifyVariableValue(variables, path, []) ?? match);
1498
+ }
1499
+ //#endregion
1500
+ //#region source/plugins/variables/index.ts
1501
+ function applyVariablesToFrontmatter(frontmatter, variables) {
1502
+ const nextFrontmatter = { ...frontmatter };
1503
+ for (const key of ["title", "description"]) {
1504
+ if (typeof nextFrontmatter[key] !== "string") continue;
1505
+ nextFrontmatter[key] = applyVariables(nextFrontmatter[key], variables);
1506
+ }
1507
+ return nextFrontmatter;
1508
+ }
1509
+ function createVariablesPlugin() {
1510
+ return {
1511
+ name: "clarify:variables",
1512
+ hooks: { "content:transform": (input, ctx) => ({
1513
+ ...input,
1514
+ frontmatter: applyVariablesToFrontmatter(input.frontmatter, ctx.projectConfig.variables),
1515
+ content: applyVariables(input.content, ctx.projectConfig.variables)
1516
+ }) }
1517
+ };
1518
+ }
1519
+ //#endregion
1309
1520
  //#region source/core/builtin.ts
1310
1521
  function createBuiltinPlugins(options = {}) {
1311
1522
  const plugins = [
1523
+ createVariablesPlugin(),
1312
1524
  createOpenAPIPlugin(),
1313
1525
  createSourceLinksPlugin(),
1314
1526
  createContentArtifactsPlugin(),
@@ -1377,6 +1589,13 @@ var clarifyFooterConfigSchema = z.object({
1377
1589
  socials: z.record(z.string(), z.string()).optional(),
1378
1590
  copyright: clarifyLocalizedTextSchema.optional()
1379
1591
  }).strict();
1592
+ var clarifyVariableValueSchema = z.lazy(() => z.union([
1593
+ z.string(),
1594
+ z.number(),
1595
+ z.boolean(),
1596
+ z.record(z.string(), clarifyVariableValueSchema)
1597
+ ]));
1598
+ var clarifyVariablesConfigSchema = z.record(z.string(), clarifyVariableValueSchema);
1380
1599
  var clarifySourceConfigSchema = z.object({
1381
1600
  repository: z.string(),
1382
1601
  branch: z.string().optional(),
@@ -1454,9 +1673,11 @@ var clarifyProjectConfigSchema = z.object({
1454
1673
  favicon: clarifyFaviconConfigSchema.optional(),
1455
1674
  theme: clarifyThemeConfigSchema.optional(),
1456
1675
  routePrefix: z.string().optional(),
1676
+ assetPrefix: z.string().optional(),
1457
1677
  navbar: z.object({ links: z.array(clarifyNavbarLinkSchema).optional() }).optional(),
1458
1678
  banner: clarifyBannerConfigSchema.optional(),
1459
1679
  footer: clarifyFooterConfigSchema.optional(),
1680
+ variables: clarifyVariablesConfigSchema.optional(),
1460
1681
  i18n: clarifyI18nConfigSchema.optional(),
1461
1682
  tabs: clarifyTabsConfigSchema.optional()
1462
1683
  });
@@ -1516,7 +1737,22 @@ function resolveI18nConfig(i18n) {
1516
1737
  locales: i18n.locales
1517
1738
  };
1518
1739
  }
1740
+ function resolveRoutePrefix(routePrefix) {
1741
+ const trimmed = routePrefix?.trim();
1742
+ if (!trimmed || trimmed === "/") return "/";
1743
+ return `/${trimmed.replace(/^\/+|\/+$/g, "")}/`;
1744
+ }
1745
+ function resolveAssetPrefix(assetPrefix, routePrefix) {
1746
+ const trimmed = assetPrefix?.trim();
1747
+ if (trimmed === void 0) return routePrefix;
1748
+ if (trimmed === "./" || trimmed === "") return trimmed || "/";
1749
+ if (!trimmed || trimmed === "/") return "/";
1750
+ if (/^[a-z][a-z\d+.-]*:\/\//i.test(trimmed)) return trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
1751
+ if (trimmed.startsWith("./") || trimmed.startsWith("../")) return trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
1752
+ return `/${trimmed.replace(/^\/+|\/+$/g, "")}/`;
1753
+ }
1519
1754
  function resolveProjectConfig(config = {}) {
1755
+ const routePrefix = resolveRoutePrefix(config.routePrefix);
1520
1756
  return {
1521
1757
  title: config.title ?? "Clarify Docs",
1522
1758
  description: config.description ?? "",
@@ -1525,11 +1761,13 @@ function resolveProjectConfig(config = {}) {
1525
1761
  logo: config.logo,
1526
1762
  homeUrl: config.homeUrl,
1527
1763
  favicon: config.favicon,
1528
- routePrefix: config.routePrefix ?? "/",
1764
+ routePrefix,
1765
+ assetPrefix: resolveAssetPrefix(config.assetPrefix, routePrefix),
1529
1766
  theme: resolveThemeConfig(config.theme),
1530
1767
  navbar: config.navbar,
1531
1768
  banner: config.banner,
1532
1769
  footer: config.footer,
1770
+ variables: config.variables ?? {},
1533
1771
  i18n: resolveI18nConfig(config.i18n),
1534
1772
  tabs: config.tabs
1535
1773
  };
@@ -1583,57 +1821,6 @@ function writeClarifyEnvDts(projectRoot, plugins) {
1583
1821
  writeFileSync(filePath, next, "utf-8");
1584
1822
  }
1585
1823
  //#endregion
1586
- //#region source/core/hooks.ts
1587
- async function runHooks(plugins, hookName, input, ctx) {
1588
- let result = input;
1589
- for (const plugin of plugins) {
1590
- const hook = plugin.hooks?.[hookName];
1591
- if (!hook) continue;
1592
- try {
1593
- result = await hook(result, ctx);
1594
- } catch (err) {
1595
- throw new Error(`[clarify] plugin "${plugin.name}" hook "${hookName}" failed: ${err}`, { cause: err });
1596
- }
1597
- }
1598
- return result;
1599
- }
1600
- async function runDevConfigureServerHooks(plugins, server, ctx) {
1601
- for (const plugin of plugins) {
1602
- const hook = plugin.hooks?.["dev:configureServer"];
1603
- if (!hook) continue;
1604
- try {
1605
- await hook(server, ctx);
1606
- } catch (err) {
1607
- throw new Error(`[clarify] plugin "${plugin.name}" hook "dev:configureServer" failed: ${err}`, { cause: err });
1608
- }
1609
- }
1610
- }
1611
- async function runBuildAssetsHooks(plugins, ctx) {
1612
- const assets = [];
1613
- for (const plugin of plugins) {
1614
- const hook = plugin.hooks?.["build:assets"];
1615
- if (!hook) continue;
1616
- try {
1617
- const pluginAssets = await hook(ctx);
1618
- assets.push(...pluginAssets);
1619
- } catch (err) {
1620
- throw new Error(`[clarify] plugin "${plugin.name}" hook "build:assets" failed: ${err}`, { cause: err });
1621
- }
1622
- }
1623
- return assets;
1624
- }
1625
- async function runBuildDoneHooks(plugins, ctx) {
1626
- for (const plugin of plugins) {
1627
- const hook = plugin.hooks?.["build:done"];
1628
- if (!hook) continue;
1629
- try {
1630
- await hook(ctx);
1631
- } catch (err) {
1632
- throw new Error(`[clarify] plugin "${plugin.name}" hook "build:done" failed: ${err}`, { cause: err });
1633
- }
1634
- }
1635
- }
1636
- //#endregion
1637
1824
  //#region source/core/options.ts
1638
1825
  function resolveBuildOptions(options = {}) {
1639
1826
  return {
@@ -1649,7 +1836,7 @@ async function discoverRoutesForRoot(routeRoot, locale, plugins, ctx) {
1649
1836
  return (await runHooks(plugins, "routes:discover", {
1650
1837
  contentRoot: routeRoot,
1651
1838
  locale,
1652
- routes: findContentRoutes(routeRoot)
1839
+ routes: await findContentRoutes(routeRoot, routeRoot, { contentProcessor: getProjectContentProcessor(ctx) })
1653
1840
  }, ctx)).routes;
1654
1841
  }
1655
1842
  async function discoverRoutes(root, contentRoot, plugins, ctx) {
@@ -1714,6 +1901,7 @@ async function resolveClarifySite(options = {}, resolveOptions = {}) {
1714
1901
  routes: [],
1715
1902
  navigation: []
1716
1903
  };
1904
+ setProjectContentProcessor(ctx, createProjectContentProcessor(plugins, ctx));
1717
1905
  let routes = await discoverRoutes(root, contentRoot, plugins, ctx);
1718
1906
  routes = await runHooks(plugins, "routes:discovered", routes, ctx);
1719
1907
  routes = applyConfiguredPageRoutePaths(routes, projectConfig.tabs, projectConfig.i18n);
@@ -2042,7 +2230,7 @@ function clarifyPlugin(options = {}) {
2042
2230
  async config() {
2043
2231
  await resolveRoutesAndSpecs();
2044
2232
  return {
2045
- base: projectConfig.routePrefix,
2233
+ base: projectConfig.assetPrefix,
2046
2234
  build: {
2047
2235
  ...generateOptions.outputDirectory ? { outDir: generateOptions.outputDirectory } : {},
2048
2236
  manifest: true
@@ -2182,4 +2370,4 @@ function clarifyPlugin(options = {}) {
2182
2370
  //#endregion
2183
2371
  export { resolveClarifySite as a, createClarifyRuntimeAliases as i, defineConfig as n, createClarifyTempDir as o, loadClarifyConfig as r, clarifyPlugin as t };
2184
2372
 
2185
- //# sourceMappingURL=plugin-CcD5HeTN.js.map
2373
+ //# sourceMappingURL=plugin-D5EF3pYl.js.map