@dominikcz/greg 0.9.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +397 -0
- package/bin/greg.js +241 -0
- package/bin/init.js +351 -0
- package/bin/templates/docs/getting-started.md +47 -0
- package/bin/templates/docs/index.md +11 -0
- package/bin/templates/greg.config.js +39 -0
- package/bin/templates/greg.config.ts +38 -0
- package/bin/templates/index.html +16 -0
- package/bin/templates/src/App.svelte +5 -0
- package/bin/templates/src/app.css +20 -0
- package/bin/templates/src/main.js +9 -0
- package/bin/templates/svelte.config.js +1 -0
- package/bin/templates/tsconfig.json +21 -0
- package/bin/templates/vite.config.js +23 -0
- package/docs/__partials/markdown/examples/basic.md +4 -0
- package/docs/__partials/markdown/examples/diff.md +10 -0
- package/docs/__partials/markdown/examples/focus.md +5 -0
- package/docs/__partials/markdown/examples/language-title.md +3 -0
- package/docs/__partials/markdown/examples/line-highlighting.md +5 -0
- package/docs/__partials/markdown/examples/line-numbers.md +5 -0
- package/docs/__partials/note.md +4 -0
- package/docs/guide/__shared-warning.md +4 -0
- package/docs/guide/asset-handling.md +88 -0
- package/docs/guide/deploying.md +162 -0
- package/docs/guide/getting-started.md +334 -0
- package/docs/guide/index.md +23 -0
- package/docs/guide/localization.md +290 -0
- package/docs/guide/markdown/code.md +95 -0
- package/docs/guide/markdown/components-and-mermaid.md +43 -0
- package/docs/guide/markdown/containers.md +110 -0
- package/docs/guide/markdown/header-anchors.md +34 -0
- package/docs/guide/markdown/includes.md +84 -0
- package/docs/guide/markdown/index.md +20 -0
- package/docs/guide/markdown/inline-attributes.md +21 -0
- package/docs/guide/markdown/links-and-toc.md +64 -0
- package/docs/guide/markdown/math.md +54 -0
- package/docs/guide/markdown/syntax-highlighting.md +75 -0
- package/docs/guide/routing.md +150 -0
- package/docs/guide/using-svelte.md +88 -0
- package/docs/guide/versioning.md +281 -0
- package/docs/incompatibilities.md +48 -0
- package/docs/index.md +43 -0
- package/docs/reference/badge.md +100 -0
- package/docs/reference/carbon-ads.md +46 -0
- package/docs/reference/code-group.md +126 -0
- package/docs/reference/home-page.md +232 -0
- package/docs/reference/index.md +18 -0
- package/docs/reference/markdowndocs.md +275 -0
- package/docs/reference/outline.md +79 -0
- package/docs/reference/search.md +263 -0
- package/docs/reference/steps.md +200 -0
- package/docs/reference/team-page.md +189 -0
- package/docs/reference/theme.md +150 -0
- package/fakeDocsGenerator/generate_docs.js +310 -0
- package/package.json +92 -0
- package/scripts/build-versions.js +609 -0
- package/scripts/generate-static.js +79 -0
- package/scripts/render-markdown.js +420 -0
- package/src/lib/MarkdownDocs/AiChat.svelte +936 -0
- package/src/lib/MarkdownDocs/BackToTop.svelte +68 -0
- package/src/lib/MarkdownDocs/Breadcrumb.svelte +68 -0
- package/src/lib/MarkdownDocs/DocsNavigation.svelte +149 -0
- package/src/lib/MarkdownDocs/DocsSiteHeader.svelte +758 -0
- package/src/lib/MarkdownDocs/DocsVersionSwitcher.svelte +103 -0
- package/src/lib/MarkdownDocs/MarkdownDocs.svelte +2115 -0
- package/src/lib/MarkdownDocs/MarkdownRenderer.svelte +487 -0
- package/src/lib/MarkdownDocs/Outline.svelte +238 -0
- package/src/lib/MarkdownDocs/PrevNext.svelte +115 -0
- package/src/lib/MarkdownDocs/SearchModal.svelte +1241 -0
- package/src/lib/MarkdownDocs/TreeView.svelte +32 -0
- package/src/lib/MarkdownDocs/TreeViewItem.svelte +219 -0
- package/src/lib/MarkdownDocs/VersionOutdatedNotice.svelte +72 -0
- package/src/lib/MarkdownDocs/__tests__/codeDirectives.test.js +54 -0
- package/src/lib/MarkdownDocs/__tests__/common.test.js +41 -0
- package/src/lib/MarkdownDocs/__tests__/docsExamplesLint.test.js +77 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/docs/markdown/__partial-basic.md +3 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/docs/markdown/snippet.js +9 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/includes/part.md +11 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/includes/wrapper.md +5 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/snippets/sample.js +8 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/snippets/sample.md +5 -0
- package/src/lib/MarkdownDocs/__tests__/helpers.js +67 -0
- package/src/lib/MarkdownDocs/__tests__/localeUtils.test.js +204 -0
- package/src/lib/MarkdownDocs/__tests__/markdown.test.js +704 -0
- package/src/lib/MarkdownDocs/__tests__/markdownRendererRuntime.test.js +65 -0
- package/src/lib/MarkdownDocs/__tests__/searchIndexBuilder.test.js +117 -0
- package/src/lib/MarkdownDocs/__tests__/sqliteStore.test.js +202 -0
- package/src/lib/MarkdownDocs/__tests__/useRouter.test.js +16 -0
- package/src/lib/MarkdownDocs/ai/adapters/customAdapter.js +14 -0
- package/src/lib/MarkdownDocs/ai/adapters/customAdapter.ts +43 -0
- package/src/lib/MarkdownDocs/ai/adapters/ollamaAdapter.js +81 -0
- package/src/lib/MarkdownDocs/ai/adapters/ollamaAdapter.ts +116 -0
- package/src/lib/MarkdownDocs/ai/adapters/openaiAdapter.js +92 -0
- package/src/lib/MarkdownDocs/ai/adapters/openaiAdapter.ts +137 -0
- package/src/lib/MarkdownDocs/ai/aiProvider.ts +31 -0
- package/src/lib/MarkdownDocs/ai/characters.js +52 -0
- package/src/lib/MarkdownDocs/ai/characters.ts +69 -0
- package/src/lib/MarkdownDocs/ai/chunkStore.ts +25 -0
- package/src/lib/MarkdownDocs/ai/chunker.js +85 -0
- package/src/lib/MarkdownDocs/ai/chunker.ts +135 -0
- package/src/lib/MarkdownDocs/ai/docLinker.js +26 -0
- package/src/lib/MarkdownDocs/ai/docLinker.ts +36 -0
- package/src/lib/MarkdownDocs/ai/promptBuilder.js +33 -0
- package/src/lib/MarkdownDocs/ai/promptBuilder.ts +53 -0
- package/src/lib/MarkdownDocs/ai/ragPipeline.js +54 -0
- package/src/lib/MarkdownDocs/ai/ragPipeline.ts +106 -0
- package/src/lib/MarkdownDocs/ai/stores/memoryStore.js +88 -0
- package/src/lib/MarkdownDocs/ai/stores/memoryStore.ts +112 -0
- package/src/lib/MarkdownDocs/ai/stores/sqliteStore.ts +372 -0
- package/src/lib/MarkdownDocs/ai/types.ts +71 -0
- package/src/lib/MarkdownDocs/aiServer.js +288 -0
- package/src/lib/MarkdownDocs/codeDirectives.js +191 -0
- package/src/lib/MarkdownDocs/codeFenceInfo.js +45 -0
- package/src/lib/MarkdownDocs/codeGroup.ts +46 -0
- package/src/lib/MarkdownDocs/common.ts +47 -0
- package/src/lib/MarkdownDocs/docsUtils.js +281 -0
- package/src/lib/MarkdownDocs/index.plugins.js +22 -0
- package/src/lib/MarkdownDocs/layouts/LayoutDoc.svelte +8 -0
- package/src/lib/MarkdownDocs/layouts/LayoutHome.svelte +58 -0
- package/src/lib/MarkdownDocs/layouts/LayoutPage.svelte +9 -0
- package/src/lib/MarkdownDocs/loadGregConfig.js +82 -0
- package/src/lib/MarkdownDocs/localeUtils.ts +682 -0
- package/src/lib/MarkdownDocs/markdownRendererRuntime.ts +314 -0
- package/src/lib/MarkdownDocs/mermaidThemes.js +319 -0
- package/src/lib/MarkdownDocs/navigationUtils.js +22 -0
- package/src/lib/MarkdownDocs/rehypeCodeGroup.js +326 -0
- package/src/lib/MarkdownDocs/rehypeCodeTitle.js +96 -0
- package/src/lib/MarkdownDocs/rehypeToc.js +170 -0
- package/src/lib/MarkdownDocs/remarkCodeMeta.js +22 -0
- package/src/lib/MarkdownDocs/remarkContainers.js +329 -0
- package/src/lib/MarkdownDocs/remarkCustomAnchors.js +42 -0
- package/src/lib/MarkdownDocs/remarkEscapeSvelte.js +33 -0
- package/src/lib/MarkdownDocs/remarkGlobalComponents.js +65 -0
- package/src/lib/MarkdownDocs/remarkImports.js +461 -0
- package/src/lib/MarkdownDocs/remarkImportsBrowser.js +349 -0
- package/src/lib/MarkdownDocs/remarkInlineAttrs.js +95 -0
- package/src/lib/MarkdownDocs/remarkMathToHtml.js +138 -0
- package/src/lib/MarkdownDocs/searchIndexBuilder.js +497 -0
- package/src/lib/MarkdownDocs/searchServer.js +263 -0
- package/src/lib/MarkdownDocs/treeViewTypes.ts +11 -0
- package/src/lib/MarkdownDocs/useRouter.svelte.ts +114 -0
- package/src/lib/MarkdownDocs/useSplitter.svelte.ts +33 -0
- package/src/lib/MarkdownDocs/versioningDefaults.js +20 -0
- package/src/lib/MarkdownDocs/vitePluginAiServer.js +204 -0
- package/src/lib/MarkdownDocs/vitePluginCopyDocs.js +153 -0
- package/src/lib/MarkdownDocs/vitePluginFrontmatter.js +109 -0
- package/src/lib/MarkdownDocs/vitePluginGregConfig.js +108 -0
- package/src/lib/MarkdownDocs/vitePluginSearchIndex.js +57 -0
- package/src/lib/MarkdownDocs/vitePluginSearchServer.js +190 -0
- package/src/lib/components/Badge.svelte +59 -0
- package/src/lib/components/Button.svelte +138 -0
- package/src/lib/components/CarbonAds.svelte +99 -0
- package/src/lib/components/CodeGroup.svelte +102 -0
- package/src/lib/components/Feature.svelte +209 -0
- package/src/lib/components/Features.svelte +123 -0
- package/src/lib/components/Hero.svelte +399 -0
- package/src/lib/components/Image.svelte +128 -0
- package/src/lib/components/Link.svelte +105 -0
- package/src/lib/components/SocialLink.svelte +84 -0
- package/src/lib/components/SocialLinks.svelte +33 -0
- package/src/lib/components/Steps.svelte +143 -0
- package/src/lib/components/TeamMember.svelte +273 -0
- package/src/lib/components/TeamMembers.svelte +81 -0
- package/src/lib/components/TeamPage.svelte +65 -0
- package/src/lib/components/TeamPageSection.svelte +108 -0
- package/src/lib/components/TeamPageTitle.svelte +89 -0
- package/src/lib/components/index.js +24 -0
- package/src/lib/portal/context.js +12 -0
- package/src/lib/portal/index.js +3 -0
- package/src/lib/portal/portal.svelte +14 -0
- package/src/lib/portal/slot.svelte +8 -0
- package/src/lib/scss/__code.scss +128 -0
- package/src/lib/scss/__containers.scss +99 -0
- package/src/lib/scss/__markdown.scss +447 -0
- package/src/lib/scss/__scrollbar.scss +60 -0
- package/src/lib/scss/__steps.scss +100 -0
- package/src/lib/scss/__theme.scss +238 -0
- package/src/lib/scss/__toc.scss +55 -0
- package/src/lib/scss/__utilities.scss +7 -0
- package/src/lib/scss/greg.scss +9 -0
- package/src/lib/spinner/spinner.svelte +42 -0
- package/svelte.config.js +146 -0
- package/types/index.d.ts +456 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remark + Rehype plugins for VitePress-style containers and GitHub-flavored alerts.
|
|
3
|
+
*
|
|
4
|
+
* Architecture: two-phase, no inner sub-pipeline needed.
|
|
5
|
+
*
|
|
6
|
+
* 1. remarkContainers (remark plugin)
|
|
7
|
+
* - Pass 0: converts GitHub-flavored alert blockquotes (`> [!NOTE]`) into
|
|
8
|
+
* containerBlock nodes.
|
|
9
|
+
* - Pass 1 & 2: converts `:::` fenced containers into containerBlock nodes.
|
|
10
|
+
* Inner content stays as real mdast so the normal pipeline processes it
|
|
11
|
+
* (rehype-shiki, rehype-slug, etc.)
|
|
12
|
+
*
|
|
13
|
+
* 2. rehypeContainers (rehype plugin)
|
|
14
|
+
* Visits `container-block` hast elements and emits the final
|
|
15
|
+
* <div class="custom-block …">…</div> or <details>…</details> HTML.
|
|
16
|
+
*
|
|
17
|
+
* ::: container shapes produced by remark:
|
|
18
|
+
*
|
|
19
|
+
* A) Single paragraph (no blank lines — remark collapses to one node):
|
|
20
|
+
* paragraph { text: "::: info\nContent\n:::" }
|
|
21
|
+
*
|
|
22
|
+
* B) Multi-node span (blank lines or block elements inside):
|
|
23
|
+
* paragraph { text: "::: info Title" }
|
|
24
|
+
* ... block nodes ...
|
|
25
|
+
* paragraph { text: ":::" }
|
|
26
|
+
*
|
|
27
|
+
* Does NOT require remark-directive or any sub-pipeline.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { visit } from 'unist-util-visit';
|
|
31
|
+
|
|
32
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
const defaultLabels = {
|
|
35
|
+
infoLabel: 'INFO',
|
|
36
|
+
tipLabel: 'TIP',
|
|
37
|
+
warningLabel: 'WARNING',
|
|
38
|
+
dangerLabel: 'DANGER',
|
|
39
|
+
detailsLabel: 'Details',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const knownTypes = ['info', 'tip', 'warning', 'danger', 'details'];
|
|
43
|
+
|
|
44
|
+
const OPEN_LINE_RE = /^:::[ \t]+(\w+)(?:[ \t]+(.+?))?[ \t]*$/;
|
|
45
|
+
const CLOSE_LINE_RE = /^:::[ \t]*$/;
|
|
46
|
+
|
|
47
|
+
/** Maps GitHub alert type names to VitePress container types. */
|
|
48
|
+
const githubAlertTypeMap = {
|
|
49
|
+
note: 'info',
|
|
50
|
+
tip: 'tip',
|
|
51
|
+
important: 'warning',
|
|
52
|
+
warning: 'warning',
|
|
53
|
+
caution: 'danger',
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
function getDefaultLabel(type, labels) {
|
|
59
|
+
return (
|
|
60
|
+
{
|
|
61
|
+
info: labels.infoLabel,
|
|
62
|
+
tip: labels.tipLabel,
|
|
63
|
+
warning: labels.warningLabel,
|
|
64
|
+
danger: labels.dangerLabel,
|
|
65
|
+
details: labels.detailsLabel,
|
|
66
|
+
}[type] ?? type.toUpperCase()
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Creates a containerBlock mdast node carrying type/title + inner mdast children. */
|
|
71
|
+
function containerBlockNode(type, rawTitle, children) {
|
|
72
|
+
return {
|
|
73
|
+
type: 'containerBlock',
|
|
74
|
+
rawTitle,
|
|
75
|
+
data: {
|
|
76
|
+
hName: 'div',
|
|
77
|
+
hProperties: {
|
|
78
|
+
className: ['container-block'],
|
|
79
|
+
'data-type': type,
|
|
80
|
+
'data-title': rawTitle,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
children,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Recursively extracts plain text from an mdast node. */
|
|
88
|
+
function nodeToText(node) {
|
|
89
|
+
if (node.type === 'text' || node.type === 'inlineCode') return node.value ?? '';
|
|
90
|
+
if (node.children) return node.children.map(nodeToText).join('');
|
|
91
|
+
return '';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Tries to parse a GitHub-flavored alert blockquote node.
|
|
96
|
+
*
|
|
97
|
+
* remark parses `[!NOTE]` as a `linkReference` (unresolved link), not plain
|
|
98
|
+
* text, so we handle both:
|
|
99
|
+
* - `text` node with value `[!NOTE]…`
|
|
100
|
+
* - `linkReference` node whose text content is `!NOTE`
|
|
101
|
+
*
|
|
102
|
+
* Returns `{ mappedType, title, innerNodes }` or `null`.
|
|
103
|
+
*/
|
|
104
|
+
function parseGithubAlert(node) {
|
|
105
|
+
if (node?.type !== 'blockquote' || !node.children?.length) return null;
|
|
106
|
+
|
|
107
|
+
const firstPara = node.children[0];
|
|
108
|
+
if (firstPara?.type !== 'paragraph') return null;
|
|
109
|
+
|
|
110
|
+
const paraChildren = firstPara.children ?? [];
|
|
111
|
+
const first = paraChildren[0];
|
|
112
|
+
if (!first) return null;
|
|
113
|
+
|
|
114
|
+
let alertKey, remainderChildren;
|
|
115
|
+
|
|
116
|
+
if (first.type === 'text') {
|
|
117
|
+
// "[!NOTE] some text" collapsed into a text node
|
|
118
|
+
const m = first.value.match(/^\[!([a-zA-Z]+)\]\s*/);
|
|
119
|
+
if (!m) return null;
|
|
120
|
+
alertKey = m[1].toLowerCase();
|
|
121
|
+
const rest = first.value.slice(m[0].length);
|
|
122
|
+
remainderChildren = rest
|
|
123
|
+
? [{ ...first, value: rest }, ...paraChildren.slice(1)]
|
|
124
|
+
: paraChildren.slice(1);
|
|
125
|
+
} else if (first.type === 'linkReference') {
|
|
126
|
+
// remark's normal parse: `[!NOTE]` → linkReference{ label: "!NOTE" }
|
|
127
|
+
const m = nodeToText(first).match(/^!([a-zA-Z]+)$/);
|
|
128
|
+
if (!m) return null;
|
|
129
|
+
alertKey = m[1].toLowerCase();
|
|
130
|
+
// Trim leading soft-break / newline between `[!TYPE]` and content
|
|
131
|
+
let rest = paraChildren.slice(1);
|
|
132
|
+
if (rest[0]?.type === 'break' || rest[0]?.type === 'softbreak') {
|
|
133
|
+
rest = rest.slice(1);
|
|
134
|
+
} else if (rest[0]?.type === 'text' && rest[0].value.startsWith('\n')) {
|
|
135
|
+
const trimmed = rest[0].value.slice(1);
|
|
136
|
+
rest = trimmed ? [{ ...rest[0], value: trimmed }, ...rest.slice(1)] : rest.slice(1);
|
|
137
|
+
}
|
|
138
|
+
remainderChildren = rest;
|
|
139
|
+
} else {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const mappedType = githubAlertTypeMap[alertKey];
|
|
144
|
+
if (!mappedType) return null;
|
|
145
|
+
|
|
146
|
+
const innerNodes = [
|
|
147
|
+
...(remainderChildren.length ? [{ type: 'paragraph', children: remainderChildren }] : []),
|
|
148
|
+
...node.children.slice(1),
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
return { mappedType, title: alertKey.toUpperCase(), innerNodes };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ─── Remark plugin ────────────────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
export function remarkContainers(userOptions = {}) {
|
|
157
|
+
return (tree) => {
|
|
158
|
+
const children = tree.children;
|
|
159
|
+
|
|
160
|
+
// Pass 0 — GitHub-flavored alerts: `> [!NOTE]`
|
|
161
|
+
for (let i = 0; i < children.length; i++) {
|
|
162
|
+
const parsed = parseGithubAlert(children[i]);
|
|
163
|
+
if (!parsed) continue;
|
|
164
|
+
children.splice(i, 1, containerBlockNode(parsed.mappedType, parsed.title, parsed.innerNodes));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Apply ::: container syntax recursively (root + list items + blockquotes)
|
|
168
|
+
applyContainerSyntaxRecursive(tree);
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Walk the mdast bottom-up and apply Pass 1 + Pass 2 container syntax to
|
|
174
|
+
* every block-level children array — including those nested inside list items
|
|
175
|
+
* and blockquotes.
|
|
176
|
+
*/
|
|
177
|
+
function applyContainerSyntaxRecursive(node) {
|
|
178
|
+
if (!node.children) return;
|
|
179
|
+
|
|
180
|
+
// Recurse into nested block containers first (bottom-up).
|
|
181
|
+
// Important: list items live under a `list` node, so recurse through any
|
|
182
|
+
// node that has children instead of whitelisting only a few node types.
|
|
183
|
+
for (const child of node.children) {
|
|
184
|
+
if (Array.isArray(child.children)) {
|
|
185
|
+
applyContainerSyntaxRecursive(child);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Then process this level's children array
|
|
190
|
+
applyContainerPasses(node.children);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Apply Pass 1 (single-paragraph :::) and Pass 2 (multi-node :::) to a
|
|
195
|
+
* specific `children` array in-place. Extracted from the original inline
|
|
196
|
+
* implementation so it can be reused recursively.
|
|
197
|
+
*/
|
|
198
|
+
function applyContainerPasses(children) {
|
|
199
|
+
// Pass 1 — single-paragraph ::: containers (no blank lines inside)
|
|
200
|
+
for (let i = 0; i < children.length; i++) {
|
|
201
|
+
const node = children[i];
|
|
202
|
+
if (node.type !== 'paragraph') continue;
|
|
203
|
+
|
|
204
|
+
const firstChild = node.children?.[0];
|
|
205
|
+
if (firstChild?.type !== 'text') continue;
|
|
206
|
+
|
|
207
|
+
const firstLine = firstChild.value.split('\n')[0].trim();
|
|
208
|
+
const openMatch = firstLine.match(OPEN_LINE_RE);
|
|
209
|
+
if (!openMatch) continue;
|
|
210
|
+
|
|
211
|
+
const lastChild = node.children[node.children.length - 1];
|
|
212
|
+
if (lastChild?.type !== 'text') continue;
|
|
213
|
+
|
|
214
|
+
const lastLine = lastChild.value.split('\n').at(-1).trim();
|
|
215
|
+
if (!CLOSE_LINE_RE.test(lastLine)) continue;
|
|
216
|
+
|
|
217
|
+
const type = openMatch[1].toLowerCase();
|
|
218
|
+
if (!knownTypes.includes(type)) continue;
|
|
219
|
+
|
|
220
|
+
// Strip the opening and closing ::: lines; collect the inline nodes in between.
|
|
221
|
+
const innerChildren = [];
|
|
222
|
+
for (let ci = 0; ci < node.children.length; ci++) {
|
|
223
|
+
const c = node.children[ci];
|
|
224
|
+
const isFirst = ci === 0;
|
|
225
|
+
const isLast = ci === node.children.length - 1;
|
|
226
|
+
|
|
227
|
+
if (isFirst && isLast) {
|
|
228
|
+
const afterOpen = c.value.slice(c.value.indexOf('\n') + 1);
|
|
229
|
+
const content = afterOpen.slice(0, afterOpen.lastIndexOf('\n'));
|
|
230
|
+
if (content.trim()) innerChildren.push({ ...c, value: content });
|
|
231
|
+
} else if (isFirst) {
|
|
232
|
+
const afterOpen = c.value.slice(c.value.indexOf('\n') + 1);
|
|
233
|
+
if (afterOpen) innerChildren.push({ ...c, value: afterOpen });
|
|
234
|
+
} else if (isLast) {
|
|
235
|
+
const beforeClose = c.value.slice(0, c.value.lastIndexOf('\n'));
|
|
236
|
+
if (beforeClose) innerChildren.push({ ...c, value: beforeClose });
|
|
237
|
+
} else {
|
|
238
|
+
innerChildren.push(c);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const innerNodes = innerChildren.length
|
|
243
|
+
? [{ type: 'paragraph', children: innerChildren }]
|
|
244
|
+
: [];
|
|
245
|
+
|
|
246
|
+
children.splice(i, 1, containerBlockNode(type, openMatch[2] ?? '', innerNodes));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Pass 2 — multi-node ::: containers (blank lines or block elements inside)
|
|
250
|
+
const spans = [];
|
|
251
|
+
const stack = [];
|
|
252
|
+
for (let i = 0; i < children.length; i++) {
|
|
253
|
+
const node = children[i];
|
|
254
|
+
if (node.type !== 'paragraph') continue;
|
|
255
|
+
const text = nodeToText(node).trim();
|
|
256
|
+
if (text.includes('\n')) continue; // already handled in Pass 1
|
|
257
|
+
|
|
258
|
+
const openMatch = text.match(OPEN_LINE_RE);
|
|
259
|
+
if (openMatch) {
|
|
260
|
+
stack.push({ openIdx: i, type: openMatch[1], title: openMatch[2] ?? '' });
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (CLOSE_LINE_RE.test(text) && stack.length > 0) {
|
|
264
|
+
const frame = stack.pop();
|
|
265
|
+
if (stack.length === 0) spans.push({ ...frame, closeIdx: i });
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
for (const { openIdx, closeIdx, type, title: rawTitle } of spans.reverse()) {
|
|
269
|
+
const lcType = type.toLowerCase();
|
|
270
|
+
if (!knownTypes.includes(lcType)) continue;
|
|
271
|
+
const innerNodes = children.slice(openIdx + 1, closeIdx);
|
|
272
|
+
children.splice(openIdx, closeIdx - openIdx + 1, containerBlockNode(lcType, rawTitle, innerNodes));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ─── Rehype plugin ────────────────────────────────────────────────────────────
|
|
277
|
+
// Runs after rehype-shiki, rehype-slug, etc. have processed the inner content.
|
|
278
|
+
// Replaces <div class="container-block"> with the final custom-block markup.
|
|
279
|
+
|
|
280
|
+
export function rehypeContainers(userOptions = {}) {
|
|
281
|
+
const labels = { ...defaultLabels, ...userOptions };
|
|
282
|
+
|
|
283
|
+
return (tree) => {
|
|
284
|
+
visit(tree, 'element', (node, index, parent) => {
|
|
285
|
+
if (node.tagName !== 'div' || !node.properties?.className?.includes('container-block')) return;
|
|
286
|
+
|
|
287
|
+
const type = node.properties['data-type'];
|
|
288
|
+
const rawTitle = node.properties['data-title'] ?? '';
|
|
289
|
+
|
|
290
|
+
const isOpen = /\{open\}/i.test(rawTitle);
|
|
291
|
+
const title = rawTitle.replace(/\s*\{open\}\s*$/i, '').trim() || getDefaultLabel(type, labels);
|
|
292
|
+
|
|
293
|
+
if (type === 'details') {
|
|
294
|
+
parent.children.splice(index, 1, {
|
|
295
|
+
type: 'element',
|
|
296
|
+
tagName: 'details',
|
|
297
|
+
properties: {
|
|
298
|
+
className: ['custom-block', 'details'],
|
|
299
|
+
...(isOpen ? { open: true } : {}),
|
|
300
|
+
},
|
|
301
|
+
children: [
|
|
302
|
+
{
|
|
303
|
+
type: 'element',
|
|
304
|
+
tagName: 'summary',
|
|
305
|
+
properties: { className: ['custom-block-title'] },
|
|
306
|
+
children: [{ type: 'text', value: title }],
|
|
307
|
+
},
|
|
308
|
+
...node.children,
|
|
309
|
+
],
|
|
310
|
+
});
|
|
311
|
+
} else {
|
|
312
|
+
parent.children.splice(index, 1, {
|
|
313
|
+
type: 'element',
|
|
314
|
+
tagName: 'div',
|
|
315
|
+
properties: { className: ['custom-block', type] },
|
|
316
|
+
children: [
|
|
317
|
+
{
|
|
318
|
+
type: 'element',
|
|
319
|
+
tagName: 'p',
|
|
320
|
+
properties: { className: ['custom-block-title'] },
|
|
321
|
+
children: [{ type: 'text', value: title }],
|
|
322
|
+
},
|
|
323
|
+
...node.children,
|
|
324
|
+
],
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
};
|
|
329
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* remarkCustomAnchors
|
|
3
|
+
*
|
|
4
|
+
* Supports VitePress-style custom heading IDs:
|
|
5
|
+
* ## My Heading {#custom-id}
|
|
6
|
+
*
|
|
7
|
+
* Strips the `{#id}` suffix from the heading text and sets
|
|
8
|
+
* `node.data.hProperties.id` so rehype-slug leaves it alone.
|
|
9
|
+
*/
|
|
10
|
+
import { visit } from 'unist-util-visit';
|
|
11
|
+
|
|
12
|
+
const CUSTOM_ID_RE = /\s*\{#([\w-]+)\}\s*$/;
|
|
13
|
+
|
|
14
|
+
export function remarkCustomAnchors() {
|
|
15
|
+
return (tree) => {
|
|
16
|
+
visit(tree, 'heading', (node) => {
|
|
17
|
+
if (!node.children?.length) return;
|
|
18
|
+
|
|
19
|
+
// The custom id is always at the end — check the last child text node
|
|
20
|
+
const last = node.children[node.children.length - 1];
|
|
21
|
+
if (last?.type !== 'text') return;
|
|
22
|
+
|
|
23
|
+
const match = last.value.match(CUSTOM_ID_RE);
|
|
24
|
+
if (!match) return;
|
|
25
|
+
|
|
26
|
+
const id = match[1];
|
|
27
|
+
|
|
28
|
+
// Strip the {#id} suffix from the text
|
|
29
|
+
last.value = last.value.slice(0, last.value.length - match[0].length);
|
|
30
|
+
|
|
31
|
+
// Remove the node entirely if it became empty
|
|
32
|
+
if (last.value === '') {
|
|
33
|
+
node.children.pop();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Set the id on the hast element — rehype-slug skips elements with existing id
|
|
37
|
+
node.data ??= {};
|
|
38
|
+
node.data.hProperties ??= {};
|
|
39
|
+
node.data.hProperties.id = id;
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* remarkEscapeSvelte
|
|
3
|
+
*
|
|
4
|
+
* Escapes `{` and `}` in plain Markdown *text* nodes so Svelte's compiler
|
|
5
|
+
* doesn't treat them as template expressions.
|
|
6
|
+
*
|
|
7
|
+
* Only TEXT nodes are touched. Raw HTML nodes (including Svelte component
|
|
8
|
+
* elements like `<Hero items={data} />`) are intentionally left unmodified so
|
|
9
|
+
* that Svelte prop-binding expressions `{…}` remain valid after compilation.
|
|
10
|
+
*
|
|
11
|
+
* Code nodes (inline code and fenced code blocks) are untouched — mdsvex
|
|
12
|
+
* wraps those in `{@html …}` itself.
|
|
13
|
+
*/
|
|
14
|
+
import { visit } from 'unist-util-visit';
|
|
15
|
+
|
|
16
|
+
/** Characters that need escaping in raw Svelte template text */
|
|
17
|
+
const ESCAPE_RE = /[{}]/g;
|
|
18
|
+
const ESCAPE_MAP = { '{': '{', '}': '}' };
|
|
19
|
+
|
|
20
|
+
function escapeText(str) {
|
|
21
|
+
return str.replace(ESCAPE_RE, (ch) => ESCAPE_MAP[ch]);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function remarkEscapeSvelte() {
|
|
25
|
+
return (tree) => {
|
|
26
|
+
// Only escape { } inside plain Markdown text nodes.
|
|
27
|
+
// HTML nodes are passed through unchanged so that Svelte component
|
|
28
|
+
// attributes like actions={[…]} compile correctly.
|
|
29
|
+
visit(tree, 'text', (node) => {
|
|
30
|
+
node.value = escapeText(node.value);
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* remarkGlobalComponents
|
|
3
|
+
*
|
|
4
|
+
* Injects a <script> block into every processed Markdown file that
|
|
5
|
+
* auto-imports all Greg UI components (Badge, Button, Hero, …).
|
|
6
|
+
*
|
|
7
|
+
* If the file already has a <script> block at or near the top, the
|
|
8
|
+
* import statement is merged into it instead of adding a second block.
|
|
9
|
+
*
|
|
10
|
+
* The import path uses the `$components` Vite alias which must be
|
|
11
|
+
* configured in vite.config.js → resolve.alias.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const COMPONENTS_IMPORT =
|
|
15
|
+
`import { Badge, Button, Image, Link, Feature, Features, Hero, ` +
|
|
16
|
+
`SocialLink, SocialLinks, TeamMember, TeamMembers, ` +
|
|
17
|
+
`TeamPage, TeamPageTitle, TeamPageSection, Steps } from '$components/index.js';`;
|
|
18
|
+
|
|
19
|
+
const SCRIPT_OPEN_RE = /^<script(\b[^>]*)>/i;
|
|
20
|
+
const SCRIPT_SETUP_ATTR_RE = /\bsetup\b/i;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Return the index of the first `html` node whose value begins with
|
|
24
|
+
* a <script> tag, searching all top-level nodes.
|
|
25
|
+
*/
|
|
26
|
+
function findScriptNodeIndex(children) {
|
|
27
|
+
for (let i = 0; i < children.length; i++) {
|
|
28
|
+
const node = children[i];
|
|
29
|
+
if (node.type === 'html' && SCRIPT_OPEN_RE.test(node.value.trimStart())) {
|
|
30
|
+
return i;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return -1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The remark plugin function.
|
|
38
|
+
*/
|
|
39
|
+
export function remarkGlobalComponents() {
|
|
40
|
+
return (tree) => {
|
|
41
|
+
const { children } = tree;
|
|
42
|
+
const idx = findScriptNodeIndex(children);
|
|
43
|
+
|
|
44
|
+
if (idx !== -1) {
|
|
45
|
+
// Merge: inject the import after the opening <script …> tag.
|
|
46
|
+
// If the existing script has a `setup` attribute (Vue syntax), strip it
|
|
47
|
+
// so Svelte sees a standard <script> block.
|
|
48
|
+
const node = children[idx];
|
|
49
|
+
node.value = node.value.replace(
|
|
50
|
+
SCRIPT_OPEN_RE,
|
|
51
|
+
(match, attrs) => {
|
|
52
|
+
const cleanAttrs = attrs.replace(SCRIPT_SETUP_ATTR_RE, '').trim();
|
|
53
|
+
const openTag = cleanAttrs ? `<script ${cleanAttrs}>` : '<script>';
|
|
54
|
+
return `${openTag}\n ${COMPONENTS_IMPORT}`;
|
|
55
|
+
},
|
|
56
|
+
);
|
|
57
|
+
} else {
|
|
58
|
+
// Prepend a fresh <script> block
|
|
59
|
+
children.unshift({
|
|
60
|
+
type: 'html',
|
|
61
|
+
value: `<script>\n ${COMPONENTS_IMPORT}\n</script>`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|