@humanspeak/svelte-markdown 1.0.4 → 1.0.5
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/dist/Parser.svelte +11 -6
- package/dist/Parser.svelte.d.ts +3 -2
- package/dist/SvelteMarkdown.svelte +6 -3
- package/dist/renderers/TableCell.svelte +2 -2
- package/dist/renderers/TableCell.svelte.d.ts +2 -2
- package/dist/types.d.ts +10 -2
- package/dist/utils/parse-and-cache.d.ts +2 -2
- package/dist/utils/parse-and-cache.js +14 -7
- package/dist/utils/token-cache.d.ts +1 -1
- package/dist/utils/token-cache.js +4 -4
- package/dist/utils/token-cleanup.d.ts +0 -45
- package/dist/utils/token-cleanup.js +26 -18
- package/package.json +6 -6
package/dist/Parser.svelte
CHANGED
|
@@ -56,6 +56,9 @@
|
|
|
56
56
|
RendererComponent
|
|
57
57
|
} from './utils/markdown-parser.js'
|
|
58
58
|
|
|
59
|
+
// trunk-ignore(eslint/@typescript-eslint/no-explicit-any)
|
|
60
|
+
type AnySnippet = (..._args: any[]) => any
|
|
61
|
+
|
|
59
62
|
interface Props<T extends Renderers = Renderers> {
|
|
60
63
|
type?: string
|
|
61
64
|
tokens?: Token[] | TokensList
|
|
@@ -63,8 +66,8 @@
|
|
|
63
66
|
rows?: Tokens.TableCell[][]
|
|
64
67
|
ordered?: boolean
|
|
65
68
|
renderers: T
|
|
66
|
-
snippetOverrides?: Record<string,
|
|
67
|
-
htmlSnippetOverrides?: Record<string,
|
|
69
|
+
snippetOverrides?: Record<string, AnySnippet>
|
|
70
|
+
htmlSnippetOverrides?: Record<string, AnySnippet>
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
const {
|
|
@@ -121,14 +124,14 @@
|
|
|
121
124
|
{#if cellSnippet}
|
|
122
125
|
{@render cellSnippet({
|
|
123
126
|
header: true,
|
|
124
|
-
align: (rest.align as string[])[i],
|
|
127
|
+
align: (rest.align as string[] | undefined)?.[i] ?? null,
|
|
125
128
|
...cellRest,
|
|
126
129
|
children: headerCellContent
|
|
127
130
|
})}
|
|
128
131
|
{:else}
|
|
129
132
|
<renderers.tablecell
|
|
130
133
|
header={true}
|
|
131
|
-
align={(rest.align as string[])[i]}
|
|
134
|
+
align={(rest.align as string[] | undefined)?.[i] ?? null}
|
|
132
135
|
{...cellRest}
|
|
133
136
|
>
|
|
134
137
|
{@render headerCellContent()}
|
|
@@ -172,7 +175,8 @@
|
|
|
172
175
|
{#if cellSnippet}
|
|
173
176
|
{@render cellSnippet({
|
|
174
177
|
header: false,
|
|
175
|
-
align:
|
|
178
|
+
align:
|
|
179
|
+
(rest.align as string[] | undefined)?.[j] ?? null,
|
|
176
180
|
...cellRest,
|
|
177
181
|
children: bodyCellContent
|
|
178
182
|
})}
|
|
@@ -180,7 +184,8 @@
|
|
|
180
184
|
<renderers.tablecell
|
|
181
185
|
{...cellRest}
|
|
182
186
|
header={false}
|
|
183
|
-
align={(rest.align as string[])[j]
|
|
187
|
+
align={(rest.align as string[] | undefined)?.[j] ??
|
|
188
|
+
null}
|
|
184
189
|
>
|
|
185
190
|
{@render bodyCellContent()}
|
|
186
191
|
</renderers.tablecell>
|
package/dist/Parser.svelte.d.ts
CHANGED
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
*/
|
|
47
47
|
import Parser from './Parser.svelte';
|
|
48
48
|
import type { Renderers, Token, TokensList, Tokens } from './utils/markdown-parser.js';
|
|
49
|
+
type AnySnippet = (..._args: any[]) => any;
|
|
49
50
|
interface Props<T extends Renderers = Renderers> {
|
|
50
51
|
type?: string;
|
|
51
52
|
tokens?: Token[] | TokensList;
|
|
@@ -53,8 +54,8 @@ interface Props<T extends Renderers = Renderers> {
|
|
|
53
54
|
rows?: Tokens.TableCell[][];
|
|
54
55
|
ordered?: boolean;
|
|
55
56
|
renderers: T;
|
|
56
|
-
snippetOverrides?: Record<string,
|
|
57
|
-
htmlSnippetOverrides?: Record<string,
|
|
57
|
+
snippetOverrides?: Record<string, AnySnippet>;
|
|
58
|
+
htmlSnippetOverrides?: Record<string, AnySnippet>;
|
|
58
59
|
}
|
|
59
60
|
type $$ComponentProps = Props & {
|
|
60
61
|
[key: string]: unknown;
|
|
@@ -62,6 +62,9 @@
|
|
|
62
62
|
import { rendererKeysInternal } from './utils/rendererKeys.js'
|
|
63
63
|
import { Marked } from 'marked'
|
|
64
64
|
|
|
65
|
+
// trunk-ignore(eslint/@typescript-eslint/no-explicit-any)
|
|
66
|
+
type AnySnippet = (..._args: any[]) => any
|
|
67
|
+
|
|
65
68
|
const {
|
|
66
69
|
source = [],
|
|
67
70
|
renderers = {},
|
|
@@ -174,7 +177,7 @@
|
|
|
174
177
|
allRendererKeys
|
|
175
178
|
.filter((key) => key in rest && rest[key] != null)
|
|
176
179
|
.map((key) => [key, rest[key]])
|
|
177
|
-
)
|
|
180
|
+
) as Record<string, AnySnippet>
|
|
178
181
|
)
|
|
179
182
|
|
|
180
183
|
// Collect HTML snippet overrides (keys matching html_<tag>)
|
|
@@ -183,7 +186,7 @@
|
|
|
183
186
|
Object.entries(rest)
|
|
184
187
|
.filter(([key, val]) => key.startsWith('html_') && val != null)
|
|
185
188
|
.map(([key, val]) => [key.slice(5), val])
|
|
186
|
-
)
|
|
189
|
+
) as Record<string, AnySnippet>
|
|
187
190
|
)
|
|
188
191
|
|
|
189
192
|
// Passthrough: everything that isn't a known snippet override
|
|
@@ -199,7 +202,7 @@
|
|
|
199
202
|
{tokens}
|
|
200
203
|
{...passThroughProps}
|
|
201
204
|
options={combinedOptions}
|
|
202
|
-
slug={(val: string): string =>
|
|
205
|
+
slug={(val: string): string => slugger.slug(val)}
|
|
203
206
|
renderers={combinedRenderers}
|
|
204
207
|
{snippetOverrides}
|
|
205
208
|
{htmlSnippetOverrides}
|
|
@@ -4,7 +4,7 @@ Renders a table cell as either `<th>` (header) or `<td>` (data), with optional
|
|
|
4
4
|
text alignment applied as an inline `text-align` style.
|
|
5
5
|
|
|
6
6
|
@prop {boolean} header - When `true`, renders a `<th>`; otherwise renders a `<td>`.
|
|
7
|
-
@prop {'left'|'center'|'right'|
|
|
7
|
+
@prop {'left'|'center'|'right'|null} align - Column alignment.
|
|
8
8
|
@prop {Snippet} [children] - Cell content.
|
|
9
9
|
-->
|
|
10
10
|
<script lang="ts">
|
|
@@ -12,7 +12,7 @@ text alignment applied as an inline `text-align` style.
|
|
|
12
12
|
|
|
13
13
|
interface Props {
|
|
14
14
|
header: boolean
|
|
15
|
-
align: 'left' | 'center' | 'right' |
|
|
15
|
+
align: 'left' | 'center' | 'right' | null
|
|
16
16
|
children?: Snippet
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
interface Props {
|
|
3
3
|
header: boolean;
|
|
4
|
-
align: 'left' | 'center' | 'right' |
|
|
4
|
+
align: 'left' | 'center' | 'right' | null;
|
|
5
5
|
children?: Snippet;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
@@ -9,7 +9,7 @@ interface Props {
|
|
|
9
9
|
* text alignment applied as an inline `text-align` style.
|
|
10
10
|
*
|
|
11
11
|
* @prop {boolean} header - When `true`, renders a `<th>`; otherwise renders a `<td>`.
|
|
12
|
-
* @prop {'left'|'center'|'right'|
|
|
12
|
+
* @prop {'left'|'center'|'right'|null} align - Column alignment.
|
|
13
13
|
* @prop {Snippet} [children] - Cell content.
|
|
14
14
|
*/
|
|
15
15
|
declare const TableCell: import("svelte").Component<Props, {}, "">;
|
package/dist/types.d.ts
CHANGED
|
@@ -75,7 +75,7 @@ export interface TableRowSnippetProps {
|
|
|
75
75
|
}
|
|
76
76
|
export interface TableCellSnippetProps {
|
|
77
77
|
header: boolean;
|
|
78
|
-
align: 'left' | 'center' | 'right' |
|
|
78
|
+
align: 'left' | 'center' | 'right' | null;
|
|
79
79
|
children?: Snippet;
|
|
80
80
|
}
|
|
81
81
|
export interface EmSnippetProps {
|
|
@@ -124,8 +124,16 @@ export type SnippetOverrides = {
|
|
|
124
124
|
rawtext?: Snippet<[RawTextSnippetProps]>;
|
|
125
125
|
escape?: Snippet<[EscapeSnippetProps]>;
|
|
126
126
|
};
|
|
127
|
+
/**
|
|
128
|
+
* Props passed to HTML snippet overrides.
|
|
129
|
+
*
|
|
130
|
+
* **Security note:** `attributes` are spread directly onto the rendered HTML element.
|
|
131
|
+
* This includes any attribute from the source markdown, such as `onclick` or `onerror`.
|
|
132
|
+
* If rendering untrusted markdown, use `allowHtmlOnly`/`excludeHtmlOnly` to restrict
|
|
133
|
+
* allowed tags, or integrate your own sanitizer to strip dangerous attributes.
|
|
134
|
+
*/
|
|
127
135
|
export interface HtmlSnippetProps {
|
|
128
|
-
attributes?: Record<string,
|
|
136
|
+
attributes?: Record<string, string | number | boolean | undefined>;
|
|
129
137
|
children?: Snippet;
|
|
130
138
|
}
|
|
131
139
|
export type HtmlSnippetOverrides = {
|
|
@@ -28,7 +28,7 @@ import type { Token, TokensList } from './markdown-parser.js';
|
|
|
28
28
|
* const cachedTokens = parseAndCacheTokens('# Hello World', { gfm: true }, false)
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
|
-
export declare
|
|
31
|
+
export declare const parseAndCacheTokens: (source: string, options: SvelteMarkdownOptions, isInline: boolean) => Token[] | TokensList;
|
|
32
32
|
/**
|
|
33
33
|
* Parses markdown source with caching (async path).
|
|
34
34
|
* Uses Marked's recursive walkTokens with Promise.all to properly
|
|
@@ -46,4 +46,4 @@ export declare function parseAndCacheTokens(source: string, options: SvelteMarkd
|
|
|
46
46
|
* const tokens = await parseAndCacheTokensAsync('# Hello', opts, false)
|
|
47
47
|
* ```
|
|
48
48
|
*/
|
|
49
|
-
export declare
|
|
49
|
+
export declare const parseAndCacheTokensAsync: (source: string, options: SvelteMarkdownOptions, isInline: boolean) => Promise<Token[] | TokensList>;
|
|
@@ -10,13 +10,20 @@ import { tokenCache } from './token-cache.js';
|
|
|
10
10
|
import { shrinkHtmlTokens } from './token-cleanup.js';
|
|
11
11
|
import { Lexer, Marked } from 'marked';
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Lexes markdown source and cleans the resulting tokens. Shared by sync and async paths.
|
|
14
|
+
*
|
|
15
|
+
* @param source - Raw markdown string to lex
|
|
16
|
+
* @param options - Parser options forwarded to the Marked lexer
|
|
17
|
+
* @param isInline - When true, uses inline tokenization (no block elements)
|
|
18
|
+
* @returns Cleaned token array with HTML tokens properly nested
|
|
19
|
+
*
|
|
20
|
+
* @internal
|
|
14
21
|
*/
|
|
15
|
-
|
|
22
|
+
const lexAndClean = (source, options, isInline) => {
|
|
16
23
|
const lexer = new Lexer(options);
|
|
17
24
|
const parsedTokens = isInline ? lexer.inlineTokens(source) : lexer.lex(source);
|
|
18
25
|
return shrinkHtmlTokens(parsedTokens);
|
|
19
|
-
}
|
|
26
|
+
};
|
|
20
27
|
/**
|
|
21
28
|
* Parses markdown source with caching (synchronous path).
|
|
22
29
|
* Checks cache first, parses on miss, stores result, and returns tokens.
|
|
@@ -37,7 +44,7 @@ function lexAndClean(source, options, isInline) {
|
|
|
37
44
|
* const cachedTokens = parseAndCacheTokens('# Hello World', { gfm: true }, false)
|
|
38
45
|
* ```
|
|
39
46
|
*/
|
|
40
|
-
export
|
|
47
|
+
export const parseAndCacheTokens = (source, options, isInline) => {
|
|
41
48
|
// Check cache first - avoids expensive parsing
|
|
42
49
|
const cached = tokenCache.getTokens(source, options);
|
|
43
50
|
if (cached) {
|
|
@@ -51,7 +58,7 @@ export function parseAndCacheTokens(source, options, isInline) {
|
|
|
51
58
|
// Cache the cleaned tokens for next time
|
|
52
59
|
tokenCache.setTokens(source, options, cleanedTokens);
|
|
53
60
|
return cleanedTokens;
|
|
54
|
-
}
|
|
61
|
+
};
|
|
55
62
|
/**
|
|
56
63
|
* Parses markdown source with caching (async path).
|
|
57
64
|
* Uses Marked's recursive walkTokens with Promise.all to properly
|
|
@@ -69,7 +76,7 @@ export function parseAndCacheTokens(source, options, isInline) {
|
|
|
69
76
|
* const tokens = await parseAndCacheTokensAsync('# Hello', opts, false)
|
|
70
77
|
* ```
|
|
71
78
|
*/
|
|
72
|
-
export async
|
|
79
|
+
export const parseAndCacheTokensAsync = async (source, options, isInline) => {
|
|
73
80
|
// Check cache first - avoids expensive parsing
|
|
74
81
|
const cached = tokenCache.getTokens(source, options);
|
|
75
82
|
if (cached) {
|
|
@@ -89,4 +96,4 @@ export async function parseAndCacheTokensAsync(source, options, isInline) {
|
|
|
89
96
|
// Cache the cleaned tokens for next time
|
|
90
97
|
tokenCache.setTokens(source, options, cleanedTokens);
|
|
91
98
|
return cleanedTokens;
|
|
92
|
-
}
|
|
99
|
+
};
|
|
@@ -35,7 +35,7 @@ import type { Token, TokensList } from './markdown-parser.js';
|
|
|
35
35
|
* console.log(hash1 !== hash2) // true - different content = different hash
|
|
36
36
|
* ```
|
|
37
37
|
*/
|
|
38
|
-
declare
|
|
38
|
+
declare const hashString: (str: string) => string;
|
|
39
39
|
/**
|
|
40
40
|
* Specialized cache for markdown token storage.
|
|
41
41
|
* Extends MemoryCache with markdown-specific convenience methods.
|
|
@@ -33,7 +33,7 @@ import { MemoryCache } from './cache.js';
|
|
|
33
33
|
* console.log(hash1 !== hash2) // true - different content = different hash
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
|
-
|
|
36
|
+
const hashString = (str) => {
|
|
37
37
|
let hash = 2166136261; // FNV offset basis (32-bit)
|
|
38
38
|
for (let i = 0; i < str.length; i++) {
|
|
39
39
|
hash ^= str.charCodeAt(i);
|
|
@@ -42,7 +42,7 @@ function hashString(str) {
|
|
|
42
42
|
}
|
|
43
43
|
// Convert to unsigned 32-bit integer and base36 string
|
|
44
44
|
return (hash >>> 0).toString(36);
|
|
45
|
-
}
|
|
45
|
+
};
|
|
46
46
|
/**
|
|
47
47
|
* Generates a cache key from markdown source and parser options.
|
|
48
48
|
* Combines hashes of both source content and options to ensure
|
|
@@ -71,7 +71,7 @@ function hashString(str) {
|
|
|
71
71
|
// reactivity system creates new objects on prop changes ($derived), so
|
|
72
72
|
// this is safe for all internal usage paths.
|
|
73
73
|
const optionsHashCache = new WeakMap();
|
|
74
|
-
|
|
74
|
+
const getCacheKey = (source, options) => {
|
|
75
75
|
const sourceHash = hashString(source);
|
|
76
76
|
let optionsHash = optionsHashCache.get(options);
|
|
77
77
|
if (!optionsHash) {
|
|
@@ -90,7 +90,7 @@ function getCacheKey(source, options) {
|
|
|
90
90
|
optionsHashCache.set(options, optionsHash);
|
|
91
91
|
}
|
|
92
92
|
return `${sourceHash}:${optionsHash}`;
|
|
93
|
-
}
|
|
93
|
+
};
|
|
94
94
|
/**
|
|
95
95
|
* Specialized cache for markdown token storage.
|
|
96
96
|
* Extends MemoryCache with markdown-specific convenience methods.
|
|
@@ -32,51 +32,6 @@ export declare const isHtmlOpenTag: (raw: string) => {
|
|
|
32
32
|
* @internal
|
|
33
33
|
*/
|
|
34
34
|
export declare const extractAttributes: (raw: string) => Record<string, string>;
|
|
35
|
-
/**
|
|
36
|
-
* Converts an HTML string into a sequence of tokens using htmlparser2.
|
|
37
|
-
* Handles complex nested structures while maintaining proper order and relationships.
|
|
38
|
-
*
|
|
39
|
-
* Key features:
|
|
40
|
-
* - Preserves original HTML structure without automatic tag closing
|
|
41
|
-
* - Handles self-closing tags with proper XML syntax (e.g., <br/> instead of <br>)
|
|
42
|
-
* - Gracefully handles malformed HTML by preserving the original structure
|
|
43
|
-
* - Maintains attribute information in opening tags
|
|
44
|
-
* - Processes text content between tags
|
|
45
|
-
*
|
|
46
|
-
* @param {string} html - HTML string to be parsed
|
|
47
|
-
* @returns {Token[]} Array of tokens representing the HTML structure
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* // Well-formed HTML
|
|
51
|
-
* parseHtmlBlock('<div>Hello <span>world</span></div>')
|
|
52
|
-
* // Returns [
|
|
53
|
-
* // { type: 'html', raw: '<div>', ... },
|
|
54
|
-
* // { type: 'text', raw: 'Hello ', ... },
|
|
55
|
-
* // { type: 'html', raw: '<span>', ... },
|
|
56
|
-
* // { type: 'text', raw: 'world', ... },
|
|
57
|
-
* // { type: 'html', raw: '</span>', ... },
|
|
58
|
-
* // { type: 'html', raw: '</div>', ... }
|
|
59
|
-
* // ]
|
|
60
|
-
*
|
|
61
|
-
* // Self-closing tags
|
|
62
|
-
* parseHtmlBlock('<div>Before<br/>After</div>')
|
|
63
|
-
* // Returns [
|
|
64
|
-
* // { type: 'html', raw: '<div>', ... },
|
|
65
|
-
* // { type: 'text', raw: 'Before', ... },
|
|
66
|
-
* // { type: 'html', raw: '<br/>', ... },
|
|
67
|
-
* // { type: 'text', raw: 'After', ... },
|
|
68
|
-
* // { type: 'html', raw: '</div>', ... }
|
|
69
|
-
* // ]
|
|
70
|
-
*
|
|
71
|
-
* // Malformed HTML
|
|
72
|
-
* parseHtmlBlock('<div>Unclosed')
|
|
73
|
-
* // Returns [
|
|
74
|
-
* // { type: 'html', raw: '<div>', ... },
|
|
75
|
-
* // { type: 'text', raw: 'Unclosed', ... }
|
|
76
|
-
* // ]
|
|
77
|
-
*
|
|
78
|
-
* @internal
|
|
79
|
-
*/
|
|
80
35
|
export declare const parseHtmlBlock: (html: string) => Token[];
|
|
81
36
|
export declare const containsMultipleTags: (html: string) => boolean;
|
|
82
37
|
/**
|
|
@@ -141,6 +141,22 @@ export const extractAttributes = (raw) => {
|
|
|
141
141
|
*
|
|
142
142
|
* @internal
|
|
143
143
|
*/
|
|
144
|
+
/**
|
|
145
|
+
* Serializes an HTML attribute map into a string for tag construction.
|
|
146
|
+
* Escapes double quotes in values to prevent attribute injection.
|
|
147
|
+
*
|
|
148
|
+
* @param {Record<string, string>} attributes - Map of attribute names to values
|
|
149
|
+
* @returns {string} Serialized attributes string with leading spaces
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* serializeAttributes({ class: 'foo', id: 'bar' })
|
|
153
|
+
* // Returns ' class="foo" id="bar"'
|
|
154
|
+
*
|
|
155
|
+
* @internal
|
|
156
|
+
*/
|
|
157
|
+
const serializeAttributes = (attributes) => Object.entries(attributes)
|
|
158
|
+
.map(([key, value]) => ` ${key}="${value.replace(/"/g, '"')}"`)
|
|
159
|
+
.join('');
|
|
144
160
|
export const parseHtmlBlock = (html) => {
|
|
145
161
|
const tokens = [];
|
|
146
162
|
let currentText = '';
|
|
@@ -158,9 +174,7 @@ export const parseHtmlBlock = (html) => {
|
|
|
158
174
|
if (SELF_CLOSING_TAGS.test(name)) {
|
|
159
175
|
tokens.push({
|
|
160
176
|
type: 'html',
|
|
161
|
-
raw: `<${name}${
|
|
162
|
-
.map(([key, value]) => ` ${key}="${value}"`)
|
|
163
|
-
.join('')}/>`,
|
|
177
|
+
raw: `<${name}${serializeAttributes(attributes)}/>`,
|
|
164
178
|
tag: name,
|
|
165
179
|
attributes
|
|
166
180
|
});
|
|
@@ -169,9 +183,7 @@ export const parseHtmlBlock = (html) => {
|
|
|
169
183
|
openTags.push(name);
|
|
170
184
|
tokens.push({
|
|
171
185
|
type: 'html',
|
|
172
|
-
raw: `<${name}${
|
|
173
|
-
.map(([key, value]) => ` ${key}="${value}"`)
|
|
174
|
-
.join('')}>`,
|
|
186
|
+
raw: `<${name}${serializeAttributes(attributes)}>`,
|
|
175
187
|
tag: name,
|
|
176
188
|
attributes
|
|
177
189
|
});
|
|
@@ -203,8 +215,7 @@ export const parseHtmlBlock = (html) => {
|
|
|
203
215
|
}
|
|
204
216
|
}
|
|
205
217
|
}, {
|
|
206
|
-
xmlMode:
|
|
207
|
-
// Add this to prevent automatic tag closing
|
|
218
|
+
xmlMode: false,
|
|
208
219
|
recognizeSelfClosing: true
|
|
209
220
|
});
|
|
210
221
|
parser.write(html);
|
|
@@ -290,19 +301,16 @@ export const shrinkHtmlTokens = (tokens) => {
|
|
|
290
301
|
}
|
|
291
302
|
else if (token.type === 'table') {
|
|
292
303
|
// Process header cells
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
304
|
+
const tableToken = token;
|
|
305
|
+
if (tableToken.header) {
|
|
306
|
+
tableToken.header = tableToken.header.map((cell) => ({
|
|
296
307
|
...cell,
|
|
297
308
|
tokens: cell.tokens ? shrinkHtmlTokens(cell.tokens) : []
|
|
298
309
|
}));
|
|
299
310
|
}
|
|
300
311
|
// Process row cells
|
|
301
|
-
if (
|
|
302
|
-
|
|
303
|
-
token.rows = token.rows.map((row) =>
|
|
304
|
-
// @ts-expect-error: expected any
|
|
305
|
-
row.map((cell) => ({
|
|
312
|
+
if (tableToken.rows) {
|
|
313
|
+
tableToken.rows = tableToken.rows.map((row) => row.map((cell) => ({
|
|
306
314
|
...cell,
|
|
307
315
|
tokens: cell.tokens ? shrinkHtmlTokens(cell.tokens) : []
|
|
308
316
|
})));
|
|
@@ -394,9 +402,9 @@ export const processHtmlTokens = (tokens) => {
|
|
|
394
402
|
result.push(token);
|
|
395
403
|
}
|
|
396
404
|
}
|
|
397
|
-
// If we have unclosed tags, return
|
|
405
|
+
// If we have unclosed tags, return partial result (better than discarding all work)
|
|
398
406
|
if (stack.length > 0) {
|
|
399
|
-
return
|
|
407
|
+
return result;
|
|
400
408
|
}
|
|
401
409
|
return result;
|
|
402
410
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanspeak/svelte-markdown",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Fast, customizable markdown renderer for Svelte with built-in caching, TypeScript support, and Svelte 5 runes",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|
|
@@ -86,8 +86,8 @@
|
|
|
86
86
|
"@testing-library/user-event": "^14.6.1",
|
|
87
87
|
"@types/katex": "^0.16.8",
|
|
88
88
|
"@types/node": "^25.5.0",
|
|
89
|
-
"@typescript-eslint/eslint-plugin": "^8.57.
|
|
90
|
-
"@typescript-eslint/parser": "^8.57.
|
|
89
|
+
"@typescript-eslint/eslint-plugin": "^8.57.1",
|
|
90
|
+
"@typescript-eslint/parser": "^8.57.1",
|
|
91
91
|
"@vitest/coverage-v8": "^4.1.0",
|
|
92
92
|
"eslint": "^10.0.3",
|
|
93
93
|
"eslint-config-prettier": "^10.1.8",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"eslint-plugin-unused-imports": "^4.4.1",
|
|
97
97
|
"globals": "^17.4.0",
|
|
98
98
|
"husky": "^9.1.7",
|
|
99
|
-
"jsdom": "^
|
|
99
|
+
"jsdom": "^29.0.0",
|
|
100
100
|
"katex": "^0.16.38",
|
|
101
101
|
"marked-katex-extension": "^5.1.7",
|
|
102
102
|
"mermaid": "^11.13.0",
|
|
@@ -106,10 +106,10 @@
|
|
|
106
106
|
"prettier-plugin-svelte": "^3.5.1",
|
|
107
107
|
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
108
108
|
"publint": "^0.3.18",
|
|
109
|
-
"svelte": "^5.53.
|
|
109
|
+
"svelte": "^5.53.13",
|
|
110
110
|
"svelte-check": "^4.4.5",
|
|
111
111
|
"typescript": "^5.9.3",
|
|
112
|
-
"typescript-eslint": "^8.57.
|
|
112
|
+
"typescript-eslint": "^8.57.1",
|
|
113
113
|
"vite": "^8.0.0",
|
|
114
114
|
"vitest": "^4.1.0"
|
|
115
115
|
},
|