@humanspeak/svelte-markdown 0.8.8 → 0.8.9
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
CHANGED
|
@@ -109,44 +109,13 @@
|
|
|
109
109
|
{#each row ?? [] as cells, i (i)}
|
|
110
110
|
{@const { align: _align, ...cellRest } = rest}
|
|
111
111
|
<renderers.tablecell
|
|
112
|
+
{...cellRest}
|
|
112
113
|
header={false}
|
|
113
114
|
align={(rest.align as string[])[i]}
|
|
114
|
-
{...cellRest}
|
|
115
115
|
>
|
|
116
|
-
{#
|
|
117
|
-
{
|
|
118
|
-
|
|
119
|
-
tokens?: Token[]
|
|
120
|
-
}}
|
|
121
|
-
{@const { tag, ...localRest } = token}
|
|
122
|
-
{@const htmlTag = tag as keyof typeof Html}
|
|
123
|
-
{#if renderers.html && htmlTag in renderers.html}
|
|
124
|
-
{@const HtmlComponent =
|
|
125
|
-
renderers.html[
|
|
126
|
-
htmlTag as keyof typeof renderers.html
|
|
127
|
-
]}
|
|
128
|
-
{#if HtmlComponent}
|
|
129
|
-
<HtmlComponent {...token}>
|
|
130
|
-
{#if token.tokens?.length}
|
|
131
|
-
<Parser
|
|
132
|
-
tokens={token.tokens}
|
|
133
|
-
{renderers}
|
|
134
|
-
{...Object.fromEntries(
|
|
135
|
-
Object.entries(
|
|
136
|
-
localRest
|
|
137
|
-
).filter(
|
|
138
|
-
([key]) =>
|
|
139
|
-
key !== 'attributes'
|
|
140
|
-
)
|
|
141
|
-
)}
|
|
142
|
-
/>
|
|
143
|
-
{/if}
|
|
144
|
-
</HtmlComponent>
|
|
145
|
-
{/if}
|
|
146
|
-
{/if}
|
|
147
|
-
{:else}
|
|
148
|
-
<Parser tokens={cells.tokens} {renderers} />
|
|
149
|
-
{/if}
|
|
116
|
+
{#each cells.tokens ?? [] as cellToken, index (index)}
|
|
117
|
+
<Parser {...cellRest} {...cellToken} {renderers} />
|
|
118
|
+
{/each}
|
|
150
119
|
</renderers.tablecell>
|
|
151
120
|
{/each}
|
|
152
121
|
</renderers.tablerow>
|
|
@@ -8,6 +8,7 @@ import B from './B.svelte';
|
|
|
8
8
|
import Bdi from './Bdi.svelte';
|
|
9
9
|
import Bdo from './Bdo.svelte';
|
|
10
10
|
import Blockquote from './Blockquote.svelte';
|
|
11
|
+
import Br from './Br.svelte';
|
|
11
12
|
import Button from './Button.svelte';
|
|
12
13
|
import Canvas from './Canvas.svelte';
|
|
13
14
|
import Cite from './Cite.svelte';
|
|
@@ -91,6 +92,7 @@ export const Html = {
|
|
|
91
92
|
bdi: Bdi,
|
|
92
93
|
bdo: Bdo,
|
|
93
94
|
blockquote: Blockquote,
|
|
95
|
+
br: Br,
|
|
94
96
|
button: Button,
|
|
95
97
|
canvas: Canvas,
|
|
96
98
|
cite: Cite,
|
|
@@ -94,6 +94,7 @@ export declare const containsMultipleTags: (html: string) => boolean;
|
|
|
94
94
|
*
|
|
95
95
|
* Key features:
|
|
96
96
|
* - Breaks down complex HTML structures into atomic tokens
|
|
97
|
+
* - Formats self-closing tags with proper syntax (e.g., <br> -> <br/>)
|
|
97
98
|
* - Maintains attribute information
|
|
98
99
|
* - Preserves proper nesting relationships
|
|
99
100
|
* - Handles malformed HTML gracefully
|
|
@@ -12,6 +12,11 @@ import * as htmlparser2 from 'htmlparser2';
|
|
|
12
12
|
*/
|
|
13
13
|
const HTML_TAG_PATTERN = /<\/?([a-zA-Z][a-zA-Z0-9-]{0,})(?:\s+[^>]*)?>/;
|
|
14
14
|
const htmlTagRegex = new RegExp(HTML_TAG_PATTERN);
|
|
15
|
+
/**
|
|
16
|
+
* Regex pattern for self-closing HTML tags.
|
|
17
|
+
* @const {RegExp}
|
|
18
|
+
*/
|
|
19
|
+
const SELF_CLOSING_TAGS = /^(br|hr|img|input|link|meta|area|base|col|embed|keygen|param|source|track|wbr)$/i;
|
|
15
20
|
/**
|
|
16
21
|
* Analyzes a string to determine if it contains an HTML tag and its characteristics.
|
|
17
22
|
*
|
|
@@ -37,6 +42,33 @@ export const isHtmlOpenTag = (raw) => {
|
|
|
37
42
|
return null;
|
|
38
43
|
return { tag: match[1], isOpening: !raw.startsWith('</') };
|
|
39
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Formats individual HTML tokens to ensure self-closing tags are properly formatted.
|
|
47
|
+
* This handles cases like <br> -> <br/> without affecting the token structure.
|
|
48
|
+
*
|
|
49
|
+
* @param {Token} token - HTML token to format
|
|
50
|
+
* @returns {Token} Formatted token with proper self-closing syntax
|
|
51
|
+
*/
|
|
52
|
+
const formatSelfClosingHtmlToken = (token) => {
|
|
53
|
+
// Extract tag name from raw HTML
|
|
54
|
+
const tagMatch = token.raw.match(/<\/?([a-zA-Z][a-zA-Z0-9-]*)/i);
|
|
55
|
+
if (!tagMatch)
|
|
56
|
+
return token;
|
|
57
|
+
const tagName = tagMatch[1];
|
|
58
|
+
if (!SELF_CLOSING_TAGS.test(tagName))
|
|
59
|
+
return token;
|
|
60
|
+
// If it's a self-closing tag and doesn't already end with />, format it properly
|
|
61
|
+
if (!token.raw.endsWith('/>')) {
|
|
62
|
+
const formattedRaw = token.raw.replace(/\s*>$/, '/>');
|
|
63
|
+
return {
|
|
64
|
+
...token,
|
|
65
|
+
raw: formattedRaw,
|
|
66
|
+
tag: tagName,
|
|
67
|
+
attributes: extractAttributes(token.raw)
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return token;
|
|
71
|
+
};
|
|
40
72
|
/**
|
|
41
73
|
* Parses HTML attributes from a tag string into a structured object.
|
|
42
74
|
* Handles both single and double quoted attributes.
|
|
@@ -117,7 +149,6 @@ export const extractAttributes = (raw) => {
|
|
|
117
149
|
export const parseHtmlBlock = (html) => {
|
|
118
150
|
const tokens = [];
|
|
119
151
|
let currentText = '';
|
|
120
|
-
const selfClosingTags = /^(br|hr|img|input|link|meta|area|base|col|embed|keygen|param|source|track|wbr)$/i;
|
|
121
152
|
const openTags = [];
|
|
122
153
|
const parser = new htmlparser2.Parser({
|
|
123
154
|
onopentag: (name, attributes) => {
|
|
@@ -129,8 +160,7 @@ export const parseHtmlBlock = (html) => {
|
|
|
129
160
|
});
|
|
130
161
|
currentText = '';
|
|
131
162
|
}
|
|
132
|
-
|
|
133
|
-
if (selfClosingTags.test(name)) {
|
|
163
|
+
if (SELF_CLOSING_TAGS.test(name)) {
|
|
134
164
|
tokens.push({
|
|
135
165
|
type: 'html',
|
|
136
166
|
raw: `<${name}${Object.entries(attributes)
|
|
@@ -141,6 +171,7 @@ export const parseHtmlBlock = (html) => {
|
|
|
141
171
|
});
|
|
142
172
|
}
|
|
143
173
|
else {
|
|
174
|
+
openTags.push(name);
|
|
144
175
|
tokens.push({
|
|
145
176
|
type: 'html',
|
|
146
177
|
raw: `<${name}${Object.entries(attributes)
|
|
@@ -165,7 +196,7 @@ export const parseHtmlBlock = (html) => {
|
|
|
165
196
|
}
|
|
166
197
|
// Only add closing tag if we found its opening tag
|
|
167
198
|
// and it's not a self-closing tag
|
|
168
|
-
if (openTags.includes(name) && !
|
|
199
|
+
if (openTags.includes(name) && !SELF_CLOSING_TAGS.test(name)) {
|
|
169
200
|
if (html.includes(`</${name}>`)) {
|
|
170
201
|
tokens.push({
|
|
171
202
|
type: 'html',
|
|
@@ -219,6 +250,7 @@ export const containsMultipleTags = (html) => {
|
|
|
219
250
|
*
|
|
220
251
|
* Key features:
|
|
221
252
|
* - Breaks down complex HTML structures into atomic tokens
|
|
253
|
+
* - Formats self-closing tags with proper syntax (e.g., <br> -> <br/>)
|
|
222
254
|
* - Maintains attribute information
|
|
223
255
|
* - Preserves proper nesting relationships
|
|
224
256
|
* - Handles malformed HTML gracefully
|
|
@@ -251,6 +283,7 @@ export const shrinkHtmlTokens = (tokens) => {
|
|
|
251
283
|
else if (token.type === 'table') {
|
|
252
284
|
// Process header cells
|
|
253
285
|
if (token.header) {
|
|
286
|
+
// @ts-expect-error: expected any
|
|
254
287
|
token.header = token.header.map((cell) => ({
|
|
255
288
|
...cell,
|
|
256
289
|
tokens: cell.tokens ? shrinkHtmlTokens(cell.tokens) : []
|
|
@@ -258,7 +291,10 @@ export const shrinkHtmlTokens = (tokens) => {
|
|
|
258
291
|
}
|
|
259
292
|
// Process row cells
|
|
260
293
|
if (token.rows) {
|
|
261
|
-
|
|
294
|
+
// @ts-expect-error: expected any
|
|
295
|
+
token.rows = token.rows.map((row) =>
|
|
296
|
+
// @ts-expect-error: expected any
|
|
297
|
+
row.map((cell) => ({
|
|
262
298
|
...cell,
|
|
263
299
|
tokens: cell.tokens ? shrinkHtmlTokens(cell.tokens) : []
|
|
264
300
|
})));
|
|
@@ -269,6 +305,11 @@ export const shrinkHtmlTokens = (tokens) => {
|
|
|
269
305
|
// Parse HTML with multiple tags into separate tokens
|
|
270
306
|
result.push(...parseHtmlBlock(token.raw));
|
|
271
307
|
}
|
|
308
|
+
else if (token.type === 'html') {
|
|
309
|
+
// Format self-closing tags properly (e.g., <br> -> <br/>)
|
|
310
|
+
const formattedToken = formatSelfClosingHtmlToken(token);
|
|
311
|
+
result.push(formattedToken);
|
|
312
|
+
}
|
|
272
313
|
else {
|
|
273
314
|
result.push(token);
|
|
274
315
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanspeak/svelte-markdown",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.9",
|
|
4
4
|
"description": "A powerful, customizable markdown renderer for Svelte with TypeScript support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|
|
@@ -77,47 +77,47 @@
|
|
|
77
77
|
"dependencies": {
|
|
78
78
|
"github-slugger": "^2.0.0",
|
|
79
79
|
"htmlparser2": "^10.0.0",
|
|
80
|
-
"marked": "^16.
|
|
80
|
+
"marked": "^16.1.1"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
83
|
"@eslint/compat": "^1.3.1",
|
|
84
|
-
"@eslint/js": "^9.
|
|
84
|
+
"@eslint/js": "^9.32.0",
|
|
85
85
|
"@playwright/test": "^1.54.1",
|
|
86
86
|
"@sveltejs/adapter-auto": "^6.0.1",
|
|
87
|
-
"@sveltejs/kit": "^2.
|
|
88
|
-
"@sveltejs/package": "^2.
|
|
89
|
-
"@sveltejs/vite-plugin-svelte": "^6.
|
|
90
|
-
"@testing-library/jest-dom": "^6.6.
|
|
87
|
+
"@sveltejs/kit": "^2.26.1",
|
|
88
|
+
"@sveltejs/package": "^2.4.0",
|
|
89
|
+
"@sveltejs/vite-plugin-svelte": "^6.1.0",
|
|
90
|
+
"@testing-library/jest-dom": "^6.6.4",
|
|
91
91
|
"@testing-library/svelte": "^5.2.8",
|
|
92
92
|
"@testing-library/user-event": "^14.6.1",
|
|
93
|
-
"@types/node": "^24.0
|
|
94
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
95
|
-
"@typescript-eslint/parser": "^8.
|
|
93
|
+
"@types/node": "^24.1.0",
|
|
94
|
+
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
|
95
|
+
"@typescript-eslint/parser": "^8.38.0",
|
|
96
96
|
"@vitest/coverage-v8": "^3.2.4",
|
|
97
|
-
"eslint": "^9.
|
|
98
|
-
"eslint-config-prettier": "^10.1.
|
|
97
|
+
"eslint": "^9.32.0",
|
|
98
|
+
"eslint-config-prettier": "^10.1.8",
|
|
99
99
|
"eslint-plugin-import": "^2.32.0",
|
|
100
|
-
"eslint-plugin-svelte": "^3.
|
|
100
|
+
"eslint-plugin-svelte": "^3.11.0",
|
|
101
101
|
"eslint-plugin-unused-imports": "^4.1.4",
|
|
102
102
|
"globals": "^16.3.0",
|
|
103
103
|
"jsdom": "^26.1.0",
|
|
104
104
|
"prettier": "^3.6.2",
|
|
105
|
-
"prettier-plugin-organize-imports": "^4.
|
|
105
|
+
"prettier-plugin-organize-imports": "^4.2.0",
|
|
106
106
|
"prettier-plugin-svelte": "^3.4.0",
|
|
107
107
|
"prettier-plugin-tailwindcss": "^0.6.14",
|
|
108
108
|
"publint": "^0.3.12",
|
|
109
|
-
"svelte": "^5.
|
|
110
|
-
"svelte-check": "^4.
|
|
109
|
+
"svelte": "^5.37.0",
|
|
110
|
+
"svelte-check": "^4.3.0",
|
|
111
111
|
"typescript": "^5.8.3",
|
|
112
|
-
"typescript-eslint": "^8.
|
|
113
|
-
"vite": "^7.0.
|
|
112
|
+
"typescript-eslint": "^8.38.0",
|
|
113
|
+
"vite": "^7.0.6",
|
|
114
114
|
"vitest": "^3.2.4"
|
|
115
115
|
},
|
|
116
116
|
"peerDependencies": {
|
|
117
117
|
"svelte": "^5.0.0"
|
|
118
118
|
},
|
|
119
119
|
"volta": {
|
|
120
|
-
"node": "22.17.
|
|
120
|
+
"node": "22.17.1"
|
|
121
121
|
},
|
|
122
122
|
"publishConfig": {
|
|
123
123
|
"access": "public"
|