blume 0.5.2 → 0.5.4

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.
Files changed (137) hide show
  1. package/dist/cli/index.js +2235 -5942
  2. package/dist/cli/index.js.map +36 -65
  3. package/dist/types/core/config.d.ts +0 -8
  4. package/dist/types/core/data.d.ts +2 -2
  5. package/dist/types/core/schema.d.ts +58 -453
  6. package/dist/types/core/types.d.ts +1 -7
  7. package/docs/advanced/meta.ts +1 -8
  8. package/docs/advanced/skills.mdx +28 -0
  9. package/docs/configuration/ai.mdx +3 -1
  10. package/docs/configuration/index.mdx +13 -17
  11. package/docs/configuration/theming.mdx +3 -11
  12. package/docs/content/components.mdx +2 -53
  13. package/docs/content/i18n.mdx +1 -5
  14. package/docs/content/sources.mdx +1 -1
  15. package/docs/content/syntax.mdx +1 -1
  16. package/docs/index.mdx +0 -1
  17. package/docs/reference/cli.mdx +12 -13
  18. package/package.json +4 -6
  19. package/skills/blume/SKILL.md +71 -0
  20. package/skills/blume-update-docs/SKILL.md +52 -0
  21. package/skills/blume-update-docs/references/audit-checklist.md +46 -0
  22. package/src/ai/mcp/discovery.ts +3 -1
  23. package/src/ai/mcp/server.ts +3 -1
  24. package/src/astro/component-slots.ts +10 -2
  25. package/src/astro/generate.ts +15 -12
  26. package/src/astro/integration.ts +0 -21
  27. package/src/astro/templates.ts +62 -37
  28. package/src/cli/coalesce.ts +43 -0
  29. package/src/cli/commands/dev.ts +30 -22
  30. package/src/cli/commands/validate.ts +0 -2
  31. package/src/cli/dev-lock.ts +4 -2
  32. package/src/cli/index.ts +0 -2
  33. package/src/components/BlumePage.astro +0 -6
  34. package/src/components/Icon.astro +1 -12
  35. package/src/components/content/AccordionItem.astro +3 -6
  36. package/src/components/content/Badge.astro +1 -3
  37. package/src/components/content/Callout.astro +3 -9
  38. package/src/components/content/Card.astro +2 -3
  39. package/src/components/content/ColorItem.astro +8 -5
  40. package/src/components/content/Column.astro +1 -1
  41. package/src/components/content/Prompt.astro +8 -4
  42. package/src/components/content/Step.astro +3 -4
  43. package/src/components/content/Tab.astro +2 -3
  44. package/src/components/content/Tabs.astro +13 -2
  45. package/src/components/content/TypeTable.astro +5 -5
  46. package/src/components/content/Update.astro +1 -1
  47. package/src/components/content/mermaid-element.ts +20 -2
  48. package/src/components/islands/ask-ai.tsx +4 -8
  49. package/src/components/islands/base-path.ts +30 -0
  50. package/src/components/islands/hooks.ts +12 -8
  51. package/src/components/layout/Header.astro +26 -3
  52. package/src/components/layout/Logo.astro +5 -1
  53. package/src/components/layout/NavSelector.astro +1 -1
  54. package/src/components/layout/NavTree.astro +14 -14
  55. package/src/components/layout/PageActions.astro +17 -11
  56. package/src/components/layout/PageLayout.astro +42 -0
  57. package/src/components/layout/ReferenceLayout.astro +1 -0
  58. package/src/components/layout/RootLayout.astro +31 -2
  59. package/src/components/layout/Search.astro +9 -6
  60. package/src/components/layout/nav-utils.ts +9 -4
  61. package/src/components/layout/search/types.ts +16 -5
  62. package/src/components/openapi/ApiOverview.astro +4 -50
  63. package/src/components/openapi/ApiTagOperations.astro +42 -0
  64. package/src/components/openapi/ParametersTable.astro +1 -1
  65. package/src/components/openapi/SchemaProperty.astro +1 -1
  66. package/src/components/openapi/SchemaTable.astro +3 -3
  67. package/src/components/openapi/helpers.ts +17 -8
  68. package/src/components/openapi/snippets.ts +17 -4
  69. package/src/core/builtin-tags.ts +1 -3
  70. package/src/core/config.ts +19 -33
  71. package/src/core/data.ts +2 -2
  72. package/src/core/graph.ts +7 -5
  73. package/src/core/links.ts +5 -19
  74. package/src/core/meta.ts +1 -1
  75. package/src/core/navigation.ts +5 -6
  76. package/src/core/project-graph.ts +0 -5
  77. package/src/core/schema.ts +13 -90
  78. package/src/core/sources/filesystem.ts +19 -1
  79. package/src/core/sources/mdx-remote.ts +20 -4
  80. package/src/core/sources/normalize.ts +28 -6
  81. package/src/core/sources/resolve.ts +0 -13
  82. package/src/core/sources/watch.ts +45 -0
  83. package/src/core/types.ts +1 -8
  84. package/src/markdown/code-title.ts +6 -3
  85. package/src/markdown/package-install.ts +3 -1
  86. package/src/og/card.ts +16 -4
  87. package/src/openapi/parse.ts +197 -14
  88. package/src/openapi/render-mdx.ts +54 -11
  89. package/src/openapi/source.ts +19 -2
  90. package/src/search/sync/orama-cloud.ts +2 -0
  91. package/src/search/sync/typesense.ts +4 -0
  92. package/src/theme/entry.ts +45 -17
  93. package/src/theme/icons.ts +22 -104
  94. package/src/theme/palette.ts +35 -47
  95. package/src/theme/twoslash.ts +6 -1
  96. package/dist/types/core/bridge.d.ts +0 -24
  97. package/dist/types/migrate/mintlify/assets.d.ts +0 -8
  98. package/dist/types/migrate/mintlify/config.d.ts +0 -16
  99. package/dist/types/migrate/mintlify/i18n.d.ts +0 -7
  100. package/docs/advanced/bridge.mdx +0 -76
  101. package/docs/advanced/migrate.mdx +0 -123
  102. package/src/astro/static-assets.ts +0 -117
  103. package/src/cli/commands/migrate.ts +0 -39
  104. package/src/components/content/ApiField.astro +0 -75
  105. package/src/components/content/ParamField.astro +0 -39
  106. package/src/components/content/RequestField.astro +0 -23
  107. package/src/components/content/ResponseField.astro +0 -23
  108. package/src/components/content/Warning.astro +0 -9
  109. package/src/core/assets.ts +0 -31
  110. package/src/core/bridge.ts +0 -102
  111. package/src/core/sources/mintlify.ts +0 -204
  112. package/src/migrate/fumadocs/config.ts +0 -155
  113. package/src/migrate/fumadocs/content.ts +0 -378
  114. package/src/migrate/fumadocs/frontmatter.ts +0 -18
  115. package/src/migrate/fumadocs/groups.ts +0 -237
  116. package/src/migrate/fumadocs/index.ts +0 -340
  117. package/src/migrate/fumadocs/meta.ts +0 -244
  118. package/src/migrate/migrate.ts +0 -53
  119. package/src/migrate/mintlify/assets.ts +0 -46
  120. package/src/migrate/mintlify/config.ts +0 -958
  121. package/src/migrate/mintlify/content.ts +0 -120
  122. package/src/migrate/mintlify/frontmatter.ts +0 -126
  123. package/src/migrate/mintlify/i18n.ts +0 -51
  124. package/src/migrate/mintlify/icons.ts +0 -128
  125. package/src/migrate/mintlify/index.ts +0 -372
  126. package/src/migrate/mintlify/snippets.ts +0 -306
  127. package/src/migrate/mintlify/transform.ts +0 -82
  128. package/src/migrate/nextra/content.ts +0 -46
  129. package/src/migrate/nextra/frontmatter.ts +0 -40
  130. package/src/migrate/nextra/index.ts +0 -374
  131. package/src/migrate/nextra/meta.ts +0 -266
  132. package/src/migrate/shared.ts +0 -728
  133. package/src/migrate/starlight/config.ts +0 -455
  134. package/src/migrate/starlight/content.ts +0 -78
  135. package/src/migrate/starlight/frontmatter.ts +0 -111
  136. package/src/migrate/starlight/i18n.ts +0 -54
  137. package/src/migrate/starlight/index.ts +0 -131
