@glw907/cairn-cms 0.60.1 → 0.62.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.
- package/CHANGELOG.md +78 -0
- package/dist/components/AdminLayout.svelte +22 -0
- package/dist/components/CairnAdmin.svelte +3 -0
- package/dist/components/CairnTidySettings.svelte +2 -2
- package/dist/components/CairnTidySettings.svelte.d.ts +1 -1
- package/dist/components/EditPage.svelte +116 -39
- package/dist/components/HelpHome.svelte +824 -0
- package/dist/components/HelpHome.svelte.d.ts +22 -0
- package/dist/components/MarkdownHelpDialog.svelte +4 -15
- package/dist/components/client-ingest.d.ts +16 -8
- package/dist/components/client-ingest.js +12 -6
- package/dist/components/editor-media.js +16 -8
- package/dist/components/editor-placeholder.d.ts +4 -2
- package/dist/components/editor-tidy.d.ts +24 -12
- package/dist/components/editor-tidy.js +8 -4
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/link-completion.d.ts +12 -6
- package/dist/components/link-completion.js +12 -6
- package/dist/components/markdown-directives.d.ts +9 -6
- package/dist/components/markdown-directives.js +9 -6
- package/dist/components/markdown-format.d.ts +7 -2
- package/dist/components/markdown-format.js +59 -28
- package/dist/components/markdown-reference.d.ts +8 -0
- package/dist/components/markdown-reference.js +22 -0
- package/dist/components/media-upload-outcome.d.ts +12 -6
- package/dist/components/objective-errors.d.ts +8 -4
- package/dist/components/objective-errors.js +8 -4
- package/dist/components/preview-doc.d.ts +4 -2
- package/dist/components/preview-doc.js +4 -2
- package/dist/components/spellcheck.d.ts +55 -29
- package/dist/components/spellcheck.js +39 -21
- package/dist/components/tidy-categorize.d.ts +20 -10
- package/dist/components/tidy-categorize.js +16 -8
- package/dist/components/tidy-validate.d.ts +12 -6
- package/dist/components/tidy-validate.js +20 -10
- package/dist/components/topbar-context.d.ts +4 -2
- package/dist/content/advisories.d.ts +56 -0
- package/dist/content/advisories.js +87 -0
- package/dist/content/compose.d.ts +4 -2
- package/dist/content/compose.js +1 -0
- package/dist/content/excerpt.js +4 -2
- package/dist/content/getting-started.d.ts +18 -0
- package/dist/content/getting-started.js +12 -0
- package/dist/content/links.d.ts +16 -8
- package/dist/content/links.js +12 -6
- package/dist/content/manifest.d.ts +36 -18
- package/dist/content/manifest.js +32 -16
- package/dist/content/media-refs.d.ts +4 -2
- package/dist/content/media-refs.js +4 -2
- package/dist/content/media-rewrite.d.ts +8 -4
- package/dist/content/media-rewrite.js +76 -38
- package/dist/content/schema.d.ts +20 -10
- package/dist/content/site-dictionary.d.ts +4 -2
- package/dist/content/site-dictionary.js +8 -4
- package/dist/content/types.d.ts +97 -42
- package/dist/delivery/content-index.d.ts +16 -8
- package/dist/delivery/feeds.js +4 -2
- package/dist/delivery/json-ld.d.ts +3 -0
- package/dist/delivery/json-ld.js +3 -0
- package/dist/delivery/manifest.d.ts +4 -2
- package/dist/delivery/manifest.js +4 -2
- package/dist/delivery/public-routes.d.ts +12 -6
- package/dist/delivery/public-routes.js +4 -2
- package/dist/delivery/seo-fields.d.ts +12 -6
- package/dist/delivery/seo-fields.js +8 -4
- package/dist/delivery/site-indexes.d.ts +4 -2
- package/dist/delivery/site-resolver.d.ts +4 -2
- package/dist/delivery/site-resolver.js +4 -2
- package/dist/doctor/cloudflare-api.d.ts +6 -0
- package/dist/doctor/cloudflare-api.js +6 -0
- package/dist/doctor/index.d.ts +12 -6
- package/dist/doctor/report.d.ts +3 -0
- package/dist/doctor/report.js +3 -0
- package/dist/doctor/run.d.ts +3 -0
- package/dist/doctor/run.js +3 -0
- package/dist/doctor/types.d.ts +10 -2
- package/dist/doctor/types.js +6 -0
- package/dist/doctor/wrangler-config.d.ts +7 -2
- package/dist/doctor/wrangler-config.js +3 -0
- package/dist/email.d.ts +4 -2
- package/dist/env.d.ts +0 -3
- package/dist/env.js +0 -3
- package/dist/github/branches.d.ts +4 -2
- package/dist/github/branches.js +4 -2
- package/dist/github/signing.d.ts +1 -1
- package/dist/github/signing.js +2 -2
- package/dist/log/events.d.ts +1 -1
- package/dist/media/bulk-delete-plan.d.ts +8 -4
- package/dist/media/config.d.ts +12 -6
- package/dist/media/config.js +16 -8
- package/dist/media/delivery-bucket.d.ts +4 -2
- package/dist/media/library-entry.d.ts +4 -2
- package/dist/media/library-entry.js +4 -2
- package/dist/media/manifest.d.ts +29 -15
- package/dist/media/manifest.js +29 -16
- package/dist/media/naming.d.ts +12 -6
- package/dist/media/naming.js +24 -12
- package/dist/media/orphan-scan.d.ts +4 -2
- package/dist/media/reconcile.d.ts +21 -11
- package/dist/media/reconcile.js +12 -6
- package/dist/media/reference.d.ts +8 -4
- package/dist/media/reference.js +12 -6
- package/dist/media/rewrite-plan.d.ts +12 -6
- package/dist/media/sniff.d.ts +4 -2
- package/dist/media/sniff.js +28 -14
- package/dist/media/store.d.ts +16 -8
- package/dist/media/store.js +4 -2
- package/dist/media/transform-url.d.ts +12 -6
- package/dist/media/transform-url.js +8 -4
- package/dist/media/usage.d.ts +8 -4
- package/dist/nav/site-config.d.ts +16 -8
- package/dist/render/component-grammar.d.ts +23 -10
- package/dist/render/component-grammar.js +19 -8
- package/dist/render/component-insert.d.ts +8 -4
- package/dist/render/component-insert.js +4 -2
- package/dist/render/component-reference.d.ts +4 -2
- package/dist/render/component-reference.js +4 -2
- package/dist/render/component-validate.d.ts +3 -0
- package/dist/render/component-validate.js +3 -0
- package/dist/render/glyph.d.ts +4 -2
- package/dist/render/glyph.js +4 -2
- package/dist/render/pipeline.d.ts +20 -10
- package/dist/render/pipeline.js +4 -2
- package/dist/render/registry.d.ts +40 -20
- package/dist/render/registry.js +16 -8
- package/dist/render/rehype-dispatch.d.ts +22 -8
- package/dist/render/rehype-dispatch.js +22 -8
- package/dist/render/remark-directives.d.ts +3 -0
- package/dist/render/remark-directives.js +3 -0
- package/dist/render/remark-figure.d.ts +4 -2
- package/dist/render/remark-figure.js +4 -2
- package/dist/render/resolve-links.d.ts +4 -2
- package/dist/render/resolve-links.js +4 -2
- package/dist/render/resolve-media.d.ts +16 -8
- package/dist/render/resolve-media.js +12 -6
- package/dist/sveltekit/admin-dispatch.d.ts +2 -0
- package/dist/sveltekit/admin-dispatch.js +9 -3
- package/dist/sveltekit/auth-routes.d.ts +3 -0
- package/dist/sveltekit/auth-routes.js +3 -0
- package/dist/sveltekit/cairn-admin.d.ts +16 -5
- package/dist/sveltekit/cairn-admin.js +26 -10
- package/dist/sveltekit/content-routes.d.ts +191 -86
- package/dist/sveltekit/content-routes.js +297 -107
- package/dist/sveltekit/editors-routes.d.ts +3 -0
- package/dist/sveltekit/editors-routes.js +3 -0
- package/dist/sveltekit/guard.d.ts +4 -2
- package/dist/sveltekit/guard.js +4 -2
- package/dist/sveltekit/https-required-page.d.ts +1 -1
- package/dist/sveltekit/https-required-page.js +1 -1
- package/dist/sveltekit/index.d.ts +1 -1
- package/dist/sveltekit/media-route.d.ts +1 -2
- package/dist/sveltekit/media-route.js +13 -8
- package/dist/sveltekit/nav-routes.d.ts +7 -2
- package/dist/sveltekit/nav-routes.js +3 -0
- package/dist/sveltekit/types.d.ts +4 -2
- package/dist/vite/index.d.ts +32 -16
- package/dist/vite/index.js +52 -26
- package/dist/vite/resolve-root.d.ts +8 -4
- package/dist/vite/resolve-root.js +4 -2
- package/package.json +7 -1
- package/src/lib/components/AdminLayout.svelte +22 -0
- package/src/lib/components/CairnAdmin.svelte +3 -0
- package/src/lib/components/CairnTidySettings.svelte +2 -2
- package/src/lib/components/ComponentForm.svelte +0 -1
- package/src/lib/components/EditPage.svelte +133 -41
- package/src/lib/components/HelpHome.svelte +850 -0
- package/src/lib/components/MarkdownHelpDialog.svelte +4 -15
- package/src/lib/components/client-ingest.ts +20 -10
- package/src/lib/components/editor-media.ts +20 -10
- package/src/lib/components/editor-placeholder.ts +12 -6
- package/src/lib/components/editor-tidy.ts +28 -14
- package/src/lib/components/index.ts +1 -0
- package/src/lib/components/link-completion.ts +12 -6
- package/src/lib/components/markdown-directives.ts +13 -8
- package/src/lib/components/markdown-format.ts +63 -30
- package/src/lib/components/markdown-reference.ts +30 -0
- package/src/lib/components/media-upload-outcome.ts +12 -6
- package/src/lib/components/objective-errors.ts +16 -8
- package/src/lib/components/preview-doc.ts +4 -2
- package/src/lib/components/spellcheck.ts +79 -41
- package/src/lib/components/tidy-categorize.ts +28 -14
- package/src/lib/components/tidy-validate.ts +28 -14
- package/src/lib/components/topbar-context.ts +4 -2
- package/src/lib/content/advisories.ts +150 -0
- package/src/lib/content/compose.ts +5 -2
- package/src/lib/content/excerpt.ts +4 -2
- package/src/lib/content/getting-started.ts +31 -0
- package/src/lib/content/links.ts +16 -8
- package/src/lib/content/manifest.ts +36 -18
- package/src/lib/content/media-refs.ts +4 -2
- package/src/lib/content/media-rewrite.ts +100 -50
- package/src/lib/content/schema.ts +20 -10
- package/src/lib/content/site-dictionary.ts +8 -4
- package/src/lib/content/types.ts +97 -42
- package/src/lib/delivery/content-index.ts +16 -8
- package/src/lib/delivery/feeds.ts +4 -2
- package/src/lib/delivery/json-ld.ts +3 -0
- package/src/lib/delivery/manifest.ts +4 -2
- package/src/lib/delivery/public-routes.ts +16 -8
- package/src/lib/delivery/seo-fields.ts +12 -6
- package/src/lib/delivery/site-indexes.ts +4 -2
- package/src/lib/delivery/site-resolver.ts +4 -2
- package/src/lib/doctor/cloudflare-api.ts +6 -0
- package/src/lib/doctor/index.ts +12 -6
- package/src/lib/doctor/report.ts +3 -0
- package/src/lib/doctor/run.ts +3 -0
- package/src/lib/doctor/types.ts +10 -2
- package/src/lib/doctor/wrangler-config.ts +7 -2
- package/src/lib/email.ts +4 -2
- package/src/lib/env.ts +0 -3
- package/src/lib/github/branches.ts +4 -2
- package/src/lib/github/signing.ts +2 -2
- package/src/lib/log/events.ts +1 -0
- package/src/lib/media/bulk-delete-plan.ts +8 -4
- package/src/lib/media/config.ts +24 -12
- package/src/lib/media/delivery-bucket.ts +4 -2
- package/src/lib/media/library-entry.ts +4 -2
- package/src/lib/media/manifest.ts +33 -18
- package/src/lib/media/naming.ts +24 -12
- package/src/lib/media/orphan-scan.ts +4 -2
- package/src/lib/media/reconcile.ts +21 -11
- package/src/lib/media/reference.ts +12 -6
- package/src/lib/media/rewrite-plan.ts +12 -6
- package/src/lib/media/sniff.ts +28 -14
- package/src/lib/media/store.ts +16 -8
- package/src/lib/media/transform-url.ts +12 -6
- package/src/lib/media/usage.ts +8 -4
- package/src/lib/nav/site-config.ts +16 -8
- package/src/lib/render/component-grammar.ts +23 -10
- package/src/lib/render/component-insert.ts +8 -4
- package/src/lib/render/component-reference.ts +4 -2
- package/src/lib/render/component-validate.ts +3 -0
- package/src/lib/render/glyph.ts +4 -2
- package/src/lib/render/pipeline.ts +20 -10
- package/src/lib/render/registry.ts +44 -22
- package/src/lib/render/rehype-dispatch.ts +22 -8
- package/src/lib/render/remark-directives.ts +3 -0
- package/src/lib/render/remark-figure.ts +4 -2
- package/src/lib/render/resolve-links.ts +4 -2
- package/src/lib/render/resolve-media.ts +16 -8
- package/src/lib/sveltekit/admin-dispatch.ts +10 -4
- package/src/lib/sveltekit/auth-routes.ts +3 -0
- package/src/lib/sveltekit/cairn-admin.ts +37 -15
- package/src/lib/sveltekit/content-routes.ts +494 -197
- package/src/lib/sveltekit/editors-routes.ts +3 -0
- package/src/lib/sveltekit/guard.ts +4 -2
- package/src/lib/sveltekit/https-required-page.ts +1 -1
- package/src/lib/sveltekit/index.ts +3 -0
- package/src/lib/sveltekit/media-route.ts +13 -8
- package/src/lib/sveltekit/nav-routes.ts +7 -2
- package/src/lib/sveltekit/types.ts +4 -2
- package/src/lib/vite/index.ts +60 -30
- package/src/lib/vite/resolve-root.ts +8 -4
|
@@ -28,6 +28,9 @@ function slotByName(def, name) {
|
|
|
28
28
|
function nestedSlots(def) {
|
|
29
29
|
return (def.slots ?? []).filter((s) => s.name !== 'title' && s.name !== 'body');
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
*/
|
|
31
34
|
export function serializeComponent(def, values) {
|
|
32
35
|
const fence = COLON.repeat(nestedSlots(def).length > 0 ? 4 : 3);
|
|
33
36
|
const title = slotByName(def, 'title') ? values.slots.title ?? '' : '';
|
|
@@ -109,18 +112,23 @@ function valuesFromRoot(root, def) {
|
|
|
109
112
|
function rawKeysFromRoot(root) {
|
|
110
113
|
return Object.keys(root?.attributes ?? {});
|
|
111
114
|
}
|
|
112
|
-
/**
|
|
115
|
+
/**
|
|
116
|
+
* Parse a serialized component directive back into guided-form values, the inverse of
|
|
113
117
|
* {@link serializeComponent}. The grammar is reversible, so the editor can round-trip a
|
|
114
|
-
* saved directive through the form.
|
|
118
|
+
* saved directive through the form.
|
|
119
|
+
*/
|
|
115
120
|
export async function parseComponent(markdown, def) {
|
|
116
121
|
return valuesFromRoot(findComponentRoot(markdown, def), def);
|
|
117
122
|
}
|
|
118
|
-
/**
|
|
119
|
-
*
|
|
123
|
+
/**
|
|
124
|
+
* The raw attribute keys present on the component's opening directive, read from the parsed tree
|
|
125
|
+
* (quote-aware, unlike a regex over the source). Used by validation to flag unknown keys.
|
|
126
|
+
*/
|
|
120
127
|
export function parseRawAttributeKeys(markdown, def) {
|
|
121
128
|
return rawKeysFromRoot(findComponentRoot(markdown, def));
|
|
122
129
|
}
|
|
123
|
-
/**
|
|
130
|
+
/**
|
|
131
|
+
* Decide whether guided edit of this placed block is provably lossless. A block a person typed by
|
|
124
132
|
* hand can carry more than the schema models (an attribute the def does not list, a child container
|
|
125
133
|
* the def does not declare, slot content the form cannot represent stably), and parsing such a block
|
|
126
134
|
* into the form then re-serializing would silently drop it. The edit affordance is offered only when
|
|
@@ -130,7 +138,8 @@ export function parseRawAttributeKeys(markdown, def) {
|
|
|
130
138
|
* 2. `unknown-attribute`: the block carries an attribute key the def does not declare.
|
|
131
139
|
* 3. `undeclared-child`: the root has a direct child container directive that is not a declared
|
|
132
140
|
* nested slot. Such a child would otherwise fold into the body slot and move on re-serialize.
|
|
133
|
-
* 4. `not-idempotent`: `parse -> serialize -> parse` does not recover the same values.
|
|
141
|
+
* 4. `not-idempotent`: `parse -> serialize -> parse` does not recover the same values.
|
|
142
|
+
*/
|
|
134
143
|
export async function componentRoundTripSafety(markdown, def) {
|
|
135
144
|
const root = findComponentRoot(markdown, def);
|
|
136
145
|
if (!root)
|
|
@@ -154,9 +163,11 @@ export async function componentRoundTripSafety(markdown, def) {
|
|
|
154
163
|
return { safe: false, reason: 'not-idempotent' };
|
|
155
164
|
return { safe: true };
|
|
156
165
|
}
|
|
157
|
-
/**
|
|
166
|
+
/**
|
|
167
|
+
* Parse the component once and derive both the guided-form values and the raw attribute keys.
|
|
158
168
|
* Validation needs both, so this seam spares it the double parse that calling
|
|
159
|
-
* {@link parseComponent} and {@link parseRawAttributeKeys} separately would cost.
|
|
169
|
+
* {@link parseComponent} and {@link parseRawAttributeKeys} separately would cost.
|
|
170
|
+
*/
|
|
160
171
|
export async function parseComponentWithRawKeys(markdown, def) {
|
|
161
172
|
const root = findComponentRoot(markdown, def);
|
|
162
173
|
return { values: valuesFromRoot(root, def), rawKeys: rawKeysFromRoot(root) };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ComponentDef, ComponentValues } from './registry.js';
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
2
|
+
/**
|
|
3
|
+
* The outcome of preparing a guided-form component for insertion: the markdown to insert, or the
|
|
4
|
+
* field-keyed errors to show on the form.
|
|
5
|
+
*/
|
|
4
6
|
export type ComponentInsert = {
|
|
5
7
|
ok: true;
|
|
6
8
|
markdown: string;
|
|
@@ -8,6 +10,8 @@ export type ComponentInsert = {
|
|
|
8
10
|
ok: false;
|
|
9
11
|
errors: Record<string, string>;
|
|
10
12
|
};
|
|
11
|
-
/**
|
|
12
|
-
*
|
|
13
|
+
/**
|
|
14
|
+
* Serialize a component's form values, then validate the result against its schema. Returns the
|
|
15
|
+
* markdown to insert at the cursor, or the field errors keyed by attribute key or slot name.
|
|
16
|
+
*/
|
|
13
17
|
export declare function buildComponentInsert(def: ComponentDef, values: ComponentValues): Promise<ComponentInsert>;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { serializeComponent } from './component-grammar.js';
|
|
2
2
|
import { validateComponent } from './component-validate.js';
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
3
|
+
/**
|
|
4
|
+
* Serialize a component's form values, then validate the result against its schema. Returns the
|
|
5
|
+
* markdown to insert at the cursor, or the field errors keyed by attribute key or slot name.
|
|
6
|
+
*/
|
|
5
7
|
export async function buildComponentInsert(def, values) {
|
|
6
8
|
const markdown = serializeComponent(def, values);
|
|
7
9
|
const verdict = await validateComponent(markdown, def);
|
|
@@ -5,6 +5,8 @@ export interface ReferenceOptions {
|
|
|
5
5
|
/** The one-line blockquote summary under the title. */
|
|
6
6
|
summary: string;
|
|
7
7
|
}
|
|
8
|
-
/**
|
|
9
|
-
*
|
|
8
|
+
/**
|
|
9
|
+
* Build a self-contained markdown reference (the llms-full.txt shape) for a component registry, for
|
|
10
|
+
* authors and for pointing an LLM at one curated file.
|
|
11
|
+
*/
|
|
10
12
|
export declare function generateComponentReference(registry: ComponentRegistry, opts: ReferenceOptions): string;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { serializeComponent } from './component-grammar.js';
|
|
2
2
|
import { emptyValues } from './registry.js';
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
3
|
+
/**
|
|
4
|
+
* Build a self-contained markdown reference (the llms-full.txt shape) for a component registry, for
|
|
5
|
+
* authors and for pointing an LLM at one curated file.
|
|
6
|
+
*/
|
|
5
7
|
export function generateComponentReference(registry, opts) {
|
|
6
8
|
const sections = registry.defs.map((def) => componentSection(def));
|
|
7
9
|
return `# ${opts.title}\n\n> ${opts.summary}\n\n${sections.join('\n\n')}\n`;
|
package/dist/render/glyph.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { Element } from 'hast';
|
|
2
2
|
/** A glyph name to SVG path-data map (the site owns the icon set). */
|
|
3
3
|
export type IconSet = Record<string, string>;
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Inline SVG glyph as a real hast node: class ec-glyph, 256 viewBox, currentColor fill.
|
|
5
6
|
* An unknown icon name yields the bare svg shell with no path child, so it never serializes
|
|
6
7
|
* a stray empty (or undefined) path. Callers always wrap the returned element, so the shell
|
|
7
|
-
* keeps them safe.
|
|
8
|
+
* keeps them safe.
|
|
9
|
+
*/
|
|
8
10
|
export declare function glyph(name: string, icons: IconSet): Element;
|
package/dist/render/glyph.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { s } from 'hastscript';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Inline SVG glyph as a real hast node: class ec-glyph, 256 viewBox, currentColor fill.
|
|
3
4
|
* An unknown icon name yields the bare svg shell with no path child, so it never serializes
|
|
4
5
|
* a stray empty (or undefined) path. Callers always wrap the returned element, so the shell
|
|
5
|
-
* keeps them safe.
|
|
6
|
+
* keeps them safe.
|
|
7
|
+
*/
|
|
6
8
|
export function glyph(name, icons) {
|
|
7
9
|
const d = icons[name];
|
|
8
10
|
return s('svg', { className: ['ec-glyph'], viewBox: '0 0 256 256', fill: 'currentColor', ariaHidden: 'true' }, d == null ? [] : [s('path', { d })]);
|
|
@@ -4,27 +4,37 @@ import { type MediaResolve } from './resolve-media.js';
|
|
|
4
4
|
import { type ComponentRegistry } from './registry.js';
|
|
5
5
|
import type { LinkResolve } from '../content/links.js';
|
|
6
6
|
export interface RendererOptions {
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Stamp a `data-rise` ordinal (0, 1, 2, …) on each top-level component so a site's
|
|
8
9
|
* CSS can drive an entrance-cascade delay off it. Omit for no stagger. The ordinal
|
|
9
|
-
* is inert, so a consumer's sanitize floor can keep `data-rise` and drop `style`.
|
|
10
|
+
* is inert, so a consumer's sanitize floor can keep `data-rise` and drop `style`.
|
|
11
|
+
*/
|
|
10
12
|
stagger?: boolean;
|
|
11
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Extend the sanitize allowlist. Receives cairn's default schema (defaultSchema plus the
|
|
12
15
|
* directive markers and the common benign tags) and returns the schema to use. Add to the
|
|
13
16
|
* allowlist for the benign HTML a site's content needs; start from the argument so the
|
|
14
|
-
* dangerous strip is preserved.
|
|
17
|
+
* dangerous strip is preserved.
|
|
18
|
+
*/
|
|
15
19
|
sanitizeSchema?: (defaults: Schema) => Schema;
|
|
16
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Developer-only escape hatch: disable the sanitize floor entirely. This reintroduces the XSS
|
|
17
22
|
* vector the floor closes, so it is only for a site whose content is fully developer-controlled.
|
|
18
|
-
* It is a code-level adapter decision, never an editor-facing setting.
|
|
23
|
+
* It is a code-level adapter decision, never an editor-facing setting.
|
|
24
|
+
*/
|
|
19
25
|
unsafeDisableSanitize?: boolean;
|
|
20
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* The `rel` value forced on every `target="_blank"` anchor, applied last so it also covers
|
|
21
28
|
* component-built anchors. Defaults to `'noopener noreferrer'`. Set a different string to change
|
|
22
|
-
* it, or `false` to disable the injection (a site that owns its own anchor hardening).
|
|
29
|
+
* it, or `false` to disable the injection (a site that owns its own anchor hardening).
|
|
30
|
+
*/
|
|
23
31
|
anchorRel?: string | false;
|
|
24
32
|
}
|
|
25
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Compose a site's render pipeline from its component registry: directive syntax to
|
|
26
35
|
* stamped markers to registry-built hast. Returns `renderMarkdown` plus the remark/
|
|
27
|
-
* rehype plugin arrays (so the admin editor preview can reuse the exact same set).
|
|
36
|
+
* rehype plugin arrays (so the admin editor preview can reuse the exact same set).
|
|
37
|
+
*/
|
|
28
38
|
export declare function createRenderer(registry?: ComponentRegistry, options?: RendererOptions): {
|
|
29
39
|
remarkPlugins: PluggableList;
|
|
30
40
|
rehypePlugins: PluggableList;
|
package/dist/render/pipeline.js
CHANGED
|
@@ -15,9 +15,11 @@ import { remarkResolveCairnLinks, CAIRN_RESOLVE } from './resolve-links.js';
|
|
|
15
15
|
import { remarkResolveMedia, MEDIA_RESOLVE } from './resolve-media.js';
|
|
16
16
|
import { rehypeDispatch } from './rehype-dispatch.js';
|
|
17
17
|
import { defineRegistry } from './registry.js';
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Compose a site's render pipeline from its component registry: directive syntax to
|
|
19
20
|
* stamped markers to registry-built hast. Returns `renderMarkdown` plus the remark/
|
|
20
|
-
* rehype plugin arrays (so the admin editor preview can reuse the exact same set).
|
|
21
|
+
* rehype plugin arrays (so the admin editor preview can reuse the exact same set).
|
|
22
|
+
*/
|
|
21
23
|
export function createRenderer(registry = defineRegistry({ components: [] }), options = {}) {
|
|
22
24
|
const remarkPlugins = [
|
|
23
25
|
remarkDirective,
|
|
@@ -20,14 +20,18 @@ export interface AttributeField {
|
|
|
20
20
|
source: string;
|
|
21
21
|
message: string;
|
|
22
22
|
};
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* A pure, browser-safe cross-field validator. Returns an error string, or null when valid.
|
|
24
25
|
* Receives the field's value and the full {@link ComponentValues} so a rule can read sibling
|
|
25
|
-
* fields. The picker wraps the call in try/catch so an author's throw never crashes the form.
|
|
26
|
+
* fields. The picker wraps the call in try/catch so an author's throw never crashes the form.
|
|
27
|
+
*/
|
|
26
28
|
validate?: (value: string | boolean, all: ComponentValues) => string | null;
|
|
27
29
|
}
|
|
28
30
|
export type SlotKind = 'markdown' | 'inline' | 'repeatable';
|
|
29
|
-
/**
|
|
30
|
-
*
|
|
31
|
+
/**
|
|
32
|
+
* One named content region of a component. The slots named `title` and `body` are special: `title`
|
|
33
|
+
* serializes to the directive `[label]` and `body` to the unmarked content (see the canonical grammar).
|
|
34
|
+
*/
|
|
31
35
|
export interface SlotDef {
|
|
32
36
|
name: string;
|
|
33
37
|
label: string;
|
|
@@ -36,14 +40,18 @@ export interface SlotDef {
|
|
|
36
40
|
help?: string;
|
|
37
41
|
/** For `kind: 'repeatable'`: the fields composing each list item (v1 uses the first field). */
|
|
38
42
|
itemFields?: AttributeField[];
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
43
|
+
/**
|
|
44
|
+
* For `kind: 'repeatable'`: derives a row's label from its item values and zero-based index.
|
|
45
|
+
* When it returns nothing, the picker falls back to `${label} ${index + 1}`.
|
|
46
|
+
*/
|
|
41
47
|
itemLabel?: (item: Record<string, string | boolean>, index: number) => string;
|
|
42
48
|
}
|
|
43
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* The structured input a component's `build` receives. The engine stamps the component's
|
|
44
51
|
* attributes and partitions its slots from the rendered hast, so `build` arranges hast and
|
|
45
52
|
* never walks the tree. `slot(name)` returns a slot's rendered children (title, body, or any
|
|
46
|
-
* named slot); `items(name)` returns a repeatable slot's items, one child list per item.
|
|
53
|
+
* named slot); `items(name)` returns a repeatable slot's items, one child list per item.
|
|
54
|
+
*/
|
|
47
55
|
export interface ComponentContext {
|
|
48
56
|
/** Declared attribute values, keyed by attribute key. Booleans are real booleans. */
|
|
49
57
|
attributes: Record<string, string | boolean>;
|
|
@@ -64,9 +72,11 @@ export interface ComponentDef {
|
|
|
64
72
|
description: string;
|
|
65
73
|
/** Markdown scaffold inserted at the cursor by the editor palette. */
|
|
66
74
|
insertTemplate?: string;
|
|
67
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* Build the final hast element from the component context (attributes plus partitioned
|
|
68
77
|
* slots). The engine stamps the entrance-stagger ordinal (`data-rise`) on the top-level
|
|
69
|
-
* result, so a build fn stays free of any motion concern.
|
|
78
|
+
* result, so a build fn stays free of any motion concern.
|
|
79
|
+
*/
|
|
70
80
|
build: (ctx: ComponentContext) => Element;
|
|
71
81
|
/** Optional role-to-default-icon, e.g. `{ caution: 'warning' }`. */
|
|
72
82
|
defaultIconByRole?: Record<string, string>;
|
|
@@ -82,8 +92,10 @@ export interface ComponentDef {
|
|
|
82
92
|
group?: string;
|
|
83
93
|
/** Omit from the top-level picker (for a nested or round-trip-only component). */
|
|
84
94
|
hidden?: boolean;
|
|
85
|
-
/**
|
|
86
|
-
*
|
|
95
|
+
/**
|
|
96
|
+
* A structured sample the picker seeds the form with and renders through the same path a real
|
|
97
|
+
* insert takes. Declaring `preview` is what opts the component into the two-pane configure layout.
|
|
98
|
+
*/
|
|
87
99
|
preview?: {
|
|
88
100
|
attributes?: Record<string, string | boolean>;
|
|
89
101
|
slots?: Record<string, string | string[]>;
|
|
@@ -97,9 +109,11 @@ export interface ComponentRegistry {
|
|
|
97
109
|
/** The component's first `type:'icon'` attribute, or undefined when it declares none. */
|
|
98
110
|
iconField(name: string): AttributeField | undefined;
|
|
99
111
|
}
|
|
100
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* The hast property name carrying one declared attribute from stamp to dispatch, e.g. `tone`
|
|
101
114
|
* becomes `dataAttrTone`. The directive stamp writes it and the rehype dispatch reads it, so both
|
|
102
|
-
* sides derive the name from this one helper rather than spelling the capitalize twice.
|
|
115
|
+
* sides derive the name from this one helper rather than spelling the capitalize twice.
|
|
116
|
+
*/
|
|
103
117
|
export declare function dataAttrProp(key: string): string;
|
|
104
118
|
/**
|
|
105
119
|
* Build a registry from a site's component definitions. The single source the render
|
|
@@ -108,16 +122,22 @@ export declare function dataAttrProp(key: string): string;
|
|
|
108
122
|
export declare function defineRegistry({ components }: {
|
|
109
123
|
components: ComponentDef[];
|
|
110
124
|
}): ComponentRegistry;
|
|
111
|
-
/**
|
|
112
|
-
*
|
|
125
|
+
/**
|
|
126
|
+
* Guided-form values for one component: attribute values keyed by attribute key, slot values keyed
|
|
127
|
+
* by slot name (a string, or a string list for a repeatable slot).
|
|
128
|
+
*/
|
|
113
129
|
export interface ComponentValues {
|
|
114
130
|
attributes: Record<string, string | boolean>;
|
|
115
131
|
slots: Record<string, string | string[]>;
|
|
116
132
|
}
|
|
117
|
-
/**
|
|
118
|
-
*
|
|
133
|
+
/**
|
|
134
|
+
* Seed an empty {@link ComponentValues} from a component's schema: attribute defaults (or '' / false)
|
|
135
|
+
* and empty slot values ([] for repeatable, '' otherwise).
|
|
136
|
+
*/
|
|
119
137
|
export declare function emptyValues(def: ComponentDef): ComponentValues;
|
|
120
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* Seed {@link ComponentValues} from a component's `preview` sample: the {@link emptyValues} base
|
|
121
140
|
* with `def.preview.attributes` and `def.preview.slots` overlaid (a shallow merge per side). When
|
|
122
|
-
* the def declares no `preview`, returns exactly the {@link emptyValues} output.
|
|
141
|
+
* the def declares no `preview`, returns exactly the {@link emptyValues} output.
|
|
142
|
+
*/
|
|
123
143
|
export declare function previewValues(def: ComponentDef): ComponentValues;
|
package/dist/render/registry.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* The hast property name carrying one declared attribute from stamp to dispatch, e.g. `tone`
|
|
2
3
|
* becomes `dataAttrTone`. The directive stamp writes it and the rehype dispatch reads it, so both
|
|
3
|
-
* sides derive the name from this one helper rather than spelling the capitalize twice.
|
|
4
|
+
* sides derive the name from this one helper rather than spelling the capitalize twice.
|
|
5
|
+
*/
|
|
4
6
|
export function dataAttrProp(key) {
|
|
5
7
|
return `dataAttr${key.charAt(0).toUpperCase()}${key.slice(1)}`;
|
|
6
8
|
}
|
|
7
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* A component's first `type:'icon'` attribute, or undefined when it declares none. Both the
|
|
8
11
|
* construction-time guard and the registry's `iconField` derive the icon field from this one
|
|
9
|
-
* predicate rather than spelling the `type === 'icon'` find twice.
|
|
12
|
+
* predicate rather than spelling the `type === 'icon'` find twice.
|
|
13
|
+
*/
|
|
10
14
|
function findIconField(def) {
|
|
11
15
|
return def.attributes?.find((field) => field.type === 'icon');
|
|
12
16
|
}
|
|
@@ -35,8 +39,10 @@ export function defineRegistry({ components }) {
|
|
|
35
39
|
},
|
|
36
40
|
};
|
|
37
41
|
}
|
|
38
|
-
/**
|
|
39
|
-
*
|
|
42
|
+
/**
|
|
43
|
+
* Seed an empty {@link ComponentValues} from a component's schema: attribute defaults (or '' / false)
|
|
44
|
+
* and empty slot values ([] for repeatable, '' otherwise).
|
|
45
|
+
*/
|
|
40
46
|
export function emptyValues(def) {
|
|
41
47
|
const attributes = {};
|
|
42
48
|
for (const field of def.attributes ?? []) {
|
|
@@ -48,9 +54,11 @@ export function emptyValues(def) {
|
|
|
48
54
|
}
|
|
49
55
|
return { attributes, slots };
|
|
50
56
|
}
|
|
51
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* Seed {@link ComponentValues} from a component's `preview` sample: the {@link emptyValues} base
|
|
52
59
|
* with `def.preview.attributes` and `def.preview.slots` overlaid (a shallow merge per side). When
|
|
53
|
-
* the def declares no `preview`, returns exactly the {@link emptyValues} output.
|
|
60
|
+
* the def declares no `preview`, returns exactly the {@link emptyValues} output.
|
|
61
|
+
*/
|
|
54
62
|
export function previewValues(def) {
|
|
55
63
|
const base = emptyValues(def);
|
|
56
64
|
if (!def.preview)
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import type { Root, Element, ElementContent } from 'hast';
|
|
2
2
|
import { type ComponentContext, type ComponentRegistry } from './registry.js';
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
3
6
|
export declare function isElement(node: ElementContent | undefined): node is Element;
|
|
4
|
-
/**
|
|
5
|
-
*
|
|
7
|
+
/**
|
|
8
|
+
* Read a declared string attribute off the component context, returning undefined for a boolean or
|
|
9
|
+
* absent value. Replaces the `typeof ctx.attributes[key] === 'string'` narrowing a build repeats.
|
|
10
|
+
*/
|
|
6
11
|
export declare function strAttr(ctx: ComponentContext, key: string): string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
*/
|
|
7
15
|
export declare function strProp(node: Element, name: string): string | undefined;
|
|
8
16
|
/** Wrap a pre-built glyph in an ec-icon span; secondary role adds the modifier. */
|
|
9
17
|
export declare function iconSpan(glyphEl: Element, role?: string): Element;
|
|
@@ -11,18 +19,24 @@ export declare function iconSpan(glyphEl: Element, role?: string): Element;
|
|
|
11
19
|
export type MakeIcon = (name: string, role?: string) => Element;
|
|
12
20
|
/** Section wrapper: `<section class=…><div class="card-body">…</div></section>`. */
|
|
13
21
|
export declare function cardShell(classes: string[], body: ElementContent[]): Element;
|
|
14
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Card head row: `<div class="ec-head">[icon]<hN class="card-title">{title}</hN></div>`.
|
|
15
24
|
* Pass the title's inline children, an optional pre-built icon element, and an optional heading
|
|
16
25
|
* level (default 2). This factors the icon-plus-heading head that a titled component build would
|
|
17
|
-
* otherwise rebuild by hand (the shape the removed `splitHead` produced).
|
|
26
|
+
* otherwise rebuild by hand (the shape the removed `splitHead` produced).
|
|
27
|
+
*/
|
|
18
28
|
export declare function headRow(title: ElementContent[], icon?: Element, level?: number): Element;
|
|
19
|
-
/**
|
|
20
|
-
*
|
|
29
|
+
/**
|
|
30
|
+
* Tag the first <ul> among children with `ec-grid` and strip its whitespace-only
|
|
31
|
+
* text nodes so the bare list serializes without newlines. Returns that <ul>.
|
|
32
|
+
*/
|
|
21
33
|
export declare function markFirstList(children: ElementContent[]): Element | undefined;
|
|
22
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Rehype transformer: dispatch each stamped element through its registry `build`
|
|
23
36
|
* fn. When `stagger` is on, each top-level primitive gets a `data-rise` attribute
|
|
24
37
|
* carrying its document-order index (0, 1, 2, …); the site's CSS maps that ordinal
|
|
25
38
|
* to an entrance delay. The index is inert, so a consumer's sanitize floor can keep
|
|
26
39
|
* `data-rise` while dropping `style`. Nested primitives never get it. Non-primitive
|
|
27
|
-
* content (lede, intro paragraphs, the page-toc nav) passes through untouched.
|
|
40
|
+
* content (lede, intro paragraphs, the page-toc nav) passes through untouched.
|
|
41
|
+
*/
|
|
28
42
|
export declare function rehypeDispatch(registry: ComponentRegistry, stagger?: boolean): (tree: Root) => void;
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { h } from 'hastscript';
|
|
2
2
|
import { dataAttrProp } from './registry.js';
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
3
6
|
export function isElement(node) {
|
|
4
7
|
return !!node && node.type === 'element';
|
|
5
8
|
}
|
|
6
|
-
/**
|
|
7
|
-
*
|
|
9
|
+
/**
|
|
10
|
+
* Read a declared string attribute off the component context, returning undefined for a boolean or
|
|
11
|
+
* absent value. Replaces the `typeof ctx.attributes[key] === 'string'` narrowing a build repeats.
|
|
12
|
+
*/
|
|
8
13
|
export function strAttr(ctx, key) {
|
|
9
14
|
const value = ctx.attributes[key];
|
|
10
15
|
return typeof value === 'string' ? value : undefined;
|
|
@@ -12,6 +17,9 @@ export function strAttr(ctx, key) {
|
|
|
12
17
|
// hast Properties values are PropertyValue (string | number | boolean | array | null).
|
|
13
18
|
// Directive markers (dataPrimitive/dataRole/dataAttr<Key>) are always stamped as strings;
|
|
14
19
|
// this reads them back with that guarantee instead of casting at each call site.
|
|
20
|
+
/**
|
|
21
|
+
*
|
|
22
|
+
*/
|
|
15
23
|
export function strProp(node, name) {
|
|
16
24
|
const value = node.properties?.[name];
|
|
17
25
|
return typeof value === 'string' ? value : undefined;
|
|
@@ -25,10 +33,12 @@ export function iconSpan(glyphEl, role) {
|
|
|
25
33
|
export function cardShell(classes, body) {
|
|
26
34
|
return h('section', { className: classes }, [h('div', { className: ['card-body'] }, body)]);
|
|
27
35
|
}
|
|
28
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Card head row: `<div class="ec-head">[icon]<hN class="card-title">{title}</hN></div>`.
|
|
29
38
|
* Pass the title's inline children, an optional pre-built icon element, and an optional heading
|
|
30
39
|
* level (default 2). This factors the icon-plus-heading head that a titled component build would
|
|
31
|
-
* otherwise rebuild by hand (the shape the removed `splitHead` produced).
|
|
40
|
+
* otherwise rebuild by hand (the shape the removed `splitHead` produced).
|
|
41
|
+
*/
|
|
32
42
|
export function headRow(title, icon, level = 2) {
|
|
33
43
|
const children = [];
|
|
34
44
|
if (icon)
|
|
@@ -36,8 +46,10 @@ export function headRow(title, icon, level = 2) {
|
|
|
36
46
|
children.push(h(`h${level}`, { className: ['card-title'] }, title));
|
|
37
47
|
return h('div', { className: ['ec-head'] }, children);
|
|
38
48
|
}
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
49
|
+
/**
|
|
50
|
+
* Tag the first <ul> among children with `ec-grid` and strip its whitespace-only
|
|
51
|
+
* text nodes so the bare list serializes without newlines. Returns that <ul>.
|
|
52
|
+
*/
|
|
41
53
|
export function markFirstList(children) {
|
|
42
54
|
const ul = children.find((c) => isElement(c) && c.tagName === 'ul');
|
|
43
55
|
if (ul) {
|
|
@@ -133,12 +145,14 @@ function transformNode(node, registry) {
|
|
|
133
145
|
};
|
|
134
146
|
return def.build(ctx);
|
|
135
147
|
}
|
|
136
|
-
/**
|
|
148
|
+
/**
|
|
149
|
+
* Rehype transformer: dispatch each stamped element through its registry `build`
|
|
137
150
|
* fn. When `stagger` is on, each top-level primitive gets a `data-rise` attribute
|
|
138
151
|
* carrying its document-order index (0, 1, 2, …); the site's CSS maps that ordinal
|
|
139
152
|
* to an entrance delay. The index is inert, so a consumer's sanitize floor can keep
|
|
140
153
|
* `data-rise` while dropping `style`. Nested primitives never get it. Non-primitive
|
|
141
|
-
* content (lede, intro paragraphs, the page-toc nav) passes through untouched.
|
|
154
|
+
* content (lede, intro paragraphs, the page-toc nav) passes through untouched.
|
|
155
|
+
*/
|
|
142
156
|
export function rehypeDispatch(registry, stagger) {
|
|
143
157
|
return (tree) => {
|
|
144
158
|
let idx = 0;
|
|
@@ -51,6 +51,9 @@ function restoreLiteral(node) {
|
|
|
51
51
|
// component name, icon, and role. No structure is built here; the rehype
|
|
52
52
|
// dispatcher rewrites the marked elements once their children are hast.
|
|
53
53
|
// Text and leaf directives are restored to literal text (accidental prose colons).
|
|
54
|
+
/**
|
|
55
|
+
*
|
|
56
|
+
*/
|
|
54
57
|
export function remarkDirectiveStamp(registry) {
|
|
55
58
|
const known = new Set(registry.names);
|
|
56
59
|
return (tree) => {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Root } from 'mdast';
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
2
|
+
/**
|
|
3
|
+
* Rewrite the reserved `figure` container directive into a placed <figure>. Every other directive
|
|
4
|
+
* is left to remarkDirectiveStamp, which already skips unregistered names.
|
|
5
|
+
*/
|
|
4
6
|
export declare function remarkFigure(): (tree: Root) => void;
|
|
@@ -42,8 +42,10 @@ function trimLeadingNewline(children) {
|
|
|
42
42
|
}
|
|
43
43
|
return children;
|
|
44
44
|
}
|
|
45
|
-
/**
|
|
46
|
-
*
|
|
45
|
+
/**
|
|
46
|
+
* Rewrite the reserved `figure` container directive into a placed <figure>. Every other directive
|
|
47
|
+
* is left to remarkDirectiveStamp, which already skips unregistered names.
|
|
48
|
+
*/
|
|
47
49
|
export function remarkFigure() {
|
|
48
50
|
return (tree) => {
|
|
49
51
|
visit(tree, 'containerDirective', (node) => {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { VFile } from 'vfile';
|
|
2
2
|
/** The VFile data key the renderer sets the per-call resolver under. */
|
|
3
3
|
export declare const CAIRN_RESOLVE = "cairnResolve";
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Resolve cairn: link nodes against the VFile's resolver. A non-cairn href and a malformed token
|
|
5
6
|
* pass through. A missing target is marked with the cairn-broken-link class (the resolver returns
|
|
6
|
-
* undefined) or, when the resolver throws, the error propagates and fails the build.
|
|
7
|
+
* undefined) or, when the resolver throws, the error propagates and fails the build.
|
|
8
|
+
*/
|
|
7
9
|
export declare function remarkResolveCairnLinks(): (tree: unknown, file: VFile) => void;
|
|
@@ -7,9 +7,11 @@ import { visit } from 'unist-util-visit';
|
|
|
7
7
|
import { parseCairnToken } from '../content/links.js';
|
|
8
8
|
/** The VFile data key the renderer sets the per-call resolver under. */
|
|
9
9
|
export const CAIRN_RESOLVE = 'cairnResolve';
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Resolve cairn: link nodes against the VFile's resolver. A non-cairn href and a malformed token
|
|
11
12
|
* pass through. A missing target is marked with the cairn-broken-link class (the resolver returns
|
|
12
|
-
* undefined) or, when the resolver throws, the error propagates and fails the build.
|
|
13
|
+
* undefined) or, when the resolver throws, the error propagates and fails the build.
|
|
14
|
+
*/
|
|
13
15
|
export function remarkResolveCairnLinks() {
|
|
14
16
|
return (tree, file) => {
|
|
15
17
|
const resolve = file.data[CAIRN_RESOLVE];
|