@@ -1,120 +0,0 @@
1
- import { dirname, join, relative } from "pathe";
2
-
3
- import { rewriteCallouts } from "../shared.ts";
4
-
5
- /**
6
- * Source-to-source rewrites that turn Mintlify-only MDX component syntax into
7
- * idiomatic Blume markup. Runs once at migration time — no Mintlify-aware
8
- * plugins remain in the Blume runtime.
9
- */
10
-
11
- /** Mintlify callout components mapped to Blume directive names. */
12
- const CALLOUT_DIRECTIVES: Record<string, string> = {
13
- Check: "success",
14
- Danger: "danger",
15
- Error: "danger",
16
- Info: "info",
17
- Note: "note",
18
- Success: "success",
19
- Tip: "tip",
20
- Warning: "warning",
21
- };
22
-
23
- /** `<Callout type="X">` values mapped to Blume directive names. */
24
- const CALLOUT_TYPE_DIRECTIVES: Record<string, string> = {
25
- caution: "warning",
26
- check: "success",
27
- danger: "danger",
28
- error: "danger",
29
- info: "info",
30
- note: "note",
31
- success: "success",
32
- tip: "tip",
33
- warning: "warning",
34
- };
35
-
36
- /**
37
- * Convert Mintlify callout components (`<Note>`, `<Warning>`, `<Callout
38
- * type="…">`, …) into Blume `:::` directives.
39
- */
40
- export const rewriteMintlifyCallouts = (source: string): string =>
41
- rewriteCallouts(source, {
42
- defaultDirective: "note",
43
- tagDirectives: CALLOUT_DIRECTIVES,
44
- tags: ["Callout", ...Object.keys(CALLOUT_DIRECTIVES)],
45
- typeDirectives: CALLOUT_TYPE_DIRECTIVES,
46
- });
47
-
48
- /**
49
- * Mintlify's `<RequestExample>`/`<ResponseExample>` are tab-style code wrappers
50
- * with no Blume equivalent; rename them to `<CodeGroup>`, which renders the
51
- * same titled-fence tabs.
52
- */
53
- export const rewriteMintlifyExampleBlocks = (source: string): string =>
54
- source.replaceAll(
55
- /<(?<close>\/?)(?:Request|Response)Example\b/gu,
56
- "<$<close>CodeGroup"
57
- );
58
-
59
- /**
60
- * Mintlify nests `<Accordion title="…">` items inside an `<AccordionGroup>`.
61
- * Blume inverts that: `<Accordion>` is the container and each item is an
62
- * `<AccordionItem title="…">`. Rewrite both in a single pass — the `\b` after
63
- * `Accordion` keeps `<AccordionGroup>` (the `Group` branch) distinct from an
64
- * item, so open/close tags of either map correctly regardless of order. Without
65
- * this, migrated pages keep `<AccordionGroup>`, which Blume doesn't ship and the
66
- * MDX build rejects with "Expected component AccordionGroup to be defined".
67
- */
68
- export const rewriteMintlifyAccordions = (source: string): string =>
69
- source.replaceAll(
70
- /<(?<close>\/?)Accordion(?<group>Group)?\b/gu,
71
- (_match, close: string, group: string | undefined) =>
72
- group ? `<${close}Accordion` : `<${close}AccordionItem`
73
- );
74
-
75
- const SNIPPET_IMPORT =
76
- /^import\s+[\s\S]*?\s+from\s+["'](?<source>\/snippets\/[^"']+)["'];?[ \t]*\n?/gmu;
77
-
78
- /**
79
- * After snippets are inlined, clean up leftover `/snippets/*` imports: drop the
80
- * now-dead markdown imports, and rewrite component imports (`.jsx`/`.tsx`/…) to
81
- * a path relative to the page so they still resolve once `/snippets` content is
82
- * gone. Returns the rewritten source and the component files still referenced.
83
- */
84
- export const rewriteSnippetImports = (
85
- source: string,
86
- options: { filePath: string; root: string }
87
- ): { components: string[]; source: string } => {
88
- const components: string[] = [];
89
- const next = source.replaceAll(
90
- SNIPPET_IMPORT,
91
- (match, importSource: string) => {
92
- if (/\.mdx?$/u.test(importSource)) {
93
- return "";
94
- }
95
- const target = join(options.root, importSource.replace(/^\/+/u, ""));
96
- components.push(importSource.replace(/^\/+/u, ""));
97
- let rel = relative(dirname(options.filePath), target);
98
- if (!rel.startsWith(".")) {
99
- rel = `./${rel}`;
100
- }
101
- return match.replace(importSource, rel);
102
- }
103
- );
104
- return { components, source: next };
105
- };
106
-
107
- /**
108
- * Component tags Blume has no equivalent for — reported for manual review.
109
- * `<ParamField>`/`<ResponseField>`/`<RequestField>` are no longer here: Blume
110
- * ships compat components for them, so migrated docs render as-is. Mintlify's
111
- * `<Update>` changelog entry has no component form in Blume (changelog is
112
- * frontmatter-driven via `type: changelog`), so it stays flagged.
113
- */
114
- const UNSUPPORTED_COMPONENTS = ["Update"];
115
-
116
- /** Names of Mintlify components in `source` that need manual attention. */
117
- export const unsupportedMintlifyComponents = (source: string): string[] =>
118
- UNSUPPORTED_COMPONENTS.filter((name) =>
119
- new RegExp(`<${name}\\b`, "u").test(source)
120
- );
@@ -1,126 +0,0 @@
1
- import { z } from "zod";
2
-
3
- /**
4
- * One-time translation of Mintlify page frontmatter into Blume's shape. This
5
- * runs at migration time only — Blume's runtime page schema stays
6
- * Mintlify-free and strict. `sidebarTitle`/`icon`/`tag`/`hidden` fold into
7
- * `sidebar`, `hidden` implies `noindex`, `canonical`/`og:image` move under
8
- * `seo`, and OpenAPI/AsyncAPI pages become `type: "api"`.
9
- */
10
-
11
- const mintlifySidebarMetaSchema = z
12
- .object({
13
- badge: z.string().optional(),
14
- hidden: z.boolean().optional(),
15
- icon: z.string().optional(),
16
- label: z.string().optional(),
17
- order: z.number().optional(),
18
- })
19
- .passthrough();
20
-
21
- const mintlifyPageMetaInputSchema = z
22
- .object({
23
- api: z.unknown().optional(),
24
- asyncapi: z.string().optional(),
25
- canonical: z.string().optional(),
26
- hidden: z.boolean().optional(),
27
- icon: z.string().optional(),
28
- noindex: z.boolean().optional(),
29
- "og:image": z.string().optional(),
30
- openapi: z.string().optional(),
31
- sidebar: mintlifySidebarMetaSchema.optional(),
32
- sidebarTitle: z.string().optional(),
33
- tag: z.string().optional(),
34
- type: z.string().optional(),
35
- })
36
- .passthrough();
37
-
38
- type MintlifyPageMetaInput = z.infer<typeof mintlifyPageMetaInputSchema>;
39
-
40
- const normalizedMintlifySidebar = (meta: MintlifyPageMetaInput) => ({
41
- ...meta.sidebar,
42
- ...(meta.sidebarTitle !== undefined && meta.sidebar?.label === undefined
43
- ? { label: meta.sidebarTitle }
44
- : {}),
45
- ...(meta.icon !== undefined && meta.sidebar?.icon === undefined
46
- ? { icon: meta.icon }
47
- : {}),
48
- ...(meta.tag !== undefined && meta.sidebar?.badge === undefined
49
- ? { badge: meta.tag }
50
- : {}),
51
- ...(meta.hidden === true && meta.sidebar?.hidden === undefined
52
- ? { hidden: true }
53
- : {}),
54
- });
55
-
56
- const mintlifyPageType = (meta: MintlifyPageMetaInput): string | undefined =>
57
- meta.type ??
58
- (meta.openapi !== undefined ||
59
- meta.asyncapi !== undefined ||
60
- meta.api !== undefined
61
- ? "api"
62
- : undefined);
63
-
64
- const mintlifyNoindex = (meta: MintlifyPageMetaInput): boolean | undefined =>
65
- meta.hidden === true && meta.noindex === undefined ? true : meta.noindex;
66
-
67
- /** Top-level Mintlify keys folded elsewhere — removed from the output. */
68
- const CONSUMED_KEYS = new Set([
69
- "canonical",
70
- "hidden",
71
- "icon",
72
- "og:image",
73
- "sidebarTitle",
74
- "tag",
75
- ]);
76
-
77
- /** Map Mintlify page frontmatter onto Blume's frontmatter shape. */
78
- export const normalizeMintlifyPageMeta = (
79
- value: unknown
80
- ): Record<string, unknown> => {
81
- const parsed = mintlifyPageMetaInputSchema.safeParse(value);
82
- if (!parsed.success || typeof value !== "object" || value === null) {
83
- return (value ?? {}) as Record<string, unknown>;
84
- }
85
-
86
- const meta = parsed.data;
87
- const source = value as Record<string, unknown>;
88
- const data: Record<string, unknown> = { ...source };
89
-
90
- const sidebar = normalizedMintlifySidebar(meta);
91
- if (Object.keys(sidebar).length > 0) {
92
- data.sidebar = sidebar;
93
- }
94
-
95
- const seo: Record<string, unknown> = {
96
- ...(typeof source.seo === "object" && source.seo !== null
97
- ? (source.seo as Record<string, unknown>)
98
- : {}),
99
- };
100
- if (meta.canonical !== undefined && seo.canonical === undefined) {
101
- seo.canonical = meta.canonical;
102
- }
103
- if (meta["og:image"] !== undefined && seo.image === undefined) {
104
- seo.image = meta["og:image"];
105
- }
106
- if (Object.keys(seo).length > 0) {
107
- data.seo = seo;
108
- }
109
-
110
- const noindex = mintlifyNoindex(meta);
111
- if (noindex !== undefined) {
112
- data.noindex = noindex;
113
- }
114
- const type = mintlifyPageType(meta);
115
- if (type !== undefined) {
116
- data.type = type;
117
- }
118
-
119
- const cleaned: Record<string, unknown> = {};
120
- for (const [key, raw] of Object.entries(data)) {
121
- if (!CONSUMED_KEYS.has(key)) {
122
- cleaned[key] = raw;
123
- }
124
- }
125
- return cleaned;
126
- };
@@ -1,51 +0,0 @@
1
- import type { BlumeConfig } from "../../core/schema.ts";
2
-
3
- /** A Mintlify `navigation.languages[]` entry. */
4
- interface MintlifyLanguage {
5
- language: string;
6
- default?: boolean;
7
- }
8
-
9
- /**
10
- * A display label for a locale code — the language's native name when the
11
- * runtime knows it (`fr` -> `Français`), else the code itself.
12
- */
13
- const localeLabel = (code: string): string => {
14
- try {
15
- const native = new Intl.DisplayNames([code], { type: "language" }).of(code);
16
- if (native && native !== code) {
17
- return native.charAt(0).toUpperCase() + native.slice(1);
18
- }
19
- } catch {
20
- // Unknown code or no ICU data; fall through to the code.
21
- }
22
- return code;
23
- };
24
-
25
- /**
26
- * Map a Mintlify `navigation.languages[]` array to a Blume `i18n` config. The
27
- * entry marked `default: true` becomes `defaultLocale`; translated content
28
- * already lives in ISO-code directories, which match Blume's `dir` parser.
29
- */
30
- export const mintlifyI18n = (
31
- spec: Record<string, unknown>
32
- ): BlumeConfig["i18n"] | null => {
33
- const navigation = spec.navigation as
34
- | { languages?: MintlifyLanguage[] }
35
- | undefined;
36
- const languages = navigation?.languages;
37
- if (!Array.isArray(languages) || languages.length < 2) {
38
- return null;
39
- }
40
- const defaultLocale =
41
- languages.find((entry) => entry.default)?.language ??
42
- languages[0]?.language ??
43
- "en";
44
- return {
45
- defaultLocale,
46
- locales: languages.map((entry) => ({
47
- code: entry.language,
48
- label: localeLabel(entry.language),
49
- })),
50
- };
51
- };
@@ -1,128 +0,0 @@
1
- const JSX_ATTRIBUTE_ALIASES: Record<string, string> = {
2
- className: "class",
3
- clipPath: "clip-path",
4
- clipRule: "clip-rule",
5
- fillOpacity: "fill-opacity",
6
- fillRule: "fill-rule",
7
- strokeDasharray: "stroke-dasharray",
8
- strokeDashoffset: "stroke-dashoffset",
9
- strokeLinecap: "stroke-linecap",
10
- strokeLinejoin: "stroke-linejoin",
11
- strokeMiterlimit: "stroke-miterlimit",
12
- strokeOpacity: "stroke-opacity",
13
- strokeWidth: "stroke-width",
14
- };
15
-
16
- const SVG_EXPRESSION = /^\s*\(?\s*<svg[\s\S]*<\/svg>\s*\)?\s*;?\s*$/u;
17
-
18
- const escapeAttribute = (value: string): string =>
19
- value
20
- .replaceAll("&", "&amp;")
21
- .replaceAll('"', "&quot;")
22
- .replaceAll("<", "&lt;")
23
- .replaceAll(">", "&gt;");
24
-
25
- const stripExpressionWrapper = (value: string): string => {
26
- let next = value.trim().replaceAll(/;$/gu, "").trim();
27
- if (next.startsWith("(") && next.endsWith(")")) {
28
- next = next.slice(1, -1).trim();
29
- }
30
- return next;
31
- };
32
-
33
- const jsxExpressionAttribute = (
34
- value: string,
35
- quoted: string | undefined,
36
- single: string | undefined,
37
- template: string | undefined,
38
- scalar: string | undefined
39
- ): string => {
40
- const literal = quoted ?? single ?? template ?? scalar;
41
- return literal === undefined ? value : `="${escapeAttribute(literal)}"`;
42
- };
43
-
44
- const normalizeJsxSvg = (value: string): string | null => {
45
- const stripped = stripExpressionWrapper(value);
46
- if (!SVG_EXPRESSION.test(stripped)) {
47
- return null;
48
- }
49
-
50
- let svg = stripped.replaceAll(
51
- /[=]\{\s*(?:"(?<quoted>[^"]*)"|'(?<single>[^']*)'|`(?<template>[^`]*)`|(?<scalar>-?\d+(?:\.\d+)?|true|false))\s*\}/gu,
52
- jsxExpressionAttribute
53
- );
54
- for (const [jsxName, htmlName] of Object.entries(JSX_ATTRIBUTE_ALIASES)) {
55
- svg = svg.replaceAll(new RegExp(`\\b${jsxName}=`, "gu"), `${htmlName}=`);
56
- }
57
- return svg;
58
- };
59
-
60
- const findExpressionEnd = (source: string, start: number): number => {
61
- let depth = 1;
62
- let quote: '"' | "'" | "`" | null = null;
63
- for (let index = start; index < source.length; index += 1) {
64
- const char = source[index];
65
- const previous = source[index - 1];
66
- if (quote) {
67
- if (char === quote && previous !== "\\") {
68
- quote = null;
69
- }
70
- continue;
71
- }
72
- if (char === '"' || char === "'" || char === "`") {
73
- quote = char;
74
- continue;
75
- }
76
- if (char === "{") {
77
- depth += 1;
78
- continue;
79
- }
80
- if (char === "}") {
81
- depth -= 1;
82
- if (depth === 0) {
83
- return index;
84
- }
85
- }
86
- }
87
- return -1;
88
- };
89
-
90
- const startsWithSvgExpression = (value: string): boolean => {
91
- const trimmed = value.trimStart();
92
- return trimmed.startsWith("<svg") || trimmed.startsWith("(<svg");
93
- };
94
-
95
- /**
96
- * Rewrite Mintlify inline-SVG icon JSX props (`icon={<svg .../>}`) to plain
97
- * string props so the migrated MDX compiles under Astro. Runs once at
98
- * migration time; non-SVG `icon={...}` expressions are left untouched.
99
- */
100
- export const rewriteMintlifySvgIconProps = (source: string): string => {
101
- let output = "";
102
- let cursor = 0;
103
- while (cursor < source.length) {
104
- const start = source.indexOf("icon={", cursor);
105
- if (start === -1) {
106
- output += source.slice(cursor);
107
- break;
108
- }
109
-
110
- const expressionStart = start + "icon={".length;
111
- const end = findExpressionEnd(source, expressionStart);
112
- if (end === -1) {
113
- output += source.slice(cursor);
114
- break;
115
- }
116
-
117
- const expression = source.slice(expressionStart, end);
118
- const svg = startsWithSvgExpression(expression)
119
- ? normalizeJsxSvg(expression)
120
- : null;
121
- output += source.slice(cursor, start);
122
- output += svg
123
- ? `icon={${JSON.stringify(svg)}}`
124
- : source.slice(start, end + 1);
125
- cursor = end + 1;
126
- }
127
- return output;
128
- };